QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsvectortilebasicrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvectortilebasicrenderer.cpp
3  --------------------------------------
4  Date : March 2020
5  Copyright : (C) 2020 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 
17 
18 #include "qgsapplication.h"
19 #include "qgscolorschemeregistry.h"
21 #include "qgsfillsymbollayer.h"
22 #include "qgslinesymbollayer.h"
23 #include "qgsmarkersymbollayer.h"
24 #include "qgssymbollayerutils.h"
25 #include "qgsvectortileutils.h"
26 #include "qgsfillsymbol.h"
27 #include "qgslinesymbol.h"
28 #include "qgsmarkersymbol.h"
29 
31  : mStyleName( stName )
32  , mLayerName( laName )
33  , mGeometryType( geomType )
34 {
35 }
36 
38 {
39  operator=( other );
40 }
41 
43 {
44  mStyleName = other.mStyleName;
45  mLayerName = other.mLayerName;
46  mGeometryType = other.mGeometryType;
47  mSymbol.reset( other.mSymbol ? other.mSymbol->clone() : nullptr );
48  mEnabled = other.mEnabled;
49  mExpression = other.mExpression;
50  mMinZoomLevel = other.mMinZoomLevel;
51  mMaxZoomLevel = other.mMaxZoomLevel;
52  return *this;
53 }
54 
56 
58 {
59  mSymbol.reset( sym );
60 }
61 
62 void QgsVectorTileBasicRendererStyle::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
63 {
64  elem.setAttribute( QStringLiteral( "name" ), mStyleName );
65  elem.setAttribute( QStringLiteral( "layer" ), mLayerName );
66  elem.setAttribute( QStringLiteral( "geometry" ), mGeometryType );
67  elem.setAttribute( QStringLiteral( "enabled" ), mEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
68  elem.setAttribute( QStringLiteral( "expression" ), mExpression );
69  elem.setAttribute( QStringLiteral( "min-zoom" ), mMinZoomLevel );
70  elem.setAttribute( QStringLiteral( "max-zoom" ), mMaxZoomLevel );
71 
72  QDomDocument doc = elem.ownerDocument();
73  QgsSymbolMap symbols;
74  symbols[QStringLiteral( "0" )] = mSymbol.get();
75  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
76  elem.appendChild( symbolsElem );
77 }
78 
79 void QgsVectorTileBasicRendererStyle::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
80 {
81  mStyleName = elem.attribute( QStringLiteral( "name" ) );
82  mLayerName = elem.attribute( QStringLiteral( "layer" ) );
83  mGeometryType = static_cast<QgsWkbTypes::GeometryType>( elem.attribute( QStringLiteral( "geometry" ) ).toInt() );
84  mEnabled = elem.attribute( QStringLiteral( "enabled" ) ).toInt();
85  mExpression = elem.attribute( QStringLiteral( "expression" ) );
86  mMinZoomLevel = elem.attribute( QStringLiteral( "min-zoom" ) ).toInt();
87  mMaxZoomLevel = elem.attribute( QStringLiteral( "max-zoom" ) ).toInt();
88 
89  mSymbol.reset();
90  QDomElement symbolsElem = elem.firstChildElement( QStringLiteral( "symbols" ) );
91  if ( !symbolsElem.isNull() )
92  {
93  QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
94  if ( symbolMap.contains( QStringLiteral( "0" ) ) )
95  {
96  mSymbol.reset( symbolMap.take( QStringLiteral( "0" ) ) );
97  }
98  }
99 }
100 
102 
103 
105 {
106 }
107 
109 {
110  return QStringLiteral( "basic" );
111 }
112 
114 {
116  r->mStyles = mStyles;
117  r->mStyles.detach(); // make a deep copy to make sure symbols get cloned
118  return r;
119 }
120 
121 void QgsVectorTileBasicRenderer::startRender( QgsRenderContext &context, int tileZoom, const QgsTileRange &tileRange )
122 {
123  Q_UNUSED( context )
124  Q_UNUSED( tileRange )
125  // figure out required fields for different layers
126  for ( const QgsVectorTileBasicRendererStyle &layerStyle : std::as_const( mStyles ) )
127  {
128  if ( layerStyle.isActive( tileZoom ) )
129  {
130  if ( !layerStyle.filterExpression().isEmpty() )
131  {
132  QgsExpression expr( layerStyle.filterExpression() );
133  mRequiredFields[layerStyle.layerName()].unite( expr.referencedColumns() );
134  }
135  if ( auto *lSymbol = layerStyle.symbol() )
136  {
137  mRequiredFields[layerStyle.layerName()].unite( lSymbol->usedAttributes( context ) );
138  }
139  }
140  }
141 }
142 
143 QMap<QString, QSet<QString> > QgsVectorTileBasicRenderer::usedAttributes( const QgsRenderContext & )
144 {
145  return mRequiredFields;
146 }
147 
148 QSet<QString> QgsVectorTileBasicRenderer::requiredLayers( QgsRenderContext &, int tileZoom ) const
149 {
150  QSet< QString > res;
151  for ( const QgsVectorTileBasicRendererStyle &layerStyle : std::as_const( mStyles ) )
152  {
153  if ( layerStyle.isActive( tileZoom ) )
154  {
155  res.insert( layerStyle.layerName() );
156  }
157  }
158  return res;
159 }
160 
162 {
163  Q_UNUSED( context )
164 }
165 
167 {
168  const QgsVectorTileFeatures tileData = tile.features();
169  int zoomLevel = tile.id().zoomLevel();
170 
171  for ( const QgsVectorTileBasicRendererStyle &layerStyle : std::as_const( mStyles ) )
172  {
173  if ( !layerStyle.isActive( zoomLevel ) || !layerStyle.symbol() )
174  continue;
175 
176  QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Layer" ) ); // will be deleted by popper
177  scope->setFields( tile.fields()[layerStyle.layerName()] );
178  QgsExpressionContextScopePopper popper( context.expressionContext(), scope );
179 
180  QgsExpression filterExpression( layerStyle.filterExpression() );
181  filterExpression.prepare( &context.expressionContext() );
182 
183  QgsSymbol *sym = layerStyle.symbol();
184  sym->startRender( context, QgsFields() );
185  if ( layerStyle.layerName() == QLatin1String( "background" ) )
186  {
187  QgsFillSymbol *fillSym = dynamic_cast<QgsFillSymbol *>( sym );
188  if ( fillSym )
189  fillSym->renderPolygon( tile.tilePolygon(), nullptr, nullptr, context );
190  }
191  else if ( layerStyle.layerName().isEmpty() )
192  {
193  // matching all layers
194  for ( QString layerName : tileData.keys() )
195  {
196  for ( const QgsFeature &f : tileData[layerName] )
197  {
198  scope->setFeature( f );
199  if ( filterExpression.isValid() && !filterExpression.evaluate( &context.expressionContext() ).toBool() )
200  continue;
201 
202  const QgsWkbTypes::GeometryType featureType = QgsWkbTypes::geometryType( f.geometry().wkbType() );
203  if ( featureType == layerStyle.geometryType() )
204  {
205  sym->renderFeature( f, context );
206  }
207  else if ( featureType == QgsWkbTypes::PolygonGeometry && layerStyle.geometryType() == QgsWkbTypes::LineGeometry )
208  {
209  // be tolerant and permit rendering polygons with a line layer style, as some style definitions use this approach
210  // to render the polygon borders only
211  QgsFeature exterior = f;
212  exterior.setGeometry( QgsGeometry( f.geometry().constGet()->boundary() ) );
213  sym->renderFeature( exterior, context );
214  }
215  else if ( featureType == QgsWkbTypes::PolygonGeometry && layerStyle.geometryType() == QgsWkbTypes::PointGeometry )
216  {
217  // be tolerant and permit rendering polygons with a point layer style, as some style definitions use this approach
218  // to render the polygon center
219  QgsFeature centroid = f;
220  const QgsRectangle boundingBox = f.geometry().boundingBox();
221  centroid.setGeometry( f.geometry().poleOfInaccessibility( std::min( boundingBox.width(), boundingBox.height() ) / 20 ) );
222  sym->renderFeature( centroid, context );
223  }
224  }
225  }
226  }
227  else if ( tileData.contains( layerStyle.layerName() ) )
228  {
229  // matching one particular layer
230  for ( const QgsFeature &f : tileData[layerStyle.layerName()] )
231  {
232  scope->setFeature( f );
233  if ( filterExpression.isValid() && !filterExpression.evaluate( &context.expressionContext() ).toBool() )
234  continue;
235 
236  const QgsWkbTypes::GeometryType featureType = QgsWkbTypes::geometryType( f.geometry().wkbType() );
237  if ( featureType == layerStyle.geometryType() )
238  {
239  sym->renderFeature( f, context );
240  }
241  else if ( featureType == QgsWkbTypes::PolygonGeometry && layerStyle.geometryType() == QgsWkbTypes::LineGeometry )
242  {
243  // be tolerant and permit rendering polygons with a line layer style, as some style definitions use this approach
244  // to render the polygon borders only
245  QgsFeature exterior = f;
246  exterior.setGeometry( QgsGeometry( f.geometry().constGet()->boundary() ) );
247  sym->renderFeature( exterior, context );
248  }
249  else if ( featureType == QgsWkbTypes::PolygonGeometry && layerStyle.geometryType() == QgsWkbTypes::PointGeometry )
250  {
251  // be tolerant and permit rendering polygons with a point layer style, as some style definitions use this approach
252  // to render the polygon center
253  QgsFeature centroid = f;
254  const QgsRectangle boundingBox = f.geometry().boundingBox();
255  centroid.setGeometry( f.geometry().poleOfInaccessibility( std::min( boundingBox.width(), boundingBox.height() ) / 20 ) );
256  sym->renderFeature( centroid, context );
257  }
258  }
259  }
260  sym->stopRender( context );
261  }
262 }
263 
264 void QgsVectorTileBasicRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
265 {
266  QDomDocument doc = elem.ownerDocument();
267  QDomElement elemStyles = doc.createElement( QStringLiteral( "styles" ) );
268  for ( const QgsVectorTileBasicRendererStyle &layerStyle : mStyles )
269  {
270  QDomElement elemStyle = doc.createElement( QStringLiteral( "style" ) );
271  layerStyle.writeXml( elemStyle, context );
272  elemStyles.appendChild( elemStyle );
273  }
274  elem.appendChild( elemStyles );
275 }
276 
277 void QgsVectorTileBasicRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
278 {
279  mStyles.clear();
280 
281  QDomElement elemStyles = elem.firstChildElement( QStringLiteral( "styles" ) );
282  QDomElement elemStyle = elemStyles.firstChildElement( QStringLiteral( "style" ) );
283  while ( !elemStyle.isNull() )
284  {
286  layerStyle.readXml( elemStyle, context );
287  mStyles.append( layerStyle );
288  elemStyle = elemStyle.nextSiblingElement( QStringLiteral( "style" ) );
289  }
290 }
291 
292 void QgsVectorTileBasicRenderer::setStyles( const QList<QgsVectorTileBasicRendererStyle> &styles )
293 {
294  mStyles = styles;
295 }
296 
297 QList<QgsVectorTileBasicRendererStyle> QgsVectorTileBasicRenderer::styles() const
298 {
299  return mStyles;
300 }
301 
302 QList<QgsVectorTileBasicRendererStyle> QgsVectorTileBasicRenderer::simpleStyleWithRandomColors()
303 {
304  QColor polygonFillColor = QgsApplication::colorSchemeRegistry()->fetchRandomStyleColor();
305  QColor polygonStrokeColor = polygonFillColor;
306  polygonFillColor.setAlpha( 100 );
307  double polygonStrokeWidth = DEFAULT_LINE_WIDTH;
308 
309  QColor lineStrokeColor = QgsApplication::colorSchemeRegistry()->fetchRandomStyleColor();
310  double lineStrokeWidth = DEFAULT_LINE_WIDTH;
311 
312  QColor pointFillColor = QgsApplication::colorSchemeRegistry()->fetchRandomStyleColor();
313  QColor pointStrokeColor = pointFillColor;
314  pointFillColor.setAlpha( 100 );
315  double pointSize = DEFAULT_POINT_SIZE;
316 
317  return simpleStyle( polygonFillColor, polygonStrokeColor, polygonStrokeWidth,
318  lineStrokeColor, lineStrokeWidth,
319  pointFillColor, pointStrokeColor, pointSize );
320 }
321 
322 QList<QgsVectorTileBasicRendererStyle> QgsVectorTileBasicRenderer::simpleStyle(
323  const QColor &polygonFillColor, const QColor &polygonStrokeColor, double polygonStrokeWidth,
324  const QColor &lineStrokeColor, double lineStrokeWidth,
325  const QColor &pointFillColor, const QColor &pointStrokeColor, double pointSize )
326 {
327  QgsSimpleFillSymbolLayer *fillSymbolLayer = new QgsSimpleFillSymbolLayer();
328  fillSymbolLayer->setFillColor( polygonFillColor );
329  fillSymbolLayer->setStrokeColor( polygonStrokeColor );
330  fillSymbolLayer->setStrokeWidth( polygonStrokeWidth );
331  QgsFillSymbol *fillSymbol = new QgsFillSymbol( QgsSymbolLayerList() << fillSymbolLayer );
332 
333  QgsSimpleLineSymbolLayer *lineSymbolLayer = new QgsSimpleLineSymbolLayer;
334  lineSymbolLayer->setColor( lineStrokeColor );
335  lineSymbolLayer->setWidth( lineStrokeWidth );
336  QgsLineSymbol *lineSymbol = new QgsLineSymbol( QgsSymbolLayerList() << lineSymbolLayer );
337 
339  markerSymbolLayer->setFillColor( pointFillColor );
340  markerSymbolLayer->setStrokeColor( pointStrokeColor );
341  markerSymbolLayer->setSize( pointSize );
342  QgsMarkerSymbol *markerSymbol = new QgsMarkerSymbol( QgsSymbolLayerList() << markerSymbolLayer );
343 
344  QgsVectorTileBasicRendererStyle st1( QStringLiteral( "Polygons" ), QString(), QgsWkbTypes::PolygonGeometry );
345  st1.setFilterExpression( QStringLiteral( "geometry_type($geometry)='Polygon'" ) );
346  st1.setSymbol( fillSymbol );
347 
348  QgsVectorTileBasicRendererStyle st2( QStringLiteral( "Lines" ), QString(), QgsWkbTypes::LineGeometry );
349  st2.setFilterExpression( QStringLiteral( "geometry_type($geometry)='Line'" ) );
350  st2.setSymbol( lineSymbol );
351 
352  QgsVectorTileBasicRendererStyle st3( QStringLiteral( "Points" ), QString(), QgsWkbTypes::PointGeometry );
353  st3.setFilterExpression( QStringLiteral( "geometry_type($geometry)='Point'" ) );
354  st3.setSymbol( markerSymbol );
355 
356  QList<QgsVectorTileBasicRendererStyle> lst;
357  lst << st1 << st2 << st3;
358  return lst;
359 }
static QgsColorSchemeRegistry * colorSchemeRegistry()
Returns the application's color scheme registry, used for managing color schemes.
QColor fetchRandomStyleColor() const
Returns a random color for use with a new symbol style (e.g.
RAII class to pop scope from an expression context on destruction.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the scope.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the scope.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
QVariant evaluate()
Evaluate the feature and return the result.
bool isValid() const
Checks if this expression is valid.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:163
Container of fields for a vector layer.
Definition: qgsfields.h:45
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
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.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
virtual void setWidth(double width)
Sets the width of the line symbol layer.
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
virtual void setSize(double size)
Sets the symbol size.
A marker symbol type, for rendering Point and MultiPoint geometries.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setStrokeWidth(double strokeWidth)
void setFillColor(const QColor &color) override
Set fill color.
void setStrokeColor(const QColor &strokeColor) override
Set stroke color.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
void setFillColor(const QColor &color) override
Set fill color.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
virtual void setColor(const QColor &color)
The fill color.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:38
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:516
void renderFeature(const QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false, Qgis::VertexMarkerType currentVertexMarkerType=Qgis::VertexMarkerType::SemiTransparentCircle, double currentVertexMarkerSize=0.0) SIP_THROW(QgsCsException)
Render a feature.
Definition: qgssymbol.cpp:918
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:489
Range of tiles in a tile matrix to be rendered.
Definition: qgstiles.h:67
int zoomLevel() const
Returns tile's zoom level (Z)
Definition: qgstiles.h:47
Definition of map rendering of a subset of vector tile data.
void setFilterExpression(const QString &expr)
Sets filter expression (empty filter means that all features match)
void setSymbol(QgsSymbol *sym)
Sets symbol for rendering. Takes ownership of the symbol.
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const
Writes object content to given DOM element.
QgsVectorTileBasicRendererStyle(const QString &stName=QString(), const QString &laName=QString(), QgsWkbTypes::GeometryType geomType=QgsWkbTypes::UnknownGeometry)
Constructs a style object.
QgsVectorTileBasicRendererStyle & operator=(const QgsVectorTileBasicRendererStyle &other)
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Reads object content from given DOM element.
The default vector tile renderer implementation.
void renderTile(const QgsVectorTileRendererData &tile, QgsRenderContext &context) override
Renders given vector tile. Must be called between startRender/stopRender.
QList< QgsVectorTileBasicRendererStyle > styles() const
Returns list of styles of the renderer.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads renderer's properties from given XML element.
QMap< QString, QSet< QString > > usedAttributes(const QgsRenderContext &) override
Returns field names of sub-layers that will be used for rendering. Must be called between startRender...
QgsVectorTileBasicRenderer()
Constructs renderer with no styles.
void setStyles(const QList< QgsVectorTileBasicRendererStyle > &styles)
Sets list of styles of the renderer.
void startRender(QgsRenderContext &context, int tileZoom, const QgsTileRange &tileRange) override
Initializes rendering. It should be paired with a stopRender() call.
QString type() const override
Returns unique type name of the renderer implementation.
static QList< QgsVectorTileBasicRendererStyle > simpleStyle(const QColor &polygonFillColor, const QColor &polygonStrokeColor, double polygonStrokeWidth, const QColor &lineStrokeColor, double lineStrokeWidth, const QColor &pointFillColor, const QColor &pointStrokeColor, double pointSize)
Returns a list of styles to render all layers with the given fill/stroke colors, stroke widths and ma...
QgsVectorTileBasicRenderer * clone() const override
Returns a clone of the renderer.
static QList< QgsVectorTileBasicRendererStyle > simpleStyleWithRandomColors()
Returns a list of styles to render all layers, using random colors.
QSet< QString > requiredLayers(QgsRenderContext &context, int tileZoom) const override
Returns a list of the layers required for rendering.
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const override
Writes renderer's properties to given XML element.
void stopRender(QgsRenderContext &context) override
Finishes rendering and cleans up any resources.
Contains decoded features of a single vector tile and any other data necessary for rendering of it.
QPolygon tilePolygon() const
Returns polygon (made out of four corners of the tile) in screen coordinates calculated from render c...
QgsVectorTileFeatures features() const
Returns features of the tile grouped by sub-layer names.
QMap< QString, QgsFields > fields() const
Returns per-layer fields.
QgsTileXYZ id() const
Returns coordinates of the tile.
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:968
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
CORE_EXPORT QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
const double DEFAULT_LINE_WIDTH
Definition: qgis.h:1698
const double DEFAULT_POINT_SIZE
Magic number that determines the default point size for point symbols.
Definition: qgis.h:1697
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition: qgsrenderer.h:45
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:27
QMap< QString, QVector< QgsFeature > > QgsVectorTileFeatures
Features of a vector tile, grouped by sub-layer names (key of the map)