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 : 1073 : p.drawLine( 50, 0, 50, 100 );
716 : 0 : }
717 : :
718 : 1073 : QgsRenderContext context = QgsRenderContext::fromQPainter( &p );
719 : 1073 : context.setFlag( QgsRenderContext::RenderSymbolPreview );
720 : 0 : if ( expressionContext )
721 : 1073 : 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 : 1073 : }
738 : : else // marker
739 : : {
740 : 0 : static_cast<QgsMarkerSymbol *>( this )->renderPoint( QPointF( 50, 50 ), nullptr, context );
741 : : }
742 : :
743 : 0 : stopRender( context );
744 : 0 : return preview;
745 : 0 : }
746 : :
747 : :
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 : : 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 : : };
1016 : 0 : QVector< PolygonInfo > polygonsToRender;
1017 : :
1018 : 0 : std::function< void ( const QgsAbstractGeometry * )> getPartGeometry;
1019 : 0 : getPartGeometry = [&pointsToRender, &linesToRender, &polygonsToRender, &getPartGeometry, &context, &clippingEnabled, &markers, &feature, &usingSegmentizedGeometry, this]( const QgsAbstractGeometry * part )
1020 : : {
1021 : 0 : Q_UNUSED( feature )
1022 : :
1023 : 0 : if ( !part )
1024 : 0 : return;
1025 : :
1026 : : // geometry preprocessing
1027 : 0 : QgsGeometry temporaryGeometryContainer;
1028 : 0 : const QgsAbstractGeometry *processedGeometry = nullptr;
1029 : :
1030 : 0 : const bool isMultiPart = qgsgeometry_cast< const QgsGeometryCollection * >( part ) && qgsgeometry_cast< const QgsGeometryCollection * >( part )->numGeometries() > 1;
1031 : :
1032 : 0 : if ( !isMultiPart )
1033 : : {
1034 : : // segmentize curved geometries
1035 : 0 : const bool needsSegmentizing = QgsWkbTypes::isCurvedType( part->wkbType() ) || part->hasCurvedSegments();
1036 : 0 : if ( needsSegmentizing )
1037 : : {
1038 : 0 : std::unique_ptr< QgsAbstractGeometry > segmentizedPart( part->segmentize( context.segmentationTolerance(), context.segmentationToleranceType() ) );
1039 : 0 : if ( !segmentizedPart )
1040 : : {
1041 : 0 : return;
1042 : : }
1043 : 0 : temporaryGeometryContainer.set( segmentizedPart.release() );
1044 : 0 : processedGeometry = temporaryGeometryContainer.constGet();
1045 : 0 : usingSegmentizedGeometry = true;
1046 : 0 : }
1047 : : else
1048 : : {
1049 : : // no segmentation required
1050 : 0 : processedGeometry = part;
1051 : : }
1052 : :
1053 : : // Simplify the geometry, if needed.
1054 : 0 : if ( context.vectorSimplifyMethod().forceLocalOptimization() )
1055 : : {
1056 : 0 : const int simplifyHints = context.vectorSimplifyMethod().simplifyHints();
1057 : 0 : const QgsMapToPixelSimplifier simplifier( simplifyHints, context.vectorSimplifyMethod().tolerance(),
1058 : 0 : static_cast< QgsMapToPixelSimplifier::SimplifyAlgorithm >( context.vectorSimplifyMethod().simplifyAlgorithm() ) );
1059 : :
1060 : 0 : std::unique_ptr< QgsAbstractGeometry > simplified( simplifier.simplify( processedGeometry ) );
1061 : 0 : if ( simplified )
1062 : : {
1063 : 0 : temporaryGeometryContainer.set( simplified.release() );
1064 : 0 : processedGeometry = temporaryGeometryContainer.constGet();
1065 : 0 : }
1066 : 0 : }
1067 : :
1068 : : // clip geometry to render context clipping regions
1069 : 0 : if ( !context.featureClipGeometry().isEmpty() )
1070 : : {
1071 : : // apply feature clipping from context to the rendered geometry only -- just like the render time simplification,
1072 : : // we should NEVER apply this to the geometry attached to the feature itself. Doing so causes issues with certain
1073 : : // renderer settings, e.g. if polygons are being rendered using a rule based renderer based on the feature's area,
1074 : : // then we need to ensure that the original feature area is used instead of the clipped area..
1075 : 0 : QgsGeos geos( processedGeometry );
1076 : 0 : std::unique_ptr< QgsAbstractGeometry > clippedGeom( geos.intersection( context.featureClipGeometry().constGet() ) );
1077 : 0 : if ( clippedGeom )
1078 : : {
1079 : 0 : temporaryGeometryContainer.set( clippedGeom.release() );
1080 : 0 : processedGeometry = temporaryGeometryContainer.constGet();
1081 : 0 : }
1082 : 0 : }
1083 : 0 : }
1084 : : else
1085 : : {
1086 : : // for multipart geometries, the processing is deferred till we're rendering the actual part...
1087 : 0 : processedGeometry = part;
1088 : : }
1089 : :
1090 : 0 : if ( !processedGeometry )
1091 : : {
1092 : : // shouldn't happen!
1093 : 0 : QgsDebugMsg( QStringLiteral( "No processed geometry to render for part!" ) );
1094 : 0 : return;
1095 : : }
1096 : :
1097 : 0 : switch ( QgsWkbTypes::flatType( processedGeometry->wkbType() ) )
1098 : : {
1099 : : case QgsWkbTypes::Point:
1100 : : {
1101 : 0 : if ( mType != QgsSymbol::Marker )
1102 : : {
1103 : 0 : QgsDebugMsgLevel( QStringLiteral( "point can be drawn only with marker symbol!" ), 2 );
1104 : 0 : break;
1105 : : }
1106 : :
1107 : 0 : PointInfo info;
1108 : 0 : info.originalGeometry = qgsgeometry_cast< const QgsPoint * >( part );
1109 : 0 : info.renderPoint = _getPoint( context, *info.originalGeometry );
1110 : 0 : pointsToRender << info;
1111 : 0 : break;
1112 : : }
1113 : :
1114 : : case QgsWkbTypes::LineString:
1115 : : {
1116 : 0 : if ( mType != QgsSymbol::Line )
1117 : : {
1118 : 0 : QgsDebugMsgLevel( QStringLiteral( "linestring can be drawn only with line symbol!" ), 2 );
1119 : 0 : break;
1120 : : }
1121 : :
1122 : 0 : LineInfo info;
1123 : 0 : info.originalGeometry = qgsgeometry_cast<const QgsCurve *>( part );
1124 : 0 : info.renderLine = _getLineString( context, *qgsgeometry_cast<const QgsCurve *>( processedGeometry ), clippingEnabled );
1125 : 0 : linesToRender << info;
1126 : : break;
1127 : 0 : }
1128 : :
1129 : : case QgsWkbTypes::Polygon:
1130 : : case QgsWkbTypes::Triangle:
1131 : : {
1132 : 0 : QPolygonF pts;
1133 : 0 : if ( mType != QgsSymbol::Fill )
1134 : : {
1135 : 0 : QgsDebugMsgLevel( QStringLiteral( "polygon can be drawn only with fill symbol!" ), 2 );
1136 : 0 : break;
1137 : : }
1138 : :
1139 : 0 : PolygonInfo info;
1140 : 0 : info.originalGeometry = qgsgeometry_cast<const QgsCurvePolygon *>( part );
1141 : 0 : if ( !qgsgeometry_cast<const QgsPolygon *>( processedGeometry )->exteriorRing() )
1142 : : {
1143 : 0 : QgsDebugMsg( QStringLiteral( "cannot render polygon with no exterior ring" ) );
1144 : 0 : break;
1145 : : }
1146 : :
1147 : 0 : _getPolygon( info.renderExterior, info.renderRings, context, *qgsgeometry_cast<const QgsPolygon *>( processedGeometry ), clippingEnabled, mForceRHR );
1148 : 0 : polygonsToRender << info;
1149 : 0 : break;
1150 : 0 : }
1151 : :
1152 : : case QgsWkbTypes::MultiPoint:
1153 : : {
1154 : 0 : const QgsMultiPoint *mp = qgsgeometry_cast< const QgsMultiPoint * >( part );
1155 : 0 : markers.reserve( mp->numGeometries() );
1156 : 0 : }
1157 : : FALLTHROUGH
1158 : : case QgsWkbTypes::MultiCurve:
1159 : : case QgsWkbTypes::MultiLineString:
1160 : : case QgsWkbTypes::GeometryCollection:
1161 : : {
1162 : 0 : const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( part );
1163 : :
1164 : 0 : const unsigned int num = geomCollection->numGeometries();
1165 : 0 : for ( unsigned int i = 0; i < num; ++i )
1166 : : {
1167 : 0 : if ( context.renderingStopped() )
1168 : 0 : break;
1169 : :
1170 : 0 : getPartGeometry( geomCollection->geometryN( i ) );
1171 : 0 : }
1172 : 0 : break;
1173 : : }
1174 : :
1175 : : case QgsWkbTypes::MultiSurface:
1176 : : case QgsWkbTypes::MultiPolygon:
1177 : : {
1178 : 0 : if ( mType != QgsSymbol::Fill )
1179 : : {
1180 : 0 : QgsDebugMsgLevel( QStringLiteral( "multi-polygon can be drawn only with fill symbol!" ), 2 );
1181 : 0 : break;
1182 : : }
1183 : :
1184 : 0 : QPolygonF pts;
1185 : :
1186 : 0 : const QgsGeometryCollection *geomCollection = dynamic_cast<const QgsGeometryCollection *>( part );
1187 : 0 : const unsigned int num = geomCollection->numGeometries();
1188 : :
1189 : : // Sort components by approximate area (probably a bit faster than using
1190 : : // area() )
1191 : 0 : std::map<double, QList<unsigned int> > thisAreaToPartNum;
1192 : 0 : for ( unsigned int i = 0; i < num; ++i )
1193 : : {
1194 : 0 : const QgsRectangle r( geomCollection->geometryN( i )->boundingBox() );
1195 : 0 : thisAreaToPartNum[ r.width() * r.height()] << i;
1196 : 0 : }
1197 : :
1198 : : // Draw starting with larger parts down to smaller parts, so that in
1199 : : // case of a part being incorrectly inside another part, it is drawn
1200 : : // on top of it (#15419)
1201 : 0 : std::map<double, QList<unsigned int> >::const_reverse_iterator iter = thisAreaToPartNum.rbegin();
1202 : 0 : for ( ; iter != thisAreaToPartNum.rend(); ++iter )
1203 : : {
1204 : 0 : const QList<unsigned int> &listPartIndex = iter->second;
1205 : 0 : for ( int idx = 0; idx < listPartIndex.size(); ++idx )
1206 : : {
1207 : 0 : const unsigned i = listPartIndex[idx];
1208 : 0 : getPartGeometry( geomCollection->geometryN( i ) );
1209 : 0 : }
1210 : 0 : }
1211 : : break;
1212 : 0 : }
1213 : :
1214 : : default:
1215 : 0 : QgsDebugMsg( QStringLiteral( "feature %1: unsupported wkb type %2/%3 for rendering" )
1216 : : .arg( feature.id() )
1217 : : .arg( QgsWkbTypes::displayString( part->wkbType() ) )
1218 : : .arg( part->wkbType(), 0, 16 ) );
1219 : 0 : }
1220 : 0 : };
1221 : :
1222 : 0 : getPartGeometry( geom.constGet() );
1223 : :
1224 : : // step 2 - determine which layers to render
1225 : 0 : std::vector< int > layers;
1226 : 0 : if ( layer == -1 )
1227 : : {
1228 : 0 : layers.reserve( mLayers.count() );
1229 : 0 : for ( int i = 0; i < mLayers.count(); ++i )
1230 : 0 : layers.emplace_back( i );
1231 : 0 : }
1232 : : else
1233 : : {
1234 : 0 : layers.emplace_back( layer );
1235 : : }
1236 : :
1237 : : // step 3 - render these geometries using the desired symbol layers.
1238 : :
1239 : 0 : if ( needsExpressionContext )
1240 : 0 : mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_count" ), mLayers.count(), true ) );
1241 : :
1242 : 0 : for ( const int symbolLayerIndex : layers )
1243 : : {
1244 : 0 : QgsSymbolLayer *symbolLayer = mLayers.value( symbolLayerIndex );
1245 : 0 : if ( !symbolLayer || !symbolLayer->enabled() )
1246 : 0 : continue;
1247 : :
1248 : 0 : if ( needsExpressionContext )
1249 : 0 : mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_index" ), symbolLayerIndex + 1, true ) );
1250 : :
1251 : 0 : symbolLayer->startFeatureRender( feature, context );
1252 : :
1253 : 0 : switch ( mType )
1254 : : {
1255 : : case QgsSymbol::Marker:
1256 : : {
1257 : 0 : int geometryPartNumber = 0;
1258 : 0 : for ( const PointInfo &point : std::as_const( pointsToRender ) )
1259 : : {
1260 : 0 : if ( context.renderingStopped() )
1261 : 0 : break;
1262 : :
1263 : 0 : mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1264 : 0 : if ( needsExpressionContext )
1265 : 0 : mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1266 : :
1267 : 0 : static_cast<QgsMarkerSymbol *>( this )->renderPoint( point.renderPoint, &feature, context, symbolLayerIndex, selected );
1268 : 0 : geometryPartNumber++;
1269 : : }
1270 : :
1271 : 0 : break;
1272 : : }
1273 : :
1274 : : case QgsSymbol::Line:
1275 : : {
1276 : 0 : if ( linesToRender.empty() )
1277 : 0 : break;
1278 : :
1279 : 0 : int geometryPartNumber = 0;
1280 : 0 : for ( const LineInfo &line : std::as_const( linesToRender ) )
1281 : : {
1282 : 0 : if ( context.renderingStopped() )
1283 : 0 : break;
1284 : :
1285 : 0 : mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1286 : 0 : if ( needsExpressionContext )
1287 : 0 : mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1288 : :
1289 : 0 : context.setGeometry( line.originalGeometry );
1290 : 0 : static_cast<QgsLineSymbol *>( this )->renderPolyline( line.renderLine, &feature, context, symbolLayerIndex, selected );
1291 : 0 : geometryPartNumber++;
1292 : : }
1293 : 0 : break;
1294 : : }
1295 : :
1296 : : case QgsSymbol::Fill:
1297 : : {
1298 : 0 : int geometryPartNumber = 0;
1299 : 0 : for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
1300 : : {
1301 : 0 : if ( context.renderingStopped() )
1302 : 0 : break;
1303 : :
1304 : 0 : mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1305 : 0 : if ( needsExpressionContext )
1306 : 0 : mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1307 : :
1308 : 0 : context.setGeometry( info.originalGeometry );
1309 : 0 : static_cast<QgsFillSymbol *>( this )->renderPolygon( info.renderExterior, ( !info.renderRings.isEmpty() ? &info.renderRings : nullptr ), &feature, context, symbolLayerIndex, selected );
1310 : 0 : geometryPartNumber++;
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 : :
|