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