Ejemplo n.º 1
0
bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext &context, QSet<QString> &attributeNames )
{
  QgsDiagramLayerSettings &s2 = mSettings;
  const QgsMapSettings &mapSettings = mEngine->mapSettings();

  if ( context.coordinateTransform().isValid() )
    // this is context for layer rendering - use its CT as it includes correct datum transform
    s2.setCoordinateTransform( context.coordinateTransform() );
  else
  {
    // otherwise fall back to creating our own CT - this one may not have the correct datum transform!
    Q_NOWARN_DEPRECATED_PUSH
    s2.setCoordinateTransform( QgsCoordinateTransform( mLayerCrs, mapSettings.destinationCrs() ) );
    Q_NOWARN_DEPRECATED_POP
  }

  s2.setRenderer( mDiagRenderer );

  bool result = s2.prepare( context.expressionContext() );

  //add attributes needed by the diagram renderer
  attributeNames.unite( s2.referencedFields( context.expressionContext() ) );

  return result;
}
void QgsCategorizedSymbolRendererV2::startRender( QgsRenderContext& context, const QgsFields& fields )
{
  mCounting = context.rendererScale() == 0.0;

  // make sure that the hash table is up to date
  rebuildHash();

  // find out classification attribute index from name
  mAttrNum = fields.fieldNameIndex( mAttrName );
  if ( mAttrNum == -1 )
  {
    mExpression.reset( new QgsExpression( mAttrName ) );
    mExpression->prepare( &context.expressionContext() );
  }

  QgsCategoryList::iterator it = mCategories.begin();
  for ( ; it != mCategories.end(); ++it )
  {
    it->symbol()->startRender( context, &fields );

    if ( mRotation.data() || mSizeScale.data() )
    {
      QgsSymbolV2* tempSymbol = it->symbol()->clone();
      tempSymbol->setRenderHints(( mRotation.data() ? QgsSymbolV2::DataDefinedRotation : 0 ) |
                                 ( mSizeScale.data() ? QgsSymbolV2::DataDefinedSizeScale : 0 ) );
      tempSymbol->startRender( context, &fields );
      mTempSymbols[ it->symbol()] = tempSymbol;
    }
  }
  return;
}
Ejemplo n.º 3
0
QSizeF QgsTextDiagram::diagramSize( const QgsFeature& feature, const QgsRenderContext& c, const QgsDiagramSettings& s, const QgsDiagramInterpolationSettings& is )
{
  QgsExpressionContext expressionContext = c.expressionContext();
  expressionContext.setFeature( feature );
  if ( feature.fields() )
    expressionContext.setFields( *feature.fields() );

  QVariant attrVal;
  if ( is.classificationAttributeIsExpression )
  {
    QgsExpression* expression = getExpression( is.classificationAttributeExpression, expressionContext );
    attrVal = expression->evaluate( &expressionContext );
  }
  else
  {
    attrVal = feature.attributes().at( is.classificationAttribute );
  }

  bool ok = false;
  double val = attrVal.toDouble( &ok );
  if ( !ok )
  {
    return QSizeF(); //zero size if attribute is missing
  }

  return sizeForValue( val, s, is );
}
Ejemplo n.º 4
0
bool QgsVectorLayerLabelProvider::prepare( const QgsRenderContext &context, QSet<QString> &attributeNames )
{
  QgsPalLayerSettings &lyr = mSettings;
  const QgsMapSettings &mapSettings = mEngine->mapSettings();

  QgsDebugMsgLevel( "PREPARE LAYER " + mLayerId, 4 );

  if ( lyr.drawLabels )
  {
    if ( lyr.fieldName.isEmpty() )
    {
      return false;
    }

    if ( lyr.isExpression )
    {
      QgsExpression exp( lyr.fieldName );
      if ( exp.hasEvalError() )
      {
        QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
        return false;
      }
    }
    else
    {
      // If we aren't an expression, we check to see if we can find the column.
      if ( mFields.lookupField( lyr.fieldName ) == -1 )
      {
        return false;
      }
    }
  }

  lyr.mCurFields = mFields;

  if ( lyr.drawLabels || lyr.obstacle )
  {
    if ( lyr.drawLabels )
    {
      // add field indices for label's text, from expression or field
      if ( lyr.isExpression )
      {
        // prepare expression for use in QgsPalLayerSettings::registerFeature()
        QgsExpression *exp = lyr.getLabelExpression();
        exp->prepare( &context.expressionContext() );
        if ( exp->hasEvalError() )
        {
          QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 );
        }
        Q_FOREACH ( const QString &name, exp->referencedColumns() )
        {
          QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 );
          attributeNames.insert( name );
        }
      }
      else
      {
        attributeNames.insert( lyr.fieldName );
      }
    }
Ejemplo n.º 5
0
void Qgs25DRenderer::startRender( QgsRenderContext& context, const QgsFields& fields )
{
  QgsExpressionContextScope* scope = new QgsExpressionContextScope( "2.5D Renderer" );
  scope->setVariable( "qgis_25d_height", mHeight.expressionOrField() );
  scope->setVariable( "qgis_25d_angle", mAngle );
  context.expressionContext().appendScope( scope );
  mSymbol->startRender( context, &fields );
}
Ejemplo n.º 6
0
bool QgsVectorLayerFeatureCounter::run()
{
  QgsLegendSymbolList symbolList = mRenderer->legendSymbolItems();
  QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin();

  for ( ; symbolIt != symbolList.constEnd(); ++symbolIt )
  {
    mSymbolFeatureCountMap.insert( symbolIt->label(), 0 );
  }

  // If there are no features to be counted, we can spare us the trouble
  if ( mFeatureCount > 0 )
  {
    int featuresCounted = 0;

    // Renderer (rule based) may depend on context scale, with scale is ignored if 0
    QgsRenderContext renderContext;
    renderContext.setRendererScale( 0 );
    renderContext.setExpressionContext( mExpressionContext );

    QgsFeatureRequest request;
    if ( !mRenderer->filterNeedsGeometry() )
      request.setFlags( QgsFeatureRequest::NoGeometry );
    request.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mSource->fields() );
    QgsFeatureIterator fit = mSource->getFeatures( request );

    // TODO: replace QgsInterruptionChecker with QgsFeedback
    // fit.setInterruptionChecker( mFeedback );

    mRenderer->startRender( renderContext, mSource->fields() );

    double progress = 0;
    QgsFeature f;
    while ( fit.nextFeature( f ) )
    {
      renderContext.expressionContext().setFeature( f );
      QSet<QString> featureKeyList = mRenderer->legendKeysForFeature( f, renderContext );
      Q_FOREACH ( const QString &key, featureKeyList )
      {
        mSymbolFeatureCountMap[key] += 1;
      }
      ++featuresCounted;

      double p = ( static_cast< double >( featuresCounted ) / mFeatureCount ) * 100;
      if ( p - progress > 1 )
      {
        progress = p;
        setProgress( progress );
      }

      if ( isCanceled() )
      {
        mRenderer->stopRender( renderContext );
        return false;
      }
    }
    mRenderer->stopRender( renderContext );
  }
void QgsPointDisplacementRenderer::drawSymbols( const ClusteredGroup &group, QgsRenderContext &context, const QList<QPointF> &symbolPositions )
{
  QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
  ClusteredGroup::const_iterator groupIt = group.constBegin();
  for ( ; symbolPosIt != symbolPositions.constEnd() && groupIt != group.constEnd();
        ++symbolPosIt, ++groupIt )
  {
    context.expressionContext().setFeature( groupIt->feature );
    groupIt->symbol()->startRender( context );
    groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected );
    groupIt->symbol()->stopRender( context );
  }
}
Ejemplo n.º 8
0
    void test_willRenderFeature_symbolsForFeature()
    {
      // prepare features
      QgsVectorLayer* layer = new QgsVectorLayer( "point?field=fld:int", "x", "memory" );
      int idx = layer->fieldNameIndex( "fld" );
      QVERIFY( idx != -1 );
      QgsFeature f1;
      f1.initAttributes( 1 );
      f1.setAttribute( idx, QVariant( 2 ) );
      QgsFeature f2;
      f2.initAttributes( 1 );
      f2.setAttribute( idx, QVariant( 8 ) );
      QgsFeature f3;
      f3.initAttributes( 1 );
      f3.setAttribute( idx, QVariant( 100 ) );

      // prepare renderer
      QgsSymbol* s1 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
      QgsSymbol* s2 = QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry );
      RRule* rootRule = new RRule( nullptr );
      rootRule->appendChild( new RRule( s1, 0, 0, "fld >= 5 and fld <= 20" ) );
      rootRule->appendChild( new RRule( s2, 0, 0, "fld <= 10" ) );
      QgsRuleBasedRenderer r( rootRule );

      QVERIFY( r.capabilities() & QgsFeatureRenderer::MoreSymbolsPerFeature );

      QgsRenderContext ctx; // dummy render context
      ctx.expressionContext().setFields( layer->fields() );
      r.startRender( ctx, layer->fields() );

      // test willRenderFeature
      ctx.expressionContext().setFeature( f1 );
      QVERIFY( r.willRenderFeature( f1, ctx ) );
      ctx.expressionContext().setFeature( f2 );
      QVERIFY( r.willRenderFeature( f2, ctx ) );
      ctx.expressionContext().setFeature( f3 );
      QVERIFY( !r.willRenderFeature( f3, ctx ) );

      // test symbolsForFeature
      ctx.expressionContext().setFeature( f1 );
      QgsSymbolList lst1 = r.symbolsForFeature( f1, ctx );
      QVERIFY( lst1.count() == 1 );
      ctx.expressionContext().setFeature( f2 );
      QgsSymbolList lst2 = r.symbolsForFeature( f2, ctx );
      QVERIFY( lst2.count() == 2 );
      ctx.expressionContext().setFeature( f3 );
      QgsSymbolList lst3 = r.symbolsForFeature( f3, ctx );
      QVERIFY( lst3.isEmpty() );

      r.stopRender( ctx );

      delete layer;
    }
Ejemplo n.º 9
0
QVariant QgsGraduatedSymbolRenderer::valueForFeature( QgsFeature &feature, QgsRenderContext &context ) const
{
  QgsAttributes attrs = feature.attributes();
  QVariant value;
  if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
  {
    value = mExpression->evaluate( &context.expressionContext() );
  }
  else
  {
    value = attrs.at( mAttrNum );
  }

  return value;
}
Ejemplo n.º 10
0
QSizeF QgsHistogramDiagram::diagramSize( const QgsFeature& feature, const QgsRenderContext& c, const QgsDiagramSettings& s, const QgsDiagramInterpolationSettings& is )
{
  QSizeF size;
  if ( feature.attributes().isEmpty() )
  {
    return size; //zero size if no attributes
  }

  if ( qgsDoubleNear( is.upperValue, is.lowerValue ) )
    return size; // invalid value range => zero size

  double maxValue = 0;

  QgsExpressionContext expressionContext = c.expressionContext();
  expressionContext.setFeature( feature );
  if ( !feature.fields().isEmpty() )
    expressionContext.setFields( feature.fields() );

  Q_FOREACH ( const QString& cat, s.categoryAttributes )
  {
    QgsExpression* expression = getExpression( cat, expressionContext );
    maxValue = qMax( expression->evaluate( &expressionContext ).toDouble(), maxValue );
  }

  // Scale, if extension is smaller than the specified minimum
  if ( maxValue < s.minimumSize )
  {
    maxValue = s.minimumSize;
  }

  switch ( s.diagramOrientation )
  {
    case QgsDiagramSettings::Up:
    case QgsDiagramSettings::Down:
      mScaleFactor = (( is.upperSize.width() - is.lowerSize.height() ) / ( is.upperValue - is.lowerValue ) );
      size.scale( s.barWidth * s.categoryAttributes.size(), maxValue * mScaleFactor, Qt::IgnoreAspectRatio );
      break;

    case QgsDiagramSettings::Right:
    case QgsDiagramSettings::Left:
      mScaleFactor = (( is.upperSize.width() - is.lowerSize.width() ) / ( is.upperValue - is.lowerValue ) );
      size.scale( maxValue * mScaleFactor, s.barWidth * s.categoryAttributes.size(), Qt::IgnoreAspectRatio );
      break;
  }

  return size;
}
QVariant QgsCategorizedSymbolRendererV2::valueForFeature( QgsFeature& feature, QgsRenderContext &context ) const
{
  QgsAttributes attrs = feature.attributes();
  QVariant value;
  if ( mAttrNum == -1 )
  {
    Q_ASSERT( mExpression.data() );

    value = mExpression->evaluate( &context.expressionContext() );
  }
  else
  {
    value = attrs.value( mAttrNum );
  }

  return value;
}
Ejemplo n.º 12
0
void QgsVectorLayerDiagramProvider::drawLabel( QgsRenderContext &context, pal::LabelPosition *label ) const
{
#if 1 // XXX strk
  // features are pre-rotated but not scaled/translated,
  // so we only disable rotation here. Ideally, they'd be
  // also pre-scaled/translated, as suggested here:
  // https://issues.qgis.org/issues/11856
  QgsMapToPixel xform = context.mapToPixel();
  xform.setMapRotation( 0, 0, 0 );
#else
  const QgsMapToPixel &xform = context.mapToPixel();
#endif

  QgsDiagramLabelFeature *dlf = dynamic_cast<QgsDiagramLabelFeature *>( label->getFeaturePart()->feature() );

  QgsFeature feature;
  feature.setFields( mFields );
  feature.setValid( true );
  feature.setId( label->getFeaturePart()->featureId() );
  feature.setAttributes( dlf->attributes() );

  context.expressionContext().setFeature( feature );

  //calculate top-left point for diagram
  //first, calculate the centroid of the label (accounts for PAL creating
  //rotated labels when we do not want to draw the diagrams rotated)
  double centerX = 0;
  double centerY = 0;
  for ( int i = 0; i < 4; ++i )
  {
    centerX += label->getX( i );
    centerY += label->getY( i );
  }
  QgsPointXY outPt( centerX / 4.0, centerY / 4.0 );
  //then, calculate the top left point for the diagram with this center position
  QgsPointXY centerPt = xform.transform( outPt.x() - label->getWidth() / 2,
                                         outPt.y() - label->getHeight() / 2 );

  mSettings.renderer()->renderDiagram( feature, context, centerPt.toQPointF(), mSettings.dataDefinedProperties() );

  //insert into label search tree to manipulate position interactively
  mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, QString(), QFont(), true, false );

}
Ejemplo n.º 13
0
void QgsHeatmapRenderer::startRender( QgsRenderContext& context, const QgsFields& fields )
{
  Q_UNUSED( fields );
  if ( !context.painter() )
  {
    return;
  }

  // find out classification attribute index from name
  mWeightAttrNum = fields.lookupField( mWeightExpressionString );
  if ( mWeightAttrNum == -1 )
  {
    mWeightExpression.reset( new QgsExpression( mWeightExpressionString ) );
    mWeightExpression->prepare( &context.expressionContext() );
  }

  initializeValues( context );
  return;
}
Ejemplo n.º 14
0
void QgsSymbolLegendNode::exportSymbolToJson( const QgsLegendSettings &settings, const QgsRenderContext &context, QJsonObject &json ) const
{
  const QgsSymbol *s = mItem.symbol();
  if ( !s )
  {
    return;
  }

  QgsRenderContext ctx;
  ctx.setScaleFactor( settings.dpi() / 25.4 );
  ctx.setRendererScale( settings.mapScale() );
  ctx.setMapToPixel( QgsMapToPixel( 1 / ( settings.mmPerMapUnit() * ctx.scaleFactor() ) ) );
  ctx.setForceVectorOutput( true );

  // ensure that a minimal expression context is available
  QgsExpressionContext expContext = context.expressionContext();
  expContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
  ctx.setExpressionContext( expContext );

  const QPixmap pix = QgsSymbolLayerUtils::symbolPreviewPixmap( mItem.symbol(), minimumIconSize(), 0, &ctx );
  QImage img( pix.toImage().convertToFormat( QImage::Format_ARGB32_Premultiplied ) );

  int opacity = 255;
  if ( QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( layerNode()->layer() ) )
    opacity = ( 255 * vectorLayer->opacity() );

  if ( opacity != 255 )
  {
    QPainter painter;
    painter.begin( &img );
    painter.setCompositionMode( QPainter::CompositionMode_DestinationIn );
    painter.fillRect( pix.rect(), QColor( 0, 0, 0, opacity ) );
    painter.end();
  }

  QByteArray byteArray;
  QBuffer buffer( &byteArray );
  img.save( &buffer, "PNG" );
  const QString base64 = QString::fromLatin1( byteArray.toBase64().data() );
  json[ "icon" ] = base64;
}
Ejemplo n.º 15
0
void QgsGraduatedSymbolRenderer::startRender( QgsRenderContext &context, const QgsFields &fields )
{
  mCounting = context.rendererScale() == 0.0;

  // find out classification attribute index from name
  mAttrNum = fields.lookupField( mAttrName );

  if ( mAttrNum == -1 )
  {
    mExpression.reset( new QgsExpression( mAttrName ) );
    mExpression->prepare( &context.expressionContext() );
  }

  Q_FOREACH ( const QgsRendererRange &range, mRanges )
  {
    if ( !range.symbol() )
      continue;

    range.symbol()->startRender( context, fields );
  }
}
Ejemplo n.º 16
0
void QgsMapHitTest::runHitTestLayer( QgsVectorLayer* vl, SymbolV2Set& usedSymbols, QgsRenderContext& context )
{
  QgsFeatureRendererV2* r = vl->rendererV2();
  bool moreSymbolsPerFeature = r->capabilities() & QgsFeatureRendererV2::MoreSymbolsPerFeature;
  r->startRender( context, vl->fields() );
  QgsFeature f;
  QgsFeatureRequest request( context.extent() );
  request.setFlags( QgsFeatureRequest::ExactIntersect );
  QgsFeatureIterator fi = vl->getFeatures( request );
  while ( fi.nextFeature( f ) )
  {
    context.expressionContext().setFeature( f );
    if ( moreSymbolsPerFeature )
    {
      Q_FOREACH ( QgsSymbolV2* s, r->originalSymbolsForFeature( f, context ) )
        usedSymbols.insert( s );
    }
    else
      usedSymbols.insert( r->originalSymbolForFeature( f, context ) );
  }
  r->stopRender( context );
}
Ejemplo n.º 17
0
void QgsMapHitTest::run()
{
  // TODO: do we need this temp image?
  QImage tmpImage( mSettings.outputSize(), mSettings.outputImageFormat() );
  tmpImage.setDotsPerMeterX( mSettings.outputDpi() * 25.4 );
  tmpImage.setDotsPerMeterY( mSettings.outputDpi() * 25.4 );
  QPainter painter( &tmpImage );

  QgsRenderContext context = QgsRenderContext::fromMapSettings( mSettings );
  context.setPainter( &painter ); // we are not going to draw anything, but we still need a working painter

  Q_FOREACH ( const QString& layerID, mSettings.layers() )
  {
    QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( layerID ) );
    if ( !vl || !vl->rendererV2() )
      continue;

    if ( vl->hasScaleBasedVisibility() && ( mSettings.scale() < vl->minimumScale() || mSettings.scale() > vl->maximumScale() ) )
    {
      mHitTest[vl] = SymbolV2Set(); // no symbols -> will not be shown
      continue;
    }

    if ( mSettings.hasCrsTransformEnabled() )
    {
      context.setCoordinateTransform( mSettings.layerTransform( vl ) );
      context.setExtent( mSettings.outputExtentToLayerExtent( vl, mSettings.visibleExtent() ) );
    }

    context.expressionContext() << QgsExpressionContextUtils::layerScope( vl );
    SymbolV2Set& usedSymbols = mHitTest[vl];
    runHitTestLayer( vl, usedSymbols, context );
  }

  painter.end();
}
void QgsAttributeTableFilterModel::generateListOfVisibleFeatures()
{
  if ( !layer() )
    return;

  bool filter = false;
  QgsRectangle rect = mCanvas->mapSettings().mapToLayerCoordinates( layer(), mCanvas->extent() );
  QgsRenderContext renderContext;
  renderContext.expressionContext() << QgsExpressionContextUtils::globalScope()
  << QgsExpressionContextUtils::projectScope()
  << QgsExpressionContextUtils::layerScope( layer() );
  QgsFeatureRendererV2* renderer = layer()->rendererV2();

  mFilteredFeatures.clear();

  if ( !renderer )
  {
    QgsDebugMsg( "Cannot get renderer" );
    return;
  }

  const QgsMapSettings& ms = mCanvas->mapSettings();
  if ( layer()->hasScaleBasedVisibility() &&
       ( layer()->minimumScale() > ms.scale() ||
         layer()->maximumScale() <= ms.scale() ) )
  {
    QgsDebugMsg( "Out of scale limits" );
  }
  else
  {
    if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent )
    {
      // setup scale
      // mapRenderer()->renderContext()->scale is not automaticaly updated when
      // render extent changes (because it's scale is used to identify if changed
      // since last render) -> use local context
      renderContext.setExtent( ms.visibleExtent() );
      renderContext.setMapToPixel( ms.mapToPixel() );
      renderContext.setRendererScale( ms.scale() );
    }

    filter = renderer && renderer->capabilities() & QgsFeatureRendererV2::Filter;
  }

  renderer->startRender( renderContext, layer()->fields() );

  QgsFeatureRequest r( masterModel()->request() );
  if ( !r.filterRect().isNull() )
  {
    r.setFilterRect( r.filterRect().intersect( &rect ) );
  }
  else
  {
    r.setFilterRect( rect );
  }
  QgsFeatureIterator features = masterModel()->layerCache()->getFeatures( r );

  QgsFeature f;

  while ( features.nextFeature( f ) )
  {
    renderContext.expressionContext().setFeature( f );
    if ( !filter || renderer->willRenderFeature( f, renderContext ) )
    {
      mFilteredFeatures << f.id();
    }
#if 0
    if ( t.elapsed() > 5000 )
    {
      bool cancel = false;
      emit progress( i, cancel );
      if ( cancel )
        break;

      t.restart();
    }
#endif
  }

  features.close();

  if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent )
  {
    renderer->stopRender( renderContext );
  }
}
Ejemplo n.º 19
0
void QgsHistogramDiagram::renderDiagram( const QgsFeature& feature, QgsRenderContext& c, const QgsDiagramSettings& s, QPointF position )
{
  QPainter* p = c.painter();
  if ( !p )
  {
    return;
  }

  QList<double> values;
  double maxValue = 0;

  QgsExpressionContext expressionContext = c.expressionContext();
  expressionContext.setFeature( feature );
  if ( !feature.fields().isEmpty() )
    expressionContext.setFields( feature.fields() );

  Q_FOREACH ( const QString& cat, s.categoryAttributes )
  {
    QgsExpression* expression = getExpression( cat, expressionContext );
    double currentVal = expression->evaluate( &expressionContext ).toDouble();
    values.push_back( currentVal );
    maxValue = qMax( currentVal, maxValue );
  }

  double scaledMaxVal = sizePainterUnits( maxValue * mScaleFactor, s, c );

  double currentOffset = 0;
  double scaledWidth = sizePainterUnits( s.barWidth, s, c );

  double baseX = position.x();
  double baseY = position.y();

  mPen.setColor( s.penColor );
  setPenWidth( mPen, s, c );
  p->setPen( mPen );

  QList<double>::const_iterator valIt = values.constBegin();
  QList< QColor >::const_iterator colIt = s.categoryColors.constBegin();
  for ( ; valIt != values.constEnd(); ++valIt, ++colIt )
  {
    double length = sizePainterUnits( *valIt * mScaleFactor, s, c );

    mCategoryBrush.setColor( *colIt );
    p->setBrush( mCategoryBrush );

    switch ( s.diagramOrientation )
    {
      case QgsDiagramSettings::Up:
        p->drawRect( baseX + currentOffset, baseY, scaledWidth, length * -1 );
        break;

      case QgsDiagramSettings::Down:
        p->drawRect( baseX + currentOffset, baseY - scaledMaxVal, scaledWidth, length );
        break;

      case QgsDiagramSettings::Right:
        p->drawRect( baseX, baseY - currentOffset, length, scaledWidth * -1 );
        break;

      case QgsDiagramSettings::Left:
        p->drawRect( baseX + scaledMaxVal, baseY - currentOffset, 0 - length, scaledWidth * -1 );
        break;
    }

    currentOffset += scaledWidth;
  }
}
Ejemplo n.º 20
0
void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,
	QgsGeometry* selectGeometry,
	bool doContains,
	bool doDifference,
	bool singleSelect )
{
	if ( selectGeometry->type() != QGis::Polygon )
	{
		return;
	}
	QgsVectorLayer* vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas );
	if ( vlayer == nullptr )
	{
		return;
	}

	// toLayerCoordinates will throw an exception for any 'invalid' points in
	// the rubber band.
	// For example, if you project a world map onto a globe using EPSG 2163
	// and then click somewhere off the globe, an exception will be thrown.
	//QgsGeometry selectGeomTrans( *selectGeometry );

	//if ( canvas->mapSettings().hasCrsTransformEnabled() )
	//{
	//	try
	//	{
	//		QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs() );
	//		selectGeomTrans.transform( ct );
	//	}
	//	catch ( QgsCsException &cse )
	//	{
	//		Q_UNUSED( cse );
	//		// catch exception for 'invalid' point and leave existing selection unchanged
	//		QgsLogger::warning( "Caught CRS exception " + QString( __FILE__ ) + ": " + QString::number( __LINE__ ) );
	//		LOG_INFO( "CRS Exception\nSelection extends beyond layer's coordinate system" );
	//		return;
	//	}
	//}
	QgsGeometry selectGeomTrans;
	try{
		selectGeomTrans = toLayerCoordinates( canvas, selectGeometry, vlayer );
	}
	catch ( QgsCsException & )
	{
		return;
	}

	QApplication::setOverrideCursor( Qt::WaitCursor );

	QgsDebugMsg( "Selection layer: " + vlayer->name() );
	QgsDebugMsg( "Selection polygon: " + selectGeomTrans.exportToWkt() );
	QgsDebugMsg( "doContains: " + QString( doContains ? "T" : "F" ) );
	QgsDebugMsg( "doDifference: " + QString( doDifference ? "T" : "F" ) );

	QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() );
	QgsFeatureRendererV2* r = vlayer->rendererV2();
	if ( r )
		r->startRender( context, vlayer->pendingFields() );

	QgsFeatureRequest request;
	request.setFilterRect( selectGeomTrans.boundingBox() );
	request.setFlags( QgsFeatureRequest::ExactIntersect );
	if ( r )
		request.setSubsetOfAttributes( r->usedAttributes(), vlayer->pendingFields() );
	else
		request.setSubsetOfAttributes( QgsAttributeList() );

	QgsFeatureIterator fit = vlayer->getFeatures( request );

	QgsFeatureIds newSelectedFeatures;
	QgsFeature f;
	QgsFeatureId closestFeatureId = 0;
	bool foundSingleFeature = false;
	double closestFeatureDist = std::numeric_limits<double>::max();
	while ( fit.nextFeature( f ) )
	{
#if (VERSION_INT >= 21601)
		context.expressionContext().setFeature( f );		//taken from QGIS 2.16.1
		// make sure to only use features that are visible
		if ( r && !r->willRenderFeature( f, context ) )
#else
		if ( r && !r->willRenderFeature( f ) )
#endif
			continue;

		QgsGeometry* g = f.geometry();
		if ( doContains )
		{
			if ( !selectGeomTrans.contains( g ) )
				continue;
		}
		else
		{
			if ( !selectGeomTrans.intersects( g ) )
				continue;
		}
		if ( singleSelect )
		{
			foundSingleFeature = true;
			double distance = g->distance( selectGeomTrans );
			if ( distance <= closestFeatureDist )
			{
				closestFeatureDist = distance;
				closestFeatureId = f.id();
			}
		}
		else
		{
			newSelectedFeatures.insert( f.id() );
		}
	}
	if ( singleSelect && foundSingleFeature )
	{
		newSelectedFeatures.insert( closestFeatureId );
	}

	if ( r )
		r->stopRender( context );

	QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) );

	if ( doDifference )
	{
		QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds();

		QgsFeatureIds selectedFeatures;
		QgsFeatureIds deselectedFeatures;

		QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd();
		while ( i != newSelectedFeatures.constBegin() )
		{
			--i;
			if ( layerSelectedFeatures.contains( *i ) )
			{
				deselectedFeatures.insert( *i );
			}
			else
			{
				selectedFeatures.insert( *i );
			}
		}

		vlayer->modifySelection( selectedFeatures, deselectedFeatures );
	}
	else
	{
		SelectFeatures( vlayer, newSelectedFeatures );		//		vlayer->setSelectedFeatures( newSelectedFeatures );
	}

	QApplication::restoreOverrideCursor();
}
Ejemplo n.º 21
0
bool QgsHeatmapRenderer::renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer, bool selected, bool drawVertexMarker )
{
  Q_UNUSED( layer );
  Q_UNUSED( selected );
  Q_UNUSED( drawVertexMarker );

  if ( !context.painter() )
  {
    return false;
  }

  if ( !feature.hasGeometry() || feature.geometry().type() != QgsWkbTypes::PointGeometry )
  {
    //can only render point type
    return false;
  }

  double weight = 1.0;
  if ( !mWeightExpressionString.isEmpty() )
  {
    QVariant value;
    if ( mWeightAttrNum == -1 )
    {
      Q_ASSERT( mWeightExpression.data() );
      value = mWeightExpression->evaluate( &context.expressionContext() );
    }
    else
    {
      QgsAttributes attrs = feature.attributes();
      value = attrs.value( mWeightAttrNum );
    }
    bool ok = false;
    double evalWeight = value.toDouble( &ok );
    if ( ok )
    {
      weight = evalWeight;
    }
  }

  int width = context.painter()->device()->width() / mRenderQuality;
  int height = context.painter()->device()->height() / mRenderQuality;

  //transform geometry if required
  QgsGeometry geom = feature.geometry();
  QgsCoordinateTransform xform = context.coordinateTransform();
  if ( xform.isValid() )
  {
    geom.transform( xform );
  }

  //convert point to multipoint
  QgsMultiPoint multiPoint = convertToMultipoint( &geom );

  //loop through all points in multipoint
  for ( QgsMultiPoint::const_iterator pointIt = multiPoint.constBegin(); pointIt != multiPoint.constEnd(); ++pointIt )
  {
    QgsPoint pixel = context.mapToPixel().transform( *pointIt );
    int pointX = pixel.x() / mRenderQuality;
    int pointY = pixel.y() / mRenderQuality;
    for ( int x = qMax( pointX - mRadiusPixels, 0 ); x < qMin( pointX + mRadiusPixels, width ); ++x )
    {
      for ( int y = qMax( pointY - mRadiusPixels, 0 ); y < qMin( pointY + mRadiusPixels, height ); ++y )
      {
        int index = y * width + x;
        if ( index >= mValues.count() )
        {
          continue;
        }
        double distanceSquared = pow( pointX - x, 2.0 ) + pow( pointY - y, 2.0 );
        if ( distanceSquared > mRadiusSquared )
        {
          continue;
        }

        double score = weight * quarticKernel( sqrt( distanceSquared ), mRadiusPixels );
        double value = mValues.at( index ) + score;
        if ( value > mCalculatedMaxValue )
        {
          mCalculatedMaxValue = value;
        }
        mValues[ index ] = value;
      }
    }
  }

  mFeaturesRendered++;
#if 0
  //TODO - enable progressive rendering
  if ( mFeaturesRendered % 200  == 0 )
  {
    renderImage( context );
  }
#endif
  return true;
}
Ejemplo n.º 22
0
QgsFeatureIds QgsMapToolSelectUtils::getMatchingFeatures( QgsMapCanvas *canvas, const QgsGeometry &selectGeometry, bool doContains, bool singleSelect )
{
  QgsFeatureIds newSelectedFeatures;

  if ( selectGeometry.type() != QgsWkbTypes::PolygonGeometry )
    return newSelectedFeatures;

  QgsVectorLayer *vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas );
  if ( !vlayer )
    return newSelectedFeatures;

  // toLayerCoordinates will throw an exception for any 'invalid' points in
  // the rubber band.
  // For example, if you project a world map onto a globe using EPSG 2163
  // and then click somewhere off the globe, an exception will be thrown.
  QgsGeometry selectGeomTrans = selectGeometry;

  try
  {
    QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs(), QgsProject::instance() );

    if ( !ct.isShortCircuited() && selectGeomTrans.type() == QgsWkbTypes::PolygonGeometry )
    {
      // convert add more points to the edges of the rectangle
      // improve transformation result
      QgsPolygonXY poly( selectGeomTrans.asPolygon() );
      if ( poly.size() == 1 && poly.at( 0 ).size() == 5 )
      {
        const QgsPolylineXY &ringIn = poly.at( 0 );

        QgsPolygonXY newpoly( 1 );
        newpoly[0].resize( 41 );
        QgsPolylineXY &ringOut = newpoly[0];

        ringOut[ 0 ] = ringIn.at( 0 );

        int i = 1;
        for ( int j = 1; j < 5; j++ )
        {
          QgsVector v( ( ringIn.at( j ) - ringIn.at( j - 1 ) ) / 10.0 );
          for ( int k = 0; k < 9; k++ )
          {
            ringOut[ i ] = ringOut[ i - 1 ] + v;
            i++;
          }
          ringOut[ i++ ] = ringIn.at( j );
        }
        selectGeomTrans = QgsGeometry::fromPolygonXY( newpoly );
      }
    }

    selectGeomTrans.transform( ct );
  }
  catch ( QgsCsException &cse )
  {
    Q_UNUSED( cse );
    // catch exception for 'invalid' point and leave existing selection unchanged
    QgsDebugMsg( QStringLiteral( "Caught CRS exception " ) );
    QgisApp::instance()->messageBar()->pushMessage(
      QObject::tr( "CRS Exception" ),
      QObject::tr( "Selection extends beyond layer's coordinate system" ),
      Qgis::Warning,
      QgisApp::instance()->messageTimeout() );
    return newSelectedFeatures;
  }

  QgsDebugMsgLevel( "Selection layer: " + vlayer->name(), 3 );
  QgsDebugMsgLevel( "Selection polygon: " + selectGeomTrans.asWkt(), 3 );
  QgsDebugMsgLevel( "doContains: " + QString( doContains ? "T" : "F" ), 3 );

  QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() );
  context.expressionContext() << QgsExpressionContextUtils::layerScope( vlayer );
  std::unique_ptr< QgsFeatureRenderer > r;
  if ( vlayer->renderer() )
  {
    r.reset( vlayer->renderer()->clone() );
    r->startRender( context, vlayer->fields() );
  }

  QgsFeatureRequest request;
  request.setFilterRect( selectGeomTrans.boundingBox() );
  request.setFlags( QgsFeatureRequest::ExactIntersect );
  if ( r )
    request.setSubsetOfAttributes( r->usedAttributes( context ), vlayer->fields() );
  else
    request.setNoAttributes();

  QgsFeatureIterator fit = vlayer->getFeatures( request );

  QgsFeature f;
  QgsFeatureId closestFeatureId = 0;
  bool foundSingleFeature = false;
  double closestFeatureDist = std::numeric_limits<double>::max();
  while ( fit.nextFeature( f ) )
  {
    context.expressionContext().setFeature( f );
    // make sure to only use features that are visible
    if ( r && !r->willRenderFeature( f, context ) )
      continue;

    QgsGeometry g = f.geometry();
    if ( doContains )
    {
      if ( !selectGeomTrans.contains( g ) )
        continue;
    }
    else
    {
      if ( !selectGeomTrans.intersects( g ) )
        continue;
    }
    if ( singleSelect )
    {
      foundSingleFeature = true;
      double distance = g.distance( selectGeomTrans );
      if ( distance <= closestFeatureDist )
      {
        closestFeatureDist = distance;
        closestFeatureId = f.id();
      }
    }
    else
    {
      newSelectedFeatures.insert( f.id() );
    }
  }
  if ( singleSelect && foundSingleFeature )
  {
    newSelectedFeatures.insert( closestFeatureId );
  }

  if ( r )
    r->stopRender( context );

  QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) );

  return newSelectedFeatures;
}
Ejemplo n.º 23
0
void QgsTextDiagram::renderDiagram( const QgsFeature& feature, QgsRenderContext& c, const QgsDiagramSettings& s, QPointF position )
{
  QPainter* p = c.painter();
  if ( !p )
  {
    return;
  }

  //convert from mm / map units to painter units
  QSizeF spu = sizePainterUnits( s.size, s, c );
  double w = spu.width();
  double h = spu.height();

  double baseX = position.x();
  double baseY = position.y() - h;

  QVector<QPointF> textPositions; //midpoints for text placement
  int nCategories = s.categoryAttributes.size();
  for ( int i = 0; i < nCategories; ++i )
  {
    if ( mOrientation == Horizontal )
    {
      textPositions.push_back( QPointF( baseX + ( w / nCategories ) * i + w / nCategories / 2.0, baseY + h / 2.0 ) );
    }
    else //vertical
    {
      textPositions.push_back( QPointF( baseX + w / 2.0, baseY + h / nCategories * i + w / nCategories / 2.0 ) );
    }
  }

  mPen.setColor( s.penColor );
  setPenWidth( mPen, s, c );
  p->setPen( mPen );
  mBrush.setColor( s.backgroundColor );
  p->setBrush( mBrush );

  //draw shapes and separator lines first
  if ( mShape == Circle )
  {
    p->drawEllipse( baseX, baseY, w, h );

    //draw separator lines
    QList<QPointF> intersect; //intersections between shape and separation lines
    QPointF center( baseX + w / 2.0, baseY + h / 2.0 );
    double r1 = w / 2.0;
    double r2 = h / 2.0;

    for ( int i = 1; i < nCategories; ++i )
    {
      if ( mOrientation == Horizontal )
      {
        lineEllipseIntersection( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ), center, r1, r2, intersect );
      }
      else //vertical
      {
        lineEllipseIntersection( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ), center, r1, r2, intersect );
      }
      if ( intersect.size() > 1 )
      {
        p->drawLine( intersect.at( 0 ), intersect.at( 1 ) );
      }
    }
  }
  else if ( mShape == Rectangle )
  {
    p->drawRect( QRectF( baseX, baseY, w, h ) );
    for ( int i = 1; i < nCategories; ++i )
    {
      if ( mOrientation == Horizontal )
      {
        p->drawLine( QPointF( baseX + w / nCategories * i, baseY ), QPointF( baseX + w / nCategories * i, baseY + h ) );
      }
      else
      {
        p->drawLine( QPointF( baseX, baseY + h / nCategories * i ), QPointF( baseX + w, baseY + h / nCategories * i ) );
      }
    }
  }
  else //triangle
  {
    QPolygonF triangle;
    triangle << QPointF( baseX, baseY + h ) << QPointF( baseX + w, baseY + h ) << QPointF( baseX + w / 2.0, baseY );
    p->drawPolygon( triangle );

    QLineF triangleEdgeLeft( baseX + w / 2.0, baseY, baseX, baseY + h );
    QLineF triangleEdgeRight( baseX + w, baseY + h, baseX + w / 2.0, baseY );
    QPointF intersectionPoint1, intersectionPoint2;

    for ( int i = 1; i < nCategories; ++i )
    {
      if ( mOrientation == Horizontal )
      {
        QLineF verticalLine( baseX + w / nCategories * i, baseY + h, baseX + w / nCategories * i, baseY );
        if ( baseX + w / nCategories * i < baseX + w / 2.0 )
        {
          verticalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
        }
        else
        {
          verticalLine.intersect( triangleEdgeRight, &intersectionPoint1 );
        }
        p->drawLine( QPointF( baseX + w / nCategories * i, baseY + h ), intersectionPoint1 );
      }
      else //vertical
      {
        QLineF horizontalLine( baseX, baseY + h / nCategories * i, baseX + w, baseY + h / nCategories * i );
        horizontalLine.intersect( triangleEdgeLeft, &intersectionPoint1 );
        horizontalLine.intersect( triangleEdgeRight, &intersectionPoint2 );
        p->drawLine( intersectionPoint1, intersectionPoint2 );
      }
    }
  }

  //draw text
  QFont sFont = scaledFont( s, c );
  QFontMetricsF fontMetrics( sFont );
  p->setFont( sFont );

  QgsExpressionContext expressionContext = c.expressionContext();
  expressionContext.setFeature( feature );
  if ( feature.fields() )
    expressionContext.setFields( *feature.fields() );

  for ( int i = 0; i < textPositions.size(); ++i )
  {
    QgsExpression* expression = getExpression( s.categoryAttributes.at( i ), expressionContext );
    QString val = expression->evaluate( &expressionContext ).toString();

    //find out dimesions
    double textWidth = fontMetrics.width( val );
    double textHeight = fontMetrics.height();

    mPen.setColor( s.categoryColors.at( i ) );
    p->setPen( mPen );
    QPointF position = textPositions.at( i );

    // Calculate vertical placement
    double xOffset = 0;

    switch ( s.labelPlacementMethod )
    {
      case QgsDiagramSettings::Height:
        xOffset = textHeight / 2.0;
        break;

      case QgsDiagramSettings::XHeight:
        xOffset = fontMetrics.xHeight();
        break;
    }
    p->drawText( QPointF( position.x() - textWidth / 2.0, position.y() + xOffset ), val );
  }
}
Ejemplo n.º 24
0
QSet<QString> QgsSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
{
  QSet<QString> columns = mDataDefinedProperties.referencedFields( context.expressionContext() );
  return columns;
}
bool QgsVectorLayerDiagramProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames )
{
  QgsDiagramLayerSettings& s2 = mSettings;
  const QgsMapSettings& mapSettings = mEngine->mapSettings();

  s2.ct = nullptr;
  if ( mapSettings.hasCrsTransformEnabled() )
  {
    if ( context.coordinateTransform() )
      // this is context for layer rendering - use its CT as it includes correct datum transform
      s2.ct = context.coordinateTransform()->clone();
    else
      // otherwise fall back to creating our own CT - this one may not have the correct datum transform!
      s2.ct = new QgsCoordinateTransform( mLayerCrs, mapSettings.destinationCrs() );
  }

  s2.xform = &mapSettings.mapToPixel();

  s2.fields = mFields;

  s2.renderer = mDiagRenderer;

  const QgsDiagramRendererV2* diagRenderer = s2.renderer;

  //add attributes needed by the diagram renderer
  QList<QString> att = diagRenderer->diagramAttributes();
  QList<QString>::const_iterator attIt = att.constBegin();
  for ( ; attIt != att.constEnd(); ++attIt )
  {
    QgsExpression* expression = diagRenderer->diagram()->getExpression( *attIt, context.expressionContext() );
    QStringList columns = expression->referencedColumns();
    QStringList::const_iterator columnsIterator = columns.constBegin();
    for ( ; columnsIterator != columns.constEnd(); ++columnsIterator )
    {
      if ( !attributeNames.contains( *columnsIterator ) )
        attributeNames << *columnsIterator;
    }
  }

  const QgsLinearlyInterpolatedDiagramRenderer* linearlyInterpolatedDiagramRenderer = dynamic_cast<const QgsLinearlyInterpolatedDiagramRenderer*>( diagRenderer );
  if ( linearlyInterpolatedDiagramRenderer )
  {
    if ( linearlyInterpolatedDiagramRenderer->classificationAttributeIsExpression() )
    {
      QgsExpression* expression = diagRenderer->diagram()->getExpression( linearlyInterpolatedDiagramRenderer->classificationAttributeExpression(), context.expressionContext() );
      QStringList columns = expression->referencedColumns();
      QStringList::const_iterator columnsIterator = columns.constBegin();
      for ( ; columnsIterator != columns.constEnd(); ++columnsIterator )
      {
        if ( !attributeNames.contains( *columnsIterator ) )
          attributeNames << *columnsIterator;
      }
    }
    else
    {
      QString name = mFields.at( linearlyInterpolatedDiagramRenderer->classificationAttribute() ).name();
      if ( !attributeNames.contains( name ) )
        attributeNames << name;
    }
  }

  //and the ones needed for data defined diagram positions
  if ( mSettings.xPosColumn != -1 )
    attributeNames << mFields.at( mSettings.xPosColumn ).name();
  if ( mSettings.yPosColumn != -1 )
    attributeNames << mFields.at( mSettings.yPosColumn ).name();

  return true;
}
void QgsDecorationCopyright::render( const QgsMapSettings &mapSettings, QgsRenderContext &context )
{
  Q_UNUSED( mapSettings );
  if ( !enabled() )
    return;

  context.painter()->save();
  context.painter()->setRenderHint( QPainter::Antialiasing, true );

  QString displayString = QgsExpression::replaceExpressionText( mLabelText, &context.expressionContext() );
  QStringList displayStringList = displayString.split( QStringLiteral( "\n" ) );

  QFontMetricsF fm( mTextFormat.scaledFont( context ) );
  double textWidth = QgsTextRenderer::textWidth( context, mTextFormat, displayStringList, &fm );
  double textHeight = QgsTextRenderer::textHeight( context, mTextFormat, displayStringList, QgsTextRenderer::Point, &fm );

  QPaintDevice *device = context.painter()->device();
  int deviceHeight = device->height() / device->devicePixelRatioF();
  int deviceWidth = device->width() / device->devicePixelRatioF();

  float xOffset( 0 ), yOffset( 0 );

  // Set  margin according to selected units
  switch ( mMarginUnit )
  {
    case QgsUnitTypes::RenderMillimeters:
    {
      int pixelsInchX = context.painter()->device()->logicalDpiX();
      int pixelsInchY = context.painter()->device()->logicalDpiY();
      xOffset = pixelsInchX * INCHES_TO_MM * mMarginHorizontal;
      yOffset = pixelsInchY * INCHES_TO_MM * mMarginVertical;
      break;
    }
    case QgsUnitTypes::RenderPixels:
    {
      xOffset = mMarginHorizontal;
      yOffset = mMarginVertical;
      break;
    }
    case QgsUnitTypes::RenderPercentage:
    {
      xOffset = ( ( deviceWidth - textWidth ) / 100. ) * mMarginHorizontal;
      yOffset = ( ( deviceHeight - textHeight ) / 100. ) * mMarginVertical;
      break;
    }
    case QgsUnitTypes::RenderMapUnits:
    case QgsUnitTypes::RenderPoints:
    case QgsUnitTypes::RenderInches:
    case QgsUnitTypes::RenderUnknownUnit:
    case QgsUnitTypes::RenderMetersInMapUnits:
      break;
  }

  // Determine placement of label from form combo box
  QgsTextRenderer::HAlignment horizontalAlignment = QgsTextRenderer::AlignLeft;
  switch ( mPlacement )
  {
    case BottomLeft: // Bottom Left, xOffset is set above
      yOffset = deviceHeight - yOffset;
      break;
    case TopLeft: // Top left, xOffset is set above
      yOffset = yOffset + textHeight;
      break;
    case TopRight: // Top Right
      yOffset = yOffset + textHeight;
      xOffset = deviceWidth - xOffset;
      horizontalAlignment = QgsTextRenderer::AlignRight;
      break;
    case BottomRight: // Bottom Right
      yOffset = deviceHeight - yOffset;
      xOffset = deviceWidth - xOffset;
      horizontalAlignment = QgsTextRenderer::AlignRight;
      break;
    default:
      QgsDebugMsg( QStringLiteral( "Unknown placement index of %1" ).arg( static_cast<int>( mPlacement ) ) );
  }

  //Paint label to canvas
  QgsTextRenderer::drawText( QPointF( xOffset, yOffset ), 0.0, horizontalAlignment, displayStringList, context, mTextFormat );

  context.painter()->restore();
}