QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
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 std::unique_ptr< QgsLineString > segmentized;
108 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( &curve ) )
109 {
110 lineString = ls;
111 }
112 else
113 {
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
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 {
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 {
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
693
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 {
712 s.reset( QgsProject::instance()->styleSettings()->defaultSymbol( Qgis::SymbolType::Marker ) );
713 break;
715 s.reset( QgsProject::instance()->styleSettings()->defaultSymbol( Qgis::SymbolType::Line ) );
716 break;
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 {
730 s = std::make_unique< QgsMarkerSymbol >();
731 break;
733 s = std::make_unique< QgsLineSymbol >();
734 break;
736 s = std::make_unique< QgsFillSymbol >();
737 break;
738 default:
739 QgsDebugError( QStringLiteral( "unknown layer's geometry type" ) );
740 break;
741 }
742 }
743
744 if ( !s )
745 return nullptr;
746
747 // set opacity
748 s->setOpacity( QgsProject::instance()->styleSettings()->defaultSymbolOpacity() );
749
750 // set random color, it project prefs allow
751 if ( QgsProject::instance()->styleSettings()->randomizeDefaultSymbolColor() )
752 {
753 s->setColor( QgsApplication::colorSchemeRegistry()->fetchRandomStyleColor() );
754 }
755
756 return s.release();
757}
758
760{
761 return mLayers.value( layer );
762}
763
764const QgsSymbolLayer *QgsSymbol::symbolLayer( int layer ) const
765{
766 return mLayers.value( layer );
767}
768
770{
771 if ( index < 0 || index > mLayers.count() ) // can be added also after the last index
772 return false;
773
774 if ( !layer || !layer->isCompatibleWithSymbol( this ) )
775 return false;
776
777 mLayers.insert( index, layer );
778 return true;
779}
780
781
783{
784 if ( !layer || !layer->isCompatibleWithSymbol( this ) )
785 return false;
786
787 mLayers.append( layer );
788 return true;
789}
790
791
793{
794 if ( index < 0 || index >= mLayers.count() )
795 return false;
796
797 delete mLayers.at( index );
798 mLayers.removeAt( index );
799 return true;
800}
801
802
804{
805 if ( index < 0 || index >= mLayers.count() )
806 return nullptr;
807
808 return mLayers.takeAt( index );
809}
810
811
813{
814 QgsSymbolLayer *oldLayer = mLayers.value( index );
815
816 if ( oldLayer == layer )
817 return false;
818
819 if ( !layer || !layer->isCompatibleWithSymbol( this ) )
820 return false;
821
822 delete oldLayer; // first delete the original layer
823 mLayers[index] = layer; // set new layer
824 return true;
825}
826
827
828void QgsSymbol::startRender( QgsRenderContext &context, const QgsFields &fields )
829{
830 Q_ASSERT_X( !mStarted, "startRender", "Rendering has already been started for this symbol instance!" );
831 mStarted = true;
832
833 mSymbolRenderContext.reset( new QgsSymbolRenderContext( context, Qgis::RenderUnit::Unknown, mOpacity, false, mRenderHints, nullptr, fields ) );
834
835 // Why do we need a copy here ? Is it to make sure the symbol layer rendering does not mess with the symbol render context ?
836 // Or is there another profound reason ?
837 QgsSymbolRenderContext symbolContext( context, Qgis::RenderUnit::Unknown, mOpacity, false, mRenderHints, nullptr, fields );
838
839 std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::updateSymbolScope( this, new QgsExpressionContextScope() ) );
840
842 {
843 const long long mapFrameNumber = context.currentFrame();
844 double animationTimeSeconds = 0;
845 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
846 {
847 // render is part of an animation, so we base the calculated frame on that
848 animationTimeSeconds = mapFrameNumber / context.frameRate();
849 }
850 else
851 {
852 // render is outside of animation, so base the calculated frame on the current epoch
853 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
854 }
855
856 const long long symbolFrame = static_cast< long long >( std::floor( animationTimeSeconds * mAnimationSettings.frameRate() ) );
857 scope->setVariable( QStringLiteral( "symbol_frame" ), symbolFrame, true );
858 }
859
860 mSymbolRenderContext->setExpressionContextScope( scope.release() );
861
862 mDataDefinedProperties.prepare( context.expressionContext() );
863
864 const auto constMLayers = mLayers;
865 for ( QgsSymbolLayer *layer : constMLayers )
866 {
867 if ( !layer->enabled() || !context.isSymbolLayerEnabled( layer ) )
868 continue;
869
870 layer->prepareExpressions( symbolContext );
871 layer->prepareMasks( symbolContext );
872 layer->startRender( symbolContext );
873 }
874}
875
877{
878 Q_ASSERT_X( mStarted, "startRender", "startRender was not called for this symbol instance!" );
879 mStarted = false;
880
881 Q_UNUSED( context )
882 if ( mSymbolRenderContext )
883 {
884 const auto constMLayers = mLayers;
885 for ( QgsSymbolLayer *layer : constMLayers )
886 {
887 if ( !layer->enabled() || !context.isSymbolLayerEnabled( layer ) )
888 continue;
889
890 layer->stopRender( *mSymbolRenderContext );
891 }
892 }
893
894 mSymbolRenderContext.reset( nullptr );
895
897 mLayer = nullptr;
899}
900
901void QgsSymbol::setColor( const QColor &color ) const
902{
903 const auto constMLayers = mLayers;
904 for ( QgsSymbolLayer *layer : constMLayers )
905 {
906 if ( !layer->isLocked() )
907 layer->setColor( color );
908 }
909}
910
911QColor QgsSymbol::color() const
912{
913 for ( const QgsSymbolLayer *layer : mLayers )
914 {
915 // return color of the first unlocked layer
916 if ( !layer->isLocked() )
917 {
918 const QColor layerColor = layer->color();
919 if ( layerColor.isValid() )
920 return layerColor;
921 }
922 }
923 return QColor( 0, 0, 0 );
924}
925
926void QgsSymbol::drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext, bool selected, const QgsExpressionContext *expressionContext, const QgsLegendPatchShape *patchShape, const QgsScreenProperties &screen )
927{
928 QgsRenderContext *context = customContext;
929 std::unique_ptr< QgsRenderContext > tempContext;
930 if ( !context )
931 {
932 tempContext.reset( new QgsRenderContext( QgsRenderContext::fromQPainter( painter ) ) );
933 context = tempContext.get();
935 }
936
937 if ( screen.isValid() )
938 {
939 screen.updateRenderContextForScreen( *context );
940 }
941
942 const bool prevForceVector = context->forceVectorOutput();
943 context->setForceVectorOutput( true );
944
945 const double opacity = expressionContext ? dataDefinedProperties().valueAsDouble( QgsSymbol::PropertyOpacity, *expressionContext, mOpacity ) : mOpacity;
946
947 QgsSymbolRenderContext symbolContext( *context, Qgis::RenderUnit::Unknown, opacity, false, mRenderHints, nullptr );
948 symbolContext.setSelected( selected );
949 switch ( mType )
950 {
953 break;
956 break;
959 break;
962 break;
963 }
964
965 if ( patchShape )
966 symbolContext.setPatchShape( *patchShape );
967
968 if ( !customContext && expressionContext )
969 {
970 context->setExpressionContext( *expressionContext );
971 }
972 else if ( !customContext )
973 {
974 // if no render context was passed, build a minimal expression context
975 QgsExpressionContext expContext;
977 context->setExpressionContext( expContext );
978 }
979
980 for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
981 {
982 if ( !layer->enabled() || ( customContext && !customContext->isSymbolLayerEnabled( layer ) ) )
983 continue;
984
986 {
987 // line symbol layer would normally draw just a line
988 // so we override this case to force it to draw a polygon stroke
989 QgsLineSymbolLayer *lsl = dynamic_cast<QgsLineSymbolLayer *>( layer );
990 if ( lsl )
991 {
992 // from QgsFillSymbolLayer::drawPreviewIcon() -- would be nicer to add the
993 // symbol type to QgsSymbolLayer::drawPreviewIcon so this logic could be avoided!
994
995 // hmm... why was this using size -1 ??
996 const QSizeF targetSize = QSizeF( size.width() - 1, size.height() - 1 );
997
998 const QList< QList< QPolygonF > > polys = patchShape ? patchShape->toQPolygonF( Qgis::SymbolType::Fill, targetSize )
1000
1001 lsl->startRender( symbolContext );
1002 QgsPaintEffect *effect = lsl->paintEffect();
1003
1004 std::unique_ptr< QgsEffectPainter > effectPainter;
1005 if ( effect && effect->enabled() )
1006 effectPainter = std::make_unique< QgsEffectPainter >( symbolContext.renderContext(), effect );
1007
1008 for ( const QList< QPolygonF > &poly : polys )
1009 {
1010 QVector< QPolygonF > rings;
1011 rings.reserve( poly.size() );
1012 for ( int i = 1; i < poly.size(); ++i )
1013 rings << poly.at( i );
1014 lsl->renderPolygonStroke( poly.value( 0 ), &rings, symbolContext );
1015 }
1016
1017 effectPainter.reset();
1018 lsl->stopRender( symbolContext );
1019 }
1020 }
1021 else
1022 layer->drawPreviewIcon( symbolContext, size );
1023 }
1024
1025 context->setForceVectorOutput( prevForceVector );
1026}
1027
1028void QgsSymbol::exportImage( const QString &path, const QString &format, QSize size )
1029{
1030 if ( format.compare( QLatin1String( "svg" ), Qt::CaseInsensitive ) == 0 )
1031 {
1032 QSvgGenerator generator;
1033 generator.setFileName( path );
1034 generator.setSize( size );
1035 generator.setViewBox( QRect( 0, 0, size.height(), size.height() ) );
1036
1037 QPainter painter( &generator );
1038 drawPreviewIcon( &painter, size );
1039 painter.end();
1040 }
1041 else
1042 {
1043 QImage image = asImage( size );
1044 image.save( path );
1045 }
1046}
1047
1048QImage QgsSymbol::asImage( QSize size, QgsRenderContext *customContext )
1049{
1050 QImage image( size, QImage::Format_ARGB32_Premultiplied );
1051 image.fill( 0 );
1052
1053 QPainter p( &image );
1054 p.setRenderHint( QPainter::Antialiasing );
1055 p.setRenderHint( QPainter::SmoothPixmapTransform );
1056
1057 drawPreviewIcon( &p, size, customContext );
1058
1059 return image;
1060}
1061
1062
1063QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, Qgis::SymbolPreviewFlags flags, const QgsScreenProperties &screen )
1064{
1065 const double devicePixelRatio = screen.isValid() ? screen.devicePixelRatio() : 1;
1066 QImage preview( QSize( 100, 100 ) * devicePixelRatio, QImage::Format_ARGB32_Premultiplied );
1067 preview.fill( 0 );
1068 preview.setDevicePixelRatio( devicePixelRatio );
1069
1070 QPainter p( &preview );
1071 p.setRenderHint( QPainter::Antialiasing );
1072 p.translate( 0.5, 0.5 ); // shift by half a pixel to avoid blurring due antialiasing
1073
1075 {
1076 p.setPen( QPen( Qt::gray ) );
1077 p.drawLine( QLineF( 0, 50, 100, 50 ) );
1078 p.drawLine( QLineF( 50, 0, 50, 100 ) );
1079 }
1080
1085 context.setPainterFlagsUsingContext( &p );
1086
1087 if ( screen.isValid() )
1088 {
1089 screen.updateRenderContextForScreen( context );
1090 }
1091
1092 if ( expressionContext )
1093 context.setExpressionContext( *expressionContext );
1094
1095 context.setIsGuiPreview( true );
1096 startRender( context );
1097
1099 {
1100 QPolygonF poly;
1101 poly << QPointF( 0, 50 ) << QPointF( 99, 50 );
1102 static_cast<QgsLineSymbol *>( this )->renderPolyline( poly, nullptr, context );
1103 }
1104 else if ( mType == Qgis::SymbolType::Fill )
1105 {
1106 QPolygonF polygon;
1107 polygon << QPointF( 20, 20 ) << QPointF( 80, 20 ) << QPointF( 80, 80 ) << QPointF( 20, 80 ) << QPointF( 20, 20 );
1108 static_cast<QgsFillSymbol *>( this )->renderPolygon( polygon, nullptr, nullptr, context );
1109 }
1110 else // marker
1111 {
1112 static_cast<QgsMarkerSymbol *>( this )->renderPoint( QPointF( 50, 50 ), nullptr, context );
1113 }
1114
1115 stopRender( context );
1116 return preview;
1117}
1118
1119QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, int flags )
1120{
1121 return bigSymbolPreviewImage( expressionContext, static_cast< Qgis::SymbolPreviewFlags >( flags ) );
1122}
1123
1124QString QgsSymbol::dump() const
1125{
1126 QString t;
1127 switch ( type() )
1128 {
1130 t = QStringLiteral( "MARKER" );
1131 break;
1133 t = QStringLiteral( "LINE" );
1134 break;
1136 t = QStringLiteral( "FILL" );
1137 break;
1138 default:
1139 Q_ASSERT( false && "unknown symbol type" );
1140 }
1141 QString s = QStringLiteral( "%1 SYMBOL (%2 layers) color %3" ).arg( t ).arg( mLayers.count() ).arg( QgsSymbolLayerUtils::encodeColor( color() ) );
1142
1143 for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
1144 {
1145 // TODO:
1146 }
1147 return s;
1148}
1149
1150void QgsSymbol::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
1151{
1152 props[ QStringLiteral( "alpha" )] = QString::number( opacity() );
1153 double scaleFactor = 1.0;
1154 props[ QStringLiteral( "uom" )] = QgsSymbolLayerUtils::encodeSldUom( outputUnit(), &scaleFactor );
1155 props[ QStringLiteral( "uomScale" )] = ( !qgsDoubleNear( scaleFactor, 1.0 ) ? qgsDoubleToString( scaleFactor ) : QString() );
1156
1157 for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
1158 {
1159 ( *it )->toSld( doc, element, props );
1160 }
1161}
1162
1164{
1166 for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
1167 {
1168 QgsSymbolLayer *layer = ( *it )->clone();
1169 layer->setLocked( ( *it )->isLocked() );
1170 layer->setRenderingPass( ( *it )->renderingPass() );
1171 layer->setEnabled( ( *it )->enabled() );
1172 layer->setId( ( *it )->id() );
1173 layer->setUserFlags( ( *it )->userFlags() );
1174 lst.append( layer );
1175 }
1176 return lst;
1177}
1178
1179void QgsSymbol::renderUsingLayer( QgsSymbolLayer *layer, QgsSymbolRenderContext &context, Qgis::GeometryType geometryType, const QPolygonF *points, const QVector<QPolygonF> *rings )
1180{
1181 Q_ASSERT( layer->type() == Qgis::SymbolType::Hybrid );
1182
1183 if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
1184 return;
1185
1186 QgsGeometryGeneratorSymbolLayer *generatorLayer = static_cast<QgsGeometryGeneratorSymbolLayer *>( layer );
1187
1188 QgsPaintEffect *effect = generatorLayer->paintEffect();
1189 if ( effect && effect->enabled() )
1190 {
1191 QgsEffectPainter p( context.renderContext(), effect );
1192 generatorLayer->render( context, geometryType, points, rings );
1193 }
1194 else
1195 {
1196 generatorLayer->render( context, geometryType, points, rings );
1197 }
1198}
1199
1200QSet<QString> QgsSymbol::usedAttributes( const QgsRenderContext &context ) const
1201{
1202 // calling referencedFields() with ignoreContext=true because in our expression context
1203 // we do not have valid QgsFields yet - because of that the field names from expressions
1204 // wouldn't get reported
1205 QSet<QString> attributes = mDataDefinedProperties.referencedFields( context.expressionContext(), true );
1206 QgsSymbolLayerList::const_iterator sIt = mLayers.constBegin();
1207 for ( ; sIt != mLayers.constEnd(); ++sIt )
1208 {
1209 if ( *sIt )
1210 {
1211 attributes.unite( ( *sIt )->usedAttributes( context ) );
1212 }
1213 }
1214 return attributes;
1215}
1216
1218{
1219 mDataDefinedProperties.setProperty( key, property );
1220}
1221
1223{
1224 if ( mDataDefinedProperties.hasActiveProperties() )
1225 return true;
1226
1227 for ( QgsSymbolLayer *layer : mLayers )
1228 {
1229 if ( layer->hasDataDefinedProperties() )
1230 return true;
1231 }
1232 return false;
1233}
1234
1236{
1237 for ( QgsSymbolLayer *layer : mLayers )
1238 {
1239 if ( layer->canCauseArtifactsBetweenAdjacentTiles() )
1240 return true;
1241 }
1242 return false;
1243}
1244
1251
1258
1260
1264class ExpressionContextScopePopper
1265{
1266 public:
1267
1268 ExpressionContextScopePopper() = default;
1269
1270 ~ExpressionContextScopePopper()
1271 {
1272 if ( context )
1273 context->popScope();
1274 }
1275
1276 QgsExpressionContext *context = nullptr;
1277};
1278
1282class GeometryRestorer
1283{
1284 public:
1285 GeometryRestorer( QgsRenderContext &context )
1286 : mContext( context ),
1287 mGeometry( context.geometry() )
1288 {}
1289
1290 ~GeometryRestorer()
1291 {
1292 mContext.setGeometry( mGeometry );
1293 }
1294
1295 private:
1296 QgsRenderContext &mContext;
1297 const QgsAbstractGeometry *mGeometry;
1298};
1300
1301void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize )
1302{
1303 if ( context.renderingStopped() )
1304 return;
1305
1306 const QgsGeometry geom = feature.geometry();
1307 if ( geom.isNull() )
1308 {
1309 return;
1310 }
1311
1312 GeometryRestorer geomRestorer( context );
1313
1314 bool usingSegmentizedGeometry = false;
1315 context.setGeometry( geom.constGet() );
1316
1317 if ( geom.type() != Qgis::GeometryType::Point && !geom.boundingBox().isNull() )
1318 {
1319 try
1320 {
1321 const QPointF boundsOrigin = _getPoint( context, QgsPoint( geom.boundingBox().xMinimum(), geom.boundingBox().yMinimum() ) );
1322 if ( std::isfinite( boundsOrigin.x() ) && std::isfinite( boundsOrigin.y() ) )
1323 context.setTextureOrigin( boundsOrigin );
1324 }
1325 catch ( QgsCsException & )
1326 {
1327
1328 }
1329 }
1330
1331 bool clippingEnabled = clipFeaturesToExtent();
1332 // do any symbol layers prevent feature clipping?
1333 for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
1334 {
1336 {
1337 clippingEnabled = false;
1338 break;
1339 }
1340 }
1341 if ( clippingEnabled && context.testFlag( Qgis::RenderContextFlag::RenderMapTile ) )
1342 {
1343 // If the "avoid artifacts between adjacent tiles" flag is set (RenderMapTile), then we'll force disable
1344 // the geometry clipping IF (and only if) this symbol can potentially have rendering artifacts when rendered as map tiles.
1345 // If the symbol won't have any artifacts anyway, then it's pointless and incredibly expensive to skip the clipping!
1347 {
1348 clippingEnabled = false;
1349 }
1350 }
1351 if ( context.extent().isEmpty() )
1352 clippingEnabled = false;
1353
1354 mSymbolRenderContext->setGeometryPartCount( geom.constGet()->partCount() );
1355 mSymbolRenderContext->setGeometryPartNum( 1 );
1356
1357 const bool needsExpressionContext = hasDataDefinedProperties();
1358 ExpressionContextScopePopper scopePopper;
1359 if ( mSymbolRenderContext->expressionContextScope() )
1360 {
1361 if ( needsExpressionContext )
1362 {
1363 // this is somewhat nasty - by appending this scope here it's now owned
1364 // by both mSymbolRenderContext AND context.expressionContext()
1365 // the RAII scopePopper is required to make sure it always has ownership transferred back
1366 // from context.expressionContext(), even if exceptions of other early exits occur in this
1367 // function
1368 context.expressionContext().appendScope( mSymbolRenderContext->expressionContextScope() );
1369 scopePopper.context = &context.expressionContext();
1370
1371 QgsExpressionContextUtils::updateSymbolScope( this, mSymbolRenderContext->expressionContextScope() );
1372 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_COUNT, mSymbolRenderContext->geometryPartCount(), true ) );
1373 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, 1, true ) );
1374 }
1375 }
1376
1377 // Collection of markers to paint, only used for no curve types.
1378 QPolygonF markers;
1379
1380 QgsGeometry renderedBoundsGeom;
1381
1382 // Step 1 - collect the set of painter coordinate geometries to render.
1383 // We do this upfront, because we only want to ever do this once, regardless how many symbol layers we need to render.
1384
1385 struct PointInfo
1386 {
1387 QPointF renderPoint;
1388 const QgsPoint *originalGeometry = nullptr;
1389 };
1390 QVector< PointInfo > pointsToRender;
1391
1392 struct LineInfo
1393 {
1394 QPolygonF renderLine;
1395 const QgsCurve *originalGeometry = nullptr;
1396 };
1397 QVector< LineInfo > linesToRender;
1398
1399 struct PolygonInfo
1400 {
1401 QPolygonF renderExterior;
1402 QVector< QPolygonF > renderRings;
1403 const QgsCurvePolygon *originalGeometry = nullptr;
1404 int originalPartIndex = 0;
1405 };
1406 QVector< PolygonInfo > polygonsToRender;
1407
1408 std::function< void ( const QgsAbstractGeometry *, int partIndex )> getPartGeometry;
1409 getPartGeometry = [&pointsToRender, &linesToRender, &polygonsToRender, &getPartGeometry, &context, &clippingEnabled, &markers, &feature, &usingSegmentizedGeometry, this]( const QgsAbstractGeometry * part, int partIndex = 0 )
1410 {
1411 Q_UNUSED( feature )
1412
1413 if ( !part )
1414 return;
1415
1416 // geometry preprocessing
1417 QgsGeometry temporaryGeometryContainer;
1418 const QgsAbstractGeometry *processedGeometry = nullptr;
1419
1420 const bool isMultiPart = qgsgeometry_cast< const QgsGeometryCollection * >( part ) && qgsgeometry_cast< const QgsGeometryCollection * >( part )->numGeometries() > 1;
1421
1422 if ( !isMultiPart )
1423 {
1424 // segmentize curved geometries
1425 const bool needsSegmentizing = QgsWkbTypes::isCurvedType( part->wkbType() ) || part->hasCurvedSegments();
1426 if ( needsSegmentizing )
1427 {
1428 std::unique_ptr< QgsAbstractGeometry > segmentizedPart( part->segmentize( context.segmentationTolerance(), context.segmentationToleranceType() ) );
1429 if ( !segmentizedPart )
1430 {
1431 return;
1432 }
1433 temporaryGeometryContainer.set( segmentizedPart.release() );
1434 processedGeometry = temporaryGeometryContainer.constGet();
1435 usingSegmentizedGeometry = true;
1436 }
1437 else
1438 {
1439 // no segmentation required
1440 processedGeometry = part;
1441 }
1442
1443 // Simplify the geometry, if needed.
1445 {
1446 const int simplifyHints = context.vectorSimplifyMethod().simplifyHints();
1447 const QgsMapToPixelSimplifier simplifier( simplifyHints, context.vectorSimplifyMethod().tolerance(),
1449
1450 std::unique_ptr< QgsAbstractGeometry > simplified( simplifier.simplify( processedGeometry ) );
1451 if ( simplified )
1452 {
1453 temporaryGeometryContainer.set( simplified.release() );
1454 processedGeometry = temporaryGeometryContainer.constGet();
1455 }
1456 }
1457
1458 // clip geometry to render context clipping regions
1459 if ( !context.featureClipGeometry().isEmpty() )
1460 {
1461 // apply feature clipping from context to the rendered geometry only -- just like the render time simplification,
1462 // we should NEVER apply this to the geometry attached to the feature itself. Doing so causes issues with certain
1463 // renderer settings, e.g. if polygons are being rendered using a rule based renderer based on the feature's area,
1464 // then we need to ensure that the original feature area is used instead of the clipped area..
1465 QgsGeos geos( processedGeometry );
1466 std::unique_ptr< QgsAbstractGeometry > clippedGeom( geos.intersection( context.featureClipGeometry().constGet() ) );
1467 if ( clippedGeom )
1468 {
1469 temporaryGeometryContainer.set( clippedGeom.release() );
1470 processedGeometry = temporaryGeometryContainer.constGet();
1471 }
1472 }
1473 }
1474 else
1475 {
1476 // for multipart geometries, the processing is deferred till we're rendering the actual part...
1477 processedGeometry = part;
1478 }
1479
1480 if ( !processedGeometry )
1481 {
1482 // shouldn't happen!
1483 QgsDebugError( QStringLiteral( "No processed geometry to render for part!" ) );
1484 return;
1485 }
1486
1487 switch ( QgsWkbTypes::flatType( processedGeometry->wkbType() ) )
1488 {
1490 {
1492 {
1493 QgsDebugMsgLevel( QStringLiteral( "point can be drawn only with marker symbol!" ), 2 );
1494 break;
1495 }
1496
1497 PointInfo info;
1498 info.originalGeometry = qgsgeometry_cast< const QgsPoint * >( part );
1499 info.renderPoint = _getPoint( context, *info.originalGeometry );
1500 pointsToRender << info;
1501 break;
1502 }
1503
1505 {
1507 {
1508 QgsDebugMsgLevel( QStringLiteral( "linestring can be drawn only with line symbol!" ), 2 );
1509 break;
1510 }
1511
1512 LineInfo info;
1513 info.originalGeometry = qgsgeometry_cast<const QgsCurve *>( part );
1514 info.renderLine = _getLineString( context, *qgsgeometry_cast<const QgsCurve *>( processedGeometry ), clippingEnabled );
1515 linesToRender << info;
1516 break;
1517 }
1518
1521 {
1522 QPolygonF pts;
1524 {
1525 QgsDebugMsgLevel( QStringLiteral( "polygon can be drawn only with fill symbol!" ), 2 );
1526 break;
1527 }
1528
1529 PolygonInfo info;
1530 info.originalGeometry = qgsgeometry_cast<const QgsCurvePolygon *>( part );
1531 info.originalPartIndex = partIndex;
1532 if ( !qgsgeometry_cast<const QgsPolygon *>( processedGeometry )->exteriorRing() )
1533 {
1534 QgsDebugError( QStringLiteral( "cannot render polygon with no exterior ring" ) );
1535 break;
1536 }
1537
1538 _getPolygon( info.renderExterior, info.renderRings, context, *qgsgeometry_cast<const QgsPolygon *>( processedGeometry ), clippingEnabled, mForceRHR );
1539 polygonsToRender << info;
1540 break;
1541 }
1542
1544 {
1545 const QgsMultiPoint *mp = qgsgeometry_cast< const QgsMultiPoint * >( processedGeometry );
1546 markers.reserve( mp->numGeometries() );
1547 }
1548 [[fallthrough]];
1552 {
1553 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( processedGeometry );
1554
1555 const unsigned int num = geomCollection->numGeometries();
1556 for ( unsigned int i = 0; i < num; ++i )
1557 {
1558 if ( context.renderingStopped() )
1559 break;
1560
1561 getPartGeometry( geomCollection->geometryN( i ), i );
1562 }
1563 break;
1564 }
1565
1568 {
1570 {
1571 QgsDebugMsgLevel( QStringLiteral( "multi-polygon can be drawn only with fill symbol!" ), 2 );
1572 break;
1573 }
1574
1575 QPolygonF pts;
1576
1577 const QgsGeometryCollection *geomCollection = dynamic_cast<const QgsGeometryCollection *>( processedGeometry );
1578 const unsigned int num = geomCollection->numGeometries();
1579
1580 // Sort components by approximate area (probably a bit faster than using
1581 // area() )
1582 std::map<double, QList<unsigned int> > thisAreaToPartNum;
1583 for ( unsigned int i = 0; i < num; ++i )
1584 {
1585 const QgsRectangle r( geomCollection->geometryN( i )->boundingBox() );
1586 thisAreaToPartNum[ r.width() * r.height()] << i;
1587 }
1588
1589 // Draw starting with larger parts down to smaller parts, so that in
1590 // case of a part being incorrectly inside another part, it is drawn
1591 // on top of it (#15419)
1592 std::map<double, QList<unsigned int> >::const_reverse_iterator iter = thisAreaToPartNum.rbegin();
1593 for ( ; iter != thisAreaToPartNum.rend(); ++iter )
1594 {
1595 const QList<unsigned int> &listPartIndex = iter->second;
1596 for ( int idx = 0; idx < listPartIndex.size(); ++idx )
1597 {
1598 const unsigned i = listPartIndex[idx];
1599 getPartGeometry( geomCollection->geometryN( i ), i );
1600 }
1601 }
1602 break;
1603 }
1604
1605 default:
1606 QgsDebugError( QStringLiteral( "feature %1: unsupported wkb type %2/%3 for rendering" )
1607 .arg( feature.id() )
1608 .arg( QgsWkbTypes::displayString( part->wkbType() ) )
1609 .arg( static_cast< quint32>( part->wkbType() ), 0, 16 ) );
1610 }
1611 };
1612
1613 // Use the simplified type ref when rendering -- this avoids some unnecessary cloning/geometry modification
1614 // (e.g. if the original geometry is a compound curve containing only a linestring curve, we don't have
1615 // to segmentize the geometry before rendering)
1616 getPartGeometry( geom.constGet()->simplifiedTypeRef(), 0 );
1617
1618 // step 2 - determine which layers to render
1619 std::vector< int > layers;
1620 if ( layer == -1 )
1621 {
1622 layers.reserve( mLayers.count() );
1623 for ( int i = 0; i < mLayers.count(); ++i )
1624 layers.emplace_back( i );
1625 }
1626 else
1627 {
1628 layers.emplace_back( layer );
1629 }
1630
1631 // step 3 - render these geometries using the desired symbol layers.
1632
1633 if ( needsExpressionContext )
1634 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_count" ), mLayers.count(), true ) );
1635
1636 for ( const int symbolLayerIndex : layers )
1637 {
1638 QgsSymbolLayer *symbolLayer = mLayers.value( symbolLayerIndex );
1639 if ( !symbolLayer || !symbolLayer->enabled() )
1640 continue;
1641
1642 if ( needsExpressionContext )
1643 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_index" ), symbolLayerIndex + 1, true ) );
1644
1645 symbolLayer->startFeatureRender( feature, context );
1646
1647 switch ( mType )
1648 {
1650 {
1651 int geometryPartNumber = 0;
1652 for ( const PointInfo &point : std::as_const( pointsToRender ) )
1653 {
1654 if ( context.renderingStopped() )
1655 break;
1656
1657 mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1658 if ( needsExpressionContext )
1659 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1660
1661 static_cast<QgsMarkerSymbol *>( this )->renderPoint( point.renderPoint, &feature, context, symbolLayerIndex, selected );
1662 geometryPartNumber++;
1663 }
1664
1665 break;
1666 }
1667
1669 {
1670 if ( linesToRender.empty() )
1671 break;
1672
1673 int geometryPartNumber = 0;
1674 for ( const LineInfo &line : std::as_const( linesToRender ) )
1675 {
1676 if ( context.renderingStopped() )
1677 break;
1678
1679 mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1680 if ( needsExpressionContext )
1681 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1682
1683 context.setGeometry( line.originalGeometry );
1684 static_cast<QgsLineSymbol *>( this )->renderPolyline( line.renderLine, &feature, context, symbolLayerIndex, selected );
1685 geometryPartNumber++;
1686 }
1687 break;
1688 }
1689
1691 {
1692 for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
1693 {
1694 if ( context.renderingStopped() )
1695 break;
1696
1697 mSymbolRenderContext->setGeometryPartNum( info.originalPartIndex + 1 );
1698 if ( needsExpressionContext )
1699 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, info.originalPartIndex + 1, true ) );
1700
1701 context.setGeometry( info.originalGeometry );
1702 static_cast<QgsFillSymbol *>( this )->renderPolygon( info.renderExterior, ( !info.renderRings.isEmpty() ? &info.renderRings : nullptr ), &feature, context, symbolLayerIndex, selected );
1703 }
1704
1705 break;
1706 }
1707
1709 break;
1710 }
1711
1712 symbolLayer->stopFeatureRender( feature, context );
1713 }
1714
1715 // step 4 - handle post processing steps
1716 switch ( mType )
1717 {
1719 {
1720 markers.reserve( pointsToRender.size() );
1721 for ( const PointInfo &info : std::as_const( pointsToRender ) )
1722 {
1724 {
1725 const QRectF bounds = static_cast<QgsMarkerSymbol *>( this )->bounds( info.renderPoint, context, feature );
1726 if ( context.hasRenderedFeatureHandlers() )
1727 {
1728 renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromRect( bounds )
1729 : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromRect( QgsRectangle( bounds ) ) << renderedBoundsGeom );
1730 }
1732 {
1733 //draw debugging rect
1734 context.painter()->setPen( Qt::red );
1735 context.painter()->setBrush( QColor( 255, 0, 0, 100 ) );
1736 context.painter()->drawRect( bounds );
1737 }
1738 }
1739
1740 if ( drawVertexMarker && !usingSegmentizedGeometry )
1741 {
1742 markers.append( info.renderPoint );
1743 }
1744 }
1745 break;
1746 }
1747
1749 {
1750 for ( const LineInfo &info : std::as_const( linesToRender ) )
1751 {
1752 if ( context.hasRenderedFeatureHandlers() && !info.renderLine.empty() )
1753 {
1754 renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderLine )
1755 : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderLine ) << renderedBoundsGeom );
1756 }
1757
1758 if ( drawVertexMarker && !usingSegmentizedGeometry )
1759 {
1760 markers << info.renderLine;
1761 }
1762 }
1763 break;
1764 }
1765
1767 {
1768 for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
1769 {
1770 if ( context.hasRenderedFeatureHandlers() && !info.renderExterior.empty() )
1771 {
1772 renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderExterior )
1773 : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderExterior ) << renderedBoundsGeom );
1774 // TODO: consider holes?
1775 }
1776
1777 if ( drawVertexMarker && !usingSegmentizedGeometry )
1778 {
1779 markers << info.renderExterior;
1780
1781 for ( const QPolygonF &hole : info.renderRings )
1782 {
1783 markers << hole;
1784 }
1785 }
1786 }
1787 break;
1788 }
1789
1791 break;
1792 }
1793
1794 if ( context.hasRenderedFeatureHandlers() && !renderedBoundsGeom.isNull() )
1795 {
1797 const QList< QgsRenderedFeatureHandlerInterface * > handlers = context.renderedFeatureHandlers();
1798 for ( QgsRenderedFeatureHandlerInterface *handler : handlers )
1799 handler->handleRenderedFeature( feature, renderedBoundsGeom, featureContext );
1800 }
1801
1802 if ( drawVertexMarker )
1803 {
1804 if ( !markers.isEmpty() && !context.renderingStopped() )
1805 {
1806 const auto constMarkers = markers;
1807 for ( QPointF marker : constMarkers )
1808 {
1809 renderVertexMarker( marker, context, currentVertexMarkerType, currentVertexMarkerSize );
1810 }
1811 }
1812 else
1813 {
1815 const QgsMapToPixel &mtp = context.mapToPixel();
1816
1817 QgsPoint vertexPoint;
1818 QgsVertexId vertexId;
1819 double x, y, z;
1820 QPointF mapPoint;
1821 while ( geom.constGet()->nextVertex( vertexId, vertexPoint ) )
1822 {
1823 //transform
1824 x = vertexPoint.x();
1825 y = vertexPoint.y();
1826 z = 0.0;
1827 if ( ct.isValid() )
1828 {
1829 ct.transformInPlace( x, y, z );
1830 }
1831 mapPoint.setX( x );
1832 mapPoint.setY( y );
1833 mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1834 renderVertexMarker( mapPoint, context, currentVertexMarkerType, currentVertexMarkerSize );
1835 }
1836 }
1837 }
1838}
1839
1841{
1842 return mSymbolRenderContext.get();
1843}
1844
1845void QgsSymbol::renderVertexMarker( QPointF pt, QgsRenderContext &context, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize )
1846{
1847 int markerSize = context.convertToPainterUnits( currentVertexMarkerSize, Qgis::RenderUnit::Millimeters );
1848 QgsSymbolLayerUtils::drawVertexMarker( pt.x(), pt.y(), *context.painter(), currentVertexMarkerType, markerSize );
1849}
1850
1851void QgsSymbol::initPropertyDefinitions()
1852{
1853 if ( !sPropertyDefinitions.isEmpty() )
1854 return;
1855
1856 QString origin = QStringLiteral( "symbol" );
1857
1858 sPropertyDefinitions = QgsPropertiesDefinition
1859 {
1860 { QgsSymbol::PropertyOpacity, QgsPropertyDefinition( "alpha", QObject::tr( "Opacity" ), QgsPropertyDefinition::Opacity, origin )},
1861 };
1862}
1863
1864void QgsSymbol::startFeatureRender( const QgsFeature &feature, QgsRenderContext &context, const int layer )
1865{
1866 if ( layer != -1 )
1867 {
1869 if ( symbolLayer && symbolLayer->enabled() )
1870 {
1871 symbolLayer->startFeatureRender( feature, context );
1872 }
1873 return;
1874 }
1875 else
1876 {
1877 const QList< QgsSymbolLayer * > layers = mLayers;
1878 for ( QgsSymbolLayer *symbolLayer : layers )
1879 {
1880 if ( !symbolLayer->enabled() )
1881 continue;
1882
1883 symbolLayer->startFeatureRender( feature, context );
1884 }
1885 }
1886}
1887
1888void QgsSymbol::stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context, int layer )
1889{
1890 if ( layer != -1 )
1891 {
1893 if ( symbolLayer && symbolLayer->enabled() )
1894 {
1895 symbolLayer->stopFeatureRender( feature, context );
1896 }
1897 return;
1898 }
1899 else
1900 {
1901 const QList< QgsSymbolLayer * > layers = mLayers;
1902 for ( QgsSymbolLayer *symbolLayer : layers )
1903 {
1904 if ( !symbolLayer->enabled() )
1905 continue;
1906
1907 symbolLayer->stopFeatureRender( feature, context );
1908 }
1909 }
1910}
@ 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:255
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
RenderUnit
Rendering size units.
Definition qgis.h:3627
@ Millimeters
Millimeters.
@ Unknown
Mixed or unknown units.
@ 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:1325
SymbolType
Attribute editing capabilities which may be supported by vector data providers.
Definition qgis.h:368
@ 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.
@ Forward
Forward transform (from source to destination)
Abstract base class for all geometries.
virtual const QgsAbstractGeometry * simplifiedTypeRef() const
Returns a reference to the simplest lossless representation of this geometry, e.g.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
bool is3D() const
Returns true if the geometry is 3D and contains a z-value.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual int partCount() const =0
Returns count of parts contained in the geometry.
virtual bool hasCurvedSegments() const
Returns true if the geometry contains curved segments.
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:44
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.
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.
Class for doing transforms between two map coordinate systems.
void transformCoords(int numPoint, double *x, double *y, double *z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform an array of coordinates to the destination CRS.
void transformInPlace(double &x, double &y, double &z, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
void transformPolygon(QPolygonF &polygon, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transforms a polygon to the destination coordinate system.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Custom exception class for Coordinate Reference System related exceptions.
Curve polygon geometry type.
int numInteriorRings() const
Returns the number of interior rings contained with the curve polygon.
const QgsCurve * exteriorRing() const
Returns the curve polygon's exterior ring.
const QgsCurve * interiorRing(int i) const
Retrieves an interior ring from 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
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
QgsFeatureId id
Definition qgsfeature.h:64
QgsGeometry geometry
Definition qgsfeature.h:67
Container of fields for a vector layer.
Definition qgsfields.h:45
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
int numGeometries() const
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.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
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.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Qgis::GeometryType type
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.
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.
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
Qgis::LayerType type
Definition qgsmaplayer.h:81
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.
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.
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
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.
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.
A rectangle specified with double values.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double width() const
Returns the width of the rectangle.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
bool isNull() const
Test if the rectangle is null (holding no spatial information).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
bool isEmpty() const
Returns true if the rectangle has no area.
double height() const
Returns the height of the rectangle.
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...
Stores properties relating to a screen.
double devicePixelRatio() const
Returns the ratio between physical pixels and device-independent pixels for the screen.
bool isValid() const
Returns true if the properties are valid.
void updateRenderContextForScreen(QgsRenderContext &context) const
Updates the settings in a render context to match the screen settings.
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 (...
Contains settings relating to symbol animation.
Definition qgssymbol.h:41
bool isAnimated() const
Returns true if the symbol is animated.
Definition qgssymbol.h:64
double frameRate() const
Returns the symbol animation frame rate (in frames per second).
Definition qgssymbol.h:78
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:94
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.
QgsSymbolLayerList cloneLayers() const
Retrieve a cloned list of all layers that make up this symbol.
void setOutputUnit(Qgis::RenderUnit unit) const
Sets the units to use for sizes and widths within the symbol.
QgsSymbolRenderContext * symbolRenderContext()
Returns the symbol render context.
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Property
Data definable properties.
Definition qgssymbol.h:131
@ PropertyOpacity
Opacity.
Definition qgssymbol.h:132
void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the symbol.
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)
Render a feature.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol's property collection, used for data defined overrides.
Definition qgssymbol.h:624
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.
qreal mOpacity
Symbol opacity (in the range 0 - 1)
Definition qgssymbol.h:791
Q_DECL_DEPRECATED const QgsVectorLayer * mLayer
Definition qgssymbol.h:807
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.
QgsSymbolAnimationSettings & animationSettings()
Returns a reference to the symbol animation settings.
void renderVertexMarker(QPointF pt, QgsRenderContext &context, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize)
Render editing vertex marker at specified point.
static QPointF _getPoint(QgsRenderContext &context, const QgsPoint &point)
Creates a point in screen coordinates from a QgsPoint in map coordinates.
Definition qgssymbol.h:720
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the symbol property definitions.
bool appendSymbolLayer(QgsSymbolLayer *layer)
Appends a symbol layer at the end of the current symbol layer list.
bool usesMapUnits() const
Returns true if the symbol has any components which use map unit based sizes.
Qgis::SymbolFlags flags() const
Returns flags for the symbol.
Definition qgssymbol.h:532
void toSld(QDomDocument &doc, QDomElement &element, QVariantMap props) const
Converts the symbol to a SLD representation.
void setColor(const QColor &color) const
Sets the color for the symbol.
bool insertSymbolLayer(int index, QgsSymbolLayer *layer)
Inserts a symbol layer to specified index.
QgsMapUnitScale mapUnitScale() const
Returns the map unit scale for the symbol.
static QString symbolTypeToString(Qgis::SymbolType type)
Returns a translated string version of the specified symbol type.
qreal opacity() const
Returns the opacity for the symbol.
Definition qgssymbol.h:497
bool canCauseArtifactsBetweenAdjacentTiles() const
Returns true if the symbol rendering can cause visible artifacts across a single feature when the fea...
static Qgis::SymbolType symbolTypeForGeometryType(Qgis::GeometryType type)
Returns the default symbol type required for the specified geometry type.
void setMapUnitScale(const QgsMapUnitScale &scale) const
Sets the map unit scale for the symbol.
bool clipFeaturesToExtent() const
Returns whether features drawn by the symbol will be clipped to the render context's extent.
Definition qgssymbol.h:554
QImage bigSymbolPreviewImage(QgsExpressionContext *expressionContext=nullptr, Qgis::SymbolPreviewFlags flags=Qgis::SymbolPreviewFlag::FlagIncludeCrosshairsForMarkerSymbols, const QgsScreenProperties &screen=QgsScreenProperties())
Returns a large (roughly 100x100 pixel) preview image for the symbol.
QImage asImage(QSize size, QgsRenderContext *customContext=nullptr)
Returns an image of the symbol at the specified size.
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.
QString dump() const
Returns a string dump of the symbol's properties.
bool hasDataDefinedProperties() const
Returns whether the symbol utilizes any data defined properties.
bool deleteSymbolLayer(int index)
Removes and deletes the symbol layer at the specified index.
virtual ~QgsSymbol()
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Qgis::SymbolType mType
Definition qgssymbol.h:787
bool changeSymbolLayer(int index, QgsSymbolLayer *layer)
Deletes the current layer at the specified index and replaces it with layer.
QgsSymbolLayer * takeSymbolLayer(int index)
Removes a symbol layer from the list and returns a pointer to it.
Qgis::SymbolRenderHints mRenderHints
Definition qgssymbol.h:793
bool mForceRHR
Definition qgssymbol.h:803
QgsSymbolLayerList mLayers
Definition qgssymbol.h:788
void drawPreviewIcon(QPainter *painter, QSize size, QgsRenderContext *customContext=nullptr, bool selected=false, const QgsExpressionContext *expressionContext=nullptr, const QgsLegendPatchShape *patchShape=nullptr, const QgsScreenProperties &screen=QgsScreenProperties())
Draws an icon of the symbol that occupies an area given by size using the specified painter.
Q_DECL_DEPRECATED const QgsVectorLayer * layer() const
QgsSymbolAnimationSettings mAnimationSettings
Definition qgssymbol.h:805
void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context, int layer=-1)
Called before symbol layers will be rendered for a particular feature.
QColor color() const
Returns the symbol's color.
Qgis::RenderUnit outputUnit() const
Returns the units to use for sizes and widths within the symbol.
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:153
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.
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Q_DECL_DEPRECATED void setLayer(const QgsVectorLayer *layer)
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.
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context, int layer=-1)
Called after symbol layers have been rendered for a particular feature.
static QgsSymbol * defaultSymbol(Qgis::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
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)
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)
Returns true if the WKB type is a curved type or can contain curved geometries.
static Qgis::WkbType flatType(Qgis::WkbType type)
Returns the flat type for a WKB type.
Contains geos related utilities and functions.
Definition qgsgeos.h:37
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:4916
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:4271
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:4915
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:4332
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition qgssymbol.h:30
Single variable definition for use within a QgsExpressionContextScope.
Utility class for identifying a unique vertex within a geometry.
Definition qgsvertexid.h:31