QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsinvertedpolygonrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsinvertedpolygonrenderer.cpp
3  ---------------------
4  begin : April 2014
5  copyright : (C) 2014 Hugo Mercier / Oslandia
6  email : hugo dot mercier at oslandia 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 
17 
18 #include "qgssymbol.h"
19 #include "qgssymbollayerutils.h"
20 
21 #include "qgslogger.h"
22 #include "qgsfeature.h"
23 #include "qgsvectorlayer.h"
24 #include "qgssymbollayer.h"
25 #include "qgsogcutils.h"
26 #include "qgspainteffect.h"
27 #include "qgspainteffectregistry.h"
28 #include "qgsstyleentityvisitor.h"
29 
30 #include <QDomDocument>
31 #include <QDomElement>
32 
34  : QgsFeatureRenderer( QStringLiteral( "invertedPolygonRenderer" ) )
35 {
36  if ( subRenderer )
37  {
38  setEmbeddedRenderer( subRenderer );
39  }
40  else
41  {
43  }
44 }
45 
47 {
48  if ( subRenderer )
49  {
50  mSubRenderer.reset( subRenderer );
51  }
52  else
53  {
54  mSubRenderer.reset( nullptr );
55  }
56 }
57 
59 {
60  return mSubRenderer.get();
61 }
62 
64 {
65  if ( !mSubRenderer )
66  return;
67 
68  mSubRenderer->setLegendSymbolItem( key, symbol );
69 }
70 
72 {
73  if ( !mSubRenderer )
74  return false;
75 
76  return mSubRenderer->legendSymbolItemsCheckable();
77 }
78 
80 {
81  if ( !mSubRenderer )
82  return false;
83 
84  return mSubRenderer->legendSymbolItemChecked( key );
85 }
86 
87 void QgsInvertedPolygonRenderer::checkLegendSymbolItem( const QString &key, bool state )
88 {
89  if ( !mSubRenderer )
90  return;
91 
92  mSubRenderer->checkLegendSymbolItem( key, state );
93 }
94 
96 {
97  if ( !mSubRenderer )
98  return true;
99 
100  return mSubRenderer->accept( visitor );
101 }
102 
104 {
105  QgsFeatureRenderer::startRender( context, fields );
106 
107  if ( !mSubRenderer )
108  {
109  return;
110  }
111 
112  // first call start render on the sub renderer
113  mSubRenderer->startRender( context, fields );
114 
115  mFeaturesCategories.clear();
116  mSymbolCategories.clear();
117  mFeatureDecorations.clear();
118  mFields = fields;
119 
120  // We compute coordinates of the extent which will serve as exterior ring
121  // for the final polygon
122  // It must be computed in the destination CRS if reprojection is enabled.
123 
124  if ( !context.painter() )
125  {
126  return;
127  }
128 
129  // convert viewport to dest CRS
130  // add some space to hide borders and tend to infinity
131  const double buffer = std::max( context.mapExtent().width(), context.mapExtent().height() ) * 0.1;
132  const QRectF outer = context.mapExtent().buffered( buffer ).toRectF();
133  QgsPolylineXY exteriorRing;
134  exteriorRing.reserve( 5 );
135  exteriorRing << outer.topLeft();
136  exteriorRing << outer.topRight();
137  exteriorRing << outer.bottomRight();
138  exteriorRing << outer.bottomLeft();
139  exteriorRing << outer.topLeft();
140 
141  // copy the rendering context
142  mContext = context;
143 
144  // If reprojection is enabled, we must reproject during renderFeature
145  // and act as if there is no reprojection
146  // If we don't do that, there is no need to have a simple rectangular extent
147  // that covers the whole screen
148  // (a rectangle in the destCRS cannot be expressed as valid coordinates in the sourceCRS in general)
149  if ( context.coordinateTransform().isValid() )
150  {
151  // disable projection
153  // recompute extent so that polygon clipping is correct
154  mContext.setExtent( context.mapExtent() );
155  // do we have to recompute the MapToPixel ?
156  }
157 
158  mExtentPolygon.clear();
159  mExtentPolygon.append( exteriorRing );
160 }
161 
162 bool QgsInvertedPolygonRenderer::renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker )
163 {
164  if ( !context.painter() )
165  {
166  return false;
167  }
168 
169  // store this feature as a feature to render with decoration if needed
170  if ( selected || drawVertexMarker )
171  {
172  mFeatureDecorations.append( FeatureDecoration( feature, selected, drawVertexMarker, layer ) );
173  }
174 
175  // Features are grouped by category of symbols (returned by symbol(s)ForFeature)
176  // This way, users can have multiple inverted polygon fills for a layer,
177  // for instance, with rule based renderer and different symbols
178  // that have transparency.
179  //
180  // In order to assign a unique category to a set of symbols
181  // during each rendering session (between startRender() and stopRender()),
182  // we build an unique id as a QByteArray that is the concatenation
183  // of each symbol's memory address.
184  // The only assumption made here is that symbol(s)ForFeature will
185  // always return the same address for the same symbol(s) shared amongst
186  // different features.
187  // This QByteArray can then be used as a key for a QMap where the list of
188  // features for this category is stored
189  QByteArray catId;
191  {
192  QgsSymbolList syms( mSubRenderer->symbolsForFeature( feature, context ) );
193  const auto constSyms = syms;
194  for ( QgsSymbol *sym : constSyms )
195  {
196  // append the memory address
197  catId.append( reinterpret_cast<const char *>( &sym ), sizeof( sym ) );
198  }
199  }
200  else
201  {
202  QgsSymbol *sym = mSubRenderer->symbolForFeature( feature, context );
203  if ( sym )
204  {
205  catId.append( reinterpret_cast<const char *>( &sym ), sizeof( sym ) );
206  }
207  }
208 
209  if ( catId.isEmpty() )
210  {
211  return false;
212  }
213 
214  if ( ! mSymbolCategories.contains( catId ) )
215  {
216  CombinedFeature cFeat;
217  // store the first feature
218  cFeat.feature = feature;
219  mSymbolCategories.insert( catId, mSymbolCategories.count() );
220  mFeaturesCategories.append( cFeat );
221  }
222 
223  // update the geometry
224  CombinedFeature &cFeat = mFeaturesCategories[ mSymbolCategories[catId] ];
225  if ( !feature.hasGeometry() )
226  {
227  return false;
228  }
229  QgsGeometry geom = feature.geometry();
230 
232  if ( xform.isValid() )
233  {
234  geom.transform( xform );
235  }
236 
237  if ( mPreprocessingEnabled )
238  {
239  // fix the polygon if it is not valid
240  if ( ! geom.isGeosValid() )
241  {
242  geom = geom.buffer( 0, 0 );
243  }
244  }
245 
246  if ( geom.isNull() )
247  return false; // do not let invalid geometries sneak in!
248 
249  // add the geometry to the list of geometries for this feature
250  cFeat.geometries.append( geom );
251 
252  return true;
253 }
254 
256 {
258  if ( context.renderingStopped() )
259  {
260  mSubRenderer->stopRender( mContext );
261  return;
262  }
263 
264  if ( !mSubRenderer )
265  {
266  return;
267  }
268  if ( !context.painter() )
269  {
270  return;
271  }
272 
273  QgsMultiPolygonXY finalMulti; //avoid expensive allocation for list for every feature
274  QgsPolygonXY newPoly;
275 
276  const auto constMFeaturesCategories = mFeaturesCategories;
277  for ( const CombinedFeature &cit : constMFeaturesCategories )
278  {
279  finalMulti.resize( 0 ); //preserve capacity - don't use clear!
280  QgsFeature feat = cit.feature; // just a copy, so that we do not accumulate geometries again
281  if ( mPreprocessingEnabled )
282  {
283  // compute the unary union on the polygons
284  QgsGeometry unioned( QgsGeometry::unaryUnion( cit.geometries ) );
285  // compute the difference with the extent
286  QgsGeometry rect = QgsGeometry::fromPolygonXY( mExtentPolygon );
287  QgsGeometry final = rect.difference( unioned );
288  feat.setGeometry( final );
289  }
290  else
291  {
292  // No preprocessing involved.
293  // We build here a "reversed" geometry of all the polygons
294  //
295  // The final geometry is a multipolygon F, with :
296  // * the first polygon of F having the current extent as its exterior ring
297  // * each polygon's exterior ring is added as interior ring of the first polygon of F
298  // * each polygon's interior ring is added as new polygons in F
299  //
300  // No validity check is done, on purpose, it will be very slow and painting
301  // operations do not need geometries to be valid
302 
303  finalMulti.append( mExtentPolygon );
304  for ( const QgsGeometry &geom : qgis::as_const( cit.geometries ) )
305  {
306  QgsMultiPolygonXY multi;
307  QgsWkbTypes::Type type = QgsWkbTypes::flatType( geom.constGet()->wkbType() );
308 
309  if ( ( type == QgsWkbTypes::Polygon ) || ( type == QgsWkbTypes::CurvePolygon ) )
310  {
311  multi.append( geom.asPolygon() );
312  }
313  else if ( ( type == QgsWkbTypes::MultiPolygon ) || ( type == QgsWkbTypes::MultiSurface ) )
314  {
315  multi = geom.asMultiPolygon();
316  }
317 
318  for ( int i = 0; i < multi.size(); i++ )
319  {
320  const QgsPolylineXY &exterior = multi[i][0];
321  // add the exterior ring as interior ring to the first polygon
322  // make sure it satisfies at least very basic requirements of GEOS
323  // (otherwise the creation of GEOS geometry will fail)
324  if ( exterior.count() < 4 || exterior[0] != exterior[exterior.count() - 1] )
325  continue;
326  finalMulti[0].append( exterior );
327 
328  // add interior rings as new polygons
329  for ( int j = 1; j < multi[i].size(); j++ )
330  {
331  newPoly.resize( 0 ); //preserve capacity - don't use clear!
332  newPoly.append( multi[i][j] );
333  finalMulti.append( newPoly );
334  }
335  }
336  }
337  feat.setGeometry( QgsGeometry::fromMultiPolygonXY( finalMulti ) );
338  }
339  if ( feat.hasGeometry() )
340  {
341  mContext.expressionContext().setFeature( feat );
342  mSubRenderer->renderFeature( feat, mContext );
343  }
344  }
345 
346  // when no features are visible, we still have to draw the exterior rectangle
347  // warning: when sub renderers have more than one possible symbols,
348  // there is no way to choose a correct one, because there is no attribute here
349  // in that case, nothing will be rendered
350  if ( mFeaturesCategories.isEmpty() )
351  {
352  // empty feature with default attributes
353  QgsFeature feat( mFields );
354  feat.setGeometry( QgsGeometry::fromPolygonXY( mExtentPolygon ) );
355  mSubRenderer->renderFeature( feat, mContext );
356  }
357 
358  // draw feature decorations
359  const auto constMFeatureDecorations = mFeatureDecorations;
360  for ( FeatureDecoration deco : constMFeatureDecorations )
361  {
362  mSubRenderer->renderFeature( deco.feature, mContext, deco.layer, deco.selected, deco.drawMarkers );
363  }
364 
365  mSubRenderer->stopRender( mContext );
366 }
367 
369 {
370  if ( !mSubRenderer )
371  {
372  return QStringLiteral( "INVERTED: NULL" );
373  }
374  return "INVERTED [" + mSubRenderer->dump() + ']';
375 }
376 
378 {
379  QgsInvertedPolygonRenderer *newRenderer = nullptr;
380  if ( !mSubRenderer )
381  {
382  newRenderer = new QgsInvertedPolygonRenderer( nullptr );
383  }
384  else
385  {
386  newRenderer = new QgsInvertedPolygonRenderer( mSubRenderer->clone() );
387  }
389  copyRendererData( newRenderer );
390  return newRenderer;
391 }
392 
394 {
396  //look for an embedded renderer <renderer-v2>
397  QDomElement embeddedRendererElem = element.firstChildElement( QStringLiteral( "renderer-v2" ) );
398  if ( !embeddedRendererElem.isNull() )
399  {
400  QgsFeatureRenderer *renderer = QgsFeatureRenderer::load( embeddedRendererElem, context );
401  r->setEmbeddedRenderer( renderer );
402  }
403  r->setPreprocessingEnabled( element.attribute( QStringLiteral( "preprocessing" ), QStringLiteral( "0" ) ).toInt() == 1 );
404  return r;
405 }
406 
407 QDomElement QgsInvertedPolygonRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
408 {
409  // clazy:skip
410 
411  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
412  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "invertedPolygonRenderer" ) );
413  rendererElem.setAttribute( QStringLiteral( "preprocessing" ), preprocessingEnabled() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
414  rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
415 
416  if ( mSubRenderer )
417  {
418  QDomElement embeddedRendererElem = mSubRenderer->save( doc, context );
419  rendererElem.appendChild( embeddedRendererElem );
420  }
421 
423  mPaintEffect->saveProperties( doc, rendererElem );
424 
425  if ( !mOrderBy.isEmpty() )
426  {
427  QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
428  mOrderBy.save( orderBy );
429  rendererElem.appendChild( orderBy );
430  }
431  rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
432 
433  return rendererElem;
434 }
435 
437 {
438  if ( !mSubRenderer )
439  {
440  return nullptr;
441  }
442  return mSubRenderer->symbolForFeature( feature, context );
443 }
444 
446 {
447  if ( !mSubRenderer )
448  return nullptr;
449  return mSubRenderer->originalSymbolForFeature( feature, context );
450 }
451 
453 {
454  if ( !mSubRenderer )
455  {
456  return QgsSymbolList();
457  }
458  return mSubRenderer->symbolsForFeature( feature, context );
459 }
460 
462 {
463  if ( !mSubRenderer )
464  return QgsSymbolList();
465  return mSubRenderer->originalSymbolsForFeature( feature, context );
466 }
467 
468 QSet<QString> QgsInvertedPolygonRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
469 {
470  if ( !mSubRenderer )
471  return QSet<QString>();
472  return mSubRenderer->legendKeysForFeature( feature, context );
473 }
474 
476 {
477  if ( !mSubRenderer )
478  {
479  return QgsSymbolList();
480  }
481  return mSubRenderer->symbols( context );
482 }
483 
484 QgsFeatureRenderer::Capabilities QgsInvertedPolygonRenderer::capabilities()
485 {
486  if ( !mSubRenderer )
487  {
488  return nullptr;
489  }
490  return mSubRenderer->capabilities();
491 }
492 
494 {
495  if ( !mSubRenderer )
496  {
497  return QSet<QString>();
498  }
499  return mSubRenderer->usedAttributes( context );
500 }
501 
503 {
504  return mSubRenderer ? mSubRenderer->filterNeedsGeometry() : false;
505 }
506 
508 {
509  if ( !mSubRenderer )
510  {
511  return QgsLegendSymbolList();
512  }
513  return mSubRenderer->legendSymbolItems();
514 }
515 
517 {
518  if ( !mSubRenderer )
519  {
520  return false;
521  }
522  return mSubRenderer->willRenderFeature( feature, context );
523 }
524 
526 {
527  if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
528  {
529  return dynamic_cast<QgsInvertedPolygonRenderer *>( renderer->clone() );
530  }
531 
532  if ( renderer->type() == QLatin1String( "singleSymbol" ) ||
533  renderer->type() == QLatin1String( "categorizedSymbol" ) ||
534  renderer->type() == QLatin1String( "graduatedSymbol" ) ||
535  renderer->type() == QLatin1String( "RuleRenderer" ) )
536  {
537  return new QgsInvertedPolygonRenderer( renderer->clone() );
538  }
539  return nullptr;
540 }
541 
static QgsGeometry fromMultiPolygonXY(const QgsMultiPolygonXY &multipoly)
Creates a new geometry from a QgsMultiPolygon.
The class is used as a container of context for various read/write operations on other objects...
QgsFeatureRenderer::Capabilities capabilities() override
Returns details about internals of this renderer.
May use more than one symbol to render a feature: symbolsForFeature() will return them...
Definition: qgsrenderer.h:244
bool legendSymbolItemChecked(const QString &key) override
items of symbology items in legend is checked
QList< QgsLegendSymbolItem > QgsLegendSymbolList
QgsFeatureRequest::OrderBy mOrderBy
Definition: qgsrenderer.h:533
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
void setCoordinateTransform(const QgsCoordinateTransform &t)
Sets the current coordinate transform for the context.
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
Creates a renderer out of an XML, for loading.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsFeatureRequest::OrderBy orderBy() const
Gets the order in which features shall be processed by this renderer.
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
QVector< QgsPolylineXY > QgsPolygonXY
Polygon: first item of the list is outer ring, inner rings (if any) start from second item...
Definition: qgsgeometry.h:74
QgsInvertedPolygonRenderer * clone() const override
Create a deep copy of this renderer.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
#define RENDERER_TAG_NAME
Definition: qgsrenderer.h:50
QgsGeometry buffer(double distance, int segments) const
Returns a buffer region around this geometry having the given width and with a specified number of se...
QgsRectangle mapExtent() const
Returns the original extent of the map being rendered.
bool willRenderFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns whether the renderer will render a feature or not.
QgsPaintEffect * mPaintEffect
Definition: qgsrenderer.h:517
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QString dump() const override
Returns debug information about this renderer.
static QgsInvertedPolygonRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
Creates a QgsInvertedPolygonRenderer by a conversion from an existing renderer.
An interface for classes which can visit style entity (e.g.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
void setExtent(const QgsRectangle &extent)
When rendering a map layer, calling this method sets the "clipping" extent for the layer (in the laye...
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QVector< QgsPolygonXY > QgsMultiPolygonXY
A collection of QgsPolygons that share a common collection of attributes.
Definition: qgsgeometry.h:91
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
QgsRectangle buffered(double width) const
Gets rectangle enlarged by buffer.
Definition: qgsrectangle.h:304
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:44
QString type() const
Definition: qgsrenderer.h:130
QgsSymbol * symbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
To be overridden.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns legend keys matching a specified feature.
bool isGeosValid(QgsGeometry::ValidityFlags flags=nullptr) const
Checks validity of the geometry using GEOS.
void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
store renderer info to XML element
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
static QgsFeatureRenderer * load(QDomElement &symbologyElem, const QgsReadWriteContext &context)
create a renderer from XML element
QgsSymbolList originalSymbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Equivalent of originalSymbolsForFeature() call extended to support renderers that may use more symbol...
QgsLegendSymbolList legendSymbolItems() const override
Returns a list of symbology items for the legend.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
QgsSymbol * originalSymbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns symbol for feature.
QgsSymbolList symbols(QgsRenderContext &context) const override
Returns list of symbols used by the renderer.
QgsExpressionContext & expressionContext()
Gets the expression context.
static QgsGeometry fromPolygonXY(const QgsPolygonXY &polygon)
Creates a new geometry from a QgsPolygon.
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.
Definition: qgsgeometry.h:50
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
QRectF toRectF() const
Returns a QRectF with same coordinates as the rectangle.
Definition: qgsrectangle.h:457
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:93
bool renderFeature(const QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false) override SIP_THROW(QgsCsException)
Renders a given feature.
QgsInvertedPolygonRenderer(QgsFeatureRenderer *embeddedRenderer=nullptr)
Constructor.
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
void setGeometry(const QgsGeometry &geometry)
Set the feature&#39;s geometry.
Definition: qgsfeature.cpp:137
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
Class for doing transforms between two map coordinate systems.
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:49
bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
QgsGeometry geometry
Definition: qgsfeature.h:67
void stopRender(QgsRenderContext &context) override
The actual rendering will take place here.
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
static QgsFeatureRenderer * defaultRenderer(QgsWkbTypes::GeometryType geomType)
Returns a new renderer - used by default in vector layers.
Definition: qgsrenderer.cpp:76
static Type flatType(Type type)
Returns the flat type for a WKB type.
Definition: qgswkbtypes.h:576
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries)
Compute the unary union on a list of geometries.
bool filterNeedsGeometry() const override
Returns true if this renderer requires the geometry to apply the filter.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns list of symbols used for rendering the feature.
void setEmbeddedRenderer(QgsFeatureRenderer *subRenderer) override
Sets an embedded renderer (subrenderer) for this feature renderer.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
QgsGeometry difference(const QgsGeometry &geometry) const
Returns a geometry representing the points making up this geometry that do not make up other...