QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 
25 #include "qgsvectortilelayer.h"
26 #include "qgsvectortileloader.h"
27 #include "qgsvectortileutils.h"
28 
29 #include "qgslabelingengine.h"
30 #include "qgsvectortilelabeling.h"
31 
32 
34  : QgsMapLayerRenderer( layer->id(), &context )
35  , mSourceType( layer->sourceType() )
36  , mSourcePath( layer->sourcePath() )
37  , mSourceMinZoom( layer->sourceMinZoom() )
38  , mSourceMaxZoom( layer->sourceMaxZoom() )
39  , mRenderer( layer->renderer()->clone() )
40  , mDrawTileBoundaries( layer->isTileBorderRenderingEnabled() )
41  , mFeedback( new QgsFeedback )
42 {
43 
44  if ( QgsLabelingEngine *engine = context.labelingEngine() )
45  {
46  if ( layer->labeling() )
47  {
48  mLabelProvider = layer->labeling()->provider( layer );
49  if ( mLabelProvider )
50  {
51  engine->addProvider( mLabelProvider );
52  }
53  }
54  }
55 
56 }
57 
59 {
61 
62  if ( ctx.renderingStopped() )
63  return false;
64 
65  QElapsedTimer tTotal;
66  tTotal.start();
67 
68  QgsDebugMsgLevel( QStringLiteral( "Vector tiles rendering extent: " ) + ctx.extent().toString( -1 ), 2 );
69  QgsDebugMsgLevel( QStringLiteral( "Vector tiles map scale 1 : %1" ).arg( ctx.rendererScale() ), 2 );
70 
71  mTileZoom = QgsVectorTileUtils::scaleToZoomLevel( ctx.rendererScale(), mSourceMinZoom, mSourceMaxZoom );
72  QgsDebugMsgLevel( QStringLiteral( "Vector tiles zoom level: %1" ).arg( mTileZoom ), 2 );
73 
74  mTileMatrix = QgsTileMatrix::fromWebMercator( mTileZoom );
75 
76  mTileRange = mTileMatrix.tileRangeFromExtent( ctx.extent() );
77  QgsDebugMsgLevel( QStringLiteral( "Vector tiles range X: %1 - %2 Y: %3 - %4" )
78  .arg( mTileRange.startColumn() ).arg( mTileRange.endColumn() )
79  .arg( mTileRange.startRow() ).arg( mTileRange.endRow() ), 2 );
80 
81  // view center is used to sort the order of tiles for fetching and rendering
82  QPointF viewCenter = mTileMatrix.mapToTileCoordinates( ctx.extent().center() );
83 
84  if ( !mTileRange.isValid() )
85  {
86  QgsDebugMsgLevel( QStringLiteral( "Vector tiles - outside of range" ), 2 );
87  return true; // nothing to do
88  }
89 
90  bool isAsync = ( mSourceType == QStringLiteral( "xyz" ) );
91 
92  std::unique_ptr<QgsVectorTileLoader> asyncLoader;
93  QList<QgsVectorTileRawData> rawTiles;
94  if ( !isAsync )
95  {
96  QElapsedTimer tFetch;
97  tFetch.start();
98  rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mSourceType, mSourcePath, mTileMatrix, viewCenter, mTileRange );
99  QgsDebugMsgLevel( QStringLiteral( "Tile fetching time: %1" ).arg( tFetch.elapsed() / 1000. ), 2 );
100  QgsDebugMsgLevel( QStringLiteral( "Fetched tiles: %1" ).arg( rawTiles.count() ), 2 );
101  }
102  else
103  {
104  asyncLoader.reset( new QgsVectorTileLoader( mSourcePath, mTileMatrix, mTileRange, viewCenter, mFeedback.get() ) );
105  QObject::connect( asyncLoader.get(), &QgsVectorTileLoader::tileRequestFinished, [this]( const QgsVectorTileRawData & rawTile )
106  {
107  QgsDebugMsgLevel( QStringLiteral( "Got tile asynchronously: " ) + rawTile.id.toString(), 2 );
108  if ( !rawTile.data.isEmpty() )
109  decodeAndDrawTile( rawTile );
110  } );
111  }
112 
113  if ( ctx.renderingStopped() )
114  return false;
115 
116  // add @zoom_level variable which can be used in styling
117  QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Tiles" ) ); // will be deleted by popper
118  scope->setVariable( "zoom_level", mTileZoom, true );
119  QgsExpressionContextScopePopper popper( ctx.expressionContext(), scope );
120 
121  mRenderer->startRender( *renderContext(), mTileZoom, mTileRange );
122 
123  QMap<QString, QSet<QString> > requiredFields = mRenderer->usedAttributes( ctx );
124 
125  if ( mLabelProvider )
126  {
127  QMap<QString, QSet<QString> > requiredFieldsLabeling = mLabelProvider->usedAttributes( ctx, mTileZoom );
128  for ( QString layerName : requiredFieldsLabeling.keys() )
129  {
130  requiredFields[layerName].unite( requiredFieldsLabeling[layerName] );
131  }
132  }
133 
134  QMap<QString, QgsFields> perLayerFields;
135  for ( QString layerName : requiredFields.keys() )
136  mPerLayerFields[layerName] = QgsVectorTileUtils::makeQgisFields( requiredFields[layerName] );
137 
138  if ( mLabelProvider )
139  {
140  mLabelProvider->setFields( mPerLayerFields );
141  QSet<QString> attributeNames; // we don't need this - already got referenced columns in provider constructor
142  if ( !mLabelProvider->prepare( ctx, attributeNames ) )
143  {
144  ctx.labelingEngine()->removeProvider( mLabelProvider );
145  mLabelProvider = nullptr; // provider is deleted by the engine
146  }
147  }
148 
149  if ( !isAsync )
150  {
151  for ( QgsVectorTileRawData &rawTile : rawTiles )
152  {
153  if ( ctx.renderingStopped() )
154  break;
155 
156  decodeAndDrawTile( rawTile );
157  }
158  }
159  else
160  {
161  // Block until tiles are fetched and rendered. If the rendering gets canceled at some point,
162  // the async loader will catch the signal, abort requests and return from downloadBlocking()
163  asyncLoader->downloadBlocking();
164  }
165 
166  mRenderer->stopRender( ctx );
167 
168  ctx.painter()->setClipping( false );
169 
170  QgsDebugMsgLevel( QStringLiteral( "Total time for decoding: %1" ).arg( mTotalDecodeTime / 1000. ), 2 );
171  QgsDebugMsgLevel( QStringLiteral( "Drawing time: %1" ).arg( mTotalDrawTime / 1000. ), 2 );
172  QgsDebugMsgLevel( QStringLiteral( "Total time: %1" ).arg( tTotal.elapsed() / 1000. ), 2 );
173 
174  return !ctx.renderingStopped();
175 }
176 
177 void QgsVectorTileLayerRenderer::decodeAndDrawTile( const QgsVectorTileRawData &rawTile )
178 {
180 
181  QgsDebugMsgLevel( QStringLiteral( "Drawing tile " ) + rawTile.id.toString(), 2 );
182 
183  QElapsedTimer tLoad;
184  tLoad.start();
185 
186  // currently only MVT encoding supported
187  QgsVectorTileMVTDecoder decoder;
188  if ( !decoder.decode( rawTile.id, rawTile.data ) )
189  {
190  QgsDebugMsgLevel( QStringLiteral( "Failed to parse raw tile data! " ) + rawTile.id.toString(), 2 );
191  return;
192  }
193 
194  if ( ctx.renderingStopped() )
195  return;
196 
198 
199  QgsVectorTileRendererData tile( rawTile.id );
200  tile.setFields( mPerLayerFields );
201  tile.setFeatures( decoder.layerFeatures( mPerLayerFields, ct ) );
202  tile.setTilePolygon( QgsVectorTileUtils::tilePolygon( rawTile.id, ct, mTileMatrix, ctx.mapToPixel() ) );
203 
204  mTotalDecodeTime += tLoad.elapsed();
205 
206  // calculate tile polygon in screen coordinates
207 
208  if ( ctx.renderingStopped() )
209  return;
210 
211  // set up clipping so that rendering does not go behind tile's extent
212 
213  ctx.painter()->setClipRegion( QRegion( tile.tilePolygon() ) );
214 
215  QElapsedTimer tDraw;
216  tDraw.start();
217 
218  mRenderer->renderTile( tile, ctx );
219  mTotalDrawTime += tDraw.elapsed();
220 
221  if ( mLabelProvider )
222  mLabelProvider->registerTileFeatures( tile, ctx );
223 
224  if ( mDrawTileBoundaries )
225  {
226  ctx.painter()->setClipping( false );
227 
228  QPen pen( Qt::red );
229  pen.setWidth( 3 );
230  ctx.painter()->setPen( pen );
231  ctx.painter()->drawPolygon( tile.tilePolygon() );
232  }
233 }
qgsexpressioncontextutils.h
QgsExpressionContextScopePopper
Definition: qgsexpressioncontextutils.h:354
QgsVectorTileLayer
Definition: qgsvectortilelayer.h:83
QgsRenderContext::mapToPixel
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
Definition: qgsrendercontext.h:309
QgsExpressionContextScope::setVariable
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.
Definition: qgsexpressioncontext.cpp:78
QgsRenderContext::expressionContext
QgsExpressionContext & expressionContext()
Gets the expression context.
Definition: qgsrendercontext.h:580
QgsVectorTileUtils::makeQgisFields
static QgsFields makeQgisFields(QSet< QString > flds)
Returns QgsFields instance based on the set of field names.
Definition: qgsvectortileutils.cpp:51
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsLabelingEngine::removeProvider
void removeProvider(QgsAbstractLabelProvider *provider)
Remove provider if the provider's initialization failed. Provider instance is deleted.
Definition: qgslabelingengine.cpp:194
qgsvectortilelabeling.h
QgsVectorTileLayerRenderer::QgsVectorTileLayerRenderer
QgsVectorTileLayerRenderer(QgsVectorTileLayer *layer, QgsRenderContext &context)
Creates the renderer. Always called from main thread, should copy whatever necessary from the layer.
Definition: qgsvectortilelayerrenderer.cpp:33
QgsRenderContext
Definition: qgsrendercontext.h:57
QgsMapLayerRenderer::renderContext
QgsRenderContext * renderContext()
Returns the render context associated with the renderer.
Definition: qgsmaplayerrenderer.h:84
QgsVectorTileRendererData
Definition: qgsvectortilerenderer.h:37
QgsRenderContext::extent
const QgsRectangle & extent() const
When rendering a map layer, calling this method returns the "clipping" extent for the layer (in the l...
Definition: qgsrendercontext.h:290
QgsVectorTileMVTDecoder
Definition: qgsvectortilemvtdecoder.h:36
QgsMapLayerRenderer
Definition: qgsmaplayerrenderer.h:50
QgsVectorTileLabeling::provider
virtual QgsVectorTileLabelProvider * provider(QgsVectorTileLayer *layer) const SIP_SKIP
Factory for label provider implementation.
Definition: qgsvectortilelabeling.h:85
qgsvectortilelayer.h
QgsRenderContext::renderingStopped
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
Definition: qgsrendercontext.h:325
QgsRenderContext::coordinateTransform
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
Definition: qgsrendercontext.h:229
QgsTileRange::endRow
int endRow() const
Returns index of the last row in the range.
Definition: qgstiles.h:82
QgsVectorTileLabelProvider::setFields
virtual void setFields(const QMap< QString, QgsFields > &perLayerFields)=0
Sets fields for each sub-layer.
QgsVectorTileLoader::tileRequestFinished
void tileRequestFinished(const QgsVectorTileRawData &rawTile)
Emitted when a tile request has finished. If a tile request has failed, the returned raw tile byte ar...
QgsVectorTileLabelProvider::usedAttributes
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.
qgsvectortilemvtdecoder.h
QgsFeedback
Definition: qgsfeedback.h:43
QgsVectorTileLoader::blockingFetchTileRawData
static QList< QgsVectorTileRawData > blockingFetchTileRawData(const QString &sourceType, const QString &sourcePath, const QgsTileMatrix &tileMatrix, const QPointF &viewCenter, const QgsTileRange &range)
Returns raw tile data for the specified range of tiles. Blocks the caller until all tiles are fetched...
Definition: qgsvectortileloader.cpp:146
QgsTileXYZ::toString
QString toString() const
Returns tile coordinates in a formatted string.
Definition: qgstiles.h:62
QgsVectorTileLoader
Definition: qgsvectortileloader.h:56
QgsTileRange::isValid
bool isValid() const
Returns whether the range is valid (when all row/column numbers are not negative)
Definition: qgstiles.h:73
QgsTileRange::endColumn
int endColumn() const
Returns index of the last column in the range.
Definition: qgstiles.h:78
QgsVectorTileUtils::scaleToZoomLevel
static int scaleToZoomLevel(double mapScale, int sourceMinZoom, int sourceMaxZoom)
Finds best fitting zoom level (assuming GoogleCRS84Quad tile matrix set) given map scale denominator ...
Definition: qgsvectortileutils.cpp:64
QgsExpressionContextScope
Single scope for storing variables and functions for use within a QgsExpressionContext....
Definition: qgsexpressioncontext.h:111
QgsLabelingEngine
The QgsLabelingEngine class provides map labeling functionality. The input for the engine is a list o...
Definition: qgslabelingengine.h:214
qgsvectortileloader.h
QgsRectangle::toString
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
Definition: qgsrectangle.cpp:127
QgsRenderContext::labelingEngine
QgsLabelingEngine * labelingEngine() const
Gets access to new labeling engine (may be nullptr)
Definition: qgsrendercontext.h:367
QgsTileRange::startRow
int startRow() const
Returns index of the first row in the range.
Definition: qgstiles.h:80
QgsRectangle::center
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:230
QgsVectorTileLabelProvider::registerTileFeatures
virtual void registerTileFeatures(const QgsVectorTileRendererData &tile, QgsRenderContext &context)=0
Registers label features for given tile to the labeling engine.
QgsVectorTileMVTDecoder::layerFeatures
QgsVectorTileFeatures layerFeatures(const QMap< QString, QgsFields > &perLayerFields, const QgsCoordinateTransform &ct) const
Returns decoded features grouped by sub-layers. It can only be called after a successful decode()
Definition: qgsvectortilemvtdecoder.cpp:80
QgsVectorTileLayer::labeling
QgsVectorTileLabeling * labeling() const
Returns currently assigned labeling.
Definition: qgsvectortilelayer.cpp:316
QgsTileMatrix::mapToTileCoordinates
QPointF mapToTileCoordinates(const QgsPointXY &mapPoint) const
Returns row/column coordinates (floating point number) from the given point in map coordinates.
Definition: qgstiles.cpp:78
QgsVectorTileRawData
Definition: qgsvectortileloader.h:31
qgsvectortilelayerrenderer.h
QgsVectorTileRawData::id
QgsTileXYZ id
Tile position in tile matrix set.
Definition: qgsvectortileloader.h:39
QgsTileMatrix::fromWebMercator
static QgsTileMatrix fromWebMercator(int mZoomLevel)
Returns a tile matrix for the usual web mercator.
Definition: qgstiles.cpp:20
QgsVectorLayerLabelProvider::prepare
virtual bool prepare(QgsRenderContext &context, QSet< QString > &attributeNames)
Prepare for registration of features.
Definition: qgsvectorlayerlabelprovider.cpp:112
qgslogger.h
QgsRenderContext::painter
QPainter * painter()
Returns the destination QPainter for the render operation.
Definition: qgsrendercontext.h:174
QgsRenderContext::rendererScale
double rendererScale() const
Returns the renderer map scale.
Definition: qgsrendercontext.h:361
qgsvectortileutils.h
QgsCoordinateTransform
Definition: qgscoordinatetransform.h:52
QgsVectorTileMVTDecoder::decode
bool decode(QgsTileXYZ tileID, const QByteArray &rawTileData)
Tries to decode raw tile data, returns true on success.
Definition: qgsvectortilemvtdecoder.cpp:36
qgsfeedback.h
QgsTileMatrix::tileRangeFromExtent
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent)
Returns tile range that fully covers the given extent.
Definition: qgstiles.cpp:54
QgsVectorTileUtils::tilePolygon
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.
Definition: qgsvectortileutils.cpp:36
qgslabelingengine.h
QgsTileRange::startColumn
int startColumn() const
Returns index of the first column in the range.
Definition: qgstiles.h:76
QgsVectorTileLayerRenderer::render
virtual bool render() override
Do the rendering (based on data stored in the class)
Definition: qgsvectortilelayerrenderer.cpp:58
QgsVectorTileRawData::data
QByteArray data
Raw tile data.
Definition: qgsvectortileloader.h:41