QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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 
56 QgsPropertiesDefinition QgsSymbol::sPropertyDefinitions;
57 
58 inline
59 QgsProperty rotateWholeSymbol( double additionalRotation, const QgsProperty &property )
60 {
61  QString exprString = property.asExpression();
62  return QgsProperty::fromExpression( QString::number( additionalRotation ) + " + (" + exprString + ')' );
63 }
64 
65 inline
66 QgsProperty scaleWholeSymbol( double scaleFactor, const QgsProperty &property )
67 {
68  QString exprString = property.asExpression();
69  return QgsProperty::fromExpression( QString::number( scaleFactor ) + "*(" + exprString + ')' );
70 }
71 
72 inline
73 QgsProperty scaleWholeSymbol( double scaleFactorX, double scaleFactorY, const QgsProperty &property )
74 {
75  QString exprString = property.asExpression();
77  ( !qgsDoubleNear( scaleFactorX, 0.0 ) ? "tostring(" + QString::number( scaleFactorX ) + "*(" + exprString + "))" : QStringLiteral( "'0'" ) ) +
78  "|| ',' || " +
79  ( !qgsDoubleNear( scaleFactorY, 0.0 ) ? "tostring(" + QString::number( scaleFactorY ) + "*(" + exprString + "))" : QStringLiteral( "'0'" ) ) );
80 }
81 
82 
84 
85 Q_NOWARN_DEPRECATED_PUSH // because of deprecated mLayer
87  : mType( type )
88  , mLayers( layers )
89 {
90 
91  // check they're all correct symbol layers
92  for ( int i = 0; i < mLayers.count(); i++ )
93  {
94  if ( !mLayers.at( i ) )
95  {
96  mLayers.removeAt( i-- );
97  }
98  else if ( !mLayers.at( i )->isCompatibleWithSymbol( this ) )
99  {
100  delete mLayers.at( i );
101  mLayers.removeAt( i-- );
102  }
103  }
104 }
106 
107 QPolygonF QgsSymbol::_getLineString( QgsRenderContext &context, const QgsCurve &curve, bool clipToExtent )
108 {
109  const unsigned int nPoints = curve.numPoints();
110 
112  const QgsMapToPixel &mtp = context.mapToPixel();
113  QPolygonF pts;
114 
115  //apply clipping for large lines to achieve a better rendering performance
116  if ( clipToExtent && nPoints > 1 && !( context.flags() & QgsRenderContext::ApplyClipAfterReprojection ) )
117  {
118  const QgsRectangle e = context.extent();
119  const double cw = e.width() / 10;
120  const double ch = e.height() / 10;
121  const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
122  pts = QgsClipper::clippedLine( curve, clipRect );
123  }
124  else
125  {
126  pts = curve.asQPolygonF();
127  }
128 
129  //transform the QPolygonF to screen coordinates
130  if ( ct.isValid() )
131  {
132  try
133  {
134  ct.transformPolygon( pts );
135  }
136  catch ( QgsCsException & )
137  {
138  // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
139  }
140  }
141 
142  // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
143  pts.erase( std::remove_if( pts.begin(), pts.end(),
144  []( const QPointF point )
145  {
146  return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
147  } ), pts.end() );
148 
149  if ( clipToExtent && nPoints > 1 && context.flags() & QgsRenderContext::ApplyClipAfterReprojection )
150  {
151  // early clipping was not possible, so we have to apply it here after transformation
152  const QgsRectangle e = context.mapExtent();
153  const double cw = e.width() / 10;
154  const double ch = e.height() / 10;
155  const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
156  pts = QgsClipper::clippedLine( pts, clipRect );
157  }
158 
159  QPointF *ptr = pts.data();
160  for ( int i = 0; i < pts.size(); ++i, ++ptr )
161  {
162  mtp.transformInPlace( ptr->rx(), ptr->ry() );
163  }
164 
165  return pts;
166 }
167 
168 QPolygonF QgsSymbol::_getPolygonRing( QgsRenderContext &context, const QgsCurve &curve, const bool clipToExtent, const bool isExteriorRing, const bool correctRingOrientation )
169 {
170  const QgsCoordinateTransform ct = context.coordinateTransform();
171  const QgsMapToPixel &mtp = context.mapToPixel();
172 
173  QPolygonF poly = curve.asQPolygonF();
174 
175  if ( curve.numPoints() < 1 )
176  return QPolygonF();
177 
178  if ( correctRingOrientation )
179  {
180  // ensure consistent polygon ring orientation
181  if ( isExteriorRing && curve.orientation() != QgsCurve::Clockwise )
182  std::reverse( poly.begin(), poly.end() );
183  else if ( !isExteriorRing && curve.orientation() != QgsCurve::CounterClockwise )
184  std::reverse( poly.begin(), poly.end() );
185  }
186 
187  //clip close to view extent, if needed
188  if ( clipToExtent && !( context.flags() & QgsRenderContext::ApplyClipAfterReprojection ) && !context.extent().contains( poly.boundingRect() ) )
189  {
190  const QgsRectangle e = context.extent();
191  const double cw = e.width() / 10;
192  const double ch = e.height() / 10;
193  const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
194  QgsClipper::trimPolygon( poly, clipRect );
195  }
196 
197  //transform the QPolygonF to screen coordinates
198  if ( ct.isValid() )
199  {
200  try
201  {
202  ct.transformPolygon( poly );
203  }
204  catch ( QgsCsException & )
205  {
206  // we don't abort the rendering here, instead we remove any invalid points and just plot those which ARE valid
207  }
208  }
209 
210  // remove non-finite points, e.g. infinite or NaN points caused by reprojecting errors
211  poly.erase( std::remove_if( poly.begin(), poly.end(),
212  []( const QPointF point )
213  {
214  return !std::isfinite( point.x() ) || !std::isfinite( point.y() );
215  } ), poly.end() );
216 
217  if ( clipToExtent && context.flags() & QgsRenderContext::ApplyClipAfterReprojection && !context.mapExtent().contains( poly.boundingRect() ) )
218  {
219  // early clipping was not possible, so we have to apply it here after transformation
220  const QgsRectangle e = context.mapExtent();
221  const double cw = e.width() / 10;
222  const double ch = e.height() / 10;
223  const QgsRectangle clipRect( e.xMinimum() - cw, e.yMinimum() - ch, e.xMaximum() + cw, e.yMaximum() + ch );
224  QgsClipper::trimPolygon( poly, clipRect );
225  }
226 
227  QPointF *ptr = poly.data();
228  for ( int i = 0; i < poly.size(); ++i, ++ptr )
229  {
230  mtp.transformInPlace( ptr->rx(), ptr->ry() );
231  }
232 
233  if ( !poly.empty() && !poly.isClosed() )
234  poly << poly.at( 0 );
235 
236  return poly;
237 }
238 
239 void QgsSymbol::_getPolygon( QPolygonF &pts, QVector<QPolygonF> &holes, QgsRenderContext &context, const QgsPolygon &polygon, const bool clipToExtent, const bool correctRingOrientation )
240 {
241  holes.clear();
242 
243  pts = _getPolygonRing( context, *polygon.exteriorRing(), clipToExtent, true, correctRingOrientation );
244  const int ringCount = polygon.numInteriorRings();
245  holes.reserve( ringCount );
246  for ( int idx = 0; idx < ringCount; idx++ )
247  {
248  const QPolygonF hole = _getPolygonRing( context, *( polygon.interiorRing( idx ) ), clipToExtent, false, correctRingOrientation );
249  if ( !hole.isEmpty() )
250  holes.append( hole );
251  }
252 }
253 
255 {
256  QgsSymbol::initPropertyDefinitions();
257  return sPropertyDefinitions;
258 }
259 
261 {
262  // delete all symbol layers (we own them, so it's okay)
263  qDeleteAll( mLayers );
264 }
265 
267 {
268  if ( mLayers.empty() )
269  {
271  }
272 
273  QgsSymbolLayerList::const_iterator it = mLayers.constBegin();
274 
275  QgsUnitTypes::RenderUnit unit = ( *it )->outputUnit();
276 
277  for ( ; it != mLayers.constEnd(); ++it )
278  {
279  if ( ( *it )->outputUnit() != unit )
280  {
282  }
283  }
284  return unit;
285 }
286 
288 {
289  if ( mLayers.empty() )
290  {
291  return false;
292  }
293 
294  for ( const QgsSymbolLayer *layer : mLayers )
295  {
296  if ( layer->usesMapUnits() )
297  {
298  return true;
299  }
300  }
301  return false;
302 }
303 
305 {
306  if ( mLayers.empty() )
307  {
308  return QgsMapUnitScale();
309  }
310 
311  QgsSymbolLayerList::const_iterator it = mLayers.constBegin();
312  if ( it == mLayers.constEnd() )
313  return QgsMapUnitScale();
314 
315  QgsMapUnitScale scale = ( *it )->mapUnitScale();
316  ++it;
317 
318  for ( ; it != mLayers.constEnd(); ++it )
319  {
320  if ( ( *it )->mapUnitScale() != scale )
321  {
322  return QgsMapUnitScale();
323  }
324  }
325  return scale;
326 }
327 
329 {
330  const auto constMLayers = mLayers;
331  for ( QgsSymbolLayer *layer : constMLayers )
332  {
333  layer->setOutputUnit( u );
334  }
335 }
336 
338 {
339  const auto constMLayers = mLayers;
340  for ( QgsSymbolLayer *layer : constMLayers )
341  {
342  layer->setMapUnitScale( scale );
343  }
344 }
345 
347 {
348  std::unique_ptr< QgsSymbol > s;
349 
350  // override global default if project has a default for this type
351  QString defaultSymbol;
352  switch ( geomType )
353  {
355  defaultSymbol = QgsProject::instance()->readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
356  break;
358  defaultSymbol = QgsProject::instance()->readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
359  break;
361  defaultSymbol = QgsProject::instance()->readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
362  break;
363  default:
364  break;
365  }
366  if ( !defaultSymbol.isEmpty() )
367  s.reset( QgsStyle::defaultStyle()->symbol( defaultSymbol ) );
368 
369  // if no default found for this type, get global default (as previously)
370  if ( !s )
371  {
372  switch ( geomType )
373  {
375  s = qgis::make_unique< QgsMarkerSymbol >();
376  break;
378  s = qgis::make_unique< QgsLineSymbol >();
379  break;
381  s = qgis::make_unique< QgsFillSymbol >();
382  break;
383  default:
384  QgsDebugMsg( QStringLiteral( "unknown layer's geometry type" ) );
385  return nullptr;
386  }
387  }
388 
389  // set opacity
390  double opacity = 1.0;
391  bool ok = false;
392  // upgrade old setting
393  double alpha = QgsProject::instance()->readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ), 255, &ok );
394  if ( ok )
395  opacity = alpha / 255.0;
396  double newOpacity = QgsProject::instance()->readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ), 1.0, &ok );
397  if ( ok )
398  opacity = newOpacity;
399  s->setOpacity( opacity );
400 
401  // set random color, it project prefs allow
402  if ( defaultSymbol.isEmpty() ||
403  QgsProject::instance()->readBoolEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ), true ) )
404  {
405  s->setColor( QgsApplication::colorSchemeRegistry()->fetchRandomStyleColor() );
406  }
407 
408  return s.release();
409 }
410 
412 {
413  return mLayers.value( layer );
414 }
415 
416 const QgsSymbolLayer *QgsSymbol::symbolLayer( int layer ) const
417 {
418  return mLayers.value( layer );
419 }
420 
422 {
423  if ( index < 0 || index > mLayers.count() ) // can be added also after the last index
424  return false;
425 
426  if ( !layer || !layer->isCompatibleWithSymbol( this ) )
427  return false;
428 
429  mLayers.insert( index, layer );
430  return true;
431 }
432 
433 
435 {
436  if ( !layer || !layer->isCompatibleWithSymbol( this ) )
437  return false;
438 
439  mLayers.append( layer );
440  return true;
441 }
442 
443 
445 {
446  if ( index < 0 || index >= mLayers.count() )
447  return false;
448 
449  delete mLayers.at( index );
450  mLayers.removeAt( index );
451  return true;
452 }
453 
454 
456 {
457  if ( index < 0 || index >= mLayers.count() )
458  return nullptr;
459 
460  return mLayers.takeAt( index );
461 }
462 
463 
465 {
466  QgsSymbolLayer *oldLayer = mLayers.value( index );
467 
468  if ( oldLayer == layer )
469  return false;
470 
471  if ( !layer || !layer->isCompatibleWithSymbol( this ) )
472  return false;
473 
474  delete oldLayer; // first delete the original layer
475  mLayers[index] = layer; // set new layer
476  return true;
477 }
478 
479 
480 void QgsSymbol::startRender( QgsRenderContext &context, const QgsFields &fields )
481 {
482  Q_ASSERT_X( !mStarted, "startRender", "Rendering has already been started for this symbol instance!" );
483  mStarted = true;
484 
485  mSymbolRenderContext.reset( new QgsSymbolRenderContext( context, QgsUnitTypes::RenderUnknownUnit, mOpacity, false, mRenderHints, nullptr, fields ) );
486 
487  // Why do we need a copy here ? Is it to make sure the symbol layer rendering does not mess with the symbol render context ?
488  // Or is there another profound reason ?
489  QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, mOpacity, false, mRenderHints, nullptr, fields );
490 
491  std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::updateSymbolScope( this, new QgsExpressionContextScope() ) );
492  mSymbolRenderContext->setExpressionContextScope( scope.release() );
493 
494  mDataDefinedProperties.prepare( context.expressionContext() );
495 
496  const auto constMLayers = mLayers;
497  for ( QgsSymbolLayer *layer : constMLayers )
498  {
499  if ( !layer->enabled() || !context.isSymbolLayerEnabled( layer ) )
500  continue;
501 
502  layer->prepareExpressions( symbolContext );
503  layer->startRender( symbolContext );
504  }
505 }
506 
508 {
509  Q_ASSERT_X( mStarted, "startRender", "startRender was not called for this symbol instance!" );
510  mStarted = false;
511 
512  Q_UNUSED( context )
513  if ( mSymbolRenderContext )
514  {
515  const auto constMLayers = mLayers;
516  for ( QgsSymbolLayer *layer : constMLayers )
517  {
518  if ( !layer->enabled() || !context.isSymbolLayerEnabled( layer ) )
519  continue;
520 
521  layer->stopRender( *mSymbolRenderContext );
522  }
523  }
524 
525  mSymbolRenderContext.reset( nullptr );
526 
528  mLayer = nullptr;
530 }
531 
532 void QgsSymbol::setColor( const QColor &color )
533 {
534  const auto constMLayers = mLayers;
535  for ( QgsSymbolLayer *layer : constMLayers )
536  {
537  if ( !layer->isLocked() )
538  layer->setColor( color );
539  }
540 }
541 
542 QColor QgsSymbol::color() const
543 {
544  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
545  {
546  // return color of the first unlocked layer
547  if ( !( *it )->isLocked() )
548  return ( *it )->color();
549  }
550  return QColor( 0, 0, 0 );
551 }
552 
553 void QgsSymbol::drawPreviewIcon( QPainter *painter, QSize size, QgsRenderContext *customContext, bool selected, const QgsExpressionContext *expressionContext, const QgsLegendPatchShape *patchShape )
554 {
555  QgsRenderContext *context = customContext;
556  std::unique_ptr< QgsRenderContext > tempContext;
557  if ( !context )
558  {
559  tempContext.reset( new QgsRenderContext( QgsRenderContext::fromQPainter( painter ) ) );
560  context = tempContext.get();
562  }
563 
564  const bool prevForceVector = context->forceVectorOutput();
565  context->setForceVectorOutput( true );
566 
567  const double opacity = expressionContext ? dataDefinedProperties().valueAsDouble( QgsSymbol::PropertyOpacity, *expressionContext, mOpacity ) : mOpacity;
568 
569  QgsSymbolRenderContext symbolContext( *context, QgsUnitTypes::RenderUnknownUnit, opacity, false, mRenderHints, nullptr );
570  symbolContext.setSelected( selected );
572  if ( patchShape )
573  symbolContext.setPatchShape( *patchShape );
574 
575  if ( !customContext && expressionContext )
576  {
577  context->setExpressionContext( *expressionContext );
578  }
579  else if ( !customContext )
580  {
581  // if no render context was passed, build a minimal expression context
582  QgsExpressionContext expContext;
584  context->setExpressionContext( expContext );
585  }
586 
587  for ( QgsSymbolLayer *layer : qgis::as_const( mLayers ) )
588  {
589  if ( !layer->enabled() || ( customContext && !customContext->isSymbolLayerEnabled( layer ) ) )
590  continue;
591 
592  if ( mType == Fill && layer->type() == Line )
593  {
594  // line symbol layer would normally draw just a line
595  // so we override this case to force it to draw a polygon stroke
596  QgsLineSymbolLayer *lsl = dynamic_cast<QgsLineSymbolLayer *>( layer );
597  if ( lsl )
598  {
599  // from QgsFillSymbolLayer::drawPreviewIcon() -- would be nicer to add the
600  // symbol type to QgsSymbolLayer::drawPreviewIcon so this logic could be avoided!
601 
602  // hmm... why was this using size -1 ??
603  const QSizeF targetSize = QSizeF( size.width() - 1, size.height() - 1 );
604 
605  const QList< QList< QPolygonF > > polys = patchShape ? patchShape->toQPolygonF( QgsSymbol::Fill, targetSize )
607 
608  lsl->startRender( symbolContext );
609  QgsPaintEffect *effect = lsl->paintEffect();
610 
611  std::unique_ptr< QgsEffectPainter > effectPainter;
612  if ( effect && effect->enabled() )
613  effectPainter = qgis::make_unique< QgsEffectPainter >( symbolContext.renderContext(), effect );
614 
615  for ( const QList< QPolygonF > &poly : polys )
616  {
617  QVector< QPolygonF > rings;
618  rings.reserve( poly.size() );
619  for ( int i = 1; i < poly.size(); ++i )
620  rings << poly.at( i );
621  lsl->renderPolygonStroke( poly.value( 0 ), &rings, symbolContext );
622  }
623 
624  effectPainter.reset();
625  lsl->stopRender( symbolContext );
626  }
627  }
628  else
629  layer->drawPreviewIcon( symbolContext, size );
630  }
631 
632  context->setForceVectorOutput( prevForceVector );
633 }
634 
635 void QgsSymbol::exportImage( const QString &path, const QString &format, QSize size )
636 {
637  if ( format.compare( QLatin1String( "svg" ), Qt::CaseInsensitive ) == 0 )
638  {
639  QSvgGenerator generator;
640  generator.setFileName( path );
641  generator.setSize( size );
642  generator.setViewBox( QRect( 0, 0, size.height(), size.height() ) );
643 
644  QPainter painter( &generator );
645  drawPreviewIcon( &painter, size );
646  painter.end();
647  }
648  else
649  {
650  QImage image = asImage( size );
651  image.save( path );
652  }
653 }
654 
655 QImage QgsSymbol::asImage( QSize size, QgsRenderContext *customContext )
656 {
657  QImage image( size, QImage::Format_ARGB32_Premultiplied );
658  image.fill( 0 );
659 
660  QPainter p( &image );
661  p.setRenderHint( QPainter::Antialiasing );
662 
663  drawPreviewIcon( &p, size, customContext );
664 
665  return image;
666 }
667 
668 
669 QImage QgsSymbol::bigSymbolPreviewImage( QgsExpressionContext *expressionContext, QgsSymbol::PreviewFlags flags )
670 {
671  QImage preview( QSize( 100, 100 ), QImage::Format_ARGB32_Premultiplied );
672  preview.fill( 0 );
673 
674  QPainter p( &preview );
675  p.setRenderHint( QPainter::Antialiasing );
676  p.translate( 0.5, 0.5 ); // shift by half a pixel to avoid blurring due antialiasing
677 
678  if ( mType == QgsSymbol::Marker && flags & PreviewFlag::FlagIncludeCrosshairsForMarkerSymbols )
679  {
680  p.setPen( QPen( Qt::gray ) );
681  p.drawLine( 0, 50, 100, 50 );
682  p.drawLine( 50, 0, 50, 100 );
683  }
684 
687  if ( expressionContext )
688  context.setExpressionContext( *expressionContext );
689 
690  context.setIsGuiPreview( true );
691  startRender( context );
692 
693  if ( mType == QgsSymbol::Line )
694  {
695  QPolygonF poly;
696  poly << QPointF( 0, 50 ) << QPointF( 99, 50 );
697  static_cast<QgsLineSymbol *>( this )->renderPolyline( poly, nullptr, context );
698  }
699  else if ( mType == QgsSymbol::Fill )
700  {
701  QPolygonF polygon;
702  polygon << QPointF( 20, 20 ) << QPointF( 80, 20 ) << QPointF( 80, 80 ) << QPointF( 20, 80 ) << QPointF( 20, 20 );
703  static_cast<QgsFillSymbol *>( this )->renderPolygon( polygon, nullptr, nullptr, context );
704  }
705  else // marker
706  {
707  static_cast<QgsMarkerSymbol *>( this )->renderPoint( QPointF( 50, 50 ), nullptr, context );
708  }
709 
710  stopRender( context );
711  return preview;
712 }
713 
714 
715 QString QgsSymbol::dump() const
716 {
717  QString t;
718  switch ( type() )
719  {
720  case QgsSymbol::Marker:
721  t = QStringLiteral( "MARKER" );
722  break;
723  case QgsSymbol::Line:
724  t = QStringLiteral( "LINE" );
725  break;
726  case QgsSymbol::Fill:
727  t = QStringLiteral( "FILL" );
728  break;
729  default:
730  Q_ASSERT( false && "unknown symbol type" );
731  }
732  QString s = QStringLiteral( "%1 SYMBOL (%2 layers) color %3" ).arg( t ).arg( mLayers.count() ).arg( QgsSymbolLayerUtils::encodeColor( color() ) );
733 
734  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
735  {
736  // TODO:
737  }
738  return s;
739 }
740 
741 void QgsSymbol::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
742 {
743  props[ QStringLiteral( "alpha" )] = QString::number( opacity() );
744  double scaleFactor = 1.0;
745  props[ QStringLiteral( "uom" )] = QgsSymbolLayerUtils::encodeSldUom( outputUnit(), &scaleFactor );
746  props[ QStringLiteral( "uomScale" )] = ( !qgsDoubleNear( scaleFactor, 1.0 ) ? qgsDoubleToString( scaleFactor ) : QString() );
747 
748  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
749  {
750  ( *it )->toSld( doc, element, props );
751  }
752 }
753 
755 {
756  QgsSymbolLayerList lst;
757  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
758  {
759  QgsSymbolLayer *layer = ( *it )->clone();
760  layer->setLocked( ( *it )->isLocked() );
761  layer->setRenderingPass( ( *it )->renderingPass() );
762  layer->setEnabled( ( *it )->enabled() );
763  lst.append( layer );
764  }
765  return lst;
766 }
767 
769 {
770  Q_ASSERT( layer->type() == Hybrid );
771 
772  if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
773  return;
774 
775  QgsGeometryGeneratorSymbolLayer *generatorLayer = static_cast<QgsGeometryGeneratorSymbolLayer *>( layer );
776 
777  QgsPaintEffect *effect = generatorLayer->paintEffect();
778  if ( effect && effect->enabled() )
779  {
780  QgsEffectPainter p( context.renderContext(), effect );
781  generatorLayer->render( context );
782  }
783  else
784  {
785  generatorLayer->render( context );
786  }
787 }
788 
789 QSet<QString> QgsSymbol::usedAttributes( const QgsRenderContext &context ) const
790 {
791  // calling referencedFields() with ignoreContext=true because in our expression context
792  // we do not have valid QgsFields yet - because of that the field names from expressions
793  // wouldn't get reported
794  QSet<QString> attributes = mDataDefinedProperties.referencedFields( context.expressionContext(), true );
795  QgsSymbolLayerList::const_iterator sIt = mLayers.constBegin();
796  for ( ; sIt != mLayers.constEnd(); ++sIt )
797  {
798  if ( *sIt )
799  {
800  attributes.unite( ( *sIt )->usedAttributes( context ) );
801  }
802  }
803  return attributes;
804 }
805 
807 {
808  mDataDefinedProperties.setProperty( key, property );
809 }
810 
812 {
813  if ( mDataDefinedProperties.hasActiveProperties() )
814  return true;
815 
816  for ( QgsSymbolLayer *layer : mLayers )
817  {
818  if ( layer->hasDataDefinedProperties() )
819  return true;
820  }
821  return false;
822 }
823 
825 {
826  for ( QgsSymbolLayer *layer : mLayers )
827  {
828  if ( layer->canCauseArtifactsBetweenAdjacentTiles() )
829  return true;
830  }
831  return false;
832 }
833 
835 {
837  mLayer = layer;
839 }
840 
842 {
844  return mLayer;
846 }
847 
849 
853 class ExpressionContextScopePopper
854 {
855  public:
856 
857  ExpressionContextScopePopper() = default;
858 
859  ~ExpressionContextScopePopper()
860  {
861  if ( context )
862  context->popScope();
863  }
864 
865  QgsExpressionContext *context = nullptr;
866 };
867 
871 class GeometryRestorer
872 {
873  public:
874  GeometryRestorer( QgsRenderContext &context )
875  : mContext( context ),
876  mGeometry( context.geometry() )
877  {}
878 
879  ~GeometryRestorer()
880  {
881  mContext.setGeometry( mGeometry );
882  }
883 
884  private:
885  QgsRenderContext &mContext;
886  const QgsAbstractGeometry *mGeometry;
887 };
889 
890 void QgsSymbol::renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker, int currentVertexMarkerType, double currentVertexMarkerSize )
891 {
892  if ( context.renderingStopped() )
893  return;
894 
895  const QgsGeometry geom = feature.geometry();
896  if ( geom.isNull() )
897  {
898  return;
899  }
900 
901  GeometryRestorer geomRestorer( context );
902 
903  bool usingSegmentizedGeometry = false;
904  context.setGeometry( geom.constGet() );
905 
906  if ( geom.type() != QgsWkbTypes::PointGeometry && !geom.boundingBox().isNull() )
907  {
908  try
909  {
910  const QPointF boundsOrigin = _getPoint( context, QgsPoint( geom.boundingBox().xMinimum(), geom.boundingBox().yMinimum() ) );
911  if ( std::isfinite( boundsOrigin.x() ) && std::isfinite( boundsOrigin.y() ) )
912  context.setTextureOrigin( boundsOrigin );
913  }
914  catch ( QgsCsException & )
915  {
916 
917  }
918  }
919 
920  bool clippingEnabled = clipFeaturesToExtent();
921  if ( clippingEnabled && context.testFlag( QgsRenderContext::RenderMapTile ) )
922  {
923  // If the "avoid artifacts between adjacent tiles" flag is set (RenderMapTile), then we'll force disable
924  // the geometry clipping IF (and only if) this symbol can potentially have rendering artifacts when rendered as map tiles.
925  // If the symbol won't have any artifacts anyway, then it's pointless and incredibly expensive to skip the clipping!
927  {
928  clippingEnabled = false;
929  }
930  }
931 
932  mSymbolRenderContext->setGeometryPartCount( geom.constGet()->partCount() );
933  mSymbolRenderContext->setGeometryPartNum( 1 );
934 
935  const bool needsExpressionContext = hasDataDefinedProperties();
936  ExpressionContextScopePopper scopePopper;
937  if ( mSymbolRenderContext->expressionContextScope() )
938  {
939  if ( needsExpressionContext )
940  {
941  // this is somewhat nasty - by appending this scope here it's now owned
942  // by both mSymbolRenderContext AND context.expressionContext()
943  // the RAII scopePopper is required to make sure it always has ownership transferred back
944  // from context.expressionContext(), even if exceptions of other early exits occur in this
945  // function
946  context.expressionContext().appendScope( mSymbolRenderContext->expressionContextScope() );
947  scopePopper.context = &context.expressionContext();
948 
949  QgsExpressionContextUtils::updateSymbolScope( this, mSymbolRenderContext->expressionContextScope() );
950  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_COUNT, mSymbolRenderContext->geometryPartCount(), true ) );
951  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, 1, true ) );
952  }
953  }
954 
955  // Collection of markers to paint, only used for no curve types.
956  QPolygonF markers;
957 
958  QgsGeometry renderedBoundsGeom;
959 
960  // Step 1 - collect the set of painter coordinate geometries to render.
961  // We do this upfront, because we only want to ever do this once, regardless how many symbol layers we need to render.
962 
963  struct PointInfo
964  {
965  QPointF renderPoint;
966  const QgsPoint *originalGeometry = nullptr;
967  };
968  QVector< PointInfo > pointsToRender;
969 
970  struct LineInfo
971  {
972  QPolygonF renderLine;
973  const QgsCurve *originalGeometry = nullptr;
974  };
975  QVector< LineInfo > linesToRender;
976 
977  struct PolygonInfo
978  {
979  QPolygonF renderExterior;
980  QVector< QPolygonF > renderRings;
981  const QgsCurvePolygon *originalGeometry = nullptr;
982  };
983  QVector< PolygonInfo > polygonsToRender;
984 
985  std::function< void ( const QgsAbstractGeometry * )> getPartGeometry;
986  getPartGeometry = [&pointsToRender, &linesToRender, &polygonsToRender, &getPartGeometry, &context, &clippingEnabled, &markers, &feature, &usingSegmentizedGeometry, this]( const QgsAbstractGeometry * part )
987  {
988  Q_UNUSED( feature )
989 
990  if ( !part )
991  return;
992 
993  // geometry preprocessing
994  QgsGeometry temporaryGeometryContainer;
995  const QgsAbstractGeometry *processedGeometry = nullptr;
996 
997  const bool isMultiPart = qgsgeometry_cast< const QgsGeometryCollection * >( part ) && qgsgeometry_cast< const QgsGeometryCollection * >( part )->numGeometries() > 1;
998 
999  if ( !isMultiPart )
1000  {
1001  // segmentize curved geometries
1002  const bool needsSegmentizing = QgsWkbTypes::isCurvedType( part->wkbType() ) || part->hasCurvedSegments();
1003  if ( needsSegmentizing )
1004  {
1005  std::unique_ptr< QgsAbstractGeometry > segmentizedPart( part->segmentize( context.segmentationTolerance(), context.segmentationToleranceType() ) );
1006  if ( !segmentizedPart )
1007  {
1008  return;
1009  }
1010  temporaryGeometryContainer.set( segmentizedPart.release() );
1011  processedGeometry = temporaryGeometryContainer.constGet();
1012  usingSegmentizedGeometry = true;
1013  }
1014  else
1015  {
1016  // no segmentation required
1017  processedGeometry = part;
1018  }
1019 
1020  // Simplify the geometry, if needed.
1021  if ( context.vectorSimplifyMethod().forceLocalOptimization() )
1022  {
1023  const int simplifyHints = context.vectorSimplifyMethod().simplifyHints();
1024  const QgsMapToPixelSimplifier simplifier( simplifyHints, context.vectorSimplifyMethod().tolerance(),
1026 
1027  std::unique_ptr< QgsAbstractGeometry > simplified( simplifier.simplify( processedGeometry ) );
1028  if ( simplified )
1029  {
1030  temporaryGeometryContainer.set( simplified.release() );
1031  processedGeometry = temporaryGeometryContainer.constGet();
1032  }
1033  }
1034 
1035  // clip geometry to render context clipping regions
1036  if ( !context.featureClipGeometry().isEmpty() )
1037  {
1038  // apply feature clipping from context to the rendered geometry only -- just like the render time simplification,
1039  // we should NEVER apply this to the geometry attached to the feature itself. Doing so causes issues with certain
1040  // renderer settings, e.g. if polygons are being rendered using a rule based renderer based on the feature's area,
1041  // then we need to ensure that the original feature area is used instead of the clipped area..
1042  QgsGeos geos( processedGeometry );
1043  std::unique_ptr< QgsAbstractGeometry > clippedGeom( geos.intersection( context.featureClipGeometry().constGet() ) );
1044  if ( clippedGeom )
1045  {
1046  temporaryGeometryContainer.set( clippedGeom.release() );
1047  processedGeometry = temporaryGeometryContainer.constGet();
1048  }
1049  }
1050  }
1051  else
1052  {
1053  // for multipart geometries, the processing is deferred till we're rendering the actual part...
1054  processedGeometry = part;
1055  }
1056 
1057  if ( !processedGeometry )
1058  {
1059  // shouldn't happen!
1060  QgsDebugMsg( QStringLiteral( "No processed geometry to render for part!" ) );
1061  return;
1062  }
1063 
1064  switch ( QgsWkbTypes::flatType( processedGeometry->wkbType() ) )
1065  {
1066  case QgsWkbTypes::Point:
1067  {
1068  if ( mType != QgsSymbol::Marker )
1069  {
1070  QgsDebugMsgLevel( QStringLiteral( "point can be drawn only with marker symbol!" ), 2 );
1071  break;
1072  }
1073 
1074  PointInfo info;
1075  info.originalGeometry = qgsgeometry_cast< const QgsPoint * >( part );
1076  info.renderPoint = _getPoint( context, *info.originalGeometry );
1077  pointsToRender << info;
1078  break;
1079  }
1080 
1082  {
1083  if ( mType != QgsSymbol::Line )
1084  {
1085  QgsDebugMsgLevel( QStringLiteral( "linestring can be drawn only with line symbol!" ), 2 );
1086  break;
1087  }
1088 
1089  LineInfo info;
1090  info.originalGeometry = qgsgeometry_cast<const QgsCurve *>( part );
1091  info.renderLine = _getLineString( context, *qgsgeometry_cast<const QgsCurve *>( processedGeometry ), clippingEnabled );
1092  linesToRender << info;
1093  break;
1094  }
1095 
1096  case QgsWkbTypes::Polygon:
1097  case QgsWkbTypes::Triangle:
1098  {
1099  QPolygonF pts;
1100  if ( mType != QgsSymbol::Fill )
1101  {
1102  QgsDebugMsgLevel( QStringLiteral( "polygon can be drawn only with fill symbol!" ), 2 );
1103  break;
1104  }
1105 
1106  PolygonInfo info;
1107  info.originalGeometry = qgsgeometry_cast<const QgsCurvePolygon *>( part );
1108  if ( !qgsgeometry_cast<const QgsPolygon *>( processedGeometry )->exteriorRing() )
1109  {
1110  QgsDebugMsg( QStringLiteral( "cannot render polygon with no exterior ring" ) );
1111  break;
1112  }
1113 
1114  _getPolygon( info.renderExterior, info.renderRings, context, *qgsgeometry_cast<const QgsPolygon *>( processedGeometry ), clippingEnabled, mForceRHR );
1115  polygonsToRender << info;
1116  break;
1117  }
1118 
1120  {
1121  const QgsMultiPoint *mp = qgsgeometry_cast< const QgsMultiPoint * >( part );
1122  markers.reserve( mp->numGeometries() );
1123  }
1124  FALLTHROUGH
1128  {
1129  const QgsGeometryCollection *geomCollection = qgsgeometry_cast<const QgsGeometryCollection *>( part );
1130 
1131  const unsigned int num = geomCollection->numGeometries();
1132  for ( unsigned int i = 0; i < num; ++i )
1133  {
1134  if ( context.renderingStopped() )
1135  break;
1136 
1137  getPartGeometry( geomCollection->geometryN( i ) );
1138  }
1139  break;
1140  }
1141 
1144  {
1145  if ( mType != QgsSymbol::Fill )
1146  {
1147  QgsDebugMsgLevel( QStringLiteral( "multi-polygon can be drawn only with fill symbol!" ), 2 );
1148  break;
1149  }
1150 
1151  QPolygonF pts;
1152 
1153  const QgsGeometryCollection *geomCollection = dynamic_cast<const QgsGeometryCollection *>( part );
1154  const unsigned int num = geomCollection->numGeometries();
1155 
1156  // Sort components by approximate area (probably a bit faster than using
1157  // area() )
1158  std::map<double, QList<unsigned int> > thisAreaToPartNum;
1159  for ( unsigned int i = 0; i < num; ++i )
1160  {
1161  const QgsRectangle r( geomCollection->geometryN( i )->boundingBox() );
1162  thisAreaToPartNum[ r.width() * r.height()] << i;
1163  }
1164 
1165  // Draw starting with larger parts down to smaller parts, so that in
1166  // case of a part being incorrectly inside another part, it is drawn
1167  // on top of it (#15419)
1168  std::map<double, QList<unsigned int> >::const_reverse_iterator iter = thisAreaToPartNum.rbegin();
1169  for ( ; iter != thisAreaToPartNum.rend(); ++iter )
1170  {
1171  const QList<unsigned int> &listPartIndex = iter->second;
1172  for ( int idx = 0; idx < listPartIndex.size(); ++idx )
1173  {
1174  const unsigned i = listPartIndex[idx];
1175  getPartGeometry( geomCollection->geometryN( i ) );
1176  }
1177  }
1178  break;
1179  }
1180 
1181  default:
1182  QgsDebugMsg( QStringLiteral( "feature %1: unsupported wkb type %2/%3 for rendering" )
1183  .arg( feature.id() )
1184  .arg( QgsWkbTypes::displayString( part->wkbType() ) )
1185  .arg( part->wkbType(), 0, 16 ) );
1186  }
1187  };
1188 
1189  getPartGeometry( geom.constGet() );
1190 
1191  // step 2 - determine which layers to render
1192  std::vector< int > layers;
1193  if ( layer == -1 )
1194  {
1195  layers.reserve( mLayers.count() );
1196  for ( int i = 0; i < mLayers.count(); ++i )
1197  layers.emplace_back( i );
1198  }
1199  else
1200  {
1201  layers.emplace_back( layer );
1202  }
1203 
1204  // step 3 - render these geometries using the desired symbol layers.
1205 
1206  if ( needsExpressionContext )
1207  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_count" ), mLayers.count(), true ) );
1208 
1209  for ( const int symbolLayerIndex : layers )
1210  {
1211  QgsSymbolLayer *symbolLayer = mLayers.value( symbolLayerIndex );
1212  if ( !symbolLayer || !symbolLayer->enabled() )
1213  continue;
1214 
1215  if ( needsExpressionContext )
1216  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_layer_index" ), symbolLayerIndex + 1, true ) );
1217 
1218  symbolLayer->startFeatureRender( feature, context );
1219 
1220  switch ( mType )
1221  {
1222  case QgsSymbol::Marker:
1223  {
1224  int geometryPartNumber = 0;
1225  for ( const PointInfo &point : qgis::as_const( pointsToRender ) )
1226  {
1227  if ( context.renderingStopped() )
1228  break;
1229 
1230  mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1231  if ( needsExpressionContext )
1232  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1233 
1234  static_cast<QgsMarkerSymbol *>( this )->renderPoint( point.renderPoint, &feature, context, symbolLayerIndex, selected );
1235  geometryPartNumber++;
1236  }
1237 
1238  break;
1239  }
1240 
1241  case QgsSymbol::Line:
1242  {
1243  if ( linesToRender.empty() )
1244  break;
1245 
1246  int geometryPartNumber = 0;
1247  for ( const LineInfo &line : qgis::as_const( linesToRender ) )
1248  {
1249  if ( context.renderingStopped() )
1250  break;
1251 
1252  mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1253  if ( needsExpressionContext )
1254  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1255 
1256  context.setGeometry( line.originalGeometry );
1257  static_cast<QgsLineSymbol *>( this )->renderPolyline( line.renderLine, &feature, context, symbolLayerIndex, selected );
1258  geometryPartNumber++;
1259  }
1260  break;
1261  }
1262 
1263  case QgsSymbol::Fill:
1264  {
1265  int geometryPartNumber = 0;
1266  for ( const PolygonInfo &info : qgis::as_const( polygonsToRender ) )
1267  {
1268  if ( context.renderingStopped() )
1269  break;
1270 
1271  mSymbolRenderContext->setGeometryPartNum( geometryPartNumber + 1 );
1272  if ( needsExpressionContext )
1273  mSymbolRenderContext->expressionContextScope()->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_GEOMETRY_PART_NUM, geometryPartNumber + 1, true ) );
1274 
1275  context.setGeometry( info.originalGeometry );
1276  static_cast<QgsFillSymbol *>( this )->renderPolygon( info.renderExterior, ( !info.renderRings.isEmpty() ? &info.renderRings : nullptr ), &feature, context, symbolLayerIndex, selected );
1277  geometryPartNumber++;
1278  }
1279 
1280  break;
1281  }
1282 
1283  case QgsSymbol::Hybrid:
1284  break;
1285  }
1286 
1287  symbolLayer->stopFeatureRender( feature, context );
1288  }
1289 
1290  // step 4 - handle post processing steps
1291  switch ( mType )
1292  {
1293  case QgsSymbol::Marker:
1294  {
1295  markers.reserve( pointsToRender.size() );
1296  for ( const PointInfo &info : qgis::as_const( pointsToRender ) )
1297  {
1299  {
1300  const QRectF bounds = static_cast<QgsMarkerSymbol *>( this )->bounds( info.renderPoint, context, feature );
1301  if ( context.hasRenderedFeatureHandlers() )
1302  {
1303  renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromRect( bounds )
1304  : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromRect( QgsRectangle( bounds ) ) << renderedBoundsGeom );
1305  }
1307  {
1308  //draw debugging rect
1309  context.painter()->setPen( Qt::red );
1310  context.painter()->setBrush( QColor( 255, 0, 0, 100 ) );
1311  context.painter()->drawRect( bounds );
1312  }
1313  }
1314 
1315  if ( drawVertexMarker && !usingSegmentizedGeometry )
1316  {
1317  markers.append( info.renderPoint );
1318  }
1319  }
1320  break;
1321  }
1322 
1323  case QgsSymbol::Line:
1324  {
1325  for ( const LineInfo &info : qgis::as_const( linesToRender ) )
1326  {
1327  if ( context.hasRenderedFeatureHandlers() && !info.renderLine.empty() )
1328  {
1329  renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderLine )
1330  : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderLine ) << renderedBoundsGeom );
1331  }
1332 
1333  if ( drawVertexMarker && !usingSegmentizedGeometry )
1334  {
1335  markers << info.renderLine;
1336  }
1337  }
1338  break;
1339  }
1340 
1341  case QgsSymbol::Fill:
1342  {
1343  int i = 0;
1344  for ( const PolygonInfo &info : qgis::as_const( polygonsToRender ) )
1345  {
1346  if ( context.hasRenderedFeatureHandlers() && !info.renderExterior.empty() )
1347  {
1348  renderedBoundsGeom = renderedBoundsGeom.isNull() ? QgsGeometry::fromQPolygonF( info.renderExterior )
1349  : QgsGeometry::collectGeometry( QVector< QgsGeometry>() << QgsGeometry::fromQPolygonF( info.renderExterior ) << renderedBoundsGeom );
1350  // TODO: consider holes?
1351  }
1352 
1353  if ( drawVertexMarker && !usingSegmentizedGeometry )
1354  {
1355  markers << info.renderExterior;
1356 
1357  for ( const QPolygonF &hole : info.renderRings )
1358  {
1359  markers << hole;
1360  }
1361  }
1362  i++;
1363  }
1364  break;
1365  }
1366 
1367  case QgsSymbol::Hybrid:
1368  break;
1369  }
1370 
1371  if ( context.hasRenderedFeatureHandlers() && !renderedBoundsGeom.isNull() )
1372  {
1374  const QList< QgsRenderedFeatureHandlerInterface * > handlers = context.renderedFeatureHandlers();
1375  for ( QgsRenderedFeatureHandlerInterface *handler : handlers )
1376  handler->handleRenderedFeature( feature, renderedBoundsGeom, featureContext );
1377  }
1378 
1379  if ( drawVertexMarker )
1380  {
1381  if ( !markers.isEmpty() && !context.renderingStopped() )
1382  {
1383  const auto constMarkers = markers;
1384  for ( QPointF marker : constMarkers )
1385  {
1386  renderVertexMarker( marker, context, currentVertexMarkerType, currentVertexMarkerSize );
1387  }
1388  }
1389  else
1390  {
1392  const QgsMapToPixel &mtp = context.mapToPixel();
1393 
1394  QgsPoint vertexPoint;
1395  QgsVertexId vertexId;
1396  double x, y, z;
1397  QPointF mapPoint;
1398  while ( geom.constGet()->nextVertex( vertexId, vertexPoint ) )
1399  {
1400  //transform
1401  x = vertexPoint.x();
1402  y = vertexPoint.y();
1403  z = 0.0;
1404  if ( ct.isValid() )
1405  {
1406  ct.transformInPlace( x, y, z );
1407  }
1408  mapPoint.setX( x );
1409  mapPoint.setY( y );
1410  mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() );
1411  renderVertexMarker( mapPoint, context, currentVertexMarkerType, currentVertexMarkerSize );
1412  }
1413  }
1414  }
1415 }
1416 
1418 {
1419  return mSymbolRenderContext.get();
1420 }
1421 
1422 void QgsSymbol::renderVertexMarker( QPointF pt, QgsRenderContext &context, int currentVertexMarkerType, double currentVertexMarkerSize )
1423 {
1424  int markerSize = context.convertToPainterUnits( currentVertexMarkerSize, QgsUnitTypes::RenderMillimeters );
1425  QgsSymbolLayerUtils::drawVertexMarker( pt.x(), pt.y(), *context.painter(), static_cast< QgsSymbolLayerUtils::VertexMarkerType >( currentVertexMarkerType ), markerSize );
1426 }
1427 
1428 void QgsSymbol::initPropertyDefinitions()
1429 {
1430  if ( !sPropertyDefinitions.isEmpty() )
1431  return;
1432 
1433  QString origin = QStringLiteral( "symbol" );
1434 
1435  sPropertyDefinitions = QgsPropertiesDefinition
1436  {
1437  { QgsSymbol::PropertyOpacity, QgsPropertyDefinition( "alpha", QObject::tr( "Opacity" ), QgsPropertyDefinition::Opacity, origin )},
1438  };
1439 }
1440 
1441 void QgsSymbol::startFeatureRender( const QgsFeature &feature, QgsRenderContext &context, const int layer )
1442 {
1443  if ( layer != -1 )
1444  {
1446  if ( symbolLayer && symbolLayer->enabled() )
1447  {
1448  symbolLayer->startFeatureRender( feature, context );
1449  }
1450  return;
1451  }
1452  else
1453  {
1454  const QList< QgsSymbolLayer * > layers = mLayers;
1455  for ( QgsSymbolLayer *symbolLayer : layers )
1456  {
1457  if ( !symbolLayer->enabled() )
1458  continue;
1459 
1460  symbolLayer->startFeatureRender( feature, context );
1461  }
1462  }
1463 }
1464 
1465 void QgsSymbol::stopFeatureRender( const QgsFeature &feature, QgsRenderContext &context, int layer )
1466 {
1467  if ( layer != -1 )
1468  {
1470  if ( symbolLayer && symbolLayer->enabled() )
1471  {
1472  symbolLayer->stopFeatureRender( feature, context );
1473  }
1474  return;
1475  }
1476  else
1477  {
1478  const QList< QgsSymbolLayer * > layers = mLayers;
1479  for ( QgsSymbolLayer *symbolLayer : layers )
1480  {
1481  if ( !symbolLayer->enabled() )
1482  continue;
1483 
1484  symbolLayer->stopFeatureRender( feature, context );
1485  }
1486  }
1487 }
1488 
1490 
1491 
1492 QgsSymbolRenderContext::QgsSymbolRenderContext( QgsRenderContext &c, QgsUnitTypes::RenderUnit u, qreal opacity, bool selected, QgsSymbol::RenderHints renderHints, const QgsFeature *f, const QgsFields &fields, const QgsMapUnitScale &mapUnitScale )
1493  : mRenderContext( c )
1494  , mOutputUnit( u )
1495  , mMapUnitScale( mapUnitScale )
1496  , mOpacity( opacity )
1497  , mSelected( selected )
1498  , mRenderHints( renderHints )
1499  , mFeature( f )
1500  , mFields( fields )
1501  , mGeometryPartCount( 0 )
1502  , mGeometryPartNum( 0 )
1503 {
1504 }
1505 
1507 
1509 {
1510  mRenderContext.expressionContext().setOriginalValueVariable( value );
1511 }
1512 
1513 double QgsSymbolRenderContext::outputLineWidth( double width ) const
1514 {
1515  return mRenderContext.convertToPainterUnits( width, mOutputUnit, mMapUnitScale );
1516 }
1517 
1518 double QgsSymbolRenderContext::outputPixelSize( double size ) const
1519 {
1520  return mRenderContext.convertToPainterUnits( size, mOutputUnit, mMapUnitScale );
1521 }
1522 
1523 // cppcheck-suppress operatorEqVarError
1525 {
1526  // This is just a dummy implementation of assignment.
1527  // sip 4.7 generates a piece of code that needs this function to exist.
1528  // It's not generated automatically by the compiler because of
1529  // mRenderContext member which is a reference (and thus can't be changed).
1530  Q_ASSERT( false );
1531  return *this;
1532 }
1533 
1535 {
1536  return mExpressionContextScope.get();
1537 }
1538 
1540 {
1541  mExpressionContextScope.reset( contextScope );
1542 }
1543 
1545 {
1546  return mPatchShape.get();
1547 }
1548 
1550 {
1551  mPatchShape.reset( new QgsLegendPatchShape( patchShape ) );
1552 }
1553 
1555 
1556 QgsMarkerSymbol *QgsMarkerSymbol::createSimple( const QVariantMap &properties )
1557 {
1559  if ( !sl )
1560  return nullptr;
1561 
1562  QgsSymbolLayerList layers;
1563  layers.append( sl );
1564  return new QgsMarkerSymbol( layers );
1565 }
1566 
1567 QgsLineSymbol *QgsLineSymbol::createSimple( const QVariantMap &properties )
1568 {
1570  if ( !sl )
1571  return nullptr;
1572 
1573  QgsSymbolLayerList layers;
1574  layers.append( sl );
1575  return new QgsLineSymbol( layers );
1576 }
1577 
1578 QgsFillSymbol *QgsFillSymbol::createSimple( const QVariantMap &properties )
1579 {
1581  if ( !sl )
1582  return nullptr;
1583 
1584  QgsSymbolLayerList layers;
1585  layers.append( sl );
1586  return new QgsFillSymbol( layers );
1587 }
1588 
1590 
1592  : QgsSymbol( Marker, layers )
1593 {
1594  if ( mLayers.isEmpty() )
1595  mLayers.append( new QgsSimpleMarkerSymbolLayer() );
1596 }
1597 
1598 void QgsMarkerSymbol::setAngle( double symbolAngle )
1599 {
1600  double origAngle = angle();
1601  double angleDiff = symbolAngle - origAngle;
1602  const auto constMLayers = mLayers;
1603  for ( QgsSymbolLayer *layer : constMLayers )
1604  {
1605  QgsMarkerSymbolLayer *markerLayer = dynamic_cast<QgsMarkerSymbolLayer *>( layer );
1606  if ( markerLayer )
1607  markerLayer->setAngle( markerLayer->angle() + angleDiff );
1608  }
1609 }
1610 
1612 {
1613  for ( QgsSymbolLayer *layer : qgis::as_const( mLayers ) )
1614  {
1615  if ( layer->type() != QgsSymbol::Marker )
1616  continue;
1617  const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
1618  return markerLayer->angle();
1619  }
1620  return 0;
1621 }
1622 
1623 void QgsMarkerSymbol::setLineAngle( double lineAng )
1624 {
1625  const auto constMLayers = mLayers;
1626  for ( QgsSymbolLayer *layer : constMLayers )
1627  {
1628  if ( layer->type() != QgsSymbol::Marker )
1629  continue;
1630  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1631  markerLayer->setLineAngle( lineAng );
1632  }
1633 }
1634 
1636 {
1637  const double symbolRotation = angle();
1638 
1639 
1640  for ( QgsSymbolLayer *layer : qgis::as_const( mLayers ) )
1641  {
1642  if ( layer->type() != QgsSymbol::Marker )
1643  continue;
1644  const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
1645  if ( !property )
1646  {
1647  layer->setDataDefinedProperty( QgsSymbolLayer::PropertyAngle, QgsProperty() );
1648  }
1649  else
1650  {
1651  if ( qgsDoubleNear( markerLayer->angle(), symbolRotation ) )
1652  {
1653  layer->setDataDefinedProperty( QgsSymbolLayer::PropertyAngle, property );
1654  }
1655  else
1656  {
1657  QgsProperty rotatedDD = rotateWholeSymbol( markerLayer->angle() - symbolRotation, property );
1658  layer->setDataDefinedProperty( QgsSymbolLayer::PropertyAngle, rotatedDD );
1659  }
1660  }
1661  }
1662 }
1663 
1665 {
1666  const double symbolRotation = angle();
1667  QgsProperty symbolDD;
1668 
1669  // find the base of the "en masse" pattern
1670  const auto layers = mLayers;
1671  for ( QgsSymbolLayer *layer : layers )
1672  {
1673  if ( layer->type() != QgsSymbol::Marker )
1674  continue;
1675  const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
1676  if ( qgsDoubleNear( markerLayer->angle(), symbolRotation ) && markerLayer->dataDefinedProperties().isActive( QgsSymbolLayer::PropertyAngle ) )
1677  {
1678  symbolDD = markerLayer->dataDefinedProperties().property( QgsSymbolLayer::PropertyAngle );
1679  break;
1680  }
1681  }
1682 
1683  if ( !symbolDD )
1684  return QgsProperty();
1685 
1686  // check that all layer's angle expressions match the "en masse" pattern
1687  for ( QgsSymbolLayer *layer : layers )
1688  {
1689  if ( layer->type() != QgsSymbol::Marker )
1690  continue;
1691  const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
1692 
1694 
1695  if ( qgsDoubleNear( markerLayer->angle(), symbolRotation ) )
1696  {
1697  if ( !layerAngleDD || layerAngleDD != symbolDD )
1698  return QgsProperty();
1699  }
1700  else
1701  {
1702  QgsProperty rotatedDD( rotateWholeSymbol( markerLayer->angle() - symbolRotation, symbolDD ) );
1703  if ( !layerAngleDD || layerAngleDD != rotatedDD )
1704  return QgsProperty();
1705  }
1706  }
1707  return symbolDD;
1708 }
1709 
1710 
1712 {
1713  double origSize = size();
1714 
1715  const auto constMLayers = mLayers;
1716  for ( QgsSymbolLayer *layer : constMLayers )
1717  {
1718  if ( layer->type() != QgsSymbol::Marker )
1719  continue;
1720  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1721  if ( qgsDoubleNear( markerLayer->size(), origSize ) )
1722  markerLayer->setSize( s );
1723  else if ( !qgsDoubleNear( origSize, 0.0 ) )
1724  {
1725  // proportionally scale size
1726  markerLayer->setSize( markerLayer->size() * s / origSize );
1727  }
1728  // also scale offset to maintain relative position
1729  if ( !qgsDoubleNear( origSize, 0.0 ) && ( !qgsDoubleNear( markerLayer->offset().x(), 0.0 ) || !qgsDoubleNear( markerLayer->offset().y(), 0.0 ) ) )
1730  markerLayer->setOffset( QPointF( markerLayer->offset().x() * s / origSize,
1731  markerLayer->offset().y() * s / origSize ) );
1732  }
1733 }
1734 
1736 {
1737  // return size of the largest symbol
1738  double maxSize = 0;
1739  const auto constMLayers = mLayers;
1740  for ( QgsSymbolLayer *layer : constMLayers )
1741  {
1742  if ( layer->type() != QgsSymbol::Marker )
1743  continue;
1744  const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
1745  double lsize = markerLayer->size();
1746  if ( lsize > maxSize )
1747  maxSize = lsize;
1748  }
1749  return maxSize;
1750 }
1751 
1752 double QgsMarkerSymbol::size( const QgsRenderContext &context ) const
1753 {
1754  // return size of the largest symbol
1755  double maxSize = 0;
1756  for ( QgsSymbolLayer *layer : mLayers )
1757  {
1758  if ( layer->type() != QgsSymbol::Marker )
1759  continue;
1760  const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
1761  const double layerSize = context.convertToPainterUnits( markerLayer->size(), markerLayer->sizeUnit(), markerLayer->sizeMapUnitScale() );
1762  maxSize = std::max( maxSize, layerSize );
1763  }
1764  return maxSize;
1765 }
1766 
1768 {
1769  const auto constMLayers = mLayers;
1770  for ( QgsSymbolLayer *layer : constMLayers )
1771  {
1772  if ( layer->type() != QgsSymbol::Marker )
1773  continue;
1774 
1775  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1776  markerLayer->setSizeUnit( unit );
1777  }
1778 }
1779 
1781 {
1782  bool first = true;
1784 
1785  const auto constMLayers = mLayers;
1786  for ( QgsSymbolLayer *layer : constMLayers )
1787  {
1788  if ( layer->type() != QgsSymbol::Marker )
1789  continue;
1790  const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
1791 
1792  if ( first )
1793  unit = markerLayer->sizeUnit();
1794  else
1795  {
1796  if ( unit != markerLayer->sizeUnit() )
1798  }
1799 
1800  first = false;
1801  }
1802  return unit;
1803 }
1804 
1806 {
1807  const auto constMLayers = mLayers;
1808  for ( QgsSymbolLayer *layer : constMLayers )
1809  {
1810  if ( layer->type() != QgsSymbol::Marker )
1811  continue;
1812 
1813  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1814  markerLayer->setSizeMapUnitScale( scale );
1815  }
1816 }
1817 
1819 {
1820  const auto constMLayers = mLayers;
1821  for ( QgsSymbolLayer *layer : constMLayers )
1822  {
1823  if ( layer->type() != QgsSymbol::Marker )
1824  continue;
1825 
1826  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1827  return markerLayer->sizeMapUnitScale();
1828  }
1829  return QgsMapUnitScale();
1830 }
1831 
1833 {
1834  const double symbolSize = size();
1835 
1836  const auto constMLayers = mLayers;
1837  for ( QgsSymbolLayer *layer : constMLayers )
1838  {
1839  if ( layer->type() != QgsSymbol::Marker )
1840  continue;
1841  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1842 
1843  if ( !property )
1844  {
1847  }
1848  else
1849  {
1850  if ( qgsDoubleNear( symbolSize, 0.0 ) || qgsDoubleNear( markerLayer->size(), symbolSize ) )
1851  {
1852  markerLayer->setDataDefinedProperty( QgsSymbolLayer::PropertySize, property );
1853  }
1854  else
1855  {
1856  markerLayer->setDataDefinedProperty( QgsSymbolLayer::PropertySize, scaleWholeSymbol( markerLayer->size() / symbolSize, property ) );
1857  }
1858 
1859  if ( !qgsDoubleNear( markerLayer->offset().x(), 0.0 ) || !qgsDoubleNear( markerLayer->offset().y(), 0.0 ) )
1860  {
1862  markerLayer->offset().x() / symbolSize,
1863  markerLayer->offset().y() / symbolSize, property ) );
1864  }
1865  }
1866  }
1867 }
1868 
1870 {
1871  const double symbolSize = size();
1872 
1873  QgsProperty symbolDD;
1874 
1875  // find the base of the "en masse" pattern
1876  const auto layers = mLayers;
1877  for ( QgsSymbolLayer *layer : layers )
1878  {
1879  if ( layer->type() != QgsSymbol::Marker )
1880  continue;
1881  const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
1882  if ( qgsDoubleNear( markerLayer->size(), symbolSize ) && markerLayer->dataDefinedProperties().isActive( QgsSymbolLayer::PropertySize ) )
1883  {
1884  symbolDD = markerLayer->dataDefinedProperties().property( QgsSymbolLayer::PropertySize );
1885  break;
1886  }
1887  }
1888 
1889  if ( !symbolDD )
1890  return QgsProperty();
1891 
1892  // check that all layers size expressions match the "en masse" pattern
1893  for ( QgsSymbolLayer *layer : layers )
1894  {
1895  if ( layer->type() != QgsSymbol::Marker )
1896  continue;
1897  const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
1898 
1901 
1902  if ( qgsDoubleNear( markerLayer->size(), symbolSize ) )
1903  {
1904  if ( !layerSizeDD || layerSizeDD != symbolDD )
1905  return QgsProperty();
1906  }
1907  else
1908  {
1909  if ( qgsDoubleNear( symbolSize, 0.0 ) )
1910  return QgsProperty();
1911 
1912  QgsProperty scaledDD( scaleWholeSymbol( markerLayer->size() / symbolSize, symbolDD ) );
1913  if ( !layerSizeDD || layerSizeDD != scaledDD )
1914  return QgsProperty();
1915  }
1916 
1917  QgsProperty scaledOffsetDD( scaleWholeSymbol( markerLayer->offset().x() / symbolSize, markerLayer->offset().y() / symbolSize, symbolDD ) );
1918  if ( layerOffsetDD && layerOffsetDD != scaledOffsetDD )
1919  return QgsProperty();
1920  }
1921 
1922  return symbolDD;
1923 }
1924 
1926 {
1927  const auto constMLayers = mLayers;
1928  for ( QgsSymbolLayer *layer : constMLayers )
1929  {
1930  if ( layer->type() != QgsSymbol::Marker )
1931  continue;
1932  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( layer );
1933  markerLayer->setScaleMethod( scaleMethod );
1934  }
1935 }
1936 
1938 {
1939  const auto constMLayers = mLayers;
1940  for ( QgsSymbolLayer *layer : constMLayers )
1941  {
1942  if ( layer->type() != QgsSymbol::Marker )
1943  continue;
1944  const QgsMarkerSymbolLayer *markerLayer = static_cast<const QgsMarkerSymbolLayer *>( layer );
1945  // return scale method of the first symbol layer
1946  return markerLayer->scaleMethod();
1947  }
1948 
1949  return DEFAULT_SCALE_METHOD;
1950 }
1951 
1952 void QgsMarkerSymbol::renderPointUsingLayer( QgsMarkerSymbolLayer *layer, QPointF point, QgsSymbolRenderContext &context )
1953 {
1954  static QPointF nullPoint( 0, 0 );
1955 
1956  if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
1957  return;
1958 
1959  QgsPaintEffect *effect = layer->paintEffect();
1960  if ( effect && effect->enabled() )
1961  {
1962  QgsEffectPainter p( context.renderContext() );
1963  p->translate( point );
1964  p.setEffect( effect );
1965  layer->renderPoint( nullPoint, context );
1966  }
1967  else
1968  {
1969  layer->renderPoint( point, context );
1970  }
1971 }
1972 
1973 void QgsMarkerSymbol::renderPoint( QPointF point, const QgsFeature *f, QgsRenderContext &context, int layerIdx, bool selected )
1974 {
1976 
1977  QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, opacity, selected, mRenderHints, f );
1978  symbolContext.setGeometryPartCount( symbolRenderContext()->geometryPartCount() );
1979  symbolContext.setGeometryPartNum( symbolRenderContext()->geometryPartNum() );
1980 
1981  if ( layerIdx != -1 )
1982  {
1983  QgsSymbolLayer *symbolLayer = mLayers.value( layerIdx );
1985  {
1986  if ( symbolLayer->type() == QgsSymbol::Marker )
1987  {
1988  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( symbolLayer );
1989  renderPointUsingLayer( markerLayer, point, symbolContext );
1990  }
1991  else
1992  renderUsingLayer( symbolLayer, symbolContext );
1993  }
1994  return;
1995  }
1996 
1997 
1998  for ( QgsSymbolLayer *symbolLayer : qgis::as_const( mLayers ) )
1999  {
2000  if ( context.renderingStopped() )
2001  break;
2002 
2003  if ( !symbolLayer->enabled() || !context.isSymbolLayerEnabled( symbolLayer ) )
2004  continue;
2005 
2006  if ( symbolLayer->type() == QgsSymbol::Marker )
2007  {
2008  QgsMarkerSymbolLayer *markerLayer = static_cast<QgsMarkerSymbolLayer *>( symbolLayer );
2009  renderPointUsingLayer( markerLayer, point, symbolContext );
2010  }
2011  else
2012  renderUsingLayer( symbolLayer, symbolContext );
2013  }
2014 }
2015 
2016 QRectF QgsMarkerSymbol::bounds( QPointF point, QgsRenderContext &context, const QgsFeature &feature ) const
2017 {
2018  QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, mOpacity, false, mRenderHints, &feature, feature.fields() );
2019 
2020  QRectF bound;
2021  const auto constMLayers = mLayers;
2022  for ( QgsSymbolLayer *layer : constMLayers )
2023  {
2024  if ( layer->type() == QgsSymbol::Marker )
2025  {
2027  if ( bound.isNull() )
2028  bound = symbolLayer->bounds( point, symbolContext );
2029  else
2030  bound = bound.united( symbolLayer->bounds( point, symbolContext ) );
2031  }
2032  }
2033  return bound;
2034 }
2035 
2037 {
2038  QgsMarkerSymbol *cloneSymbol = new QgsMarkerSymbol( cloneLayers() );
2039  cloneSymbol->setOpacity( mOpacity );
2041  cloneSymbol->setLayer( mLayer );
2044  cloneSymbol->setForceRHR( mForceRHR );
2046  return cloneSymbol;
2047 }
2048 
2049 
2051 // LINE
2052 
2054  : QgsSymbol( Line, layers )
2055 {
2056  if ( mLayers.isEmpty() )
2057  mLayers.append( new QgsSimpleLineSymbolLayer() );
2058 }
2059 
2060 void QgsLineSymbol::setWidth( double w )
2061 {
2062  double origWidth = width();
2063 
2064  const auto constMLayers = mLayers;
2065  for ( QgsSymbolLayer *layer : constMLayers )
2066  {
2067  QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( layer );
2068 
2069  if ( lineLayer )
2070  {
2071  if ( qgsDoubleNear( lineLayer->width(), origWidth ) )
2072  {
2073  lineLayer->setWidth( w );
2074  }
2075  else if ( !qgsDoubleNear( origWidth, 0.0 ) )
2076  {
2077  // proportionally scale the width
2078  lineLayer->setWidth( lineLayer->width() * w / origWidth );
2079  }
2080  // also scale offset to maintain relative position
2081  if ( !qgsDoubleNear( origWidth, 0.0 ) && !qgsDoubleNear( lineLayer->offset(), 0.0 ) )
2082  lineLayer->setOffset( lineLayer->offset() * w / origWidth );
2083  }
2084  }
2085 }
2086 
2088 {
2089  const auto constLLayers = mLayers;
2090  for ( QgsSymbolLayer *layer : constLLayers )
2091  {
2092  if ( layer->type() != QgsSymbol::Line )
2093  continue;
2094 
2095  QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( layer );
2096  lineLayer->setWidthUnit( unit );
2097  }
2098 }
2099 
2100 double QgsLineSymbol::width() const
2101 {
2102  double maxWidth = 0;
2103  if ( mLayers.isEmpty() )
2104  return maxWidth;
2105 
2106  const auto constMLayers = mLayers;
2107  for ( QgsSymbolLayer *symbolLayer : constMLayers )
2108  {
2109  const QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( symbolLayer );
2110  if ( lineLayer )
2111  {
2112  double width = lineLayer->width();
2113  if ( width > maxWidth )
2114  maxWidth = width;
2115  }
2116  }
2117  return maxWidth;
2118 }
2119 
2120 double QgsLineSymbol::width( const QgsRenderContext &context ) const
2121 {
2122  // return width of the largest symbol
2123  double maxWidth = 0;
2124  for ( QgsSymbolLayer *layer : mLayers )
2125  {
2126  if ( layer->type() != QgsSymbol::Line )
2127  continue;
2128  const QgsLineSymbolLayer *lineLayer = static_cast<const QgsLineSymbolLayer *>( layer );
2129  const double layerWidth = lineLayer->width( context );
2130  maxWidth = std::max( maxWidth, layerWidth );
2131  }
2132  return maxWidth;
2133 }
2134 
2136 {
2137  const double symbolWidth = width();
2138 
2139  const auto constMLayers = mLayers;
2140  for ( QgsSymbolLayer *layer : constMLayers )
2141  {
2142  QgsLineSymbolLayer *lineLayer = dynamic_cast<QgsLineSymbolLayer *>( layer );
2143 
2144  if ( lineLayer )
2145  {
2146  if ( !property )
2147  {
2150  }
2151  else
2152  {
2153  if ( qgsDoubleNear( symbolWidth, 0.0 ) || qgsDoubleNear( lineLayer->width(), symbolWidth ) )
2154  {
2156  }
2157  else
2158  {
2159  lineLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyStrokeWidth, scaleWholeSymbol( lineLayer->width() / symbolWidth, property ) );
2160  }
2161 
2162  if ( !qgsDoubleNear( lineLayer->offset(), 0.0 ) )
2163  {
2164  lineLayer->setDataDefinedProperty( QgsSymbolLayer::PropertyOffset, scaleWholeSymbol( lineLayer->offset() / symbolWidth, property ) );
2165  }
2166  }
2167  }
2168  }
2169 }
2170 
2172 {
2173  const double symbolWidth = width();
2174 
2175  QgsProperty symbolDD;
2176 
2177  // find the base of the "en masse" pattern
2178  for ( QgsSymbolLayerList::const_iterator it = mLayers.begin(); it != mLayers.end(); ++it )
2179  {
2180  const QgsLineSymbolLayer *layer = dynamic_cast<const QgsLineSymbolLayer *>( *it );
2181  if ( layer && qgsDoubleNear( layer->width(), symbolWidth ) && layer->dataDefinedProperties().isActive( QgsSymbolLayer::PropertyStrokeWidth ) )
2182  {
2183  symbolDD = layer->dataDefinedProperties().property( QgsSymbolLayer::PropertyStrokeWidth );
2184  break;
2185  }
2186  }
2187 
2188  if ( !symbolDD )
2189  return QgsProperty();
2190 
2191  // check that all layers width expressions match the "en masse" pattern
2192  const auto constMLayers = mLayers;
2193  for ( QgsSymbolLayer *layer : constMLayers )
2194  {
2195  if ( layer->type() != QgsSymbol::Line )
2196  continue;
2197  const QgsLineSymbolLayer *lineLayer = static_cast<const QgsLineSymbolLayer *>( layer );
2198 
2201 
2202  if ( qgsDoubleNear( lineLayer->width(), symbolWidth ) )
2203  {
2204  if ( !layerWidthDD || layerWidthDD != symbolDD )
2205  return QgsProperty();
2206  }
2207  else
2208  {
2209  if ( qgsDoubleNear( symbolWidth, 0.0 ) )
2210  return QgsProperty();
2211 
2212  QgsProperty scaledDD( scaleWholeSymbol( lineLayer->width() / symbolWidth, symbolDD ) );
2213  if ( !layerWidthDD || layerWidthDD != scaledDD )
2214  return QgsProperty();
2215  }
2216 
2217  QgsProperty scaledOffsetDD( scaleWholeSymbol( lineLayer->offset() / symbolWidth, symbolDD ) );
2218  if ( layerOffsetDD && layerOffsetDD != scaledOffsetDD )
2219  return QgsProperty();
2220  }
2221 
2222  return symbolDD;
2223 }
2224 
2225 void QgsLineSymbol::renderPolyline( const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layerIdx, bool selected )
2226 {
2228 
2229  //save old painter
2230  QPainter *renderPainter = context.painter();
2231  QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, opacity, selected, mRenderHints, f );
2233  symbolContext.setGeometryPartCount( symbolRenderContext()->geometryPartCount() );
2234  symbolContext.setGeometryPartNum( symbolRenderContext()->geometryPartNum() );
2235 
2236  if ( layerIdx != -1 )
2237  {
2238  QgsSymbolLayer *symbolLayer = mLayers.value( layerIdx );
2240  {
2241  if ( symbolLayer->type() == QgsSymbol::Line )
2242  {
2243  QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( symbolLayer );
2244  renderPolylineUsingLayer( lineLayer, points, symbolContext );
2245  }
2246  else
2247  renderUsingLayer( symbolLayer, symbolContext );
2248  }
2249  return;
2250  }
2251 
2252  const auto constMLayers = mLayers;
2253  for ( QgsSymbolLayer *symbolLayer : constMLayers )
2254  {
2255  if ( context.renderingStopped() )
2256  break;
2257 
2258  if ( !symbolLayer->enabled() || !context.isSymbolLayerEnabled( symbolLayer ) )
2259  continue;
2260 
2261  if ( symbolLayer->type() == QgsSymbol::Line )
2262  {
2263  QgsLineSymbolLayer *lineLayer = static_cast<QgsLineSymbolLayer *>( symbolLayer );
2264  renderPolylineUsingLayer( lineLayer, points, symbolContext );
2265  }
2266  else
2267  {
2268  renderUsingLayer( symbolLayer, symbolContext );
2269  }
2270  }
2271 
2272  context.setPainter( renderPainter );
2273 }
2274 
2275 void QgsLineSymbol::renderPolylineUsingLayer( QgsLineSymbolLayer *layer, const QPolygonF &points, QgsSymbolRenderContext &context )
2276 {
2277  if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
2278  return;
2279 
2280  QgsPaintEffect *effect = layer->paintEffect();
2281  if ( effect && effect->enabled() )
2282  {
2283  QgsEffectPainter p( context.renderContext() );
2284  p->translate( points.boundingRect().topLeft() );
2285  p.setEffect( effect );
2286  layer->renderPolyline( points.translated( -points.boundingRect().topLeft() ), context );
2287  }
2288  else
2289  {
2290  layer->renderPolyline( points, context );
2291  }
2292 }
2293 
2294 
2296 {
2297  QgsLineSymbol *cloneSymbol = new QgsLineSymbol( cloneLayers() );
2298  cloneSymbol->setOpacity( mOpacity );
2300  cloneSymbol->setLayer( mLayer );
2303  cloneSymbol->setForceRHR( mForceRHR );
2305  return cloneSymbol;
2306 }
2307 
2309 // FILL
2310 
2312  : QgsSymbol( Fill, layers )
2313 {
2314  if ( mLayers.isEmpty() )
2315  mLayers.append( new QgsSimpleFillSymbolLayer() );
2316 }
2317 
2318 void QgsFillSymbol::renderPolygon( const QPolygonF &points, const QVector<QPolygonF> *rings, const QgsFeature *f, QgsRenderContext &context, int layerIdx, bool selected )
2319 {
2321 
2322  QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderUnknownUnit, opacity, selected, mRenderHints, f );
2324  symbolContext.setGeometryPartCount( symbolRenderContext()->geometryPartCount() );
2325  symbolContext.setGeometryPartNum( symbolRenderContext()->geometryPartNum() );
2326 
2327  if ( layerIdx != -1 )
2328  {
2329  QgsSymbolLayer *symbolLayer = mLayers.value( layerIdx );
2331  {
2332  if ( symbolLayer->type() == Fill || symbolLayer->type() == Line )
2333  renderPolygonUsingLayer( symbolLayer, points, rings, symbolContext );
2334  else
2335  renderUsingLayer( symbolLayer, symbolContext );
2336  }
2337  return;
2338  }
2339 
2340  const auto constMLayers = mLayers;
2341  for ( QgsSymbolLayer *symbolLayer : constMLayers )
2342  {
2343  if ( context.renderingStopped() )
2344  break;
2345 
2346  if ( !symbolLayer->enabled() || !context.isSymbolLayerEnabled( symbolLayer ) )
2347  continue;
2348 
2349  if ( symbolLayer->type() == Fill || symbolLayer->type() == Line )
2350  renderPolygonUsingLayer( symbolLayer, points, rings, symbolContext );
2351  else
2352  renderUsingLayer( symbolLayer, symbolContext );
2353  }
2354 }
2355 
2356 void QgsFillSymbol::renderPolygonUsingLayer( QgsSymbolLayer *layer, const QPolygonF &points, const QVector<QPolygonF> *rings, QgsSymbolRenderContext &context )
2357 {
2358  if ( layer->dataDefinedProperties().hasActiveProperties() && !layer->dataDefinedProperties().valueAsBool( QgsSymbolLayer::PropertyLayerEnabled, context.renderContext().expressionContext(), true ) )
2359  return;
2360 
2361  QgsSymbol::SymbolType layertype = layer->type();
2362 
2363  QgsPaintEffect *effect = layer->paintEffect();
2364  if ( effect && effect->enabled() )
2365  {
2366  QRectF bounds = polygonBounds( points, rings );
2367  QVector<QPolygonF> *translatedRings = translateRings( rings, -bounds.left(), -bounds.top() );
2368 
2369  QgsEffectPainter p( context.renderContext() );
2370  p->translate( bounds.topLeft() );
2371  p.setEffect( effect );
2372  if ( layertype == QgsSymbol::Fill )
2373  {
2374  ( static_cast<QgsFillSymbolLayer *>( layer ) )->renderPolygon( points.translated( -bounds.topLeft() ), translatedRings, context );
2375  }
2376  else if ( layertype == QgsSymbol::Line )
2377  {
2378  ( static_cast<QgsLineSymbolLayer *>( layer ) )->renderPolygonStroke( points.translated( -bounds.topLeft() ), translatedRings, context );
2379  }
2380  delete translatedRings;
2381  }
2382  else
2383  {
2384  if ( layertype == QgsSymbol::Fill )
2385  {
2386  ( static_cast<QgsFillSymbolLayer *>( layer ) )->renderPolygon( points, rings, context );
2387  }
2388  else if ( layertype == QgsSymbol::Line )
2389  {
2390  ( static_cast<QgsLineSymbolLayer *>( layer ) )->renderPolygonStroke( points, rings, context );
2391  }
2392  }
2393 }
2394 
2395 QRectF QgsFillSymbol::polygonBounds( const QPolygonF &points, const QVector<QPolygonF> *rings ) const
2396 {
2397  QRectF bounds = points.boundingRect();
2398  if ( rings )
2399  {
2400  for ( auto it = rings->constBegin(); it != rings->constEnd(); ++it )
2401  {
2402  bounds = bounds.united( ( *it ).boundingRect() );
2403  }
2404  }
2405  return bounds;
2406 }
2407 
2408 QVector<QPolygonF> *QgsFillSymbol::translateRings( const QVector<QPolygonF> *rings, double dx, double dy ) const
2409 {
2410  if ( !rings )
2411  return nullptr;
2412 
2413  QVector<QPolygonF> *translatedRings = new QVector<QPolygonF>;
2414  translatedRings->reserve( rings->size() );
2415  for ( auto it = rings->constBegin(); it != rings->constEnd(); ++it )
2416  {
2417  translatedRings->append( ( *it ).translated( dx, dy ) );
2418  }
2419  return translatedRings;
2420 }
2421 
2423 {
2424  QgsFillSymbol *cloneSymbol = new QgsFillSymbol( cloneLayers() );
2425  cloneSymbol->setOpacity( mOpacity );
2427  cloneSymbol->setLayer( mLayer );
2430  cloneSymbol->setForceRHR( mForceRHR );
2432  return cloneSymbol;
2433 }
2434 
2436 {
2437  const auto constMLayers = mLayers;
2438  for ( QgsSymbolLayer *layer : constMLayers )
2439  {
2440  if ( layer->type() != QgsSymbol::Fill )
2441  continue;
2442 
2443  QgsFillSymbolLayer *fillLayer = static_cast<QgsFillSymbolLayer *>( layer );
2444 
2445  if ( fillLayer )
2446  fillLayer->setAngle( angle );
2447  }
2448 }
2449 
2450 
Abstract base class for all geometries.
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
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, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transforms a polygon to the destination coordinate system.
void transformInPlace(double &x, double &y, double &z, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transforms an array of x, y and z double coordinates in place, from the source CRS to the destination...
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
Custom exception class for Coordinate Reference System related exceptions.
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:250
virtual QPolygonF asQPolygonF() const
Returns a QPolygonF representing the points.
Definition: qgscurve.cpp:230
@ Clockwise
Clockwise orientation.
Definition: qgscurve.h:240
@ CounterClockwise
Counter-clockwise orientation.
Definition: qgscurve.h:241
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.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
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 id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsFields fields
Definition: qgsfeature.h:66
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
void setAngle(double angle)
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgssymbol.h:1307
QgsFillSymbol(const QgsSymbolLayerList &layers=QgsSymbolLayerList())
Constructor for QgsFillSymbol, with the specified list of initial symbol layers.
Definition: qgssymbol.cpp:2311
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Definition: qgssymbol.cpp:1578
void setAngle(double angle)
Definition: qgssymbol.cpp:2435
void renderPolygon(const QPolygonF &points, const QVector< QPolygonF > *rings, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol using the given render context.
Definition: qgssymbol.cpp:2318
QgsFillSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:2422
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.
virtual void render(QgsSymbolRenderContext &context)
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(QgsSymbol::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 setWidth(double width)
Sets the width of the line symbol layer.
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.
void setWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the line's width.
void setOffset(double offset)
Sets the line's offset.
virtual double width() const
Returns the estimated width for the line symbol layer.
double offset() const
Returns the line's offset.
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgssymbol.h:1204
QgsLineSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:2295
void setWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the width units for the whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:2087
void renderPolyline(const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol along the line joining points, using the given render context.
Definition: qgssymbol.cpp:2225
double width() const
Returns the estimated width for the whole symbol, which is the maximum width of all marker symbol lay...
Definition: qgssymbol.cpp:2100
static QgsLineSymbol * createSimple(const QVariantMap &properties)
Create a line symbol with one symbol layer: SimpleLine with specified properties.
Definition: qgssymbol.cpp:1567
QgsLineSymbol(const QgsSymbolLayerList &layers=QgsSymbolLayerList())
Constructor for QgsLineSymbol, with the specified list of initial symbol layers.
Definition: qgssymbol.cpp:2053
void setWidth(double width)
Sets the width for the whole line symbol.
Definition: qgssymbol.cpp:2060
void setDataDefinedWidth(const QgsProperty &property)
Set data defined width for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:2135
QgsProperty dataDefinedWidth() const
Returns data defined width for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:2171
QgsMapLayerType type
Definition: qgsmaplayer.h:92
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.
Abstract base class for marker symbol layers.
virtual void setSize(double size)
Sets the symbol size.
QgsSymbol::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker's size.
QPointF offset() const
Returns the marker's offset, which is the horizontal and vertical displacement which the rendered mar...
void setAngle(double angle)
Sets the rotation angle for the marker.
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the units for the symbol's size.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's size.
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's size.
void setLineAngle(double lineAngle)
Sets the line angle modification for the symbol's angle.
double size() const
Returns the symbol size.
const QgsMapUnitScale & sizeMapUnitScale() const
Returns the map unit scale for the symbol's size.
void setScaleMethod(QgsSymbol::ScaleMethod scaleMethod)
Sets the method to use for scaling the marker's size.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:1004
QgsUnitTypes::RenderUnit sizeUnit() const
Returns the size units for the whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1780
static QgsMarkerSymbol * createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
Definition: qgssymbol.cpp:1556
QgsProperty dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1664
void setSize(double size)
Sets the size for the whole symbol.
Definition: qgssymbol.cpp:1711
void setLineAngle(double lineAngle)
Sets the line angle modification for the symbol's angle.
Definition: qgssymbol.cpp:1623
QgsMarkerSymbol(const QgsSymbolLayerList &layers=QgsSymbolLayerList())
Constructor for QgsMarkerSymbol, with the specified list of initial symbol layers.
Definition: qgssymbol.cpp:1591
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the size units for the whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1767
void setScaleMethod(QgsSymbol::ScaleMethod scaleMethod)
Definition: qgssymbol.cpp:1925
double size() const
Returns the estimated size for the whole symbol, which is the maximum size of all marker symbol layer...
Definition: qgssymbol.cpp:1735
double angle() const
Returns the marker angle for the whole symbol.
Definition: qgssymbol.cpp:1611
QgsMapUnitScale sizeMapUnitScale() const
Returns the size map unit scale for the whole symbol.
Definition: qgssymbol.cpp:1818
void setDataDefinedSize(const QgsProperty &property)
Set data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1832
ScaleMethod scaleMethod()
Definition: qgssymbol.cpp:1937
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the size map unit scale for the whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1805
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1869
void setAngle(double symbolAngle)
Sets the angle for the whole symbol.
Definition: qgssymbol.cpp:1598
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
Definition: qgssymbol.cpp:2036
void renderPoint(QPointF point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol at the specified point, using the given render context.
Definition: qgssymbol.cpp:1973
void setDataDefinedAngle(const QgsProperty &property)
Set data defined angle for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1635
QRectF bounds(QPointF point, QgsRenderContext &context, const QgsFeature &feature=QgsFeature()) const
Returns the approximate bounding box of the marker symbol, which includes the bounding box of all sym...
Definition: qgssymbol.cpp:2016
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:38
Q_GADGET double x
Definition: qgspoint.h:41
double y
Definition: qgspoint.h:42
Polygon geometry type.
Definition: qgspolygon.h:34
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:501
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.
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
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 isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
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
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
Definition: qgsrectangle.h:342
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:447
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
Contains information about the context of a rendering operation.
void setForceVectorOutput(bool force)
Sets whether rendering operations should use vector operations instead of any faster raster shortcuts...
void setTextureOrigin(const QPointF &origin)
Sets the texture origin, which should be used as a brush transform when rendering using QBrush object...
bool hasRenderedFeatureHandlers() const
Returns true if the context has any rendered feature handlers.
double segmentationTolerance() const
Gets the segmentation tolerance applied when rendering curved geometries.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
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 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.
@ DrawSymbolBounds
Draw bounds of symbols (for debugging/testing)
@ RenderMapTile
Draw map such that there are no problems between adjacent tiles.
@ 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...
Flags flags() const
Returns combination of flags used for rendering.
void setPainter(QPainter *p)
Sets the destination QPainter for the render operation.
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.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
bool testFlag(Flag flag) const
Check whether a particular flag is enabled.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
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 QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsSimpleFillSymbolLayer using the specified properties map containing symbol propertie...
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsSimpleLineSymbolLayer, using the settings serialized in the properties map (correspo...
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsSimpleMarkerSymbolLayer.
QList< QList< QPolygonF > > defaultPatchAsQPolygonF(QgsSymbol::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:1202
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition: qgsstyle.cpp:128
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)
VertexMarkerType
Editing vertex markers.
static void drawVertexMarker(double x, double y, QPainter &p, QgsSymbolLayerUtils::VertexMarkerType type, int markerSize)
Draws a vertex symbol at (painter) coordinates x, y.
@ PropertyAngle
Symbol angle.
@ PropertySize
Symbol size.
@ PropertyOffset
Symbol offset.
@ PropertyStrokeWidth
Stroke width.
@ 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 setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
QgsSymbol::SymbolType type() const
virtual void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context)
Called after the layer has been rendered for a particular feature.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer's property collection, used for data defined overrides.
void setExpressionContextScope(QgsExpressionContextScope *contextScope)
Set an expression scope for this symbol.
Definition: qgssymbol.cpp:1539
void setSelected(bool selected)
Sets whether symbols should be rendered using the selected symbol coloring and style.
Definition: qgssymbol.h:856
QgsSymbolRenderContext & operator=(const QgsSymbolRenderContext &)
Definition: qgssymbol.cpp:1524
Q_DECL_DEPRECATED double outputLineWidth(double width) const
Definition: qgssymbol.cpp:1513
QgsSymbolRenderContext(QgsRenderContext &c, QgsUnitTypes::RenderUnit u, qreal opacity=1.0, bool selected=false, QgsSymbol::RenderHints renderHints=QgsSymbol::RenderHints(), const QgsFeature *f=nullptr, const QgsFields &fields=QgsFields(), const QgsMapUnitScale &mapUnitScale=QgsMapUnitScale())
Constructor for QgsSymbolRenderContext.
Definition: qgssymbol.cpp:1492
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Definition: qgssymbol.h:794
Q_DECL_DEPRECATED double outputPixelSize(double size) const
Definition: qgssymbol.cpp:1518
void setGeometryPartCount(int count)
Sets the part count of current geometry.
Definition: qgssymbol.h:912
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
Definition: qgssymbol.cpp:1508
void setPatchShape(const QgsLegendPatchShape &shape)
Sets the symbol patch shape, to use if rendering symbol preview icons.
Definition: qgssymbol.cpp:1549
void setGeometryPartNum(int num)
Sets the part number of current geometry.
Definition: qgssymbol.h:924
void setOriginalGeometryType(QgsWkbTypes::GeometryType type)
Sets the geometry type for the original feature geometry being rendered.
Definition: qgssymbol.h:882
QgsExpressionContextScope * expressionContextScope()
This scope is always available when a symbol of this type is being rendered.
Definition: qgssymbol.cpp:1534
const QgsLegendPatchShape * patchShape() const
Returns the symbol patch shape, to use if rendering symbol preview icons.
Definition: qgssymbol.cpp:1544
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:65
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the symbol's property collection, used for data defined overrides.
Definition: qgssymbol.h:583
QgsSymbolLayerList cloneLayers() const
Retrieve a cloned list of all layers that make up this symbol.
Definition: qgssymbol.cpp:754
QgsSymbolRenderContext * symbolRenderContext()
Returns the symbol render context.
Definition: qgssymbol.cpp:1417
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:411
Property
Data definable properties.
Definition: qgssymbol.h:116
@ PropertyOpacity
Opacity.
Definition: qgssymbol.h:117
void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the symbol.
Definition: qgssymbol.cpp:806
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:107
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:507
qreal mOpacity
Symbol opacity (in the range 0 - 1)
Definition: qgssymbol.h:700
Q_DECL_DEPRECATED const QgsVectorLayer * mLayer
Definition: qgssymbol.h:706
void setOutputUnit(QgsUnitTypes::RenderUnit unit)
Sets the units to use for sizes and widths within the symbol.
Definition: qgssymbol.cpp:328
QImage bigSymbolPreviewImage(QgsExpressionContext *expressionContext=nullptr, QgsSymbol::PreviewFlags flags=QgsSymbol::FlagIncludeCrosshairsForMarkerSymbols)
Returns a large (roughly 100x100 pixel) preview image for the symbol.
Definition: qgssymbol.cpp:669
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:168
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:553
SymbolType mType
Definition: qgssymbol.h:696
ScaleMethod
Scale method.
Definition: qgssymbol.h:98
void renderFeature(const QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false, int currentVertexMarkerType=0, double currentVertexMarkerSize=0.0) SIP_THROW(QgsCsException)
Render a feature.
Definition: qgssymbol.cpp:890
static QPointF _getPoint(QgsRenderContext &context, const QgsPoint &point)
Creates a point in screen coordinates from a QgsPoint in map coordinates.
Definition: qgssymbol.h:632
static const QgsPropertiesDefinition & propertyDefinitions()
Returns the symbol property definitions.
Definition: qgssymbol.cpp:254
bool appendSymbolLayer(QgsSymbolLayer *layer)
Appends a symbol layer at the end of the current symbol layer list.
Definition: qgssymbol.cpp:434
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
Returns a new default symbol for the specified geometry type.
Definition: qgssymbol.cpp:346
bool mClipFeaturesToExtent
Definition: qgssymbol.h:703
bool usesMapUnits() const
Returns true if the symbol has any components which use map unit based sizes.
Definition: qgssymbol.cpp:287
QgsUnitTypes::RenderUnit outputUnit() const
Returns the units to use for sizes and widths within the symbol.
Definition: qgssymbol.cpp:266
void toSld(QDomDocument &doc, QDomElement &element, QVariantMap props) const
Converts the symbol to a SLD representation.
Definition: qgssymbol.cpp:741
bool insertSymbolLayer(int index, QgsSymbolLayer *layer)
Inserts a symbol layer to specified index.
Definition: qgssymbol.cpp:421
QgsMapUnitScale mapUnitScale() const
Returns the map unit scale for the symbol.
Definition: qgssymbol.cpp:304
SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:138
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:481
bool canCauseArtifactsBetweenAdjacentTiles() const
Returns true if the symbol rendering can cause visible artifacts across a single feature when the fea...
Definition: qgssymbol.cpp:824
bool clipFeaturesToExtent() const
Returns whether features drawn by the symbol will be clipped to the render context's extent.
Definition: qgssymbol.h:522
QImage asImage(QSize size, QgsRenderContext *customContext=nullptr)
Returns an image of the symbol at the specified size.
Definition: qgssymbol.cpp:655
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:239
QString dump() const
Returns a string dump of the symbol's properties.
Definition: qgssymbol.cpp:715
bool hasDataDefinedProperties() const
Returns whether the symbol utilizes any data defined properties.
Definition: qgssymbol.cpp:811
bool deleteSymbolLayer(int index)
Removes and deletes the symbol layer at the specified index.
Definition: qgssymbol.cpp:444
virtual ~QgsSymbol()
Definition: qgssymbol.cpp:260
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Definition: qgssymbol.cpp:789
void setColor(const QColor &color)
Sets the color for the symbol.
Definition: qgssymbol.cpp:532
RenderHints mRenderHints
Definition: qgssymbol.h:702
bool changeSymbolLayer(int index, QgsSymbolLayer *layer)
Deletes the current layer at the specified index and replaces it with layer.
Definition: qgssymbol.cpp:464
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol's property collection, used for data defined overrides.
Definition: qgssymbol.h:568
SymbolType
Type of the symbol.
Definition: qgssymbol.h:87
@ Line
Line symbol.
Definition: qgssymbol.h:89
@ Hybrid
Hybrid symbol.
Definition: qgssymbol.h:91
@ Fill
Fill symbol.
Definition: qgssymbol.h:90
@ Marker
Marker symbol.
Definition: qgssymbol.h:88
void setOpacity(qreal opacity)
Sets the opacity for the symbol.
Definition: qgssymbol.h:488
QgsSymbolLayer * takeSymbolLayer(int index)
Removes a symbol layer from the list and returns a pointer to it.
Definition: qgssymbol.cpp:455
void setMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol.
Definition: qgssymbol.cpp:337
bool mForceRHR
Definition: qgssymbol.h:704
QgsSymbolLayerList mLayers
Definition: qgssymbol.h:697
Q_DECL_DEPRECATED const QgsVectorLayer * layer() const
Definition: qgssymbol.cpp:841
void renderUsingLayer(QgsSymbolLayer *layer, QgsSymbolRenderContext &context)
Renders a context using a particular symbol layer without passing in a geometry.
Definition: qgssymbol.cpp:768
QColor color() const
Returns the symbol's color.
Definition: qgssymbol.cpp:542
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:480
Q_DECL_DEPRECATED void setLayer(const QgsVectorLayer *layer)
Definition: qgssymbol.cpp:834
void renderVertexMarker(QPointF pt, QgsRenderContext &context, int currentVertexMarkerType, double currentVertexMarkerSize)
Render editing vertex marker at specified point.
Definition: qgssymbol.cpp:1422
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:635
void setClipFeaturesToExtent(bool clipFeaturesToExtent)
Sets whether features drawn by the symbol should be clipped to the render context's extent.
Definition: qgssymbol.h:511
void setForceRHR(bool force)
Sets whether polygon features drawn by the symbol should be reoriented to follow the standard right-h...
Definition: qgssymbol.h:533
QgsSymbol(SymbolType type, const QgsSymbolLayerList &layers)
Definition: qgssymbol.cpp:86
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:167
@ RenderUnknownUnit
Mixed or unknown units.
Definition: qgsunittypes.h:174
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:168
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
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
Contains geos related utilities and functions.
Definition: qgsgeos.h:42
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define FALLTHROUGH
Definition: qgis.h:829
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:798
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:276
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:797
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:316
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QgsProperty scaleWholeSymbol(double scaleFactor, const QgsProperty &property)
Definition: qgssymbol.cpp:66
QgsProperty rotateWholeSymbol(double additionalRotation, const QgsProperty &property)
Definition: qgssymbol.cpp:59
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:54
#define DEFAULT_SCALE_METHOD
Single variable definition for use within a QgsExpressionContextScope.
Utility class for identifying a unique vertex within a geometry.