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