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