QGIS API Documentation 3.99.0-Master (09f76ad7019)
Loading...
Searching...
No Matches
qgsvectortilebasiclabeling.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectortilebasiclabeling.cpp
3 --------------------------------------
4 Date : April 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
19#include "qgslogger.h"
20#include "qgsrendercontext.h"
21#include "qgsvectortilelayer.h"
23
24#include <QString>
25
26using namespace Qt::StringLiterals;
27
28void QgsVectorTileBasicLabelingStyle::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
29{
30 elem.setAttribute( u"name"_s, mStyleName );
31 elem.setAttribute( u"layer"_s, mLayerName );
32 elem.setAttribute( u"geometry"_s, static_cast<int>( mGeometryType ) );
33 elem.setAttribute( u"enabled"_s, mEnabled ? u"1"_s : u"0"_s );
34 elem.setAttribute( u"expression"_s, mExpression );
35 elem.setAttribute( u"min-zoom"_s, mMinZoomLevel );
36 elem.setAttribute( u"max-zoom"_s, mMaxZoomLevel );
37
38 QDomDocument doc = elem.ownerDocument();
39 QDomElement elemLabelSettings = mLabelSettings.writeXml( doc, context );
40 elem.appendChild( elemLabelSettings );
41}
42
43void QgsVectorTileBasicLabelingStyle::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
44{
45 mStyleName = elem.attribute( u"name"_s );
46 mLayerName = elem.attribute( u"layer"_s );
47 mGeometryType = static_cast<Qgis::GeometryType>( elem.attribute( u"geometry"_s ).toInt() );
48 mEnabled = elem.attribute( u"enabled"_s ).toInt();
49 mExpression = elem.attribute( u"expression"_s );
50 mMinZoomLevel = elem.attribute( u"min-zoom"_s ).toInt();
51 mMaxZoomLevel = elem.attribute( u"max-zoom"_s ).toInt();
52
53 QDomElement elemLabelSettings = elem.firstChildElement( u"settings"_s );
54 mLabelSettings.readXml( elemLabelSettings, context );
55}
56
57
58//
59
60
64
66{
67 return u"basic"_s;
68}
69
76
81
82void QgsVectorTileBasicLabeling::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
83{
84 QDomDocument doc = elem.ownerDocument();
85 QDomElement elemStyles = doc.createElement( u"styles"_s );
86 for ( const QgsVectorTileBasicLabelingStyle &layerStyle : mStyles )
87 {
88 QDomElement elemStyle = doc.createElement( u"style"_s );
89 layerStyle.writeXml( elemStyle, context );
90 elemStyles.appendChild( elemStyle );
91 }
92 elem.appendChild( elemStyles );
93}
94
95void QgsVectorTileBasicLabeling::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
96{
97 mStyles.clear();
98
99 QDomElement elemStyles = elem.firstChildElement( u"styles"_s );
100 QDomElement elemStyle = elemStyles.firstChildElement( u"style"_s );
101 while ( !elemStyle.isNull() )
102 {
104 layerStyle.readXml( elemStyle, context );
105 mStyles.append( layerStyle );
106 elemStyle = elemStyle.nextSiblingElement( u"style"_s );
107 }
108}
109
110
111//
112
113
116 , mStyles( styles )
117{
118
119 for ( int i = 0; i < mStyles.count(); ++i )
120 {
121 const QgsVectorTileBasicLabelingStyle &style = mStyles[i];
122 //QgsFields fields = QgsVectorTileUtils::makeQgisFields( mRequiredFields[style.layerName()] );
123 QString providerId = QString::number( i );
124 QgsPalLayerSettings labelSettings = style.labelSettings();
125 mSubProviders.append( new QgsVectorLayerLabelProvider( style.geometryType(), QgsFields(), layer->crs(), providerId, &labelSettings, layer ) );
126 }
127}
128
129QMap<QString, QSet<QString> > QgsVectorTileBasicLabelProvider::usedAttributes( const QgsRenderContext &context, int tileZoom ) const
130{
131 QMap<QString, QSet<QString> > requiredFields;
132 for ( const QgsVectorTileBasicLabelingStyle &layerStyle : std::as_const( mStyles ) )
133 {
134 if ( !layerStyle.isActive( tileZoom ) )
135 continue;
136
137 if ( !layerStyle.filterExpression().isEmpty() )
138 {
139 QgsExpression expr( layerStyle.filterExpression() );
140 requiredFields[layerStyle.layerName()].unite( expr.referencedColumns() );
141 }
142
143 requiredFields[layerStyle.layerName()].unite( layerStyle.labelSettings().referencedFields( context ) );
144 }
145 return requiredFields;
146}
147
149{
150 QSet< QString > res;
151 for ( const QgsVectorTileBasicLabelingStyle &layerStyle : std::as_const( mStyles ) )
152 {
153 if ( layerStyle.isActive( tileZoom ) )
154 {
155 res.insert( layerStyle.layerName() );
156 }
157 }
158 return res;
159}
160
161void QgsVectorTileBasicLabelProvider::setFields( const QMap<QString, QgsFields> &perLayerFields )
162{
163 mPerLayerFields = perLayerFields;
164}
165
166QList<QgsAbstractLabelProvider *> QgsVectorTileBasicLabelProvider::subProviders()
167{
168 QList<QgsAbstractLabelProvider *> lst;
169 for ( QgsVectorLayerLabelProvider *subprovider : std::as_const( mSubProviders ) )
170 {
171 if ( subprovider ) // sub-providers that failed to initialize are set to null
172 lst << subprovider;
173 }
174 return lst;
175}
176
177bool QgsVectorTileBasicLabelProvider::prepare( QgsRenderContext &context, QSet<QString> &attributeNames )
178{
179 for ( QgsVectorLayerLabelProvider *provider : std::as_const( mSubProviders ) )
180 provider->setEngine( mEngine );
181
182 // populate sub-providers
183 for ( int i = 0; i < mSubProviders.count(); ++i )
184 {
185 QgsFields fields = mPerLayerFields[mStyles[i].layerName()];
186
187 QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Layer" ) ); // will be deleted by popper
188 scope->setFields( fields );
189 QgsExpressionContextScopePopper popper( context.expressionContext(), scope );
190
191 mSubProviders[i]->setFields( fields );
192 // check is required as fields are not available through the GUI, which can lead to isExpression wrongly set to true
193 mSubProviders[i]->mSettings.isExpression = !fields.names().contains( mSubProviders[i]->mSettings.fieldName );
194 if ( !mSubProviders[i]->prepare( context, attributeNames ) )
195 {
196 QgsDebugError( u"Failed to prepare labeling for style index"_s + QString::number( i ) );
197 mSubProviders[i] = nullptr;
198 }
199 }
200 return true;
201}
202
204{
205 const QgsVectorTileFeatures tileData = tile.features();
206 const int zoomLevel = tile.renderZoomLevel();
207
208 for ( int i = 0; i < mStyles.count(); ++i )
209 {
210 const QgsVectorTileBasicLabelingStyle &layerStyle = mStyles.at( i );
211 if ( !layerStyle.isActive( zoomLevel ) )
212 continue;
213
214 QgsFields fields = mPerLayerFields[layerStyle.layerName()];
215
216 QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Layer" ) ); // will be deleted by popper
217 scope->setFields( fields );
218 QgsExpressionContextScopePopper popper( context.expressionContext(), scope );
219
220 QgsExpression filterExpression( layerStyle.filterExpression() );
221 filterExpression.prepare( &context.expressionContext() );
222
223 QgsVectorLayerLabelProvider *subProvider = mSubProviders[i];
224 if ( !subProvider )
225 continue; // sub-providers that failed to initialize are set to null
226
227 if ( layerStyle.layerName().isEmpty() )
228 {
229 // matching all layers
230 for ( const auto &features : tileData )
231 {
232 for ( const QgsFeature &f : features )
233 {
234 scope->setFeature( f );
235 if ( filterExpression.isValid() && !filterExpression.evaluate( &context.expressionContext() ).toBool() )
236 continue;
237
238 const Qgis::GeometryType featureType = QgsWkbTypes::geometryType( f.geometry().wkbType() );
239 if ( featureType == layerStyle.geometryType() )
240 {
241 subProvider->registerFeature( f, context );
242 }
243 else if ( featureType == Qgis::GeometryType::Polygon && layerStyle.geometryType() == Qgis::GeometryType::Point )
244 {
245 // be tolerant and permit labeling polygons with a point layer style, as some style definitions use this approach
246 // to label the polygon center
247 QgsFeature centroid = f;
248 const QgsRectangle boundingBox = f.geometry().boundingBox();
249 centroid.setGeometry( f.geometry().poleOfInaccessibility( std::min( boundingBox.width(), boundingBox.height() ) / 20 ) );
250 subProvider->registerFeature( centroid, context );
251 }
252 }
253 }
254 }
255 else if ( tileData.contains( layerStyle.layerName() ) )
256 {
257 // matching one particular layer
258 for ( const QgsFeature &f : tileData[layerStyle.layerName()] )
259 {
260 scope->setFeature( f );
261 if ( filterExpression.isValid() && !filterExpression.evaluate( &context.expressionContext() ).toBool() )
262 continue;
263
264 const Qgis::GeometryType featureType = QgsWkbTypes::geometryType( f.geometry().wkbType() );
265 if ( featureType == layerStyle.geometryType() )
266 {
267 subProvider->registerFeature( f, context );
268 }
269 else if ( featureType == Qgis::GeometryType::Polygon && layerStyle.geometryType() == Qgis::GeometryType::Point )
270 {
271 // be tolerant and permit labeling polygons with a point layer style, as some style definitions use this approach
272 // to label the polygon center
273 QgsFeature centroid = f;
274 const QgsRectangle boundingBox = f.geometry().boundingBox();
275 centroid.setGeometry( f.geometry().poleOfInaccessibility( std::min( boundingBox.width(), boundingBox.height() ) / 20 ) );
276 subProvider->registerFeature( centroid, context );
277 }
278 }
279 }
280 }
281}
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:365
@ Point
Points.
Definition qgis.h:366
@ Polygon
Polygons.
Definition qgis.h:368
const QgsLabelingEngine * mEngine
Associated labeling engine.
QgsMapLayer * layer() const
Returns the associated layer, or nullptr if no layer is associated with the provider.
QString providerId() const
Returns provider ID - useful in case there is more than one label provider within a layer (e....
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.
Handles 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:60
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Container of fields for a vector layer.
Definition qgsfields.h:46
QStringList names
Definition qgsfields.h:51
Contains settings for how a map layer will be labeled.
A container for the context for various read/write operations on objects.
A rectangle specified with double values.
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
QgsVectorLayerLabelProvider(QgsVectorLayer *layer, const QString &providerId, bool withFeatureLoop, const QgsPalLayerSettings *settings, const QString &layerName=QString())
Convenience constructor to initialize the provider from given vector layer.
QgsPalLayerSettings mSettings
Layer's labeling configuration.
virtual QList< QgsLabelFeature * > registerFeature(const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry=QgsGeometry(), const QgsSymbol *symbol=nullptr)
Register a feature for labeling as one or more QgsLabelFeature objects stored into mLabels.
Implementation class for QgsVectorTileBasicLabeling.
QMap< QString, QgsFields > mPerLayerFields
Names of required fields for each sub-layer (only valid between startRender/stopRender calls).
bool prepare(QgsRenderContext &context, QSet< QString > &attributeNames) override
Prepare for registration of features.
void setFields(const QMap< QString, QgsFields > &perLayerFields) override
Sets fields for each sub-layer.
void registerTileFeatures(const QgsVectorTileRendererData &tile, QgsRenderContext &context) override
Registers label features for given tile to the labeling engine.
QMap< QString, QSet< QString > > usedAttributes(const QgsRenderContext &context, int tileZoom) const override
Returns field names for each sub-layer that are required for labeling.
QSet< QString > requiredLayers(QgsRenderContext &context, int tileZoom) const override
Returns a list of the layers required for labeling.
QList< QgsAbstractLabelProvider * > subProviders() override
Returns list of child providers - useful if the provider needs to put labels into more layers with di...
Configuration of a single style within QgsVectorTileBasicLabeling.
QgsPalLayerSettings labelSettings() const
Returns labeling configuration of this style.
QString layerName() const
Returns name of the sub-layer to render (empty layer means that all layers match).
QString filterExpression() const
Returns filter expression (empty filter means that all features match).
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const
Writes object content to given DOM element.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Reads object content from given DOM element.
bool isActive(int zoomLevel) const
Returns whether the style is active at given zoom level (also checks "enabled" flag).
Qgis::GeometryType geometryType() const
Returns type of the geometry that will be used (point / line / polygon).
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads labeling properties from given XML element.
QgsVectorTileLabelProvider * provider(QgsVectorTileLayer *layer) const override SIP_SKIP
Factory for label provider implementation.
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const override
Writes labeling properties to given XML element.
QgsVectorTileLabeling * clone() const override SIP_FACTORY
Returns a new copy of the object.
QString type() const override
Unique type string of the labeling configuration implementation.
Internal base class for implementation of label providers for vector tile labeling.
QgsVectorTileLabelProvider(QgsVectorTileLayer *layer)
Constructs base label provider class for the given vector tile layer.
Base class for labeling configuration classes for vector tile layers.
Implements a map layer that is dedicated to rendering of vector tiles.
Contains decoded features of a single vector tile and any other data necessary for rendering of it.
QgsVectorTileFeatures features() const
Returns features of the tile grouped by sub-layer names.
int renderZoomLevel() const
Returns the zoom level corresponding to the target render.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
#define QgsDebugError(str)
Definition qgslogger.h:59
QMap< QString, QVector< QgsFeature > > QgsVectorTileFeatures
Features of a vector tile, grouped by sub-layer names (key of the map).