QGIS API Documentation  3.25.0-Master (dec16ba68b)
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  const unsigned int nPoints = curve.numPoints();
87 
89  const QgsMapToPixel &mtp = context.mapToPixel();
90  QPolygonF pts;
91 
92  //apply clipping for large lines to achieve a better rendering performance
93  if ( clipToExtent && nPoints > 1 && !( context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection ) )
94  {
95  const QgsRectangle e = context.extent();
96  const double cw = e.width() / 10;
97  const double ch = e.height() / 10;
98  const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
99  pts = QgsClipper::clippedLine( curve, clipRect );
100  }
101  else
102  {
103  pts = curve.asQPolygonF();
104  }
105 
106  //transform the QPolygonF to screen coordinates
107  if ( ct.isValid() )
108  {
109  try
110  {
111  ct.transformPolygon( pts );
112  }
113  catch ( QgsCsException & )
114  {
115  // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
116  }
117  }
118 
119  // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
120  pts.erase( std::remove_if( pts.begin(), pts.end(),
121  []( const QPointF point )
122  {
123  return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
124  } ), pts.end() );
125 
126  if ( clipToExtent && nPoints > 1 && context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection )
127  {
128  // early clipping was not possible, so we have to apply it here after transformation
129  const QgsRectangle e = context.mapExtent();
130  const double cw = e.width() / 10;
131  const double ch = e.height() / 10;
132  const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
133  pts = QgsClipper::clippedLine( pts, clipRect );
134  }
135 
136  QPointF *ptr = pts.data();
137  for ( int i = 0; i < pts.size(); ++i, ++ptr )
138  {
139  mtp.transformInPlace( ptr->rx(), ptr->ry() );
140  }
141 
142  return pts;
143 }
144 
145 QPolygonF QgsSymbol::_getPolygonRing( QgsRenderContext &context, const QgsCurve &curve, const bool clipToExtent, const bool isExteriorRing, const bool correctRingOrientation )
146 {
147  const QgsCoordinateTransform ct = context.coordinateTransform();
148  const QgsMapToPixel &mtp = context.mapToPixel();
149 
150  QPolygonF poly = curve.asQPolygonF();
151 
152  if ( curve.numPoints() < 1 )
153  return QPolygonF();
154 
155  if ( correctRingOrientation )
156  {
157  // ensure consistent polygon ring orientation
158  if ( isExteriorRing && curve.orientation() != Qgis::AngularDirection::Clockwise )
159  std::reverse( poly.begin(), poly.end() );
160  else if ( !isExteriorRing && curve.orientation() != Qgis::AngularDirection::CounterClockwise )
161  std::reverse( poly.begin(), poly.end() );
162  }
163 
164  //clip close to view extent, if needed
165  if ( clipToExtent && !( context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection ) && !context.extent().contains( poly.boundingRect() ) )
166  {
167  const QgsRectangle e = context.extent();
168  const double cw = e.width() / 10;
169  const double ch = e.height() / 10;
170  const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
171  QgsClipper::trimPolygon( poly, clipRect );
172  }
173 
174  //transform the QPolygonF to screen coordinates
175  if ( ct.isValid() )
176  {
177  try
178  {
179  ct.transformPolygon( poly );
180  }
181  catch ( QgsCsException & )
182  {
183  // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
184  }
185  }
186 
187  // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
188  poly.erase( std::remove_if( poly.begin(), poly.end(),
189  []( const QPointF point )
190  {
191  return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
192  } ), poly.end() );
193 
194  if ( clipToExtent && context.flags() & Qgis::RenderContextFlag::ApplyClipAfterReprojection && !context.mapExtent().contains( poly.boundingRect() ) )
195  {
196  // early clipping was not possible, so we have to apply it here after transformation
197  const QgsRectangle e = context.mapExtent();
198  const double cw = e.width() / 10;
199  const double ch = e.height() / 10;
200  const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
201  QgsClipper::trimPolygon( poly, clipRect );
202  }
203 
204  QPointF *ptr = poly.data();
205  for ( int i = 0; i < poly.size(); ++i, ++ptr )
206  {
207  mtp.transformInPlace( ptr->rx(), ptr->ry() );
208  }
209 
210  if ( !poly.empty() && !poly.isClosed() )
211  poly << poly.at( 0 );
212 
213  return poly;
214 }
215 
216 void QgsSymbol::_getPolygon( QPolygonF &pts, QVector<QPolygonF> &holes, QgsRenderContext &context, const QgsPolygon &polygon, const bool clipToExtent, const bool correctRingOrientation )
217 {
218  holes.clear();
219 
220  pts = _getPolygonRing( context, *polygon.exteriorRing(), clipToExtent, true, correctRingOrientation );
221  const int ringCount = polygon.numInteriorRings();
222  holes.reserve( ringCount );
223  for ( int idx = 0; idx < ringCount; idx++ )
224  {
225  const QPolygonF hole = _getPolygonRing( context, *( polygon.interiorRing( idx ) ), clipToExtent, false, correctRingOrientation );
226  if ( !hole.isEmpty() )
227  holes.append( hole );
228  }
229 }
230 
232 {
233  switch ( type )
234  {
236  return QObject::tr( "Marker" );
238  return QObject::tr( "Line" );
240  return QObject::tr( "Fill" );
242  return QObject::tr( "Hybrid" );
243  }
244  return QString();
245 }
246 
248 {
249  switch ( type )
250  {
254  return Qgis::SymbolType::Line;
256  return Qgis::SymbolType::Fill;
260  }
262 }
263 
265 {
266  QgsSymbol::initPropertyDefinitions();
267  return sPropertyDefinitions;
268 }
269 
271 {
272  // delete all symbol layers (we own them, so it's okay)
273  qDeleteAll( mLayers );
274 }
275 
277 {
278  if ( mLayers.empty() )
279  {
281  }
282 
283  QgsSymbolLayerList::const_iterator it = mLayers.constBegin();
284 
285  QgsUnitTypes::RenderUnit unit = ( *it )->outputUnit();
286 
287  for ( ; it != mLayers.constEnd(); ++it )
288  {
289  if ( ( *it )->outputUnit() != unit )
290  {
292  }
293  }
294  return unit;
295 }
296 
298 {
299  if ( mLayers.empty() )
300  {
301  return false;
302  }
303 
304  for ( const QgsSymbolLayer *layer : mLayers )
305  {
306  if ( layer->usesMapUnits() )
307  {
308  return true;
309  }
310  }
311  return false;
312 }
313 
315 {
316  if ( mLayers.empty() )
317  {
318  return QgsMapUnitScale();
319  }
320 
321  QgsSymbolLayerList::const_iterator it = mLayers.constBegin();
322  if ( it == mLayers.constEnd() )
323  return QgsMapUnitScale();
324 
325  QgsMapUnitScale scale = ( *it )->mapUnitScale();
326  ++it;
327 
328  for ( ; it != mLayers.constEnd(); ++it )
329  {
330  if ( ( *it )->mapUnitScale() != scale )
331  {
332  return QgsMapUnitScale();
333  }
334  }
335  return scale;
336 }
337 
339 {
340  const auto constMLayers = mLayers;
341  for ( QgsSymbolLayer *layer : constMLayers )
342  {
343  layer->setOutputUnit( u );
344  }
345 }
346 
347 void QgsSymbol::setMapUnitScale( const QgsMapUnitScale &scale ) const
348 {
349  const auto constMLayers = mLayers;
350  for ( QgsSymbolLayer *layer : constMLayers )
351  {
352  layer->setMapUnitScale( scale );
353  }
354 }
355 
357 {
358  return mAnimationSettings;
359 }
360 
362 {
363  return mAnimationSettings;
364 }
365 
367 {
368  mAnimationSettings = settings;
369 }
370 
372 {
373  std::unique_ptr< QgsSymbol > s;
374 
375  // override global default if project has a default for this type
376  switch ( geomType )
377  {
379  s.reset( QgsProject::instance()->styleSettings()->defaultSymbol( Qgis::SymbolType::Marker ) );
380  break;
382  s.reset( QgsProject::instance()->styleSettings()->defaultSymbol( Qgis::SymbolType::Line ) );
383  break;
385  s.reset( QgsProject::instance()->styleSettings()->defaultSymbol( Qgis::SymbolType::Fill ) );
386  break;
387  default:
388  break;
389  }
390 
391  // if no default found for this type, get global default (as previously)
392  if ( !s )
393  {
394  switch ( geomType )
395  {
397  s = std::make_unique< QgsMarkerSymbol >();
398  break;
400  s = std::make_unique< QgsLineSymbol >();
401  break;
403  s = std::make_unique< QgsFillSymbol >();
404  break;
405  default:
406  QgsDebugMsg( QStringLiteral( "unknown layer's geometry type" ) );
407  return nullptr;
408  }
409  }
410 
411  // set opacity
412  s->setOpacity( QgsProject::instance()->styleSettings()->defaultSymbolOpacity() );
413 
414  // set random color, it project prefs allow
415  if ( QgsProject::instance()->styleSettings()->randomizeDefaultSymbolColor() )
416  {
417  s->setColor( QgsApplication::colorSchemeRegistry()->fetchRandomStyleColor() );
418  }
419 
420  return s.release();
421 }
422 
424 {
425  return mLayers.value( layer );
426 }
427 
428 const QgsSymbolLayer *QgsSymbol::symbolLayer( int layer ) const
429 {
430  return mLayers.value( layer );
431 }
432 
434 {
435  if ( index < 0 || index > mLayers.count() ) // can be added also after the last index
436  return false;
437 
438  if ( !layer || !layer->isCompatibleWithSymbol( this ) )
439  return false;
440 
441  mLayers.insert( index, layer );
442  return true;
443 }
444 
445 
447 {
448  if ( !layer || !layer->isCompatibleWithSymbol( this ) )
449  return false;
450 
451  mLayers.append( layer );
452  return true;
453 }
454 
455 
457 {
458  if ( index < 0 || index >= mLayers.count() )
459  return false;
460 
461  delete mLayers.at( index );
462  mLayers.removeAt( index );
463  return true;
464 }
465 
466 
468 {
469  if ( index < 0 || index >= mLayers.count() )
470  return nullptr;
471 
472  return mLayers.takeAt( index );
473 }
474 
475 
477 {
478  QgsSymbolLayer *oldLayer = mLayers.value( index );
479 
480  if ( oldLayer == layer )
481  return false;
482 
483  if ( !layer || !layer->isCompatibleWithSymbol( this ) )
484  return false;
485 
486  delete oldLayer; // first delete the original layer
487  mLayers[index] = layer; // set new layer
488  return true;
489 }
490 
491 
492 void QgsSymbol::startRender( QgsRenderContext &context, const QgsFields &fields )
493 {
494  Q_ASSERT_X( !mStarted, "startRender", "Rendering has already been started for this symbol instance!" );
495  mStarted = true;
496 
497  mSymbolRenderContext.reset( new QgsSymbolRenderContext( context, QgsUnitTypes::RenderUnknownUnit, mOpacity, false, mRenderHints, nullptr, fields ) );
498 
499  // Why do we need a copy here ? Is it to make sure the symbol layer rendering does not mess with the symbol render context ?
500  // Or is there another profound reason ?
501  QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, mOpacity, false, mRenderHints, nullptr, fields );
502 
503  std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::updateSymbolScope( this, new QgsExpressionContextScope() ) );
504 
506  {
507  const long long mapFrameNumber = context.currentFrame();
508  double animationTimeSeconds = 0;
509  if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
510  {
511  // render is part of an animation, so we base the calculated frame on that
512  animationTimeSeconds = mapFrameNumber / context.frameRate();
513  }
514  else
515  {
516  // render is outside of animation, so base the calculated frame on the current epoch
517  animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
518  }
519 
520  const long long symbolFrame = static_cast< long long >( std::floor( animationTimeSeconds * mAnimationSettings.frameRate() ) );
521  scope->setVariable( QStringLiteral( "symbol_frame" ), symbolFrame, true );
522  }
523 
524  mSymbolRenderContext->setExpressionContextScope( scope.release() );
525 
526  mDataDefinedProperties.prepare( context.expressionContext() );
527 
528  const auto constMLayers = mLayers;
529  for ( QgsSymbolLayer *layer : constMLayers )
530  {
531  if ( !layer->enabled() || !context.isSymbolLayerEnabled( layer ) )
532  continue;
533 
534  layer->prepareExpressions( symbolContext );
535  layer->prepareMasks( symbolContext );
536  layer->startRender( symbolContext );
537  }
538 }
539 
541 {
542  Q_ASSERT_X( mStarted, "startRender", "startRender was not called for this symbol instance!" );
543  mStarted = false;
544 
545  Q_UNUSED( context )
546  if ( mSymbolRenderContext )
547  {
548  const auto constMLayers = mLayers;
549  for ( QgsSymbolLayer *layer : constMLayers )
550  {
551  if ( !layer->enabled() || !context.isSymbolLayerEnabled( layer ) )
552  continue;
553 
554  layer->stopRender( *mSymbolRenderContext );
555  }
556  }
557 
558  mSymbolRenderContext.reset( nullptr );
559 
561  mLayer = nullptr;
563 }
564 
565 void QgsSymbol::setColor( const QColor &color ) const
566 {
567  const auto constMLayers = mLayers;
568  for ( QgsSymbolLayer *layer : constMLayers )
569  {
570  if ( !layer->isLocked() )
571  layer->setColor( color );
572  }
573 }
574 
575 QColor QgsSymbol::color() const
576 {
577  for ( const QgsSymbolLayer *layer : mLayers )
578  {
579  // return color of the first unlocked layer
580  if ( !layer->isLocked() )
581  {
582  const QColor layerColor = layer->color();
583  if ( layerColor.isValid() )
584  return layerColor;
585  }
586  }
587  return QColor( 0, 0, 0 );
588 }
589 
590 void QgsSymbol::drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext, bool selected, const QgsExpressionContext *expressionContext, const QgsLegendPatchShape *patchShape )
591 {
592  QgsRenderContext *context = customContext;
593  std::unique_ptr< QgsRenderContext > tempContext;
594  if ( !context )
595  {
596  tempContext.reset( new QgsRenderContext( QgsRenderContext::fromQPainter( painter ) ) );
597  context = tempContext.get();
599  }
600 
601  const bool prevForceVector = context->forceVectorOutput();
602  context->setForceVectorOutput( true );
603 
604  const double opacity = expressionContext ? dataDefinedProperties().valueAsDouble( QgsSymbol::PropertyOpacity, *expressionContext, mOpacity ) : mOpacity;
605 
606  QgsSymbolRenderContext symbolContext( *context, QgsUnitTypes::RenderUnknownUnit, opacity, false, mRenderHints, nullptr );
607  symbolContext.setSelected( selected );
608  switch ( mType )
609  {
612  break;
615  break;
618  break;
621  break;
622  }
623 
624  if ( patchShape )
625  symbolContext.setPatchShape( *patchShape );
626 
627  if ( !customContext && expressionContext )
628  {
629  context->setExpressionContext( *expressionContext );
630  }
631  else if ( !customContext )
632  {
633  // if no render context was passed, build a minimal expression context
634  QgsExpressionContext expContext;
636  context->setExpressionContext( expContext );
637  }
638 
639  for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
640  {
641  if ( !layer->enabled() || ( customContext && !customContext->isSymbolLayerEnabled( layer ) ) )
642  continue;
643 
645  {
646  // line symbol layer would normally draw just a line
647  // so we override this case to force it to draw a polygon stroke
648  QgsLineSymbolLayer *lsl = dynamic_cast<QgsLineSymbolLayer *>( layer );
649  if ( lsl )
650  {
651  // from QgsFillSymbolLayer::drawPreviewIcon() -- would be nicer to add the
652  // symbol type to QgsSymbolLayer::drawPreviewIcon so this logic could be avoided!
653 
654  // hmm... why was this using size -1 ??
655  const QSizeF targetSize = QSizeF( size.width() - 1, size.height() - 1 );
656 
657  const QList< QList< QPolygonF > > polys = patchShape ? patchShape->toQPolygonF( Qgis::SymbolType::Fill, targetSize )
659 
660  lsl->startRender( symbolContext );
661  QgsPaintEffect *effect = lsl->paintEffect();
662 
663  std::unique_ptr< QgsEffectPainter > effectPainter;
664  if ( effect && effect->enabled() )
665  effectPainter = std::make_unique< QgsEffectPainter >( symbolContext.renderContext(), effect );
666 
667  for ( const QList< QPolygonF > &poly : polys )
668  {
669  QVector< QPolygonF > rings;
670  rings.reserve( poly.size() );
671  for ( int i = 1; i < poly.size(); ++i )
672  rings << poly.at( i );
673  lsl->renderPolygonStroke( poly.value( 0 ), &rings, symbolContext );
674  }
675 
676  effectPainter.reset();
677  lsl->stopRender( symbolContext );
678  }
679  }
680  else
681  layer->drawPreviewIcon( symbolContext, size );
682  }
683 
684  context->setForceVectorOutput( prevForceVector );
685 }
686 
687 void QgsSymbol::exportImage( const QString &path, const QString &format, QSize size )
688 {
689  if ( format.compare( QLatin1String( "svg" ), Qt::CaseInsensitive ) == 0 )
690  {
691  QSvgGenerator generator;
692  generator.setFileName( path );
693  generator.setSize( size );
694  generator.setViewBox( QRect( 0, 0, size.height(), size.height() ) );
695 
696  QPainter painter( &generator );
697  drawPreviewIcon( &painter, size );
698  painter.end();
699  }
700  else
701  {
702  QImage image = asImage( size );
703  image.save( path );
704  }
705 }
706 
707 QImage QgsSymbol::asImage( QSize size, QgsRenderContext *customContext )
708 {
709  QImage image( size, QImage::Format_ARGB32_Premultiplied );
710  image.fill( 0 );
711 
712  QPainter p( &image );
713  p.setRenderHint( QPainter::Antialiasing );
714  p.setRenderHint( QPainter::SmoothPixmapTransform );
715 
716  drawPreviewIcon( &p, size, customContext );
717 
718  return image;
719 }
720 
721 
722 QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, Qgis::SymbolPreviewFlags flags )
723 {
724  QImage preview( QSize( 100, 100 ), QImage::Format_ARGB32_Premultiplied );
725  preview.fill( 0 );
726 
727  QPainter p( &preview );
728  p.setRenderHint( QPainter::Antialiasing );
729  p.translate( 0.5, 0.5 ); // shift by half a pixel to avoid blurring due antialiasing
730 
732  {
733  p.setPen( QPen( Qt::gray ) );
734  p.drawLine( 0, 50, 100, 50 );
735  p.drawLine( 50, 0, 50, 100 );
736  }
737 
742  context.setPainterFlagsUsingContext( &p );
743  if ( expressionContext )
744  context.setExpressionContext( *expressionContext );
745 
746  context.setIsGuiPreview( true );
747  startRender( context );
748 
749  if ( mType == Qgis::SymbolType::Line )
750  {
751  QPolygonF poly;
752  poly << QPointF( 0, 50 ) << QPointF( 99, 50 );
753  static_cast<QgsLineSymbol *>( this )->renderPolyline( poly, nullptr, context );
754  }
755  else if ( mType == Qgis::SymbolType::Fill )
756  {
757  QPolygonF polygon;
758  polygon << QPointF( 20, 20 ) << QPointF( 80, 20 ) << QPointF( 80, 80 ) << QPointF( 20, 80 ) << QPointF( 20, 20 );
759  static_cast<QgsFillSymbol *>( this )->renderPolygon( polygon, nullptr, nullptr, context );
760  }
761  else // marker
762  {
763  static_cast<QgsMarkerSymbol *>( this )->renderPoint( QPointF( 50, 50 ), nullptr, context );
764  }
765 
766  stopRender( context );
767  return preview;
768 }
769 
770 QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, int flags )
771 {
772  return bigSymbolPreviewImage( expressionContext, static_cast< Qgis::SymbolPreviewFlags >( flags ) );
773 }
774 
775 QString QgsSymbol::dump() const
776 {
777  QString t;
778  switch ( type() )
779  {
781  t = QStringLiteral( "MARKER" );
782  break;
784  t = QStringLiteral( "LINE" );
785  break;
787  t = QStringLiteral( "FILL" );
788  break;
789  default:
790  Q_ASSERT( false && "unknown symbol type" );
791  }
792  QString s = QStringLiteral( "%1 SYMBOL (%2 layers) color %3" ).arg( t ).arg( mLayers.count() ).arg( QgsSymbolLayerUtils::encodeColor( color() ) );
793 
794  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
795  {
796  // TODO:
797  }
798  return s;
799 }
800 
801 void QgsSymbol::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
802 {
803  props[ QStringLiteral( "alpha" )] = QString::number( opacity() );
804  double scaleFactor = 1.0;
805  props[ QStringLiteral( "uom" )] = QgsSymbolLayerUtils::encodeSldUom( outputUnit(), &scaleFactor );
806  props[ QStringLiteral( "uomScale" )] = ( !qgsDoubleNear( scaleFactor, 1.0 ) ? qgsDoubleToString( scaleFactor ) : QString() );
807 
808  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
809  {
810  ( *it )->toSld( doc, element, props );
811  }
812 }
813 
815 {
816  QgsSymbolLayerList lst;
817  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
818  {
819  QgsSymbolLayer *layer = ( *it )->clone();
820  layer->setLocked( ( *it )->isLocked() );
821  layer->setRenderingPass( ( *it )->renderingPass() );
822  layer->setEnabled( ( *it )->enabled() );
823  lst.append( layer );
824  }
825  return lst;
826 }
827 
828 void QgsSymbol::renderUsingLayer( QgsSymbolLayer *layer, QgsSymbolRenderContext &context, QgsWkbTypes::GeometryType geometryType, const QPolygonF *points, const QVector<QPolygonF> *rings )
829 {
830  Q_ASSERT( layer->type() == Qgis::SymbolType::Hybrid );
831 
832  if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
833  return;
834 
835  QgsGeometryGeneratorSymbolLayer *generatorLayer = static_cast<QgsGeometryGeneratorSymbolLayer *>( layer );
836 
837  QgsPaintEffect *effect = generatorLayer->paintEffect();
838  if ( effect && effect->enabled() )
839  {
840  QgsEffectPainter p( context.renderContext(), effect );
841  generatorLayer->render( context, geometryType, points, rings );
842  }
843  else
844  {
845  generatorLayer->render( context, geometryType, points, rings );
846  }
847 }
848 
849 QSet<QString> QgsSymbol::usedAttributes( const QgsRenderContext &context ) const
850 {
851  // calling referencedFields() with ignoreContext=true because in our expression context
852  // we do not have valid QgsFields yet - because of that the field names from expressions
853  // wouldn't get reported
854  QSet<QString> attributes = mDataDefinedProperties.referencedFields( context.expressionContext(), true );
855  QgsSymbolLayerList::const_iterator sIt = mLayers.constBegin();
856  for ( ; sIt != mLayers.constEnd(); ++sIt )
857  {
858  if ( *sIt )
859  {
860  attributes.unite( ( *sIt )->usedAttributes( context ) );
861  }
862  }
863  return attributes;
864 }
865 
867 {
868  mDataDefinedProperties.setProperty( key, property );
869 }
870 
872 {
873  if ( mDataDefinedProperties.hasActiveProperties() )
874  return true;
875 
876  for ( QgsSymbolLayer *layer : mLayers )
877  {
878  if ( layer->hasDataDefinedProperties() )
879  return true;
880  }
881  return false;
882 }
883 
885 {
886  for ( QgsSymbolLayer *layer : mLayers )
887  {
888  if ( layer->canCauseArtifactsBetweenAdjacentTiles() )
889  return true;
890  }
891  return false;
892 }
893 
895 {
897  mLayer = layer;
899 }
900 
902 {
904  return mLayer;
906 }
907 
909 
913 class ExpressionContextScopePopper
914 {
915  public:
916 
917  ExpressionContextScopePopper() = default;
918 
919  ~ExpressionContextScopePopper()
920  {
921  if ( context )
922  context->popScope();
923  }
924 
925  QgsExpressionContext *context = nullptr;
926 };
927 
931 class GeometryRestorer
932 {
933  public:
934  GeometryRestorer( QgsRenderContext &context )
935  : mContext( context ),
936  mGeometry( context.geometry() )
937  {}
938 
939  ~GeometryRestorer()
940  {
941  mContext.setGeometry( mGeometry );
942  }
943 
944  private:
945  QgsRenderContext &mContext;
946  const QgsAbstractGeometry *mGeometry;
947 };
949 
950 void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize )
951 {
952  if ( context.renderingStopped() )
953  return;
954 
955  const QgsGeometry geom = feature.geometry();
956  if ( geom.isNull() )
957  {
958  return;
959  }
960 
961  GeometryRestorer geomRestorer( context );
962 
963  bool usingSegmentizedGeometry = false;
964  context.setGeometry( geom.constGet() );
965 
966  if ( geom.type() != QgsWkbTypes::PointGeometry && !geom.boundingBox().isNull() )
967  {
968  try
969  {
970  const QPointF boundsOrigin = _getPoint( context, QgsPoint( geom.boundingBox().xMinimum(), geom.boundingBox().yMinimum() ) );
971  if ( std::isfinite( boundsOrigin.x() ) && std::isfinite( boundsOrigin.y() ) )
972  context.setTextureOrigin( boundsOrigin );
973  }
974  catch ( QgsCsException & )
975  {
976 
977  }
978  }
979 
980  bool clippingEnabled = clipFeaturesToExtent();
981  // do any symbol layers prevent feature clipping?
982  for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
983  {
985  {
986  clippingEnabled = false;
987  break;
988  }
989  }
990  if ( clippingEnabled && context.testFlag( Qgis::RenderContextFlag::RenderMapTile ) )
991  {
992  // If the "avoid artifacts between adjacent tiles" flag is set (RenderMapTile), then we'll force disable
993  // the geometry clipping IF (and only if) this symbol can potentially have rendering artifacts when rendered as map tiles.
994  // If the symbol won't have any artifacts anyway, then it's pointless and incredibly expensive to skip the clipping!
996  {
997  clippingEnabled = false;
998  }
999  }
1000  if ( context.extent().isEmpty() )
1001  clippingEnabled = false;
1002 
1003  mSymbolRenderContext->setGeometryPartCount( geom.constGet()->partCount() );
1004  mSymbolRenderContext->setGeometryPartNum( 1 );
1005 
1006  const bool needsExpressionContext = hasDataDefinedProperties();
1007  ExpressionContextScopePopper scopePopper;
1008  if ( mSymbolRenderContext->expressionContextScope() )
1009  {
1010  if ( needsExpressionContext )
1011  {
1012  // this is somewhat nasty - by appending this scope here it's now owned
1013  // by both mSymbolRenderContext AND context.expressionContext()
1014  // the RAII scopePopper is required to make sure it always has ownership transferred back
1015  // from context.expressionContext(), even if exceptions of other early exits occur in this
1016  // function
1017  context.expressionContext().appendScope( mSymbolRenderContext->expressionContextScope() );
1018  scopePopper.context = &context.expressionContext();
1019 
1020  QgsExpressionContextUtils::updateSymbolScope( this, mSymbolRenderContext->expressionContextScope() );
1021  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_COUNT, mSymbolRenderContext->geometryPartCount(), true ) );
1022  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, 1, true ) );
1023  }
1024  }
1025 
1026  // Collection of markers to paint, only used for no curve types.
1027  QPolygonF markers;
1028 
1029  QgsGeometry renderedBoundsGeom;
1030 
1031  // Step 1 - collect the set of painter coordinate geometries to render.
1032  // We do this upfront, because we only want to ever do this once, regardless how many symbol layers we need to render.
1033 
1034  struct PointInfo
1035  {
1036  QPointF renderPoint;
1037  const QgsPoint *originalGeometry = nullptr;
1038  };
1039  QVector< PointInfo > pointsToRender;
1040 
1041  struct LineInfo
1042  {
1043  QPolygonF renderLine;
1044  const QgsCurve *originalGeometry = nullptr;
1045  };
1046  QVector< LineInfo > linesToRender;
1047 
1048  struct PolygonInfo
1049  {
1050  QPolygonF renderExterior;
1051  QVector< QPolygonF > renderRings;
1052  const QgsCurvePolygon *originalGeometry = nullptr;
1053  int originalPartIndex = 0;
1054  };
1055  QVector< PolygonInfo > polygonsToRender;
1056 
1057  std::function< void ( const QgsAbstractGeometry *, int partIndex )> getPartGeometry;
1058  getPartGeometry = [&pointsToRender, &linesToRender, &polygonsToRender, &getPartGeometry, &context, &clippingEnabled, &markers, &feature, &usingSegmentizedGeometry, this]( const QgsAbstractGeometry * part, int partIndex = 0 )
1059  {
1060  Q_UNUSED( feature )
1061 
1062  if ( !part )
1063  return;
1064 
1065  // geometry preprocessing
1066  QgsGeometry temporaryGeometryContainer;
1067  const QgsAbstractGeometry *processedGeometry = nullptr;
1068 
1069  const bool isMultiPart = qgsgeometry_cast< const QgsGeometryCollection * >( part ) && qgsgeometry_cast< const QgsGeometryCollection * >( part )->numGeometries() > 1;
1070 
1071  if ( !isMultiPart )
1072  {
1073  // segmentize curved geometries
1074  const bool needsSegmentizing = QgsWkbTypes::isCurvedType( part->wkbType() ) || part->hasCurvedSegments();
1075  if ( needsSegmentizing )
1076  {
1077  std::unique_ptr< QgsAbstractGeometry > segmentizedPart( part->segmentize( context.segmentationTolerance(), context.segmentationToleranceType() ) );
1078  if ( !segmentizedPart )
1079  {
1080  return;
1081  }
1082  temporaryGeometryContainer.set( segmentizedPart.release() );
1083  processedGeometry = temporaryGeometryContainer.constGet();
1084  usingSegmentizedGeometry = true;
1085  }
1086  else
1087  {
1088  // no segmentation required
1089  processedGeometry = part;
1090  }
1091 
1092  // Simplify the geometry, if needed.
1093  if ( context.vectorSimplifyMethod().forceLocalOptimization() )
1094  {
1095  const int simplifyHints = context.vectorSimplifyMethod().simplifyHints();
1096  const QgsMapToPixelSimplifier simplifier( simplifyHints, context.vectorSimplifyMethod().tolerance(),
1098 
1099  std::unique_ptr< QgsAbstractGeometry > simplified( simplifier.simplify( processedGeometry ) );
1100  if ( simplified )
1101  {
1102  temporaryGeometryContainer.set( simplified.release() );
1103  processedGeometry = temporaryGeometryContainer.constGet();
1104  }
1105  }
1106 
1107  // clip geometry to render context clipping regions
1108  if ( !context.featureClipGeometry().isEmpty() )
1109  {
1110  // apply feature clipping from context to the rendered geometry only -- just like the render time simplification,
1111  // we should NEVER apply this to the geometry attached to the feature itself. Doing so causes issues with certain
1112  // renderer settings, e.g. if polygons are being rendered using a rule based renderer based on the feature's area,
1113  // then we need to ensure that the original feature area is used instead of the clipped area..
1114  QgsGeos geos( processedGeometry );
1115  std::unique_ptr< QgsAbstractGeometry > clippedGeom( geos.intersection( context.featureClipGeometry().constGet() ) );
1116  if ( clippedGeom )
1117  {
1118  temporaryGeometryContainer.set( clippedGeom.release() );
1119  processedGeometry = temporaryGeometryContainer.constGet();
1120  }
1121  }
1122  }
1123  else
1124  {
1125  // for multipart geometries, the processing is deferred till we're rendering the actual part...
1126  processedGeometry = part;
1127  }
1128 
1129  if ( !processedGeometry )
1130  {
1131  // shouldn't happen!
1132  QgsDebugMsg( QStringLiteral( "No processed geometry to render for part!" ) );
1133  return;
1134  }
1135 
1136  switch ( QgsWkbTypes::flatType( processedGeometry->wkbType() ) )
1137  {
1138  case QgsWkbTypes::Point:
1139  {
1141  {
1142  QgsDebugMsgLevel( QStringLiteral( "point can be drawn only with marker symbol!" ), 2 );
1143  break;
1144  }
1145 
1146  PointInfo info;
1147  info.originalGeometry = qgsgeometry_cast< const QgsPoint * >( part );
1148  info.renderPoint = _getPoint( context, *info.originalGeometry );
1149  pointsToRender << info;
1150  break;
1151  }
1152 
1154  {
1155  if ( mType != Qgis::SymbolType::Line )
1156  {
1157  QgsDebugMsgLevel( QStringLiteral( "linestring can be drawn only with line symbol!" ), 2 );
1158  break;
1159  }
1160 
1161  LineInfo info;
1162  info.originalGeometry = qgsgeometry_cast<const QgsCurve *>( part );
1163  info.renderLine = _getLineString( context, *qgsgeometry_cast<const QgsCurve *>( processedGeometry ), clippingEnabled );
1164  linesToRender << info;
1165  break;
1166  }
1167 
1168  case QgsWkbTypes::Polygon:
1169  case QgsWkbTypes::Triangle:
1170  {
1171  QPolygonF pts;
1172  if ( mType != Qgis::SymbolType::Fill )
1173  {
1174  QgsDebugMsgLevel( QStringLiteral( "polygon can be drawn only with fill symbol!" ), 2 );
1175  break;
1176  }
1177 
1178  PolygonInfo info;
1179  info.originalGeometry = qgsgeometry_cast<const QgsCurvePolygon *>( part );
1180  info.originalPartIndex = partIndex;
1181  if ( !qgsgeometry_cast<const QgsPolygon *>( processedGeometry )->exteriorRing() )
1182  {
1183  QgsDebugMsg( QStringLiteral( "cannot render polygon with no exterior ring" ) );
1184  break;
1185  }
1186 
1187  _getPolygon( info.renderExterior, info.renderRings, context, *qgsgeometry_cast<const QgsPolygon *>( processedGeometry ), clippingEnabled, mForceRHR );
1188  polygonsToRender << info;
1189  break;
1190  }
1191 
1193  {
1194  const QgsMultiPoint *mp = qgsgeometry_cast< const QgsMultiPoint * >( processedGeometry );
1195  markers.reserve( mp->numGeometries() );
1196  }
1197  FALLTHROUGH
1201  {
1202  const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( processedGeometry );
1203 
1204  const unsigned int num = geomCollection->numGeometries();
1205  for ( unsigned int i = 0; i < num; ++i )
1206  {
1207  if ( context.renderingStopped() )
1208  break;
1209 
1210  getPartGeometry( geomCollection->geometryN( i ), i );
1211  }
1212  break;
1213  }
1214 
1217  {
1218  if ( mType != Qgis::SymbolType::Fill )
1219  {
1220  QgsDebugMsgLevel( QStringLiteral( "multi-polygon can be drawn only with fill symbol!" ), 2 );
1221  break;
1222  }
1223 
1224  QPolygonF pts;
1225 
1226  const QgsGeometryCollection *geomCollection = dynamic_cast<const QgsGeometryCollection *>( processedGeometry );
1227  const unsigned int num = geomCollection->numGeometries();
1228 
1229  // Sort components by approximate area (probably a bit faster than using
1230  // area() )
1231  std::map<double, QList<unsigned int> > thisAreaToPartNum;
1232  for ( unsigned int i = 0; i < num; ++i )
1233  {
1234  const QgsRectangle r( geomCollection->geometryN( i )->boundingBox() );
1235  thisAreaToPartNum[ r.width() * r.height()] << i;
1236  }
1237 
1238  // Draw starting with larger parts down to smaller parts, so that in
1239  // case of a part being incorrectly inside another part, it is drawn
1240  // on top of it (#15419)
1241  std::map<double, QList<unsigned int> >::const_reverse_iterator iter = thisAreaToPartNum.rbegin();
1242  for ( ; iter != thisAreaToPartNum.rend(); ++iter )
1243  {
1244  const QList<unsigned int> &listPartIndex = iter->second;
1245  for ( int idx = 0; idx < listPartIndex.size(); ++idx )
1246  {
1247  const unsigned i = listPartIndex[idx];
1248  getPartGeometry( geomCollection->geometryN( i ), i );
1249  }
1250  }
1251  break;
1252  }
1253 
1254  default:
1255  QgsDebugMsg( QStringLiteral( "feature %1: unsupported wkb type %2/%3 for rendering" )
1256  .arg( feature.id() )
1257  .arg( QgsWkbTypes::displayString( part->wkbType() ) )
1258  .arg( part->wkbType(), 0, 16 ) );
1259  }
1260  };
1261 
1262  // Use the simplified type ref when rendering -- this avoids some unnecessary cloning/geometry modification
1263  // (e.g. if the original geometry is a compound curve containing only a linestring curve, we don't have
1264  // to segmentize the geometry before rendering)
1265  getPartGeometry( geom.constGet()->simplifiedTypeRef(), 0 );
1266 
1267  // step 2 - determine which layers to render
1268  std::vector< int > layers;
1269  if ( layer == -1 )
1270  {
1271  layers.reserve( mLayers.count() );
1272  for ( int i = 0; i < mLayers.count(); ++i )
1273  layers.emplace_back( i );
1274  }
1275  else
1276  {
1277  layers.emplace_back( layer );
1278  }
1279 
1280  // step 3 - render these geometries using the desired symbol layers.
1281 
1282  if ( needsExpressionContext )
1283  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_count" ), mLayers.count(), true ) );
1284 
1285  for ( const int symbolLayerIndex : layers )
1286  {
1287  QgsSymbolLayer *symbolLayer = mLayers.value( symbolLayerIndex );
1288  if ( !symbolLayer || !symbolLayer->enabled() )
1289  continue;
1290 
1291  if ( needsExpressionContext )
1292  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_index" ), symbolLayerIndex + 1, true ) );
1293 
1294  symbolLayer->startFeatureRender( feature, context );
1295 
1296  switch ( mType )
1297  {
1299  {
1300  int geometryPartNumber = 0;
1301  for ( const PointInfo &point : std::as_const( pointsToRender ) )
1302  {
1303  if ( context.renderingStopped() )
1304  break;
1305 
1306  mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1307  if ( needsExpressionContext )
1308  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1309 
1310  static_cast<QgsMarkerSymbol *>( this )->renderPoint( point.renderPoint, &feature, context, symbolLayerIndex, selected );
1311  geometryPartNumber++;
1312  }
1313 
1314  break;
1315  }
1316 
1318  {
1319  if ( linesToRender.empty() )
1320  break;
1321 
1322  int geometryPartNumber = 0;
1323  for ( const LineInfo &line : std::as_const( linesToRender ) )
1324  {
1325  if ( context.renderingStopped() )
1326  break;
1327 
1328  mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1329  if ( needsExpressionContext )
1330  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1331 
1332  context.setGeometry( line.originalGeometry );
1333  static_cast<QgsLineSymbol *>( this )->renderPolyline( line.renderLine, &feature, context, symbolLayerIndex, selected );
1334  geometryPartNumber++;
1335  }
1336  break;
1337  }
1338 
1340  {
1341  for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
1342  {
1343  if ( context.renderingStopped() )
1344  break;
1345 
1346  mSymbolRenderContext->setGeometryPartNum( info.originalPartIndex + 1 );
1347  if ( needsExpressionContext )
1348  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, info.originalPartIndex + 1, true ) );
1349 
1350  context.setGeometry( info.originalGeometry );
1351  static_cast<QgsFillSymbol *>( this )->renderPolygon( info.renderExterior, ( !info.renderRings.isEmpty() ? &info.renderRings : nullptr ), &feature, context, symbolLayerIndex, selected );
1352  }
1353 
1354  break;
1355  }
1356 
1358  break;
1359  }
1360 
1361  symbolLayer->stopFeatureRender( feature, context );
1362  }
1363 
1364  // step 4 - handle post processing steps
1365  switch ( mType )
1366  {
1368  {
1369  markers.reserve( pointsToRender.size() );
1370  for ( const PointInfo &info : std::as_const( pointsToRender ) )
1371  {
1373  {
1374  const QRectF bounds = static_cast<QgsMarkerSymbol *>( this )->bounds( info.renderPoint, context, feature );
1375  if ( context.hasRenderedFeatureHandlers() )
1376  {
1377  renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromRect( bounds )
1378  : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromRect( QgsRectangle( bounds ) ) << renderedBoundsGeom );
1379  }
1381  {
1382  //draw debugging rect
1383  context.painter()->setPen( Qt::red );
1384  context.painter()->setBrush( QColor( 255, 0, 0, 100 ) );
1385  context.painter()->drawRect( bounds );
1386  }
1387  }
1388 
1389  if ( drawVertexMarker && !usingSegmentizedGeometry )
1390  {
1391  markers.append( info.renderPoint );
1392  }
1393  }
1394  break;
1395  }
1396 
1398  {
1399  for ( const LineInfo &info : std::as_const( linesToRender ) )
1400  {
1401  if ( context.hasRenderedFeatureHandlers() && !info.renderLine.empty() )
1402  {
1403  renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderLine )
1404  : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderLine ) << renderedBoundsGeom );
1405  }
1406 
1407  if ( drawVertexMarker && !usingSegmentizedGeometry )
1408  {
1409  markers << info.renderLine;
1410  }
1411  }
1412  break;
1413  }
1414 
1416  {
1417  int i = 0;
1418  for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
1419  {
1420  if ( context.hasRenderedFeatureHandlers() && !info.renderExterior.empty() )
1421  {
1422  renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderExterior )
1423  : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderExterior ) << renderedBoundsGeom );
1424  // TODO: consider holes?
1425  }
1426 
1427  if ( drawVertexMarker && !usingSegmentizedGeometry )
1428  {
1429  markers << info.renderExterior;
1430 
1431  for ( const QPolygonF &hole : info.renderRings )
1432  {
1433  markers << hole;
1434  }
1435  }
1436  i++;
1437  }
1438  break;
1439  }
1440 
1442  break;
1443  }
1444 
1445  if ( context.hasRenderedFeatureHandlers() && !renderedBoundsGeom.isNull() )
1446  {
1448  const QList< QgsRenderedFeatureHandlerInterface * > handlers = context.renderedFeatureHandlers();
1449  for ( QgsRenderedFeatureHandlerInterface *handler : handlers )
1450  handler->handleRenderedFeature( feature, renderedBoundsGeom, featureContext );
1451  }
1452 
1453  if ( drawVertexMarker )
1454  {
1455  if ( !markers.isEmpty() && !context.renderingStopped() )
1456  {
1457  const auto constMarkers = markers;
1458  for ( QPointF marker : constMarkers )
1459  {
1460  renderVertexMarker( marker, context, currentVertexMarkerType, currentVertexMarkerSize );
1461  }
1462  }
1463  else
1464  {
1466  const QgsMapToPixel &mtp = context.mapToPixel();
1467 
1468  QgsPoint vertexPoint;
1469  QgsVertexId vertexId;
1470  double x, y, z;
1471  QPointF mapPoint;
1472  while ( geom.constGet()->nextVertex( vertexId, vertexPoint ) )
1473  {
1474  //transform
1475  x = vertexPoint.x();
1476  y = vertexPoint.y();
1477  z = 0.0;
1478  if ( ct.isValid() )
1479  {
1480  ct.transformInPlace( x, y, z );
1481  }
1482  mapPoint.setX( x );
1483  mapPoint.setY( y );
1484  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1485  renderVertexMarker( mapPoint, context, currentVertexMarkerType, currentVertexMarkerSize );
1486  }
1487  }
1488  }
1489 }
1490 
1492 {
1493  return mSymbolRenderContext.get();
1494 }
1495 
1496 void QgsSymbol::renderVertexMarker( QPointF pt, QgsRenderContext &context, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize )
1497 {
1498  int markerSize = context.convertToPainterUnits( currentVertexMarkerSize, QgsUnitTypes::RenderMillimeters );
1499  QgsSymbolLayerUtils::drawVertexMarker( pt.x(), pt.y(), *context.painter(), currentVertexMarkerType, markerSize );
1500 }
1501 
1502 void QgsSymbol::initPropertyDefinitions()
1503 {
1504  if ( !sPropertyDefinitions.isEmpty() )
1505  return;
1506 
1507  QString origin = QStringLiteral( "symbol" );
1508 
1509  sPropertyDefinitions = QgsPropertiesDefinition
1510  {
1511  { QgsSymbol::PropertyOpacity, QgsPropertyDefinition( "alpha", QObject::tr( "Opacity" ), QgsPropertyDefinition::Opacity, origin )},
1512  };
1513 }
1514 
1515 void QgsSymbol::startFeatureRender( const QgsFeature &feature, QgsRenderContext &context, const int layer )
1516 {
1517  if ( layer != -1 )
1518  {
1520  if ( symbolLayer && symbolLayer->enabled() )
1521  {
1522  symbolLayer->startFeatureRender( feature, context );
1523  }
1524  return;
1525  }
1526  else
1527  {
1528  const QList< QgsSymbolLayer * > layers = mLayers;
1529  for ( QgsSymbolLayer *symbolLayer : layers )
1530  {
1531  if ( !symbolLayer->enabled() )
1532  continue;
1533 
1534  symbolLayer->startFeatureRender( feature, context );
1535  }
1536  }
1537 }
1538 
1539 void QgsSymbol::stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context, int layer )
1540 {
1541  if ( layer != -1 )
1542  {
1544  if ( symbolLayer && symbolLayer->enabled() )
1545  {
1546  symbolLayer->stopFeatureRender( feature, context );
1547  }
1548  return;
1549  }
1550  else
1551  {
1552  const QList< QgsSymbolLayer * > layers = mLayers;
1553  for ( QgsSymbolLayer *symbolLayer : layers )
1554  {
1555  if ( !symbolLayer->enabled() )
1556  continue;
1557 
1558  symbolLayer->stopFeatureRender( feature, context );
1559  }
1560  }
1561 }
@ 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:672
SymbolType
Symbol types.
Definition: qgis.h:205
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
@ Hybrid
Hybrid symbol.
Abstract base class for all geometries.
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.
static void trimPolygon(QPolygonF &pts, const QgsRectangle &clipRect)
Definition: qgsclipper.h:226
static QPolygonF clippedLine(const QgsCurve &curve, const QgsRectangle &clipExtent)
Takes a linestring and clips it to clipExtent.
Definition: qgsclipper.cpp:41
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.
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
virtual int numPoints() const =0
Returns the number of points in the curve.
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...
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:474
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:143
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:1231
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:814
QgsSymbolRenderContext * symbolRenderContext()
Returns the symbol render context.
Definition: qgssymbol.cpp:1491
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:423
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:866
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:828
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:338
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:540
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:145
QgsSymbolAnimationSettings & animationSettings()
Returns a reference to the symbol animation settings.
Definition: qgssymbol.cpp:356
static Qgis::SymbolType symbolTypeForGeometryType(QgsWkbTypes::GeometryType type)
Returns the default symbol type required for the specified geometry type.
Definition: qgssymbol.cpp:247
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:590
void renderVertexMarker(QPointF pt, QgsRenderContext &context, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize)
Render editing vertex marker at specified point.
Definition: qgssymbol.cpp:1496
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:264
bool appendSymbolLayer(QgsSymbolLayer *layer)
Appends a symbol layer at the end of the current symbol layer list.
Definition: qgssymbol.cpp:446
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
Definition: qgssymbol.cpp:371
bool usesMapUnits() const
Returns true if the symbol has any components which use map unit based sizes.
Definition: qgssymbol.cpp:297
QgsUnitTypes::RenderUnit outputUnit() const
Returns the units to use for sizes and widths within the symbol.
Definition: qgssymbol.cpp:276
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:801
void setColor(const QColor &color) const
Sets the color for the symbol.
Definition: qgssymbol.cpp:565
bool insertSymbolLayer(int index, QgsSymbolLayer *layer)
Inserts a symbol layer to specified index.
Definition: qgssymbol.cpp:433
QgsMapUnitScale mapUnitScale() const
Returns the map unit scale for the symbol.
Definition: qgssymbol.cpp:314
static QString symbolTypeToString(Qgis::SymbolType type)
Returns a translated string version of the specified symbol type.
Definition: qgssymbol.cpp:231
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:884
void setMapUnitScale(const QgsMapUnitScale &scale) const
Sets the map unit scale for the symbol.
Definition: qgssymbol.cpp:347
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:707
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:216
QString dump() const
Returns a string dump of the symbol's properties.
Definition: qgssymbol.cpp:775
bool hasDataDefinedProperties() const
Returns whether the symbol utilizes any data defined properties.
Definition: qgssymbol.cpp:871
bool deleteSymbolLayer(int index)
Removes and deletes the symbol layer at the specified index.
Definition: qgssymbol.cpp:456
virtual ~QgsSymbol()
Definition: qgssymbol.cpp:270
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Definition: qgssymbol.cpp:849
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:722
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:476
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:467
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:901
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:1515
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:950
QColor color() const
Returns the symbol's color.
Definition: qgssymbol.cpp:575
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:366
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:492
Q_DECL_DEPRECATED void setLayer(const QgsVectorLayer *layer)
Definition: qgssymbol.cpp:894
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:687
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context, int layer=-1)
Called after symbol layers have been rendered for a particular feature.
Definition: qgssymbol.cpp:1539
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:2572
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2545
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:1942
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2544
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1990
#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