1 /***************************************************************************
2  qgssymbollayer.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 "qgssymbollayer.h"
17 #include "qgsclipper.h"
18 #include "qgsexpression.h"
19 #include "qgsrendercontext.h"
20 #include "qgsvectorlayer.h"
21 #include "qgsdxfexport.h"
22 #include "qgsgeometrysimplifier.h"
23 #include "qgspainteffect.h"
24 #include "qgseffectstack.h"
25 #include "qgspainteffectregistry.h"
26 #include "qgsproperty.h"
27 #include "qgsexpressioncontext.h"
28 #include "qgssymbollayerutils.h"
29 #include "qgsapplication.h"
30 #include "qgsmultipoint.h"
31 #include "qgslegendpatchshape.h"
32 #include "qgsstyle.h"
33 #include "qgsexpressioncontextutils.h"
34 #include "qgssymbol.h"
35 #include "qgssymbollayerreference.h"
36 
37 #include <QSize>
38 #include <QPainter>
39 #include <QPointF>
40 #include <QPolygonF>
41 
42 QgsPropertiesDefinition QgsSymbolLayer::sPropertyDefinitions;
43 
initPropertyDefinitions()44 void QgsSymbolLayer::initPropertyDefinitions()
45 {
46   if ( !sPropertyDefinitions.isEmpty() )
47     return;
48 
49   QString origin = QStringLiteral( "symbol" );
50 
51   sPropertyDefinitions = QgsPropertiesDefinition
52   {
53     { QgsSymbolLayer::PropertySize, QgsPropertyDefinition( "size", QObject::tr( "Symbol size" ), QgsPropertyDefinition::Size, origin ) },
54     { QgsSymbolLayer::PropertyAngle, QgsPropertyDefinition( "angle", QObject::tr( "Rotation angle" ), QgsPropertyDefinition::Rotation, origin ) },
55     { QgsSymbolLayer::PropertyName, QgsPropertyDefinition( "name", QObject::tr( "Symbol name" ), QgsPropertyDefinition::String, origin ) },
56     { QgsSymbolLayer::PropertyFillColor, QgsPropertyDefinition( "fillColor", QObject::tr( "Symbol fill color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
57     { QgsSymbolLayer::PropertyStrokeColor, QgsPropertyDefinition( "outlineColor", QObject::tr( "Symbol stroke color" ), QgsPropertyDefinition::ColorWithAlpha, origin ) },
58     { QgsSymbolLayer::PropertyStrokeWidth, QgsPropertyDefinition( "outlineWidth", QObject::tr( "Symbol stroke width" ), QgsPropertyDefinition::StrokeWidth, origin ) },
59     { QgsSymbolLayer::PropertyStrokeStyle, QgsPropertyDefinition( "outlineStyle", QObject::tr( "Symbol stroke style" ), QgsPropertyDefinition::LineStyle, origin )},
60     { QgsSymbolLayer::PropertyOffset, QgsPropertyDefinition( "offset", QObject::tr( "Symbol offset" ), QgsPropertyDefinition::Offset, origin )},
61     { QgsSymbolLayer::PropertyCharacter, QgsPropertyDefinition( "char", QObject::tr( "Marker character(s)" ), QgsPropertyDefinition::String, origin )},
62     { QgsSymbolLayer::PropertyFontFamily, QgsPropertyDefinition( "fontFamily", QObject::tr( "Font family" ), QgsPropertyDefinition::String, origin )},
63     { QgsSymbolLayer::PropertyFontStyle, QgsPropertyDefinition( "fontStyle", QObject::tr( "Font style" ), QgsPropertyDefinition::String, origin )},
64     { QgsSymbolLayer::PropertyWidth, QgsPropertyDefinition( "width", QObject::tr( "Symbol width" ), QgsPropertyDefinition::DoublePositive, origin )},
65     { QgsSymbolLayer::PropertyHeight, QgsPropertyDefinition( "height", QObject::tr( "Symbol height" ), QgsPropertyDefinition::DoublePositive, origin )},
66     { QgsSymbolLayer::PropertyPreserveAspectRatio, QgsPropertyDefinition( "preserveAspectRatio", QObject::tr( "Preserve aspect ratio between width and height" ), QgsPropertyDefinition::Boolean, origin )},
67     { QgsSymbolLayer::PropertyFillStyle, QgsPropertyDefinition( "fillStyle", QObject::tr( "Symbol fill style" ), QgsPropertyDefinition::FillStyle, origin )},
68     { QgsSymbolLayer::PropertyJoinStyle, QgsPropertyDefinition( "joinStyle", QObject::tr( "Outline join style" ), QgsPropertyDefinition::PenJoinStyle, origin )},
69     { QgsSymbolLayer::PropertySecondaryColor, QgsPropertyDefinition( "color2", QObject::tr( "Secondary fill color" ), QgsPropertyDefinition::ColorWithAlpha, origin )},
70     { QgsSymbolLayer::PropertyLineAngle, QgsPropertyDefinition( "lineAngle", QObject::tr( "Angle for line fills" ), QgsPropertyDefinition::Rotation, origin )},
71     { QgsSymbolLayer::PropertyGradientType, QgsPropertyDefinition( "gradientType", QgsPropertyDefinition::DataTypeString, QObject::tr( "Gradient type" ),  QObject::tr( "string " ) + QLatin1String( "[<b>linear</b>|<b>radial</b>|<b>conical</b>]" ), origin )},
72     { QgsSymbolLayer::PropertyCoordinateMode, QgsPropertyDefinition( "gradientMode", QgsPropertyDefinition::DataTypeString, QObject::tr( "Gradient mode" ), QObject::tr( "string " ) + QLatin1String( "[<b>feature</b>|<b>viewport</b>]" ), origin )},
73     { QgsSymbolLayer::PropertyGradientSpread, QgsPropertyDefinition( "gradientSpread", QgsPropertyDefinition::DataTypeString, QObject::tr( "Gradient spread" ), QObject::tr( "string " ) + QLatin1String( "[<b>pad</b>|<b>repeat</b>|<b>reflect</b>]" ), origin )},
74     { QgsSymbolLayer::PropertyGradientReference1X, QgsPropertyDefinition( "gradientRef1X", QObject::tr( "Reference point 1 (X)" ), QgsPropertyDefinition::Double0To1, origin )},
75     { QgsSymbolLayer::PropertyGradientReference1Y, QgsPropertyDefinition( "gradientRef1Y", QObject::tr( "Reference point 1 (Y)" ), QgsPropertyDefinition::Double0To1, origin )},
76     { QgsSymbolLayer::PropertyGradientReference2X, QgsPropertyDefinition( "gradientRef2X", QObject::tr( "Reference point 2 (X)" ), QgsPropertyDefinition::Double0To1, origin )},
77     { QgsSymbolLayer::PropertyGradientReference2Y, QgsPropertyDefinition( "gradientRef2Y", QObject::tr( "Reference point 2 (Y)" ), QgsPropertyDefinition::Double0To1, origin )},
78     { QgsSymbolLayer::PropertyGradientReference1IsCentroid, QgsPropertyDefinition( "gradientRef1Centroid", QObject::tr( "Reference point 1 follows feature centroid" ), QgsPropertyDefinition::Boolean, origin )},
79     { QgsSymbolLayer::PropertyGradientReference2IsCentroid, QgsPropertyDefinition( "gradientRef2Centroid", QObject::tr( "Reference point 2 follows feature centroid" ), QgsPropertyDefinition::Boolean, origin )},
80     { QgsSymbolLayer::PropertyBlurRadius, QgsPropertyDefinition( "blurRadius", QgsPropertyDefinition::DataTypeNumeric, QObject::tr( "Blur radius" ), QObject::tr( "Integer between 0 and 18" ), origin )},
81     { QgsSymbolLayer::PropertyLineDistance, QgsPropertyDefinition( "lineDistance", QObject::tr( "Distance between lines" ), QgsPropertyDefinition::DoublePositive, origin )},
82     { QgsSymbolLayer::PropertyShapeburstUseWholeShape, QgsPropertyDefinition( "shapeburstWholeShape", QObject::tr( "Shade whole shape" ), QgsPropertyDefinition::Boolean, origin )},
83     { QgsSymbolLayer::PropertyShapeburstMaxDistance, QgsPropertyDefinition( "shapeburstMaxDist", QObject::tr( "Maximum distance for shapeburst fill" ), QgsPropertyDefinition::DoublePositive, origin )},
84     { QgsSymbolLayer::PropertyShapeburstIgnoreRings, QgsPropertyDefinition( "shapeburstIgnoreRings", QObject::tr( "Ignore rings in feature" ), QgsPropertyDefinition::Boolean, origin )},
85     { QgsSymbolLayer::PropertyFile, QgsPropertyDefinition( "file", QObject::tr( "Symbol file path" ), QgsPropertyDefinition::String, origin )},
86     { QgsSymbolLayer::PropertyDistanceX, QgsPropertyDefinition( "distanceX", QObject::tr( "Horizontal distance between markers" ), QgsPropertyDefinition::DoublePositive, origin )},
87     { QgsSymbolLayer::PropertyDistanceY, QgsPropertyDefinition( "distanceY", QObject::tr( "Vertical distance between markers" ), QgsPropertyDefinition::DoublePositive, origin )},
88     { QgsSymbolLayer::PropertyDisplacementX, QgsPropertyDefinition( "displacementX", QObject::tr( "Horizontal displacement between rows" ), QgsPropertyDefinition::DoublePositive, origin )},
89     { QgsSymbolLayer::PropertyDisplacementY, QgsPropertyDefinition( "displacementY", QObject::tr( "Vertical displacement between columns" ), QgsPropertyDefinition::DoublePositive, origin )},
90     { QgsSymbolLayer::PropertyOffsetX, QgsPropertyDefinition( "offsetX", QObject::tr( "Horizontal offset" ), QgsPropertyDefinition::Double, origin )},
91     { QgsSymbolLayer::PropertyOffsetY, QgsPropertyDefinition( "offsetY", QObject::tr( "Vertical offset" ), QgsPropertyDefinition::Double, origin )},
92     { QgsSymbolLayer::PropertyOpacity, QgsPropertyDefinition( "alpha", QObject::tr( "Opacity" ), QgsPropertyDefinition::Opacity, origin )},
93     { QgsSymbolLayer::PropertyCustomDash, QgsPropertyDefinition( "customDash", QgsPropertyDefinition::DataTypeString, QObject::tr( "Custom dash pattern" ), QObject::tr( "[<b><dash>;<space></b>] e.g. '8;2;1;2'" ), origin )},
94     { QgsSymbolLayer::PropertyCapStyle, QgsPropertyDefinition( "capStyle", QObject::tr( "Line cap style" ), QgsPropertyDefinition::CapStyle, origin )},
95     { QgsSymbolLayer::PropertyPlacement, QgsPropertyDefinition( "placement", QgsPropertyDefinition::DataTypeString, QObject::tr( "Marker placement" ), QObject::tr( "string " ) + "[<b>interval</b>|<b>vertex</b>|<b>lastvertex</b>|<b>firstvertex</b>|<b>centerpoint</b>|<b>curvepoint</b>|<b>segmentcenter</b>]", origin )},
96     { QgsSymbolLayer::PropertyInterval, QgsPropertyDefinition( "interval", QObject::tr( "Marker interval" ), QgsPropertyDefinition::DoublePositive, origin )},
97     { QgsSymbolLayer::PropertyOffsetAlongLine, QgsPropertyDefinition( "offsetAlongLine", QObject::tr( "Offset along line" ), QgsPropertyDefinition::DoublePositive, origin )},
98     { QgsSymbolLayer::PropertyAverageAngleLength, QgsPropertyDefinition( "averageAngleLength", QObject::tr( "Average line angles over" ), QgsPropertyDefinition::DoublePositive, origin )},
99     { QgsSymbolLayer::PropertyHorizontalAnchor, QgsPropertyDefinition( "hAnchor", QObject::tr( "Horizontal anchor point" ), QgsPropertyDefinition::HorizontalAnchor, origin )},
100     { QgsSymbolLayer::PropertyVerticalAnchor, QgsPropertyDefinition( "vAnchor", QObject::tr( "Vertical anchor point" ), QgsPropertyDefinition::VerticalAnchor, origin )},
101     { QgsSymbolLayer::PropertyLayerEnabled, QgsPropertyDefinition( "enabled", QObject::tr( "Layer enabled" ), QgsPropertyDefinition::Boolean, origin )},
102     { QgsSymbolLayer::PropertyArrowWidth, QgsPropertyDefinition( "arrowWidth", QObject::tr( "Arrow line width" ), QgsPropertyDefinition::StrokeWidth, origin )},
103     { QgsSymbolLayer::PropertyArrowStartWidth, QgsPropertyDefinition( "arrowStartWidth", QObject::tr( "Arrow line start width" ), QgsPropertyDefinition::StrokeWidth, origin )},
104     { QgsSymbolLayer::PropertyArrowHeadLength, QgsPropertyDefinition( "arrowHeadLength", QObject::tr( "Arrow head length" ), QgsPropertyDefinition::DoublePositive, origin )},
105     { QgsSymbolLayer::PropertyArrowHeadThickness, QgsPropertyDefinition( "arrowHeadThickness", QObject::tr( "Arrow head thickness" ), QgsPropertyDefinition::DoublePositive, origin )},
106     { QgsSymbolLayer::PropertyArrowHeadType, QgsPropertyDefinition( "arrowHeadType", QgsPropertyDefinition::DataTypeString, QObject::tr( "Arrow head type" ), QObject::tr( "string " ) + QLatin1String( "[<b>single</b>|<b>reversed</b>|<b>double</b>]" ), origin )},
107     { QgsSymbolLayer::PropertyArrowType, QgsPropertyDefinition( "arrowType", QgsPropertyDefinition::DataTypeString, QObject::tr( "Arrow type" ), QObject::tr( "string " ) + QLatin1String( "[<b>plain</b>|<b>lefthalf</b>|<b>righthalf</b>]" ), origin )},
108     { QgsSymbolLayer::PropertyPointCount, QgsPropertyDefinition( "pointCount", QObject::tr( "Point count" ), QgsPropertyDefinition::IntegerPositive, origin )},
109     { QgsSymbolLayer::PropertyRandomSeed, QgsPropertyDefinition( "randomSeed", QgsPropertyDefinition::DataTypeNumeric, QObject::tr( "Random number seed" ), QObject::tr( "integer > 0, or 0 for completely random sequence" ), origin )},
110     { QgsSymbolLayer::PropertyClipPoints, QgsPropertyDefinition( "clipPoints", QObject::tr( "Clip markers" ), QgsPropertyDefinition::Boolean, origin )},
111     { QgsSymbolLayer::PropertyClipPoints, QgsPropertyDefinition( "densityArea", QObject::tr( "Density area" ), QgsPropertyDefinition::DoublePositive, origin )},
112     { QgsSymbolLayer::PropertyDashPatternOffset, QgsPropertyDefinition( "dashPatternOffset", QObject::tr( "Dash pattern offset" ), QgsPropertyDefinition::DoublePositive, origin )},
113     { QgsSymbolLayer::PropertyTrimStart, QgsPropertyDefinition( "trimStart", QObject::tr( "Start trim distance" ), QgsPropertyDefinition::DoublePositive, origin )},
114     { QgsSymbolLayer::PropertyTrimEnd, QgsPropertyDefinition( "trimEnd", QObject::tr( "End trim distance" ), QgsPropertyDefinition::DoublePositive, origin )},
115     { QgsSymbolLayer::PropertyLineStartWidthValue, QgsPropertyDefinition( "lineStartWidthValue", QObject::tr( "Line start width value" ), QgsPropertyDefinition::Double, origin )},
116     { QgsSymbolLayer::PropertyLineEndWidthValue, QgsPropertyDefinition( "lineEndWidthValue", QObject::tr( "Line end width value" ), QgsPropertyDefinition::Double, origin )},
117     { QgsSymbolLayer::PropertyLineStartColorValue, QgsPropertyDefinition( "lineStartColorValue", QObject::tr( "Line start color value" ), QgsPropertyDefinition::Double, origin )},
118     { QgsSymbolLayer::PropertyLineEndColorValue, QgsPropertyDefinition( "lineEndColorValue", QObject::tr( "Line end color value" ), QgsPropertyDefinition::Double, origin )},
119   };
120 }
121 
setDataDefinedProperty(QgsSymbolLayer::Property key,const QgsProperty & property)122 void QgsSymbolLayer::setDataDefinedProperty( QgsSymbolLayer::Property key, const QgsProperty &property )
123 {
124   dataDefinedProperties().setProperty( key, property );
125 }
126 
startFeatureRender(const QgsFeature & feature,QgsRenderContext & context)127 void QgsSymbolLayer::startFeatureRender( const QgsFeature &feature, QgsRenderContext &context )
128 {
129   if ( QgsSymbol *lSubSymbol = subSymbol() )
130     lSubSymbol->startFeatureRender( feature, context );
131 }
132 
stopFeatureRender(const QgsFeature & feature,QgsRenderContext & context)133 void QgsSymbolLayer::stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context )
134 {
135   if ( QgsSymbol *lSubSymbol = subSymbol() )
136     lSubSymbol->stopFeatureRender( feature, context );
137 }
138 
subSymbol()139 QgsSymbol *QgsSymbolLayer::subSymbol()
140 {
141   return nullptr;
142 }
143 
setSubSymbol(QgsSymbol * symbol)144 bool QgsSymbolLayer::setSubSymbol( QgsSymbol *symbol )
145 {
146   delete symbol;
147   return false;
148 }
149 
writeDxf(QgsDxfExport & e,double mmMapUnitScaleFactor,const QString & layerName,QgsSymbolRenderContext & context,QPointF shift) const150 bool QgsSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
151 {
152   Q_UNUSED( e )
153   Q_UNUSED( mmMapUnitScaleFactor )
154   Q_UNUSED( layerName )
155   Q_UNUSED( context )
156   Q_UNUSED( shift )
157   return false;
158 }
159 
dxfWidth(const QgsDxfExport & e,QgsSymbolRenderContext & context) const160 double QgsSymbolLayer::dxfWidth( const QgsDxfExport &e, QgsSymbolRenderContext &context ) const
161 {
162   Q_UNUSED( e )
163   Q_UNUSED( context )
164   return 1.0;
165 }
166 
dxfOffset(const QgsDxfExport & e,QgsSymbolRenderContext & context) const167 double QgsSymbolLayer::dxfOffset( const QgsDxfExport &e, QgsSymbolRenderContext &context ) const
168 {
169   Q_UNUSED( e )
170   Q_UNUSED( context )
171   return 0.0;
172 }
173 
dxfColor(QgsSymbolRenderContext & context) const174 QColor QgsSymbolLayer::dxfColor( QgsSymbolRenderContext &context ) const
175 {
176   Q_UNUSED( context )
177   return color();
178 }
179 
dxfAngle(QgsSymbolRenderContext & context) const180 double QgsSymbolLayer::dxfAngle( QgsSymbolRenderContext &context ) const
181 {
182   Q_UNUSED( context )
183   return 0.0;
184 }
185 
dxfCustomDashPattern(QgsUnitTypes::RenderUnit & unit) const186 QVector<qreal> QgsSymbolLayer::dxfCustomDashPattern( QgsUnitTypes::RenderUnit &unit ) const
187 {
188   Q_UNUSED( unit )
189   return QVector<qreal>();
190 }
191 
dxfPenStyle() const192 Qt::PenStyle QgsSymbolLayer::dxfPenStyle() const
193 {
194   return Qt::SolidLine;
195 }
196 
dxfBrushColor(QgsSymbolRenderContext & context) const197 QColor QgsSymbolLayer::dxfBrushColor( QgsSymbolRenderContext &context ) const
198 {
199   Q_UNUSED( context )
200   return color();
201 }
202 
dxfBrushStyle() const203 Qt::BrushStyle QgsSymbolLayer::dxfBrushStyle() const
204 {
205   return Qt::NoBrush;
206 }
207 
paintEffect() const208 QgsPaintEffect *QgsSymbolLayer::paintEffect() const
209 {
210   return mPaintEffect.get();
211 }
212 
setPaintEffect(QgsPaintEffect * effect)213 void QgsSymbolLayer::setPaintEffect( QgsPaintEffect *effect )
214 {
215   if ( effect == mPaintEffect.get() )
216     return;
217 
218   mPaintEffect.reset( effect );
219 }
220 
QgsSymbolLayer(Qgis::SymbolType type,bool locked)221 QgsSymbolLayer::QgsSymbolLayer( Qgis::SymbolType type, bool locked )
222   : mType( type )
223   , mLocked( locked )
224 {
225 }
226 
flags() const227 Qgis::SymbolLayerFlags QgsSymbolLayer::flags() const
228 {
229   return Qgis::SymbolLayerFlags();
230 }
231 
prepareExpressions(const QgsSymbolRenderContext & context)232 void QgsSymbolLayer::prepareExpressions( const QgsSymbolRenderContext &context )
233 {
234   mDataDefinedProperties.prepare( context.renderContext().expressionContext() );
235 
236   if ( !context.fields().isEmpty() )
237   {
238     //QgsFields is implicitly shared, so it's cheap to make a copy
239     mFields = context.fields();
240   }
241 }
242 
hasDataDefinedProperties() const243 bool QgsSymbolLayer::hasDataDefinedProperties() const
244 {
245   return mDataDefinedProperties.hasActiveProperties();
246 }
247 
propertyDefinitions()248 const QgsPropertiesDefinition &QgsSymbolLayer::propertyDefinitions()
249 {
250   QgsSymbolLayer::initPropertyDefinitions();
251   return sPropertyDefinitions;
252 }
253 
254 QgsSymbolLayer::~QgsSymbolLayer() = default;
255 
isCompatibleWithSymbol(QgsSymbol * symbol) const256 bool QgsSymbolLayer::isCompatibleWithSymbol( QgsSymbol *symbol ) const
257 {
258   if ( symbol->type() == Qgis::SymbolType::Fill && mType == Qgis::SymbolType::Line )
259     return true;
260 
261   return symbol->type() == mType;
262 }
263 
canCauseArtifactsBetweenAdjacentTiles() const264 bool QgsSymbolLayer::canCauseArtifactsBetweenAdjacentTiles() const
265 {
266   return false;
267 }
268 
usesMapUnits() const269 bool QgsSymbolLayer::usesMapUnits() const
270 {
271   return false;
272 }
273 
setRenderingPass(int renderingPass)274 void QgsSymbolLayer::setRenderingPass( int renderingPass )
275 {
276   mRenderingPass = renderingPass;
277 }
278 
renderingPass() const279 int QgsSymbolLayer::renderingPass() const
280 {
281   return mRenderingPass;
282 }
283 
usedAttributes(const QgsRenderContext & context) const284 QSet<QString> QgsSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
285 {
286   // calling referencedFields() with ignoreContext=true because in our expression context
287   // we do not have valid QgsFields yet - because of that the field names from expressions
288   // wouldn't get reported
289   QSet<QString> columns = mDataDefinedProperties.referencedFields( context.expressionContext(), true );
290   return columns;
291 }
292 
propertyFromMap(const QVariantMap & map,const QString & baseName)293 QgsProperty propertyFromMap( const QVariantMap &map, const QString &baseName )
294 {
295   QString prefix;
296   if ( !baseName.isEmpty() )
297   {
298     prefix.append( QStringLiteral( "%1_dd_" ).arg( baseName ) );
299   }
300 
301   if ( !map.contains( QStringLiteral( "%1expression" ).arg( prefix ) ) )
302   {
303     //requires at least the expression value
304     return QgsProperty();
305   }
306 
307   bool active = ( map.value( QStringLiteral( "%1active" ).arg( prefix ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
308   QString expression = map.value( QStringLiteral( "%1expression" ).arg( prefix ) ).toString();
309   bool useExpression = ( map.value( QStringLiteral( "%1useexpr" ).arg( prefix ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
310   QString field = map.value( QStringLiteral( "%1field" ).arg( prefix ), QString() ).toString();
311 
312   if ( useExpression )
313     return QgsProperty::fromExpression( expression, active );
314   else
315     return QgsProperty::fromField( field, active );
316 }
317 
restoreOldDataDefinedProperties(const QVariantMap & stringMap)318 void QgsSymbolLayer::restoreOldDataDefinedProperties( const QVariantMap &stringMap )
319 {
320   // property string to type upgrade map
321   static const QMap< QString, QgsSymbolLayer::Property > OLD_PROPS
322   {
323     { "color", QgsSymbolLayer::PropertyFillColor },
324     { "arrow_width", QgsSymbolLayer::PropertyArrowWidth },
325     { "arrow_start_width", QgsSymbolLayer::PropertyArrowStartWidth },
326     { "head_length", QgsSymbolLayer::PropertyArrowHeadLength },
327     { "head_thickness", QgsSymbolLayer::PropertyArrowHeadThickness },
328     { "offset", QgsSymbolLayer::PropertyOffset },
329     { "head_type", QgsSymbolLayer::PropertyArrowHeadType },
330     { "arrow_type", QgsSymbolLayer::PropertyArrowType },
331     { "width_field", QgsSymbolLayer::PropertyWidth },
332     { "height_field", QgsSymbolLayer::PropertyHeight },
333     { "rotation_field", QgsSymbolLayer::PropertyAngle },
334     { "outline_width_field", QgsSymbolLayer::PropertyStrokeWidth },
335     { "fill_color_field", QgsSymbolLayer::PropertyFillColor },
336     { "outline_color_field", QgsSymbolLayer::PropertyStrokeColor },
337     { "symbol_name_field", QgsSymbolLayer::PropertyName },
338     { "outline_width", QgsSymbolLayer::PropertyStrokeWidth },
339     { "outline_style", QgsSymbolLayer::PropertyStrokeStyle },
340     { "join_style", QgsSymbolLayer::PropertyJoinStyle },
341     { "fill_color", QgsSymbolLayer::PropertyFillColor },
342     { "outline_color", QgsSymbolLayer::PropertyStrokeColor },
343     { "width", QgsSymbolLayer::PropertyWidth },
344     { "height", QgsSymbolLayer::PropertyHeight },
345     { "symbol_name", QgsSymbolLayer::PropertyName },
346     { "angle", QgsSymbolLayer::PropertyAngle },
347     { "fill_style", QgsSymbolLayer::PropertyFillStyle },
348     { "color_border", QgsSymbolLayer::PropertyStrokeColor },
349     { "width_border", QgsSymbolLayer::PropertyStrokeWidth },
350     { "border_color", QgsSymbolLayer::PropertyStrokeColor },
351     { "border_style", QgsSymbolLayer::PropertyStrokeStyle },
352     { "color2", QgsSymbolLayer::PropertySecondaryColor },
353     { "gradient_type", QgsSymbolLayer::PropertyGradientType },
354     { "coordinate_mode", QgsSymbolLayer::PropertyCoordinateMode },
355     { "spread", QgsSymbolLayer::PropertyGradientSpread },
356     { "reference1_x", QgsSymbolLayer::PropertyGradientReference1X },
357     { "reference1_y", QgsSymbolLayer::PropertyGradientReference1Y },
358     { "reference2_x", QgsSymbolLayer::PropertyGradientReference2X },
359     { "reference2_y", QgsSymbolLayer::PropertyGradientReference2Y },
360     { "reference1_iscentroid", QgsSymbolLayer::PropertyGradientReference1IsCentroid },
361     { "reference2_iscentroid", QgsSymbolLayer::PropertyGradientReference2IsCentroid },
362     { "blur_radius", QgsSymbolLayer::PropertyBlurRadius },
363     { "use_whole_shape", QgsSymbolLayer::PropertyShapeburstUseWholeShape },
364     { "max_distance", QgsSymbolLayer::PropertyShapeburstMaxDistance },
365     { "ignore_rings", QgsSymbolLayer::PropertyShapeburstIgnoreRings },
366     { "svgFillColor", QgsSymbolLayer::PropertyFillColor },
367     { "svgOutlineColor", QgsSymbolLayer::PropertyStrokeColor },
368     { "svgOutlineWidth", QgsSymbolLayer::PropertyStrokeWidth },
369     { "svgFile", QgsSymbolLayer::PropertyFile },
370     { "lineangle", QgsSymbolLayer::PropertyLineAngle },
371     { "distance", QgsSymbolLayer::PropertyLineDistance },
372     { "distance_x", QgsSymbolLayer::PropertyDistanceX },
373     { "distance_y", QgsSymbolLayer::PropertyDistanceY },
374     { "displacement_x", QgsSymbolLayer::PropertyDisplacementX },
375     { "displacement_y", QgsSymbolLayer::PropertyDisplacementY },
376     { "file", QgsSymbolLayer::PropertyFile },
377     { "alpha", QgsSymbolLayer::PropertyOpacity },
378     { "customdash", QgsSymbolLayer::PropertyCustomDash },
379     { "line_style", QgsSymbolLayer::PropertyStrokeStyle },
380     { "joinstyle", QgsSymbolLayer::PropertyJoinStyle },
381     { "capstyle", QgsSymbolLayer::PropertyCapStyle },
382     { "placement", QgsSymbolLayer::PropertyPlacement },
383     { "interval", QgsSymbolLayer::PropertyInterval },
384     { "offset_along_line", QgsSymbolLayer::PropertyOffsetAlongLine },
385     { "name", QgsSymbolLayer::PropertyName },
386     { "size", QgsSymbolLayer::PropertySize },
387     { "fill", QgsSymbolLayer::PropertyFillColor },
388     { "outline", QgsSymbolLayer::PropertyStrokeColor },
389     { "char", QgsSymbolLayer::PropertyCharacter },
390     { "enabled", QgsSymbolLayer::PropertyLayerEnabled },
391     { "rotation", QgsSymbolLayer::PropertyAngle },
392     { "horizontal_anchor_point", QgsSymbolLayer::PropertyHorizontalAnchor },
393     { "vertical_anchor_point", QgsSymbolLayer::PropertyVerticalAnchor },
394   };
395 
396   QVariantMap::const_iterator propIt = stringMap.constBegin();
397   for ( ; propIt != stringMap.constEnd(); ++propIt )
398   {
399     std::unique_ptr<QgsProperty> prop;
400     QString propertyName;
401 
402     if ( propIt.key().endsWith( QLatin1String( "_dd_expression" ) ) )
403     {
404       //found a data defined property
405 
406       //get data defined property name by stripping "_dd_expression" from property key
407       propertyName = propIt.key().left( propIt.key().length() - 14 );
408 
409       prop = std::make_unique<QgsProperty>( propertyFromMap( stringMap, propertyName ) );
410     }
411     else if ( propIt.key().endsWith( QLatin1String( "_expression" ) ) )
412     {
413       //old style data defined property, upgrade
414 
415       //get data defined property name by stripping "_expression" from property key
416       propertyName = propIt.key().left( propIt.key().length() - 11 );
417 
418       prop = std::make_unique<QgsProperty>( QgsProperty::fromExpression( propIt.value().toString() ) );
419     }
420 
421     if ( !prop || !OLD_PROPS.contains( propertyName ) )
422       continue;
423 
424     QgsSymbolLayer::Property key = static_cast< QgsSymbolLayer::Property >( OLD_PROPS.value( propertyName ) );
425 
426     if ( type() == Qgis::SymbolType::Line )
427     {
428       //these keys had different meaning for line symbol layers
429       if ( propertyName == QLatin1String( "width" ) )
430         key = QgsSymbolLayer::PropertyStrokeWidth;
431       else if ( propertyName == QLatin1String( "color" ) )
432         key = QgsSymbolLayer::PropertyStrokeColor;
433     }
434 
435     setDataDefinedProperty( key, QgsProperty( *prop.get() ) );
436   }
437 }
438 
copyDataDefinedProperties(QgsSymbolLayer * destLayer) const439 void QgsSymbolLayer::copyDataDefinedProperties( QgsSymbolLayer *destLayer ) const
440 {
441   if ( !destLayer )
442     return;
443 
444   destLayer->setDataDefinedProperties( mDataDefinedProperties );
445 }
446 
copyPaintEffect(QgsSymbolLayer * destLayer) const447 void QgsSymbolLayer::copyPaintEffect( QgsSymbolLayer *destLayer ) const
448 {
449   if ( !destLayer || !mPaintEffect )
450     return;
451 
452   if ( !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect.get() ) )
453     destLayer->setPaintEffect( mPaintEffect->clone() );
454   else
455     destLayer->setPaintEffect( nullptr );
456 }
457 
QgsMarkerSymbolLayer(bool locked)458 QgsMarkerSymbolLayer::QgsMarkerSymbolLayer( bool locked )
459   : QgsSymbolLayer( Qgis::SymbolType::Marker, locked )
460 {
461 
462 }
463 
QgsLineSymbolLayer(bool locked)464 QgsLineSymbolLayer::QgsLineSymbolLayer( bool locked )
465   : QgsSymbolLayer( Qgis::SymbolType::Line, locked )
466 {
467 }
468 
ringFilter() const469 QgsLineSymbolLayer::RenderRingFilter QgsLineSymbolLayer::ringFilter() const
470 {
471   return mRingFilter;
472 }
473 
setRingFilter(const RenderRingFilter filter)474 void QgsLineSymbolLayer::setRingFilter( const RenderRingFilter filter )
475 {
476   mRingFilter = filter;
477 }
478 
QgsFillSymbolLayer(bool locked)479 QgsFillSymbolLayer::QgsFillSymbolLayer( bool locked )
480   : QgsSymbolLayer( Qgis::SymbolType::Fill, locked )
481 {
482 }
483 
startRender(QgsSymbolRenderContext & context)484 void QgsMarkerSymbolLayer::startRender( QgsSymbolRenderContext &context )
485 {
486   Q_UNUSED( context )
487 }
488 
stopRender(QgsSymbolRenderContext & context)489 void QgsMarkerSymbolLayer::stopRender( QgsSymbolRenderContext &context )
490 {
491   Q_UNUSED( context )
492 }
493 
drawPreviewIcon(QgsSymbolRenderContext & context,QSize size)494 void QgsMarkerSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSize size )
495 {
496   startRender( context );
497   QgsPaintEffect *effect = paintEffect();
498 
499   QPolygonF points = context.patchShape() ? context.patchShape()->toQPolygonF( Qgis::SymbolType::Marker, size ).value( 0 ).value( 0 )
500                      : QgsStyle::defaultStyle()->defaultPatchAsQPolygonF( Qgis::SymbolType::Marker, size ).value( 0 ).value( 0 );
501 
502   std::unique_ptr< QgsEffectPainter > effectPainter;
503   if ( effect && effect->enabled() )
504     effectPainter = std::make_unique< QgsEffectPainter >( context.renderContext(), effect );
505 
506   for ( QPointF point : std::as_const( points ) )
507     renderPoint( point, context );
508 
509   effectPainter.reset();
510 
511   stopRender( context );
512 }
513 
markerOffset(QgsSymbolRenderContext & context,double & offsetX,double & offsetY) const514 void QgsMarkerSymbolLayer::markerOffset( QgsSymbolRenderContext &context, double &offsetX, double &offsetY ) const
515 {
516   markerOffset( context, mSize, mSize, mSizeUnit, mSizeUnit, offsetX, offsetY, mSizeMapUnitScale, mSizeMapUnitScale );
517 }
518 
markerOffset(QgsSymbolRenderContext & context,double width,double height,double & offsetX,double & offsetY) const519 void QgsMarkerSymbolLayer::markerOffset( QgsSymbolRenderContext &context, double width, double height, double &offsetX, double &offsetY ) const
520 {
521   markerOffset( context, width, height, mSizeUnit, mSizeUnit, offsetX, offsetY, mSizeMapUnitScale, mSizeMapUnitScale );
522 }
523 
markerOffset(QgsSymbolRenderContext & context,double width,double height,QgsUnitTypes::RenderUnit widthUnit,QgsUnitTypes::RenderUnit heightUnit,double & offsetX,double & offsetY,const QgsMapUnitScale & widthMapUnitScale,const QgsMapUnitScale & heightMapUnitScale) const524 void QgsMarkerSymbolLayer::markerOffset( QgsSymbolRenderContext &context, double width, double height,
525     QgsUnitTypes::RenderUnit widthUnit, QgsUnitTypes::RenderUnit heightUnit,
526     double &offsetX, double &offsetY, const QgsMapUnitScale &widthMapUnitScale, const QgsMapUnitScale &heightMapUnitScale ) const
527 {
528   offsetX = mOffset.x();
529   offsetY = mOffset.y();
530 
531   if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyOffset ) )
532   {
533     context.setOriginalValueVariable( QgsSymbolLayerUtils::encodePoint( mOffset ) );
534     QVariant exprVal = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext() );
535     bool ok = false;
536     const QPointF offset = QgsSymbolLayerUtils::toPoint( exprVal, &ok );
537     if ( ok )
538     {
539       offsetX = offset.x();
540       offsetY = offset.y();
541     }
542   }
543 
544   offsetX = context.renderContext().convertToPainterUnits( offsetX, mOffsetUnit, mOffsetMapUnitScale );
545   offsetY = context.renderContext().convertToPainterUnits( offsetY, mOffsetUnit, mOffsetMapUnitScale );
546 
547   HorizontalAnchorPoint horizontalAnchorPoint = mHorizontalAnchorPoint;
548   VerticalAnchorPoint verticalAnchorPoint = mVerticalAnchorPoint;
549   if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHorizontalAnchor ) )
550   {
551     QVariant exprVal = mDataDefinedProperties.value( QgsSymbolLayer::PropertyHorizontalAnchor, context.renderContext().expressionContext() );
552     if ( !exprVal.isNull() )
553     {
554       horizontalAnchorPoint = decodeHorizontalAnchorPoint( exprVal.toString() );
555     }
556   }
557   if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyVerticalAnchor ) )
558   {
559     QVariant exprVal = mDataDefinedProperties.value( QgsSymbolLayer::PropertyVerticalAnchor, context.renderContext().expressionContext() );
560     if ( !exprVal.isNull() )
561     {
562       verticalAnchorPoint = decodeVerticalAnchorPoint( exprVal.toString() );
563     }
564   }
565 
566   //correct horizontal position according to anchor point
567   if ( horizontalAnchorPoint == HCenter && verticalAnchorPoint == VCenter )
568   {
569     return;
570   }
571 
572   double anchorPointCorrectionX = context.renderContext().convertToPainterUnits( width, widthUnit, widthMapUnitScale ) / 2.0;
573   if ( widthUnit == QgsUnitTypes::RenderMetersInMapUnits && context.renderContext().flags() & Qgis::RenderContextFlag::RenderSymbolPreview )
574   {
575     // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
576     // and clamp it to a reasonable range. It's the best we can do in this situation!
577     anchorPointCorrectionX = std::min( std::max( context.renderContext().convertToPainterUnits( width, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 ) / 2.0;
578   }
579 
580   double anchorPointCorrectionY = context.renderContext().convertToPainterUnits( height, heightUnit, heightMapUnitScale ) / 2.0;
581   if ( heightUnit == QgsUnitTypes::RenderMetersInMapUnits && context.renderContext().flags() & Qgis::RenderContextFlag::RenderSymbolPreview )
582   {
583     // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
584     // and clamp it to a reasonable range. It's the best we can do in this situation!
585     anchorPointCorrectionY = std::min( std::max( context.renderContext().convertToPainterUnits( height, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 ) / 2.0;
586   }
587 
588   if ( horizontalAnchorPoint == Left )
589   {
590     offsetX += anchorPointCorrectionX;
591   }
592   else if ( horizontalAnchorPoint == Right )
593   {
594     offsetX -= anchorPointCorrectionX;
595   }
596 
597   //correct vertical position according to anchor point
598   if ( verticalAnchorPoint == Top )
599   {
600     offsetY += anchorPointCorrectionY;
601   }
602   else if ( verticalAnchorPoint == Bottom )
603   {
604     offsetY -= anchorPointCorrectionY;
605   }
606 }
607 
_rotatedOffset(QPointF offset,double angle)608 QPointF QgsMarkerSymbolLayer::_rotatedOffset( QPointF offset, double angle )
609 {
610   angle = DEG2RAD( angle );
611   double c = std::cos( angle ), s = std::sin( angle );
612   return QPointF( offset.x() * c - offset.y() * s, offset.x() * s + offset.y() * c );
613 }
614 
decodeHorizontalAnchorPoint(const QString & str)615 QgsMarkerSymbolLayer::HorizontalAnchorPoint QgsMarkerSymbolLayer::decodeHorizontalAnchorPoint( const QString &str )
616 {
617   if ( str.compare( QLatin1String( "left" ), Qt::CaseInsensitive ) == 0 )
618   {
619     return QgsMarkerSymbolLayer::Left;
620   }
621   else if ( str.compare( QLatin1String( "right" ), Qt::CaseInsensitive ) == 0 )
622   {
623     return QgsMarkerSymbolLayer::Right;
624   }
625   else
626   {
627     return QgsMarkerSymbolLayer::HCenter;
628   }
629 }
630 
decodeVerticalAnchorPoint(const QString & str)631 QgsMarkerSymbolLayer::VerticalAnchorPoint QgsMarkerSymbolLayer::decodeVerticalAnchorPoint( const QString &str )
632 {
633   if ( str.compare( QLatin1String( "top" ), Qt::CaseInsensitive ) == 0 )
634   {
635     return QgsMarkerSymbolLayer::Top;
636   }
637   else if ( str.compare( QLatin1String( "bottom" ), Qt::CaseInsensitive ) == 0 )
638   {
639     return QgsMarkerSymbolLayer::Bottom;
640   }
641   else
642   {
643     return QgsMarkerSymbolLayer::VCenter;
644   }
645 }
646 
setOutputUnit(QgsUnitTypes::RenderUnit unit)647 void QgsMarkerSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit unit )
648 {
649   mSizeUnit = unit;
650   mOffsetUnit = unit;
651 }
652 
outputUnit() const653 QgsUnitTypes::RenderUnit QgsMarkerSymbolLayer::outputUnit() const
654 {
655   if ( mOffsetUnit != mSizeUnit )
656   {
657     return QgsUnitTypes::RenderUnknownUnit;
658   }
659   return mOffsetUnit;
660 }
661 
setMapUnitScale(const QgsMapUnitScale & scale)662 void QgsMarkerSymbolLayer::setMapUnitScale( const QgsMapUnitScale &scale )
663 {
664   mSizeMapUnitScale = scale;
665   mOffsetMapUnitScale = scale;
666 }
667 
mapUnitScale() const668 QgsMapUnitScale QgsMarkerSymbolLayer::mapUnitScale() const
669 {
670   if ( mSizeMapUnitScale == mOffsetMapUnitScale )
671   {
672     return mSizeMapUnitScale;
673   }
674   return QgsMapUnitScale();
675 }
676 
setOutputUnit(QgsUnitTypes::RenderUnit unit)677 void QgsLineSymbolLayer::setOutputUnit( QgsUnitTypes::RenderUnit unit )
678 {
679   mWidthUnit = unit;
680 }
681 
outputUnit() const682 QgsUnitTypes::RenderUnit QgsLineSymbolLayer::outputUnit() const
683 {
684   return mWidthUnit;
685 }
686 
setMapUnitScale(const QgsMapUnitScale & scale)687 void QgsLineSymbolLayer::setMapUnitScale( const QgsMapUnitScale &scale )
688 {
689   mWidthMapUnitScale = scale;
690 }
691 
mapUnitScale() const692 QgsMapUnitScale QgsLineSymbolLayer::mapUnitScale() const
693 {
694   return mWidthMapUnitScale;
695 }
696 
697 
drawPreviewIcon(QgsSymbolRenderContext & context,QSize size)698 void QgsLineSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSize size )
699 {
700   const QList< QList< QPolygonF > > points = context.patchShape() ? context.patchShape()->toQPolygonF( Qgis::SymbolType::Line, size )
701       : QgsStyle::defaultStyle()->defaultPatchAsQPolygonF( Qgis::SymbolType::Line, size );
702   startRender( context );
703   QgsPaintEffect *effect = paintEffect();
704 
705   std::unique_ptr< QgsEffectPainter > effectPainter;
706   if ( effect && effect->enabled() )
707     effectPainter = std::make_unique< QgsEffectPainter >( context.renderContext(), effect );
708 
709   for ( const QList< QPolygonF > &line : points )
710     renderPolyline( line.value( 0 ), context );
711 
712   effectPainter.reset();
713 
714   stopRender( context );
715 }
716 
renderPolygonStroke(const QPolygonF & points,const QVector<QPolygonF> * rings,QgsSymbolRenderContext & context)717 void QgsLineSymbolLayer::renderPolygonStroke( const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
718 {
719   QgsExpressionContextScope *scope = nullptr;
720   std::unique_ptr< QgsExpressionContextScopePopper > scopePopper;
721   if ( hasDataDefinedProperties() )
722   {
723     scope = new QgsExpressionContextScope();
724     scopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.renderContext().expressionContext(), scope );
725   }
726 
727   switch ( mRingFilter )
728   {
729     case AllRings:
730     case ExteriorRingOnly:
731     {
732       if ( scope )
733         scope->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_RING_NUM, 0, true ) );
734       renderPolyline( points, context );
735       break;
736     }
737     case InteriorRingsOnly:
738       break;
739   }
740 
741   if ( rings )
742   {
743     switch ( mRingFilter )
744     {
745       case AllRings:
746       case InteriorRingsOnly:
747       {
748         int ringIndex = 1;
749         for ( const QPolygonF &ring : std::as_const( *rings ) )
750         {
751           if ( scope )
752             scope->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_RING_NUM, ringIndex, true ) );
753 
754           renderPolyline( ring, context );
755           ringIndex++;
756         }
757       }
758       break;
759       case ExteriorRingOnly:
760         break;
761     }
762   }
763 }
764 
width(const QgsRenderContext & context) const765 double QgsLineSymbolLayer::width( const QgsRenderContext &context ) const
766 {
767   return context.convertToPainterUnits( mWidth, mWidthUnit, mWidthMapUnitScale );
768 }
769 
dxfWidth(const QgsDxfExport & e,QgsSymbolRenderContext & context) const770 double QgsLineSymbolLayer::dxfWidth( const QgsDxfExport &e, QgsSymbolRenderContext &context ) const
771 {
772   Q_UNUSED( context )
773   return width() * e.mapUnitScaleFactor( e.symbologyScale(), widthUnit(), e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
774 }
775 
776 
drawPreviewIcon(QgsSymbolRenderContext & context,QSize size)777 void QgsFillSymbolLayer::drawPreviewIcon( QgsSymbolRenderContext &context, QSize size )
778 {
779   const QList< QList< QPolygonF > > polys = context.patchShape() ? context.patchShape()->toQPolygonF( Qgis::SymbolType::Fill, size )
780       : QgsStyle::defaultStyle()->defaultPatchAsQPolygonF( Qgis::SymbolType::Fill, size );
781 
782   startRender( context );
783   QgsPaintEffect *effect = paintEffect();
784 
785   std::unique_ptr< QgsEffectPainter > effectPainter;
786   if ( effect && effect->enabled() )
787     effectPainter = std::make_unique< QgsEffectPainter >( context.renderContext(), effect );
788 
789   for ( const QList< QPolygonF > &poly : polys )
790   {
791     QVector< QPolygonF > rings;
792     for ( int i = 1; i < poly.size(); ++i )
793       rings << poly.at( i );
794     renderPolygon( poly.value( 0 ), &rings, context );
795   }
796 
797   effectPainter.reset();
798 
799   stopRender( context );
800 }
801 
_renderPolygon(QPainter * p,const QPolygonF & points,const QVector<QPolygonF> * rings,QgsSymbolRenderContext & context)802 void QgsFillSymbolLayer::_renderPolygon( QPainter *p, const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
803 {
804   if ( !p )
805   {
806     return;
807   }
808 
809   // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #5 points).
810   if ( points.size() <= 5 &&
811        ( context.renderContext().vectorSimplifyMethod().simplifyHints() & QgsVectorSimplifyMethod::AntialiasingSimplification ) &&
812        QgsAbstractGeometrySimplifier::isGeneralizableByDeviceBoundingBox( points, context.renderContext().vectorSimplifyMethod().threshold() ) &&
813        ( p->renderHints() & QPainter::Antialiasing ) )
814   {
815     p->setRenderHint( QPainter::Antialiasing, false );
816     p->drawRect( points.boundingRect() );
817     p->setRenderHint( QPainter::Antialiasing, true );
818     return;
819   }
820 
821   // polygons outlines are sometimes rendered wrongly with drawPolygon, when
822   // clipped (see #13343), so use drawPath instead.
823   if ( !rings && p->pen().style() == Qt::NoPen )
824   {
825     // simple polygon without holes
826     p->drawPolygon( points );
827   }
828   else
829   {
830     // polygon with holes must be drawn using painter path
831     QPainterPath path;
832     path.addPolygon( points );
833 
834     if ( rings )
835     {
836       for ( auto it = rings->constBegin(); it != rings->constEnd(); ++it )
837       {
838         QPolygonF ring = *it;
839         path.addPolygon( ring );
840       }
841     }
842 
843     p->drawPath( path );
844   }
845 }
846 
toSld(QDomDocument & doc,QDomElement & element,const QVariantMap & props) const847 void QgsMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
848 {
849   QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PointSymbolizer" ) );
850   if ( !props.value( QStringLiteral( "uom" ), QString() ).toString().isEmpty() )
851     symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ).toString() );
852   element.appendChild( symbolizerElem );
853 
854   // <Geometry>
855   QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ).toString() );
856 
857   writeSldMarker( doc, symbolizerElem, props );
858 }
859 
masks() const860 QList<QgsSymbolLayerReference> QgsSymbolLayer::masks() const
861 {
862   return {};
863 }
864 
865