QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgssymbol.cpp
Go to the documentation of this file.
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
31#include "qgslogger.h"
32#include "qgsrendercontext.h" // for bigSymbolPreview
33#include "qgsproject.h"
35#include "qgsstyle.h"
36#include "qgspainteffect.h"
37#include "qgsvectorlayer.h"
38#include "qgsfeature.h"
39#include "qgsgeometry.h"
40#include "qgsmultipoint.h"
42#include "qgslinestring.h"
43#include "qgspolygon.h"
44#include "qgsclipper.h"
45#include "qgsproperty.h"
47#include "qgsapplication.h"
50#include "qgslegendpatchshape.h"
51#include "qgsgeos.h"
52#include "qgsmarkersymbol.h"
53#include "qgslinesymbol.h"
54#include "qgsfillsymbol.h"
55
56QgsPropertiesDefinition QgsSymbol::sPropertyDefinitions;
57
58Q_NOWARN_DEPRECATED_PUSH // because of deprecated mLayer
60 : mType( type )
61 , mLayers( layers )
62{
63
64 // check they're all correct symbol layers
65 for ( int i = 0; i < mLayers.count(); i++ )
66 {
67 if ( !mLayers.at( i ) )
68 {
69 mLayers.removeAt( i-- );
70 }
71 else if ( !mLayers.at( i )->isCompatibleWithSymbol( this ) )
72 {
73 delete mLayers.at( i );
74 mLayers.removeAt( i-- );
75 }
76 }
77}
79
80QPolygonF QgsSymbol::_getLineString( QgsRenderContext &context, const QgsCurve &curve, bool clipToExtent )
81{
82 if ( curve.is3D() )
83 return _getLineString3d( context, curve, clipToExtent );
84 else
85 return _getLineString2d( context, curve, clipToExtent );
86}
87
88QPolygonF QgsSymbol::_getLineString3d( QgsRenderContext &context, const QgsCurve &curve, bool clipToExtent )
89{
90 const unsigned int nPoints = curve.numPoints();
91
93 const QgsMapToPixel &mtp = context.mapToPixel();
94 QVector< double > pointsX;
95 QVector< double > pointsY;
96 QVector< double > pointsZ;
97
98 // apply clipping for large lines to achieve a better rendering performance
99 if ( clipToExtent && nPoints > 1 && !( context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection ) )
100 {
101 const QgsRectangle e = context.extent();
102 const double cw = e.width() / 10;
103 const double ch = e.height() / 10;
104 const QgsBox3d clipRect( e.xMinimum() - cw, e.yMinimum() - ch, -HUGE_VAL, e.xMaximum() + cw, e.yMaximum() + ch, HUGE_VAL ); // TODO also need to be clipped according to z axis
105
106 const QgsLineString *lineString = nullptr;
107 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( &curve ) )
108 {
109 lineString = ls;
110 }
111 else
112 {
113 std::unique_ptr< QgsLineString > segmentized;
114 segmentized.reset( qgsgeometry_cast< QgsLineString * >( curve.segmentize( ) ) );
115 lineString = segmentized.get();
116 }
117
118 QgsClipper::clipped3dLine( lineString->xVector(), lineString->yVector(), lineString->zVector(), pointsX, pointsY, pointsZ, clipRect );
119 }
120 else
121 {
122 // clone...
123 if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( &curve ) )
124 {
125 pointsX = ls->xVector();
126 pointsY = ls->yVector();
127 pointsZ = ls->zVector();
128 }
129 else
130 {
131 std::unique_ptr< QgsLineString > segmentized;
132 segmentized.reset( qgsgeometry_cast< QgsLineString * >( curve.segmentize( ) ) );
133
134 pointsX = segmentized->xVector();
135 pointsY = segmentized->yVector();
136 pointsZ = segmentized->zVector();
137 }
138 }
139
140 // transform the points to screen coordinates
141 const QVector< double > preTransformPointsZ = pointsZ;
142 bool wasTransformed = false;
143 if ( ct.isValid() )
144 {
145 //create x, y arrays
146 const int nVertices = pointsX.size();
147 wasTransformed = true;
148
149 try
150 {
151 ct.transformCoords( nVertices, pointsX.data(), pointsY.data(), pointsZ.data(), Qgis::TransformDirection::Forward );
152 }
153 catch ( QgsCsException & )
154 {
155 // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
156 }
157 }
158
159 // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
160 {
161 const int size = pointsX.size();
162
163 const double *xIn = pointsX.data();
164 const double *yIn = pointsY.data();
165 const double *zIn = pointsZ.data();
166
167 const double *preTransformZIn = wasTransformed ? preTransformPointsZ.constData() : nullptr;
168
169 double *xOut = pointsX.data();
170 double *yOut = pointsY.data();
171 double *zOut = pointsZ.data();
172 int outSize = 0;
173 for ( int i = 0; i < size; ++i )
174 {
175 bool pointOk = std::isfinite( *xIn ) && std::isfinite( *yIn );
176
177 // skip z points which have been made non-finite during transformations only. Ie if:
178 // - we did no transformation, then always render even if non-finite z
179 // - we did transformation and z is finite then render
180 // - we did transformation and z is non-finite BUT input z was also non finite then render
181 // - we did transformation and z is non-finite AND input z WAS finite then skip
182 pointOk &= !wasTransformed || std::isfinite( *zIn ) || !std::isfinite( *preTransformZIn );
183
184 if ( pointOk )
185 {
186 *xOut++ = *xIn++;
187 *yOut++ = *yIn++;
188 *zOut++ = *zIn++;
189 outSize++;
190 }
191 else
192 {
193 xIn++;
194 yIn++;
195 zIn++;
196 }
197
198 if ( preTransformZIn )
199 preTransformZIn++;
200 }
201 pointsX.resize( outSize );
202 pointsY.resize( outSize );
203 pointsZ.resize( outSize );
204 }
205
206 if ( clipToExtent && nPoints > 1 && context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection )
207 {
208 // early clipping was not possible, so we have to apply it here after transformation
209 const QgsRectangle e = context.mapExtent();
210 const double cw = e.width() / 10;
211 const double ch = e.height() / 10;
212 const QgsBox3d clipRect( e.xMinimum() - cw, e.yMinimum() - ch, -HUGE_VAL, e.xMaximum() + cw, e.yMaximum() + ch, HUGE_VAL ); // TODO also need to be clipped according to z axis
213
214 QVector< double > tempX;
215 QVector< double > tempY;
216 QVector< double > tempZ;
217 QgsClipper::clipped3dLine( pointsX, pointsY, pointsZ, tempX, tempY, tempZ, clipRect );
218 pointsX = tempX;
219 pointsY = tempY;
220 pointsZ = tempZ;
221 }
222
223 const int polygonSize = pointsX.size();
224 QPolygonF out( polygonSize );
225 const double *x = pointsX.constData();
226 const double *y = pointsY.constData();
227 QPointF *dest = out.data();
228 for ( int i = 0; i < polygonSize; ++i )
229 {
230 double screenX = *x++;
231 double screenY = *y++;
232 mtp.transformInPlace( screenX, screenY );
233 *dest++ = QPointF( screenX, screenY );
234 }
235
236 return out;
237}
238
239QPolygonF QgsSymbol::_getLineString2d( QgsRenderContext &context, const QgsCurve &curve, bool clipToExtent )
240{
241 const unsigned int nPoints = curve.numPoints();
242
244 const QgsMapToPixel &mtp = context.mapToPixel();
245 QPolygonF pts;
246
247 // apply clipping for large lines to achieve a better rendering performance
248 if ( clipToExtent && nPoints > 1 && !( context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection ) )
249 {
250 const QgsRectangle e = context.extent();
251 const double cw = e.width() / 10;
252 const double ch = e.height() / 10;
253 const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
254 pts = QgsClipper::clippedLine( curve, clipRect );
255 }
256 else
257 {
258 pts = curve.asQPolygonF();
259 }
260
261 // transform the QPolygonF to screen coordinates
262 if ( ct.isValid() )
263 {
264 try
265 {
266 ct.transformPolygon( pts );
267 }
268 catch ( QgsCsException & )
269 {
270 // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
271 }
272 }
273
274 // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
275 pts.erase( std::remove_if( pts.begin(), pts.end(),
276 []( const QPointF point )
277 {
278 return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
279 } ), pts.end() );
280
281 if ( clipToExtent && nPoints > 1 && context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection )
282 {
283 // early clipping was not possible, so we have to apply it here after transformation
284 const QgsRectangle e = context.mapExtent();
285 const double cw = e.width() / 10;
286 const double ch = e.height() / 10;
287 const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
288 pts = QgsClipper::clippedLine( pts, clipRect );
289 }
290
291 QPointF *ptr = pts.data();
292 for ( int i = 0; i < pts.size(); ++i, ++ptr )
293 {
294 mtp.transformInPlace( ptr->rx(), ptr->ry() );
295 }
296
297 return pts;
298}
299
300
301QPolygonF QgsSymbol::_getPolygonRing( QgsRenderContext &context, const QgsCurve &curve, const bool clipToExtent, const bool isExteriorRing, const bool correctRingOrientation )
302{
303 if ( curve.is3D() )
304 return _getPolygonRing3d( context, curve, clipToExtent, isExteriorRing, correctRingOrientation );
305 else
306 return _getPolygonRing2d( context, curve, clipToExtent, isExteriorRing, correctRingOrientation );
307}
308
309QPolygonF QgsSymbol::_getPolygonRing3d( QgsRenderContext &context, const QgsCurve &curve, const bool clipToExtent, const bool isExteriorRing, const bool correctRingOrientation )
310{
311 const QgsCoordinateTransform ct = context.coordinateTransform();
312 const QgsMapToPixel &mtp = context.mapToPixel();
313
314 QVector< double > pointsX;
315 QVector< double > pointsY;
316 QVector< double > pointsZ;
317
318 if ( curve.numPoints() < 1 )
319 return QPolygonF();
320
321 bool reverseRing = false;
322 if ( correctRingOrientation )
323 {
324 // ensure consistent polygon ring orientation
325 if ( ( isExteriorRing && curve.orientation() != Qgis::AngularDirection::Clockwise ) || ( !isExteriorRing && curve.orientation() != Qgis::AngularDirection::CounterClockwise ) )
326 {
327 reverseRing = true;
328 }
329 }
330
331 //clip close to view extent, if needed
332 if ( clipToExtent && !( context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection ) && !context.extent().contains( curve.boundingBox() ) )
333 {
334 const QgsRectangle e = context.extent();
335 const double cw = e.width() / 10;
336 const double ch = e.height() / 10;
337 const QgsBox3d clipRect( e.xMinimum() - cw, e.yMinimum() - ch, -HUGE_VAL, e.xMaximum() + cw, e.yMaximum() + ch, HUGE_VAL ); // TODO also need to be clipped according to z axis
338
339 const QgsLineString *lineString = nullptr;
340 std::unique_ptr< QgsLineString > segmentized;
341 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( &curve ) )
342 {
343 lineString = ls;
344 }
345 else
346 {
347 segmentized.reset( qgsgeometry_cast< QgsLineString * >( curve.segmentize( ) ) );
348 lineString = segmentized.get();
349 }
350
351 pointsX = lineString->xVector();
352 pointsY = lineString->yVector();
353 pointsZ = lineString->zVector();
354
355 QgsClipper::trimPolygon( pointsX, pointsY, pointsZ, clipRect );
356 }
357 else
358 {
359 // clone...
360 if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( &curve ) )
361 {
362 pointsX = ls->xVector();
363 pointsY = ls->yVector();
364 pointsZ = ls->zVector();
365 }
366 else
367 {
368 std::unique_ptr< QgsLineString > segmentized;
369 segmentized.reset( qgsgeometry_cast< QgsLineString * >( curve.segmentize( ) ) );
370
371 pointsX = segmentized->xVector();
372 pointsY = segmentized->yVector();
373 pointsZ = segmentized->zVector();
374 }
375 }
376
377 if ( reverseRing )
378 {
379 std::reverse( pointsX.begin(), pointsX.end() );
380 std::reverse( pointsY.begin(), pointsY.end() );
381 std::reverse( pointsZ.begin(), pointsZ.end() );
382 }
383
384 //transform the QPolygonF to screen coordinates
385 const QVector< double > preTransformPointsZ = pointsZ;
386 bool wasTransformed = false;
387 if ( ct.isValid() )
388 {
389 const int nVertices = pointsX.size();
390 wasTransformed = true;
391 try
392 {
393 ct.transformCoords( nVertices, pointsX.data(), pointsY.data(), pointsZ.data(), Qgis::TransformDirection::Forward );
394 }
395 catch ( QgsCsException & )
396 {
397 // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
398 }
399 }
400
401 // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
402 {
403 const int size = pointsX.size();
404
405 const double *xIn = pointsX.data();
406 const double *yIn = pointsY.data();
407 const double *zIn = pointsZ.data();
408
409 const double *preTransformZIn = wasTransformed ? preTransformPointsZ.constData() : nullptr;
410
411 double *xOut = pointsX.data();
412 double *yOut = pointsY.data();
413 double *zOut = pointsZ.data();
414 int outSize = 0;
415 for ( int i = 0; i < size; ++i )
416 {
417 bool pointOk = std::isfinite( *xIn ) && std::isfinite( *yIn );
418 // skip z points which have been made non-finite during transformations only. Ie if:
419 // - we did no transformation, then always render even if non-finite z
420 // - we did transformation and z is finite then render
421 // - we did transformation and z is non-finite BUT input z was also non finite then render
422 // - we did transformation and z is non-finite AND input z WAS finite then skip
423 pointOk &= !wasTransformed || std::isfinite( *zIn ) || !std::isfinite( *preTransformZIn );
424
425 if ( pointOk )
426 {
427 *xOut++ = *xIn++;
428 *yOut++ = *yIn++;
429 *zOut++ = *zIn++;
430 outSize++;
431 }
432 else
433 {
434 xIn++;
435 yIn++;
436 zIn++;
437 }
438
439 if ( preTransformZIn )
440 preTransformZIn++;
441 }
442 pointsX.resize( outSize );
443 pointsY.resize( outSize );
444 pointsZ.resize( outSize );
445 }
446
447 if ( clipToExtent && context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection && !context.mapExtent().contains( curve.boundingBox() ) )
448 {
449 // early clipping was not possible, so we have to apply it here after transformation
450 const QgsRectangle e = context.mapExtent();
451 const double cw = e.width() / 10;
452 const double ch = e.height() / 10;
453 const QgsBox3d clipRect( e.xMinimum() - cw, e.yMinimum() - ch, -HUGE_VAL, e.xMaximum() + cw, e.yMaximum() + ch, HUGE_VAL ); // TODO also need to be clipped according to z axis
454
455 QgsClipper::trimPolygon( pointsX, pointsY, pointsZ, clipRect );
456 }
457
458 const int polygonSize = pointsX.size();
459 QPolygonF out( polygonSize );
460 const double *x = pointsX.constData();
461 const double *y = pointsY.constData();
462 QPointF *dest = out.data();
463 for ( int i = 0; i < polygonSize; ++i )
464 {
465 double screenX = *x++;
466 double screenY = *y++;
467 mtp.transformInPlace( screenX, screenY );
468 *dest++ = QPointF( screenX, screenY );
469 }
470
471 if ( !out.empty() && !out.isClosed() )
472 out << out.at( 0 );
473
474 return out;
475}
476
477
478QPolygonF QgsSymbol::_getPolygonRing2d( QgsRenderContext &context, const QgsCurve &curve, const bool clipToExtent, const bool isExteriorRing, const bool correctRingOrientation )
479{
480 const QgsCoordinateTransform ct = context.coordinateTransform();
481 const QgsMapToPixel &mtp = context.mapToPixel();
482
483 QPolygonF poly = curve.asQPolygonF();
484
485 if ( curve.numPoints() < 1 )
486 return QPolygonF();
487
488 if ( correctRingOrientation )
489 {
490 // ensure consistent polygon ring orientation
491 if ( isExteriorRing && curve.orientation() != Qgis::AngularDirection::Clockwise )
492 std::reverse( poly.begin(), poly.end() );
493 else if ( !isExteriorRing && curve.orientation() != Qgis::AngularDirection::CounterClockwise )
494 std::reverse( poly.begin(), poly.end() );
495 }
496
497 //clip close to view extent, if needed
498 if ( clipToExtent && !( context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection ) && !context.extent().contains( poly.boundingRect() ) )
499 {
500 const QgsRectangle e = context.extent();
501 const double cw = e.width() / 10;
502 const double ch = e.height() / 10;
503 const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
504 QgsClipper::trimPolygon( poly, clipRect );
505 }
506
507 //transform the QPolygonF to screen coordinates
508 if ( ct.isValid() )
509 {
510 try
511 {
512 ct.transformPolygon( poly );
513 }
514 catch ( QgsCsException & )
515 {
516 // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
517 }
518 }
519
520 // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
521 poly.erase( std::remove_if( poly.begin(), poly.end(),
522 []( const QPointF point )
523 {
524 return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
525 } ), poly.end() );
526
527 if ( clipToExtent && context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection && !context.mapExtent().contains( poly.boundingRect() ) )
528 {
529 // early clipping was not possible, so we have to apply it here after transformation
530 const QgsRectangle e = context.mapExtent();
531 const double cw = e.width() / 10;
532 const double ch = e.height() / 10;
533 const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
534 QgsClipper::trimPolygon( poly, clipRect );
535 }
536
537 QPointF *ptr = poly.data();
538 for ( int i = 0; i < poly.size(); ++i, ++ptr )
539 {
540 mtp.transformInPlace( ptr->rx(), ptr->ry() );
541 }
542
543 if ( !poly.empty() && !poly.isClosed() )
544 poly << poly.at( 0 );
545
546 return poly;
547}
548
549void QgsSymbol::_getPolygon( QPolygonF &pts, QVector<QPolygonF> &holes, QgsRenderContext &context, const QgsPolygon &polygon, const bool clipToExtent, const bool correctRingOrientation )
550{
551 holes.clear();
552
553 pts = _getPolygonRing( context, *polygon.exteriorRing(), clipToExtent, true, correctRingOrientation );
554 const int ringCount = polygon.numInteriorRings();
555 holes.reserve( ringCount );
556 for ( int idx = 0; idx < ringCount; idx++ )
557 {
558 const QPolygonF hole = _getPolygonRing( context, *( polygon.interiorRing( idx ) ), clipToExtent, false, correctRingOrientation );
559 if ( !hole.isEmpty() )
560 holes.append( hole );
561 }
562}
563
565{
566 switch ( type )
567 {
569 return QObject::tr( "Marker" );
571 return QObject::tr( "Line" );
573 return QObject::tr( "Fill" );
575 return QObject::tr( "Hybrid" );
576 }
577 return QString();
578}
579
581{
582 switch ( type )
583 {
584 case Qgis::GeometryType::Point:
586 case Qgis::GeometryType::Line:
588 case Qgis::GeometryType::Polygon:
590 case Qgis::GeometryType::Unknown:
591 case Qgis::GeometryType::Null:
593 }
595}
596
598{
599 QgsSymbol::initPropertyDefinitions();
600 return sPropertyDefinitions;
601}
602
604{
605 // delete all symbol layers (we own them, so it's okay)
606 qDeleteAll( mLayers );
607}
608
610{
611 if ( mLayers.empty() )
612 {
613 return Qgis::RenderUnit::Unknown;
614 }
615
616 QgsSymbolLayerList::const_iterator it = mLayers.constBegin();
617
618 Qgis::RenderUnit unit = ( *it )->outputUnit();
619
620 for ( ; it != mLayers.constEnd(); ++it )
621 {
622 if ( ( *it )->outputUnit() != unit )
623 {
624 return Qgis::RenderUnit::Unknown;
625 }
626 }
627 return unit;
628}
629
631{
632 if ( mLayers.empty() )
633 {
634 return false;
635 }
636
637 for ( const QgsSymbolLayer *layer : mLayers )
638 {
639 if ( layer->usesMapUnits() )
640 {
641 return true;
642 }
643 }
644 return false;
645}
646
648{
649 if ( mLayers.empty() )
650 {
651 return QgsMapUnitScale();
652 }
653
654 QgsSymbolLayerList::const_iterator it = mLayers.constBegin();
655 if ( it == mLayers.constEnd() )
656 return QgsMapUnitScale();
657
658 QgsMapUnitScale scale = ( *it )->mapUnitScale();
659 ++it;
660
661 for ( ; it != mLayers.constEnd(); ++it )
662 {
663 if ( ( *it )->mapUnitScale() != scale )
664 {
665 return QgsMapUnitScale();
666 }
667 }
668 return scale;
669}
670
672{
673 const auto constMLayers = mLayers;
674 for ( QgsSymbolLayer *layer : constMLayers )
675 {
676 layer->setOutputUnit( u );
677 }
678}
679
681{
682 const auto constMLayers = mLayers;
683 for ( QgsSymbolLayer *layer : constMLayers )
684 {
685 layer->setMapUnitScale( scale );
686 }
687}
688
690{
691 return mAnimationSettings;
692}
693
695{
696 return mAnimationSettings;
697}
698
700{
701 mAnimationSettings = settings;
702}
703
705{
706 std::unique_ptr< QgsSymbol > s;
707
708 // override global default if project has a default for this type
709 switch ( geomType )
710 {
711 case Qgis::GeometryType::Point:
712 s.reset( QgsProject::instance()->styleSettings()->defaultSymbol( Qgis::SymbolType::Marker ) );
713 break;
714 case Qgis::GeometryType::Line:
715 s.reset( QgsProject::instance()->styleSettings()->defaultSymbol( Qgis::SymbolType::Line ) );
716 break;
717 case Qgis::GeometryType::Polygon:
718 s.reset( QgsProject::instance()->styleSettings()->defaultSymbol( Qgis::SymbolType::Fill ) );
719 break;
720 default:
721 break;
722 }
723
724 // if no default found for this type, get global default (as previously)
725 if ( !s )
726 {
727 switch ( geomType )
728 {
729 case Qgis::GeometryType::Point:
730 s = std::make_unique< QgsMarkerSymbol >();
731 break;
732 case Qgis::GeometryType::Line:
733 s = std::make_unique< QgsLineSymbol >();
734 break;
735 case Qgis::GeometryType::Polygon:
736 s = std::make_unique< QgsFillSymbol >();
737 break;
738 default:
739 QgsDebugMsg( QStringLiteral( "unknown layer's geometry type" ) );
740 return nullptr;
741 }
742 }
743
744 // set opacity
745 s->setOpacity( QgsProject::instance()->styleSettings()->defaultSymbolOpacity() );
746
747 // set random color, it project prefs allow
748 if ( QgsProject::instance()->styleSettings()->randomizeDefaultSymbolColor() )
749 {
750 s->setColor( QgsApplication::colorSchemeRegistry()->fetchRandomStyleColor() );
751 }
752
753 return s.release();
754}
755
757{
758 return mLayers.value( layer );
759}
760
761const QgsSymbolLayer *QgsSymbol::symbolLayer( int layer ) const
762{
763 return mLayers.value( layer );
764}
765
767{
768 if ( index < 0 || index > mLayers.count() ) // can be added also after the last index
769 return false;
770
771 if ( !layer || !layer->isCompatibleWithSymbol( this ) )
772 return false;
773
774 mLayers.insert( index, layer );
775 return true;
776}
777
778
780{
781 if ( !layer || !layer->isCompatibleWithSymbol( this ) )
782 return false;
783
784 mLayers.append( layer );
785 return true;
786}
787
788
790{
791 if ( index < 0 || index >= mLayers.count() )
792 return false;
793
794 delete mLayers.at( index );
795 mLayers.removeAt( index );
796 return true;
797}
798
799
801{
802 if ( index < 0 || index >= mLayers.count() )
803 return nullptr;
804
805 return mLayers.takeAt( index );
806}
807
808
810{
811 QgsSymbolLayer *oldLayer = mLayers.value( index );
812
813 if ( oldLayer == layer )
814 return false;
815
816 if ( !layer || !layer->isCompatibleWithSymbol( this ) )
817 return false;
818
819 delete oldLayer; // first delete the original layer
820 mLayers[index] = layer; // set new layer
821 return true;
822}
823
824
825void QgsSymbol::startRender( QgsRenderContext &context, const QgsFields &fields )
826{
827 Q_ASSERT_X( !mStarted, "startRender", "Rendering has already been started for this symbol instance!" );
828 mStarted = true;
829
830 mSymbolRenderContext.reset( new QgsSymbolRenderContext( context, Qgis::RenderUnit::Unknown, mOpacity, false, mRenderHints, nullptr, fields ) );
831
832 // Why do we need a copy here ? Is it to make sure the symbol layer rendering does not mess with the symbol render context ?
833 // Or is there another profound reason ?
834 QgsSymbolRenderContext symbolContext( context, Qgis::RenderUnit::Unknown, mOpacity, false, mRenderHints, nullptr, fields );
835
836 std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::updateSymbolScope( this, new QgsExpressionContextScope() ) );
837
839 {
840 const long long mapFrameNumber = context.currentFrame();
841 double animationTimeSeconds = 0;
842 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
843 {
844 // render is part of an animation, so we base the calculated frame on that
845 animationTimeSeconds = mapFrameNumber / context.frameRate();
846 }
847 else
848 {
849 // render is outside of animation, so base the calculated frame on the current epoch
850 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
851 }
852
853 const long long symbolFrame = static_cast< long long >( std::floor( animationTimeSeconds * mAnimationSettings.frameRate() ) );
854 scope->setVariable( QStringLiteral( "symbol_frame" ), symbolFrame, true );
855 }
856
857 mSymbolRenderContext->setExpressionContextScope( scope.release() );
858
859 mDataDefinedProperties.prepare( context.expressionContext() );
860
861 const auto constMLayers = mLayers;
862 for ( QgsSymbolLayer *layer : constMLayers )
863 {
864 if ( !layer->enabled() || !context.isSymbolLayerEnabled( layer ) )
865 continue;
866
867 layer->prepareExpressions( symbolContext );
868 layer->prepareMasks( symbolContext );
869 layer->startRender( symbolContext );
870 }
871}
872
874{
875 Q_ASSERT_X( mStarted, "startRender", "startRender was not called for this symbol instance!" );
876 mStarted = false;
877
878 Q_UNUSED( context )
879 if ( mSymbolRenderContext )
880 {
881 const auto constMLayers = mLayers;
882 for ( QgsSymbolLayer *layer : constMLayers )
883 {
884 if ( !layer->enabled() || !context.isSymbolLayerEnabled( layer ) )
885 continue;
886
887 layer->stopRender( *mSymbolRenderContext );
888 }
889 }
890
891 mSymbolRenderContext.reset( nullptr );
892
894 mLayer = nullptr;
896}
897
898void QgsSymbol::setColor( const QColor &color ) const
899{
900 const auto constMLayers = mLayers;
901 for ( QgsSymbolLayer *layer : constMLayers )
902 {
903 if ( !layer->isLocked() )
904 layer->setColor( color );
905 }
906}
907
908QColor QgsSymbol::color() const
909{
910 for ( const QgsSymbolLayer *layer : mLayers )
911 {
912 // return color of the first unlocked layer
913 if ( !layer->isLocked() )
914 {
915 const QColor layerColor = layer->color();
916 if ( layerColor.isValid() )
917 return layerColor;
918 }
919 }
920 return QColor( 0, 0, 0 );
921}
922
923void QgsSymbol::drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext, bool selected, const QgsExpressionContext *expressionContext, const QgsLegendPatchShape *patchShape )
924{
925 QgsRenderContext *context = customContext;
926 std::unique_ptr< QgsRenderContext > tempContext;
927 if ( !context )
928 {
929 tempContext.reset( new QgsRenderContext( QgsRenderContext::fromQPainter( painter ) ) );
930 context = tempContext.get();
932 }
933
934 const bool prevForceVector = context->forceVectorOutput();
935 context->setForceVectorOutput( true );
936
937 const double opacity = expressionContext ? dataDefinedProperties().valueAsDouble( QgsSymbol::PropertyOpacity, *expressionContext, mOpacity ) : mOpacity;
938
939 QgsSymbolRenderContext symbolContext( *context, Qgis::RenderUnit::Unknown, opacity, false, mRenderHints, nullptr );
940 symbolContext.setSelected( selected );
941 switch ( mType )
942 {
944 symbolContext.setOriginalGeometryType( Qgis::GeometryType::Point );
945 break;
947 symbolContext.setOriginalGeometryType( Qgis::GeometryType::Line );
948 break;
950 symbolContext.setOriginalGeometryType( Qgis::GeometryType::Polygon );
951 break;
953 symbolContext.setOriginalGeometryType( Qgis::GeometryType::Unknown );
954 break;
955 }
956
957 if ( patchShape )
958 symbolContext.setPatchShape( *patchShape );
959
960 if ( !customContext && expressionContext )
961 {
962 context->setExpressionContext( *expressionContext );
963 }
964 else if ( !customContext )
965 {
966 // if no render context was passed, build a minimal expression context
967 QgsExpressionContext expContext;
969 context->setExpressionContext( expContext );
970 }
971
972 for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
973 {
974 if ( !layer->enabled() || ( customContext && !customContext->isSymbolLayerEnabled( layer ) ) )
975 continue;
976
978 {
979 // line symbol layer would normally draw just a line
980 // so we override this case to force it to draw a polygon stroke
981 QgsLineSymbolLayer *lsl = dynamic_cast<QgsLineSymbolLayer *>( layer );
982 if ( lsl )
983 {
984 // from QgsFillSymbolLayer::drawPreviewIcon() -- would be nicer to add the
985 // symbol type to QgsSymbolLayer::drawPreviewIcon so this logic could be avoided!
986
987 // hmm... why was this using size -1 ??
988 const QSizeF targetSize = QSizeF( size.width() - 1, size.height() - 1 );
989
990 const QList< QList< QPolygonF > > polys = patchShape ? patchShape->toQPolygonF( Qgis::SymbolType::Fill, targetSize )
992
993 lsl->startRender( symbolContext );
994 QgsPaintEffect *effect = lsl->paintEffect();
995
996 std::unique_ptr< QgsEffectPainter > effectPainter;
997 if ( effect && effect->enabled() )
998 effectPainter = std::make_unique< QgsEffectPainter >( symbolContext.renderContext(), effect );
999
1000 for ( const QList< QPolygonF > &poly : polys )
1001 {
1002 QVector< QPolygonF > rings;
1003 rings.reserve( poly.size() );
1004 for ( int i = 1; i < poly.size(); ++i )
1005 rings << poly.at( i );
1006 lsl->renderPolygonStroke( poly.value( 0 ), &rings, symbolContext );
1007 }
1008
1009 effectPainter.reset();
1010 lsl->stopRender( symbolContext );
1011 }
1012 }
1013 else
1014 layer->drawPreviewIcon( symbolContext, size );
1015 }
1016
1017 context->setForceVectorOutput( prevForceVector );
1018}
1019
1020void QgsSymbol::exportImage( const QString &path, const QString &format, QSize size )
1021{
1022 if ( format.compare( QLatin1String( "svg" ), Qt::CaseInsensitive ) == 0 )
1023 {
1024 QSvgGenerator generator;
1025 generator.setFileName( path );
1026 generator.setSize( size );
1027 generator.setViewBox( QRect( 0, 0, size.height(), size.height() ) );
1028
1029 QPainter painter( &generator );
1030 drawPreviewIcon( &painter, size );
1031 painter.end();
1032 }
1033 else
1034 {
1035 QImage image = asImage( size );
1036 image.save( path );
1037 }
1038}
1039
1040QImage QgsSymbol::asImage( QSize size, QgsRenderContext *customContext )
1041{
1042 QImage image( size, QImage::Format_ARGB32_Premultiplied );
1043 image.fill( 0 );
1044
1045 QPainter p( &image );
1046 p.setRenderHint( QPainter::Antialiasing );
1047 p.setRenderHint( QPainter::SmoothPixmapTransform );
1048
1049 drawPreviewIcon( &p, size, customContext );
1050
1051 return image;
1052}
1053
1054
1055QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, Qgis::SymbolPreviewFlags flags )
1056{
1057 QImage preview( QSize( 100, 100 ), QImage::Format_ARGB32_Premultiplied );
1058 preview.fill( 0 );
1059
1060 QPainter p( &preview );
1061 p.setRenderHint( QPainter::Antialiasing );
1062 p.translate( 0.5, 0.5 ); // shift by half a pixel to avoid blurring due antialiasing
1063
1065 {
1066 p.setPen( QPen( Qt::gray ) );
1067 p.drawLine( 0, 50, 100, 50 );
1068 p.drawLine( 50, 0, 50, 100 );
1069 }
1070
1075 context.setPainterFlagsUsingContext( &p );
1076 if ( expressionContext )
1077 context.setExpressionContext( *expressionContext );
1078
1079 context.setIsGuiPreview( true );
1080 startRender( context );
1081
1083 {
1084 QPolygonF poly;
1085 poly << QPointF( 0, 50 ) << QPointF( 99, 50 );
1086 static_cast<QgsLineSymbol *>( this )->renderPolyline( poly, nullptr, context );
1087 }
1088 else if ( mType == Qgis::SymbolType::Fill )
1089 {
1090 QPolygonF polygon;
1091 polygon << QPointF( 20, 20 ) << QPointF( 80, 20 ) << QPointF( 80, 80 ) << QPointF( 20, 80 ) << QPointF( 20, 20 );
1092 static_cast<QgsFillSymbol *>( this )->renderPolygon( polygon, nullptr, nullptr, context );
1093 }
1094 else // marker
1095 {
1096 static_cast<QgsMarkerSymbol *>( this )->renderPoint( QPointF( 50, 50 ), nullptr, context );
1097 }
1098
1099 stopRender( context );
1100 return preview;
1101}
1102
1103QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, int flags )
1104{
1105 return bigSymbolPreviewImage( expressionContext, static_cast< Qgis::SymbolPreviewFlags >( flags ) );
1106}
1107
1108QString QgsSymbol::dump() const
1109{
1110 QString t;
1111 switch ( type() )
1112 {
1114 t = QStringLiteral( "MARKER" );
1115 break;
1117 t = QStringLiteral( "LINE" );
1118 break;
1120 t = QStringLiteral( "FILL" );
1121 break;
1122 default:
1123 Q_ASSERT( false && "unknown symbol type" );
1124 }
1125 QString s = QStringLiteral( "%1 SYMBOL (%2 layers) color %3" ).arg( t ).arg( mLayers.count() ).arg( QgsSymbolLayerUtils::encodeColor( color() ) );
1126
1127 for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
1128 {
1129 // TODO:
1130 }
1131 return s;
1132}
1133
1134void QgsSymbol::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
1135{
1136 props[ QStringLiteral( "alpha" )] = QString::number( opacity() );
1137 double scaleFactor = 1.0;
1138 props[ QStringLiteral( "uom" )] = QgsSymbolLayerUtils::encodeSldUom( outputUnit(), &scaleFactor );
1139 props[ QStringLiteral( "uomScale" )] = ( !qgsDoubleNear( scaleFactor, 1.0 ) ? qgsDoubleToString( scaleFactor ) : QString() );
1140
1141 for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
1142 {
1143 ( *it )->toSld( doc, element, props );
1144 }
1145}
1146
1148{
1150 for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
1151 {
1152 QgsSymbolLayer *layer = ( *it )->clone();
1153 layer->setLocked( ( *it )->isLocked() );
1154 layer->setRenderingPass( ( *it )->renderingPass() );
1155 layer->setEnabled( ( *it )->enabled() );
1156 layer->setId( ( *it )->id() );
1157 lst.append( layer );
1158 }
1159 return lst;
1160}
1161
1162void QgsSymbol::renderUsingLayer( QgsSymbolLayer *layer, QgsSymbolRenderContext &context, Qgis::GeometryType geometryType, const QPolygonF *points, const QVector<QPolygonF> *rings )
1163{
1164 Q_ASSERT( layer->type() == Qgis::SymbolType::Hybrid );
1165
1166 if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
1167 return;
1168
1169 QgsGeometryGeneratorSymbolLayer *generatorLayer = static_cast<QgsGeometryGeneratorSymbolLayer *>( layer );
1170
1171 QgsPaintEffect *effect = generatorLayer->paintEffect();
1172 if ( effect && effect->enabled() )
1173 {
1174 QgsEffectPainter p( context.renderContext(), effect );
1175 generatorLayer->render( context, geometryType, points, rings );
1176 }
1177 else
1178 {
1179 generatorLayer->render( context, geometryType, points, rings );
1180 }
1181}
1182
1183QSet<QString> QgsSymbol::usedAttributes( const QgsRenderContext &context ) const
1184{
1185 // calling referencedFields() with ignoreContext=true because in our expression context
1186 // we do not have valid QgsFields yet - because of that the field names from expressions
1187 // wouldn't get reported
1188 QSet<QString> attributes = mDataDefinedProperties.referencedFields( context.expressionContext(), true );
1189 QgsSymbolLayerList::const_iterator sIt = mLayers.constBegin();
1190 for ( ; sIt != mLayers.constEnd(); ++sIt )
1191 {
1192 if ( *sIt )
1193 {
1194 attributes.unite( ( *sIt )->usedAttributes( context ) );
1195 }
1196 }
1197 return attributes;
1198}
1199
1201{
1202 mDataDefinedProperties.setProperty( key, property );
1203}
1204
1206{
1207 if ( mDataDefinedProperties.hasActiveProperties() )
1208 return true;
1209
1210 for ( QgsSymbolLayer *layer : mLayers )
1211 {
1212 if ( layer->hasDataDefinedProperties() )
1213 return true;
1214 }
1215 return false;
1216}
1217
1219{
1220 for ( QgsSymbolLayer *layer : mLayers )
1221 {
1222 if ( layer->canCauseArtifactsBetweenAdjacentTiles() )
1223 return true;
1224 }
1225 return false;
1226}
1227
1229{
1231 mLayer = layer;
1233}
1234
1236{
1238 return mLayer;
1240}
1241
1243
1247class ExpressionContextScopePopper
1248{
1249 public:
1250
1251 ExpressionContextScopePopper() = default;
1252
1253 ~ExpressionContextScopePopper()
1254 {
1255 if ( context )
1256 context->popScope();
1257 }
1258
1259 QgsExpressionContext *context = nullptr;
1260};
1261
1265class GeometryRestorer
1266{
1267 public:
1268 GeometryRestorer( QgsRenderContext &context )
1269 : mContext( context ),
1270 mGeometry( context.geometry() )
1271 {}
1272
1273 ~GeometryRestorer()
1274 {
1275 mContext.setGeometry( mGeometry );
1276 }
1277
1278 private:
1279 QgsRenderContext &mContext;
1280 const QgsAbstractGeometry *mGeometry;
1281};
1283
1284void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize )
1285{
1286 if ( context.renderingStopped() )
1287 return;
1288
1289 const QgsGeometry geom = feature.geometry();
1290 if ( geom.isNull() )
1291 {
1292 return;
1293 }
1294
1295 GeometryRestorer geomRestorer( context );
1296
1297 bool usingSegmentizedGeometry = false;
1298 context.setGeometry( geom.constGet() );
1299
1300 if ( geom.type() != Qgis::GeometryType::Point && !geom.boundingBox().isNull() )
1301 {
1302 try
1303 {
1304 const QPointF boundsOrigin = _getPoint( context, QgsPoint( geom.boundingBox().xMinimum(), geom.boundingBox().yMinimum() ) );
1305 if ( std::isfinite( boundsOrigin.x() ) && std::isfinite( boundsOrigin.y() ) )
1306 context.setTextureOrigin( boundsOrigin );
1307 }
1308 catch ( QgsCsException & )
1309 {
1310
1311 }
1312 }
1313
1314 bool clippingEnabled = clipFeaturesToExtent();
1315 // do any symbol layers prevent feature clipping?
1316 for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
1317 {
1319 {
1320 clippingEnabled = false;
1321 break;
1322 }
1323 }
1324 if ( clippingEnabled && context.testFlag( Qgis::RenderContextFlag::RenderMapTile ) )
1325 {
1326 // If the "avoid artifacts between adjacent tiles" flag is set (RenderMapTile), then we'll force disable
1327 // the geometry clipping IF (and only if) this symbol can potentially have rendering artifacts when rendered as map tiles.
1328 // If the symbol won't have any artifacts anyway, then it's pointless and incredibly expensive to skip the clipping!
1330 {
1331 clippingEnabled = false;
1332 }
1333 }
1334 if ( context.extent().isEmpty() )
1335 clippingEnabled = false;
1336
1337 mSymbolRenderContext->setGeometryPartCount( geom.constGet()->partCount() );
1338 mSymbolRenderContext->setGeometryPartNum( 1 );
1339
1340 const bool needsExpressionContext = hasDataDefinedProperties();
1341 ExpressionContextScopePopper scopePopper;
1342 if ( mSymbolRenderContext->expressionContextScope() )
1343 {
1344 if ( needsExpressionContext )
1345 {
1346 // this is somewhat nasty - by appending this scope here it's now owned
1347 // by both mSymbolRenderContext AND context.expressionContext()
1348 // the RAII scopePopper is required to make sure it always has ownership transferred back
1349 // from context.expressionContext(), even if exceptions of other early exits occur in this
1350 // function
1351 context.expressionContext().appendScope( mSymbolRenderContext->expressionContextScope() );
1352 scopePopper.context = &context.expressionContext();
1353
1354 QgsExpressionContextUtils::updateSymbolScope( this, mSymbolRenderContext->expressionContextScope() );
1355 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_COUNT, mSymbolRenderContext->geometryPartCount(), true ) );
1356 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, 1, true ) );
1357 }
1358 }
1359
1360 // Collection of markers to paint, only used for no curve types.
1361 QPolygonF markers;
1362
1363 QgsGeometry renderedBoundsGeom;
1364
1365 // Step 1 - collect the set of painter coordinate geometries to render.
1366 // We do this upfront, because we only want to ever do this once, regardless how many symbol layers we need to render.
1367
1368 struct PointInfo
1369 {
1370 QPointF renderPoint;
1371 const QgsPoint *originalGeometry = nullptr;
1372 };
1373 QVector< PointInfo > pointsToRender;
1374
1375 struct LineInfo
1376 {
1377 QPolygonF renderLine;
1378 const QgsCurve *originalGeometry = nullptr;
1379 };
1380 QVector< LineInfo > linesToRender;
1381
1382 struct PolygonInfo
1383 {
1384 QPolygonF renderExterior;
1385 QVector< QPolygonF > renderRings;
1386 const QgsCurvePolygon *originalGeometry = nullptr;
1387 int originalPartIndex = 0;
1388 };
1389 QVector< PolygonInfo > polygonsToRender;
1390
1391 std::function< void ( const QgsAbstractGeometry *, int partIndex )> getPartGeometry;
1392 getPartGeometry = [&pointsToRender, &linesToRender, &polygonsToRender, &getPartGeometry, &context, &clippingEnabled, &markers, &feature, &usingSegmentizedGeometry, this]( const QgsAbstractGeometry * part, int partIndex = 0 )
1393 {
1394 Q_UNUSED( feature )
1395
1396 if ( !part )
1397 return;
1398
1399 // geometry preprocessing
1400 QgsGeometry temporaryGeometryContainer;
1401 const QgsAbstractGeometry *processedGeometry = nullptr;
1402
1403 const bool isMultiPart = qgsgeometry_cast< const QgsGeometryCollection * >( part ) && qgsgeometry_cast< const QgsGeometryCollection * >( part )->numGeometries() > 1;
1404
1405 if ( !isMultiPart )
1406 {
1407 // segmentize curved geometries
1408 const bool needsSegmentizing = QgsWkbTypes::isCurvedType( part->wkbType() ) || part->hasCurvedSegments();
1409 if ( needsSegmentizing )
1410 {
1411 std::unique_ptr< QgsAbstractGeometry > segmentizedPart( part->segmentize( context.segmentationTolerance(), context.segmentationToleranceType() ) );
1412 if ( !segmentizedPart )
1413 {
1414 return;
1415 }
1416 temporaryGeometryContainer.set( segmentizedPart.release() );
1417 processedGeometry = temporaryGeometryContainer.constGet();
1418 usingSegmentizedGeometry = true;
1419 }
1420 else
1421 {
1422 // no segmentation required
1423 processedGeometry = part;
1424 }
1425
1426 // Simplify the geometry, if needed.
1428 {
1429 const int simplifyHints = context.vectorSimplifyMethod().simplifyHints();
1430 const QgsMapToPixelSimplifier simplifier( simplifyHints, context.vectorSimplifyMethod().tolerance(),
1432
1433 std::unique_ptr< QgsAbstractGeometry > simplified( simplifier.simplify( processedGeometry ) );
1434 if ( simplified )
1435 {
1436 temporaryGeometryContainer.set( simplified.release() );
1437 processedGeometry = temporaryGeometryContainer.constGet();
1438 }
1439 }
1440
1441 // clip geometry to render context clipping regions
1442 if ( !context.featureClipGeometry().isEmpty() )
1443 {
1444 // apply feature clipping from context to the rendered geometry only -- just like the render time simplification,
1445 // we should NEVER apply this to the geometry attached to the feature itself. Doing so causes issues with certain
1446 // renderer settings, e.g. if polygons are being rendered using a rule based renderer based on the feature's area,
1447 // then we need to ensure that the original feature area is used instead of the clipped area..
1448 QgsGeos geos( processedGeometry );
1449 std::unique_ptr< QgsAbstractGeometry > clippedGeom( geos.intersection( context.featureClipGeometry().constGet() ) );
1450 if ( clippedGeom )
1451 {
1452 temporaryGeometryContainer.set( clippedGeom.release() );
1453 processedGeometry = temporaryGeometryContainer.constGet();
1454 }
1455 }
1456 }
1457 else
1458 {
1459 // for multipart geometries, the processing is deferred till we're rendering the actual part...
1460 processedGeometry = part;
1461 }
1462
1463 if ( !processedGeometry )
1464 {
1465 // shouldn't happen!
1466 QgsDebugMsg( QStringLiteral( "No processed geometry to render for part!" ) );
1467 return;
1468 }
1469
1470 switch ( QgsWkbTypes::flatType( processedGeometry->wkbType() ) )
1471 {
1473 {
1475 {
1476 QgsDebugMsgLevel( QStringLiteral( "point can be drawn only with marker symbol!" ), 2 );
1477 break;
1478 }
1479
1480 PointInfo info;
1481 info.originalGeometry = qgsgeometry_cast< const QgsPoint * >( part );
1482 info.renderPoint = _getPoint( context, *info.originalGeometry );
1483 pointsToRender << info;
1484 break;
1485 }
1486
1488 {
1490 {
1491 QgsDebugMsgLevel( QStringLiteral( "linestring can be drawn only with line symbol!" ), 2 );
1492 break;
1493 }
1494
1495 LineInfo info;
1496 info.originalGeometry = qgsgeometry_cast<const QgsCurve *>( part );
1497 info.renderLine = _getLineString( context, *qgsgeometry_cast<const QgsCurve *>( processedGeometry ), clippingEnabled );
1498 linesToRender << info;
1499 break;
1500 }
1501
1504 {
1505 QPolygonF pts;
1507 {
1508 QgsDebugMsgLevel( QStringLiteral( "polygon can be drawn only with fill symbol!" ), 2 );
1509 break;
1510 }
1511
1512 PolygonInfo info;
1513 info.originalGeometry = qgsgeometry_cast<const QgsCurvePolygon *>( part );
1514 info.originalPartIndex = partIndex;
1515 if ( !qgsgeometry_cast<const QgsPolygon *>( processedGeometry )->exteriorRing() )
1516 {
1517 QgsDebugMsg( QStringLiteral( "cannot render polygon with no exterior ring" ) );
1518 break;
1519 }
1520
1521 _getPolygon( info.renderExterior, info.renderRings, context, *qgsgeometry_cast<const QgsPolygon *>( processedGeometry ), clippingEnabled, mForceRHR );
1522 polygonsToRender << info;
1523 break;
1524 }
1525
1527 {
1528 const QgsMultiPoint *mp = qgsgeometry_cast< const QgsMultiPoint * >( processedGeometry );
1529 markers.reserve( mp->numGeometries() );
1530 }
1535 {
1536 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( processedGeometry );
1537
1538 const unsigned int num = geomCollection->numGeometries();
1539 for ( unsigned int i = 0; i < num; ++i )
1540 {
1541 if ( context.renderingStopped() )
1542 break;
1543
1544 getPartGeometry( geomCollection->geometryN( i ), i );
1545 }
1546 break;
1547 }
1548
1551 {
1553 {
1554 QgsDebugMsgLevel( QStringLiteral( "multi-polygon can be drawn only with fill symbol!" ), 2 );
1555 break;
1556 }
1557
1558 QPolygonF pts;
1559
1560 const QgsGeometryCollection *geomCollection = dynamic_cast<const QgsGeometryCollection *>( processedGeometry );
1561 const unsigned int num = geomCollection->numGeometries();
1562
1563 // Sort components by approximate area (probably a bit faster than using
1564 // area() )
1565 std::map<double, QList<unsigned int> > thisAreaToPartNum;
1566 for ( unsigned int i = 0; i < num; ++i )
1567 {
1568 const QgsRectangle r( geomCollection->geometryN( i )->boundingBox() );
1569 thisAreaToPartNum[ r.width() * r.height()] << i;
1570 }
1571
1572 // Draw starting with larger parts down to smaller parts, so that in
1573 // case of a part being incorrectly inside another part, it is drawn
1574 // on top of it (#15419)
1575 std::map<double, QList<unsigned int> >::const_reverse_iterator iter = thisAreaToPartNum.rbegin();
1576 for ( ; iter != thisAreaToPartNum.rend(); ++iter )
1577 {
1578 const QList<unsigned int> &listPartIndex = iter->second;
1579 for ( int idx = 0; idx < listPartIndex.size(); ++idx )
1580 {
1581 const unsigned i = listPartIndex[idx];
1582 getPartGeometry( geomCollection->geometryN( i ), i );
1583 }
1584 }
1585 break;
1586 }
1587
1588 default:
1589 QgsDebugMsg( QStringLiteral( "feature %1: unsupported wkb type %2/%3 for rendering" )
1590 .arg( feature.id() )
1591 .arg( QgsWkbTypes::displayString( part->wkbType() ) )
1592 .arg( static_cast< quint32>( part->wkbType() ), 0, 16 ) );
1593 }
1594 };
1595
1596 // Use the simplified type ref when rendering -- this avoids some unnecessary cloning/geometry modification
1597 // (e.g. if the original geometry is a compound curve containing only a linestring curve, we don't have
1598 // to segmentize the geometry before rendering)
1599 getPartGeometry( geom.constGet()->simplifiedTypeRef(), 0 );
1600
1601 // step 2 - determine which layers to render
1602 std::vector< int > layers;
1603 if ( layer == -1 )
1604 {
1605 layers.reserve( mLayers.count() );
1606 for ( int i = 0; i < mLayers.count(); ++i )
1607 layers.emplace_back( i );
1608 }
1609 else
1610 {
1611 layers.emplace_back( layer );
1612 }
1613
1614 // step 3 - render these geometries using the desired symbol layers.
1615
1616 if ( needsExpressionContext )
1617 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_count" ), mLayers.count(), true ) );
1618
1619 for ( const int symbolLayerIndex : layers )
1620 {
1621 QgsSymbolLayer *symbolLayer = mLayers.value( symbolLayerIndex );
1622 if ( !symbolLayer || !symbolLayer->enabled() )
1623 continue;
1624
1625 if ( needsExpressionContext )
1626 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_index" ), symbolLayerIndex + 1, true ) );
1627
1628 symbolLayer->startFeatureRender( feature, context );
1629
1630 switch ( mType )
1631 {
1633 {
1634 int geometryPartNumber = 0;
1635 for ( const PointInfo &point : std::as_const( pointsToRender ) )
1636 {
1637 if ( context.renderingStopped() )
1638 break;
1639
1640 mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1641 if ( needsExpressionContext )
1642 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1643
1644 static_cast<QgsMarkerSymbol *>( this )->renderPoint( point.renderPoint, &feature, context, symbolLayerIndex, selected );
1645 geometryPartNumber++;
1646 }
1647
1648 break;
1649 }
1650
1652 {
1653 if ( linesToRender.empty() )
1654 break;
1655
1656 int geometryPartNumber = 0;
1657 for ( const LineInfo &line : std::as_const( linesToRender ) )
1658 {
1659 if ( context.renderingStopped() )
1660 break;
1661
1662 mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1663 if ( needsExpressionContext )
1664 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1665
1666 context.setGeometry( line.originalGeometry );
1667 static_cast<QgsLineSymbol *>( this )->renderPolyline( line.renderLine, &feature, context, symbolLayerIndex, selected );
1668 geometryPartNumber++;
1669 }
1670 break;
1671 }
1672
1674 {
1675 for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
1676 {
1677 if ( context.renderingStopped() )
1678 break;
1679
1680 mSymbolRenderContext->setGeometryPartNum( info.originalPartIndex + 1 );
1681 if ( needsExpressionContext )
1682 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, info.originalPartIndex + 1, true ) );
1683
1684 context.setGeometry( info.originalGeometry );
1685 static_cast<QgsFillSymbol *>( this )->renderPolygon( info.renderExterior, ( !info.renderRings.isEmpty() ? &info.renderRings : nullptr ), &feature, context, symbolLayerIndex, selected );
1686 }
1687
1688 break;
1689 }
1690
1692 break;
1693 }
1694
1695 symbolLayer->stopFeatureRender( feature, context );
1696 }
1697
1698 // step 4 - handle post processing steps
1699 switch ( mType )
1700 {
1702 {
1703 markers.reserve( pointsToRender.size() );
1704 for ( const PointInfo &info : std::as_const( pointsToRender ) )
1705 {
1707 {
1708 const QRectF bounds = static_cast<QgsMarkerSymbol *>( this )->bounds( info.renderPoint, context, feature );
1709 if ( context.hasRenderedFeatureHandlers() )
1710 {
1711 renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromRect( bounds )
1712 : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromRect( QgsRectangle( bounds ) ) << renderedBoundsGeom );
1713 }
1715 {
1716 //draw debugging rect
1717 context.painter()->setPen( Qt::red );
1718 context.painter()->setBrush( QColor( 255, 0, 0, 100 ) );
1719 context.painter()->drawRect( bounds );
1720 }
1721 }
1722
1723 if ( drawVertexMarker && !usingSegmentizedGeometry )
1724 {
1725 markers.append( info.renderPoint );
1726 }
1727 }
1728 break;
1729 }
1730
1732 {
1733 for ( const LineInfo &info : std::as_const( linesToRender ) )
1734 {
1735 if ( context.hasRenderedFeatureHandlers() && !info.renderLine.empty() )
1736 {
1737 renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderLine )
1738 : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderLine ) << renderedBoundsGeom );
1739 }
1740
1741 if ( drawVertexMarker && !usingSegmentizedGeometry )
1742 {
1743 markers << info.renderLine;
1744 }
1745 }
1746 break;
1747 }
1748
1750 {
1751 for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
1752 {
1753 if ( context.hasRenderedFeatureHandlers() && !info.renderExterior.empty() )
1754 {
1755 renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderExterior )
1756 : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderExterior ) << renderedBoundsGeom );
1757 // TODO: consider holes?
1758 }
1759
1760 if ( drawVertexMarker && !usingSegmentizedGeometry )
1761 {
1762 markers << info.renderExterior;
1763
1764 for ( const QPolygonF &hole : info.renderRings )
1765 {
1766 markers << hole;
1767 }
1768 }
1769 }
1770 break;
1771 }
1772
1774 break;
1775 }
1776
1777 if ( context.hasRenderedFeatureHandlers() && !renderedBoundsGeom.isNull() )
1778 {
1780 const QList< QgsRenderedFeatureHandlerInterface * > handlers = context.renderedFeatureHandlers();
1781 for ( QgsRenderedFeatureHandlerInterface *handler : handlers )
1782 handler->handleRenderedFeature( feature, renderedBoundsGeom, featureContext );
1783 }
1784
1785 if ( drawVertexMarker )
1786 {
1787 if ( !markers.isEmpty() && !context.renderingStopped() )
1788 {
1789 const auto constMarkers = markers;
1790 for ( QPointF marker : constMarkers )
1791 {
1792 renderVertexMarker( marker, context, currentVertexMarkerType, currentVertexMarkerSize );
1793 }
1794 }
1795 else
1796 {
1798 const QgsMapToPixel &mtp = context.mapToPixel();
1799
1800 QgsPoint vertexPoint;
1801 QgsVertexId vertexId;
1802 double x, y, z;
1803 QPointF mapPoint;
1804 while ( geom.constGet()->nextVertex( vertexId, vertexPoint ) )
1805 {
1806 //transform
1807 x = vertexPoint.x();
1808 y = vertexPoint.y();
1809 z = 0.0;
1810 if ( ct.isValid() )
1811 {
1812 ct.transformInPlace( x, y, z );
1813 }
1814 mapPoint.setX( x );
1815 mapPoint.setY( y );
1816 mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1817 renderVertexMarker( mapPoint, context, currentVertexMarkerType, currentVertexMarkerSize );
1818 }
1819 }
1820 }
1821}
1822
1824{
1825 return mSymbolRenderContext.get();
1826}
1827
1828void QgsSymbol::renderVertexMarker( QPointF pt, QgsRenderContext &context, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize )
1829{
1830 int markerSize = context.convertToPainterUnits( currentVertexMarkerSize, Qgis::RenderUnit::Millimeters );
1831 QgsSymbolLayerUtils::drawVertexMarker( pt.x(), pt.y(), *context.painter(), currentVertexMarkerType, markerSize );
1832}
1833
1834void QgsSymbol::initPropertyDefinitions()
1835{
1836 if ( !sPropertyDefinitions.isEmpty() )
1837 return;
1838
1839 QString origin = QStringLiteral( "symbol" );
1840
1841 sPropertyDefinitions = QgsPropertiesDefinition
1842 {
1843 { QgsSymbol::PropertyOpacity, QgsPropertyDefinition( "alpha", QObject::tr( "Opacity" ), QgsPropertyDefinition::Opacity, origin )},
1844 };
1845}
1846
1847void QgsSymbol::startFeatureRender( const QgsFeature &feature, QgsRenderContext &context, const int layer )
1848{
1849 if ( layer != -1 )
1850 {
1852 if ( symbolLayer && symbolLayer->enabled() )
1853 {
1854 symbolLayer->startFeatureRender( feature, context );
1855 }
1856 return;
1857 }
1858 else
1859 {
1860 const QList< QgsSymbolLayer * > layers = mLayers;
1861 for ( QgsSymbolLayer *symbolLayer : layers )
1862 {
1863 if ( !symbolLayer->enabled() )
1864 continue;
1865
1866 symbolLayer->startFeatureRender( feature, context );
1867 }
1868 }
1869}
1870
1871void QgsSymbol::stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context, int layer )
1872{
1873 if ( layer != -1 )
1874 {
1876 if ( symbolLayer && symbolLayer->enabled() )
1877 {
1878 symbolLayer->stopFeatureRender( feature, context );
1879 }
1880 return;
1881 }
1882 else
1883 {
1884 const QList< QgsSymbolLayer * > layers = mLayers;
1885 for ( QgsSymbolLayer *symbolLayer : layers )
1886 {
1887 if ( !symbolLayer->enabled() )
1888 continue;
1889
1890 symbolLayer->stopFeatureRender( feature, context );
1891 }
1892 }
1893}
@ CounterClockwise
Counter-clockwise direction.
@ Clockwise
Clockwise direction.
@ DisableFeatureClipping
If present, indicates that features should never be clipped to the map extent during rendering.
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition: qgis.h:228
RenderUnit
Rendering size units.
Definition: qgis.h:3176
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ ApplyClipAfterReprojection
Feature geometry clipping to mapExtent() must be performed after the geometries are transformed using...
@ DrawSymbolBounds
Draw bounds of symbols (for debugging/testing)
@ RenderMapTile
Draw map such that there are no problems between adjacent tiles.
@ Antialiasing
Use antialiasing while drawing.
@ HighQualityImageTransforms
Enable high quality image transformations, which results in better appearance of scaled or rotated ra...
@ FlagIncludeCrosshairsForMarkerSymbols
Include a crosshairs reference image in the background of marker symbol previews.
VertexMarkerType
Editing vertex markers, used for showing vertices during a edit operation.
Definition: qgis.h:1071
SymbolType
Symbol types.
Definition: qgis.h:320
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
@ Hybrid
Hybrid symbol.
@ LineString
LineString.
@ MultiPoint
MultiPoint.
@ Polygon
Polygon.
@ MultiPolygon
MultiPolygon.
@ Triangle
Triangle.
@ MultiLineString
MultiLineString.
@ GeometryCollection
GeometryCollection.
@ MultiCurve
MultiCurve.
@ MultiSurface
MultiSurface.
Abstract base class for all geometries.
bool is3D() const SIP_HOLDGIL
Returns true if the geometry is 3D and contains a z-value.
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const SIP_HOLDGIL
Returns a reference to the simplest lossless representation of this geometry, e.g.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
Qgis::WkbType wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
virtual bool nextVertex(QgsVertexId &id, QgsPoint &vertex) const =0
Returns next vertex id and coordinates.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
static QgsColorSchemeRegistry * colorSchemeRegistry()
Returns the application's color scheme registry, used for managing color schemes.
A 3-dimensional box composed of x, y, z coordinates.
Definition: qgsbox3d.h:39
static void trimPolygon(QPolygonF &pts, const QgsRectangle &clipRect)
Trims the given polygon to a rectangular box, by modifying the given polygon in place.
Definition: qgsclipper.h:285
static QPolygonF clippedLine(const QgsCurve &curve, const QgsRectangle &clipExtent)
Takes a linestring and clips it to clipExtent.
Definition: qgsclipper.cpp:110
static void clipped3dLine(const QVector< double > &xIn, const QVector< double > &yIn, const QVector< double > &zIn, QVector< double > &x, QVector< double > &y, QVector< double > &z, const QgsBox3d &clipExtent)
Takes a line with 3D coordinates and clips it to clipExtent.
Definition: qgsclipper.cpp:40
Class for doing transforms between two map coordinate systems.
void transformPolygon(QPolygonF &polygon, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transforms a polygon to the destination coordinate system.
void transformCoords(int numPoint, double *x, double *y, double *z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transform an array of coordinates to the destination CRS.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
void transformInPlace(double &x, double &y, double &z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Curve polygon geometry type.
const QgsCurve * interiorRing(int i) const SIP_HOLDGIL
Retrieves an interior ring from the curve polygon.
const QgsCurve * exteriorRing() const SIP_HOLDGIL
Returns the curve polygon's exterior ring.
int numInteriorRings() const SIP_HOLDGIL
Returns the number of interior rings contained with the curve polygon.
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
Qgis::AngularDirection orientation() const
Returns the curve's orientation, e.g.
Definition: qgscurve.cpp:286
QgsRectangle boundingBox() const override
Returns the minimal bounding box for the geometry.
Definition: qgscurve.cpp:238
virtual int numPoints() const =0
Returns the number of points in the curve.
QgsCurve * segmentize(double tolerance=M_PI_2/90, SegmentationToleranceType toleranceType=MaximumAngle) const override
Returns a geometry without curves.
Definition: qgscurve.cpp:175
virtual QPolygonF asQPolygonF() const
Returns a QPolygonF representing the points.
Definition: qgscurve.cpp:266
A class to manager painter saving and restoring required for effect drawing.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * updateSymbolScope(const QgsSymbol *symbol, QgsExpressionContextScope *symbolScope=nullptr)
Updates a symbol scope related to a QgsSymbol to an expression context.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static const QString EXPR_GEOMETRY_PART_COUNT
Inbuilt variable name for geometry part count variable.
static const QString EXPR_GEOMETRY_PART_NUM
Inbuilt variable name for geometry part number variable.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Container of fields for a vector layer.
Definition: qgsfields.h:45
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
Geometry collection.
int numGeometries() const SIP_HOLDGIL
Returns the number of geometries within the collection.
const QgsAbstractGeometry * geometryN(int n) const
Returns a const reference to a geometry from within the collection.
void render(QgsSymbolRenderContext &context, Qgis::GeometryType geometryType=Qgis::GeometryType::Unknown, const QPolygonF *points=nullptr, const QVector< QPolygonF > *rings=nullptr)
Will render this symbol layer using the context.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometry collectGeometry(const QVector< QgsGeometry > &geometries)
Creates a new multipart geometry from a list of QgsGeometry objects.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
Qgis::GeometryType type
Definition: qgsgeometry.h:167
void set(QgsAbstractGeometry *geometry)
Sets the underlying geometry store.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Does vector analysis using the geos library and handles import, export, exception handling*.
Definition: qgsgeos.h:99
Represents a patch shape for use in map legends.
QList< QList< QPolygonF > > toQPolygonF(Qgis::SymbolType type, QSizeF size) const
Converts the patch shape to a set of QPolygonF objects representing how the patch should be drawn for...
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
QVector< double > xVector() const
Returns the x vertex values as a vector.
QVector< double > yVector() const
Returns the y vertex values as a vector.
QVector< double > zVector() const
Returns the z vertex values as a vector.
virtual void renderPolygonStroke(const QPolygonF &points, const QVector< QPolygonF > *rings, QgsSymbolRenderContext &context)
Renders the line symbol layer along the outline of polygon, using the given render context.
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
Qgis::LayerType type
Definition: qgsmaplayer.h:80
Implementation of GeometrySimplifier using the "MapToPixel" algorithm.
SimplifyAlgorithm
Types of simplification algorithms that can be used.
QgsGeometry simplify(const QgsGeometry &geometry) const override
Returns a simplified version the specified geometry.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
void transformInPlace(double &x, double &y) const
Transforms device coordinates to map coordinates.
Struct for storing maximum and minimum scales for measurements in map units.
A marker symbol type, for rendering Point and MultiPoint geometries.
Multi point geometry collection.
Definition: qgsmultipoint.h:30
Base class for visual effects which can be applied to QPicture drawings.
bool enabled() const
Returns whether the effect is enabled.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
Q_GADGET double x
Definition: qgspoint.h:52
double y
Definition: qgspoint.h:53
Polygon geometry type.
Definition: qgspolygon.h:34
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
void setProperty(int key, const QgsProperty &property)
Adds a property to the collection and takes ownership of it.
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext(), bool ignoreContext=false) const override
Returns the set of any fields referenced by the active properties from the collection.
bool hasActiveProperties() const override
Returns true if the collection has any active properties, or false if all properties within the colle...
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const override
Prepares the collection against a specified expression context.
Definition for a property.
Definition: qgsproperty.h:46
@ Opacity
Opacity (0-100)
Definition: qgsproperty.h:61
A store for object properties.
Definition: qgsproperty.h:230
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:469
bool contains(const QgsRectangle &rect) const SIP_HOLDGIL
Returns true when rectangle contains other rectangle.
Definition: qgsrectangle.h:363
Contains information about the context of a rendering operation.
void setForceVectorOutput(bool force)
Sets whether rendering operations should use vector operations instead of any faster raster shortcuts...
void setTextureOrigin(const QPointF &origin)
Sets the texture origin, which should be used as a brush transform when rendering using QBrush object...
bool hasRenderedFeatureHandlers() const
Returns true if the context has any rendered feature handlers.
double segmentationTolerance() const
Gets the segmentation tolerance applied when rendering curved geometries.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsExpressionContext & expressionContext()
Gets the expression context.
void setGeometry(const QgsAbstractGeometry *geometry)
Sets pointer to original (unsegmentized) geometry.
QgsGeometry featureClipGeometry() const
Returns the geometry to use to clip features at render time.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
bool testFlag(Qgis::RenderContextFlag flag) const
Check whether a particular flag is enabled.
bool forceVectorOutput() const
Returns true if rendering operations should use vector operations instead of any faster raster shortc...
long long currentFrame() const
Returns the current frame number of the map (in frames per second), for maps which are part of an ani...
void setIsGuiPreview(bool preview)
Sets GUI preview mode.
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
QList< QgsRenderedFeatureHandlerInterface * > renderedFeatureHandlers() const
Returns the list of rendered feature handlers to use while rendering map layers.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double frameRate() const
Returns the frame rate of the map, for maps which are part of an animation.
const QgsVectorSimplifyMethod & vectorSimplifyMethod() const
Returns the simplification settings to use when rendering vector layers.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
bool isSymbolLayerEnabled(const QgsSymbolLayer *layer) const
When rendering a map layer in a second pass (for selective masking), some symbol layers may be disabl...
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
QgsAbstractGeometry::SegmentationToleranceType segmentationToleranceType() const
Gets segmentation tolerance type (maximum angle or maximum difference between curve and approximation...
An interface for classes which provider custom handlers for features rendered as part of a map render...
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:145
QList< QList< QPolygonF > > defaultPatchAsQPolygonF(Qgis::SymbolType type, QSizeF size) const
Returns the default patch geometry for the given symbol type and size as a set of QPolygonF objects (...
Definition: qgsstyle.cpp:1205
Contains settings relating to symbol animation.
Definition: qgssymbol.h:40
bool isAnimated() const
Returns true if the symbol is animated.
Definition: qgssymbol.h:63
double frameRate() const
Returns the symbol animation frame rate (in frames per second).
Definition: qgssymbol.h:77
static void drawVertexMarker(double x, double y, QPainter &p, Qgis::VertexMarkerType type, int markerSize)
Draws a vertex symbol at (painter) coordinates x, y.
static QString encodeSldUom(Qgis::RenderUnit unit, double *scaleFactor)
Encodes a render unit into an SLD unit of measure string.
static QString encodeColor(const QColor &color)
@ PropertyLayerEnabled
Whether symbol layer is enabled.
virtual void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context)
Called before the layer will be rendered for a particular feature.
QgsPaintEffect * paintEffect() const
Returns the current paint effect for the layer.
virtual void startRender(QgsSymbolRenderContext &context)=0
Called before a set of rendering operations commences on the supplied render context.
bool enabled() const
Returns true if symbol layer is enabled and will be drawn.
virtual void stopRender(QgsSymbolRenderContext &context)=0
Called after a set of rendering operations has finished on the supplied render context.
virtual void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context)
Called after the layer has been rendered for a particular feature.
void setSelected(bool selected)
Sets whether symbols should be rendered using the selected symbol coloring and style.
void setOriginalGeometryType(Qgis::GeometryType type)
Sets the geometry type for the original feature geometry being rendered.
void setPatchShape(const QgsLegendPatchShape &shape)
Sets the symbol patch shape, to use if rendering symbol preview icons.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
void renderUsingLayer(QgsSymbolLayer *layer, QgsSymbolRenderContext &context, Qgis::GeometryType geometryType=Qgis::GeometryType::Unknown, const QPolygonF *points=nullptr, const QVector< QPolygonF > *rings=nullptr)
Renders a context using a particular symbol layer without passing in a geometry.
Definition: qgssymbol.cpp:1162
QgsSymbolLayerList cloneLayers() const
Retrieve a cloned list of all layers that make up this symbol.
Definition: qgssymbol.cpp:1147
void setOutputUnit(Qgis::RenderUnit unit) const
Sets the units to use for sizes and widths within the symbol.
Definition: qgssymbol.cpp:671
QgsSymbolRenderContext * symbolRenderContext()
Returns the symbol render context.
Definition: qgssymbol.cpp:1823
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:756
Property
Data definable properties.
Definition: qgssymbol.h:130
@ PropertyOpacity
Opacity.
Definition: qgssymbol.h:131
void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the symbol.
Definition: qgssymbol.cpp:1200
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol's property collection, used for data defined overrides.
Definition: qgssymbol.h:622
static QPolygonF _getLineString(QgsRenderContext &context, const QgsCurve &curve, bool clipToExtent=true)
Creates a line string in screen coordinates from a QgsCurve in map coordinates.
Definition: qgssymbol.cpp:80
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:873
qreal mOpacity
Symbol opacity (in the range 0 - 1)
Definition: qgssymbol.h:789
Q_DECL_DEPRECATED const QgsVectorLayer * mLayer
Definition: qgssymbol.h:805
static QPolygonF _getPolygonRing(QgsRenderContext &context, const QgsCurve &curve, bool clipToExtent, bool isExteriorRing=false, bool correctRingOrientation=false)
Creates a polygon ring in screen coordinates from a QgsCurve in map coordinates.
Definition: qgssymbol.cpp:301
QgsSymbolAnimationSettings & animationSettings()
Returns a reference to the symbol animation settings.
Definition: qgssymbol.cpp:689
void drawPreviewIcon(QPainter *painter, QSize size, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *patchShape=nullptr)
Draws an icon of the symbol that occupies an area given by size using the specified painter.
Definition: qgssymbol.cpp:923
void renderVertexMarker(QPointF pt, QgsRenderContext &context, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize)
Render editing vertex marker at specified point.
Definition: qgssymbol.cpp:1828
static QPointF _getPoint(QgsRenderContext &context, const QgsPoint &point)
Creates a point in screen coordinates from a QgsPoint in map coordinates.
Definition: qgssymbol.h:718
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the symbol property definitions.
Definition: qgssymbol.cpp:597
bool appendSymbolLayer(QgsSymbolLayer *layer)
Appends a symbol layer at the end of the current symbol layer list.
Definition: qgssymbol.cpp:779
bool usesMapUnits() const
Returns true if the symbol has any components which use map unit based sizes.
Definition: qgssymbol.cpp:630
Qgis::SymbolFlags flags() const
Returns flags for the symbol.
Definition: qgssymbol.h:530
void toSld(QDomDocument &doc, QDomElement &element, QVariantMap props) const
Converts the symbol to a SLD representation.
Definition: qgssymbol.cpp:1134
void setColor(const QColor &color) const
Sets the color for the symbol.
Definition: qgssymbol.cpp:898
bool insertSymbolLayer(int index, QgsSymbolLayer *layer)
Inserts a symbol layer to specified index.
Definition: qgssymbol.cpp:766
QgsMapUnitScale mapUnitScale() const
Returns the map unit scale for the symbol.
Definition: qgssymbol.cpp:647
static QString symbolTypeToString(Qgis::SymbolType type)
Returns a translated string version of the specified symbol type.
Definition: qgssymbol.cpp:564
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:495
bool canCauseArtifactsBetweenAdjacentTiles() const
Returns true if the symbol rendering can cause visible artifacts across a single feature when the fea...
Definition: qgssymbol.cpp:1218
static Qgis::SymbolType symbolTypeForGeometryType(Qgis::GeometryType type)
Returns the default symbol type required for the specified geometry type.
Definition: qgssymbol.cpp:580
void setMapUnitScale(const QgsMapUnitScale &scale) const
Sets the map unit scale for the symbol.
Definition: qgssymbol.cpp:680
bool clipFeaturesToExtent() const
Returns whether features drawn by the symbol will be clipped to the render context's extent.
Definition: qgssymbol.h:552
QImage asImage(QSize size, QgsRenderContext *customContext=nullptr)
Returns an image of the symbol at the specified size.
Definition: qgssymbol.cpp:1040
static void _getPolygon(QPolygonF &pts, QVector< QPolygonF > &holes, QgsRenderContext &context, const QgsPolygon &polygon, bool clipToExtent=true, bool correctRingOrientation=false)
Creates a polygon in screen coordinates from a QgsPolygonXYin map coordinates.
Definition: qgssymbol.cpp:549
QString dump() const
Returns a string dump of the symbol's properties.
Definition: qgssymbol.cpp:1108
bool hasDataDefinedProperties() const
Returns whether the symbol utilizes any data defined properties.
Definition: qgssymbol.cpp:1205
bool deleteSymbolLayer(int index)
Removes and deletes the symbol layer at the specified index.
Definition: qgssymbol.cpp:789
virtual ~QgsSymbol()
Definition: qgssymbol.cpp:603
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Definition: qgssymbol.cpp:1183
QImage bigSymbolPreviewImage(QgsExpressionContext *expressionContext=nullptr, Qgis::SymbolPreviewFlags flags=Qgis::SymbolPreviewFlag::FlagIncludeCrosshairsForMarkerSymbols)
Returns a large (roughly 100x100 pixel) preview image for the symbol.
Definition: qgssymbol.cpp:1055
Qgis::SymbolType mType
Definition: qgssymbol.h:785
bool changeSymbolLayer(int index, QgsSymbolLayer *layer)
Deletes the current layer at the specified index and replaces it with layer.
Definition: qgssymbol.cpp:809
QgsSymbolLayer * takeSymbolLayer(int index)
Removes a symbol layer from the list and returns a pointer to it.
Definition: qgssymbol.cpp:800
Qgis::SymbolRenderHints mRenderHints
Definition: qgssymbol.h:791
bool mForceRHR
Definition: qgssymbol.h:801
QgsSymbolLayerList mLayers
Definition: qgssymbol.h:786
Q_DECL_DEPRECATED const QgsVectorLayer * layer() const
Definition: qgssymbol.cpp:1235
QgsSymbolAnimationSettings mAnimationSettings
Definition: qgssymbol.h:803
void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context, int layer=-1)
Called before symbol layers will be rendered for a particular feature.
Definition: qgssymbol.cpp:1847
void renderFeature(const QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false, Qgis::VertexMarkerType currentVertexMarkerType=Qgis::VertexMarkerType::SemiTransparentCircle, double currentVertexMarkerSize=0.0) SIP_THROW(QgsCsException)
Render a feature.
Definition: qgssymbol.cpp:1284
QColor color() const
Returns the symbol's color.
Definition: qgssymbol.cpp:908
Qgis::RenderUnit outputUnit() const
Returns the units to use for sizes and widths within the symbol.
Definition: qgssymbol.cpp:609
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:152
QgsSymbol(Qgis::SymbolType type, const QgsSymbolLayerList &layers)
Constructor for a QgsSymbol of the specified type.
Definition: qgssymbol.cpp:59
void setAnimationSettings(const QgsSymbolAnimationSettings &settings)
Sets a the symbol animation settings.
Definition: qgssymbol.cpp:699
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:825
Q_DECL_DEPRECATED void setLayer(const QgsVectorLayer *layer)
Definition: qgssymbol.cpp:1228
void exportImage(const QString &path, const QString &format, QSize size)
Export the symbol as an image format, to the specified path and with the given size.
Definition: qgssymbol.cpp:1020
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context, int layer=-1)
Called after symbol layers have been rendered for a particular feature.
Definition: qgssymbol.cpp:1871
static QgsSymbol * defaultSymbol(Qgis::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
Definition: qgssymbol.cpp:704
Represents a vector layer which manages a vector based data sets.
QgsVectorLayer * clone() const override
Returns a new instance equivalent to this one.
double tolerance() const
Gets the tolerance of simplification in map units. Represents the maximum distance in map units betwe...
bool forceLocalOptimization() const
Gets where the simplification executes, after fetch the geometries from provider, or when supported,...
SimplifyHints simplifyHints() const
Gets the simplification hints of the vector layer managed.
SimplifyAlgorithm simplifyAlgorithm() const
Gets the local simplification algorithm of the vector layer managed.
static QString displayString(Qgis::WkbType type) SIP_HOLDGIL
Returns a non-translated display string type for a WKB type, e.g., the geometry name used in WKT geom...
static bool isCurvedType(Qgis::WkbType type) SIP_HOLDGIL
Returns true if the WKB type is a curved type or can contain curved geometries.
Definition: qgswkbtypes.h:808
static Qgis::WkbType flatType(Qgis::WkbType type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:629
Contains geos related utilities and functions.
Definition: qgsgeos.h:37
#define FALLTHROUGH
Definition: qgis.h:4120
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:4093
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:3448
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:4092
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3509
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:29
Single variable definition for use within a QgsExpressionContextScope.
Utility class for identifying a unique vertex within a geometry.
Definition: qgsvertexid.h:31