QGIS API Documentation 3.28.14-Firenze (exported)
Loading...
Searching...
No Matches
qgsvectortilelayerrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectortilelayerrenderer.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
17
18#include <QElapsedTimer>
19
21#include "qgsfeedback.h"
22#include "qgslogger.h"
23
26#include "qgsvectortilelayer.h"
27#include "qgsvectortileloader.h"
28#include "qgsvectortileutils.h"
29
30#include "qgslabelingengine.h"
32#include "qgsmapclippingutils.h"
33#include "qgsrendercontext.h"
34
36 : QgsMapLayerRenderer( layer->id(), &context )
37 , mSourceType( layer->sourceType() )
38 , mSourcePath( layer->sourcePath() )
39 , mRenderer( layer->renderer()->clone() )
40 , mDrawTileBoundaries( layer->isTileBorderRenderingEnabled() )
41 , mFeedback( new QgsFeedback )
42 , mSelectedFeatures( layer->selectedFeatures() )
43 , mLayerOpacity( layer->opacity() )
44 , mTileMatrixSet( layer->tileMatrixSet() )
45{
46
47 QgsDataSourceUri dsUri;
48 dsUri.setEncodedUri( layer->source() );
49 mAuthCfg = dsUri.authConfigId();
50 mHeaders = dsUri.httpHeaders();
51
52 if ( QgsLabelingEngine *engine = context.labelingEngine() )
53 {
54 if ( layer->labeling() )
55 {
56 mLabelProvider = layer->labeling()->provider( layer );
57 if ( mLabelProvider )
58 {
59 engine->addProvider( mLabelProvider );
60 }
61 }
62 }
63
65}
66
68{
70
71 if ( ctx.renderingStopped() )
72 return false;
73
74 const QgsScopedQPainterState painterState( ctx.painter() );
75
76 if ( !mClippingRegions.empty() )
77 {
78 bool needsPainterClipPath = false;
79 const QPainterPath path = QgsMapClippingUtils::calculatePainterClipRegion( mClippingRegions, *renderContext(), QgsMapLayerType::VectorTileLayer, needsPainterClipPath );
80 if ( needsPainterClipPath )
81 renderContext()->painter()->setClipPath( path, Qt::IntersectClip );
82 }
83
84 QElapsedTimer tTotal;
85 tTotal.start();
86
87 const double tileRenderScale = mTileMatrixSet.scaleForRenderContext( ctx );
88 QgsDebugMsgLevel( QStringLiteral( "Vector tiles rendering extent: " ) + ctx.extent().toString( -1 ), 2 );
89 QgsDebugMsgLevel( QStringLiteral( "Vector tiles map scale 1 : %1" ).arg( tileRenderScale ), 2 );
90
91 mTileZoom = mTileMatrixSet.scaleToZoomLevel( tileRenderScale );
92 QgsDebugMsgLevel( QStringLiteral( "Vector tiles zoom level: %1" ).arg( mTileZoom ), 2 );
93
94 mTileMatrix = mTileMatrixSet.tileMatrix( mTileZoom );
95
96 mTileRange = mTileMatrix.tileRangeFromExtent( ctx.extent() );
97 QgsDebugMsgLevel( QStringLiteral( "Vector tiles range X: %1 - %2 Y: %3 - %4" )
98 .arg( mTileRange.startColumn() ).arg( mTileRange.endColumn() )
99 .arg( mTileRange.startRow() ).arg( mTileRange.endRow() ), 2 );
100
101 // view center is used to sort the order of tiles for fetching and rendering
102 const QPointF viewCenter = mTileMatrix.mapToTileCoordinates( ctx.extent().center() );
103
104 if ( !mTileRange.isValid() )
105 {
106 QgsDebugMsgLevel( QStringLiteral( "Vector tiles - outside of range" ), 2 );
107 return true; // nothing to do
108 }
109
110 const bool isAsync = ( mSourceType == QLatin1String( "xyz" ) );
111
112 if ( mSourceType == QLatin1String( "xyz" ) && mSourcePath.contains( QLatin1String( "{usage}" ) ) )
113 {
114 switch ( renderContext()->rendererUsage() )
115 {
117 mSourcePath.replace( QLatin1String( "{usage}" ), QLatin1String( "view" ) );
118 break;
120 mSourcePath.replace( QLatin1String( "{usage}" ), QLatin1String( "export" ) );
121 break;
123 mSourcePath.replace( QLatin1String( "{usage}" ), QString() );
124 break;
125 }
126 }
127
128 std::unique_ptr<QgsVectorTileLoader> asyncLoader;
129 QList<QgsVectorTileRawData> rawTiles;
130 if ( !isAsync )
131 {
132 QElapsedTimer tFetch;
133 tFetch.start();
134 rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, mTileMatrix, viewCenter, mTileRange, mAuthCfg, mHeaders, mFeedback.get() );
135 QgsDebugMsgLevel( QStringLiteral( "Tile fetching time: %1" ).arg( tFetch.elapsed() / 1000. ), 2 );
136 QgsDebugMsgLevel( QStringLiteral( "Fetched tiles: %1" ).arg( rawTiles.count() ), 2 );
137 }
138 else
139 {
140 asyncLoader.reset( new QgsVectorTileLoader( mSourcePath, mTileMatrix, mTileRange, viewCenter, mAuthCfg, mHeaders, mFeedback.get() ) );
141 QObject::connect( asyncLoader.get(), &QgsVectorTileLoader::tileRequestFinished, asyncLoader.get(), [this]( const QgsVectorTileRawData & rawTile )
142 {
143 QgsDebugMsgLevel( QStringLiteral( "Got tile asynchronously: " ) + rawTile.id.toString(), 2 );
144 if ( !rawTile.data.isEmpty() )
145 decodeAndDrawTile( rawTile );
146 } );
147 }
148
149 if ( ctx.renderingStopped() )
150 return false;
151
152 // add @zoom_level variable which can be used in styling
153 QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Tiles" ) ); // will be deleted by popper
154 scope->setVariable( QStringLiteral( "zoom_level" ), mTileZoom, true );
155 scope->setVariable( QStringLiteral( "vector_tile_zoom" ), mTileMatrixSet.scaleToZoom( tileRenderScale ), true );
156 const QgsExpressionContextScopePopper popper( ctx.expressionContext(), scope );
157
158 mRenderer->startRender( *renderContext(), mTileZoom, mTileRange );
159
160 // Draw background style if present
161 mRenderer->renderBackground( ctx );
162
163 QMap<QString, QSet<QString> > requiredFields = mRenderer->usedAttributes( ctx );
164
165 if ( mLabelProvider )
166 {
167 const QMap<QString, QSet<QString> > requiredFieldsLabeling = mLabelProvider->usedAttributes( ctx, mTileZoom );
168 for ( auto it = requiredFieldsLabeling.begin(); it != requiredFieldsLabeling.end(); ++it )
169 {
170 requiredFields[it.key()].unite( it.value() );
171 }
172 }
173
174 for ( auto it = requiredFields.constBegin(); it != requiredFields.constEnd(); ++it )
175 mPerLayerFields[it.key()] = QgsVectorTileUtils::makeQgisFields( it.value() );
176
177 mRequiredLayers = mRenderer->requiredLayers( ctx, mTileZoom );
178
179 if ( mLabelProvider )
180 {
181 mLabelProvider->setFields( mPerLayerFields );
182 QSet<QString> attributeNames; // we don't need this - already got referenced columns in provider constructor
183 if ( !mLabelProvider->prepare( ctx, attributeNames ) )
184 {
185 ctx.labelingEngine()->removeProvider( mLabelProvider );
186 mLabelProvider = nullptr; // provider is deleted by the engine
187 }
188 else
189 {
190 mRequiredLayers.unite( mLabelProvider->requiredLayers( ctx, mTileZoom ) );
191 }
192 }
193
194 if ( !isAsync )
195 {
196 for ( const QgsVectorTileRawData &rawTile : std::as_const( rawTiles ) )
197 {
198 if ( ctx.renderingStopped() )
199 break;
200
201 decodeAndDrawTile( rawTile );
202 }
203 }
204 else
205 {
206 // Block until tiles are fetched and rendered. If the rendering gets canceled at some point,
207 // the async loader will catch the signal, abort requests and return from downloadBlocking()
208 asyncLoader->downloadBlocking();
209 if ( !asyncLoader->error().isEmpty() )
210 mErrors.append( asyncLoader->error() );
211 }
212
214 mRenderer->renderSelectedFeatures( mSelectedFeatures, ctx );
215
216 mRenderer->stopRender( ctx );
217
218 QgsDebugMsgLevel( QStringLiteral( "Total time for decoding: %1" ).arg( mTotalDecodeTime / 1000. ), 2 );
219 QgsDebugMsgLevel( QStringLiteral( "Drawing time: %1" ).arg( mTotalDrawTime / 1000. ), 2 );
220 QgsDebugMsgLevel( QStringLiteral( "Total time: %1" ).arg( tTotal.elapsed() / 1000. ), 2 );
221
222 return !ctx.renderingStopped();
223}
224
229
230void QgsVectorTileLayerRenderer::decodeAndDrawTile( const QgsVectorTileRawData &rawTile )
231{
233
234 QgsDebugMsgLevel( QStringLiteral( "Drawing tile " ) + rawTile.id.toString(), 2 );
235
236 QElapsedTimer tLoad;
237 tLoad.start();
238
239 // currently only MVT encoding supported
240 QgsVectorTileMVTDecoder decoder( mTileMatrixSet );
241 if ( !decoder.decode( rawTile.id, rawTile.data ) )
242 {
243 QgsDebugMsgLevel( QStringLiteral( "Failed to parse raw tile data! " ) + rawTile.id.toString(), 2 );
244 return;
245 }
246
247 if ( ctx.renderingStopped() )
248 return;
249
251
252 QgsVectorTileRendererData tile( rawTile.id );
253 tile.setFields( mPerLayerFields );
254 tile.setFeatures( decoder.layerFeatures( mPerLayerFields, ct, &mRequiredLayers ) );
255
256 try
257 {
258 tile.setTilePolygon( QgsVectorTileUtils::tilePolygon( rawTile.id, ct, mTileMatrix, ctx.mapToPixel() ) );
259 }
260 catch ( QgsCsException & )
261 {
262 QgsDebugMsgLevel( QStringLiteral( "Failed to generate tile polygon " ) + rawTile.id.toString(), 2 );
263 return;
264 }
265
266 mTotalDecodeTime += tLoad.elapsed();
267
268 // calculate tile polygon in screen coordinates
269
270 if ( ctx.renderingStopped() )
271 return;
272
273 {
274 // set up clipping so that rendering does not go behind tile's extent
275 const QgsScopedQPainterState savePainterState( ctx.painter() );
276 // we have to intersect with any existing painter clip regions, or we risk overwriting valid clip
277 // regions setup outside of the vector tile renderer (e.g. layout map clip region)
278 ctx.painter()->setClipRegion( QRegion( tile.tilePolygon() ), Qt::IntersectClip );
279
280 QElapsedTimer tDraw;
281 tDraw.start();
282
283 mRenderer->renderTile( tile, ctx );
284 mTotalDrawTime += tDraw.elapsed();
285 }
286
287 if ( mLabelProvider )
288 mLabelProvider->registerTileFeatures( tile, ctx );
289
290 if ( mDrawTileBoundaries )
291 {
292 const QgsScopedQPainterState savePainterState( ctx.painter() );
293 ctx.painter()->setClipping( false );
294
295 QPen pen( Qt::red );
296 pen.setWidth( 3 );
297 QBrush brush( QColor( 255, 0, 0, 40 ), Qt::BrushStyle::Dense3Pattern );
298
299 ctx.painter()->setPen( pen );
300 ctx.painter()->setBrush( brush );
301 ctx.painter()->drawPolygon( tile.tilePolygon() );
302#if 0
303 ctx.painter()->setBrush( QBrush( QColor( 255, 0, 0 ) ) );
304 ctx.painter()->drawText( tile.tilePolygon().boundingRect().center(), tile.id().toString() );
305#endif
306 }
307}
@ DrawSelection
Whether vector selections should be shown in the rendered map.
@ UseAdvancedEffects
Enable layer opacity and blending effects.
@ Export
Renderer used for printing or exporting to a file.
@ View
Renderer used for displaying on screen.
@ Unknown
Renderer used for unknown usage.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Class for storing the component parts of a RDBMS data source URI (e.g.
void setEncodedUri(const QByteArray &uri)
Sets the complete encoded uri.
QgsHttpHeaders httpHeaders() const
Returns http headers.
QString authConfigId() const
Returns any associated authentication configuration ID stored in the URI.
RAII class to pop scope from an expression context on destruction.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:45
The QgsLabelingEngine class provides map labeling functionality.
void removeProvider(QgsAbstractLabelProvider *provider)
Remove provider if the provider's initialization failed. Provider instance is deleted.
static QList< QgsMapClippingRegion > collectClippingRegionsForLayer(const QgsRenderContext &context, const QgsMapLayer *layer)
Collects the list of map clipping regions from a context which apply to a map layer.
static QPainterPath calculatePainterClipRegion(const QList< QgsMapClippingRegion > &regions, const QgsRenderContext &context, QgsMapLayerType layerType, bool &shouldClip)
Returns a QPainterPath representing the intersection of clipping regions from context which should be...
Base class for utility classes that encapsulate information necessary for rendering of map layers.
QgsRenderContext * renderContext()
Returns the render context associated with the renderer.
QString source() const
Returns the source for the layer.
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
QgsPointXY center() const
Returns the center point of the rectangle.
Contains information about the context of a rendering operation.
Qgis::RendererUsage rendererUsage() const
Returns the renderer usage.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
bool testFlag(Qgis::RenderContextFlag flag) const
Check whether a particular flag is enabled.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
QgsLabelingEngine * labelingEngine() const
Gets access to new labeling engine (may be nullptr).
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
Scoped object for saving and restoring a QPainter object's state.
double scaleForRenderContext(const QgsRenderContext &context) const
Calculates the correct scale to use for the tiles when rendered using the specified render context.
Definition qgstiles.cpp:287
int scaleToZoomLevel(double scale) const
Finds the best fitting (integer) zoom level given a map scale denominator.
Definition qgstiles.cpp:271
double scaleToZoom(double scale) const
Calculates a fractional zoom level given a map scale denominator.
Definition qgstiles.cpp:214
QgsTileMatrix tileMatrix(int zoom) const
Returns the tile matrix corresponding to the specified zoom.
Definition qgstiles.cpp:149
QPointF mapToTileCoordinates(const QgsPointXY &mapPoint) const
Returns row/column coordinates (floating point number) from the given point in map coordinates.
Definition qgstiles.cpp:120
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition qgstiles.cpp:96
int endColumn() const
Returns index of the last column in the range.
Definition qgstiles.h:83
int endRow() const
Returns index of the last row in the range.
Definition qgstiles.h:87
int startRow() const
Returns index of the first row in the range.
Definition qgstiles.h:85
int startColumn() const
Returns index of the first column in the range.
Definition qgstiles.h:81
bool isValid() const
Returns whether the range is valid (when all row/column numbers are not negative)
Definition qgstiles.h:78
QString toString() const
Returns tile coordinates in a formatted string.
Definition qgstiles.h:54
virtual bool prepare(QgsRenderContext &context, QSet< QString > &attributeNames)
Prepare for registration of features.
virtual QMap< QString, QSet< QString > > usedAttributes(const QgsRenderContext &context, int tileZoom) const =0
Returns field names for each sub-layer that are required for labeling.
virtual void setFields(const QMap< QString, QgsFields > &perLayerFields)=0
Sets fields for each sub-layer.
virtual QSet< QString > requiredLayers(QgsRenderContext &context, int tileZoom) const
Returns a list of the layers required for labeling.
virtual void registerTileFeatures(const QgsVectorTileRendererData &tile, QgsRenderContext &context)=0
Registers label features for given tile to the labeling engine.
virtual QgsVectorTileLabelProvider * provider(QgsVectorTileLayer *layer) const SIP_SKIP
Factory for label provider implementation.
bool forceRasterRender() const override
Returns true if the renderer must be rendered to a raster paint device (e.g.
QgsVectorTileLayerRenderer(QgsVectorTileLayer *layer, QgsRenderContext &context)
Creates the renderer. Always called from main thread, should copy whatever necessary from the layer.
virtual bool render() override
Do the rendering (based on data stored in the class).
Implements a map layer that is dedicated to rendering of vector tiles.
QgsVectorTileLabeling * labeling() const
Returns currently assigned labeling.
The loader class takes care of loading raw vector tile data from a tile source.
void tileRequestFinished(const QgsVectorTileRawData &rawTile)
Emitted when a tile request has finished. If a tile request has failed, the returned raw tile byte ar...
static QList< QgsVectorTileRawData > blockingFetchTileRawData(const QString &sourceType, const QString &sourcePath, const QgsTileMatrix &tileMatrix, const QPointF &viewCenter, const QgsTileRange &range, const QString &authid, const QgsHttpHeaders &headers, QgsFeedback *feedback=nullptr)
Returns raw tile data for the specified range of tiles. Blocks the caller until all tiles are fetched...
This class is responsible for decoding raw tile data written with Mapbox Vector Tiles encoding.
Keeps track of raw tile data that need to be decoded.
QByteArray data
Raw tile data.
QgsTileXYZ id
Tile position in tile matrix set.
Contains decoded features of a single vector tile and any other data necessary for rendering of it.
static QPolygon tilePolygon(QgsTileXYZ id, const QgsCoordinateTransform &ct, const QgsTileMatrix &tm, const QgsMapToPixel &mtp)
Returns polygon (made by four corners of the tile) in screen coordinates.
static QgsFields makeQgisFields(const QSet< QString > &flds)
Returns QgsFields instance based on the set of field names.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:2527
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39