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
16 #include "qgsvectortilebasiclabeling.h"
17
18 #include "qgsexpressioncontextutils.h"
19 #include "qgslogger.h"
20 #include "qgsvectortilelayer.h"
21 #include "qgsvectortilerenderer.h"
22 #include "qgsvectortileutils.h"
23
24
25
writeXml(QDomElement & elem,const QgsReadWriteContext & context) const26 void 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
readXml(const QDomElement & elem,const QgsReadWriteContext & context)41 void 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
QgsVectorTileBasicLabeling()59 QgsVectorTileBasicLabeling::QgsVectorTileBasicLabeling()
60 {
61 }
62
type() const63 QString QgsVectorTileBasicLabeling::type() const
64 {
65 return QStringLiteral( "basic" );
66 }
67
clone() const68 QgsVectorTileLabeling *QgsVectorTileBasicLabeling::clone() const
69 {
70 QgsVectorTileBasicLabeling *l = new QgsVectorTileBasicLabeling;
71 l->mStyles = mStyles;
72 return l;
73 }
74
provider(QgsVectorTileLayer * layer) const75 QgsVectorTileLabelProvider *QgsVectorTileBasicLabeling::provider( QgsVectorTileLayer *layer ) const
76 {
77 return new QgsVectorTileBasicLabelProvider( layer, mStyles );
78 }
79
writeXml(QDomElement & elem,const QgsReadWriteContext & context) const80 void 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
readXml(const QDomElement & elem,const QgsReadWriteContext & context)93 void 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 {
101 QgsVectorTileBasicLabelingStyle layerStyle;
102 layerStyle.readXml( elemStyle, context );
103 mStyles.append( layerStyle );
104 elemStyle = elemStyle.nextSiblingElement( QStringLiteral( "style" ) );
105 }
106 }
107
108
109 //
110
111
QgsVectorTileBasicLabelProvider(QgsVectorTileLayer * layer,const QList<QgsVectorTileBasicLabelingStyle> & styles)112 QgsVectorTileBasicLabelProvider::QgsVectorTileBasicLabelProvider( QgsVectorTileLayer *layer, const QList<QgsVectorTileBasicLabelingStyle> &styles )
113 : QgsVectorTileLabelProvider( layer )
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
usedAttributes(const QgsRenderContext & context,int tileZoom) const127 QMap<QString, QSet<QString> > QgsVectorTileBasicLabelProvider::usedAttributes( const QgsRenderContext &context, int tileZoom ) const
128 {
129 QMap<QString, QSet<QString> > requiredFields;
130 for ( const QgsVectorTileBasicLabelingStyle &layerStyle : qgis::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
requiredLayers(QgsRenderContext &,int tileZoom) const146 QSet<QString> QgsVectorTileBasicLabelProvider::requiredLayers( QgsRenderContext &, int tileZoom ) const
147 {
148 QSet< QString > res;
149 for ( const QgsVectorTileBasicLabelingStyle &layerStyle : qgis::as_const( mStyles ) )
150 {
151 if ( layerStyle.isActive( tileZoom ) )
152 {
153 res.insert( layerStyle.layerName() );
154 }
155 }
156 return res;
157 }
158
setFields(const QMap<QString,QgsFields> & perLayerFields)159 void QgsVectorTileBasicLabelProvider::setFields( const QMap<QString, QgsFields> &perLayerFields )
160 {
161 mPerLayerFields = perLayerFields;
162 }
163
subProviders()164 QList<QgsAbstractLabelProvider *> QgsVectorTileBasicLabelProvider::subProviders()
165 {
166 QList<QgsAbstractLabelProvider *> lst;
167 for ( QgsVectorLayerLabelProvider *subprovider : qgis::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
prepare(QgsRenderContext & context,QSet<QString> & attributeNames)175 bool QgsVectorTileBasicLabelProvider::prepare( QgsRenderContext &context, QSet<QString> &attributeNames )
176 {
177 for ( QgsVectorLayerLabelProvider *provider : qgis::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 if ( !mSubProviders[i]->prepare( context, attributeNames ) )
191 {
192 QgsDebugMsg( QStringLiteral( "Failed to prepare labeling for style index" ) + QString::number( i ) );
193 mSubProviders[i] = nullptr;
194 }
195 }
196 return true;
197 }
198
registerTileFeatures(const QgsVectorTileRendererData & tile,QgsRenderContext & context)199 void QgsVectorTileBasicLabelProvider::registerTileFeatures( const QgsVectorTileRendererData &tile, QgsRenderContext &context )
200 {
201 const QgsVectorTileFeatures tileData = tile.features();
202 int zoomLevel = tile.id().zoomLevel();
203
204 for ( int i = 0; i < mStyles.count(); ++i )
205 {
206 const QgsVectorTileBasicLabelingStyle &layerStyle = mStyles.at( i );
207 if ( !layerStyle.isActive( zoomLevel ) )
208 continue;
209
210 QgsFields fields = mPerLayerFields[layerStyle.layerName()];
211
212 QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Layer" ) ); // will be deleted by popper
213 scope->setFields( fields );
214 QgsExpressionContextScopePopper popper( context.expressionContext(), scope );
215
216 QgsExpression filterExpression( layerStyle.filterExpression() );
217 filterExpression.prepare( &context.expressionContext() );
218
219 QgsVectorLayerLabelProvider *subProvider = mSubProviders[i];
220 if ( !subProvider )
221 continue; // sub-providers that failed to initialize are set to null
222
223 if ( layerStyle.layerName().isEmpty() )
224 {
225 // matching all layers
226 for ( QString layerName : tileData.keys() )
227 {
228 for ( const QgsFeature &f : tileData[layerName] )
229 {
230 scope->setFeature( f );
231 if ( filterExpression.isValid() && !filterExpression.evaluate( &context.expressionContext() ).toBool() )
232 continue;
233
234 if ( QgsWkbTypes::geometryType( f.geometry().wkbType() ) == layerStyle.geometryType() )
235 subProvider->registerFeature( f, context );
236 }
237 }
238 }
239 else if ( tileData.contains( layerStyle.layerName() ) )
240 {
241 // matching one particular layer
242 for ( const QgsFeature &f : tileData[layerStyle.layerName()] )
243 {
244 scope->setFeature( f );
245 if ( filterExpression.isValid() && !filterExpression.evaluate( &context.expressionContext() ).toBool() )
246 continue;
247
248 if ( QgsWkbTypes::geometryType( f.geometry().wkbType() ) == layerStyle.geometryType() )
249 subProvider->registerFeature( f, context );
250 }
251 }
252 }
253 }
254