QGIS API Documentation 3.37.0-Master (4ace69f83af)
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#include "qgscolorutils.h"
56
57QgsPropertiesDefinition QgsSymbol::sPropertyDefinitions;
58
59Q_NOWARN_DEPRECATED_PUSH // because of deprecated mLayer
61 : mType( type )
62 , mLayers( layers )
63{
64
65 // check they're all correct symbol layers
66 for ( int i = 0; i < mLayers.count(); i++ )
67 {
68 if ( !mLayers.at( i ) )
69 {
70 mLayers.removeAt( i-- );
71 }
72 else if ( !mLayers.at( i )->isCompatibleWithSymbol( this ) )
73 {
74 delete mLayers.at( i );
75 mLayers.removeAt( i-- );
76 }
77 }
78}
80
81QPolygonF QgsSymbol::_getLineString( QgsRenderContext &context, const QgsCurve &curve, bool clipToExtent )
82{
83 if ( curve.is3D() )
84 return _getLineString3d( context, curve, clipToExtent );
85 else
86 return _getLineString2d( context, curve, clipToExtent );
87}
88
89QPolygonF QgsSymbol::_getLineString3d( QgsRenderContext &context, const QgsCurve &curve, bool clipToExtent )
90{
91 const unsigned int nPoints = curve.numPoints();
92
94 const QgsMapToPixel &mtp = context.mapToPixel();
95 QVector< double > pointsX;
96 QVector< double > pointsY;
97 QVector< double > pointsZ;
98
99 // apply clipping for large lines to achieve a better rendering performance
100 if ( clipToExtent && nPoints > 1 && !( context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection ) )
101 {
102 const QgsRectangle e = context.extent();
103 const double cw = e.width() / 10;
104 const double ch = e.height() / 10;
105 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
106
107 const QgsLineString *lineString = nullptr;
108 std::unique_ptr< QgsLineString > segmentized;
109 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( &curve ) )
110 {
111 lineString = ls;
112 }
113 else
114 {
115 segmentized.reset( qgsgeometry_cast< QgsLineString * >( curve.segmentize( ) ) );
116 lineString = segmentized.get();
117 }
118
119 QgsClipper::clipped3dLine( lineString->xVector(), lineString->yVector(), lineString->zVector(), pointsX, pointsY, pointsZ, clipRect );
120 }
121 else
122 {
123 // clone...
124 if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( &curve ) )
125 {
126 pointsX = ls->xVector();
127 pointsY = ls->yVector();
128 pointsZ = ls->zVector();
129 }
130 else
131 {
132 std::unique_ptr< QgsLineString > segmentized;
133 segmentized.reset( qgsgeometry_cast< QgsLineString * >( curve.segmentize( ) ) );
134
135 pointsX = segmentized->xVector();
136 pointsY = segmentized->yVector();
137 pointsZ = segmentized->zVector();
138 }
139 }
140
141 // transform the points to screen coordinates
142 const QVector< double > preTransformPointsZ = pointsZ;
143 bool wasTransformed = false;
144 if ( ct.isValid() )
145 {
146 //create x, y arrays
147 const int nVertices = pointsX.size();
148 wasTransformed = true;
149
150 try
151 {
152 ct.transformCoords( nVertices, pointsX.data(), pointsY.data(), pointsZ.data(), Qgis::TransformDirection::Forward );
153 }
154 catch ( QgsCsException & )
155 {
156 // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
157 }
158 }
159
160 // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
161 {
162 const int size = pointsX.size();
163
164 const double *xIn = pointsX.data();
165 const double *yIn = pointsY.data();
166 const double *zIn = pointsZ.data();
167
168 const double *preTransformZIn = wasTransformed ? preTransformPointsZ.constData() : nullptr;
169
170 double *xOut = pointsX.data();
171 double *yOut = pointsY.data();
172 double *zOut = pointsZ.data();
173 int outSize = 0;
174 for ( int i = 0; i < size; ++i )
175 {
176 bool pointOk = std::isfinite( *xIn ) && std::isfinite( *yIn );
177
178 // skip z points which have been made non-finite during transformations only. Ie if:
179 // - we did no transformation, then always render even if non-finite z
180 // - we did transformation and z is finite then render
181 // - we did transformation and z is non-finite BUT input z was also non finite then render
182 // - we did transformation and z is non-finite AND input z WAS finite then skip
183 pointOk &= !wasTransformed || std::isfinite( *zIn ) || !std::isfinite( *preTransformZIn );
184
185 if ( pointOk )
186 {
187 *xOut++ = *xIn++;
188 *yOut++ = *yIn++;
189 *zOut++ = *zIn++;
190 outSize++;
191 }
192 else
193 {
194 xIn++;
195 yIn++;
196 zIn++;
197 }
198
199 if ( preTransformZIn )
200 preTransformZIn++;
201 }
202 pointsX.resize( outSize );
203 pointsY.resize( outSize );
204 pointsZ.resize( outSize );
205 }
206
207 if ( clipToExtent && nPoints > 1 && context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection )
208 {
209 // early clipping was not possible, so we have to apply it here after transformation
210 const QgsRectangle e = context.mapExtent();
211 const double cw = e.width() / 10;
212 const double ch = e.height() / 10;
213 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
214
215 QVector< double > tempX;
216 QVector< double > tempY;
217 QVector< double > tempZ;
218 QgsClipper::clipped3dLine( pointsX, pointsY, pointsZ, tempX, tempY, tempZ, clipRect );
219 pointsX = tempX;
220 pointsY = tempY;
221 pointsZ = tempZ;
222 }
223
224 const int polygonSize = pointsX.size();
225 QPolygonF out( polygonSize );
226 const double *x = pointsX.constData();
227 const double *y = pointsY.constData();
228 QPointF *dest = out.data();
229 for ( int i = 0; i < polygonSize; ++i )
230 {
231 double screenX = *x++;
232 double screenY = *y++;
233 mtp.transformInPlace( screenX, screenY );
234 *dest++ = QPointF( screenX, screenY );
235 }
236
237 return out;
238}
239
240QPolygonF QgsSymbol::_getLineString2d( QgsRenderContext &context, const QgsCurve &curve, bool clipToExtent )
241{
242 const unsigned int nPoints = curve.numPoints();
243
245 const QgsMapToPixel &mtp = context.mapToPixel();
246 QPolygonF pts;
247
248 // apply clipping for large lines to achieve a better rendering performance
249 if ( clipToExtent && nPoints > 1 && !( context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection ) )
250 {
251 const QgsRectangle e = context.extent();
252 const double cw = e.width() / 10;
253 const double ch = e.height() / 10;
254 const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
255 pts = QgsClipper::clippedLine( curve, clipRect );
256 }
257 else
258 {
259 pts = curve.asQPolygonF();
260 }
261
262 // transform the QPolygonF to screen coordinates
263 if ( ct.isValid() )
264 {
265 try
266 {
267 ct.transformPolygon( pts );
268 }
269 catch ( QgsCsException & )
270 {
271 // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
272 }
273 }
274
275 // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
276 pts.erase( std::remove_if( pts.begin(), pts.end(),
277 []( const QPointF point )
278 {
279 return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
280 } ), pts.end() );
281
282 if ( clipToExtent && nPoints > 1 && context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection )
283 {
284 // early clipping was not possible, so we have to apply it here after transformation
285 const QgsRectangle e = context.mapExtent();
286 const double cw = e.width() / 10;
287 const double ch = e.height() / 10;
288 const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
289 pts = QgsClipper::clippedLine( pts, clipRect );
290 }
291
292 QPointF *ptr = pts.data();
293 for ( int i = 0; i < pts.size(); ++i, ++ptr )
294 {
295 mtp.transformInPlace( ptr->rx(), ptr->ry() );
296 }
297
298 return pts;
299}
300
301
302QPolygonF QgsSymbol::_getPolygonRing( QgsRenderContext &context, const QgsCurve &curve, const bool clipToExtent, const bool isExteriorRing, const bool correctRingOrientation )
303{
304 if ( curve.is3D() )
305 return _getPolygonRing3d( context, curve, clipToExtent, isExteriorRing, correctRingOrientation );
306 else
307 return _getPolygonRing2d( context, curve, clipToExtent, isExteriorRing, correctRingOrientation );
308}
309
310QPolygonF QgsSymbol::_getPolygonRing3d( QgsRenderContext &context, const QgsCurve &curve, const bool clipToExtent, const bool isExteriorRing, const bool correctRingOrientation )
311{
312 const QgsCoordinateTransform ct = context.coordinateTransform();
313 const QgsMapToPixel &mtp = context.mapToPixel();
314
315 QVector< double > pointsX;
316 QVector< double > pointsY;
317 QVector< double > pointsZ;
318
319 if ( curve.numPoints() < 1 )
320 return QPolygonF();
321
322 bool reverseRing = false;
323 if ( correctRingOrientation )
324 {
325 // ensure consistent polygon ring orientation
326 if ( ( isExteriorRing && curve.orientation() != Qgis::AngularDirection::Clockwise ) || ( !isExteriorRing && curve.orientation() != Qgis::AngularDirection::CounterClockwise ) )
327 {
328 reverseRing = true;
329 }
330 }
331
332 //clip close to view extent, if needed
333 if ( clipToExtent && !( context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection ) && !context.extent().contains( curve.boundingBox() ) )
334 {
335 const QgsRectangle e = context.extent();
336 const double cw = e.width() / 10;
337 const double ch = e.height() / 10;
338 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
339
340 const QgsLineString *lineString = nullptr;
341 std::unique_ptr< QgsLineString > segmentized;
342 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( &curve ) )
343 {
344 lineString = ls;
345 }
346 else
347 {
348 segmentized.reset( qgsgeometry_cast< QgsLineString * >( curve.segmentize( ) ) );
349 lineString = segmentized.get();
350 }
351
352 pointsX = lineString->xVector();
353 pointsY = lineString->yVector();
354 pointsZ = lineString->zVector();
355
356 QgsClipper::trimPolygon( pointsX, pointsY, pointsZ, clipRect );
357 }
358 else
359 {
360 // clone...
361 if ( const QgsLineString *ls = qgsgeometry_cast<const QgsLineString *>( &curve ) )
362 {
363 pointsX = ls->xVector();
364 pointsY = ls->yVector();
365 pointsZ = ls->zVector();
366 }
367 else
368 {
369 std::unique_ptr< QgsLineString > segmentized;
370 segmentized.reset( qgsgeometry_cast< QgsLineString * >( curve.segmentize( ) ) );
371
372 pointsX = segmentized->xVector();
373 pointsY = segmentized->yVector();
374 pointsZ = segmentized->zVector();
375 }
376 }
377
378 if ( reverseRing )
379 {
380 std::reverse( pointsX.begin(), pointsX.end() );
381 std::reverse( pointsY.begin(), pointsY.end() );
382 std::reverse( pointsZ.begin(), pointsZ.end() );
383 }
384
385 //transform the QPolygonF to screen coordinates
386 const QVector< double > preTransformPointsZ = pointsZ;
387 bool wasTransformed = false;
388 if ( ct.isValid() )
389 {
390 const int nVertices = pointsX.size();
391 wasTransformed = true;
392 try
393 {
394 ct.transformCoords( nVertices, pointsX.data(), pointsY.data(), pointsZ.data(), Qgis::TransformDirection::Forward );
395 }
396 catch ( QgsCsException & )
397 {
398 // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
399 }
400 }
401
402 // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
403 {
404 const int size = pointsX.size();
405
406 const double *xIn = pointsX.data();
407 const double *yIn = pointsY.data();
408 const double *zIn = pointsZ.data();
409
410 const double *preTransformZIn = wasTransformed ? preTransformPointsZ.constData() : nullptr;
411
412 double *xOut = pointsX.data();
413 double *yOut = pointsY.data();
414 double *zOut = pointsZ.data();
415 int outSize = 0;
416 for ( int i = 0; i < size; ++i )
417 {
418 bool pointOk = std::isfinite( *xIn ) && std::isfinite( *yIn );
419 // skip z points which have been made non-finite during transformations only. Ie if:
420 // - we did no transformation, then always render even if non-finite z
421 // - we did transformation and z is finite then render
422 // - we did transformation and z is non-finite BUT input z was also non finite then render
423 // - we did transformation and z is non-finite AND input z WAS finite then skip
424 pointOk &= !wasTransformed || std::isfinite( *zIn ) || !std::isfinite( *preTransformZIn );
425
426 if ( pointOk )
427 {
428 *xOut++ = *xIn++;
429 *yOut++ = *yIn++;
430 *zOut++ = *zIn++;
431 outSize++;
432 }
433 else
434 {
435 xIn++;
436 yIn++;
437 zIn++;
438 }
439
440 if ( preTransformZIn )
441 preTransformZIn++;
442 }
443 pointsX.resize( outSize );
444 pointsY.resize( outSize );
445 pointsZ.resize( outSize );
446 }
447
448 if ( clipToExtent && context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection && !context.mapExtent().contains( curve.boundingBox() ) )
449 {
450 // early clipping was not possible, so we have to apply it here after transformation
451 const QgsRectangle e = context.mapExtent();
452 const double cw = e.width() / 10;
453 const double ch = e.height() / 10;
454 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
455
456 QgsClipper::trimPolygon( pointsX, pointsY, pointsZ, clipRect );
457 }
458
459 const int polygonSize = pointsX.size();
460 QPolygonF out( polygonSize );
461 const double *x = pointsX.constData();
462 const double *y = pointsY.constData();
463 QPointF *dest = out.data();
464 for ( int i = 0; i < polygonSize; ++i )
465 {
466 double screenX = *x++;
467 double screenY = *y++;
468 mtp.transformInPlace( screenX, screenY );
469 *dest++ = QPointF( screenX, screenY );
470 }
471
472 if ( !out.empty() && !out.isClosed() )
473 out << out.at( 0 );
474
475 return out;
476}
477
478
479QPolygonF QgsSymbol::_getPolygonRing2d( QgsRenderContext &context, const QgsCurve &curve, const bool clipToExtent, const bool isExteriorRing, const bool correctRingOrientation )
480{
481 const QgsCoordinateTransform ct = context.coordinateTransform();
482 const QgsMapToPixel &mtp = context.mapToPixel();
483
484 QPolygonF poly = curve.asQPolygonF();
485
486 if ( curve.numPoints() < 1 )
487 return QPolygonF();
488
489 if ( correctRingOrientation )
490 {
491 // ensure consistent polygon ring orientation
492 if ( isExteriorRing && curve.orientation() != Qgis::AngularDirection::Clockwise )
493 std::reverse( poly.begin(), poly.end() );
494 else if ( !isExteriorRing && curve.orientation() != Qgis::AngularDirection::CounterClockwise )
495 std::reverse( poly.begin(), poly.end() );
496 }
497
498 //clip close to view extent, if needed
499 if ( clipToExtent && !( context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection ) && !context.extent().contains( poly.boundingRect() ) )
500 {
501 const QgsRectangle e = context.extent();
502 const double cw = e.width() / 10;
503 const double ch = e.height() / 10;
504 const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
505 QgsClipper::trimPolygon( poly, clipRect );
506 }
507
508 //transform the QPolygonF to screen coordinates
509 if ( ct.isValid() )
510 {
511 try
512 {
513 ct.transformPolygon( poly );
514 }
515 catch ( QgsCsException & )
516 {
517 // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
518 }
519 }
520
521 // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
522 poly.erase( std::remove_if( poly.begin(), poly.end(),
523 []( const QPointF point )
524 {
525 return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
526 } ), poly.end() );
527
528 if ( clipToExtent && context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection && !context.mapExtent().contains( poly.boundingRect() ) )
529 {
530 // early clipping was not possible, so we have to apply it here after transformation
531 const QgsRectangle e = context.mapExtent();
532 const double cw = e.width() / 10;
533 const double ch = e.height() / 10;
534 const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
535 QgsClipper::trimPolygon( poly, clipRect );
536 }
537
538 QPointF *ptr = poly.data();
539 for ( int i = 0; i < poly.size(); ++i, ++ptr )
540 {
541 mtp.transformInPlace( ptr->rx(), ptr->ry() );
542 }
543
544 if ( !poly.empty() && !poly.isClosed() )
545 poly << poly.at( 0 );
546
547 return poly;
548}
549
550void QgsSymbol::_getPolygon( QPolygonF &pts, QVector<QPolygonF> &holes, QgsRenderContext &context, const QgsPolygon &polygon, const bool clipToExtent, const bool correctRingOrientation )
551{
552 holes.clear();
553
554 pts = _getPolygonRing( context, *polygon.exteriorRing(), clipToExtent, true, correctRingOrientation );
555 const int ringCount = polygon.numInteriorRings();
556 holes.reserve( ringCount );
557 for ( int idx = 0; idx < ringCount; idx++ )
558 {
559 const QPolygonF hole = _getPolygonRing( context, *( polygon.interiorRing( idx ) ), clipToExtent, false, correctRingOrientation );
560 if ( !hole.isEmpty() )
561 holes.append( hole );
562 }
563}
564
566{
567 switch ( type )
568 {
570 return QObject::tr( "Marker" );
572 return QObject::tr( "Line" );
574 return QObject::tr( "Fill" );
576 return QObject::tr( "Hybrid" );
577 }
578 return QString();
579}
580
597
599{
600 QgsSymbol::initPropertyDefinitions();
601 return sPropertyDefinitions;
602}
603
605{
606 // delete all symbol layers (we own them, so it's okay)
607 qDeleteAll( mLayers );
608}
609
611{
612 if ( mLayers.empty() )
613 {
615 }
616
617 QgsSymbolLayerList::const_iterator it = mLayers.constBegin();
618
619 Qgis::RenderUnit unit = ( *it )->outputUnit();
620
621 for ( ; it != mLayers.constEnd(); ++it )
622 {
623 if ( ( *it )->outputUnit() != unit )
624 {
626 }
627 }
628 return unit;
629}
630
632{
633 if ( mLayers.empty() )
634 {
635 return false;
636 }
637
638 for ( const QgsSymbolLayer *layer : mLayers )
639 {
640 if ( layer->usesMapUnits() )
641 {
642 return true;
643 }
644 }
645 return false;
646}
647
649{
650 if ( mLayers.empty() )
651 {
652 return QgsMapUnitScale();
653 }
654
655 QgsSymbolLayerList::const_iterator it = mLayers.constBegin();
656 if ( it == mLayers.constEnd() )
657 return QgsMapUnitScale();
658
659 QgsMapUnitScale scale = ( *it )->mapUnitScale();
660 ++it;
661
662 for ( ; it != mLayers.constEnd(); ++it )
663 {
664 if ( ( *it )->mapUnitScale() != scale )
665 {
666 return QgsMapUnitScale();
667 }
668 }
669 return scale;
670}
671
673{
674 const auto constMLayers = mLayers;
675 for ( QgsSymbolLayer *layer : constMLayers )
676 {
677 layer->setOutputUnit( u );
678 }
679}
680
682{
683 const auto constMLayers = mLayers;
684 for ( QgsSymbolLayer *layer : constMLayers )
685 {
686 layer->setMapUnitScale( scale );
687 }
688}
689
694
699
701{
702 mAnimationSettings = settings;
703}
704
706{
707 std::unique_ptr< QgsSymbol > s;
708
709 // override global default if project has a default for this type
710 switch ( geomType )
711 {
713 s.reset( QgsProject::instance()->styleSettings()->defaultSymbol( Qgis::SymbolType::Marker ) );
714 break;
716 s.reset( QgsProject::instance()->styleSettings()->defaultSymbol( Qgis::SymbolType::Line ) );
717 break;
719 s.reset( QgsProject::instance()->styleSettings()->defaultSymbol( Qgis::SymbolType::Fill ) );
720 break;
721 default:
722 break;
723 }
724
725 // if no default found for this type, get global default (as previously)
726 if ( !s )
727 {
728 switch ( geomType )
729 {
731 s = std::make_unique< QgsMarkerSymbol >();
732 break;
734 s = std::make_unique< QgsLineSymbol >();
735 break;
737 s = std::make_unique< QgsFillSymbol >();
738 break;
739 default:
740 QgsDebugError( QStringLiteral( "unknown layer's geometry type" ) );
741 break;
742 }
743 }
744
745 if ( !s )
746 return nullptr;
747
748 // set opacity
749 s->setOpacity( QgsProject::instance()->styleSettings()->defaultSymbolOpacity() );
750
751 // set random color, it project prefs allow
752 if ( QgsProject::instance()->styleSettings()->randomizeDefaultSymbolColor() )
753 {
754 s->setColor( QgsApplication::colorSchemeRegistry()->fetchRandomStyleColor() );
755 }
756
757 return s.release();
758}
759
761{
762 return mLayers.value( layer );
763}
764
765const QgsSymbolLayer *QgsSymbol::symbolLayer( int layer ) const
766{
767 return mLayers.value( layer );
768}
769
771{
772 if ( index < 0 || index > mLayers.count() ) // can be added also after the last index
773 return false;
774
775 if ( !layer || !layer->isCompatibleWithSymbol( this ) )
776 return false;
777
778 mLayers.insert( index, layer );
779 return true;
780}
781
782
784{
785 if ( !layer || !layer->isCompatibleWithSymbol( this ) )
786 return false;
787
788 mLayers.append( layer );
789 return true;
790}
791
792
794{
795 if ( index < 0 || index >= mLayers.count() )
796 return false;
797
798 delete mLayers.at( index );
799 mLayers.removeAt( index );
800 return true;
801}
802
803
805{
806 if ( index < 0 || index >= mLayers.count() )
807 return nullptr;
808
809 return mLayers.takeAt( index );
810}
811
812
814{
815 QgsSymbolLayer *oldLayer = mLayers.value( index );
816
817 if ( oldLayer == layer )
818 return false;
819
820 if ( !layer || !layer->isCompatibleWithSymbol( this ) )
821 return false;
822
823 delete oldLayer; // first delete the original layer
824 mLayers[index] = layer; // set new layer
825 return true;
826}
827
828
829void QgsSymbol::startRender( QgsRenderContext &context, const QgsFields &fields )
830{
831 Q_ASSERT_X( !mStarted, "startRender", "Rendering has already been started for this symbol instance!" );
832 mStarted = true;
833
834 mSymbolRenderContext.reset( new QgsSymbolRenderContext( context, Qgis::RenderUnit::Unknown, mOpacity, false, mRenderHints, nullptr, fields ) );
835
836 // Why do we need a copy here ? Is it to make sure the symbol layer rendering does not mess with the symbol render context ?
837 // Or is there another profound reason ?
838 QgsSymbolRenderContext symbolContext( context, Qgis::RenderUnit::Unknown, mOpacity, false, mRenderHints, nullptr, fields );
839
840 std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::updateSymbolScope( this, new QgsExpressionContextScope() ) );
841
843 {
844 const long long mapFrameNumber = context.currentFrame();
845 double animationTimeSeconds = 0;
846 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
847 {
848 // render is part of an animation, so we base the calculated frame on that
849 animationTimeSeconds = mapFrameNumber / context.frameRate();
850 }
851 else
852 {
853 // render is outside of animation, so base the calculated frame on the current epoch
854 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
855 }
856
857 const long long symbolFrame = static_cast< long long >( std::floor( animationTimeSeconds * mAnimationSettings.frameRate() ) );
858 scope->setVariable( QStringLiteral( "symbol_frame" ), symbolFrame, true );
859 }
860
861 mSymbolRenderContext->setExpressionContextScope( scope.release() );
862
863 mDataDefinedProperties.prepare( context.expressionContext() );
864
865 const auto constMLayers = mLayers;
866 for ( QgsSymbolLayer *layer : constMLayers )
867 {
868 if ( !layer->enabled() || !context.isSymbolLayerEnabled( layer ) )
869 continue;
870
871 layer->prepareExpressions( symbolContext );
872 layer->prepareMasks( symbolContext );
873 layer->startRender( symbolContext );
874 }
875}
876
878{
879 Q_ASSERT_X( mStarted, "startRender", "startRender was not called for this symbol instance!" );
880 mStarted = false;
881
882 Q_UNUSED( context )
883 if ( mSymbolRenderContext )
884 {
885 const auto constMLayers = mLayers;
886 for ( QgsSymbolLayer *layer : constMLayers )
887 {
888 if ( !layer->enabled() || !context.isSymbolLayerEnabled( layer ) )
889 continue;
890
891 layer->stopRender( *mSymbolRenderContext );
892 }
893 }
894
895 mSymbolRenderContext.reset( nullptr );
896
898 mLayer = nullptr;
900}
901
902void QgsSymbol::setColor( const QColor &color ) const
903{
904 const auto constMLayers = mLayers;
905 for ( QgsSymbolLayer *layer : constMLayers )
906 {
907 if ( !layer->isLocked() )
908 layer->setColor( color );
909 }
910}
911
912QColor QgsSymbol::color() const
913{
914 for ( const QgsSymbolLayer *layer : mLayers )
915 {
916 // return color of the first unlocked layer
917 if ( !layer->isLocked() )
918 {
919 const QColor layerColor = layer->color();
920 if ( layerColor.isValid() )
921 return layerColor;
922 }
923 }
924 return QColor( 0, 0, 0 );
925}
926
927void QgsSymbol::drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext, bool selected, const QgsExpressionContext *expressionContext, const QgsLegendPatchShape *patchShape, const QgsScreenProperties &screen )
928{
929 QgsRenderContext *context = customContext;
930 std::unique_ptr< QgsRenderContext > tempContext;
931 if ( !context )
932 {
933 tempContext.reset( new QgsRenderContext( QgsRenderContext::fromQPainter( painter ) ) );
934 context = tempContext.get();
936 }
937
938 if ( screen.isValid() )
939 {
940 screen.updateRenderContextForScreen( *context );
941 }
942
943 const bool prevForceVector = context->forceVectorOutput();
944 context->setForceVectorOutput( true );
945
946 const double opacity = expressionContext ? dataDefinedProperties().valueAsDouble( QgsSymbol::Property::Opacity, *expressionContext, mOpacity ) : mOpacity;
947
948 QgsSymbolRenderContext symbolContext( *context, Qgis::RenderUnit::Unknown, opacity, false, mRenderHints, nullptr );
949 symbolContext.setSelected( selected );
950 switch ( mType )
951 {
954 break;
957 break;
960 break;
963 break;
964 }
965
966 if ( patchShape )
967 symbolContext.setPatchShape( *patchShape );
968
969 if ( !customContext && expressionContext )
970 {
971 context->setExpressionContext( *expressionContext );
972 }
973 else if ( !customContext )
974 {
975 // if no render context was passed, build a minimal expression context
976 QgsExpressionContext expContext;
978 context->setExpressionContext( expContext );
979 }
980
981 for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
982 {
983 if ( !layer->enabled() || ( customContext && !customContext->isSymbolLayerEnabled( layer ) ) )
984 continue;
985
987 {
988 // line symbol layer would normally draw just a line
989 // so we override this case to force it to draw a polygon stroke
990 QgsLineSymbolLayer *lsl = dynamic_cast<QgsLineSymbolLayer *>( layer );
991 if ( lsl )
992 {
993 // from QgsFillSymbolLayer::drawPreviewIcon() -- would be nicer to add the
994 // symbol type to QgsSymbolLayer::drawPreviewIcon so this logic could be avoided!
995
996 // hmm... why was this using size -1 ??
997 const QSizeF targetSize = QSizeF( size.width() - 1, size.height() - 1 );
998
999 const QList< QList< QPolygonF > > polys = patchShape ? patchShape->toQPolygonF( Qgis::SymbolType::Fill, targetSize )
1001
1002 lsl->startRender( symbolContext );
1003 QgsPaintEffect *effect = lsl->paintEffect();
1004
1005 std::unique_ptr< QgsEffectPainter > effectPainter;
1006 if ( effect && effect->enabled() )
1007 effectPainter = std::make_unique< QgsEffectPainter >( symbolContext.renderContext(), effect );
1008
1009 for ( const QList< QPolygonF > &poly : polys )
1010 {
1011 QVector< QPolygonF > rings;
1012 rings.reserve( poly.size() );
1013 for ( int i = 1; i < poly.size(); ++i )
1014 rings << poly.at( i );
1015 lsl->renderPolygonStroke( poly.value( 0 ), &rings, symbolContext );
1016 }
1017
1018 effectPainter.reset();
1019 lsl->stopRender( symbolContext );
1020 }
1021 }
1022 else
1023 layer->drawPreviewIcon( symbolContext, size );
1024 }
1025
1026 context->setForceVectorOutput( prevForceVector );
1027}
1028
1029void QgsSymbol::exportImage( const QString &path, const QString &format, QSize size )
1030{
1031 if ( format.compare( QLatin1String( "svg" ), Qt::CaseInsensitive ) == 0 )
1032 {
1033 QSvgGenerator generator;
1034 generator.setFileName( path );
1035 generator.setSize( size );
1036 generator.setViewBox( QRect( 0, 0, size.height(), size.height() ) );
1037
1038 QPainter painter( &generator );
1039 drawPreviewIcon( &painter, size );
1040 painter.end();
1041 }
1042 else
1043 {
1044 QImage image = asImage( size );
1045 image.save( path );
1046 }
1047}
1048
1049QImage QgsSymbol::asImage( QSize size, QgsRenderContext *customContext )
1050{
1051 QImage image( size, QImage::Format_ARGB32_Premultiplied );
1052 image.fill( 0 );
1053
1054 QPainter p( &image );
1055 p.setRenderHint( QPainter::Antialiasing );
1056 p.setRenderHint( QPainter::SmoothPixmapTransform );
1057
1058 drawPreviewIcon( &p, size, customContext );
1059
1060 return image;
1061}
1062
1063
1064QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, Qgis::SymbolPreviewFlags flags, const QgsScreenProperties &screen )
1065{
1066 const double devicePixelRatio = screen.isValid() ? screen.devicePixelRatio() : 1;
1067 QImage preview( QSize( 100, 100 ) * devicePixelRatio, QImage::Format_ARGB32_Premultiplied );
1068 preview.fill( 0 );
1069 preview.setDevicePixelRatio( devicePixelRatio );
1070
1071 QPainter p( &preview );
1072 p.setRenderHint( QPainter::Antialiasing );
1073 p.translate( 0.5, 0.5 ); // shift by half a pixel to avoid blurring due antialiasing
1074
1076 {
1077 p.setPen( QPen( Qt::gray ) );
1078 p.drawLine( QLineF( 0, 50, 100, 50 ) );
1079 p.drawLine( QLineF( 50, 0, 50, 100 ) );
1080 }
1081
1086 context.setPainterFlagsUsingContext( &p );
1087
1088 if ( screen.isValid() )
1089 {
1090 screen.updateRenderContextForScreen( context );
1091 }
1092
1093 if ( expressionContext )
1094 context.setExpressionContext( *expressionContext );
1095
1096 context.setIsGuiPreview( true );
1097 startRender( context );
1098
1100 {
1101 QPolygonF poly;
1102 poly << QPointF( 0, 50 ) << QPointF( 99, 50 );
1103 static_cast<QgsLineSymbol *>( this )->renderPolyline( poly, nullptr, context );
1104 }
1105 else if ( mType == Qgis::SymbolType::Fill )
1106 {
1107 QPolygonF polygon;
1108 polygon << QPointF( 20, 20 ) << QPointF( 80, 20 ) << QPointF( 80, 80 ) << QPointF( 20, 80 ) << QPointF( 20, 20 );
1109 static_cast<QgsFillSymbol *>( this )->renderPolygon( polygon, nullptr, nullptr, context );
1110 }
1111 else // marker
1112 {
1113 static_cast<QgsMarkerSymbol *>( this )->renderPoint( QPointF( 50, 50 ), nullptr, context );
1114 }
1115
1116 stopRender( context );
1117 return preview;
1118}
1119
1120QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, int flags )
1121{
1122 return bigSymbolPreviewImage( expressionContext, static_cast< Qgis::SymbolPreviewFlags >( flags ) );
1123}
1124
1125QString QgsSymbol::dump() const
1126{
1127 QString t;
1128 switch ( type() )
1129 {
1131 t = QStringLiteral( "MARKER" );
1132 break;
1134 t = QStringLiteral( "LINE" );
1135 break;
1137 t = QStringLiteral( "FILL" );
1138 break;
1139 default:
1140 Q_ASSERT( false && "unknown symbol type" );
1141 }
1142 QString s = QStringLiteral( "%1 SYMBOL (%2 layers) color %3" ).arg( t ).arg( mLayers.count() ).arg( QgsColorUtils::colorToString( color() ) );
1143
1144 for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
1145 {
1146 // TODO:
1147 }
1148 return s;
1149}
1150
1151void QgsSymbol::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
1152{
1153 props[ QStringLiteral( "alpha" )] = QString::number( opacity() );
1154 double scaleFactor = 1.0;
1155 props[ QStringLiteral( "uom" )] = QgsSymbolLayerUtils::encodeSldUom( outputUnit(), &scaleFactor );
1156 props[ QStringLiteral( "uomScale" )] = ( !qgsDoubleNear( scaleFactor, 1.0 ) ? qgsDoubleToString( scaleFactor ) : QString() );
1157
1158 for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
1159 {
1160 ( *it )->toSld( doc, element, props );
1161 }
1162}
1163
1165{
1167 for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
1168 {
1169 QgsSymbolLayer *layer = ( *it )->clone();
1170 layer->setLocked( ( *it )->isLocked() );
1171 layer->setRenderingPass( ( *it )->renderingPass() );
1172 layer->setEnabled( ( *it )->enabled() );
1173 layer->setId( ( *it )->id() );
1174 layer->setUserFlags( ( *it )->userFlags() );
1175 lst.append( layer );
1176 }
1177 return lst;
1178}
1179
1180void QgsSymbol::renderUsingLayer( QgsSymbolLayer *layer, QgsSymbolRenderContext &context, Qgis::GeometryType geometryType, const QPolygonF *points, const QVector<QPolygonF> *rings )
1181{
1182 Q_ASSERT( layer->type() == Qgis::SymbolType::Hybrid );
1183
1184 if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::Property::LayerEnabled, context.renderContext().expressionContext(), true ) )
1185 return;
1186
1187 QgsGeometryGeneratorSymbolLayer *generatorLayer = static_cast<QgsGeometryGeneratorSymbolLayer *>( layer );
1188
1189 QgsPaintEffect *effect = generatorLayer->paintEffect();
1190 if ( effect && effect->enabled() )
1191 {
1192 QgsEffectPainter p( context.renderContext(), effect );
1193 generatorLayer->render( context, geometryType, points, rings );
1194 }
1195 else
1196 {
1197 generatorLayer->render( context, geometryType, points, rings );
1198 }
1199}
1200
1201QSet<QString> QgsSymbol::usedAttributes( const QgsRenderContext &context ) const
1202{
1203 // calling referencedFields() with ignoreContext=true because in our expression context
1204 // we do not have valid QgsFields yet - because of that the field names from expressions
1205 // wouldn't get reported
1206 QSet<QString> attributes = mDataDefinedProperties.referencedFields( context.expressionContext(), true );
1207 QgsSymbolLayerList::const_iterator sIt = mLayers.constBegin();
1208 for ( ; sIt != mLayers.constEnd(); ++sIt )
1209 {
1210 if ( *sIt )
1211 {
1212 attributes.unite( ( *sIt )->usedAttributes( context ) );
1213 }
1214 }
1215 return attributes;
1216}
1217
1219{
1220 mDataDefinedProperties.setProperty( key, property );
1221}
1222
1224{
1225 if ( mDataDefinedProperties.hasActiveProperties() )
1226 return true;
1227
1228 for ( QgsSymbolLayer *layer : mLayers )
1229 {
1230 if ( layer->hasDataDefinedProperties() )
1231 return true;
1232 }
1233 return false;
1234}
1235
1237{
1238 for ( QgsSymbolLayer *layer : mLayers )
1239 {
1240 if ( layer->canCauseArtifactsBetweenAdjacentTiles() )
1241 return true;
1242 }
1243 return false;
1244}
1245
1252
1259
1261
1265class ExpressionContextScopePopper
1266{
1267 public:
1268
1269 ExpressionContextScopePopper() = default;
1270
1271 ~ExpressionContextScopePopper()
1272 {
1273 if ( context )
1274 context->popScope();
1275 }
1276
1277 QgsExpressionContext *context = nullptr;
1278};
1279
1283class GeometryRestorer
1284{
1285 public:
1286 GeometryRestorer( QgsRenderContext &context )
1287 : mContext( context ),
1288 mGeometry( context.geometry() )
1289 {}
1290
1291 ~GeometryRestorer()
1292 {
1293 mContext.setGeometry( mGeometry );
1294 }
1295
1296 private:
1297 QgsRenderContext &mContext;
1298 const QgsAbstractGeometry *mGeometry;
1299};
1301
1302void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize )
1303{
1304 if ( context.renderingStopped() )
1305 return;
1306
1307 const QgsGeometry geom = feature.geometry();
1308 if ( geom.isNull() )
1309 {
1310 return;
1311 }
1312
1313 GeometryRestorer geomRestorer( context );
1314
1315 bool usingSegmentizedGeometry = false;
1316 context.setGeometry( geom.constGet() );
1317
1318 if ( geom.type() != Qgis::GeometryType::Point && !geom.boundingBox().isNull() )
1319 {
1320 try
1321 {
1322 const QPointF boundsOrigin = _getPoint( context, QgsPoint( geom.boundingBox().xMinimum(), geom.boundingBox().yMinimum() ) );
1323 if ( std::isfinite( boundsOrigin.x() ) && std::isfinite( boundsOrigin.y() ) )
1324 context.setTextureOrigin( boundsOrigin );
1325 }
1326 catch ( QgsCsException & )
1327 {
1328
1329 }
1330 }
1331
1332 bool clippingEnabled = clipFeaturesToExtent();
1333 // do any symbol layers prevent feature clipping?
1334 for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
1335 {
1337 {
1338 clippingEnabled = false;
1339 break;
1340 }
1341 }
1342 if ( clippingEnabled && context.testFlag( Qgis::RenderContextFlag::RenderMapTile ) )
1343 {
1344 // If the "avoid artifacts between adjacent tiles" flag is set (RenderMapTile), then we'll force disable
1345 // the geometry clipping IF (and only if) this symbol can potentially have rendering artifacts when rendered as map tiles.
1346 // If the symbol won't have any artifacts anyway, then it's pointless and incredibly expensive to skip the clipping!
1348 {
1349 clippingEnabled = false;
1350 }
1351 }
1352 if ( context.extent().isEmpty() )
1353 clippingEnabled = false;
1354
1355 mSymbolRenderContext->setGeometryPartCount( geom.constGet()->partCount() );
1356 mSymbolRenderContext->setGeometryPartNum( 1 );
1357
1358 const bool needsExpressionContext = hasDataDefinedProperties();
1359 ExpressionContextScopePopper scopePopper;
1360 if ( mSymbolRenderContext->expressionContextScope() )
1361 {
1362 if ( needsExpressionContext )
1363 {
1364 // this is somewhat nasty - by appending this scope here it's now owned
1365 // by both mSymbolRenderContext AND context.expressionContext()
1366 // the RAII scopePopper is required to make sure it always has ownership transferred back
1367 // from context.expressionContext(), even if exceptions of other early exits occur in this
1368 // function
1369 context.expressionContext().appendScope( mSymbolRenderContext->expressionContextScope() );
1370 scopePopper.context = &context.expressionContext();
1371
1372 QgsExpressionContextUtils::updateSymbolScope( this, mSymbolRenderContext->expressionContextScope() );
1373 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_COUNT, mSymbolRenderContext->geometryPartCount(), true ) );
1374 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, 1, true ) );
1375 }
1376 }
1377
1378 // Collection of markers to paint, only used for no curve types.
1379 QPolygonF markers;
1380
1381 QgsGeometry renderedBoundsGeom;
1382
1383 // Step 1 - collect the set of painter coordinate geometries to render.
1384 // We do this upfront, because we only want to ever do this once, regardless how many symbol layers we need to render.
1385
1386 struct PointInfo
1387 {
1388 QPointF renderPoint;
1389 const QgsPoint *originalGeometry = nullptr;
1390 };
1391 QVector< PointInfo > pointsToRender;
1392
1393 struct LineInfo
1394 {
1395 QPolygonF renderLine;
1396 const QgsCurve *originalGeometry = nullptr;
1397 };
1398 QVector< LineInfo > linesToRender;
1399
1400 struct PolygonInfo
1401 {
1402 QPolygonF renderExterior;
1403 QVector< QPolygonF > renderRings;
1404 const QgsCurvePolygon *originalGeometry = nullptr;
1405 int originalPartIndex = 0;
1406 };
1407 QVector< PolygonInfo > polygonsToRender;
1408
1409 std::function< void ( const QgsAbstractGeometry *, int partIndex )> getPartGeometry;
1410 getPartGeometry = [&pointsToRender, &linesToRender, &polygonsToRender, &getPartGeometry, &context, &clippingEnabled, &markers, &feature, &usingSegmentizedGeometry, this]( const QgsAbstractGeometry * part, int partIndex = 0 )
1411 {
1412 Q_UNUSED( feature )
1413
1414 if ( !part )
1415 return;
1416
1417 // geometry preprocessing
1418 QgsGeometry temporaryGeometryContainer;
1419 const QgsAbstractGeometry *processedGeometry = nullptr;
1420
1421 const bool isMultiPart = qgsgeometry_cast< const QgsGeometryCollection * >( part ) && qgsgeometry_cast< const QgsGeometryCollection * >( part )->numGeometries() > 1;
1422
1423 if ( !isMultiPart )
1424 {
1425 // segmentize curved geometries
1426 const bool needsSegmentizing = QgsWkbTypes::isCurvedType( part->wkbType() ) || part->hasCurvedSegments();
1427 if ( needsSegmentizing )
1428 {
1429 std::unique_ptr< QgsAbstractGeometry > segmentizedPart( part->segmentize( context.segmentationTolerance(), context.segmentationToleranceType() ) );
1430 if ( !segmentizedPart )
1431 {
1432 return;
1433 }
1434 temporaryGeometryContainer.set( segmentizedPart.release() );
1435 processedGeometry = temporaryGeometryContainer.constGet();
1436 usingSegmentizedGeometry = true;
1437 }
1438 else
1439 {
1440 // no segmentation required
1441 processedGeometry = part;
1442 }
1443
1444 // Simplify the geometry, if needed.
1446 {
1447 const int simplifyHints = context.vectorSimplifyMethod().simplifyHints();
1448 const QgsMapToPixelSimplifier simplifier( simplifyHints, context.vectorSimplifyMethod().tolerance(),
1450
1451 std::unique_ptr< QgsAbstractGeometry > simplified( simplifier.simplify( processedGeometry ) );
1452 if ( simplified )
1453 {
1454 temporaryGeometryContainer.set( simplified.release() );
1455 processedGeometry = temporaryGeometryContainer.constGet();
1456 }
1457 }
1458
1459 // clip geometry to render context clipping regions
1460 if ( !context.featureClipGeometry().isEmpty() )
1461 {
1462 // apply feature clipping from context to the rendered geometry only -- just like the render time simplification,
1463 // we should NEVER apply this to the geometry attached to the feature itself. Doing so causes issues with certain
1464 // renderer settings, e.g. if polygons are being rendered using a rule based renderer based on the feature's area,
1465 // then we need to ensure that the original feature area is used instead of the clipped area..
1466 QgsGeos geos( processedGeometry );
1467 std::unique_ptr< QgsAbstractGeometry > clippedGeom( geos.intersection( context.featureClipGeometry().constGet() ) );
1468 if ( clippedGeom )
1469 {
1470 temporaryGeometryContainer.set( clippedGeom.release() );
1471 processedGeometry = temporaryGeometryContainer.constGet();
1472 }
1473 }
1474 }
1475 else
1476 {
1477 // for multipart geometries, the processing is deferred till we're rendering the actual part...
1478 processedGeometry = part;
1479 }
1480
1481 if ( !processedGeometry )
1482 {
1483 // shouldn't happen!
1484 QgsDebugError( QStringLiteral( "No processed geometry to render for part!" ) );
1485 return;
1486 }
1487
1488 switch ( QgsWkbTypes::flatType( processedGeometry->wkbType() ) )
1489 {
1491 {
1493 {
1494 QgsDebugMsgLevel( QStringLiteral( "point can be drawn only with marker symbol!" ), 2 );
1495 break;
1496 }
1497
1498 PointInfo info;
1499 info.originalGeometry = qgsgeometry_cast< const QgsPoint * >( part );
1500 info.renderPoint = _getPoint( context, *info.originalGeometry );
1501 pointsToRender << info;
1502 break;
1503 }
1504
1506 {
1508 {
1509 QgsDebugMsgLevel( QStringLiteral( "linestring can be drawn only with line symbol!" ), 2 );
1510 break;
1511 }
1512
1513 LineInfo info;
1514 info.originalGeometry = qgsgeometry_cast<const QgsCurve *>( part );
1515 info.renderLine = _getLineString( context, *qgsgeometry_cast<const QgsCurve *>( processedGeometry ), clippingEnabled );
1516 linesToRender << info;
1517 break;
1518 }
1519
1522 {
1523 QPolygonF pts;
1525 {
1526 QgsDebugMsgLevel( QStringLiteral( "polygon can be drawn only with fill symbol!" ), 2 );
1527 break;
1528 }
1529
1530 PolygonInfo info;
1531 info.originalGeometry = qgsgeometry_cast<const QgsCurvePolygon *>( part );
1532 info.originalPartIndex = partIndex;
1533 if ( !qgsgeometry_cast<const QgsPolygon *>( processedGeometry )->exteriorRing() )
1534 {
1535 QgsDebugError( QStringLiteral( "cannot render polygon with no exterior ring" ) );
1536 break;
1537 }
1538
1539 _getPolygon( info.renderExterior, info.renderRings, context, *qgsgeometry_cast<const QgsPolygon *>( processedGeometry ), clippingEnabled, mForceRHR );
1540 polygonsToRender << info;
1541 break;
1542 }
1543
1545 {
1546 const QgsMultiPoint *mp = qgsgeometry_cast< const QgsMultiPoint * >( processedGeometry );
1547 markers.reserve( mp->numGeometries() );
1548 }
1549 [[fallthrough]];
1553 {
1554 const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( processedGeometry );
1555
1556 const unsigned int num = geomCollection->numGeometries();
1557 for ( unsigned int i = 0; i < num; ++i )
1558 {
1559 if ( context.renderingStopped() )
1560 break;
1561
1562 getPartGeometry( geomCollection->geometryN( i ), i );
1563 }
1564 break;
1565 }
1566
1569 {
1571 {
1572 QgsDebugMsgLevel( QStringLiteral( "multi-polygon can be drawn only with fill symbol!" ), 2 );
1573 break;
1574 }
1575
1576 QPolygonF pts;
1577
1578 const QgsGeometryCollection *geomCollection = dynamic_cast<const QgsGeometryCollection *>( processedGeometry );
1579 const unsigned int num = geomCollection->numGeometries();
1580
1581 // Sort components by approximate area (probably a bit faster than using
1582 // area() )
1583 std::map<double, QList<unsigned int> > thisAreaToPartNum;
1584 for ( unsigned int i = 0; i < num; ++i )
1585 {
1586 const QgsRectangle r( geomCollection->geometryN( i )->boundingBox() );
1587 thisAreaToPartNum[ r.width() * r.height()] << i;
1588 }
1589
1590 // Draw starting with larger parts down to smaller parts, so that in
1591 // case of a part being incorrectly inside another part, it is drawn
1592 // on top of it (#15419)
1593 std::map<double, QList<unsigned int> >::const_reverse_iterator iter = thisAreaToPartNum.rbegin();
1594 for ( ; iter != thisAreaToPartNum.rend(); ++iter )
1595 {
1596 const QList<unsigned int> &listPartIndex = iter->second;
1597 for ( int idx = 0; idx < listPartIndex.size(); ++idx )
1598 {
1599 const unsigned i = listPartIndex[idx];
1600 getPartGeometry( geomCollection->geometryN( i ), i );
1601 }
1602 }
1603 break;
1604 }
1605
1606 default:
1607 QgsDebugError( QStringLiteral( "feature %1: unsupported wkb type %2/%3 for rendering" )
1608 .arg( feature.id() )
1609 .arg( QgsWkbTypes::displayString( part->wkbType() ) )
1610 .arg( static_cast< quint32>( part->wkbType() ), 0, 16 ) );
1611 }
1612 };
1613
1614 // Use the simplified type ref when rendering -- this avoids some unnecessary cloning/geometry modification
1615 // (e.g. if the original geometry is a compound curve containing only a linestring curve, we don't have
1616 // to segmentize the geometry before rendering)
1617 getPartGeometry( geom.constGet()->simplifiedTypeRef(), 0 );
1618
1619 // step 2 - determine which layers to render
1620 std::vector< int > layers;
1621 if ( layer == -1 )
1622 {
1623 layers.reserve( mLayers.count() );
1624 for ( int i = 0; i < mLayers.count(); ++i )
1625 layers.emplace_back( i );
1626 }
1627 else
1628 {
1629 layers.emplace_back( layer );
1630 }
1631
1632 // step 3 - render these geometries using the desired symbol layers.
1633
1634 if ( needsExpressionContext )
1635 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_count" ), mLayers.count(), true ) );
1636
1637 for ( const int symbolLayerIndex : layers )
1638 {
1639 QgsSymbolLayer *symbolLayer = mLayers.value( symbolLayerIndex );
1640 if ( !symbolLayer || !symbolLayer->enabled() )
1641 continue;
1642
1643 if ( needsExpressionContext )
1644 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_index" ), symbolLayerIndex + 1, true ) );
1645
1646 symbolLayer->startFeatureRender( feature, context );
1647
1648 switch ( mType )
1649 {
1651 {
1652 int geometryPartNumber = 0;
1653 for ( const PointInfo &point : std::as_const( pointsToRender ) )
1654 {
1655 if ( context.renderingStopped() )
1656 break;
1657
1658 mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1659 if ( needsExpressionContext )
1660 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1661
1662 static_cast<QgsMarkerSymbol *>( this )->renderPoint( point.renderPoint, &feature, context, symbolLayerIndex, selected );
1663 geometryPartNumber++;
1664 }
1665
1666 break;
1667 }
1668
1670 {
1671 if ( linesToRender.empty() )
1672 break;
1673
1674 int geometryPartNumber = 0;
1675 for ( const LineInfo &line : std::as_const( linesToRender ) )
1676 {
1677 if ( context.renderingStopped() )
1678 break;
1679
1680 mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1681 if ( needsExpressionContext )
1682 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1683
1684 context.setGeometry( line.originalGeometry );
1685 static_cast<QgsLineSymbol *>( this )->renderPolyline( line.renderLine, &feature, context, symbolLayerIndex, selected );
1686 geometryPartNumber++;
1687 }
1688 break;
1689 }
1690
1692 {
1693 for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
1694 {
1695 if ( context.renderingStopped() )
1696 break;
1697
1698 mSymbolRenderContext->setGeometryPartNum( info.originalPartIndex + 1 );
1699 if ( needsExpressionContext )
1700 mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, info.originalPartIndex + 1, true ) );
1701
1702 context.setGeometry( info.originalGeometry );
1703 static_cast<QgsFillSymbol *>( this )->renderPolygon( info.renderExterior, ( !info.renderRings.isEmpty() ? &info.renderRings : nullptr ), &feature, context, symbolLayerIndex, selected );
1704 }
1705
1706 break;
1707 }
1708
1710 break;
1711 }
1712
1713 symbolLayer->stopFeatureRender( feature, context );
1714 }
1715
1716 // step 4 - handle post processing steps
1717 switch ( mType )
1718 {
1720 {
1721 markers.reserve( pointsToRender.size() );
1722 for ( const PointInfo &info : std::as_const( pointsToRender ) )
1723 {
1725 {
1726 const QRectF bounds = static_cast<QgsMarkerSymbol *>( this )->bounds( info.renderPoint, context, feature );
1727 if ( context.hasRenderedFeatureHandlers() )
1728 {
1729 renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromRect( bounds )
1730 : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromRect( QgsRectangle( bounds ) ) << renderedBoundsGeom );
1731 }
1733 {
1734 //draw debugging rect
1735 context.painter()->setPen( Qt::red );
1736 context.painter()->setBrush( QColor( 255, 0, 0, 100 ) );
1737 context.painter()->drawRect( bounds );
1738 }
1739 }
1740
1741 if ( drawVertexMarker && !usingSegmentizedGeometry )
1742 {
1743 markers.append( info.renderPoint );
1744 }
1745 }
1746 break;
1747 }
1748
1750 {
1751 for ( const LineInfo &info : std::as_const( linesToRender ) )
1752 {
1753 if ( context.hasRenderedFeatureHandlers() && !info.renderLine.empty() )
1754 {
1755 renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderLine )
1756 : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderLine ) << renderedBoundsGeom );
1757 }
1758
1759 if ( drawVertexMarker && !usingSegmentizedGeometry )
1760 {
1761 markers << info.renderLine;
1762 }
1763 }
1764 break;
1765 }
1766
1768 {
1769 for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
1770 {
1771 if ( context.hasRenderedFeatureHandlers() && !info.renderExterior.empty() )
1772 {
1773 renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderExterior )
1774 : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderExterior ) << renderedBoundsGeom );
1775 // TODO: consider holes?
1776 }
1777
1778 if ( drawVertexMarker && !usingSegmentizedGeometry )
1779 {
1780 markers << info.renderExterior;
1781
1782 for ( const QPolygonF &hole : info.renderRings )
1783 {
1784 markers << hole;
1785 }
1786 }
1787 }
1788 break;
1789 }
1790
1792 break;
1793 }
1794
1795 if ( context.hasRenderedFeatureHandlers() && !renderedBoundsGeom.isNull() )
1796 {
1798 const QList< QgsRenderedFeatureHandlerInterface * > handlers = context.renderedFeatureHandlers();
1799 for ( QgsRenderedFeatureHandlerInterface *handler : handlers )
1800 handler->handleRenderedFeature( feature, renderedBoundsGeom, featureContext );
1801 }
1802
1803 if ( drawVertexMarker )
1804 {
1805 if ( !markers.isEmpty() && !context.renderingStopped() )
1806 {
1807 const auto constMarkers = markers;
1808 for ( QPointF marker : constMarkers )
1809 {
1810 renderVertexMarker( marker, context, currentVertexMarkerType, currentVertexMarkerSize );
1811 }
1812 }
1813 else
1814 {
1816 const QgsMapToPixel &mtp = context.mapToPixel();
1817
1818 QgsPoint vertexPoint;
1819 QgsVertexId vertexId;
1820 double x, y, z;
1821 QPointF mapPoint;
1822 while ( geom.constGet()->nextVertex( vertexId, vertexPoint ) )
1823 {
1824 //transform
1825 x = vertexPoint.x();
1826 y = vertexPoint.y();
1827 z = 0.0;
1828 if ( ct.isValid() )
1829 {
1830 ct.transformInPlace( x, y, z );
1831 }
1832 mapPoint.setX( x );
1833 mapPoint.setY( y );
1834 mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1835 renderVertexMarker( mapPoint, context, currentVertexMarkerType, currentVertexMarkerSize );
1836 }
1837 }
1838 }
1839}
1840
1842{
1843 return mSymbolRenderContext.get();
1844}
1845
1846void QgsSymbol::renderVertexMarker( QPointF pt, QgsRenderContext &context, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize )
1847{
1848 int markerSize = context.convertToPainterUnits( currentVertexMarkerSize, Qgis::RenderUnit::Millimeters );
1849 QgsSymbolLayerUtils::drawVertexMarker( pt.x(), pt.y(), *context.painter(), currentVertexMarkerType, markerSize );
1850}
1851
1852void QgsSymbol::initPropertyDefinitions()
1853{
1854 if ( !sPropertyDefinitions.isEmpty() )
1855 return;
1856
1857 QString origin = QStringLiteral( "symbol" );
1858
1859 sPropertyDefinitions = QgsPropertiesDefinition
1860 {
1861 { static_cast< int >( QgsSymbol::Property::Opacity ), QgsPropertyDefinition( "alpha", QObject::tr( "Opacity" ), QgsPropertyDefinition::Opacity, origin )},
1862 };
1863}
1864
1865void QgsSymbol::startFeatureRender( const QgsFeature &feature, QgsRenderContext &context, const int layer )
1866{
1867 if ( layer != -1 )
1868 {
1870 if ( symbolLayer && symbolLayer->enabled() )
1871 {
1872 symbolLayer->startFeatureRender( feature, context );
1873 }
1874 return;
1875 }
1876 else
1877 {
1878 const QList< QgsSymbolLayer * > layers = mLayers;
1879 for ( QgsSymbolLayer *symbolLayer : layers )
1880 {
1881 if ( !symbolLayer->enabled() )
1882 continue;
1883
1884 symbolLayer->startFeatureRender( feature, context );
1885 }
1886 }
1887}
1888
1889void QgsSymbol::stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context, int layer )
1890{
1891 if ( layer != -1 )
1892 {
1894 if ( symbolLayer && symbolLayer->enabled() )
1895 {
1896 symbolLayer->stopFeatureRender( feature, context );
1897 }
1898 return;
1899 }
1900 else
1901 {
1902 const QList< QgsSymbolLayer * > layers = mLayers;
1903 for ( QgsSymbolLayer *symbolLayer : layers )
1904 {
1905 if ( !symbolLayer->enabled() )
1906 continue;
1907
1908 symbolLayer->stopFeatureRender( feature, context );
1909 }
1910 }
1911}
@ 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:4193
@ 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:1401
SymbolType
Attribute editing capabilities which may be supported by vector data providers.
Definition qgis.h:401
@ 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:43
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.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
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:35
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:98
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:82
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:33
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.
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const final
Prepares the collection against a specified expression context.
bool hasActiveProperties() const final
Returns true if the collection has any active properties, or false if all properties within the colle...
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext(), bool ignoreContext=false) const final
Returns the set of any fields referenced by the active properties from the collection.
Definition for a property.
Definition qgsproperty.h:45
@ Opacity
Opacity (0-100)
Definition qgsproperty.h:60
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(bool initialize=true)
Returns the 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.
virtual void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context)
Called before the layer will be rendered for a particular feature.
@ LayerEnabled
Whether symbol layer is enabled.
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.
Property
Data definable properties.
Definition qgssymbol.h:133
QgsSymbolRenderContext * symbolRenderContext()
Returns the symbol render context.
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
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:620
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:81
void stopRender(QgsRenderContext &context)
Ends the rendering process.
qreal mOpacity
Symbol opacity (in the range 0 - 1)
Definition qgssymbol.h:785
Q_DECL_DEPRECATED const QgsVectorLayer * mLayer
Definition qgssymbol.h:801
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:715
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:530
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:495
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:550
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:781
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:787
bool mForceRHR
Definition qgssymbol.h:797
QgsSymbolLayerList mLayers
Definition qgssymbol.h:782
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:799
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:156
QgsSymbol(Qgis::SymbolType type, const QgsSymbolLayerList &layers)
Constructor for a QgsSymbol of the specified type.
Definition qgssymbol.cpp:60
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:36
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:5713
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:5061
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:5712
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5144
#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:30