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