LCOV - code coverage report
Current view: top level - core/symbology - qgssymbol.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 129 1414 9.1 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :  qgssymbol.cpp
       3                 :            :  ---------------------
       4                 :            :  begin                : November 2009
       5                 :            :  copyright            : (C) 2009 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 <QColor>
      17                 :            : #include <QImage>
      18                 :            : #include <QPainter>
      19                 :            : #include <QSize>
      20                 :            : #include <QSvgGenerator>
      21                 :            : 
      22                 :            : #include <cmath>
      23                 :            : #include <map>
      24                 :            : #include <random>
      25                 :            : 
      26                 :            : #include "qgssymbol.h"
      27                 :            : #include "qgssymbollayer.h"
      28                 :            : 
      29                 :            : #include "qgslinesymbollayer.h"
      30                 :            : #include "qgsmarkersymbollayer.h"
      31                 :            : #include "qgsfillsymbollayer.h"
      32                 :            : #include "qgsgeometrygeneratorsymbollayer.h"
      33                 :            : #include "qgsmaptopixelgeometrysimplifier.h"
      34                 :            : #include "qgslogger.h"
      35                 :            : #include "qgsrendercontext.h" // for bigSymbolPreview
      36                 :            : #include "qgsproject.h"
      37                 :            : #include "qgsstyle.h"
      38                 :            : #include "qgspainteffect.h"
      39                 :            : #include "qgseffectstack.h"
      40                 :            : #include "qgsvectorlayer.h"
      41                 :            : #include "qgsfeature.h"
      42                 :            : #include "qgsgeometry.h"
      43                 :            : #include "qgsmultipoint.h"
      44                 :            : #include "qgsgeometrycollection.h"
      45                 :            : #include "qgslinestring.h"
      46                 :            : #include "qgspolygon.h"
      47                 :            : #include "qgsclipper.h"
      48                 :            : #include "qgsproperty.h"
      49                 :            : #include "qgscolorschemeregistry.h"
      50                 :            : #include "qgsapplication.h"
      51                 :            : #include "qgsexpressioncontextutils.h"
      52                 :            : #include "qgsrenderedfeaturehandlerinterface.h"
      53                 :            : #include "qgslegendpatchshape.h"
      54                 :            : #include "qgsgeos.h"
      55                 :            : 
      56                 :          5 : QgsPropertiesDefinition QgsSymbol::sPropertyDefinitions;
      57                 :            : 
      58                 :            : inline
      59                 :          0 : QgsProperty rotateWholeSymbol( double additionalRotation, const QgsProperty &property )
      60                 :            : {
      61                 :          0 :   QString exprString = property.asExpression();
      62                 :          0 :   return QgsProperty::fromExpression( QString::number( additionalRotation ) + " + (" + exprString + ')' );
      63                 :          0 : }
      64                 :            : 
      65                 :            : inline
      66                 :          0 : QgsProperty scaleWholeSymbol( double scaleFactor, const QgsProperty &property )
      67                 :            : {
      68                 :          0 :   QString exprString = property.asExpression();
      69                 :          0 :   return QgsProperty::fromExpression( QString::number( scaleFactor ) + "*(" + exprString + ')' );
      70                 :          0 : }
      71                 :            : 
      72                 :            : inline
      73                 :          0 : QgsProperty scaleWholeSymbol( double scaleFactorX, double scaleFactorY, const QgsProperty &property )
      74                 :            : {
      75                 :          0 :   QString exprString = property.asExpression();
      76                 :          0 :   return QgsProperty::fromExpression(
      77                 :          0 :            ( !qgsDoubleNear( scaleFactorX, 0.0 ) ? "tostring(" + QString::number( scaleFactorX ) + "*(" + exprString + "))" : QStringLiteral( "'0'" ) ) +
      78                 :          0 :            "|| ',' || " +
      79                 :          0 :            ( !qgsDoubleNear( scaleFactorY, 0.0 ) ? "tostring(" + QString::number( scaleFactorY ) + "*(" + exprString + "))" : QStringLiteral( "'0'" ) ) );
      80                 :          0 : }
      81                 :            : 
      82                 :            : 
      83                 :            : ////////////////////
      84                 :            : 
      85                 :            : Q_NOWARN_DEPRECATED_PUSH // because of deprecated mLayer
      86                 :       1073 : QgsSymbol::QgsSymbol( SymbolType type, const QgsSymbolLayerList &layers )
      87                 :       1073 :   : mType( type )
      88                 :       1073 :   , mLayers( layers )
      89                 :       1073 : {
      90                 :            : 
      91                 :            :   // check they're all correct symbol layers
      92                 :       2093 :   for ( int i = 0; i < mLayers.count(); i++ )
      93                 :            :   {
      94                 :       1020 :     if ( !mLayers.at( i ) )
      95                 :            :     {
      96                 :          0 :       mLayers.removeAt( i-- );
      97                 :          0 :     }
      98                 :       1020 :     else if ( !mLayers.at( i )->isCompatibleWithSymbol( this ) )
      99                 :            :     {
     100                 :          0 :       delete mLayers.at( i );
     101                 :          0 :       mLayers.removeAt( i-- );
     102                 :          0 :     }
     103                 :       1020 :   }
     104                 :       1073 : }
     105                 :            : Q_NOWARN_DEPRECATED_POP
     106                 :            : 
     107                 :          0 : QPolygonF QgsSymbol::_getLineString( QgsRenderContext &context, const QgsCurve &curve, bool clipToExtent )
     108                 :            : {
     109                 :          0 :   const unsigned int nPoints = curve.numPoints();
     110                 :            : 
     111                 :          0 :   QgsCoordinateTransform ct = context.coordinateTransform();
     112                 :          0 :   const QgsMapToPixel &mtp = context.mapToPixel();
     113                 :          0 :   QPolygonF pts;
     114                 :            : 
     115                 :            :   //apply clipping for large lines to achieve a better rendering performance
     116                 :          0 :   if ( clipToExtent && nPoints > 1 && !( context.flags() & QgsRenderContext::ApplyClipAfterReprojection ) )
     117                 :            :   {
     118                 :          0 :     const QgsRectangle e = context.extent();
     119                 :          0 :     const double cw = e.width() / 10;
     120                 :          0 :     const double ch = e.height() / 10;
     121                 :          0 :     const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
     122                 :          0 :     pts = QgsClipper::clippedLine( curve, clipRect );
     123                 :          0 :   }
     124                 :            :   else
     125                 :            :   {
     126                 :          0 :     pts = curve.asQPolygonF();
     127                 :            :   }
     128                 :            : 
     129                 :            :   //transform the QPolygonF to screen coordinates
     130                 :          0 :   if ( ct.isValid() )
     131                 :            :   {
     132                 :            :     try
     133                 :            :     {
     134                 :          0 :       ct.transformPolygon( pts );
     135                 :          0 :     }
     136                 :            :     catch ( QgsCsException & )
     137                 :            :     {
     138                 :            :       // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
     139                 :          0 :     }
     140                 :          0 :   }
     141                 :            : 
     142                 :            :   // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
     143                 :          0 :   pts.erase( std::remove_if( pts.begin(), pts.end(),
     144                 :          0 :                              []( const QPointF point )
     145                 :            :   {
     146                 :          0 :     return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
     147                 :          0 :   } ), pts.end() );
     148                 :            : 
     149                 :          0 :   if ( clipToExtent && nPoints > 1 && context.flags() & QgsRenderContext::ApplyClipAfterReprojection )
     150                 :            :   {
     151                 :            :     // early clipping was not possible, so we have to apply it here after transformation
     152                 :          0 :     const QgsRectangle e = context.mapExtent();
     153                 :          0 :     const double cw = e.width() / 10;
     154                 :          0 :     const double ch = e.height() / 10;
     155                 :          0 :     const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
     156                 :          0 :     pts = QgsClipper::clippedLine( pts, clipRect );
     157                 :          0 :   }
     158                 :            : 
     159                 :          0 :   QPointF *ptr = pts.data();
     160                 :          0 :   for ( int i = 0; i < pts.size(); ++i, ++ptr )
     161                 :            :   {
     162                 :          0 :     mtp.transformInPlace( ptr->rx(), ptr->ry() );
     163                 :          0 :   }
     164                 :            : 
     165                 :          0 :   return pts;
     166                 :          0 : }
     167                 :            : 
     168                 :          0 : QPolygonF QgsSymbol::_getPolygonRing( QgsRenderContext &context, const QgsCurve &curve, const bool clipToExtent, const bool isExteriorRing, const bool correctRingOrientation )
     169                 :            : {
     170                 :          0 :   const QgsCoordinateTransform ct = context.coordinateTransform();
     171                 :          0 :   const QgsMapToPixel &mtp = context.mapToPixel();
     172                 :            : 
     173                 :          0 :   QPolygonF poly = curve.asQPolygonF();
     174                 :            : 
     175                 :          0 :   if ( curve.numPoints() < 1 )
     176                 :          0 :     return QPolygonF();
     177                 :            : 
     178                 :          0 :   if ( correctRingOrientation )
     179                 :            :   {
     180                 :            :     // ensure consistent polygon ring orientation
     181                 :          0 :     if ( isExteriorRing && curve.orientation() != QgsCurve::Clockwise )
     182                 :          0 :       std::reverse( poly.begin(), poly.end() );
     183                 :          0 :     else if ( !isExteriorRing && curve.orientation() != QgsCurve::CounterClockwise )
     184                 :          0 :       std::reverse( poly.begin(), poly.end() );
     185                 :          0 :   }
     186                 :            : 
     187                 :            :   //clip close to view extent, if needed
     188                 :          0 :   if ( clipToExtent && !( context.flags() & QgsRenderContext::ApplyClipAfterReprojection ) && !context.extent().contains( poly.boundingRect() ) )
     189                 :            :   {
     190                 :          0 :     const QgsRectangle e = context.extent();
     191                 :          0 :     const double cw = e.width() / 10;
     192                 :          0 :     const double ch = e.height() / 10;
     193                 :          0 :     const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
     194                 :          0 :     QgsClipper::trimPolygon( poly, clipRect );
     195                 :          0 :   }
     196                 :            : 
     197                 :            :   //transform the QPolygonF to screen coordinates
     198                 :          0 :   if ( ct.isValid() )
     199                 :            :   {
     200                 :            :     try
     201                 :            :     {
     202                 :          0 :       ct.transformPolygon( poly );
     203                 :          0 :     }
     204                 :            :     catch ( QgsCsException & )
     205                 :            :     {
     206                 :            :       // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
     207                 :          0 :     }
     208                 :          0 :   }
     209                 :            : 
     210                 :            :   // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
     211                 :          0 :   poly.erase( std::remove_if( poly.begin(), poly.end(),
     212                 :          0 :                               []( const QPointF point )
     213                 :            :   {
     214                 :          0 :     return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
     215                 :          0 :   } ), poly.end() );
     216                 :            : 
     217                 :          0 :   if ( clipToExtent && context.flags() & QgsRenderContext::ApplyClipAfterReprojection && !context.mapExtent().contains( poly.boundingRect() ) )
     218                 :            :   {
     219                 :            :     // early clipping was not possible, so we have to apply it here after transformation
     220                 :          0 :     const QgsRectangle e = context.mapExtent();
     221                 :          0 :     const double cw = e.width() / 10;
     222                 :          0 :     const double ch = e.height() / 10;
     223                 :          0 :     const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
     224                 :          0 :     QgsClipper::trimPolygon( poly, clipRect );
     225                 :          0 :   }
     226                 :            : 
     227                 :          0 :   QPointF *ptr = poly.data();
     228                 :          0 :   for ( int i = 0; i < poly.size(); ++i, ++ptr )
     229                 :            :   {
     230                 :          0 :     mtp.transformInPlace( ptr->rx(), ptr->ry() );
     231                 :          0 :   }
     232                 :            : 
     233                 :          0 :   if ( !poly.empty() && !poly.isClosed() )
     234                 :          0 :     poly << poly.at( 0 );
     235                 :            : 
     236                 :          0 :   return poly;
     237                 :          0 : }
     238                 :            : 
     239                 :          0 : void QgsSymbol::_getPolygon( QPolygonF &pts, QVector<QPolygonF> &holes, QgsRenderContext &context, const QgsPolygon &polygon, const bool clipToExtent, const bool correctRingOrientation )
     240                 :            : {
     241                 :          0 :   holes.clear();
     242                 :            : 
     243                 :          0 :   pts = _getPolygonRing( context, *polygon.exteriorRing(), clipToExtent, true, correctRingOrientation );
     244                 :          0 :   const int ringCount = polygon.numInteriorRings();
     245                 :          0 :   holes.reserve( ringCount );
     246                 :          0 :   for ( int idx = 0; idx < ringCount; idx++ )
     247                 :            :   {
     248                 :          0 :     const QPolygonF hole = _getPolygonRing( context, *( polygon.interiorRing( idx ) ), clipToExtent, false, correctRingOrientation );
     249                 :          0 :     if ( !hole.isEmpty() )
     250                 :          0 :       holes.append( hole );
     251                 :          0 :   }
     252                 :          0 : }
     253                 :            : 
     254                 :          0 : QString QgsSymbol::symbolTypeToString( QgsSymbol::SymbolType type )
     255                 :            : {
     256                 :          0 :   switch ( type )
     257                 :            :   {
     258                 :            :     case QgsSymbol::Marker:
     259                 :          0 :       return QObject::tr( "Marker" );
     260                 :            :     case QgsSymbol::Line:
     261                 :          0 :       return QObject::tr( "Line" );
     262                 :            :     case QgsSymbol::Fill:
     263                 :          0 :       return QObject::tr( "Fill" );
     264                 :            :     case QgsSymbol::Hybrid:
     265                 :          0 :       return QObject::tr( "Hybrid" );
     266                 :            :   }
     267                 :          0 :   return QString();
     268                 :          0 : }
     269                 :            : 
     270                 :         92 : QgsSymbol::SymbolType QgsSymbol::symbolTypeForGeometryType( QgsWkbTypes::GeometryType type )
     271                 :            : {
     272                 :         92 :   switch ( type )
     273                 :            :   {
     274                 :            :     case QgsWkbTypes::PointGeometry:
     275                 :         19 :       return Marker;
     276                 :            :     case QgsWkbTypes::LineGeometry:
     277                 :         22 :       return Line;
     278                 :            :     case QgsWkbTypes::PolygonGeometry:
     279                 :         51 :       return Fill;
     280                 :            :     case QgsWkbTypes::UnknownGeometry:
     281                 :            :     case QgsWkbTypes::NullGeometry:
     282                 :          0 :       return Hybrid;
     283                 :            :   }
     284                 :          0 :   return Hybrid;
     285                 :         92 : }
     286                 :            : 
     287                 :        580 : const QgsPropertiesDefinition &QgsSymbol::propertyDefinitions()
     288                 :            : {
     289                 :        580 :   QgsSymbol::initPropertyDefinitions();
     290                 :        580 :   return sPropertyDefinitions;
     291                 :            : }
     292                 :            : 
     293                 :        761 : QgsSymbol::~QgsSymbol()
     294                 :        761 : {
     295                 :            :   // delete all symbol layers (we own them, so it's okay)
     296                 :        761 :   qDeleteAll( mLayers );
     297                 :        761 : }
     298                 :            : 
     299                 :          0 : QgsUnitTypes::RenderUnit QgsSymbol::outputUnit() const
     300                 :            : {
     301                 :          0 :   if ( mLayers.empty() )
     302                 :            :   {
     303                 :          0 :     return QgsUnitTypes::RenderUnknownUnit;
     304                 :            :   }
     305                 :            : 
     306                 :          0 :   QgsSymbolLayerList::const_iterator it = mLayers.constBegin();
     307                 :            : 
     308                 :          0 :   QgsUnitTypes::RenderUnit unit = ( *it )->outputUnit();
     309                 :            : 
     310                 :          0 :   for ( ; it != mLayers.constEnd(); ++it )
     311                 :            :   {
     312                 :          0 :     if ( ( *it )->outputUnit() != unit )
     313                 :            :     {
     314                 :          0 :       return QgsUnitTypes::RenderUnknownUnit;
     315                 :            :     }
     316                 :          0 :   }
     317                 :          0 :   return unit;
     318                 :          0 : }
     319                 :            : 
     320                 :          0 : bool QgsSymbol::usesMapUnits() const
     321                 :            : {
     322                 :          0 :   if ( mLayers.empty() )
     323                 :            :   {
     324                 :          0 :     return false;
     325                 :            :   }
     326                 :            : 
     327                 :          0 :   for ( const QgsSymbolLayer *layer : mLayers )
     328                 :            :   {
     329                 :          0 :     if ( layer->usesMapUnits() )
     330                 :            :     {
     331                 :          0 :       return true;
     332                 :            :     }
     333                 :            :   }
     334                 :          0 :   return false;
     335                 :          0 : }
     336                 :            : 
     337                 :          0 : QgsMapUnitScale QgsSymbol::mapUnitScale() const
     338                 :            : {
     339                 :          0 :   if ( mLayers.empty() )
     340                 :            :   {
     341                 :          0 :     return QgsMapUnitScale();
     342                 :            :   }
     343                 :            : 
     344                 :          0 :   QgsSymbolLayerList::const_iterator it = mLayers.constBegin();
     345                 :          0 :   if ( it == mLayers.constEnd() )
     346                 :          0 :     return QgsMapUnitScale();
     347                 :            : 
     348                 :          0 :   QgsMapUnitScale scale = ( *it )->mapUnitScale();
     349                 :          0 :   ++it;
     350                 :            : 
     351                 :          0 :   for ( ; it != mLayers.constEnd(); ++it )
     352                 :            :   {
     353                 :          0 :     if ( ( *it )->mapUnitScale() != scale )
     354                 :            :     {
     355                 :          0 :       return QgsMapUnitScale();
     356                 :            :     }
     357                 :          0 :   }
     358                 :          0 :   return scale;
     359                 :          0 : }
     360                 :            : 
     361                 :          0 : void QgsSymbol::setOutputUnit( QgsUnitTypes::RenderUnit u )
     362                 :            : {
     363                 :          0 :   const auto constMLayers = mLayers;
     364                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
     365                 :            :   {
     366                 :          0 :     layer->setOutputUnit( u );
     367                 :            :   }
     368                 :          0 : }
     369                 :            : 
     370                 :          0 : void QgsSymbol::setMapUnitScale( const QgsMapUnitScale &scale )
     371                 :            : {
     372                 :          0 :   const auto constMLayers = mLayers;
     373                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
     374                 :            :   {
     375                 :          0 :     layer->setMapUnitScale( scale );
     376                 :            :   }
     377                 :          0 : }
     378                 :            : 
     379                 :         78 : QgsSymbol *QgsSymbol::defaultSymbol( QgsWkbTypes::GeometryType geomType )
     380                 :            : {
     381                 :         78 :   std::unique_ptr< QgsSymbol > s;
     382                 :            : 
     383                 :            :   // override global default if project has a default for this type
     384                 :         78 :   QString defaultSymbol;
     385                 :         78 :   switch ( geomType )
     386                 :            :   {
     387                 :            :     case QgsWkbTypes::PointGeometry :
     388                 :         66 :       defaultSymbol = QgsProject::instance()->readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
     389                 :         22 :       break;
     390                 :            :     case QgsWkbTypes::LineGeometry :
     391                 :         75 :       defaultSymbol = QgsProject::instance()->readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
     392                 :         25 :       break;
     393                 :            :     case QgsWkbTypes::PolygonGeometry :
     394                 :         93 :       defaultSymbol = QgsProject::instance()->readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
     395                 :         31 :       break;
     396                 :            :     default:
     397                 :          0 :       break;
     398                 :            :   }
     399                 :         78 :   if ( !defaultSymbol.isEmpty() )
     400                 :          0 :     s.reset( QgsStyle::defaultStyle()->symbol( defaultSymbol ) );
     401                 :            : 
     402                 :            :   // if no default found for this type, get global default (as previously)
     403                 :         78 :   if ( !s )
     404                 :            :   {
     405                 :         78 :     switch ( geomType )
     406                 :            :     {
     407                 :            :       case QgsWkbTypes::PointGeometry:
     408                 :         22 :         s = std::make_unique< QgsMarkerSymbol >();
     409                 :         22 :         break;
     410                 :            :       case QgsWkbTypes::LineGeometry:
     411                 :         25 :         s = std::make_unique< QgsLineSymbol >();
     412                 :         25 :         break;
     413                 :            :       case QgsWkbTypes::PolygonGeometry:
     414                 :         31 :         s = std::make_unique< QgsFillSymbol >();
     415                 :         31 :         break;
     416                 :            :       default:
     417                 :          0 :         QgsDebugMsg( QStringLiteral( "unknown layer's geometry type" ) );
     418                 :          0 :         return nullptr;
     419                 :            :     }
     420                 :         78 :   }
     421                 :            : 
     422                 :            :   // set opacity
     423                 :         78 :   double opacity = 1.0;
     424                 :         78 :   bool ok = false;
     425                 :            :   // upgrade old setting
     426                 :        234 :   double alpha = QgsProject::instance()->readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ), 255, &ok );
     427                 :         78 :   if ( ok )
     428                 :          0 :     opacity = alpha / 255.0;
     429                 :        234 :   double newOpacity = QgsProject::instance()->readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ), 1.0, &ok );
     430                 :         78 :   if ( ok )
     431                 :          0 :     opacity = newOpacity;
     432                 :         78 :   s->setOpacity( opacity );
     433                 :            : 
     434                 :            :   // set random color, it project prefs allow
     435                 :         78 :   if ( defaultSymbol.isEmpty() ||
     436                 :          0 :        QgsProject::instance()->readBoolEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ), true ) )
     437                 :            :   {
     438                 :         78 :     s->setColor( QgsApplication::colorSchemeRegistry()->fetchRandomStyleColor() );
     439                 :         78 :   }
     440                 :            : 
     441                 :         78 :   return s.release();
     442                 :         78 : }
     443                 :            : 
     444                 :          0 : QgsSymbolLayer *QgsSymbol::symbolLayer( int layer )
     445                 :            : {
     446                 :          0 :   return mLayers.value( layer );
     447                 :            : }
     448                 :            : 
     449                 :          0 : const QgsSymbolLayer *QgsSymbol::symbolLayer( int layer ) const
     450                 :            : {
     451                 :          0 :   return mLayers.value( layer );
     452                 :            : }
     453                 :            : 
     454                 :          0 : bool QgsSymbol::insertSymbolLayer( int index, QgsSymbolLayer *layer )
     455                 :            : {
     456                 :          0 :   if ( index < 0 || index > mLayers.count() ) // can be added also after the last index
     457                 :          0 :     return false;
     458                 :            : 
     459                 :          0 :   if ( !layer || !layer->isCompatibleWithSymbol( this ) )
     460                 :          0 :     return false;
     461                 :            : 
     462                 :          0 :   mLayers.insert( index, layer );
     463                 :          0 :   return true;
     464                 :          0 : }
     465                 :            : 
     466                 :            : 
     467                 :          0 : bool QgsSymbol::appendSymbolLayer( QgsSymbolLayer *layer )
     468                 :            : {
     469                 :          0 :   if ( !layer || !layer->isCompatibleWithSymbol( this ) )
     470                 :          0 :     return false;
     471                 :            : 
     472                 :          0 :   mLayers.append( layer );
     473                 :          0 :   return true;
     474                 :          0 : }
     475                 :            : 
     476                 :            : 
     477                 :          0 : bool QgsSymbol::deleteSymbolLayer( int index )
     478                 :            : {
     479                 :          0 :   if ( index < 0 || index >= mLayers.count() )
     480                 :          0 :     return false;
     481                 :            : 
     482                 :          0 :   delete mLayers.at( index );
     483                 :          0 :   mLayers.removeAt( index );
     484                 :          0 :   return true;
     485                 :          0 : }
     486                 :            : 
     487                 :            : 
     488                 :          0 : QgsSymbolLayer *QgsSymbol::takeSymbolLayer( int index )
     489                 :            : {
     490                 :          0 :   if ( index < 0 || index >= mLayers.count() )
     491                 :          0 :     return nullptr;
     492                 :            : 
     493                 :          0 :   return mLayers.takeAt( index );
     494                 :          0 : }
     495                 :            : 
     496                 :            : 
     497                 :          0 : bool QgsSymbol::changeSymbolLayer( int index, QgsSymbolLayer *layer )
     498                 :            : {
     499                 :          0 :   QgsSymbolLayer *oldLayer = mLayers.value( index );
     500                 :            : 
     501                 :          0 :   if ( oldLayer == layer )
     502                 :          0 :     return false;
     503                 :            : 
     504                 :          0 :   if ( !layer || !layer->isCompatibleWithSymbol( this ) )
     505                 :          0 :     return false;
     506                 :            : 
     507                 :          0 :   delete oldLayer; // first delete the original layer
     508                 :          0 :   mLayers[index] = layer; // set new layer
     509                 :          0 :   return true;
     510                 :          0 : }
     511                 :            : 
     512                 :            : 
     513                 :          0 : void QgsSymbol::startRender( QgsRenderContext &context, const QgsFields &fields )
     514                 :            : {
     515                 :            :   Q_ASSERT_X( !mStarted, "startRender", "Rendering has already been started for this symbol instance!" );
     516                 :          0 :   mStarted = true;
     517                 :            : 
     518                 :          0 :   mSymbolRenderContext.reset( new QgsSymbolRenderContext( context, QgsUnitTypes::RenderUnknownUnit, mOpacity, false, mRenderHints, nullptr, fields ) );
     519                 :            : 
     520                 :            :   // Why do we need a copy here ? Is it to make sure the symbol layer rendering does not mess with the symbol render context ?
     521                 :            :   // Or is there another profound reason ?
     522                 :          0 :   QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, mOpacity, false, mRenderHints, nullptr, fields );
     523                 :            : 
     524                 :          0 :   std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::updateSymbolScope( this, new QgsExpressionContextScope() ) );
     525                 :          0 :   mSymbolRenderContext->setExpressionContextScope( scope.release() );
     526                 :            : 
     527                 :          0 :   mDataDefinedProperties.prepare( context.expressionContext() );
     528                 :            : 
     529                 :          0 :   const auto constMLayers = mLayers;
     530                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
     531                 :            :   {
     532                 :          0 :     if ( !layer->enabled() || !context.isSymbolLayerEnabled( layer ) )
     533                 :          0 :       continue;
     534                 :            : 
     535                 :          0 :     layer->prepareExpressions( symbolContext );
     536                 :          0 :     layer->startRender( symbolContext );
     537                 :            :   }
     538                 :          0 : }
     539                 :            : 
     540                 :          0 : void QgsSymbol::stopRender( QgsRenderContext &context )
     541                 :            : {
     542                 :            :   Q_ASSERT_X( mStarted, "startRender", "startRender was not called for this symbol instance!" );
     543                 :          0 :   mStarted = false;
     544                 :            : 
     545                 :          0 :   Q_UNUSED( context )
     546                 :          0 :   if ( mSymbolRenderContext )
     547                 :            :   {
     548                 :          0 :     const auto constMLayers = mLayers;
     549                 :          0 :     for ( QgsSymbolLayer *layer : constMLayers )
     550                 :            :     {
     551                 :          0 :       if ( !layer->enabled()  || !context.isSymbolLayerEnabled( layer ) )
     552                 :          0 :         continue;
     553                 :            : 
     554                 :          0 :       layer->stopRender( *mSymbolRenderContext );
     555                 :            :     }
     556                 :          0 :   }
     557                 :            : 
     558                 :          0 :   mSymbolRenderContext.reset( nullptr );
     559                 :            : 
     560                 :            :   Q_NOWARN_DEPRECATED_PUSH
     561                 :          0 :   mLayer = nullptr;
     562                 :            :   Q_NOWARN_DEPRECATED_POP
     563                 :          0 : }
     564                 :            : 
     565                 :        148 : void QgsSymbol::setColor( const QColor &color )
     566                 :            : {
     567                 :        148 :   const auto constMLayers = mLayers;
     568                 :        296 :   for ( QgsSymbolLayer *layer : constMLayers )
     569                 :            :   {
     570                 :        148 :     if ( !layer->isLocked() )
     571                 :        148 :       layer->setColor( color );
     572                 :            :   }
     573                 :        148 : }
     574                 :            : 
     575                 :         90 : QColor QgsSymbol::color() const
     576                 :            : {
     577                 :         90 :   for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
     578                 :            :   {
     579                 :            :     // return color of the first unlocked layer
     580                 :         90 :     if ( !( *it )->isLocked() )
     581                 :         90 :       return ( *it )->color();
     582                 :          0 :   }
     583                 :          0 :   return QColor( 0, 0, 0 );
     584                 :         90 : }
     585                 :            : 
     586                 :          0 : void QgsSymbol::drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext, bool selected, const QgsExpressionContext *expressionContext, const QgsLegendPatchShape *patchShape )
     587                 :            : {
     588                 :          0 :   QgsRenderContext *context = customContext;
     589                 :          0 :   std::unique_ptr< QgsRenderContext > tempContext;
     590                 :          0 :   if ( !context )
     591                 :            :   {
     592                 :          0 :     tempContext.reset( new QgsRenderContext( QgsRenderContext::fromQPainter( painter ) ) );
     593                 :          0 :     context = tempContext.get();
     594                 :          0 :     context->setFlag( QgsRenderContext::RenderSymbolPreview, true );
     595                 :          0 :   }
     596                 :            : 
     597                 :          0 :   const bool prevForceVector = context->forceVectorOutput();
     598                 :          0 :   context->setForceVectorOutput( true );
     599                 :            : 
     600                 :          0 :   const double opacity = expressionContext ? dataDefinedProperties().valueAsDouble( QgsSymbol::PropertyOpacity, *expressionContext, mOpacity ) : mOpacity;
     601                 :            : 
     602                 :          0 :   QgsSymbolRenderContext symbolContext( *context, QgsUnitTypes::RenderUnknownUnit, opacity, false, mRenderHints, nullptr );
     603                 :          0 :   symbolContext.setSelected( selected );
     604                 :          0 :   symbolContext.setOriginalGeometryType( mType == Fill ? QgsWkbTypes::PolygonGeometry : QgsWkbTypes::UnknownGeometry );
     605                 :          0 :   if ( patchShape )
     606                 :          0 :     symbolContext.setPatchShape( *patchShape );
     607                 :            : 
     608                 :          0 :   if ( !customContext && expressionContext )
     609                 :            :   {
     610                 :          0 :     context->setExpressionContext( *expressionContext );
     611                 :          0 :   }
     612                 :          0 :   else if ( !customContext )
     613                 :            :   {
     614                 :            :     // if no render context was passed, build a minimal expression context
     615                 :          0 :     QgsExpressionContext expContext;
     616                 :          0 :     expContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) );
     617                 :          0 :     context->setExpressionContext( expContext );
     618                 :          0 :   }
     619                 :            : 
     620                 :          0 :   for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
     621                 :            :   {
     622                 :          0 :     if ( !layer->enabled()  || ( customContext && !customContext->isSymbolLayerEnabled( layer ) ) )
     623                 :          0 :       continue;
     624                 :            : 
     625                 :          0 :     if ( mType == Fill && layer->type() == Line )
     626                 :            :     {
     627                 :            :       // line symbol layer would normally draw just a line
     628                 :            :       // so we override this case to force it to draw a polygon stroke
     629                 :          0 :       QgsLineSymbolLayer *lsl = dynamic_cast<QgsLineSymbolLayer *>( layer );
     630                 :          0 :       if ( lsl )
     631                 :            :       {
     632                 :            :         // from QgsFillSymbolLayer::drawPreviewIcon() -- would be nicer to add the
     633                 :            :         // symbol type to QgsSymbolLayer::drawPreviewIcon so this logic could be avoided!
     634                 :            : 
     635                 :            :         // hmm... why was this using size -1 ??
     636                 :          0 :         const QSizeF targetSize = QSizeF( size.width() - 1, size.height() - 1 );
     637                 :            : 
     638                 :          0 :         const QList< QList< QPolygonF > > polys = patchShape ? patchShape->toQPolygonF( QgsSymbol::Fill, targetSize )
     639                 :          0 :             : QgsStyle::defaultStyle()->defaultPatchAsQPolygonF( QgsSymbol::Fill, targetSize );
     640                 :            : 
     641                 :          0 :         lsl->startRender( symbolContext );
     642                 :          0 :         QgsPaintEffect *effect = lsl->paintEffect();
     643                 :            : 
     644                 :          0 :         std::unique_ptr< QgsEffectPainter > effectPainter;
     645                 :          0 :         if ( effect && effect->enabled() )
     646                 :          0 :           effectPainter = std::make_unique< QgsEffectPainter >( symbolContext.renderContext(), effect );
     647                 :            : 
     648                 :          0 :         for ( const QList< QPolygonF > &poly : polys )
     649                 :            :         {
     650                 :          0 :           QVector< QPolygonF > rings;
     651                 :          0 :           rings.reserve( poly.size() );
     652                 :          0 :           for ( int i = 1; i < poly.size(); ++i )
     653                 :          0 :             rings << poly.at( i );
     654                 :          0 :           lsl->renderPolygonStroke( poly.value( 0 ), &rings, symbolContext );
     655                 :          0 :         }
     656                 :            : 
     657                 :          0 :         effectPainter.reset();
     658                 :          0 :         lsl->stopRender( symbolContext );
     659                 :          0 :       }
     660                 :          0 :     }
     661                 :            :     else
     662                 :          0 :       layer->drawPreviewIcon( symbolContext, size );
     663                 :            :   }
     664                 :            : 
     665                 :          0 :   context->setForceVectorOutput( prevForceVector );
     666                 :          0 : }
     667                 :            : 
     668                 :          0 : void QgsSymbol::exportImage( const QString &path, const QString &format, QSize size )
     669                 :            : {
     670                 :          0 :   if ( format.compare( QLatin1String( "svg" ), Qt::CaseInsensitive ) == 0 )
     671                 :            :   {
     672                 :          0 :     QSvgGenerator generator;
     673                 :          0 :     generator.setFileName( path );
     674                 :          0 :     generator.setSize( size );
     675                 :          0 :     generator.setViewBox( QRect( 0, 0, size.height(), size.height() ) );
     676                 :            : 
     677                 :          0 :     QPainter painter( &generator );
     678                 :          0 :     drawPreviewIcon( &painter, size );
     679                 :          0 :     painter.end();
     680                 :          0 :   }
     681                 :            :   else
     682                 :            :   {
     683                 :          0 :     QImage image = asImage( size );
     684                 :          0 :     image.save( path );
     685                 :          0 :   }
     686                 :          0 : }
     687                 :            : 
     688                 :          0 : QImage QgsSymbol::asImage( QSize size, QgsRenderContext *customContext )
     689                 :            : {
     690                 :          0 :   QImage image( size, QImage::Format_ARGB32_Premultiplied );
     691                 :          0 :   image.fill( 0 );
     692                 :            : 
     693                 :          0 :   QPainter p( &image );
     694                 :          0 :   p.setRenderHint( QPainter::Antialiasing );
     695                 :            : 
     696                 :          0 :   drawPreviewIcon( &p, size, customContext );
     697                 :            : 
     698                 :          0 :   return image;
     699                 :          0 : }
     700                 :            : 
     701                 :            : 
     702                 :          0 : QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, QgsSymbol::PreviewFlags flags )
     703                 :            : {
     704                 :          0 :   QImage preview( QSize( 100, 100 ), QImage::Format_ARGB32_Premultiplied );
     705                 :          0 :   preview.fill( 0 );
     706                 :            : 
     707                 :          0 :   QPainter p( &preview );
     708                 :          0 :   p.setRenderHint( QPainter::Antialiasing );
     709                 :          0 :   p.translate( 0.5, 0.5 ); // shift by half a pixel to avoid blurring due antialiasing
     710                 :            : 
     711                 :          0 :   if ( mType == QgsSymbol::Marker && flags & PreviewFlag::FlagIncludeCrosshairsForMarkerSymbols )
     712                 :            :   {
     713                 :          0 :     p.setPen( QPen( Qt::gray ) );
     714                 :          0 :     p.drawLine( 0, 50, 100, 50 );
     715                 :          0 :     p.drawLine( 50, 0, 50, 100 );
     716                 :          0 :   }
     717                 :            : 
     718                 :          0 :   QgsRenderContext context = QgsRenderContext::fromQPainter( &p );
     719                 :          0 :   context.setFlag( QgsRenderContext::RenderSymbolPreview );
     720                 :          0 :   if ( expressionContext )
     721                 :          0 :     context.setExpressionContext( *expressionContext );
     722                 :            : 
     723                 :          0 :   context.setIsGuiPreview( true );
     724                 :          0 :   startRender( context );
     725                 :            : 
     726                 :          0 :   if ( mType == QgsSymbol::Line )
     727                 :            :   {
     728                 :          0 :     QPolygonF poly;
     729                 :          0 :     poly << QPointF( 0, 50 ) << QPointF( 99, 50 );
     730                 :          0 :     static_cast<QgsLineSymbol *>( this )->renderPolyline( poly, nullptr, context );
     731                 :          0 :   }
     732                 :          0 :   else if ( mType == QgsSymbol::Fill )
     733                 :            :   {
     734                 :          0 :     QPolygonF polygon;
     735                 :          0 :     polygon << QPointF( 20, 20 ) << QPointF( 80, 20 ) << QPointF( 80, 80 ) << QPointF( 20, 80 ) << QPointF( 20, 20 );
     736                 :          0 :     static_cast<QgsFillSymbol *>( this )->renderPolygon( polygon, nullptr, nullptr, context );
     737                 :          0 :   }
     738                 :            :   else // marker
     739                 :            :   {
     740                 :          0 :     static_cast<QgsMarkerSymbol *>( this )->renderPoint( QPointF( 50, 50 ), nullptr, context );
     741                 :       1073 :   }
     742                 :            : 
     743                 :          0 :   stopRender( context );
     744                 :       1073 :   return preview;
     745                 :       1073 : }
     746                 :            : 
     747                 :       1073 : 
     748                 :          0 : QString QgsSymbol::dump() const
     749                 :            : {
     750                 :          0 :   QString t;
     751                 :          0 :   switch ( type() )
     752                 :            :   {
     753                 :            :     case QgsSymbol::Marker:
     754                 :          0 :       t = QStringLiteral( "MARKER" );
     755                 :          0 :       break;
     756                 :            :     case QgsSymbol::Line:
     757                 :          0 :       t = QStringLiteral( "LINE" );
     758                 :          0 :       break;
     759                 :            :     case QgsSymbol::Fill:
     760                 :          0 :       t = QStringLiteral( "FILL" );
     761                 :          0 :       break;
     762                 :            :     default:
     763                 :       1073 :       Q_ASSERT( false && "unknown symbol type" );
     764                 :          0 :   }
     765                 :          0 :   QString s = QStringLiteral( "%1 SYMBOL (%2 layers) color %3" ).arg( t ).arg( mLayers.count() ).arg( QgsSymbolLayerUtils::encodeColor( color() ) );
     766                 :            : 
     767                 :          0 :   for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
     768                 :            :   {
     769                 :            :     // TODO:
     770                 :          0 :   }
     771                 :          0 :   return s;
     772                 :          0 : }
     773                 :            : 
     774                 :          0 : void QgsSymbol::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
     775                 :            : {
     776                 :          0 :   props[ QStringLiteral( "alpha" )] = QString::number( opacity() );
     777                 :          0 :   double scaleFactor = 1.0;
     778                 :          0 :   props[ QStringLiteral( "uom" )] = QgsSymbolLayerUtils::encodeSldUom( outputUnit(), &scaleFactor );
     779                 :          0 :   props[ QStringLiteral( "uomScale" )] = ( !qgsDoubleNear( scaleFactor, 1.0 ) ? qgsDoubleToString( scaleFactor ) : QString() );
     780                 :            : 
     781                 :          0 :   for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
     782                 :            :   {
     783                 :          0 :     ( *it )->toSld( doc, element, props );
     784                 :          0 :   }
     785                 :          0 : }
     786                 :            : 
     787                 :          0 : QgsSymbolLayerList QgsSymbol::cloneLayers() const
     788                 :            : {
     789                 :          0 :   QgsSymbolLayerList lst;
     790                 :          0 :   for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
     791                 :            :   {
     792                 :          0 :     QgsSymbolLayer *layer = ( *it )->clone();
     793                 :          0 :     layer->setLocked( ( *it )->isLocked() );
     794                 :          0 :     layer->setRenderingPass( ( *it )->renderingPass() );
     795                 :          0 :     layer->setEnabled( ( *it )->enabled() );
     796                 :          0 :     lst.append( layer );
     797                 :          0 :   }
     798                 :          0 :   return lst;
     799                 :          0 : }
     800                 :            : 
     801                 :          0 : void QgsSymbol::renderUsingLayer( QgsSymbolLayer *layer, QgsSymbolRenderContext &context )
     802                 :            : {
     803                 :            :   Q_ASSERT( layer->type() == Hybrid );
     804                 :            : 
     805                 :          0 :   if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
     806                 :          0 :     return;
     807                 :            : 
     808                 :          0 :   QgsGeometryGeneratorSymbolLayer *generatorLayer = static_cast<QgsGeometryGeneratorSymbolLayer *>( layer );
     809                 :            : 
     810                 :          0 :   QgsPaintEffect *effect = generatorLayer->paintEffect();
     811                 :          0 :   if ( effect && effect->enabled() )
     812                 :            :   {
     813                 :          0 :     QgsEffectPainter p( context.renderContext(), effect );
     814                 :          0 :     generatorLayer->render( context );
     815                 :          0 :   }
     816                 :            :   else
     817                 :            :   {
     818                 :          0 :     generatorLayer->render( context );
     819                 :            :   }
     820                 :          0 : }
     821                 :            : 
     822                 :          0 : QSet<QString> QgsSymbol::usedAttributes( const QgsRenderContext &context ) const
     823                 :            : {
     824                 :            :   // calling referencedFields() with ignoreContext=true because in our expression context
     825                 :            :   // we do not have valid QgsFields yet - because of that the field names from expressions
     826                 :            :   // wouldn't get reported
     827                 :          0 :   QSet<QString> attributes = mDataDefinedProperties.referencedFields( context.expressionContext(), true );
     828                 :          0 :   QgsSymbolLayerList::const_iterator sIt = mLayers.constBegin();
     829                 :          0 :   for ( ; sIt != mLayers.constEnd(); ++sIt )
     830                 :            :   {
     831                 :          0 :     if ( *sIt )
     832                 :            :     {
     833                 :          0 :       attributes.unite( ( *sIt )->usedAttributes( context ) );
     834                 :          0 :     }
     835                 :          0 :   }
     836                 :          0 :   return attributes;
     837                 :          0 : }
     838                 :            : 
     839                 :          0 : void QgsSymbol::setDataDefinedProperty( QgsSymbol::Property key, const QgsProperty &property )
     840                 :            : {
     841                 :          0 :   mDataDefinedProperties.setProperty( key, property );
     842                 :          0 : }
     843                 :            : 
     844                 :          0 : bool QgsSymbol::hasDataDefinedProperties() const
     845                 :            : {
     846                 :          0 :   if ( mDataDefinedProperties.hasActiveProperties() )
     847                 :          0 :     return true;
     848                 :            : 
     849                 :          0 :   for ( QgsSymbolLayer *layer : mLayers )
     850                 :            :   {
     851                 :          0 :     if ( layer->hasDataDefinedProperties() )
     852                 :          0 :       return true;
     853                 :            :   }
     854                 :          0 :   return false;
     855                 :          0 : }
     856                 :            : 
     857                 :          0 : bool QgsSymbol::canCauseArtifactsBetweenAdjacentTiles() const
     858                 :            : {
     859                 :          0 :   for ( QgsSymbolLayer *layer : mLayers )
     860                 :            :   {
     861                 :          0 :     if ( layer->canCauseArtifactsBetweenAdjacentTiles() )
     862                 :          0 :       return true;
     863                 :            :   }
     864                 :          0 :   return false;
     865                 :          0 : }
     866                 :            : 
     867                 :          0 : void QgsSymbol::setLayer( const QgsVectorLayer *layer )
     868                 :            : {
     869                 :            :   Q_NOWARN_DEPRECATED_PUSH
     870                 :          0 :   mLayer = layer;
     871                 :            :   Q_NOWARN_DEPRECATED_POP
     872                 :          0 : }
     873                 :            : 
     874                 :          0 : const QgsVectorLayer *QgsSymbol::layer() const
     875                 :            : {
     876                 :            :   Q_NOWARN_DEPRECATED_PUSH
     877                 :          0 :   return mLayer;
     878                 :            :   Q_NOWARN_DEPRECATED_POP
     879                 :            : }
     880                 :            : 
     881                 :            : ///@cond PRIVATE
     882                 :            : 
     883                 :            : /**
     884                 :            :  * RAII class to pop scope from an expression context on destruction
     885                 :            :  */
     886                 :            : class ExpressionContextScopePopper
     887                 :            : {
     888                 :            :   public:
     889                 :            : 
     890                 :          0 :     ExpressionContextScopePopper() = default;
     891                 :            : 
     892                 :          0 :     ~ExpressionContextScopePopper()
     893                 :            :     {
     894                 :          0 :       if ( context )
     895                 :          0 :         context->popScope();
     896                 :          0 :     }
     897                 :            : 
     898                 :          0 :     QgsExpressionContext *context = nullptr;
     899                 :            : };
     900                 :            : 
     901                 :            : /**
     902                 :            :  * RAII class to restore original geometry on a render context on destruction
     903                 :            :  */
     904                 :            : class GeometryRestorer
     905                 :            : {
     906                 :            :   public:
     907                 :          0 :     GeometryRestorer( QgsRenderContext &context )
     908                 :          0 :       : mContext( context ),
     909                 :          0 :         mGeometry( context.geometry() )
     910                 :          0 :     {}
     911                 :            : 
     912                 :          0 :     ~GeometryRestorer()
     913                 :            :     {
     914                 :          0 :       mContext.setGeometry( mGeometry );
     915                 :          0 :     }
     916                 :            : 
     917                 :            :   private:
     918                 :            :     QgsRenderContext &mContext;
     919                 :            :     const QgsAbstractGeometry *mGeometry;
     920                 :            : };
     921                 :            : ///@endcond PRIVATE
     922                 :            : 
     923                 :          0 : void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker, int currentVertexMarkerType, double currentVertexMarkerSize )
     924                 :            : {
     925                 :          0 :   if ( context.renderingStopped() )
     926                 :          0 :     return;
     927                 :            : 
     928                 :          0 :   const QgsGeometry geom = feature.geometry();
     929                 :          0 :   if ( geom.isNull() )
     930                 :            :   {
     931                 :          0 :     return;
     932                 :            :   }
     933                 :            : 
     934                 :          0 :   GeometryRestorer geomRestorer( context );
     935                 :            : 
     936                 :          0 :   bool usingSegmentizedGeometry = false;
     937                 :          0 :   context.setGeometry( geom.constGet() );
     938                 :            : 
     939                 :          0 :   if ( geom.type() != QgsWkbTypes::PointGeometry && !geom.boundingBox().isNull() )
     940                 :            :   {
     941                 :            :     try
     942                 :            :     {
     943                 :          0 :       const QPointF boundsOrigin = _getPoint( context, QgsPoint( geom.boundingBox().xMinimum(), geom.boundingBox().yMinimum() ) );
     944                 :          0 :       if ( std::isfinite( boundsOrigin.x() ) && std::isfinite( boundsOrigin.y() ) )
     945                 :          0 :         context.setTextureOrigin( boundsOrigin );
     946                 :          0 :     }
     947                 :            :     catch ( QgsCsException & )
     948                 :            :     {
     949                 :            : 
     950                 :          0 :     }
     951                 :          0 :   }
     952                 :            : 
     953                 :          0 :   bool clippingEnabled = clipFeaturesToExtent();
     954                 :          0 :   if ( clippingEnabled && context.testFlag( QgsRenderContext::RenderMapTile ) )
     955                 :            :   {
     956                 :            :     // If the "avoid artifacts between adjacent tiles" flag is set (RenderMapTile), then we'll force disable
     957                 :            :     // the geometry clipping IF (and only if) this symbol can potentially have rendering artifacts when rendered as map tiles.
     958                 :            :     // If the symbol won't have any artifacts anyway, then it's pointless and incredibly expensive to skip the clipping!
     959                 :          0 :     if ( canCauseArtifactsBetweenAdjacentTiles() )
     960                 :            :     {
     961                 :          0 :       clippingEnabled = false;
     962                 :          0 :     }
     963                 :          0 :   }
     964                 :            : 
     965                 :          0 :   mSymbolRenderContext->setGeometryPartCount( geom.constGet()->partCount() );
     966                 :          0 :   mSymbolRenderContext->setGeometryPartNum( 1 );
     967                 :            : 
     968                 :          0 :   const bool needsExpressionContext = hasDataDefinedProperties();
     969                 :          0 :   ExpressionContextScopePopper scopePopper;
     970                 :          0 :   if ( mSymbolRenderContext->expressionContextScope() )
     971                 :            :   {
     972                 :          0 :     if ( needsExpressionContext )
     973                 :            :     {
     974                 :            :       // this is somewhat nasty - by appending this scope here it's now owned
     975                 :            :       // by both mSymbolRenderContext AND context.expressionContext()
     976                 :            :       // the RAII scopePopper is required to make sure it always has ownership transferred back
     977                 :            :       // from context.expressionContext(), even if exceptions of other early exits occur in this
     978                 :            :       // function
     979                 :          0 :       context.expressionContext().appendScope( mSymbolRenderContext->expressionContextScope() );
     980                 :          0 :       scopePopper.context = &context.expressionContext();
     981                 :            : 
     982                 :          0 :       QgsExpressionContextUtils::updateSymbolScope( this, mSymbolRenderContext->expressionContextScope() );
     983                 :          0 :       mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_COUNT, mSymbolRenderContext->geometryPartCount(), true ) );
     984                 :          0 :       mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, 1, true ) );
     985                 :          0 :     }
     986                 :          0 :   }
     987                 :            : 
     988                 :            :   // Collection of markers to paint, only used for no curve types.
     989                 :          0 :   QPolygonF markers;
     990                 :            : 
     991                 :          0 :   QgsGeometry renderedBoundsGeom;
     992                 :            : 
     993                 :            :   // Step 1 - collect the set of painter coordinate geometries to render.
     994                 :            :   // We do this upfront, because we only want to ever do this once, regardless how many symbol layers we need to render.
     995                 :            : 
     996                 :          0 :   struct PointInfo
     997                 :            :   {
     998                 :            :     QPointF renderPoint;
     999                 :          0 :     const QgsPoint *originalGeometry = nullptr;
    1000                 :            :   };
    1001                 :          0 :   QVector< PointInfo > pointsToRender;
    1002                 :            : 
    1003                 :          0 :   struct LineInfo
    1004                 :            :   {
    1005                 :            :     QPolygonF renderLine;
    1006                 :          0 :     const QgsCurve *originalGeometry = nullptr;
    1007                 :            :   };
    1008                 :          0 :   QVector< LineInfo > linesToRender;
    1009                 :            : 
    1010                 :          0 :   struct PolygonInfo
    1011                 :            :   {
    1012                 :            :     QPolygonF renderExterior;
    1013                 :            :     QVector< QPolygonF > renderRings;
    1014                 :          0 :     const QgsCurvePolygon *originalGeometry = nullptr;
    1015                 :          0 :     int originalPartIndex = 0;
    1016                 :            :   };
    1017                 :          0 :   QVector< PolygonInfo > polygonsToRender;
    1018                 :            : 
    1019                 :          0 :   std::function< void ( const QgsAbstractGeometry *, int partIndex )> getPartGeometry;
    1020                 :          0 :   getPartGeometry = [&pointsToRender, &linesToRender, &polygonsToRender, &getPartGeometry, &context, &clippingEnabled, &markers, &feature, &usingSegmentizedGeometry, this]( const QgsAbstractGeometry * part, int partIndex = 0 )
    1021                 :            :   {
    1022                 :          0 :     Q_UNUSED( feature )
    1023                 :            : 
    1024                 :          0 :     if ( !part )
    1025                 :          0 :       return;
    1026                 :            : 
    1027                 :            :     // geometry preprocessing
    1028                 :          0 :     QgsGeometry temporaryGeometryContainer;
    1029                 :          0 :     const QgsAbstractGeometry *processedGeometry = nullptr;
    1030                 :            : 
    1031                 :          0 :     const bool isMultiPart = qgsgeometry_cast< const QgsGeometryCollection * >( part ) && qgsgeometry_cast< const QgsGeometryCollection * >( part )->numGeometries() > 1;
    1032                 :            : 
    1033                 :          0 :     if ( !isMultiPart )
    1034                 :            :     {
    1035                 :            :       // segmentize curved geometries
    1036                 :          0 :       const bool needsSegmentizing = QgsWkbTypes::isCurvedType( part->wkbType() ) || part->hasCurvedSegments();
    1037                 :          0 :       if ( needsSegmentizing )
    1038                 :            :       {
    1039                 :          0 :         std::unique_ptr< QgsAbstractGeometry > segmentizedPart( part->segmentize( context.segmentationTolerance(), context.segmentationToleranceType() ) );
    1040                 :          0 :         if ( !segmentizedPart )
    1041                 :            :         {
    1042                 :          0 :           return;
    1043                 :            :         }
    1044                 :          0 :         temporaryGeometryContainer.set( segmentizedPart.release() );
    1045                 :          0 :         processedGeometry = temporaryGeometryContainer.constGet();
    1046                 :          0 :         usingSegmentizedGeometry = true;
    1047                 :          0 :       }
    1048                 :            :       else
    1049                 :            :       {
    1050                 :            :         // no segmentation required
    1051                 :          0 :         processedGeometry = part;
    1052                 :            :       }
    1053                 :            : 
    1054                 :            :       // Simplify the geometry, if needed.
    1055                 :          0 :       if ( context.vectorSimplifyMethod().forceLocalOptimization() )
    1056                 :            :       {
    1057                 :          0 :         const int simplifyHints = context.vectorSimplifyMethod().simplifyHints();
    1058                 :          0 :         const QgsMapToPixelSimplifier simplifier( simplifyHints, context.vectorSimplifyMethod().tolerance(),
    1059                 :          0 :             static_cast< QgsMapToPixelSimplifier::SimplifyAlgorithm >( context.vectorSimplifyMethod().simplifyAlgorithm() ) );
    1060                 :            : 
    1061                 :          0 :         std::unique_ptr< QgsAbstractGeometry > simplified( simplifier.simplify( processedGeometry ) );
    1062                 :          0 :         if ( simplified )
    1063                 :            :         {
    1064                 :          0 :           temporaryGeometryContainer.set( simplified.release() );
    1065                 :          0 :           processedGeometry = temporaryGeometryContainer.constGet();
    1066                 :          0 :         }
    1067                 :          0 :       }
    1068                 :            : 
    1069                 :            :       // clip geometry to render context clipping regions
    1070                 :          0 :       if ( !context.featureClipGeometry().isEmpty() )
    1071                 :            :       {
    1072                 :            :         // apply feature clipping from context to the rendered geometry only -- just like the render time simplification,
    1073                 :            :         // we should NEVER apply this to the geometry attached to the feature itself. Doing so causes issues with certain
    1074                 :            :         // renderer settings, e.g. if polygons are being rendered using a rule based renderer based on the feature's area,
    1075                 :            :         // then we need to ensure that the original feature area is used instead of the clipped area..
    1076                 :          0 :         QgsGeos geos( processedGeometry );
    1077                 :          0 :         std::unique_ptr< QgsAbstractGeometry > clippedGeom( geos.intersection( context.featureClipGeometry().constGet() ) );
    1078                 :          0 :         if ( clippedGeom )
    1079                 :            :         {
    1080                 :          0 :           temporaryGeometryContainer.set( clippedGeom.release() );
    1081                 :          0 :           processedGeometry = temporaryGeometryContainer.constGet();
    1082                 :          0 :         }
    1083                 :          0 :       }
    1084                 :          0 :     }
    1085                 :            :     else
    1086                 :            :     {
    1087                 :            :       // for multipart geometries, the processing is deferred till we're rendering the actual part...
    1088                 :          0 :       processedGeometry = part;
    1089                 :            :     }
    1090                 :            : 
    1091                 :          0 :     if ( !processedGeometry )
    1092                 :            :     {
    1093                 :            :       // shouldn't happen!
    1094                 :          0 :       QgsDebugMsg( QStringLiteral( "No processed geometry to render for part!" ) );
    1095                 :          0 :       return;
    1096                 :            :     }
    1097                 :            : 
    1098                 :          0 :     switch ( QgsWkbTypes::flatType( processedGeometry->wkbType() ) )
    1099                 :            :     {
    1100                 :            :       case QgsWkbTypes::Point:
    1101                 :            :       {
    1102                 :          0 :         if ( mType != QgsSymbol::Marker )
    1103                 :            :         {
    1104                 :          0 :           QgsDebugMsgLevel( QStringLiteral( "point can be drawn only with marker symbol!" ), 2 );
    1105                 :          0 :           break;
    1106                 :            :         }
    1107                 :            : 
    1108                 :          0 :         PointInfo info;
    1109                 :          0 :         info.originalGeometry = qgsgeometry_cast< const QgsPoint * >( part );
    1110                 :          0 :         info.renderPoint = _getPoint( context, *info.originalGeometry );
    1111                 :          0 :         pointsToRender << info;
    1112                 :          0 :         break;
    1113                 :            :       }
    1114                 :            : 
    1115                 :            :       case QgsWkbTypes::LineString:
    1116                 :            :       {
    1117                 :          0 :         if ( mType != QgsSymbol::Line )
    1118                 :            :         {
    1119                 :          0 :           QgsDebugMsgLevel( QStringLiteral( "linestring can be drawn only with line symbol!" ), 2 );
    1120                 :          0 :           break;
    1121                 :            :         }
    1122                 :            : 
    1123                 :          0 :         LineInfo info;
    1124                 :          0 :         info.originalGeometry = qgsgeometry_cast<const QgsCurve *>( part );
    1125                 :          0 :         info.renderLine = _getLineString( context, *qgsgeometry_cast<const QgsCurve *>( processedGeometry ), clippingEnabled );
    1126                 :          0 :         linesToRender << info;
    1127                 :            :         break;
    1128                 :          0 :       }
    1129                 :            : 
    1130                 :            :       case QgsWkbTypes::Polygon:
    1131                 :            :       case QgsWkbTypes::Triangle:
    1132                 :            :       {
    1133                 :          0 :         QPolygonF pts;
    1134                 :          0 :         if ( mType != QgsSymbol::Fill )
    1135                 :            :         {
    1136                 :          0 :           QgsDebugMsgLevel( QStringLiteral( "polygon can be drawn only with fill symbol!" ), 2 );
    1137                 :          0 :           break;
    1138                 :            :         }
    1139                 :            : 
    1140                 :          0 :         PolygonInfo info;
    1141                 :          0 :         info.originalGeometry = qgsgeometry_cast<const QgsCurvePolygon *>( part );
    1142                 :          0 :         info.originalPartIndex = partIndex;
    1143                 :          0 :         if ( !qgsgeometry_cast<const QgsPolygon *>( processedGeometry )->exteriorRing() )
    1144                 :            :         {
    1145                 :          0 :           QgsDebugMsg( QStringLiteral( "cannot render polygon with no exterior ring" ) );
    1146                 :          0 :           break;
    1147                 :            :         }
    1148                 :            : 
    1149                 :          0 :         _getPolygon( info.renderExterior, info.renderRings, context, *qgsgeometry_cast<const QgsPolygon *>( processedGeometry ), clippingEnabled, mForceRHR );
    1150                 :          0 :         polygonsToRender << info;
    1151                 :          0 :         break;
    1152                 :          0 :       }
    1153                 :            : 
    1154                 :            :       case QgsWkbTypes::MultiPoint:
    1155                 :            :       {
    1156                 :          0 :         const QgsMultiPoint *mp = qgsgeometry_cast< const QgsMultiPoint * >( part );
    1157                 :          0 :         markers.reserve( mp->numGeometries() );
    1158                 :          0 :       }
    1159                 :            :       FALLTHROUGH
    1160                 :            :       case QgsWkbTypes::MultiCurve:
    1161                 :            :       case QgsWkbTypes::MultiLineString:
    1162                 :            :       case QgsWkbTypes::GeometryCollection:
    1163                 :            :       {
    1164                 :          0 :         const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( part );
    1165                 :            : 
    1166                 :          0 :         const unsigned int num = geomCollection->numGeometries();
    1167                 :          0 :         for ( unsigned int i = 0; i < num; ++i )
    1168                 :            :         {
    1169                 :          0 :           if ( context.renderingStopped() )
    1170                 :          0 :             break;
    1171                 :            : 
    1172                 :          0 :           getPartGeometry( geomCollection->geometryN( i ), i );
    1173                 :          0 :         }
    1174                 :          0 :         break;
    1175                 :            :       }
    1176                 :            : 
    1177                 :            :       case QgsWkbTypes::MultiSurface:
    1178                 :            :       case QgsWkbTypes::MultiPolygon:
    1179                 :            :       {
    1180                 :          0 :         if ( mType != QgsSymbol::Fill )
    1181                 :            :         {
    1182                 :          0 :           QgsDebugMsgLevel( QStringLiteral( "multi-polygon can be drawn only with fill symbol!" ), 2 );
    1183                 :          0 :           break;
    1184                 :            :         }
    1185                 :            : 
    1186                 :          0 :         QPolygonF pts;
    1187                 :            : 
    1188                 :          0 :         const QgsGeometryCollection *geomCollection = dynamic_cast<const QgsGeometryCollection *>( part );
    1189                 :          0 :         const unsigned int num = geomCollection->numGeometries();
    1190                 :            : 
    1191                 :            :         // Sort components by approximate area (probably a bit faster than using
    1192                 :            :         // area() )
    1193                 :          0 :         std::map<double, QList<unsigned int> > thisAreaToPartNum;
    1194                 :          0 :         for ( unsigned int i = 0; i < num; ++i )
    1195                 :            :         {
    1196                 :          0 :           const QgsRectangle r( geomCollection->geometryN( i )->boundingBox() );
    1197                 :          0 :           thisAreaToPartNum[ r.width() * r.height()] << i;
    1198                 :          0 :         }
    1199                 :            : 
    1200                 :            :         // Draw starting with larger parts down to smaller parts, so that in
    1201                 :            :         // case of a part being incorrectly inside another part, it is drawn
    1202                 :            :         // on top of it (#15419)
    1203                 :          0 :         std::map<double, QList<unsigned int> >::const_reverse_iterator iter = thisAreaToPartNum.rbegin();
    1204                 :          0 :         for ( ; iter != thisAreaToPartNum.rend(); ++iter )
    1205                 :            :         {
    1206                 :          0 :           const QList<unsigned int> &listPartIndex = iter->second;
    1207                 :          0 :           for ( int idx = 0; idx < listPartIndex.size(); ++idx )
    1208                 :            :           {
    1209                 :          0 :             const unsigned i = listPartIndex[idx];
    1210                 :          0 :             getPartGeometry( geomCollection->geometryN( i ), i );
    1211                 :          0 :           }
    1212                 :          0 :         }
    1213                 :            :         break;
    1214                 :          0 :       }
    1215                 :            : 
    1216                 :            :       default:
    1217                 :          0 :         QgsDebugMsg( QStringLiteral( "feature %1: unsupported wkb type %2/%3 for rendering" )
    1218                 :            :                      .arg( feature.id() )
    1219                 :            :                      .arg( QgsWkbTypes::displayString( part->wkbType() ) )
    1220                 :            :                      .arg( part->wkbType(), 0, 16 ) );
    1221                 :          0 :     }
    1222                 :          0 :   };
    1223                 :            : 
    1224                 :          0 :   getPartGeometry( geom.constGet(), 0 );
    1225                 :            : 
    1226                 :            :   // step 2 - determine which layers to render
    1227                 :          0 :   std::vector< int > layers;
    1228                 :          0 :   if ( layer == -1 )
    1229                 :            :   {
    1230                 :          0 :     layers.reserve( mLayers.count() );
    1231                 :          0 :     for ( int i = 0; i < mLayers.count(); ++i )
    1232                 :          0 :       layers.emplace_back( i );
    1233                 :          0 :   }
    1234                 :            :   else
    1235                 :            :   {
    1236                 :          0 :     layers.emplace_back( layer );
    1237                 :            :   }
    1238                 :            : 
    1239                 :            :   // step 3 - render these geometries using the desired symbol layers.
    1240                 :            : 
    1241                 :          0 :   if ( needsExpressionContext )
    1242                 :          0 :     mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_count" ), mLayers.count(), true ) );
    1243                 :            : 
    1244                 :          0 :   for ( const int symbolLayerIndex : layers )
    1245                 :            :   {
    1246                 :          0 :     QgsSymbolLayer *symbolLayer = mLayers.value( symbolLayerIndex );
    1247                 :          0 :     if ( !symbolLayer || !symbolLayer->enabled() )
    1248                 :          0 :       continue;
    1249                 :            : 
    1250                 :          0 :     if ( needsExpressionContext )
    1251                 :          0 :       mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_index" ), symbolLayerIndex + 1, true ) );
    1252                 :            : 
    1253                 :          0 :     symbolLayer->startFeatureRender( feature, context );
    1254                 :            : 
    1255                 :          0 :     switch ( mType )
    1256                 :            :     {
    1257                 :            :       case QgsSymbol::Marker:
    1258                 :            :       {
    1259                 :          0 :         int geometryPartNumber = 0;
    1260                 :          0 :         for ( const PointInfo &point : std::as_const( pointsToRender ) )
    1261                 :            :         {
    1262                 :          0 :           if ( context.renderingStopped() )
    1263                 :          0 :             break;
    1264                 :            : 
    1265                 :          0 :           mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
    1266                 :          0 :           if ( needsExpressionContext )
    1267                 :          0 :             mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
    1268                 :            : 
    1269                 :          0 :           static_cast<QgsMarkerSymbol *>( this )->renderPoint( point.renderPoint, &feature, context, symbolLayerIndex, selected );
    1270                 :          0 :           geometryPartNumber++;
    1271                 :            :         }
    1272                 :            : 
    1273                 :          0 :         break;
    1274                 :            :       }
    1275                 :            : 
    1276                 :            :       case QgsSymbol::Line:
    1277                 :            :       {
    1278                 :          0 :         if ( linesToRender.empty() )
    1279                 :          0 :           break;
    1280                 :            : 
    1281                 :          0 :         int geometryPartNumber = 0;
    1282                 :          0 :         for ( const LineInfo &line : std::as_const( linesToRender ) )
    1283                 :            :         {
    1284                 :          0 :           if ( context.renderingStopped() )
    1285                 :          0 :             break;
    1286                 :            : 
    1287                 :          0 :           mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
    1288                 :          0 :           if ( needsExpressionContext )
    1289                 :          0 :             mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
    1290                 :            : 
    1291                 :          0 :           context.setGeometry( line.originalGeometry );
    1292                 :          0 :           static_cast<QgsLineSymbol *>( this )->renderPolyline( line.renderLine, &feature, context, symbolLayerIndex, selected );
    1293                 :          0 :           geometryPartNumber++;
    1294                 :            :         }
    1295                 :          0 :         break;
    1296                 :            :       }
    1297                 :            : 
    1298                 :            :       case QgsSymbol::Fill:
    1299                 :            :       {
    1300                 :          0 :         for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
    1301                 :            :         {
    1302                 :          0 :           if ( context.renderingStopped() )
    1303                 :          0 :             break;
    1304                 :            : 
    1305                 :          0 :           mSymbolRenderContext->setGeometryPartNum( info.originalPartIndex + 1 );
    1306                 :          0 :           if ( needsExpressionContext )
    1307                 :          0 :             mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, info.originalPartIndex + 1, true ) );
    1308                 :            : 
    1309                 :          0 :           context.setGeometry( info.originalGeometry );
    1310                 :          0 :           static_cast<QgsFillSymbol *>( this )->renderPolygon( info.renderExterior, ( !info.renderRings.isEmpty() ? &info.renderRings : nullptr ), &feature, context, symbolLayerIndex, selected );
    1311                 :            :         }
    1312                 :            : 
    1313                 :          0 :         break;
    1314                 :            :       }
    1315                 :            : 
    1316                 :            :       case QgsSymbol::Hybrid:
    1317                 :          0 :         break;
    1318                 :            :     }
    1319                 :            : 
    1320                 :          0 :     symbolLayer->stopFeatureRender( feature, context );
    1321                 :            :   }
    1322                 :            : 
    1323                 :            :   // step 4 - handle post processing steps
    1324                 :          0 :   switch ( mType )
    1325                 :            :   {
    1326                 :            :     case QgsSymbol::Marker:
    1327                 :            :     {
    1328                 :          0 :       markers.reserve( pointsToRender.size() );
    1329                 :          0 :       for ( const PointInfo &info : std::as_const( pointsToRender ) )
    1330                 :            :       {
    1331                 :          0 :         if ( context.hasRenderedFeatureHandlers() || context.testFlag( QgsRenderContext::DrawSymbolBounds ) )
    1332                 :            :         {
    1333                 :          0 :           const QRectF bounds = static_cast<QgsMarkerSymbol *>( this )->bounds( info.renderPoint, context, feature );
    1334                 :          0 :           if ( context.hasRenderedFeatureHandlers() )
    1335                 :            :           {
    1336                 :          0 :             renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromRect( bounds )
    1337                 :          0 :                                  : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromRect( QgsRectangle( bounds ) ) << renderedBoundsGeom );
    1338                 :          0 :           }
    1339                 :          0 :           if ( context.testFlag( QgsRenderContext::DrawSymbolBounds ) )
    1340                 :            :           {
    1341                 :            :             //draw debugging rect
    1342                 :          0 :             context.painter()->setPen( Qt::red );
    1343                 :          0 :             context.painter()->setBrush( QColor( 255, 0, 0, 100 ) );
    1344                 :          0 :             context.painter()->drawRect( bounds );
    1345                 :          0 :           }
    1346                 :          0 :         }
    1347                 :            : 
    1348                 :          0 :         if ( drawVertexMarker && !usingSegmentizedGeometry )
    1349                 :            :         {
    1350                 :          0 :           markers.append( info.renderPoint );
    1351                 :          0 :         }
    1352                 :            :       }
    1353                 :          0 :       break;
    1354                 :            :     }
    1355                 :            : 
    1356                 :            :     case QgsSymbol::Line:
    1357                 :            :     {
    1358                 :          0 :       for ( const LineInfo &info : std::as_const( linesToRender ) )
    1359                 :            :       {
    1360                 :          0 :         if ( context.hasRenderedFeatureHandlers() && !info.renderLine.empty() )
    1361                 :            :         {
    1362                 :          0 :           renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderLine )
    1363                 :          0 :                                : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderLine ) << renderedBoundsGeom );
    1364                 :          0 :         }
    1365                 :            : 
    1366                 :          0 :         if ( drawVertexMarker && !usingSegmentizedGeometry )
    1367                 :            :         {
    1368                 :          0 :           markers << info.renderLine;
    1369                 :          0 :         }
    1370                 :            :       }
    1371                 :          0 :       break;
    1372                 :            :     }
    1373                 :            : 
    1374                 :            :     case QgsSymbol::Fill:
    1375                 :            :     {
    1376                 :          0 :       int i = 0;
    1377                 :          0 :       for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
    1378                 :            :       {
    1379                 :          0 :         if ( context.hasRenderedFeatureHandlers() && !info.renderExterior.empty() )
    1380                 :            :         {
    1381                 :          0 :           renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderExterior )
    1382                 :          0 :                                : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderExterior ) << renderedBoundsGeom );
    1383                 :            :           // TODO: consider holes?
    1384                 :          0 :         }
    1385                 :            : 
    1386                 :          0 :         if ( drawVertexMarker && !usingSegmentizedGeometry )
    1387                 :            :         {
    1388                 :          0 :           markers << info.renderExterior;
    1389                 :            : 
    1390                 :          0 :           for ( const QPolygonF &hole : info.renderRings )
    1391                 :            :           {
    1392                 :          0 :             markers << hole;
    1393                 :            :           }
    1394                 :          0 :         }
    1395                 :          0 :         i++;
    1396                 :            :       }
    1397                 :          0 :       break;
    1398                 :            :     }
    1399                 :            : 
    1400                 :            :     case QgsSymbol::Hybrid:
    1401                 :          0 :       break;
    1402                 :            :   }
    1403                 :            : 
    1404                 :          0 :   if ( context.hasRenderedFeatureHandlers() && !renderedBoundsGeom.isNull() )
    1405                 :            :   {
    1406                 :          0 :     QgsRenderedFeatureHandlerInterface::RenderedFeatureContext featureContext( context );
    1407                 :          0 :     const QList< QgsRenderedFeatureHandlerInterface * > handlers = context.renderedFeatureHandlers();
    1408                 :          0 :     for ( QgsRenderedFeatureHandlerInterface *handler : handlers )
    1409                 :          0 :       handler->handleRenderedFeature( feature, renderedBoundsGeom, featureContext );
    1410                 :          0 :   }
    1411                 :            : 
    1412                 :          0 :   if ( drawVertexMarker )
    1413                 :            :   {
    1414                 :          0 :     if ( !markers.isEmpty() && !context.renderingStopped() )
    1415                 :            :     {
    1416                 :          0 :       const auto constMarkers = markers;
    1417                 :          0 :       for ( QPointF marker : constMarkers )
    1418                 :            :       {
    1419                 :          0 :         renderVertexMarker( marker, context, currentVertexMarkerType, currentVertexMarkerSize );
    1420                 :            :       }
    1421                 :          0 :     }
    1422                 :            :     else
    1423                 :            :     {
    1424                 :          0 :       QgsCoordinateTransform ct = context.coordinateTransform();
    1425                 :          0 :       const QgsMapToPixel &mtp = context.mapToPixel();
    1426                 :            : 
    1427                 :          0 :       QgsPoint vertexPoint;
    1428                 :          0 :       QgsVertexId vertexId;
    1429                 :            :       double x, y, z;
    1430                 :          0 :       QPointF mapPoint;
    1431                 :          0 :       while ( geom.constGet()->nextVertex( vertexId, vertexPoint ) )
    1432                 :            :       {
    1433                 :            :         //transform
    1434                 :          0 :         x = vertexPoint.x();
    1435                 :          0 :         y = vertexPoint.y();
    1436                 :          0 :         z = 0.0;
    1437                 :          0 :         if ( ct.isValid() )
    1438                 :            :         {
    1439                 :          0 :           ct.transformInPlace( x, y, z );
    1440                 :          0 :         }
    1441                 :          0 :         mapPoint.setX( x );
    1442                 :          0 :         mapPoint.setY( y );
    1443                 :          0 :         mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
    1444                 :          0 :         renderVertexMarker( mapPoint, context, currentVertexMarkerType, currentVertexMarkerSize );
    1445                 :            :       }
    1446                 :          0 :     }
    1447                 :          0 :   }
    1448                 :          0 : }
    1449                 :            : 
    1450                 :          0 : QgsSymbolRenderContext *QgsSymbol::symbolRenderContext()
    1451                 :            : {
    1452                 :          0 :   return mSymbolRenderContext.get();
    1453                 :            : }
    1454                 :            : 
    1455                 :          0 : void QgsSymbol::renderVertexMarker( QPointF pt, QgsRenderContext &context, int currentVertexMarkerType, double currentVertexMarkerSize )
    1456                 :            : {
    1457                 :          0 :   int markerSize = context.convertToPainterUnits( currentVertexMarkerSize, QgsUnitTypes::RenderMillimeters );
    1458                 :          0 :   QgsSymbolLayerUtils::drawVertexMarker( pt.x(), pt.y(), *context.painter(), static_cast< QgsSymbolLayerUtils::VertexMarkerType >( currentVertexMarkerType ), markerSize );
    1459                 :          0 : }
    1460                 :            : 
    1461                 :        580 : void QgsSymbol::initPropertyDefinitions()
    1462                 :            : {
    1463                 :        580 :   if ( !sPropertyDefinitions.isEmpty() )
    1464                 :        576 :     return;
    1465                 :            : 
    1466                 :          8 :   QString origin = QStringLiteral( "symbol" );
    1467                 :            : 
    1468                 :          4 :   sPropertyDefinitions = QgsPropertiesDefinition
    1469                 :          8 :   {
    1470                 :          4 :     { QgsSymbol::PropertyOpacity, QgsPropertyDefinition( "alpha", QObject::tr( "Opacity" ), QgsPropertyDefinition::Opacity, origin )},
    1471                 :            :   };
    1472                 :        580 : }
    1473                 :            : 
    1474                 :          0 : void QgsSymbol::startFeatureRender( const QgsFeature &feature, QgsRenderContext &context, const int layer )
    1475                 :            : {
    1476                 :          0 :   if ( layer != -1 )
    1477                 :            :   {
    1478                 :          0 :     QgsSymbolLayer *symbolLayer = mLayers.value( layer );
    1479                 :          0 :     if ( symbolLayer && symbolLayer->enabled() )
    1480                 :            :     {
    1481                 :          0 :       symbolLayer->startFeatureRender( feature, context );
    1482                 :          0 :     }
    1483                 :          0 :     return;
    1484                 :            :   }
    1485                 :            :   else
    1486                 :            :   {
    1487                 :          0 :     const QList< QgsSymbolLayer * > layers = mLayers;
    1488                 :          0 :     for ( QgsSymbolLayer *symbolLayer : layers )
    1489                 :            :     {
    1490                 :          0 :       if ( !symbolLayer->enabled() )
    1491                 :          0 :         continue;
    1492                 :            : 
    1493                 :          0 :       symbolLayer->startFeatureRender( feature, context );
    1494                 :            :     }
    1495                 :          0 :   }
    1496                 :          0 : }
    1497                 :            : 
    1498                 :          0 : void QgsSymbol::stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context, int layer )
    1499                 :            : {
    1500                 :          0 :   if ( layer != -1 )
    1501                 :            :   {
    1502                 :          0 :     QgsSymbolLayer *symbolLayer = mLayers.value( layer );
    1503                 :          0 :     if ( symbolLayer && symbolLayer->enabled() )
    1504                 :            :     {
    1505                 :          0 :       symbolLayer->stopFeatureRender( feature, context );
    1506                 :          0 :     }
    1507                 :          0 :     return;
    1508                 :            :   }
    1509                 :            :   else
    1510                 :            :   {
    1511                 :          0 :     const QList< QgsSymbolLayer * > layers = mLayers;
    1512                 :          0 :     for ( QgsSymbolLayer *symbolLayer : layers )
    1513                 :            :     {
    1514                 :          0 :       if ( !symbolLayer->enabled() )
    1515                 :          0 :         continue;
    1516                 :            : 
    1517                 :          0 :       symbolLayer->stopFeatureRender( feature, context );
    1518                 :            :     }
    1519                 :          0 :   }
    1520                 :          0 : }
    1521                 :            : 
    1522                 :            : ////////////////////
    1523                 :            : 
    1524                 :            : 
    1525                 :          0 : QgsSymbolRenderContext::QgsSymbolRenderContext( QgsRenderContext &c, QgsUnitTypes::RenderUnit u, qreal opacity, bool selected, QgsSymbol::RenderHints renderHints, const QgsFeature *f, const QgsFields &fields, const QgsMapUnitScale &mapUnitScale )
    1526                 :          0 :   : mRenderContext( c )
    1527                 :          0 :   , mOutputUnit( u )
    1528                 :          0 :   , mMapUnitScale( mapUnitScale )
    1529                 :          0 :   , mOpacity( opacity )
    1530                 :          0 :   , mSelected( selected )
    1531                 :          0 :   , mRenderHints( renderHints )
    1532                 :          0 :   , mFeature( f )
    1533                 :          0 :   , mFields( fields )
    1534                 :          0 :   , mGeometryPartCount( 0 )
    1535                 :          0 :   , mGeometryPartNum( 0 )
    1536                 :            : {
    1537                 :          0 : }
    1538                 :            : 
    1539                 :          0 : QgsSymbolRenderContext::~QgsSymbolRenderContext() = default;
    1540                 :            : 
    1541                 :          0 : void QgsSymbolRenderContext::setOriginalValueVariable( const QVariant &value )
    1542                 :            : {
    1543                 :          0 :   mRenderContext.expressionContext().setOriginalValueVariable( value );
    1544                 :          0 : }
    1545                 :            : 
    1546                 :          0 : double QgsSymbolRenderContext::outputLineWidth( double width ) const
    1547                 :            : {
    1548                 :          0 :   return mRenderContext.convertToPainterUnits( width, mOutputUnit, mMapUnitScale );
    1549                 :            : }
    1550                 :            : 
    1551                 :          0 : double QgsSymbolRenderContext::outputPixelSize( double size ) const
    1552                 :            : {
    1553                 :          0 :   return mRenderContext.convertToPainterUnits( size, mOutputUnit, mMapUnitScale );
    1554                 :            : }
    1555                 :            : 
    1556                 :            : // cppcheck-suppress operatorEqVarError
    1557                 :          0 : QgsSymbolRenderContext &QgsSymbolRenderContext::operator=( const QgsSymbolRenderContext & )
    1558                 :            : {
    1559                 :            :   // This is just a dummy implementation of assignment.
    1560                 :            :   // sip 4.7 generates a piece of code that needs this function to exist.
    1561                 :            :   // It's not generated automatically by the compiler because of
    1562                 :            :   // mRenderContext member which is a reference (and thus can't be changed).
    1563                 :            :   Q_ASSERT( false );
    1564                 :          0 :   return *this;
    1565                 :            : }
    1566                 :            : 
    1567                 :          0 : QgsExpressionContextScope *QgsSymbolRenderContext::expressionContextScope()
    1568                 :            : {
    1569                 :          0 :   return mExpressionContextScope.get();
    1570                 :            : }
    1571                 :            : 
    1572                 :          0 : void QgsSymbolRenderContext::setExpressionContextScope( QgsExpressionContextScope *contextScope )
    1573                 :            : {
    1574                 :          0 :   mExpressionContextScope.reset( contextScope );
    1575                 :          0 : }
    1576                 :            : 
    1577                 :          0 : const QgsLegendPatchShape *QgsSymbolRenderContext::patchShape() const
    1578                 :            : {
    1579                 :          0 :   return mPatchShape.get();
    1580                 :            : }
    1581                 :            : 
    1582                 :          0 : void QgsSymbolRenderContext::setPatchShape( const QgsLegendPatchShape &patchShape )
    1583                 :            : {
    1584                 :          0 :   mPatchShape.reset( new QgsLegendPatchShape( patchShape ) );
    1585                 :          0 : }
    1586                 :            : 
    1587                 :            : ///////////////////
    1588                 :            : 
    1589                 :          0 : QgsMarkerSymbol *QgsMarkerSymbol::createSimple( const QVariantMap &properties )
    1590                 :            : {
    1591                 :          0 :   QgsSymbolLayer *sl = QgsSimpleMarkerSymbolLayer::create( properties );
    1592                 :          0 :   if ( !sl )
    1593                 :          0 :     return nullptr;
    1594                 :            : 
    1595                 :          0 :   QgsSymbolLayerList layers;
    1596                 :          0 :   layers.append( sl );
    1597                 :          0 :   return new QgsMarkerSymbol( layers );
    1598                 :          0 : }
    1599                 :            : 
    1600                 :          0 : QgsLineSymbol *QgsLineSymbol::createSimple( const QVariantMap &properties )
    1601                 :            : {
    1602                 :          0 :   QgsSymbolLayer *sl = QgsSimpleLineSymbolLayer::create( properties );
    1603                 :          0 :   if ( !sl )
    1604                 :          0 :     return nullptr;
    1605                 :            : 
    1606                 :          0 :   QgsSymbolLayerList layers;
    1607                 :          0 :   layers.append( sl );
    1608                 :          0 :   return new QgsLineSymbol( layers );
    1609                 :          0 : }
    1610                 :            : 
    1611                 :         10 : QgsFillSymbol *QgsFillSymbol::createSimple( const QVariantMap &properties )
    1612                 :            : {
    1613                 :         10 :   QgsSymbolLayer *sl = QgsSimpleFillSymbolLayer::create( properties );
    1614                 :         10 :   if ( !sl )
    1615                 :          0 :     return nullptr;
    1616                 :            : 
    1617                 :         10 :   QgsSymbolLayerList layers;
    1618                 :         10 :   layers.append( sl );
    1619                 :         10 :   return new QgsFillSymbol( layers );
    1620                 :         10 : }
    1621                 :            : 
    1622                 :            : ///////////////////
    1623                 :            : 
    1624                 :        327 : QgsMarkerSymbol::QgsMarkerSymbol( const QgsSymbolLayerList &layers )
    1625                 :        327 :   : QgsSymbol( Marker, layers )
    1626                 :        654 : {
    1627                 :        327 :   if ( mLayers.isEmpty() )
    1628                 :         97 :     mLayers.append( new QgsSimpleMarkerSymbolLayer() );
    1629                 :        327 : }
    1630                 :            : 
    1631                 :          0 : void QgsMarkerSymbol::setAngle( double symbolAngle )
    1632                 :            : {
    1633                 :          0 :   double origAngle = angle();
    1634                 :          0 :   double angleDiff = symbolAngle - origAngle;
    1635                 :          0 :   const auto constMLayers = mLayers;
    1636                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    1637                 :            :   {
    1638                 :          0 :     QgsMarkerSymbolLayer *markerLayer = dynamic_cast<QgsMarkerSymbolLayer *>( layer );
    1639                 :          0 :     if ( markerLayer )
    1640                 :          0 :       markerLayer->setAngle( markerLayer->angle() + angleDiff );
    1641                 :            :   }
    1642                 :          0 : }
    1643                 :            : 
    1644                 :          0 : double QgsMarkerSymbol::angle() const
    1645                 :            : {
    1646                 :          0 :   for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
    1647                 :            :   {
    1648                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1649                 :          0 :       continue;
    1650                 :          0 :     const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
    1651                 :          0 :     return markerLayer->angle();
    1652                 :            :   }
    1653                 :          0 :   return 0;
    1654                 :          0 : }
    1655                 :            : 
    1656                 :          0 : void QgsMarkerSymbol::setLineAngle( double lineAng )
    1657                 :            : {
    1658                 :          0 :   const auto constMLayers = mLayers;
    1659                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    1660                 :            :   {
    1661                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1662                 :          0 :       continue;
    1663                 :          0 :     QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
    1664                 :          0 :     markerLayer->setLineAngle( lineAng );
    1665                 :            :   }
    1666                 :          0 : }
    1667                 :            : 
    1668                 :          0 : void QgsMarkerSymbol::setDataDefinedAngle( const QgsProperty &property )
    1669                 :            : {
    1670                 :          0 :   const double symbolRotation = angle();
    1671                 :            : 
    1672                 :            : 
    1673                 :          0 :   for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
    1674                 :            :   {
    1675                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1676                 :          0 :       continue;
    1677                 :          0 :     const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
    1678                 :          0 :     if ( !property )
    1679                 :            :     {
    1680                 :          0 :       layer->setDataDefinedProperty( QgsSymbolLayer::PropertyAngle, QgsProperty() );
    1681                 :          0 :     }
    1682                 :            :     else
    1683                 :            :     {
    1684                 :          0 :       if ( qgsDoubleNear( markerLayer->angle(), symbolRotation ) )
    1685                 :            :       {
    1686                 :          0 :         layer->setDataDefinedProperty( QgsSymbolLayer::PropertyAngle, property );
    1687                 :          0 :       }
    1688                 :            :       else
    1689                 :            :       {
    1690                 :          0 :         QgsProperty rotatedDD = rotateWholeSymbol( markerLayer->angle() - symbolRotation, property );
    1691                 :          0 :         layer->setDataDefinedProperty( QgsSymbolLayer::PropertyAngle, rotatedDD );
    1692                 :          0 :       }
    1693                 :            :     }
    1694                 :            :   }
    1695                 :          0 : }
    1696                 :            : 
    1697                 :          0 : QgsProperty QgsMarkerSymbol::dataDefinedAngle() const
    1698                 :            : {
    1699                 :          0 :   const double symbolRotation = angle();
    1700                 :          0 :   QgsProperty symbolDD;
    1701                 :            : 
    1702                 :            :   // find the base of the "en masse" pattern
    1703                 :          0 :   const auto layers = mLayers;
    1704                 :          0 :   for ( QgsSymbolLayer *layer : layers )
    1705                 :            :   {
    1706                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1707                 :          0 :       continue;
    1708                 :          0 :     const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
    1709                 :          0 :     if ( qgsDoubleNear( markerLayer->angle(), symbolRotation ) && markerLayer->dataDefinedProperties().isActive( QgsSymbolLayer::PropertyAngle ) )
    1710                 :            :     {
    1711                 :          0 :       symbolDD = markerLayer->dataDefinedProperties().property( QgsSymbolLayer::PropertyAngle );
    1712                 :          0 :       break;
    1713                 :            :     }
    1714                 :            :   }
    1715                 :            : 
    1716                 :          0 :   if ( !symbolDD )
    1717                 :          0 :     return QgsProperty();
    1718                 :            : 
    1719                 :            :   // check that all layer's angle expressions match the "en masse" pattern
    1720                 :          0 :   for ( QgsSymbolLayer *layer : layers )
    1721                 :            :   {
    1722                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1723                 :          0 :       continue;
    1724                 :          0 :     const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
    1725                 :            : 
    1726                 :          0 :     QgsProperty layerAngleDD = markerLayer->dataDefinedProperties().property( QgsSymbolLayer::PropertyAngle );
    1727                 :            : 
    1728                 :          0 :     if ( qgsDoubleNear( markerLayer->angle(), symbolRotation ) )
    1729                 :            :     {
    1730                 :          0 :       if ( !layerAngleDD || layerAngleDD != symbolDD )
    1731                 :          0 :         return QgsProperty();
    1732                 :          0 :     }
    1733                 :            :     else
    1734                 :            :     {
    1735                 :          0 :       QgsProperty rotatedDD( rotateWholeSymbol( markerLayer->angle() - symbolRotation, symbolDD ) );
    1736                 :          0 :       if ( !layerAngleDD || layerAngleDD != rotatedDD )
    1737                 :          0 :         return QgsProperty();
    1738                 :          0 :     }
    1739                 :          0 :   }
    1740                 :          0 :   return symbolDD;
    1741                 :          0 : }
    1742                 :            : 
    1743                 :            : 
    1744                 :          0 : void QgsMarkerSymbol::setSize( double s )
    1745                 :            : {
    1746                 :          0 :   double origSize = size();
    1747                 :            : 
    1748                 :          0 :   const auto constMLayers = mLayers;
    1749                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    1750                 :            :   {
    1751                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1752                 :          0 :       continue;
    1753                 :          0 :     QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
    1754                 :          0 :     if ( qgsDoubleNear( markerLayer->size(), origSize ) )
    1755                 :          0 :       markerLayer->setSize( s );
    1756                 :          0 :     else if ( !qgsDoubleNear( origSize, 0.0 ) )
    1757                 :            :     {
    1758                 :            :       // proportionally scale size
    1759                 :          0 :       markerLayer->setSize( markerLayer->size() * s / origSize );
    1760                 :          0 :     }
    1761                 :            :     // also scale offset to maintain relative position
    1762                 :          0 :     if ( !qgsDoubleNear( origSize, 0.0 ) && ( !qgsDoubleNear( markerLayer->offset().x(), 0.0 ) || !qgsDoubleNear( markerLayer->offset().y(), 0.0 ) ) )
    1763                 :          0 :       markerLayer->setOffset( QPointF( markerLayer->offset().x() * s / origSize,
    1764                 :          0 :                                        markerLayer->offset().y() * s / origSize ) );
    1765                 :            :   }
    1766                 :          0 : }
    1767                 :            : 
    1768                 :          0 : double QgsMarkerSymbol::size() const
    1769                 :            : {
    1770                 :            :   // return size of the largest symbol
    1771                 :          0 :   double maxSize = 0;
    1772                 :          0 :   const auto constMLayers = mLayers;
    1773                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    1774                 :            :   {
    1775                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1776                 :          0 :       continue;
    1777                 :          0 :     const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
    1778                 :          0 :     double lsize = markerLayer->size();
    1779                 :          0 :     if ( lsize > maxSize )
    1780                 :          0 :       maxSize = lsize;
    1781                 :            :   }
    1782                 :          0 :   return maxSize;
    1783                 :          0 : }
    1784                 :            : 
    1785                 :          0 : double QgsMarkerSymbol::size( const QgsRenderContext &context ) const
    1786                 :            : {
    1787                 :            :   // return size of the largest symbol
    1788                 :          0 :   double maxSize = 0;
    1789                 :          0 :   for ( QgsSymbolLayer *layer : mLayers )
    1790                 :            :   {
    1791                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1792                 :          0 :       continue;
    1793                 :          0 :     const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
    1794                 :          0 :     const double layerSize = context.convertToPainterUnits( markerLayer->size(), markerLayer->sizeUnit(), markerLayer->sizeMapUnitScale() );
    1795                 :          0 :     maxSize = std::max( maxSize, layerSize );
    1796                 :            :   }
    1797                 :          0 :   return maxSize;
    1798                 :            : }
    1799                 :            : 
    1800                 :          0 : void QgsMarkerSymbol::setSizeUnit( QgsUnitTypes::RenderUnit unit )
    1801                 :            : {
    1802                 :          0 :   const auto constMLayers = mLayers;
    1803                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    1804                 :            :   {
    1805                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1806                 :          0 :       continue;
    1807                 :            : 
    1808                 :          0 :     QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
    1809                 :          0 :     markerLayer->setSizeUnit( unit );
    1810                 :            :   }
    1811                 :          0 : }
    1812                 :            : 
    1813                 :          0 : QgsUnitTypes::RenderUnit QgsMarkerSymbol::sizeUnit() const
    1814                 :            : {
    1815                 :          0 :   bool first = true;
    1816                 :          0 :   QgsUnitTypes::RenderUnit unit = QgsUnitTypes::RenderUnknownUnit;
    1817                 :            : 
    1818                 :          0 :   const auto constMLayers = mLayers;
    1819                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    1820                 :            :   {
    1821                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1822                 :          0 :       continue;
    1823                 :          0 :     const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
    1824                 :            : 
    1825                 :          0 :     if ( first )
    1826                 :          0 :       unit = markerLayer->sizeUnit();
    1827                 :            :     else
    1828                 :            :     {
    1829                 :          0 :       if ( unit != markerLayer->sizeUnit() )
    1830                 :          0 :         return QgsUnitTypes::RenderUnknownUnit;
    1831                 :            :     }
    1832                 :            : 
    1833                 :          0 :     first = false;
    1834                 :            :   }
    1835                 :          0 :   return unit;
    1836                 :          0 : }
    1837                 :            : 
    1838                 :          0 : void QgsMarkerSymbol::setSizeMapUnitScale( const QgsMapUnitScale &scale )
    1839                 :            : {
    1840                 :          0 :   const auto constMLayers = mLayers;
    1841                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    1842                 :            :   {
    1843                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1844                 :          0 :       continue;
    1845                 :            : 
    1846                 :          0 :     QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
    1847                 :          0 :     markerLayer->setSizeMapUnitScale( scale );
    1848                 :            :   }
    1849                 :          0 : }
    1850                 :            : 
    1851                 :          0 : QgsMapUnitScale QgsMarkerSymbol::sizeMapUnitScale() const
    1852                 :            : {
    1853                 :          0 :   const auto constMLayers = mLayers;
    1854                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    1855                 :            :   {
    1856                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1857                 :          0 :       continue;
    1858                 :            : 
    1859                 :          0 :     QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
    1860                 :          0 :     return markerLayer->sizeMapUnitScale();
    1861                 :            :   }
    1862                 :          0 :   return QgsMapUnitScale();
    1863                 :          0 : }
    1864                 :            : 
    1865                 :          0 : void QgsMarkerSymbol::setDataDefinedSize( const QgsProperty &property )
    1866                 :            : {
    1867                 :          0 :   const double symbolSize = size();
    1868                 :            : 
    1869                 :          0 :   const auto constMLayers = mLayers;
    1870                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    1871                 :            :   {
    1872                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1873                 :          0 :       continue;
    1874                 :          0 :     QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
    1875                 :            : 
    1876                 :          0 :     if ( !property )
    1877                 :            :     {
    1878                 :          0 :       markerLayer->setDataDefinedProperty( QgsSymbolLayer::PropertySize, QgsProperty() );
    1879                 :          0 :       markerLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyOffset, QgsProperty() );
    1880                 :          0 :     }
    1881                 :            :     else
    1882                 :            :     {
    1883                 :          0 :       if ( qgsDoubleNear( symbolSize, 0.0 ) || qgsDoubleNear( markerLayer->size(), symbolSize ) )
    1884                 :            :       {
    1885                 :          0 :         markerLayer->setDataDefinedProperty( QgsSymbolLayer::PropertySize, property );
    1886                 :          0 :       }
    1887                 :            :       else
    1888                 :            :       {
    1889                 :          0 :         markerLayer->setDataDefinedProperty( QgsSymbolLayer::PropertySize, scaleWholeSymbol( markerLayer->size() / symbolSize, property ) );
    1890                 :            :       }
    1891                 :            : 
    1892                 :          0 :       if ( !qgsDoubleNear( markerLayer->offset().x(), 0.0 ) || !qgsDoubleNear( markerLayer->offset().y(), 0.0 ) )
    1893                 :            :       {
    1894                 :          0 :         markerLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyOffset, scaleWholeSymbol(
    1895                 :          0 :                                                markerLayer->offset().x() / symbolSize,
    1896                 :          0 :                                                markerLayer->offset().y() / symbolSize, property ) );
    1897                 :          0 :       }
    1898                 :            :     }
    1899                 :            :   }
    1900                 :          0 : }
    1901                 :            : 
    1902                 :          0 : QgsProperty QgsMarkerSymbol::dataDefinedSize() const
    1903                 :            : {
    1904                 :          0 :   const double symbolSize = size();
    1905                 :            : 
    1906                 :          0 :   QgsProperty symbolDD;
    1907                 :            : 
    1908                 :            :   // find the base of the "en masse" pattern
    1909                 :          0 :   const auto layers = mLayers;
    1910                 :          0 :   for ( QgsSymbolLayer *layer : layers )
    1911                 :            :   {
    1912                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1913                 :          0 :       continue;
    1914                 :          0 :     const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
    1915                 :          0 :     if ( qgsDoubleNear( markerLayer->size(), symbolSize ) && markerLayer->dataDefinedProperties().isActive( QgsSymbolLayer::PropertySize ) )
    1916                 :            :     {
    1917                 :          0 :       symbolDD = markerLayer->dataDefinedProperties().property( QgsSymbolLayer::PropertySize );
    1918                 :          0 :       break;
    1919                 :            :     }
    1920                 :            :   }
    1921                 :            : 
    1922                 :          0 :   if ( !symbolDD )
    1923                 :          0 :     return QgsProperty();
    1924                 :            : 
    1925                 :            :   // check that all layers size expressions match the "en masse" pattern
    1926                 :          0 :   for ( QgsSymbolLayer *layer : layers )
    1927                 :            :   {
    1928                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1929                 :          0 :       continue;
    1930                 :          0 :     const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
    1931                 :            : 
    1932                 :          0 :     QgsProperty layerSizeDD = markerLayer->dataDefinedProperties().property( QgsSymbolLayer::PropertySize );
    1933                 :          0 :     QgsProperty layerOffsetDD = markerLayer->dataDefinedProperties().property( QgsSymbolLayer::PropertyOffset );
    1934                 :            : 
    1935                 :          0 :     if ( qgsDoubleNear( markerLayer->size(), symbolSize ) )
    1936                 :            :     {
    1937                 :          0 :       if ( !layerSizeDD || layerSizeDD != symbolDD )
    1938                 :          0 :         return QgsProperty();
    1939                 :          0 :     }
    1940                 :            :     else
    1941                 :            :     {
    1942                 :          0 :       if ( qgsDoubleNear( symbolSize, 0.0 ) )
    1943                 :          0 :         return QgsProperty();
    1944                 :            : 
    1945                 :          0 :       QgsProperty scaledDD( scaleWholeSymbol( markerLayer->size() / symbolSize, symbolDD ) );
    1946                 :          0 :       if ( !layerSizeDD || layerSizeDD != scaledDD )
    1947                 :          0 :         return QgsProperty();
    1948                 :          0 :     }
    1949                 :            : 
    1950                 :          0 :     QgsProperty scaledOffsetDD( scaleWholeSymbol( markerLayer->offset().x() / symbolSize, markerLayer->offset().y() / symbolSize, symbolDD ) );
    1951                 :          0 :     if ( layerOffsetDD && layerOffsetDD != scaledOffsetDD )
    1952                 :          0 :       return QgsProperty();
    1953                 :          0 :   }
    1954                 :            : 
    1955                 :          0 :   return symbolDD;
    1956                 :          0 : }
    1957                 :            : 
    1958                 :          0 : void QgsMarkerSymbol::setScaleMethod( QgsSymbol::ScaleMethod scaleMethod )
    1959                 :            : {
    1960                 :          0 :   const auto constMLayers = mLayers;
    1961                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    1962                 :            :   {
    1963                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1964                 :          0 :       continue;
    1965                 :          0 :     QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
    1966                 :          0 :     markerLayer->setScaleMethod( scaleMethod );
    1967                 :            :   }
    1968                 :          0 : }
    1969                 :            : 
    1970                 :          0 : QgsSymbol::ScaleMethod QgsMarkerSymbol::scaleMethod()
    1971                 :            : {
    1972                 :          0 :   const auto constMLayers = mLayers;
    1973                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    1974                 :            :   {
    1975                 :          0 :     if ( layer->type() != QgsSymbol::Marker )
    1976                 :          0 :       continue;
    1977                 :          0 :     const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
    1978                 :            :     // return scale method of the first symbol layer
    1979                 :          0 :     return markerLayer->scaleMethod();
    1980                 :            :   }
    1981                 :            : 
    1982                 :          0 :   return DEFAULT_SCALE_METHOD;
    1983                 :          0 : }
    1984                 :            : 
    1985                 :          0 : void QgsMarkerSymbol::renderPointUsingLayer( QgsMarkerSymbolLayer *layer, QPointF point, QgsSymbolRenderContext &context )
    1986                 :            : {
    1987                 :            :   static QPointF nullPoint( 0, 0 );
    1988                 :            : 
    1989                 :          0 :   if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
    1990                 :          0 :     return;
    1991                 :            : 
    1992                 :          0 :   QgsPaintEffect *effect = layer->paintEffect();
    1993                 :          0 :   if ( effect && effect->enabled() )
    1994                 :            :   {
    1995                 :          0 :     QgsEffectPainter p( context.renderContext() );
    1996                 :          0 :     p->translate( point );
    1997                 :          0 :     p.setEffect( effect );
    1998                 :          0 :     layer->renderPoint( nullPoint, context );
    1999                 :          0 :   }
    2000                 :            :   else
    2001                 :            :   {
    2002                 :          0 :     layer->renderPoint( point, context );
    2003                 :            :   }
    2004                 :          0 : }
    2005                 :            : 
    2006                 :          0 : void QgsMarkerSymbol::renderPoint( QPointF point, const QgsFeature *f, QgsRenderContext &context, int layerIdx, bool selected )
    2007                 :            : {
    2008                 :          0 :   const double opacity = dataDefinedProperties().valueAsDouble( QgsSymbol::PropertyOpacity, context.expressionContext(), mOpacity * 100 ) * 0.01;
    2009                 :            : 
    2010                 :          0 :   QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, opacity, selected, mRenderHints, f );
    2011                 :          0 :   symbolContext.setGeometryPartCount( symbolRenderContext()->geometryPartCount() );
    2012                 :          0 :   symbolContext.setGeometryPartNum( symbolRenderContext()->geometryPartNum() );
    2013                 :            : 
    2014                 :          0 :   if ( layerIdx != -1 )
    2015                 :            :   {
    2016                 :          0 :     QgsSymbolLayer *symbolLayer = mLayers.value( layerIdx );
    2017                 :          0 :     if ( symbolLayer && symbolLayer->enabled() && context.isSymbolLayerEnabled( symbolLayer ) )
    2018                 :            :     {
    2019                 :          0 :       if ( symbolLayer->type() == QgsSymbol::Marker )
    2020                 :            :       {
    2021                 :          0 :         QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( symbolLayer );
    2022                 :          0 :         renderPointUsingLayer( markerLayer, point, symbolContext );
    2023                 :          0 :       }
    2024                 :            :       else
    2025                 :          0 :         renderUsingLayer( symbolLayer, symbolContext );
    2026                 :          0 :     }
    2027                 :          0 :     return;
    2028                 :            :   }
    2029                 :            : 
    2030                 :            : 
    2031                 :          0 :   for ( QgsSymbolLayer *symbolLayer : std::as_const( mLayers ) )
    2032                 :            :   {
    2033                 :          0 :     if ( context.renderingStopped() )
    2034                 :          0 :       break;
    2035                 :            : 
    2036                 :          0 :     if ( !symbolLayer->enabled() || !context.isSymbolLayerEnabled( symbolLayer ) )
    2037                 :          0 :       continue;
    2038                 :            : 
    2039                 :          0 :     if ( symbolLayer->type() == QgsSymbol::Marker )
    2040                 :            :     {
    2041                 :          0 :       QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( symbolLayer );
    2042                 :          0 :       renderPointUsingLayer( markerLayer, point, symbolContext );
    2043                 :          0 :     }
    2044                 :            :     else
    2045                 :          0 :       renderUsingLayer( symbolLayer, symbolContext );
    2046                 :            :   }
    2047                 :          0 : }
    2048                 :            : 
    2049                 :          0 : QRectF QgsMarkerSymbol::bounds( QPointF point, QgsRenderContext &context, const QgsFeature &feature ) const
    2050                 :            : {
    2051                 :          0 :   QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, mOpacity, false, mRenderHints, &feature, feature.fields() );
    2052                 :            : 
    2053                 :          0 :   QRectF bound;
    2054                 :          0 :   const auto constMLayers = mLayers;
    2055                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    2056                 :            :   {
    2057                 :          0 :     if ( layer->type() == QgsSymbol::Marker )
    2058                 :            :     {
    2059                 :          0 :       QgsMarkerSymbolLayer *symbolLayer = static_cast< QgsMarkerSymbolLayer * >( layer );
    2060                 :          0 :       if ( bound.isNull() )
    2061                 :          0 :         bound = symbolLayer->bounds( point, symbolContext );
    2062                 :            :       else
    2063                 :          0 :         bound = bound.united( symbolLayer->bounds( point, symbolContext ) );
    2064                 :          0 :     }
    2065                 :            :   }
    2066                 :            :   return bound;
    2067                 :          0 : }
    2068                 :            : 
    2069                 :          0 : QgsMarkerSymbol *QgsMarkerSymbol::clone() const
    2070                 :            : {
    2071                 :          0 :   QgsMarkerSymbol *cloneSymbol = new QgsMarkerSymbol( cloneLayers() );
    2072                 :          0 :   cloneSymbol->setOpacity( mOpacity );
    2073                 :            :   Q_NOWARN_DEPRECATED_PUSH
    2074                 :          0 :   cloneSymbol->setLayer( mLayer );
    2075                 :            :   Q_NOWARN_DEPRECATED_POP
    2076                 :          0 :   cloneSymbol->setClipFeaturesToExtent( mClipFeaturesToExtent );
    2077                 :          0 :   cloneSymbol->setForceRHR( mForceRHR );
    2078                 :          0 :   cloneSymbol->setDataDefinedProperties( dataDefinedProperties() );
    2079                 :          0 :   return cloneSymbol;
    2080                 :          0 : }
    2081                 :            : 
    2082                 :            : 
    2083                 :            : ///////////////////
    2084                 :            : // LINE
    2085                 :            : 
    2086                 :        435 : QgsLineSymbol::QgsLineSymbol( const QgsSymbolLayerList &layers )
    2087                 :        435 :   : QgsSymbol( Line, layers )
    2088                 :        870 : {
    2089                 :        435 :   if ( mLayers.isEmpty() )
    2090                 :        195 :     mLayers.append( new QgsSimpleLineSymbolLayer() );
    2091                 :        435 : }
    2092                 :            : 
    2093                 :         70 : void QgsLineSymbol::setWidth( double w )
    2094                 :            : {
    2095                 :         70 :   double origWidth = width();
    2096                 :            : 
    2097                 :         70 :   const auto constMLayers = mLayers;
    2098                 :        140 :   for ( QgsSymbolLayer *layer : constMLayers )
    2099                 :            :   {
    2100                 :         70 :     QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( layer );
    2101                 :            : 
    2102                 :         70 :     if ( lineLayer )
    2103                 :            :     {
    2104                 :         70 :       if ( qgsDoubleNear( lineLayer->width(), origWidth ) )
    2105                 :            :       {
    2106                 :         70 :         lineLayer->setWidth( w );
    2107                 :         70 :       }
    2108                 :          0 :       else if ( !qgsDoubleNear( origWidth, 0.0 ) )
    2109                 :            :       {
    2110                 :            :         // proportionally scale the width
    2111                 :          0 :         lineLayer->setWidth( lineLayer->width() * w / origWidth );
    2112                 :          0 :       }
    2113                 :            :       // also scale offset to maintain relative position
    2114                 :         70 :       if ( !qgsDoubleNear( origWidth, 0.0 ) && !qgsDoubleNear( lineLayer->offset(), 0.0 ) )
    2115                 :          0 :         lineLayer->setOffset( lineLayer->offset() * w / origWidth );
    2116                 :         70 :     }
    2117                 :            :   }
    2118                 :         70 : }
    2119                 :            : 
    2120                 :          0 : void QgsLineSymbol::setWidthUnit( QgsUnitTypes::RenderUnit unit )
    2121                 :            : {
    2122                 :          0 :   const auto constLLayers = mLayers;
    2123                 :          0 :   for ( QgsSymbolLayer *layer : constLLayers )
    2124                 :            :   {
    2125                 :          0 :     if ( layer->type() != QgsSymbol::Line )
    2126                 :          0 :       continue;
    2127                 :            : 
    2128                 :          0 :     QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( layer );
    2129                 :          0 :     lineLayer->setWidthUnit( unit );
    2130                 :            :   }
    2131                 :          0 : }
    2132                 :            : 
    2133                 :         70 : double QgsLineSymbol::width() const
    2134                 :            : {
    2135                 :         70 :   double maxWidth = 0;
    2136                 :         70 :   if ( mLayers.isEmpty() )
    2137                 :          0 :     return maxWidth;
    2138                 :            : 
    2139                 :         70 :   const auto constMLayers = mLayers;
    2140                 :        140 :   for ( QgsSymbolLayer *symbolLayer : constMLayers )
    2141                 :            :   {
    2142                 :         70 :     const QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( symbolLayer );
    2143                 :         70 :     if ( lineLayer )
    2144                 :            :     {
    2145                 :         70 :       double width = lineLayer->width();
    2146                 :         70 :       if ( width > maxWidth )
    2147                 :         70 :         maxWidth = width;
    2148                 :         70 :     }
    2149                 :            :   }
    2150                 :         70 :   return maxWidth;
    2151                 :         70 : }
    2152                 :            : 
    2153                 :          0 : double QgsLineSymbol::width( const QgsRenderContext &context ) const
    2154                 :            : {
    2155                 :            :   // return width of the largest symbol
    2156                 :          0 :   double maxWidth = 0;
    2157                 :          0 :   for ( QgsSymbolLayer *layer : mLayers )
    2158                 :            :   {
    2159                 :          0 :     if ( layer->type() != QgsSymbol::Line )
    2160                 :          0 :       continue;
    2161                 :          0 :     const QgsLineSymbolLayer *lineLayer = static_cast<const QgsLineSymbolLayer *>( layer );
    2162                 :          0 :     const double layerWidth = lineLayer->width( context );
    2163                 :          0 :     maxWidth = std::max( maxWidth, layerWidth );
    2164                 :            :   }
    2165                 :          0 :   return maxWidth;
    2166                 :            : }
    2167                 :            : 
    2168                 :          0 : void QgsLineSymbol::setDataDefinedWidth( const QgsProperty &property )
    2169                 :            : {
    2170                 :          0 :   const double symbolWidth = width();
    2171                 :            : 
    2172                 :          0 :   const auto constMLayers = mLayers;
    2173                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    2174                 :            :   {
    2175                 :          0 :     QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( layer );
    2176                 :            : 
    2177                 :          0 :     if ( lineLayer )
    2178                 :            :     {
    2179                 :          0 :       if ( !property )
    2180                 :            :       {
    2181                 :          0 :         lineLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyStrokeWidth, QgsProperty() );
    2182                 :          0 :         lineLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyOffset, QgsProperty() );
    2183                 :          0 :       }
    2184                 :            :       else
    2185                 :            :       {
    2186                 :          0 :         if ( qgsDoubleNear( symbolWidth, 0.0 ) || qgsDoubleNear( lineLayer->width(), symbolWidth ) )
    2187                 :            :         {
    2188                 :          0 :           lineLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyStrokeWidth, property );
    2189                 :          0 :         }
    2190                 :            :         else
    2191                 :            :         {
    2192                 :          0 :           lineLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyStrokeWidth, scaleWholeSymbol( lineLayer->width() / symbolWidth, property ) );
    2193                 :            :         }
    2194                 :            : 
    2195                 :          0 :         if ( !qgsDoubleNear( lineLayer->offset(), 0.0 ) )
    2196                 :            :         {
    2197                 :          0 :           lineLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyOffset, scaleWholeSymbol( lineLayer->offset() / symbolWidth, property ) );
    2198                 :          0 :         }
    2199                 :            :       }
    2200                 :          0 :     }
    2201                 :            :   }
    2202                 :          0 : }
    2203                 :            : 
    2204                 :          0 : QgsProperty QgsLineSymbol::dataDefinedWidth() const
    2205                 :            : {
    2206                 :          0 :   const double symbolWidth = width();
    2207                 :            : 
    2208                 :          0 :   QgsProperty symbolDD;
    2209                 :            : 
    2210                 :            :   // find the base of the "en masse" pattern
    2211                 :          0 :   for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
    2212                 :            :   {
    2213                 :          0 :     const QgsLineSymbolLayer *layer = dynamic_cast<const QgsLineSymbolLayer *>( *it );
    2214                 :          0 :     if ( layer && qgsDoubleNear( layer->width(), symbolWidth ) && layer->dataDefinedProperties().isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
    2215                 :            :     {
    2216                 :          0 :       symbolDD = layer->dataDefinedProperties().property( QgsSymbolLayer::PropertyStrokeWidth );
    2217                 :          0 :       break;
    2218                 :            :     }
    2219                 :          0 :   }
    2220                 :            : 
    2221                 :          0 :   if ( !symbolDD )
    2222                 :          0 :     return QgsProperty();
    2223                 :            : 
    2224                 :            :   // check that all layers width expressions match the "en masse" pattern
    2225                 :          0 :   const auto constMLayers = mLayers;
    2226                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    2227                 :            :   {
    2228                 :          0 :     if ( layer->type() != QgsSymbol::Line )
    2229                 :          0 :       continue;
    2230                 :          0 :     const QgsLineSymbolLayer *lineLayer = static_cast<const QgsLineSymbolLayer *>( layer );
    2231                 :            : 
    2232                 :          0 :     QgsProperty layerWidthDD = lineLayer->dataDefinedProperties().property( QgsSymbolLayer::PropertyStrokeWidth );
    2233                 :          0 :     QgsProperty layerOffsetDD = lineLayer->dataDefinedProperties().property( QgsSymbolLayer::PropertyOffset );
    2234                 :            : 
    2235                 :          0 :     if ( qgsDoubleNear( lineLayer->width(), symbolWidth ) )
    2236                 :            :     {
    2237                 :          0 :       if ( !layerWidthDD || layerWidthDD != symbolDD )
    2238                 :          0 :         return QgsProperty();
    2239                 :          0 :     }
    2240                 :            :     else
    2241                 :            :     {
    2242                 :          0 :       if ( qgsDoubleNear( symbolWidth, 0.0 ) )
    2243                 :          0 :         return QgsProperty();
    2244                 :            : 
    2245                 :          0 :       QgsProperty scaledDD( scaleWholeSymbol( lineLayer->width() / symbolWidth, symbolDD ) );
    2246                 :          0 :       if ( !layerWidthDD || layerWidthDD != scaledDD )
    2247                 :          0 :         return QgsProperty();
    2248                 :          0 :     }
    2249                 :            : 
    2250                 :          0 :     QgsProperty scaledOffsetDD( scaleWholeSymbol( lineLayer->offset() / symbolWidth, symbolDD ) );
    2251                 :          0 :     if ( layerOffsetDD && layerOffsetDD != scaledOffsetDD )
    2252                 :          0 :       return QgsProperty();
    2253                 :          0 :   }
    2254                 :            : 
    2255                 :          0 :   return symbolDD;
    2256                 :          0 : }
    2257                 :            : 
    2258                 :          0 : void QgsLineSymbol::renderPolyline( const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layerIdx, bool selected )
    2259                 :            : {
    2260                 :          0 :   const double opacity = dataDefinedProperties().valueAsDouble( QgsSymbol::PropertyOpacity, context.expressionContext(), mOpacity * 100 ) * 0.01;
    2261                 :            : 
    2262                 :            :   //save old painter
    2263                 :          0 :   QPainter *renderPainter = context.painter();
    2264                 :          0 :   QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, opacity, selected, mRenderHints, f );
    2265                 :          0 :   symbolContext.setOriginalGeometryType( QgsWkbTypes::LineGeometry );
    2266                 :          0 :   symbolContext.setGeometryPartCount( symbolRenderContext()->geometryPartCount() );
    2267                 :          0 :   symbolContext.setGeometryPartNum( symbolRenderContext()->geometryPartNum() );
    2268                 :            : 
    2269                 :          0 :   if ( layerIdx != -1 )
    2270                 :            :   {
    2271                 :          0 :     QgsSymbolLayer *symbolLayer = mLayers.value( layerIdx );
    2272                 :          0 :     if ( symbolLayer && symbolLayer->enabled() && context.isSymbolLayerEnabled( symbolLayer ) )
    2273                 :            :     {
    2274                 :          0 :       if ( symbolLayer->type() == QgsSymbol::Line )
    2275                 :            :       {
    2276                 :          0 :         QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( symbolLayer );
    2277                 :          0 :         renderPolylineUsingLayer( lineLayer, points, symbolContext );
    2278                 :          0 :       }
    2279                 :            :       else
    2280                 :          0 :         renderUsingLayer( symbolLayer, symbolContext );
    2281                 :          0 :     }
    2282                 :          0 :     return;
    2283                 :            :   }
    2284                 :            : 
    2285                 :          0 :   const auto constMLayers = mLayers;
    2286                 :          0 :   for ( QgsSymbolLayer *symbolLayer : constMLayers )
    2287                 :            :   {
    2288                 :          0 :     if ( context.renderingStopped() )
    2289                 :          0 :       break;
    2290                 :            : 
    2291                 :          0 :     if ( !symbolLayer->enabled() || !context.isSymbolLayerEnabled( symbolLayer ) )
    2292                 :          0 :       continue;
    2293                 :            : 
    2294                 :          0 :     if ( symbolLayer->type() == QgsSymbol::Line )
    2295                 :            :     {
    2296                 :          0 :       QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( symbolLayer );
    2297                 :          0 :       renderPolylineUsingLayer( lineLayer, points, symbolContext );
    2298                 :          0 :     }
    2299                 :            :     else
    2300                 :            :     {
    2301                 :          0 :       renderUsingLayer( symbolLayer, symbolContext );
    2302                 :            :     }
    2303                 :            :   }
    2304                 :            : 
    2305                 :          0 :   context.setPainter( renderPainter );
    2306                 :          0 : }
    2307                 :            : 
    2308                 :          0 : void QgsLineSymbol::renderPolylineUsingLayer( QgsLineSymbolLayer *layer, const QPolygonF &points, QgsSymbolRenderContext &context )
    2309                 :            : {
    2310                 :          0 :   if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
    2311                 :          0 :     return;
    2312                 :            : 
    2313                 :          0 :   QgsPaintEffect *effect = layer->paintEffect();
    2314                 :          0 :   if ( effect && effect->enabled() )
    2315                 :            :   {
    2316                 :          0 :     QgsEffectPainter p( context.renderContext() );
    2317                 :          0 :     p->translate( points.boundingRect().topLeft() );
    2318                 :          0 :     p.setEffect( effect );
    2319                 :          0 :     layer->renderPolyline( points.translated( -points.boundingRect().topLeft() ), context );
    2320                 :          0 :   }
    2321                 :            :   else
    2322                 :            :   {
    2323                 :          0 :     layer->renderPolyline( points, context );
    2324                 :            :   }
    2325                 :          0 : }
    2326                 :            : 
    2327                 :            : 
    2328                 :          0 : QgsLineSymbol *QgsLineSymbol::clone() const
    2329                 :            : {
    2330                 :          0 :   QgsLineSymbol *cloneSymbol = new QgsLineSymbol( cloneLayers() );
    2331                 :          0 :   cloneSymbol->setOpacity( mOpacity );
    2332                 :            :   Q_NOWARN_DEPRECATED_PUSH
    2333                 :          0 :   cloneSymbol->setLayer( mLayer );
    2334                 :            :   Q_NOWARN_DEPRECATED_POP
    2335                 :          0 :   cloneSymbol->setClipFeaturesToExtent( mClipFeaturesToExtent );
    2336                 :          0 :   cloneSymbol->setForceRHR( mForceRHR );
    2337                 :          0 :   cloneSymbol->setDataDefinedProperties( dataDefinedProperties() );
    2338                 :          0 :   return cloneSymbol;
    2339                 :          0 : }
    2340                 :            : 
    2341                 :            : ///////////////////
    2342                 :            : // FILL
    2343                 :            : 
    2344                 :        311 : QgsFillSymbol::QgsFillSymbol( const QgsSymbolLayerList &layers )
    2345                 :        311 :   : QgsSymbol( Fill, layers )
    2346                 :        622 : {
    2347                 :        311 :   if ( mLayers.isEmpty() )
    2348                 :         31 :     mLayers.append( new QgsSimpleFillSymbolLayer() );
    2349                 :        311 : }
    2350                 :            : 
    2351                 :          0 : void QgsFillSymbol::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, const QgsFeature *f, QgsRenderContext &context, int layerIdx, bool selected )
    2352                 :            : {
    2353                 :          0 :   const double opacity = dataDefinedProperties().valueAsDouble( QgsSymbol::PropertyOpacity, context.expressionContext(), mOpacity * 100 ) * 0.01;
    2354                 :            : 
    2355                 :          0 :   QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, opacity, selected, mRenderHints, f );
    2356                 :          0 :   symbolContext.setOriginalGeometryType( QgsWkbTypes::PolygonGeometry );
    2357                 :          0 :   symbolContext.setGeometryPartCount( symbolRenderContext()->geometryPartCount() );
    2358                 :          0 :   symbolContext.setGeometryPartNum( symbolRenderContext()->geometryPartNum() );
    2359                 :            : 
    2360                 :          0 :   if ( layerIdx != -1 )
    2361                 :            :   {
    2362                 :          0 :     QgsSymbolLayer *symbolLayer = mLayers.value( layerIdx );
    2363                 :          0 :     if ( symbolLayer && symbolLayer->enabled() && context.isSymbolLayerEnabled( symbolLayer ) )
    2364                 :            :     {
    2365                 :          0 :       if ( symbolLayer->type() == Fill || symbolLayer->type() == Line )
    2366                 :          0 :         renderPolygonUsingLayer( symbolLayer, points, rings, symbolContext );
    2367                 :            :       else
    2368                 :          0 :         renderUsingLayer( symbolLayer, symbolContext );
    2369                 :          0 :     }
    2370                 :          0 :     return;
    2371                 :            :   }
    2372                 :            : 
    2373                 :          0 :   const auto constMLayers = mLayers;
    2374                 :          0 :   for ( QgsSymbolLayer *symbolLayer : constMLayers )
    2375                 :            :   {
    2376                 :          0 :     if ( context.renderingStopped() )
    2377                 :          0 :       break;
    2378                 :            : 
    2379                 :          0 :     if ( !symbolLayer->enabled() || !context.isSymbolLayerEnabled( symbolLayer ) )
    2380                 :          0 :       continue;
    2381                 :            : 
    2382                 :          0 :     if ( symbolLayer->type() == Fill || symbolLayer->type() == Line )
    2383                 :          0 :       renderPolygonUsingLayer( symbolLayer, points, rings, symbolContext );
    2384                 :            :     else
    2385                 :          0 :       renderUsingLayer( symbolLayer, symbolContext );
    2386                 :            :   }
    2387                 :          0 : }
    2388                 :            : 
    2389                 :          0 : void QgsFillSymbol::renderPolygonUsingLayer( QgsSymbolLayer *layer, const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
    2390                 :            : {
    2391                 :          0 :   if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
    2392                 :          0 :     return;
    2393                 :            : 
    2394                 :          0 :   QgsSymbol::SymbolType layertype = layer->type();
    2395                 :            : 
    2396                 :          0 :   QgsPaintEffect *effect = layer->paintEffect();
    2397                 :          0 :   if ( effect && effect->enabled() )
    2398                 :            :   {
    2399                 :          0 :     QRectF bounds = polygonBounds( points, rings );
    2400                 :          0 :     QVector<QPolygonF> *translatedRings = translateRings( rings, -bounds.left(), -bounds.top() );
    2401                 :            : 
    2402                 :          0 :     QgsEffectPainter p( context.renderContext() );
    2403                 :          0 :     p->translate( bounds.topLeft() );
    2404                 :          0 :     p.setEffect( effect );
    2405                 :          0 :     if ( layertype == QgsSymbol::Fill )
    2406                 :            :     {
    2407                 :          0 :       ( static_cast<QgsFillSymbolLayer *>( layer ) )->renderPolygon( points.translated( -bounds.topLeft() ), translatedRings, context );
    2408                 :          0 :     }
    2409                 :          0 :     else if ( layertype == QgsSymbol::Line )
    2410                 :            :     {
    2411                 :          0 :       ( static_cast<QgsLineSymbolLayer *>( layer ) )->renderPolygonStroke( points.translated( -bounds.topLeft() ), translatedRings, context );
    2412                 :          0 :     }
    2413                 :          0 :     delete translatedRings;
    2414                 :          0 :   }
    2415                 :            :   else
    2416                 :            :   {
    2417                 :          0 :     if ( layertype == QgsSymbol::Fill )
    2418                 :            :     {
    2419                 :          0 :       ( static_cast<QgsFillSymbolLayer *>( layer ) )->renderPolygon( points, rings, context );
    2420                 :          0 :     }
    2421                 :          0 :     else if ( layertype == QgsSymbol::Line )
    2422                 :            :     {
    2423                 :          0 :       ( static_cast<QgsLineSymbolLayer *>( layer ) )->renderPolygonStroke( points, rings, context );
    2424                 :          0 :     }
    2425                 :            :   }
    2426                 :          0 : }
    2427                 :            : 
    2428                 :          0 : QRectF QgsFillSymbol::polygonBounds( const QPolygonF &points, const QVector<QPolygonF> *rings ) const
    2429                 :            : {
    2430                 :          0 :   QRectF bounds = points.boundingRect();
    2431                 :          0 :   if ( rings )
    2432                 :            :   {
    2433                 :          0 :     for ( auto it = rings->constBegin(); it != rings->constEnd(); ++it )
    2434                 :            :     {
    2435                 :          0 :       bounds = bounds.united( ( *it ).boundingRect() );
    2436                 :          0 :     }
    2437                 :          0 :   }
    2438                 :          0 :   return bounds;
    2439                 :            : }
    2440                 :            : 
    2441                 :          0 : QVector<QPolygonF> *QgsFillSymbol::translateRings( const QVector<QPolygonF> *rings, double dx, double dy ) const
    2442                 :            : {
    2443                 :          0 :   if ( !rings )
    2444                 :          0 :     return nullptr;
    2445                 :            : 
    2446                 :          0 :   QVector<QPolygonF> *translatedRings = new QVector<QPolygonF>;
    2447                 :          0 :   translatedRings->reserve( rings->size() );
    2448                 :          0 :   for ( auto it = rings->constBegin(); it != rings->constEnd(); ++it )
    2449                 :            :   {
    2450                 :          0 :     translatedRings->append( ( *it ).translated( dx, dy ) );
    2451                 :          0 :   }
    2452                 :          0 :   return translatedRings;
    2453                 :          0 : }
    2454                 :            : 
    2455                 :          0 : QgsFillSymbol *QgsFillSymbol::clone() const
    2456                 :            : {
    2457                 :          0 :   QgsFillSymbol *cloneSymbol = new QgsFillSymbol( cloneLayers() );
    2458                 :          0 :   cloneSymbol->setOpacity( mOpacity );
    2459                 :            :   Q_NOWARN_DEPRECATED_PUSH
    2460                 :          0 :   cloneSymbol->setLayer( mLayer );
    2461                 :            :   Q_NOWARN_DEPRECATED_POP
    2462                 :          0 :   cloneSymbol->setClipFeaturesToExtent( mClipFeaturesToExtent );
    2463                 :          0 :   cloneSymbol->setForceRHR( mForceRHR );
    2464                 :          0 :   cloneSymbol->setDataDefinedProperties( dataDefinedProperties() );
    2465                 :          0 :   return cloneSymbol;
    2466                 :          0 : }
    2467                 :            : 
    2468                 :          0 : void QgsFillSymbol::setAngle( double angle )
    2469                 :            : {
    2470                 :          0 :   const auto constMLayers = mLayers;
    2471                 :          0 :   for ( QgsSymbolLayer *layer : constMLayers )
    2472                 :            :   {
    2473                 :          0 :     if ( layer->type() != QgsSymbol::Fill )
    2474                 :          0 :       continue;
    2475                 :            : 
    2476                 :          0 :     QgsFillSymbolLayer *fillLayer = static_cast<QgsFillSymbolLayer *>( layer );
    2477                 :            : 
    2478                 :          0 :     if ( fillLayer )
    2479                 :          0 :       fillLayer->setAngle( angle );
    2480                 :            :   }
    2481                 :          0 : }
    2482                 :            : 
    2483                 :            : 

Generated by: LCOV version 1.14