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