LCOV - code coverage report
Current view: top level - core/labeling - qgsvectorlayerlabelprovider.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 375 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :   qgsvectorlayerlabelprovider.cpp
       3                 :            :   --------------------------------------
       4                 :            :   Date                 : September 2015
       5                 :            :   Copyright            : (C) 2015 by Martin Dobias
       6                 :            :   Email                : wonder dot sk at gmail dot com
       7                 :            :  ***************************************************************************
       8                 :            :  *                                                                         *
       9                 :            :  *   This program is free software; you can redistribute it and/or modify  *
      10                 :            :  *   it under the terms of the GNU General Public License as published by  *
      11                 :            :  *   the Free Software Foundation; either version 2 of the License, or     *
      12                 :            :  *   (at your option) any later version.                                   *
      13                 :            :  *                                                                         *
      14                 :            :  ***************************************************************************/
      15                 :            : 
      16                 :            : #include "qgsvectorlayerlabelprovider.h"
      17                 :            : 
      18                 :            : #include "qgsgeometry.h"
      19                 :            : #include "qgslabelsearchtree.h"
      20                 :            : #include "qgspallabeling.h"
      21                 :            : #include "qgstextlabelfeature.h"
      22                 :            : #include "qgsvectorlayer.h"
      23                 :            : #include "qgsvectorlayerfeatureiterator.h"
      24                 :            : #include "qgsrenderer.h"
      25                 :            : #include "qgspolygon.h"
      26                 :            : #include "qgslinestring.h"
      27                 :            : #include "qgsmultipolygon.h"
      28                 :            : #include "qgslogger.h"
      29                 :            : #include "qgsexpressioncontextutils.h"
      30                 :            : #include "qgsmaskidprovider.h"
      31                 :            : #include "qgstextcharacterformat.h"
      32                 :            : #include "qgstextfragment.h"
      33                 :            : #include "qgslabelingresults.h"
      34                 :            : 
      35                 :            : #include "feature.h"
      36                 :            : #include "labelposition.h"
      37                 :            : #include "callouts/qgscallout.h"
      38                 :            : 
      39                 :            : #include "pal/layer.h"
      40                 :            : 
      41                 :            : #include <QPicture>
      42                 :            : #include <QTextDocument>
      43                 :            : #include <QTextFragment>
      44                 :            : 
      45                 :            : using namespace pal;
      46                 :            : 
      47                 :          0 : QgsVectorLayerLabelProvider::QgsVectorLayerLabelProvider( QgsVectorLayer *layer, const QString &providerId, bool withFeatureLoop, const QgsPalLayerSettings *settings, const QString &layerName )
      48                 :          0 :   : QgsAbstractLabelProvider( layer, providerId )
      49                 :          0 :   , mSettings( settings ? * settings : QgsPalLayerSettings() ) // TODO: all providers should have valid settings?
      50                 :          0 :   , mLayerGeometryType( layer->geometryType() )
      51                 :          0 :   , mRenderer( layer->renderer() )
      52                 :          0 :   , mFields( layer->fields() )
      53                 :          0 :   , mCrs( layer->crs() )
      54                 :          0 : {
      55                 :          0 :   mName = layerName.isEmpty() ? layer->id() : layerName;
      56                 :            : 
      57                 :          0 :   if ( withFeatureLoop )
      58                 :            :   {
      59                 :          0 :     mSource = std::make_unique<QgsVectorLayerFeatureSource>( layer );
      60                 :          0 :   }
      61                 :            : 
      62                 :          0 :   init();
      63                 :          0 : }
      64                 :            : 
      65                 :          0 : QgsVectorLayerLabelProvider::QgsVectorLayerLabelProvider( QgsWkbTypes::GeometryType geometryType, const QgsFields &fields, const QgsCoordinateReferenceSystem &crs, const QString &providerId, const QgsPalLayerSettings *settings, QgsMapLayer *layer, const QString &layerName )
      66                 :          0 :   : QgsAbstractLabelProvider( layer, providerId )
      67                 :          0 :   , mSettings( settings ? * settings : QgsPalLayerSettings() ) // TODO: all providers should have valid settings?
      68                 :          0 :   , mLayerGeometryType( geometryType )
      69                 :          0 :   , mRenderer( nullptr )
      70                 :          0 :   , mFields( fields )
      71                 :          0 :   , mCrs( crs )
      72                 :          0 : {
      73                 :          0 :   mName = layerName.isEmpty() ? layer->id() : layerName;
      74                 :            : 
      75                 :          0 :   init();
      76                 :          0 : }
      77                 :            : 
      78                 :          0 : void QgsVectorLayerLabelProvider::init()
      79                 :            : {
      80                 :          0 :   mPlacement = mSettings.placement;
      81                 :          0 :   mFlags = Flags();
      82                 :          0 :   if ( mSettings.drawLabels )
      83                 :          0 :     mFlags |= DrawLabels;
      84                 :          0 :   if ( mSettings.displayAll )
      85                 :          0 :     mFlags |= DrawAllLabels;
      86                 :          0 :   if ( mSettings.lineSettings().mergeLines() && !mSettings.lineSettings().addDirectionSymbol() )
      87                 :          0 :     mFlags |= MergeConnectedLines;
      88                 :          0 :   if ( mSettings.centroidInside )
      89                 :          0 :     mFlags |= CentroidMustBeInside;
      90                 :            : 
      91                 :          0 :   mPriority = 1 - mSettings.priority / 10.0; // convert 0..10 --> 1..0
      92                 :            : 
      93                 :          0 :   if ( mLayerGeometryType == QgsWkbTypes::PointGeometry && mRenderer )
      94                 :            :   {
      95                 :            :     //override obstacle type to treat any intersection of a label with the point symbol as a high cost conflict
      96                 :          0 :     mObstacleType = QgsLabelObstacleSettings::PolygonWhole;
      97                 :          0 :   }
      98                 :            :   else
      99                 :            :   {
     100                 :          0 :     mObstacleType = mSettings.obstacleSettings().type();
     101                 :            :   }
     102                 :            : 
     103                 :          0 :   mUpsidedownLabels = mSettings.upsidedownLabels;
     104                 :          0 : }
     105                 :            : 
     106                 :            : 
     107                 :          0 : QgsVectorLayerLabelProvider::~QgsVectorLayerLabelProvider()
     108                 :          0 : {
     109                 :          0 :   qDeleteAll( mLabels );
     110                 :          0 : }
     111                 :            : 
     112                 :            : 
     113                 :          0 : bool QgsVectorLayerLabelProvider::prepare( QgsRenderContext &context, QSet<QString> &attributeNames )
     114                 :            : {
     115                 :          0 :   const QgsMapSettings &mapSettings = mEngine->mapSettings();
     116                 :            : 
     117                 :          0 :   return mSettings.prepare( context, attributeNames, mFields, mapSettings, mCrs );
     118                 :            : }
     119                 :            : 
     120                 :          0 : void QgsVectorLayerLabelProvider::startRender( QgsRenderContext &context )
     121                 :            : {
     122                 :          0 :   QgsAbstractLabelProvider::startRender( context );
     123                 :          0 :   mSettings.startRender( context );
     124                 :          0 : }
     125                 :            : 
     126                 :          0 : void QgsVectorLayerLabelProvider::stopRender( QgsRenderContext &context )
     127                 :            : {
     128                 :          0 :   QgsAbstractLabelProvider::stopRender( context );
     129                 :          0 :   mSettings.stopRender( context );
     130                 :          0 : }
     131                 :            : 
     132                 :          0 : QList<QgsLabelFeature *> QgsVectorLayerLabelProvider::labelFeatures( QgsRenderContext &ctx )
     133                 :            : {
     134                 :          0 :   if ( !mSource )
     135                 :            :   {
     136                 :            :     // we have created the provider with "own feature loop" == false
     137                 :            :     // so it is assumed that prepare() has been already called followed by registerFeature() calls
     138                 :          0 :     return mLabels;
     139                 :            :   }
     140                 :            : 
     141                 :          0 :   QSet<QString> attrNames;
     142                 :          0 :   if ( !prepare( ctx, attrNames ) )
     143                 :          0 :     return QList<QgsLabelFeature *>();
     144                 :            : 
     145                 :          0 :   if ( mRenderer )
     146                 :          0 :     mRenderer->startRender( ctx, mFields );
     147                 :            : 
     148                 :          0 :   QgsRectangle layerExtent = ctx.extent();
     149                 :          0 :   if ( mSettings.ct.isValid() && !mSettings.ct.isShortCircuited() )
     150                 :          0 :     layerExtent = mSettings.ct.transformBoundingBox( ctx.extent(), QgsCoordinateTransform::ReverseTransform );
     151                 :            : 
     152                 :          0 :   QgsFeatureRequest request;
     153                 :          0 :   request.setFilterRect( layerExtent );
     154                 :          0 :   request.setSubsetOfAttributes( attrNames, mFields );
     155                 :          0 :   QgsFeatureIterator fit = mSource->getFeatures( request );
     156                 :            : 
     157                 :          0 :   QgsExpressionContextScope *symbolScope = new QgsExpressionContextScope();
     158                 :          0 :   ctx.expressionContext().appendScope( symbolScope );
     159                 :          0 :   QgsFeature fet;
     160                 :          0 :   while ( fit.nextFeature( fet ) )
     161                 :            :   {
     162                 :          0 :     QgsGeometry obstacleGeometry;
     163                 :          0 :     const QgsSymbol *symbol = nullptr;
     164                 :          0 :     if ( mRenderer )
     165                 :            :     {
     166                 :          0 :       QgsSymbolList symbols = mRenderer->originalSymbolsForFeature( fet, ctx );
     167                 :          0 :       if ( !symbols.isEmpty() && fet.geometry().type() == QgsWkbTypes::PointGeometry )
     168                 :            :       {
     169                 :            :         //point feature, use symbol bounds as obstacle
     170                 :          0 :         obstacleGeometry = QgsVectorLayerLabelProvider::getPointObstacleGeometry( fet, ctx, symbols );
     171                 :          0 :       }
     172                 :          0 :       if ( !symbols.isEmpty() )
     173                 :            :       {
     174                 :          0 :         symbol = symbols.at( 0 );
     175                 :          0 :         symbolScope = QgsExpressionContextUtils::updateSymbolScope( symbol, symbolScope );
     176                 :          0 :       }
     177                 :          0 :     }
     178                 :          0 :     ctx.expressionContext().setFeature( fet );
     179                 :          0 :     registerFeature( fet, ctx, obstacleGeometry, symbol );
     180                 :          0 :   }
     181                 :            : 
     182                 :          0 :   if ( ctx.expressionContext().lastScope() == symbolScope )
     183                 :          0 :     delete ctx.expressionContext().popScope();
     184                 :            : 
     185                 :          0 :   if ( mRenderer )
     186                 :          0 :     mRenderer->stopRender( ctx );
     187                 :            : 
     188                 :          0 :   return mLabels;
     189                 :          0 : }
     190                 :            : 
     191                 :          0 : void QgsVectorLayerLabelProvider::registerFeature( const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry, const QgsSymbol *symbol )
     192                 :            : {
     193                 :          0 :   QgsLabelFeature *label = nullptr;
     194                 :            : 
     195                 :          0 :   mSettings.registerFeature( feature, context, &label, obstacleGeometry, symbol );
     196                 :          0 :   if ( label )
     197                 :          0 :     mLabels << label;
     198                 :          0 : }
     199                 :            : 
     200                 :          0 : QgsGeometry QgsVectorLayerLabelProvider::getPointObstacleGeometry( QgsFeature &fet, QgsRenderContext &context, const QgsSymbolList &symbols )
     201                 :            : {
     202                 :          0 :   if ( !fet.hasGeometry() || fet.geometry().type() != QgsWkbTypes::PointGeometry )
     203                 :          0 :     return QgsGeometry();
     204                 :            : 
     205                 :          0 :   bool isMultiPoint = fet.geometry().constGet()->nCoordinates() > 1;
     206                 :          0 :   std::unique_ptr< QgsAbstractGeometry > obstacleGeom;
     207                 :          0 :   if ( isMultiPoint )
     208                 :          0 :     obstacleGeom = std::make_unique< QgsMultiPolygon >();
     209                 :            : 
     210                 :            :   // for each point
     211                 :          0 :   for ( int i = 0; i < fet.geometry().constGet()->nCoordinates(); ++i )
     212                 :            :   {
     213                 :          0 :     QRectF bounds;
     214                 :          0 :     QgsPoint p = fet.geometry().constGet()->vertexAt( QgsVertexId( i, 0, 0 ) );
     215                 :          0 :     double x = p.x();
     216                 :          0 :     double y = p.y();
     217                 :          0 :     double z = 0; // dummy variable for coordinate transforms
     218                 :            : 
     219                 :            :     //transform point to pixels
     220                 :          0 :     if ( context.coordinateTransform().isValid() )
     221                 :            :     {
     222                 :            :       try
     223                 :            :       {
     224                 :          0 :         context.coordinateTransform().transformInPlace( x, y, z );
     225                 :          0 :       }
     226                 :            :       catch ( QgsCsException & )
     227                 :            :       {
     228                 :          0 :         return QgsGeometry();
     229                 :          0 :       }
     230                 :          0 :     }
     231                 :          0 :     context.mapToPixel().transformInPlace( x, y );
     232                 :            : 
     233                 :          0 :     QPointF pt( x, y );
     234                 :          0 :     const auto constSymbols = symbols;
     235                 :          0 :     for ( QgsSymbol *symbol : constSymbols )
     236                 :            :     {
     237                 :          0 :       if ( symbol->type() == QgsSymbol::Marker )
     238                 :            :       {
     239                 :          0 :         if ( bounds.isValid() )
     240                 :          0 :           bounds = bounds.united( static_cast< QgsMarkerSymbol * >( symbol )->bounds( pt, context, fet ) );
     241                 :            :         else
     242                 :          0 :           bounds = static_cast< QgsMarkerSymbol * >( symbol )->bounds( pt, context, fet );
     243                 :          0 :       }
     244                 :            :     }
     245                 :            : 
     246                 :            :     //convert bounds to a geometry
     247                 :          0 :     QVector< double > bX;
     248                 :          0 :     bX << bounds.left() << bounds.right() << bounds.right() << bounds.left();
     249                 :          0 :     QVector< double > bY;
     250                 :          0 :     bY << bounds.top() << bounds.top() << bounds.bottom() << bounds.bottom();
     251                 :          0 :     std::unique_ptr< QgsLineString > boundLineString = std::make_unique< QgsLineString >( bX, bY );
     252                 :            : 
     253                 :            :     //then transform back to map units
     254                 :            :     //TODO - remove when labeling is refactored to use screen units
     255                 :          0 :     for ( int i = 0; i < boundLineString->numPoints(); ++i )
     256                 :            :     {
     257                 :          0 :       QgsPointXY point = context.mapToPixel().toMapCoordinates( static_cast<int>( boundLineString->xAt( i ) ),
     258                 :          0 :                          static_cast<int>( boundLineString->yAt( i ) ) );
     259                 :          0 :       boundLineString->setXAt( i, point.x() );
     260                 :          0 :       boundLineString->setYAt( i, point.y() );
     261                 :          0 :     }
     262                 :          0 :     if ( context.coordinateTransform().isValid() )
     263                 :            :     {
     264                 :            :       try
     265                 :            :       {
     266                 :          0 :         boundLineString->transform( context.coordinateTransform(), QgsCoordinateTransform::ReverseTransform );
     267                 :          0 :       }
     268                 :            :       catch ( QgsCsException & )
     269                 :            :       {
     270                 :          0 :         return QgsGeometry();
     271                 :          0 :       }
     272                 :          0 :     }
     273                 :          0 :     boundLineString->close();
     274                 :            : 
     275                 :          0 :     if ( context.coordinateTransform().isValid() )
     276                 :            :     {
     277                 :            :       // coordinate transforms may have resulted in nan coordinates - if so, strip these out
     278                 :          0 :       boundLineString->filterVertices( []( const QgsPoint & point )->bool
     279                 :            :       {
     280                 :          0 :         return std::isfinite( point.x() ) && std::isfinite( point.y() );
     281                 :            :       } );
     282                 :          0 :       if ( !boundLineString->isRing() )
     283                 :          0 :         return QgsGeometry();
     284                 :          0 :     }
     285                 :            : 
     286                 :          0 :     std::unique_ptr< QgsPolygon > obstaclePolygon = std::make_unique< QgsPolygon >();
     287                 :          0 :     obstaclePolygon->setExteriorRing( boundLineString.release() );
     288                 :            : 
     289                 :          0 :     if ( isMultiPoint )
     290                 :            :     {
     291                 :          0 :       static_cast<QgsMultiPolygon *>( obstacleGeom.get() )->addGeometry( obstaclePolygon.release() );
     292                 :          0 :     }
     293                 :            :     else
     294                 :            :     {
     295                 :          0 :       obstacleGeom = std::move( obstaclePolygon );
     296                 :            :     }
     297                 :          0 :   }
     298                 :            : 
     299                 :          0 :   return QgsGeometry( std::move( obstacleGeom ) );
     300                 :          0 : }
     301                 :            : 
     302                 :          0 : void QgsVectorLayerLabelProvider::drawLabelBackground( QgsRenderContext &context, LabelPosition *label ) const
     303                 :            : {
     304                 :          0 :   if ( !mSettings.drawLabels )
     305                 :          0 :     return;
     306                 :            : 
     307                 :            :   // render callout
     308                 :          0 :   if ( mSettings.callout() && mSettings.callout()->drawOrder() == QgsCallout::OrderBelowAllLabels )
     309                 :            :   {
     310                 :          0 :     drawCallout( context, label );
     311                 :          0 :   }
     312                 :          0 : }
     313                 :            : 
     314                 :          0 : void QgsVectorLayerLabelProvider::drawCallout( QgsRenderContext &context, pal::LabelPosition *label ) const
     315                 :            : {
     316                 :          0 :   bool enabled = mSettings.callout()->enabled();
     317                 :          0 :   if ( mSettings.dataDefinedProperties().isActive( QgsPalLayerSettings::CalloutDraw ) )
     318                 :            :   {
     319                 :          0 :     context.expressionContext().setOriginalValueVariable( enabled );
     320                 :          0 :     enabled = mSettings.dataDefinedProperties().valueAsBool( QgsPalLayerSettings::CalloutDraw, context.expressionContext(), enabled );
     321                 :          0 :   }
     322                 :          0 :   if ( enabled )
     323                 :            :   {
     324                 :          0 :     QgsMapToPixel xform = context.mapToPixel();
     325                 :          0 :     xform.setMapRotation( 0, 0, 0 );
     326                 :          0 :     QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF();
     327                 :          0 :     QgsPointXY outPt2 = xform.transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
     328                 :          0 :     QRectF rect( outPt.x(), outPt.y(), outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
     329                 :            : 
     330                 :          0 :     QgsGeometry g( QgsGeos::fromGeos( label->getFeaturePart()->feature()->geometry() ) );
     331                 :          0 :     g.transform( xform.transform() );
     332                 :          0 :     QgsCallout::QgsCalloutContext calloutContext;
     333                 :          0 :     calloutContext.allFeaturePartsLabeled = label->getFeaturePart()->feature()->labelAllParts();
     334                 :          0 :     calloutContext.originalFeatureCrs = label->getFeaturePart()->feature()->originalFeatureCrs();
     335                 :          0 :     mSettings.callout()->render( context, rect, label->getAlpha() * 180 / M_PI, g, calloutContext );
     336                 :            : 
     337                 :          0 :     const QList< QgsCalloutPosition > renderedPositions = calloutContext.positions();
     338                 :            : 
     339                 :          0 :     for ( QgsCalloutPosition position : renderedPositions )
     340                 :            :     {
     341                 :          0 :       position.layerID = mLayerId;
     342                 :          0 :       position.featureId = label->getFeaturePart()->featureId();
     343                 :          0 :       position.providerID = mProviderId;
     344                 :          0 :       mEngine->results()->mLabelSearchTree->insertCallout( position );
     345                 :          0 :     }
     346                 :          0 :   }
     347                 :          0 : }
     348                 :            : 
     349                 :          0 : void QgsVectorLayerLabelProvider::drawLabel( QgsRenderContext &context, pal::LabelPosition *label ) const
     350                 :            : {
     351                 :          0 :   if ( !mSettings.drawLabels )
     352                 :          0 :     return;
     353                 :            : 
     354                 :          0 :   QgsTextLabelFeature *lf = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
     355                 :            : 
     356                 :            :   // Copy to temp, editable layer settings
     357                 :            :   // these settings will be changed by any data defined values, then used for rendering label components
     358                 :            :   // settings may be adjusted during rendering of components
     359                 :          0 :   QgsPalLayerSettings tmpLyr( mSettings );
     360                 :            : 
     361                 :            :   // apply any previously applied data defined settings for the label
     362                 :          0 :   const QMap< QgsPalLayerSettings::Property, QVariant > &ddValues = lf->dataDefinedValues();
     363                 :            : 
     364                 :            :   //font
     365                 :          0 :   QFont dFont = lf->definedFont();
     366                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "PAL font tmpLyr: %1, Style: %2" ).arg( tmpLyr.format().font().toString(), tmpLyr.format().font().styleName() ), 4 );
     367                 :          0 :   QgsDebugMsgLevel( QStringLiteral( "PAL font definedFont: %1, Style: %2" ).arg( dFont.toString(), dFont.styleName() ), 4 );
     368                 :            : 
     369                 :          0 :   QgsTextFormat format = tmpLyr.format();
     370                 :          0 :   format.setFont( dFont );
     371                 :            : 
     372                 :            :   // size has already been calculated and stored in the defined font - this calculated size
     373                 :            :   // is in pixels
     374                 :          0 :   format.setSize( dFont.pixelSize() );
     375                 :          0 :   format.setSizeUnit( QgsUnitTypes::RenderPixels );
     376                 :          0 :   tmpLyr.setFormat( format );
     377                 :            : 
     378                 :          0 :   if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiFollowPlacement )
     379                 :            :   {
     380                 :            :     //calculate font alignment based on label quadrant
     381                 :          0 :     switch ( label->getQuadrant() )
     382                 :            :     {
     383                 :            :       case LabelPosition::QuadrantAboveLeft:
     384                 :            :       case LabelPosition::QuadrantLeft:
     385                 :            :       case LabelPosition::QuadrantBelowLeft:
     386                 :          0 :         tmpLyr.multilineAlign = QgsPalLayerSettings::MultiRight;
     387                 :          0 :         break;
     388                 :            :       case LabelPosition::QuadrantAbove:
     389                 :            :       case LabelPosition::QuadrantOver:
     390                 :            :       case LabelPosition::QuadrantBelow:
     391                 :          0 :         tmpLyr.multilineAlign = QgsPalLayerSettings::MultiCenter;
     392                 :          0 :         break;
     393                 :            :       case LabelPosition::QuadrantAboveRight:
     394                 :            :       case LabelPosition::QuadrantRight:
     395                 :            :       case LabelPosition::QuadrantBelowRight:
     396                 :          0 :         tmpLyr.multilineAlign = QgsPalLayerSettings::MultiLeft;
     397                 :          0 :         break;
     398                 :            :     }
     399                 :          0 :   }
     400                 :            : 
     401                 :            :   // update tmpLyr with any data defined text style values
     402                 :          0 :   QgsPalLabeling::dataDefinedTextStyle( tmpLyr, ddValues );
     403                 :            : 
     404                 :            :   // update tmpLyr with any data defined text buffer values
     405                 :          0 :   QgsPalLabeling::dataDefinedTextBuffer( tmpLyr, ddValues );
     406                 :            : 
     407                 :            :   // update tmpLyr with any data defined text mask values
     408                 :          0 :   QgsPalLabeling::dataDefinedTextMask( tmpLyr, ddValues );
     409                 :            : 
     410                 :            :   // update tmpLyr with any data defined text formatting values
     411                 :          0 :   QgsPalLabeling::dataDefinedTextFormatting( tmpLyr, ddValues );
     412                 :            : 
     413                 :            :   // update tmpLyr with any data defined shape background values
     414                 :          0 :   QgsPalLabeling::dataDefinedShapeBackground( tmpLyr, ddValues );
     415                 :            : 
     416                 :            :   // update tmpLyr with any data defined drop shadow values
     417                 :          0 :   QgsPalLabeling::dataDefinedDropShadow( tmpLyr, ddValues );
     418                 :            : 
     419                 :            :   // Render the components of a label in reverse order
     420                 :            :   //   (backgrounds -> text)
     421                 :            : 
     422                 :            :   // render callout
     423                 :          0 :   if ( mSettings.callout() && mSettings.callout()->drawOrder() == QgsCallout::OrderBelowIndividualLabels )
     424                 :            :   {
     425                 :          0 :     drawCallout( context, label );
     426                 :          0 :   }
     427                 :            : 
     428                 :          0 :   if ( tmpLyr.format().shadow().enabled() && tmpLyr.format().shadow().shadowPlacement() == QgsTextShadowSettings::ShadowLowest )
     429                 :            :   {
     430                 :          0 :     QgsTextFormat format = tmpLyr.format();
     431                 :            : 
     432                 :          0 :     if ( tmpLyr.format().background().enabled() && tmpLyr.format().background().type() != QgsTextBackgroundSettings::ShapeMarkerSymbol ) // background shadows not compatible with marker symbol backgrounds
     433                 :            :     {
     434                 :          0 :       format.shadow().setShadowPlacement( QgsTextShadowSettings::ShadowShape );
     435                 :          0 :     }
     436                 :          0 :     else if ( tmpLyr.format().buffer().enabled() )
     437                 :            :     {
     438                 :          0 :       format.shadow().setShadowPlacement( QgsTextShadowSettings::ShadowBuffer );
     439                 :          0 :     }
     440                 :            :     else
     441                 :            :     {
     442                 :          0 :       format.shadow().setShadowPlacement( QgsTextShadowSettings::ShadowText );
     443                 :            :     }
     444                 :            : 
     445                 :          0 :     tmpLyr.setFormat( format );
     446                 :          0 :   }
     447                 :            : 
     448                 :          0 :   if ( tmpLyr.format().background().enabled() )
     449                 :            :   {
     450                 :          0 :     drawLabelPrivate( label, context, tmpLyr, QgsTextRenderer::Background );
     451                 :          0 :   }
     452                 :            : 
     453                 :          0 :   if ( tmpLyr.format().buffer().enabled() )
     454                 :            :   {
     455                 :          0 :     drawLabelPrivate( label, context, tmpLyr, QgsTextRenderer::Buffer );
     456                 :          0 :   }
     457                 :            : 
     458                 :          0 :   drawLabelPrivate( label, context, tmpLyr, QgsTextRenderer::Text );
     459                 :            : 
     460                 :            :   // add to the results
     461                 :          0 :   QString labeltext = label->getFeaturePart()->feature()->labelText();
     462                 :          0 :   mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, labeltext, dFont, false, lf->hasFixedPosition(), mProviderId );
     463                 :          0 : }
     464                 :            : 
     465                 :          0 : void QgsVectorLayerLabelProvider::drawUnplacedLabel( QgsRenderContext &context, LabelPosition *label ) const
     466                 :            : {
     467                 :          0 :   if ( !mSettings.drawLabels )
     468                 :          0 :     return;
     469                 :            : 
     470                 :          0 :   QgsTextLabelFeature *lf = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
     471                 :            : 
     472                 :          0 :   QgsPalLayerSettings tmpLyr( mSettings );
     473                 :          0 :   QgsTextFormat format = tmpLyr.format();
     474                 :          0 :   format.setColor( mEngine->engineSettings().unplacedLabelColor() );
     475                 :          0 :   tmpLyr.setFormat( format );
     476                 :          0 :   drawLabelPrivate( label, context, tmpLyr, QgsTextRenderer::Text );
     477                 :            : 
     478                 :            :   // add to the results
     479                 :          0 :   QString labeltext = label->getFeaturePart()->feature()->labelText();
     480                 :          0 :   mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, labeltext, tmpLyr.format().font(), false, lf->hasFixedPosition(), mProviderId, true );
     481                 :          0 : }
     482                 :            : 
     483                 :          0 : void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, QgsRenderContext &context, QgsPalLayerSettings &tmpLyr, QgsTextRenderer::TextPart drawType, double dpiRatio ) const
     484                 :            : {
     485                 :            :   // NOTE: this is repeatedly called for multi-part labels
     486                 :          0 :   QPainter *painter = context.painter();
     487                 :            : 
     488                 :            :   // features are pre-rotated but not scaled/translated,
     489                 :            :   // so we only disable rotation here. Ideally, they'd be
     490                 :            :   // also pre-scaled/translated, as suggested here:
     491                 :            :   // https://github.com/qgis/QGIS/issues/20071
     492                 :          0 :   QgsMapToPixel xform = context.mapToPixel();
     493                 :          0 :   xform.setMapRotation( 0, 0, 0 );
     494                 :            : 
     495                 :          0 :   QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF();
     496                 :            : 
     497                 :          0 :   if ( mEngine->engineSettings().testFlag( QgsLabelingEngineSettings::DrawLabelRectOnly ) )  // TODO: this should get directly to labeling engine
     498                 :            :   {
     499                 :            :     //debugging rect
     500                 :          0 :     if ( drawType != QgsTextRenderer::Text )
     501                 :          0 :       return;
     502                 :            : 
     503                 :          0 :     QgsPointXY outPt2 = xform.transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
     504                 :          0 :     QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
     505                 :          0 :     painter->save();
     506                 :          0 :     painter->setRenderHint( QPainter::Antialiasing, false );
     507                 :          0 :     painter->translate( QPointF( outPt.x(), outPt.y() ) );
     508                 :          0 :     painter->rotate( -label->getAlpha() * 180 / M_PI );
     509                 :            : 
     510                 :          0 :     if ( label->conflictsWithObstacle() )
     511                 :            :     {
     512                 :          0 :       painter->setBrush( QColor( 255, 0, 0, 100 ) );
     513                 :          0 :       painter->setPen( QColor( 255, 0, 0, 150 ) );
     514                 :          0 :     }
     515                 :            :     else
     516                 :            :     {
     517                 :          0 :       painter->setBrush( QColor( 0, 255, 0, 100 ) );
     518                 :          0 :       painter->setPen( QColor( 0, 255, 0, 150 ) );
     519                 :            :     }
     520                 :            : 
     521                 :          0 :     painter->drawRect( rect );
     522                 :          0 :     painter->restore();
     523                 :            : 
     524                 :          0 :     if ( label->nextPart() )
     525                 :          0 :       drawLabelPrivate( label->nextPart(), context, tmpLyr, drawType, dpiRatio );
     526                 :            : 
     527                 :          0 :     return;
     528                 :            :   }
     529                 :            : 
     530                 :          0 :   QgsTextRenderer::Component component;
     531                 :          0 :   component.dpiRatio = dpiRatio;
     532                 :          0 :   component.origin = outPt;
     533                 :          0 :   component.rotation = label->getAlpha();
     534                 :            : 
     535                 :          0 :   if ( drawType == QgsTextRenderer::Background )
     536                 :            :   {
     537                 :            :     // get rotated label's center point
     538                 :          0 :     QPointF centerPt( outPt );
     539                 :          0 :     QgsPointXY outPt2 = xform.transform( label->getX() + label->getWidth() / 2,
     540                 :          0 :                                          label->getY() + label->getHeight() / 2 );
     541                 :            : 
     542                 :          0 :     double xc = outPt2.x() - outPt.x();
     543                 :          0 :     double yc = outPt2.y() - outPt.y();
     544                 :            : 
     545                 :          0 :     double angle = -component.rotation;
     546                 :          0 :     double xd = xc * std::cos( angle ) - yc * std::sin( angle );
     547                 :          0 :     double yd = xc * std::sin( angle ) + yc * std::cos( angle );
     548                 :            : 
     549                 :          0 :     centerPt.setX( centerPt.x() + xd );
     550                 :          0 :     centerPt.setY( centerPt.y() + yd );
     551                 :            : 
     552                 :          0 :     component.center = centerPt;
     553                 :            : 
     554                 :            :     // convert label size to render units
     555                 :          0 :     double labelWidthPx = context.convertToPainterUnits( label->getWidth(), QgsUnitTypes::RenderMapUnits, QgsMapUnitScale() );
     556                 :          0 :     double labelHeightPx = context.convertToPainterUnits( label->getHeight(), QgsUnitTypes::RenderMapUnits, QgsMapUnitScale() );
     557                 :            : 
     558                 :          0 :     component.size = QSizeF( labelWidthPx, labelHeightPx );
     559                 :            : 
     560                 :          0 :     QgsTextRenderer::drawBackground( context, component, tmpLyr.format(), QgsTextDocument(), QgsTextRenderer::Label );
     561                 :          0 :   }
     562                 :            : 
     563                 :          0 :   else if ( drawType == QgsTextRenderer::Buffer
     564                 :          0 :             || drawType == QgsTextRenderer::Text )
     565                 :            :   {
     566                 :            : 
     567                 :            :     // TODO: optimize access :)
     568                 :          0 :     QgsTextLabelFeature *lf = static_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
     569                 :          0 :     QString txt = lf->text( label->getPartId() );
     570                 :          0 :     QFontMetricsF *labelfm = lf->labelFontMetrics();
     571                 :            : 
     572                 :          0 :     if ( auto *lMaskIdProvider = context.maskIdProvider() )
     573                 :            :     {
     574                 :          0 :       int maskId = lMaskIdProvider->maskId( label->getFeaturePart()->layer()->provider()->layerId(),
     575                 :          0 :                                             label->getFeaturePart()->layer()->provider()->providerId() );
     576                 :          0 :       context.setCurrentMaskId( maskId );
     577                 :          0 :     }
     578                 :            : 
     579                 :            :     //add the direction symbol if needed
     580                 :          0 :     if ( !txt.isEmpty() && tmpLyr.placement == QgsPalLayerSettings::Line &&
     581                 :          0 :          tmpLyr.lineSettings().addDirectionSymbol() )
     582                 :            :     {
     583                 :          0 :       bool prependSymb = false;
     584                 :          0 :       QString symb = tmpLyr.lineSettings().rightDirectionSymbol();
     585                 :            : 
     586                 :          0 :       if ( label->getReversed() )
     587                 :            :       {
     588                 :          0 :         prependSymb = true;
     589                 :          0 :         symb = tmpLyr.lineSettings().leftDirectionSymbol();
     590                 :          0 :       }
     591                 :            : 
     592                 :          0 :       if ( tmpLyr.lineSettings().reverseDirectionSymbol() )
     593                 :            :       {
     594                 :          0 :         if ( symb == tmpLyr.lineSettings().rightDirectionSymbol() )
     595                 :            :         {
     596                 :          0 :           prependSymb = true;
     597                 :          0 :           symb = tmpLyr.lineSettings().leftDirectionSymbol();
     598                 :          0 :         }
     599                 :            :         else
     600                 :            :         {
     601                 :          0 :           prependSymb = false;
     602                 :          0 :           symb = tmpLyr.lineSettings().rightDirectionSymbol();
     603                 :            :         }
     604                 :          0 :       }
     605                 :            : 
     606                 :          0 :       switch ( tmpLyr.lineSettings().directionSymbolPlacement() )
     607                 :            :       {
     608                 :            :         case QgsLabelLineSettings::DirectionSymbolPlacement::SymbolAbove:
     609                 :          0 :           prependSymb = true;
     610                 :          0 :           symb = symb + QStringLiteral( "\n" );
     611                 :          0 :           break;
     612                 :            : 
     613                 :            :         case QgsLabelLineSettings::DirectionSymbolPlacement::SymbolBelow:
     614                 :          0 :           prependSymb = false;
     615                 :          0 :           symb = QStringLiteral( "\n" ) + symb;
     616                 :          0 :           break;
     617                 :            : 
     618                 :            :         case QgsLabelLineSettings::DirectionSymbolPlacement::SymbolLeftRight:
     619                 :          0 :           break;
     620                 :            :       }
     621                 :            : 
     622                 :          0 :       if ( prependSymb )
     623                 :            :       {
     624                 :          0 :         txt.prepend( symb );
     625                 :          0 :       }
     626                 :            :       else
     627                 :            :       {
     628                 :          0 :         txt.append( symb );
     629                 :            :       }
     630                 :          0 :     }
     631                 :            : 
     632                 :            : 
     633                 :          0 :     QgsTextRenderer::HAlignment hAlign = QgsTextRenderer::AlignLeft;
     634                 :          0 :     if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiCenter )
     635                 :          0 :       hAlign = QgsTextRenderer::AlignCenter;
     636                 :          0 :     else if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiRight )
     637                 :          0 :       hAlign = QgsTextRenderer::AlignRight;
     638                 :          0 :     else if ( tmpLyr.multilineAlign == QgsPalLayerSettings::MultiJustify )
     639                 :          0 :       hAlign = QgsTextRenderer::AlignJustify;
     640                 :            : 
     641                 :          0 :     QgsTextRenderer::Component component;
     642                 :          0 :     component.origin = outPt;
     643                 :          0 :     component.rotation = label->getAlpha();
     644                 :            : 
     645                 :          0 :     QgsTextDocument document;
     646                 :          0 :     if ( !tmpLyr.format().allowHtmlFormatting() || tmpLyr.placement == QgsPalLayerSettings::Curved )
     647                 :            :     {
     648                 :          0 :       const QgsTextCharacterFormat c = lf->characterFormat( label->getPartId() );
     649                 :          0 :       const QStringList multiLineList = QgsPalLabeling::splitToLines( txt, tmpLyr.wrapChar, tmpLyr.autoWrapLength, tmpLyr.useMaxLineLengthForAutoWrap );
     650                 :          0 :       for ( const QString &line : multiLineList )
     651                 :          0 :         document.append( QgsTextBlock( QgsTextFragment( line, c ) ) );
     652                 :          0 :     }
     653                 :            :     else
     654                 :            :     {
     655                 :          0 :       document = lf->document();
     656                 :            :     }
     657                 :            : 
     658                 :          0 :     QgsTextRenderer::drawTextInternal( drawType, context, tmpLyr.format(), component, document, labelfm,
     659                 :          0 :                                        hAlign, QgsTextRenderer::AlignTop, QgsTextRenderer::Label );
     660                 :            : 
     661                 :          0 :   }
     662                 :          0 :   if ( label->nextPart() )
     663                 :          0 :     drawLabelPrivate( label->nextPart(), context, tmpLyr, drawType, dpiRatio );
     664                 :          0 : }
     665                 :            : 
     666                 :          0 : const QgsPalLayerSettings &QgsVectorLayerLabelProvider::settings() const
     667                 :            : {
     668                 :          0 :   return mSettings;
     669                 :            : }

Generated by: LCOV version 1.14