QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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 "qgsvectortilelayer.h"
22#include "qgsrendercontext.h"
23
24
25void QgsVectorTileBasicLabelingStyle::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
26{
27 elem.setAttribute( QStringLiteral( "name" ), mStyleName );
28 elem.setAttribute( QStringLiteral( "layer" ), mLayerName );
29 elem.setAttribute( QStringLiteral( "geometry" ), static_cast<int>( mGeometryType ) );
30 elem.setAttribute( QStringLiteral( "enabled" ), mEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
31 elem.setAttribute( QStringLiteral( "expression" ), mExpression );
32 elem.setAttribute( QStringLiteral( "min-zoom" ), mMinZoomLevel );
33 elem.setAttribute( QStringLiteral( "max-zoom" ), mMaxZoomLevel );
34
35 QDomDocument doc = elem.ownerDocument();
36 QDomElement elemLabelSettings = mLabelSettings.writeXml( doc, context );
37 elem.appendChild( elemLabelSettings );
38}
39
40void QgsVectorTileBasicLabelingStyle::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
41{
42 mStyleName = elem.attribute( QStringLiteral( "name" ) );
43 mLayerName = elem.attribute( QStringLiteral( "layer" ) );
44 mGeometryType = static_cast<Qgis::GeometryType>( elem.attribute( QStringLiteral( "geometry" ) ).toInt() );
45 mEnabled = elem.attribute( QStringLiteral( "enabled" ) ).toInt();
46 mExpression = elem.attribute( QStringLiteral( "expression" ) );
47 mMinZoomLevel = elem.attribute( QStringLiteral( "min-zoom" ) ).toInt();
48 mMaxZoomLevel = elem.attribute( QStringLiteral( "max-zoom" ) ).toInt();
49
50 QDomElement elemLabelSettings = elem.firstChildElement( QStringLiteral( "settings" ) );
51 mLabelSettings.readXml( elemLabelSettings, context );
52}
53
54
55//
56
57
59{
60}
61
63{
64 return QStringLiteral( "basic" );
65}
66
68{
70 l->mStyles = mStyles;
71 return l;
72}
73
75{
76 return new QgsVectorTileBasicLabelProvider( layer, mStyles );
77}
78
79void QgsVectorTileBasicLabeling::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
80{
81 QDomDocument doc = elem.ownerDocument();
82 QDomElement elemStyles = doc.createElement( QStringLiteral( "styles" ) );
83 for ( const QgsVectorTileBasicLabelingStyle &layerStyle : mStyles )
84 {
85 QDomElement elemStyle = doc.createElement( QStringLiteral( "style" ) );
86 layerStyle.writeXml( elemStyle, context );
87 elemStyles.appendChild( elemStyle );
88 }
89 elem.appendChild( elemStyles );
90}
91
92void QgsVectorTileBasicLabeling::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
93{
94 mStyles.clear();
95
96 QDomElement elemStyles = elem.firstChildElement( QStringLiteral( "styles" ) );
97 QDomElement elemStyle = elemStyles.firstChildElement( QStringLiteral( "style" ) );
98 while ( !elemStyle.isNull() )
99 {
101 layerStyle.readXml( elemStyle, context );
102 mStyles.append( layerStyle );
103 elemStyle = elemStyle.nextSiblingElement( QStringLiteral( "style" ) );
104 }
105}
106
107
108//
109
110
111QgsVectorTileBasicLabelProvider::QgsVectorTileBasicLabelProvider( QgsVectorTileLayer *layer, const QList<QgsVectorTileBasicLabelingStyle> &styles )
113 , mStyles( styles )
114{
115
116 for ( int i = 0; i < mStyles.count(); ++i )
117 {
118 const QgsVectorTileBasicLabelingStyle &style = mStyles[i];
119 //QgsFields fields = QgsVectorTileUtils::makeQgisFields( mRequiredFields[style.layerName()] );
120 QString providerId = QString::number( i );
121 QgsPalLayerSettings labelSettings = style.labelSettings();
122 mSubProviders.append( new QgsVectorLayerLabelProvider( style.geometryType(), QgsFields(), layer->crs(), providerId, &labelSettings, layer ) );
123 }
124}
125
126QMap<QString, QSet<QString> > QgsVectorTileBasicLabelProvider::usedAttributes( const QgsRenderContext &context, int tileZoom ) const
127{
128 QMap<QString, QSet<QString> > requiredFields;
129 for ( const QgsVectorTileBasicLabelingStyle &layerStyle : std::as_const( mStyles ) )
130 {
131 if ( !layerStyle.isActive( tileZoom ) )
132 continue;
133
134 if ( !layerStyle.filterExpression().isEmpty() )
135 {
136 QgsExpression expr( layerStyle.filterExpression() );
137 requiredFields[layerStyle.layerName()].unite( expr.referencedColumns() );
138 }
139
140 requiredFields[layerStyle.layerName()].unite( layerStyle.labelSettings().referencedFields( context ) );
141 }
142 return requiredFields;
143}
144
146{
147 QSet< QString > res;
148 for ( const QgsVectorTileBasicLabelingStyle &layerStyle : std::as_const( mStyles ) )
149 {
150 if ( layerStyle.isActive( tileZoom ) )
151 {
152 res.insert( layerStyle.layerName() );
153 }
154 }
155 return res;
156}
157
158void QgsVectorTileBasicLabelProvider::setFields( const QMap<QString, QgsFields> &perLayerFields )
159{
160 mPerLayerFields = perLayerFields;
161}
162
163QList<QgsAbstractLabelProvider *> QgsVectorTileBasicLabelProvider::subProviders()
164{
165 QList<QgsAbstractLabelProvider *> lst;
166 for ( QgsVectorLayerLabelProvider *subprovider : std::as_const( mSubProviders ) )
167 {
168 if ( subprovider ) // sub-providers that failed to initialize are set to null
169 lst << subprovider;
170 }
171 return lst;
172}
173
174bool QgsVectorTileBasicLabelProvider::prepare( QgsRenderContext &context, QSet<QString> &attributeNames )
175{
176 for ( QgsVectorLayerLabelProvider *provider : std::as_const( mSubProviders ) )
177 provider->setEngine( mEngine );
178
179 // populate sub-providers
180 for ( int i = 0; i < mSubProviders.count(); ++i )
181 {
182 QgsFields fields = mPerLayerFields[mStyles[i].layerName()];
183
184 QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Layer" ) ); // will be deleted by popper
185 scope->setFields( fields );
186 QgsExpressionContextScopePopper popper( context.expressionContext(), scope );
187
188 mSubProviders[i]->setFields( fields );
189 // check is required as fields are not available through the GUI, which can lead to isExpression wrongly set to true
190 mSubProviders[i]->mSettings.isExpression = !fields.names().contains( mSubProviders[i]->mSettings.fieldName );
191 if ( !mSubProviders[i]->prepare( context, attributeNames ) )
192 {
193 QgsDebugError( QStringLiteral( "Failed to prepare labeling for style index" ) + QString::number( i ) );
194 mSubProviders[i] = nullptr;
195 }
196 }
197 return true;
198}
199
201{
202 const QgsVectorTileFeatures tileData = tile.features();
203 const int zoomLevel = tile.renderZoomLevel();
204
205 for ( int i = 0; i < mStyles.count(); ++i )
206 {
207 const QgsVectorTileBasicLabelingStyle &layerStyle = mStyles.at( i );
208 if ( !layerStyle.isActive( zoomLevel ) )
209 continue;
210
211 QgsFields fields = mPerLayerFields[layerStyle.layerName()];
212
213 QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Layer" ) ); // will be deleted by popper
214 scope->setFields( fields );
215 QgsExpressionContextScopePopper popper( context.expressionContext(), scope );
216
217 QgsExpression filterExpression( layerStyle.filterExpression() );
218 filterExpression.prepare( &context.expressionContext() );
219
220 QgsVectorLayerLabelProvider *subProvider = mSubProviders[i];
221 if ( !subProvider )
222 continue; // sub-providers that failed to initialize are set to null
223
224 if ( layerStyle.layerName().isEmpty() )
225 {
226 // matching all layers
227 for ( const auto &features : tileData )
228 {
229 for ( const QgsFeature &f : features )
230 {
231 scope->setFeature( f );
232 if ( filterExpression.isValid() && !filterExpression.evaluate( &context.expressionContext() ).toBool() )
233 continue;
234
235 const Qgis::GeometryType featureType = QgsWkbTypes::geometryType( f.geometry().wkbType() );
236 if ( featureType == layerStyle.geometryType() )
237 {
238 subProvider->registerFeature( f, context );
239 }
240 else if ( featureType == Qgis::GeometryType::Polygon && layerStyle.geometryType() == Qgis::GeometryType::Point )
241 {
242 // be tolerant and permit labeling polygons with a point layer style, as some style definitions use this approach
243 // to label the polygon center
245 const QgsRectangle boundingBox = f.geometry().boundingBox();
246 centroid.setGeometry( f.geometry().poleOfInaccessibility( std::min( boundingBox.width(), boundingBox.height() ) / 20 ) );
247 subProvider->registerFeature( centroid, context );
248 }
249 }
250 }
251 }
252 else if ( tileData.contains( layerStyle.layerName() ) )
253 {
254 // matching one particular layer
255 for ( const QgsFeature &f : tileData[layerStyle.layerName()] )
256 {
257 scope->setFeature( f );
258 if ( filterExpression.isValid() && !filterExpression.evaluate( &context.expressionContext() ).toBool() )
259 continue;
260
261 const Qgis::GeometryType featureType = QgsWkbTypes::geometryType( f.geometry().wkbType() );
262 if ( featureType == layerStyle.geometryType() )
263 {
264 subProvider->registerFeature( f, context );
265 }
266 else if ( featureType == Qgis::GeometryType::Polygon && layerStyle.geometryType() == Qgis::GeometryType::Point )
267 {
268 // be tolerant and permit labeling polygons with a point layer style, as some style definitions use this approach
269 // to label the polygon center
271 const QgsRectangle boundingBox = f.geometry().boundingBox();
272 centroid.setGeometry( f.geometry().poleOfInaccessibility( std::min( boundingBox.width(), boundingBox.height() ) / 20 ) );
273 subProvider->registerFeature( centroid, context );
274 }
275 }
276 }
277 }
278}
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition: qgis.h:255
@ Polygon
Polygons.
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.
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
Container of fields for a vector layer.
Definition: qgsfields.h:45
QStringList names() const
Returns a list with field names.
Definition: qgsfields.cpp:143
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:81
Contains settings for how a map layer will be labeled.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context)
Read settings from a DOM element.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Write settings into a DOM element.
QString fieldName
Name of field (or an expression) to use for label text.
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 width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:236
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:243
Contains information about the context of a rendering operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
The QgsVectorLayerLabelProvider class implements a label provider for vector layers.
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)
Basic labeling configuration for vector tile layers.
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.
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...
Definition: qgswkbtypes.h:862
CORE_EXPORT QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
#define QgsDebugError(str)
Definition: qgslogger.h:38
QMap< QString, QVector< QgsFeature > > QgsVectorTileFeatures
Features of a vector tile, grouped by sub-layer names (key of the map)