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
16 #include "qgsvectortilebasicrenderer.h"
17
18 #include "qgsapplication.h"
19 #include "qgscolorschemeregistry.h"
20 #include "qgsexpressioncontextutils.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
QgsVectorTileBasicRendererStyle(const QString & stName,const QString & laName,QgsWkbTypes::GeometryType geomType)30 QgsVectorTileBasicRendererStyle::QgsVectorTileBasicRendererStyle( const QString &stName, const QString &laName, QgsWkbTypes::GeometryType geomType )
31 : mStyleName( stName )
32 , mLayerName( laName )
33 , mGeometryType( geomType )
34 {
35 }
36
QgsVectorTileBasicRendererStyle(const QgsVectorTileBasicRendererStyle & other)37 QgsVectorTileBasicRendererStyle::QgsVectorTileBasicRendererStyle( const QgsVectorTileBasicRendererStyle &other )
38 {
39 operator=( other );
40 }
41
operator =(const QgsVectorTileBasicRendererStyle & other)42 QgsVectorTileBasicRendererStyle &QgsVectorTileBasicRendererStyle::operator=( const QgsVectorTileBasicRendererStyle &other )
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
55 QgsVectorTileBasicRendererStyle::~QgsVectorTileBasicRendererStyle() = default;
56
setSymbol(QgsSymbol * sym)57 void QgsVectorTileBasicRendererStyle::setSymbol( QgsSymbol *sym )
58 {
59 mSymbol.reset( sym );
60 }
61
writeXml(QDomElement & elem,const QgsReadWriteContext & context) const62 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
readXml(const QDomElement & elem,const QgsReadWriteContext & context)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
101 ////////
102
103
QgsVectorTileBasicRenderer()104 QgsVectorTileBasicRenderer::QgsVectorTileBasicRenderer()
105 {
106 }
107
type() const108 QString QgsVectorTileBasicRenderer::type() const
109 {
110 return QStringLiteral( "basic" );
111 }
112
clone() const113 QgsVectorTileBasicRenderer *QgsVectorTileBasicRenderer::clone() const
114 {
115 QgsVectorTileBasicRenderer *r = new QgsVectorTileBasicRenderer;
116 r->mStyles = mStyles;
117 r->mStyles.detach(); // make a deep copy to make sure symbols get cloned
118 return r;
119 }
120
startRender(QgsRenderContext & context,int tileZoom,const QgsTileRange & tileRange)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
usedAttributes(const QgsRenderContext &)143 QMap<QString, QSet<QString> > QgsVectorTileBasicRenderer::usedAttributes( const QgsRenderContext & )
144 {
145 return mRequiredFields;
146 }
147
requiredLayers(QgsRenderContext &,int tileZoom) const148 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
stopRender(QgsRenderContext & context)161 void QgsVectorTileBasicRenderer::stopRender( QgsRenderContext &context )
162 {
163 Q_UNUSED( context )
164 }
165
renderTile(const QgsVectorTileRendererData & tile,QgsRenderContext & context)166 void QgsVectorTileBasicRenderer::renderTile( const QgsVectorTileRendererData &tile, QgsRenderContext &context )
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 ) )
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
writeXml(QDomElement & elem,const QgsReadWriteContext & context) const264 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
readXml(const QDomElement & elem,const QgsReadWriteContext & context)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 {
285 QgsVectorTileBasicRendererStyle layerStyle;
286 layerStyle.readXml( elemStyle, context );
287 mStyles.append( layerStyle );
288 elemStyle = elemStyle.nextSiblingElement( QStringLiteral( "style" ) );
289 }
290 }
291
setStyles(const QList<QgsVectorTileBasicRendererStyle> & styles)292 void QgsVectorTileBasicRenderer::setStyles( const QList<QgsVectorTileBasicRendererStyle> &styles )
293 {
294 mStyles = styles;
295 }
296
styles() const297 QList<QgsVectorTileBasicRendererStyle> QgsVectorTileBasicRenderer::styles() const
298 {
299 return mStyles;
300 }
301
simpleStyleWithRandomColors()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
simpleStyle(const QColor & polygonFillColor,const QColor & polygonStrokeColor,double polygonStrokeWidth,const QColor & lineStrokeColor,double lineStrokeWidth,const QColor & pointFillColor,const QColor & pointStrokeColor,double pointSize)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
338 QgsSimpleMarkerSymbolLayer *markerSymbolLayer = new QgsSimpleMarkerSymbolLayer;
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 }
360