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