QGIS API Documentation  3.21.0-Master (5b68dc587e)
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 );
581  if ( patchShape )
582  symbolContext.setPatchShape( *patchShape );
583 
584  if ( !customContext && expressionContext )
585  {
586  context->setExpressionContext( *expressionContext );
587  }
588  else if ( !customContext )
589  {
590  // if no render context was passed, build a minimal expression context
591  QgsExpressionContext expContext;
593  context->setExpressionContext( expContext );
594  }
595 
596  for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
597  {
598  if ( !layer->enabled() || ( customContext && !customContext->isSymbolLayerEnabled( layer ) ) )
599  continue;
600 
602  {
603  // line symbol layer would normally draw just a line
604  // so we override this case to force it to draw a polygon stroke
605  QgsLineSymbolLayer *lsl = dynamic_cast<QgsLineSymbolLayer *>( layer );
606  if ( lsl )
607  {
608  // from QgsFillSymbolLayer::drawPreviewIcon() -- would be nicer to add the
609  // symbol type to QgsSymbolLayer::drawPreviewIcon so this logic could be avoided!
610 
611  // hmm... why was this using size -1 ??
612  const QSizeF targetSize = QSizeF( size.width() - 1, size.height() - 1 );
613 
614  const QList< QList< QPolygonF > > polys = patchShape ? patchShape->toQPolygonF( Qgis::SymbolType::Fill, targetSize )
616 
617  lsl->startRender( symbolContext );
618  QgsPaintEffect *effect = lsl->paintEffect();
619 
620  std::unique_ptr< QgsEffectPainter > effectPainter;
621  if ( effect && effect->enabled() )
622  effectPainter = std::make_unique< QgsEffectPainter >( symbolContext.renderContext(), effect );
623 
624  for ( const QList< QPolygonF > &poly : polys )
625  {
626  QVector< QPolygonF > rings;
627  rings.reserve( poly.size() );
628  for ( int i = 1; i < poly.size(); ++i )
629  rings << poly.at( i );
630  lsl->renderPolygonStroke( poly.value( 0 ), &rings, symbolContext );
631  }
632 
633  effectPainter.reset();
634  lsl->stopRender( symbolContext );
635  }
636  }
637  else
638  layer->drawPreviewIcon( symbolContext, size );
639  }
640 
641  context->setForceVectorOutput( prevForceVector );
642 }
643 
644 void QgsSymbol::exportImage( const QString &path, const QString &format, QSize size )
645 {
646  if ( format.compare( QLatin1String( "svg" ), Qt::CaseInsensitive ) == 0 )
647  {
648  QSvgGenerator generator;
649  generator.setFileName( path );
650  generator.setSize( size );
651  generator.setViewBox( QRect( 0, 0, size.height(), size.height() ) );
652 
653  QPainter painter( &generator );
654  drawPreviewIcon( &painter, size );
655  painter.end();
656  }
657  else
658  {
659  QImage image = asImage( size );
660  image.save( path );
661  }
662 }
663 
664 QImage QgsSymbol::asImage( QSize size, QgsRenderContext *customContext )
665 {
666  QImage image( size, QImage::Format_ARGB32_Premultiplied );
667  image.fill( 0 );
668 
669  QPainter p( &image );
670  p.setRenderHint( QPainter::Antialiasing );
671 
672  drawPreviewIcon( &p, size, customContext );
673 
674  return image;
675 }
676 
677 
678 QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, Qgis::SymbolPreviewFlags flags )
679 {
680  QImage preview( QSize( 100, 100 ), QImage::Format_ARGB32_Premultiplied );
681  preview.fill( 0 );
682 
683  QPainter p( &preview );
684  p.setRenderHint( QPainter::Antialiasing );
685  p.translate( 0.5, 0.5 ); // shift by half a pixel to avoid blurring due antialiasing
686 
688  {
689  p.setPen( QPen( Qt::gray ) );
690  p.drawLine( 0, 50, 100, 50 );
691  p.drawLine( 50, 0, 50, 100 );
692  }
693 
696  if ( expressionContext )
697  context.setExpressionContext( *expressionContext );
698 
699  context.setIsGuiPreview( true );
700  startRender( context );
701 
702  if ( mType == Qgis::SymbolType::Line )
703  {
704  QPolygonF poly;
705  poly << QPointF( 0, 50 ) << QPointF( 99, 50 );
706  static_cast<QgsLineSymbol *>( this )->renderPolyline( poly, nullptr, context );
707  }
708  else if ( mType == Qgis::SymbolType::Fill )
709  {
710  QPolygonF polygon;
711  polygon << QPointF( 20, 20 ) << QPointF( 80, 20 ) << QPointF( 80, 80 ) << QPointF( 20, 80 ) << QPointF( 20, 20 );
712  static_cast<QgsFillSymbol *>( this )->renderPolygon( polygon, nullptr, nullptr, context );
713  }
714  else // marker
715  {
716  static_cast<QgsMarkerSymbol *>( this )->renderPoint( QPointF( 50, 50 ), nullptr, context );
717  }
718 
719  stopRender( context );
720  return preview;
721 }
722 
723 QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, int flags )
724 {
725  return bigSymbolPreviewImage( expressionContext, static_cast< Qgis::SymbolPreviewFlags >( flags ) );
726 }
727 
728 QString QgsSymbol::dump() const
729 {
730  QString t;
731  switch ( type() )
732  {
734  t = QStringLiteral( "MARKER" );
735  break;
737  t = QStringLiteral( "LINE" );
738  break;
740  t = QStringLiteral( "FILL" );
741  break;
742  default:
743  Q_ASSERT( false && "unknown symbol type" );
744  }
745  QString s = QStringLiteral( "%1 SYMBOL (%2 layers) color %3" ).arg( t ).arg( mLayers.count() ).arg( QgsSymbolLayerUtils::encodeColor( color() ) );
746 
747  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
748  {
749  // TODO:
750  }
751  return s;
752 }
753 
754 void QgsSymbol::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
755 {
756  props[ QStringLiteral( "alpha" )] = QString::number( opacity() );
757  double scaleFactor = 1.0;
758  props[ QStringLiteral( "uom" )] = QgsSymbolLayerUtils::encodeSldUom( outputUnit(), &scaleFactor );
759  props[ QStringLiteral( "uomScale" )] = ( !qgsDoubleNear( scaleFactor, 1.0 ) ? qgsDoubleToString( scaleFactor ) : QString() );
760 
761  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
762  {
763  ( *it )->toSld( doc, element, props );
764  }
765 }
766 
768 {
769  QgsSymbolLayerList lst;
770  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
771  {
772  QgsSymbolLayer *layer = ( *it )->clone();
773  layer->setLocked( ( *it )->isLocked() );
774  layer->setRenderingPass( ( *it )->renderingPass() );
775  layer->setEnabled( ( *it )->enabled() );
776  lst.append( layer );
777  }
778  return lst;
779 }
780 
781 void QgsSymbol::renderUsingLayer( QgsSymbolLayer *layer, QgsSymbolRenderContext &context, QgsWkbTypes::GeometryType geometryType, const QPolygonF *points, const QVector<QPolygonF> *rings )
782 {
783  Q_ASSERT( layer->type() == Qgis::SymbolType::Hybrid );
784 
785  if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
786  return;
787 
788  QgsGeometryGeneratorSymbolLayer *generatorLayer = static_cast<QgsGeometryGeneratorSymbolLayer *>( layer );
789 
790  QgsPaintEffect *effect = generatorLayer->paintEffect();
791  if ( effect && effect->enabled() )
792  {
793  QgsEffectPainter p( context.renderContext(), effect );
794  generatorLayer->render( context, geometryType, points, rings );
795  }
796  else
797  {
798  generatorLayer->render( context, geometryType, points, rings );
799  }
800 }
801 
802 QSet<QString> QgsSymbol::usedAttributes( const QgsRenderContext &context ) const
803 {
804  // calling referencedFields() with ignoreContext=true because in our expression context
805  // we do not have valid QgsFields yet - because of that the field names from expressions
806  // wouldn't get reported
807  QSet<QString> attributes = mDataDefinedProperties.referencedFields( context.expressionContext(), true );
808  QgsSymbolLayerList::const_iterator sIt = mLayers.constBegin();
809  for ( ; sIt != mLayers.constEnd(); ++sIt )
810  {
811  if ( *sIt )
812  {
813  attributes.unite( ( *sIt )->usedAttributes( context ) );
814  }
815  }
816  return attributes;
817 }
818 
820 {
821  mDataDefinedProperties.setProperty( key, property );
822 }
823 
825 {
826  if ( mDataDefinedProperties.hasActiveProperties() )
827  return true;
828 
829  for ( QgsSymbolLayer *layer : mLayers )
830  {
831  if ( layer->hasDataDefinedProperties() )
832  return true;
833  }
834  return false;
835 }
836 
838 {
839  for ( QgsSymbolLayer *layer : mLayers )
840  {
841  if ( layer->canCauseArtifactsBetweenAdjacentTiles() )
842  return true;
843  }
844  return false;
845 }
846 
848 {
850  mLayer = layer;
852 }
853 
855 {
857  return mLayer;
859 }
860 
862 
866 class ExpressionContextScopePopper
867 {
868  public:
869 
870  ExpressionContextScopePopper() = default;
871 
872  ~ExpressionContextScopePopper()
873  {
874  if ( context )
875  context->popScope();
876  }
877 
878  QgsExpressionContext *context = nullptr;
879 };
880 
884 class GeometryRestorer
885 {
886  public:
887  GeometryRestorer( QgsRenderContext &context )
888  : mContext( context ),
889  mGeometry( context.geometry() )
890  {}
891 
892  ~GeometryRestorer()
893  {
894  mContext.setGeometry( mGeometry );
895  }
896 
897  private:
898  QgsRenderContext &mContext;
899  const QgsAbstractGeometry *mGeometry;
900 };
902 
903 void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize )
904 {
905  if ( context.renderingStopped() )
906  return;
907 
908  const QgsGeometry geom = feature.geometry();
909  if ( geom.isNull() )
910  {
911  return;
912  }
913 
914  GeometryRestorer geomRestorer( context );
915 
916  bool usingSegmentizedGeometry = false;
917  context.setGeometry( geom.constGet() );
918 
919  if ( geom.type() != QgsWkbTypes::PointGeometry && !geom.boundingBox().isNull() )
920  {
921  try
922  {
923  const QPointF boundsOrigin = _getPoint( context, QgsPoint( geom.boundingBox().xMinimum(), geom.boundingBox().yMinimum() ) );
924  if ( std::isfinite( boundsOrigin.x() ) && std::isfinite( boundsOrigin.y() ) )
925  context.setTextureOrigin( boundsOrigin );
926  }
927  catch ( QgsCsException & )
928  {
929 
930  }
931  }
932 
933  bool clippingEnabled = clipFeaturesToExtent();
934  // do any symbol layers prevent feature clipping?
935  for ( QgsSymbolLayer *layer : std::as_const( mLayers ) )
936  {
938  {
939  clippingEnabled = false;
940  break;
941  }
942  }
943  if ( clippingEnabled && context.testFlag( Qgis::RenderContextFlag::RenderMapTile ) )
944  {
945  // If the "avoid artifacts between adjacent tiles" flag is set (RenderMapTile), then we'll force disable
946  // the geometry clipping IF (and only if) this symbol can potentially have rendering artifacts when rendered as map tiles.
947  // If the symbol won't have any artifacts anyway, then it's pointless and incredibly expensive to skip the clipping!
949  {
950  clippingEnabled = false;
951  }
952  }
953  if ( context.extent().isEmpty() )
954  clippingEnabled = false;
955 
956  mSymbolRenderContext->setGeometryPartCount( geom.constGet()->partCount() );
957  mSymbolRenderContext->setGeometryPartNum( 1 );
958 
959  const bool needsExpressionContext = hasDataDefinedProperties();
960  ExpressionContextScopePopper scopePopper;
961  if ( mSymbolRenderContext->expressionContextScope() )
962  {
963  if ( needsExpressionContext )
964  {
965  // this is somewhat nasty - by appending this scope here it's now owned
966  // by both mSymbolRenderContext AND context.expressionContext()
967  // the RAII scopePopper is required to make sure it always has ownership transferred back
968  // from context.expressionContext(), even if exceptions of other early exits occur in this
969  // function
970  context.expressionContext().appendScope( mSymbolRenderContext->expressionContextScope() );
971  scopePopper.context = &context.expressionContext();
972 
973  QgsExpressionContextUtils::updateSymbolScope( this, mSymbolRenderContext->expressionContextScope() );
974  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_COUNT, mSymbolRenderContext->geometryPartCount(), true ) );
975  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, 1, true ) );
976  }
977  }
978 
979  // Collection of markers to paint, only used for no curve types.
980  QPolygonF markers;
981 
982  QgsGeometry renderedBoundsGeom;
983 
984  // Step 1 - collect the set of painter coordinate geometries to render.
985  // We do this upfront, because we only want to ever do this once, regardless how many symbol layers we need to render.
986 
987  struct PointInfo
988  {
989  QPointF renderPoint;
990  const QgsPoint *originalGeometry = nullptr;
991  };
992  QVector< PointInfo > pointsToRender;
993 
994  struct LineInfo
995  {
996  QPolygonF renderLine;
997  const QgsCurve *originalGeometry = nullptr;
998  };
999  QVector< LineInfo > linesToRender;
1000 
1001  struct PolygonInfo
1002  {
1003  QPolygonF renderExterior;
1004  QVector< QPolygonF > renderRings;
1005  const QgsCurvePolygon *originalGeometry = nullptr;
1006  int originalPartIndex = 0;
1007  };
1008  QVector< PolygonInfo > polygonsToRender;
1009 
1010  std::function< void ( const QgsAbstractGeometry *, int partIndex )> getPartGeometry;
1011  getPartGeometry = [&pointsToRender, &linesToRender, &polygonsToRender, &getPartGeometry, &context, &clippingEnabled, &markers, &feature, &usingSegmentizedGeometry, this]( const QgsAbstractGeometry * part, int partIndex = 0 )
1012  {
1013  Q_UNUSED( feature )
1014 
1015  if ( !part )
1016  return;
1017 
1018  // geometry preprocessing
1019  QgsGeometry temporaryGeometryContainer;
1020  const QgsAbstractGeometry *processedGeometry = nullptr;
1021 
1022  const bool isMultiPart = qgsgeometry_cast< const QgsGeometryCollection * >( part ) && qgsgeometry_cast< const QgsGeometryCollection * >( part )->numGeometries() > 1;
1023 
1024  if ( !isMultiPart )
1025  {
1026  // segmentize curved geometries
1027  const bool needsSegmentizing = QgsWkbTypes::isCurvedType( part->wkbType() ) || part->hasCurvedSegments();
1028  if ( needsSegmentizing )
1029  {
1030  std::unique_ptr< QgsAbstractGeometry > segmentizedPart( part->segmentize( context.segmentationTolerance(), context.segmentationToleranceType() ) );
1031  if ( !segmentizedPart )
1032  {
1033  return;
1034  }
1035  temporaryGeometryContainer.set( segmentizedPart.release() );
1036  processedGeometry = temporaryGeometryContainer.constGet();
1037  usingSegmentizedGeometry = true;
1038  }
1039  else
1040  {
1041  // no segmentation required
1042  processedGeometry = part;
1043  }
1044 
1045  // Simplify the geometry, if needed.
1046  if ( context.vectorSimplifyMethod().forceLocalOptimization() )
1047  {
1048  const int simplifyHints = context.vectorSimplifyMethod().simplifyHints();
1049  const QgsMapToPixelSimplifier simplifier( simplifyHints, context.vectorSimplifyMethod().tolerance(),
1051 
1052  std::unique_ptr< QgsAbstractGeometry > simplified( simplifier.simplify( processedGeometry ) );
1053  if ( simplified )
1054  {
1055  temporaryGeometryContainer.set( simplified.release() );
1056  processedGeometry = temporaryGeometryContainer.constGet();
1057  }
1058  }
1059 
1060  // clip geometry to render context clipping regions
1061  if ( !context.featureClipGeometry().isEmpty() )
1062  {
1063  // apply feature clipping from context to the rendered geometry only -- just like the render time simplification,
1064  // we should NEVER apply this to the geometry attached to the feature itself. Doing so causes issues with certain
1065  // renderer settings, e.g. if polygons are being rendered using a rule based renderer based on the feature's area,
1066  // then we need to ensure that the original feature area is used instead of the clipped area..
1067  QgsGeos geos( processedGeometry );
1068  std::unique_ptr< QgsAbstractGeometry > clippedGeom( geos.intersection( context.featureClipGeometry().constGet() ) );
1069  if ( clippedGeom )
1070  {
1071  temporaryGeometryContainer.set( clippedGeom.release() );
1072  processedGeometry = temporaryGeometryContainer.constGet();
1073  }
1074  }
1075  }
1076  else
1077  {
1078  // for multipart geometries, the processing is deferred till we're rendering the actual part...
1079  processedGeometry = part;
1080  }
1081 
1082  if ( !processedGeometry )
1083  {
1084  // shouldn't happen!
1085  QgsDebugMsg( QStringLiteral( "No processed geometry to render for part!" ) );
1086  return;
1087  }
1088 
1089  switch ( QgsWkbTypes::flatType( processedGeometry->wkbType() ) )
1090  {
1091  case QgsWkbTypes::Point:
1092  {
1094  {
1095  QgsDebugMsgLevel( QStringLiteral( "point can be drawn only with marker symbol!" ), 2 );
1096  break;
1097  }
1098 
1099  PointInfo info;
1100  info.originalGeometry = qgsgeometry_cast< const QgsPoint * >( part );
1101  info.renderPoint = _getPoint( context, *info.originalGeometry );
1102  pointsToRender << info;
1103  break;
1104  }
1105 
1107  {
1108  if ( mType != Qgis::SymbolType::Line )
1109  {
1110  QgsDebugMsgLevel( QStringLiteral( "linestring can be drawn only with line symbol!" ), 2 );
1111  break;
1112  }
1113 
1114  LineInfo info;
1115  info.originalGeometry = qgsgeometry_cast<const QgsCurve *>( part );
1116  info.renderLine = _getLineString( context, *qgsgeometry_cast<const QgsCurve *>( processedGeometry ), clippingEnabled );
1117  linesToRender << info;
1118  break;
1119  }
1120 
1121  case QgsWkbTypes::Polygon:
1122  case QgsWkbTypes::Triangle:
1123  {
1124  QPolygonF pts;
1125  if ( mType != Qgis::SymbolType::Fill )
1126  {
1127  QgsDebugMsgLevel( QStringLiteral( "polygon can be drawn only with fill symbol!" ), 2 );
1128  break;
1129  }
1130 
1131  PolygonInfo info;
1132  info.originalGeometry = qgsgeometry_cast<const QgsCurvePolygon *>( part );
1133  info.originalPartIndex = partIndex;
1134  if ( !qgsgeometry_cast<const QgsPolygon *>( processedGeometry )->exteriorRing() )
1135  {
1136  QgsDebugMsg( QStringLiteral( "cannot render polygon with no exterior ring" ) );
1137  break;
1138  }
1139 
1140  _getPolygon( info.renderExterior, info.renderRings, context, *qgsgeometry_cast<const QgsPolygon *>( processedGeometry ), clippingEnabled, mForceRHR );
1141  polygonsToRender << info;
1142  break;
1143  }
1144 
1146  {
1147  const QgsMultiPoint *mp = qgsgeometry_cast< const QgsMultiPoint * >( part );
1148  markers.reserve( mp->numGeometries() );
1149  }
1150  FALLTHROUGH
1154  {
1155  const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( part );
1156 
1157  const unsigned int num = geomCollection->numGeometries();
1158  for ( unsigned int i = 0; i < num; ++i )
1159  {
1160  if ( context.renderingStopped() )
1161  break;
1162 
1163  getPartGeometry( geomCollection->geometryN( i ), i );
1164  }
1165  break;
1166  }
1167 
1170  {
1171  if ( mType != Qgis::SymbolType::Fill )
1172  {
1173  QgsDebugMsgLevel( QStringLiteral( "multi-polygon can be drawn only with fill symbol!" ), 2 );
1174  break;
1175  }
1176 
1177  QPolygonF pts;
1178 
1179  const QgsGeometryCollection *geomCollection = dynamic_cast<const QgsGeometryCollection *>( part );
1180  const unsigned int num = geomCollection->numGeometries();
1181 
1182  // Sort components by approximate area (probably a bit faster than using
1183  // area() )
1184  std::map<double, QList<unsigned int> > thisAreaToPartNum;
1185  for ( unsigned int i = 0; i < num; ++i )
1186  {
1187  const QgsRectangle r( geomCollection->geometryN( i )->boundingBox() );
1188  thisAreaToPartNum[ r.width() * r.height()] << i;
1189  }
1190 
1191  // Draw starting with larger parts down to smaller parts, so that in
1192  // case of a part being incorrectly inside another part, it is drawn
1193  // on top of it (#15419)
1194  std::map<double, QList<unsigned int> >::const_reverse_iterator iter = thisAreaToPartNum.rbegin();
1195  for ( ; iter != thisAreaToPartNum.rend(); ++iter )
1196  {
1197  const QList<unsigned int> &listPartIndex = iter->second;
1198  for ( int idx = 0; idx < listPartIndex.size(); ++idx )
1199  {
1200  const unsigned i = listPartIndex[idx];
1201  getPartGeometry( geomCollection->geometryN( i ), i );
1202  }
1203  }
1204  break;
1205  }
1206 
1207  default:
1208  QgsDebugMsg( QStringLiteral( "feature %1: unsupported wkb type %2/%3 for rendering" )
1209  .arg( feature.id() )
1210  .arg( QgsWkbTypes::displayString( part->wkbType() ) )
1211  .arg( part->wkbType(), 0, 16 ) );
1212  }
1213  };
1214 
1215  // Use the simplified type ref when rendering -- this avoids some unnecessary cloning/geometry modification
1216  // (e.g. if the original geometry is a compound curve containing only a linestring curve, we don't have
1217  // to segmentize the geometry before rendering)
1218  getPartGeometry( geom.constGet()->simplifiedTypeRef(), 0 );
1219 
1220  // step 2 - determine which layers to render
1221  std::vector< int > layers;
1222  if ( layer == -1 )
1223  {
1224  layers.reserve( mLayers.count() );
1225  for ( int i = 0; i < mLayers.count(); ++i )
1226  layers.emplace_back( i );
1227  }
1228  else
1229  {
1230  layers.emplace_back( layer );
1231  }
1232 
1233  // step 3 - render these geometries using the desired symbol layers.
1234 
1235  if ( needsExpressionContext )
1236  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_count" ), mLayers.count(), true ) );
1237 
1238  for ( const int symbolLayerIndex : layers )
1239  {
1240  QgsSymbolLayer *symbolLayer = mLayers.value( symbolLayerIndex );
1241  if ( !symbolLayer || !symbolLayer->enabled() )
1242  continue;
1243 
1244  if ( needsExpressionContext )
1245  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_index" ), symbolLayerIndex + 1, true ) );
1246 
1247  symbolLayer->startFeatureRender( feature, context );
1248 
1249  switch ( mType )
1250  {
1252  {
1253  int geometryPartNumber = 0;
1254  for ( const PointInfo &point : std::as_const( pointsToRender ) )
1255  {
1256  if ( context.renderingStopped() )
1257  break;
1258 
1259  mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1260  if ( needsExpressionContext )
1261  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1262 
1263  static_cast<QgsMarkerSymbol *>( this )->renderPoint( point.renderPoint, &feature, context, symbolLayerIndex, selected );
1264  geometryPartNumber++;
1265  }
1266 
1267  break;
1268  }
1269 
1271  {
1272  if ( linesToRender.empty() )
1273  break;
1274 
1275  int geometryPartNumber = 0;
1276  for ( const LineInfo &line : std::as_const( linesToRender ) )
1277  {
1278  if ( context.renderingStopped() )
1279  break;
1280 
1281  mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1282  if ( needsExpressionContext )
1283  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1284 
1285  context.setGeometry( line.originalGeometry );
1286  static_cast<QgsLineSymbol *>( this )->renderPolyline( line.renderLine, &feature, context, symbolLayerIndex, selected );
1287  geometryPartNumber++;
1288  }
1289  break;
1290  }
1291 
1293  {
1294  for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
1295  {
1296  if ( context.renderingStopped() )
1297  break;
1298 
1299  mSymbolRenderContext->setGeometryPartNum( info.originalPartIndex + 1 );
1300  if ( needsExpressionContext )
1301  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, info.originalPartIndex + 1, true ) );
1302 
1303  context.setGeometry( info.originalGeometry );
1304  static_cast<QgsFillSymbol *>( this )->renderPolygon( info.renderExterior, ( !info.renderRings.isEmpty() ? &info.renderRings : nullptr ), &feature, context, symbolLayerIndex, selected );
1305  }
1306 
1307  break;
1308  }
1309 
1311  break;
1312  }
1313 
1314  symbolLayer->stopFeatureRender( feature, context );
1315  }
1316 
1317  // step 4 - handle post processing steps
1318  switch ( mType )
1319  {
1321  {
1322  markers.reserve( pointsToRender.size() );
1323  for ( const PointInfo &info : std::as_const( pointsToRender ) )
1324  {
1326  {
1327  const QRectF bounds = static_cast<QgsMarkerSymbol *>( this )->bounds( info.renderPoint, context, feature );
1328  if ( context.hasRenderedFeatureHandlers() )
1329  {
1330  renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromRect( bounds )
1331  : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromRect( QgsRectangle( bounds ) ) << renderedBoundsGeom );
1332  }
1334  {
1335  //draw debugging rect
1336  context.painter()->setPen( Qt::red );
1337  context.painter()->setBrush( QColor( 255, 0, 0, 100 ) );
1338  context.painter()->drawRect( bounds );
1339  }
1340  }
1341 
1342  if ( drawVertexMarker && !usingSegmentizedGeometry )
1343  {
1344  markers.append( info.renderPoint );
1345  }
1346  }
1347  break;
1348  }
1349 
1351  {
1352  for ( const LineInfo &info : std::as_const( linesToRender ) )
1353  {
1354  if ( context.hasRenderedFeatureHandlers() && !info.renderLine.empty() )
1355  {
1356  renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderLine )
1357  : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderLine ) << renderedBoundsGeom );
1358  }
1359 
1360  if ( drawVertexMarker && !usingSegmentizedGeometry )
1361  {
1362  markers << info.renderLine;
1363  }
1364  }
1365  break;
1366  }
1367 
1369  {
1370  int i = 0;
1371  for ( const PolygonInfo &info : std::as_const( polygonsToRender ) )
1372  {
1373  if ( context.hasRenderedFeatureHandlers() && !info.renderExterior.empty() )
1374  {
1375  renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderExterior )
1376  : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderExterior ) << renderedBoundsGeom );
1377  // TODO: consider holes?
1378  }
1379 
1380  if ( drawVertexMarker && !usingSegmentizedGeometry )
1381  {
1382  markers << info.renderExterior;
1383 
1384  for ( const QPolygonF &hole : info.renderRings )
1385  {
1386  markers << hole;
1387  }
1388  }
1389  i++;
1390  }
1391  break;
1392  }
1393 
1395  break;
1396  }
1397 
1398  if ( context.hasRenderedFeatureHandlers() && !renderedBoundsGeom.isNull() )
1399  {
1401  const QList< QgsRenderedFeatureHandlerInterface * > handlers = context.renderedFeatureHandlers();
1402  for ( QgsRenderedFeatureHandlerInterface *handler : handlers )
1403  handler->handleRenderedFeature( feature, renderedBoundsGeom, featureContext );
1404  }
1405 
1406  if ( drawVertexMarker )
1407  {
1408  if ( !markers.isEmpty() && !context.renderingStopped() )
1409  {
1410  const auto constMarkers = markers;
1411  for ( QPointF marker : constMarkers )
1412  {
1413  renderVertexMarker( marker, context, currentVertexMarkerType, currentVertexMarkerSize );
1414  }
1415  }
1416  else
1417  {
1419  const QgsMapToPixel &mtp = context.mapToPixel();
1420 
1421  QgsPoint vertexPoint;
1422  QgsVertexId vertexId;
1423  double x, y, z;
1424  QPointF mapPoint;
1425  while ( geom.constGet()->nextVertex( vertexId, vertexPoint ) )
1426  {
1427  //transform
1428  x = vertexPoint.x();
1429  y = vertexPoint.y();
1430  z = 0.0;
1431  if ( ct.isValid() )
1432  {
1433  ct.transformInPlace( x, y, z );
1434  }
1435  mapPoint.setX( x );
1436  mapPoint.setY( y );
1437  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1438  renderVertexMarker( mapPoint, context, currentVertexMarkerType, currentVertexMarkerSize );
1439  }
1440  }
1441  }
1442 }
1443 
1445 {
1446  return mSymbolRenderContext.get();
1447 }
1448 
1449 void QgsSymbol::renderVertexMarker( QPointF pt, QgsRenderContext &context, Qgis::VertexMarkerType currentVertexMarkerType, double currentVertexMarkerSize )
1450 {
1451  int markerSize = context.convertToPainterUnits( currentVertexMarkerSize, QgsUnitTypes::RenderMillimeters );
1452  QgsSymbolLayerUtils::drawVertexMarker( pt.x(), pt.y(), *context.painter(), currentVertexMarkerType, markerSize );
1453 }
1454 
1455 void QgsSymbol::initPropertyDefinitions()
1456 {
1457  if ( !sPropertyDefinitions.isEmpty() )
1458  return;
1459 
1460  QString origin = QStringLiteral( "symbol" );
1461 
1462  sPropertyDefinitions = QgsPropertiesDefinition
1463  {
1464  { QgsSymbol::PropertyOpacity, QgsPropertyDefinition( "alpha", QObject::tr( "Opacity" ), QgsPropertyDefinition::Opacity, origin )},
1465  };
1466 }
1467 
1468 void QgsSymbol::startFeatureRender( const QgsFeature &feature, QgsRenderContext &context, const int layer )
1469 {
1470  if ( layer != -1 )
1471  {
1473  if ( symbolLayer && symbolLayer->enabled() )
1474  {
1475  symbolLayer->startFeatureRender( feature, context );
1476  }
1477  return;
1478  }
1479  else
1480  {
1481  const QList< QgsSymbolLayer * > layers = mLayers;
1482  for ( QgsSymbolLayer *symbolLayer : layers )
1483  {
1484  if ( !symbolLayer->enabled() )
1485  continue;
1486 
1487  symbolLayer->startFeatureRender( feature, context );
1488  }
1489  }
1490 }
1491 
1492 void QgsSymbol::stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context, int layer )
1493 {
1494  if ( layer != -1 )
1495  {
1497  if ( symbolLayer && symbolLayer->enabled() )
1498  {
1499  symbolLayer->stopFeatureRender( feature, context );
1500  }
1501  return;
1502  }
1503  else
1504  {
1505  const QList< QgsSymbolLayer * > layers = mLayers;
1506  for ( QgsSymbolLayer *symbolLayer : layers )
1507  {
1508  if ( !symbolLayer->enabled() )
1509  continue;
1510 
1511  symbolLayer->stopFeatureRender( feature, context );
1512  }
1513  }
1514 }
@ 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:285
virtual QPolygonF asQPolygonF() const
Returns a QPolygonF representing the points.
Definition: qgscurve.cpp:265
@ 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:124
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:126
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:127
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:767
QgsSymbolRenderContext * symbolRenderContext()
Returns the symbol render context.
Definition: qgssymbol.cpp:1444
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:819
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:781
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:1449
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:754
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:837
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:664
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:728
bool hasDataDefinedProperties() const
Returns whether the symbol utilizes any data defined properties.
Definition: qgssymbol.cpp:824
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:802
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:678
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:854
void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context, int layer=-1)
Called before symbol layers will be rendered for a particular feature.
Definition: qgssymbol.cpp:1468
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:903
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:847
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:644
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context, int layer=-1)
Called after symbol layers have been rendered for a particular feature.
Definition: qgssymbol.cpp:1492
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:881
static Type flatType(Type type) SIP_HOLDGIL
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:702
Contains geos related utilities and functions.
Definition: qgsgeos.h:42
#define FALLTHROUGH
Definition: qgis.h:1757
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:1730
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:1186
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1729
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1234
#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.