QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsvtpkvectortiledataprovider.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvtpkvectortiledataprovider.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#include "qgsthreadingutils.h"
18#include "qgsvtpktiles.h"
19#include "qgsvectortileloader.h"
20#include "qgsapplication.h"
21#include "qgslogger.h"
23#include "qgsproviderutils.h"
24
25#include <QIcon>
26#include <QFileInfo>
27
29
30
31QString QgsVtpkVectorTileDataProvider::DATA_PROVIDER_KEY = QStringLiteral( "vtpkvectortiles" );
32QString QgsVtpkVectorTileDataProvider::DATA_PROVIDER_DESCRIPTION = QObject::tr( "VTPK Vector Tiles data provider" );
33
34
35QgsVtpkVectorTileDataProvider::QgsVtpkVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, ReadFlags flags )
36 : QgsVectorTileDataProvider( uri, providerOptions, flags )
37{
38 QgsDataSourceUri dsUri;
39 dsUri.setEncodedUri( dataSourceUri() );
40 const QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
41
42 QgsVtpkTiles reader( sourcePath );
43 if ( !reader.open() )
44 {
45 QgsDebugError( QStringLiteral( "failed to open VTPK file: " ) + sourcePath );
46 mIsValid = false;
47 return;
48 }
49
50 const QVariantMap metadata = reader.metadata();
51 const QString format = metadata.value( QStringLiteral( "tileInfo" ) ).toMap().value( QStringLiteral( "format" ) ).toString();
52 if ( format != QLatin1String( "pbf" ) )
53 {
54 QgsDebugError( QStringLiteral( "Cannot open VTPK for vector tiles. Format = " ) + format );
55 mIsValid = false;
56 return;
57 }
58
59 mMatrixSet = reader.matrixSet();
60 mCrs = mMatrixSet.crs();
61 mExtent = reader.extent( transformContext() );
62 mLayerMetadata = reader.layerMetadata();
63 mStyleDefinition = reader.styleDefinition();
64 mSpriteDefinition = reader.spriteDefinition();
65 if ( !mSpriteDefinition.isEmpty() )
66 {
67 mSpriteImage = reader.spriteImage();
68 }
69
70 mIsValid = true;
71}
72
73QgsVtpkVectorTileDataProvider::QgsVtpkVectorTileDataProvider( const QgsVtpkVectorTileDataProvider &other )
75{
76 mIsValid = other.mIsValid;
77 mCrs = other.mCrs;;
78 mExtent = other.mExtent;
79 mMatrixSet = other.mMatrixSet;
80 mLayerMetadata = other.mLayerMetadata;
81 mStyleDefinition = other.mStyleDefinition;
82 mSpriteDefinition = other.mSpriteDefinition;
83 mSpriteImage = other.mSpriteImage;
84}
85
86Qgis::DataProviderFlags QgsVtpkVectorTileDataProvider::flags() const
87{
89}
90
91Qgis::VectorTileProviderFlags QgsVtpkVectorTileDataProvider::providerFlags() const
92{
94}
95
96Qgis::VectorTileProviderCapabilities QgsVtpkVectorTileDataProvider::providerCapabilities() const
97{
99}
100
101QString QgsVtpkVectorTileDataProvider::name() const
102{
104
105 return DATA_PROVIDER_KEY;
106}
107
108QString QgsVtpkVectorTileDataProvider::description() const
109{
111
112 return DATA_PROVIDER_DESCRIPTION;
113}
114
115QgsVectorTileDataProvider *QgsVtpkVectorTileDataProvider::clone() const
116{
118 return new QgsVtpkVectorTileDataProvider( *this );
119}
120
121QString QgsVtpkVectorTileDataProvider::sourcePath() const
122{
124
125 QgsDataSourceUri dsUri;
126 dsUri.setEncodedUri( dataSourceUri() );
127 return dsUri.param( QStringLiteral( "url" ) );
128}
129
130bool QgsVtpkVectorTileDataProvider::isValid() const
131{
133
134 return mIsValid;
135}
136
138{
140
141 return mCrs;
142}
143
144QgsRectangle QgsVtpkVectorTileDataProvider::extent() const
145{
147
148 return mExtent;
149}
150
151QgsLayerMetadata QgsVtpkVectorTileDataProvider::layerMetadata() const
152{
153 return mLayerMetadata;
154}
155
156const QgsVectorTileMatrixSet &QgsVtpkVectorTileDataProvider::tileMatrixSet() const
157{
159
160 return mMatrixSet;
161}
162
163QVariantMap QgsVtpkVectorTileDataProvider::styleDefinition() const
164{
166
167 return mStyleDefinition;
168}
169
170QVariantMap QgsVtpkVectorTileDataProvider::spriteDefinition() const
171{
173
174 return mSpriteDefinition;
175}
176
177QImage QgsVtpkVectorTileDataProvider::spriteImage() const
178{
180
181 return mSpriteImage;
182}
183
184QgsVectorTileRawData QgsVtpkVectorTileDataProvider::readTile( const QgsTileMatrixSet &, const QgsTileXYZ &id, QgsFeedback *feedback ) const
185{
187
189 if ( mShared->getCachedTileData( data, id ) )
190 return data;
191
192 QgsDataSourceUri dsUri;
193 dsUri.setEncodedUri( dataSourceUri() );
194 QgsVtpkTiles reader( dsUri.param( QStringLiteral( "url" ) ) );
195 reader.open();
196 const QgsVectorTileRawData rawData = loadFromVtpk( reader, id, feedback );
197 mShared->storeCachedTileData( rawData );
198 return rawData;
199}
200
201QList<QgsVectorTileRawData> QgsVtpkVectorTileDataProvider::readTiles( const QgsTileMatrixSet &, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback, Qgis::RendererUsage ) const
202{
204
205 QgsDataSourceUri dsUri;
206 dsUri.setEncodedUri( dataSourceUri() );
207
208 // defer actual creation of reader until we need it -- maybe everything is already present in the cache!
209 std::unique_ptr< QgsVtpkTiles > reader;
210
211 QList<QgsVectorTileRawData> rawTiles;
212 QSet< QgsTileXYZ > fetchedTiles;
213 rawTiles.reserve( tiles.size() );
214 fetchedTiles.reserve( tiles.size() );
215 for ( QgsTileXYZ id : std::as_const( tiles ) )
216 {
217 if ( feedback && feedback->isCanceled() )
218 break;
219
220 if ( fetchedTiles.contains( id ) )
221 continue;
222
224 if ( mShared->getCachedTileData( data, id ) )
225 {
226 rawTiles.append( data );
227 fetchedTiles.insert( data.id );
228 }
229 else
230 {
231 if ( !reader )
232 {
233 reader = std::make_unique< QgsVtpkTiles >( dsUri.param( QStringLiteral( "url" ) ) );
234 reader->open();
235 }
236 const QgsVectorTileRawData rawData = loadFromVtpk( *reader, id, feedback );
237 if ( !rawData.data.isEmpty() && !fetchedTiles.contains( rawData.id ) )
238 {
239 rawTiles.append( rawData );
240 fetchedTiles.insert( rawData.id );
241 mShared->storeCachedTileData( rawData );
242 }
243 }
244 }
245 return rawTiles;
246}
247
248QString QgsVtpkVectorTileDataProvider::htmlMetadata() const
249{
250 QString metadata;
251
252 QgsDataSourceUri dsUri;
253 dsUri.setEncodedUri( dataSourceUri() );
254 QgsVtpkTiles reader( dsUri.param( QStringLiteral( "url" ) ) );
255 reader.open();
256
257 if ( !reader.rootTileMap().isEmpty() )
258 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "VTPK storage" ) % QStringLiteral( "</td><td>" ) % tr( "Indexed VTPK (tilemap is present)" ) % QStringLiteral( "</td></tr>\n" );
259 else
260 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "VTPK storage" ) % QStringLiteral( "</td><td>" ) % tr( "Flat VTPK (no tilemap)" ) % QStringLiteral( "</td></tr>\n" );
261
262 if ( reader.metadata().contains( QStringLiteral( "minLOD" ) ) )
263 {
264 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Tile detail levels" ) % QStringLiteral( "</td><td>" ) % QStringLiteral( "%1 - %2" ).arg( reader.metadata().value( QStringLiteral( "minLOD" ) ).toInt() ).arg( reader.metadata().value( QStringLiteral( "maxLOD" ) ).toInt() ) % QStringLiteral( "</td></tr>\n" );
265 }
266
267 return metadata;
268}
269
270QgsVectorTileRawData QgsVtpkVectorTileDataProvider::loadFromVtpk( QgsVtpkTiles &vtpkTileReader, const QgsTileXYZ &id, QgsFeedback * )
271{
272 QgsTileXYZ requestedTile = id;
273 QByteArray tileData = vtpkTileReader.tileData( requestedTile.zoomLevel(), requestedTile.column(), requestedTile.row() );
274 // I **think** here ESRI software will detect a zero size tile and automatically fallback to lower zoom level tiles
275 // I.e. they treat EVERY vtpk a bit like an indexed VTPK, but without the up-front tilemap information.
276 // See https://github.com/qgis/QGIS/issues/52872
277 while ( !tileData.isNull() && tileData.size() == 0 && requestedTile.zoomLevel() > vtpkTileReader.matrixSet().minimumZoom() )
278 {
279 requestedTile = QgsTileXYZ( requestedTile.column() / 2, requestedTile.row() / 2, requestedTile.zoomLevel() - 1 );
280 tileData = vtpkTileReader.tileData( requestedTile.zoomLevel(), requestedTile.column(), requestedTile.row() );
281 }
282
283 if ( tileData.isNull() )
284 return QgsVectorTileRawData();
285
286 QgsVectorTileRawData res( id, tileData );
287 res.tileGeometryId = requestedTile;
288 return res;
289}
290
291
292//
293// QgsVtpkVectorTileDataProviderMetadata
294//
295
296QgsVtpkVectorTileDataProviderMetadata::QgsVtpkVectorTileDataProviderMetadata()
297 : QgsProviderMetadata( QgsVtpkVectorTileDataProvider::DATA_PROVIDER_KEY, QgsVtpkVectorTileDataProvider::DATA_PROVIDER_DESCRIPTION )
298{
299}
300
301QgsProviderMetadata::ProviderMetadataCapabilities QgsVtpkVectorTileDataProviderMetadata::capabilities() const
302{
303 return ProviderMetadataCapability::LayerTypesForUri
304 | ProviderMetadataCapability::PriorityForUri
305 | ProviderMetadataCapability::QuerySublayers;
306}
307
308QgsVtpkVectorTileDataProvider *QgsVtpkVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
309{
310 return new QgsVtpkVectorTileDataProvider( uri, options, flags );
311}
312
313QIcon QgsVtpkVectorTileDataProviderMetadata::icon() const
314{
315 return QgsApplication::getThemeIcon( QStringLiteral( "mIconVectorTileLayer.svg" ) );
316}
317
318QgsProviderMetadata::ProviderCapabilities QgsVtpkVectorTileDataProviderMetadata::providerCapabilities() const
319{
320 return FileBasedUris;
321}
322
323QString QgsVtpkVectorTileDataProviderMetadata::filters( Qgis::FileFilterType type )
324{
325 switch ( type )
326 {
333 return QString();
334
336 return QObject::tr( "VTPK Vector Tiles" ) + QStringLiteral( " (*.vtpk *.VTPK)" );
337 }
338 return QString();
339}
340
341QList<QgsProviderSublayerDetails> QgsVtpkVectorTileDataProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags, QgsFeedback * ) const
342{
343 QString fileName;
344 const QFileInfo fi( uri );
345 if ( fi.isFile() )
346 {
347 fileName = uri;
348 }
349 else
350 {
351 const QVariantMap parts = decodeUri( uri );
352 fileName = parts.value( QStringLiteral( "path" ) ).toString();
353 }
354
355 if ( fileName.isEmpty() )
356 return {};
357
358 if ( QFileInfo( fileName ).suffix().compare( QLatin1String( "vtpk" ), Qt::CaseInsensitive ) == 0 )
359 {
360 QVariantMap parts;
361 parts.insert( QStringLiteral( "path" ), fileName );
362
364 details.setUri( encodeUri( parts ) );
365 details.setProviderKey( key() );
368 return {details};
369 }
370 else
371 {
372 return {};
373 }
374}
375
376int QgsVtpkVectorTileDataProviderMetadata::priorityForUri( const QString &uri ) const
377{
378 if ( validLayerTypesForUri( uri ).contains( Qgis::LayerType::VectorTile ) )
379 return 100;
380
381 return 0;
382}
383
384QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::validLayerTypesForUri( const QString &uri ) const
385{
386 const QFileInfo fi( uri );
387 if ( fi.isFile() && fi.suffix().compare( QLatin1String( "vtpk" ), Qt::CaseInsensitive ) == 0 )
388 {
390 }
391
392 const QVariantMap parts = decodeUri( uri );
393 if ( parts.value( QStringLiteral( "path" ) ).toString().endsWith( ".vtpk", Qt::CaseSensitivity::CaseInsensitive ) )
395
396 return {};
397}
398
399QVariantMap QgsVtpkVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
400{
401 QgsDataSourceUri dsUri;
402 dsUri.setEncodedUri( uri );
403
404 QVariantMap uriComponents;
405 uriComponents.insert( QStringLiteral( "type" ), QStringLiteral( "vtpk" ) );
406 uriComponents.insert( QStringLiteral( "path" ), dsUri.param( QStringLiteral( "url" ) ) );
407
408 return uriComponents;
409}
410
411QString QgsVtpkVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
412{
413 QgsDataSourceUri dsUri;
414 dsUri.setParam( QStringLiteral( "type" ), QStringLiteral( "vtpk" ) );
415 dsUri.setParam( QStringLiteral( "url" ), parts.value( parts.contains( QStringLiteral( "path" ) ) ? QStringLiteral( "path" ) : QStringLiteral( "url" ) ).toString() );
416 return dsUri.encodedUri();
417}
418
419QString QgsVtpkVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
420{
421 QVariantMap parts = decodeUri( uri );
422
423 const QString originalPath = parts.value( QStringLiteral( "path" ) ).toString();
424 parts.insert( QStringLiteral( "path" ), context.pathResolver().writePath( originalPath ) );
425
426 return encodeUri( parts );
427}
428
429QString QgsVtpkVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
430{
431 QVariantMap parts = decodeUri( uri );
432
433 const QString originalPath = parts.value( QStringLiteral( "path" ) ).toString();
434 parts.insert( QStringLiteral( "path" ), context.pathResolver().readPath( originalPath ) );
435
436 return encodeUri( parts );
437}
438
439QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::supportedLayerTypes() const
440{
442}
443
444
446
447
@ ReadLayerMetadata
Provider can read layer metadata from data store. See QgsDataProvider::layerMetadata()
@ AlwaysUseTileMatrixSetFromProvider
Vector tile layer must always use the tile matrix set from the data provider, and should never store,...
QFlags< DataProviderFlag > DataProviderFlags
Data provider flags.
Definition: qgis.h:1853
FileFilterType
Type of file filters.
Definition: qgis.h:1073
@ TiledScene
Tiled scene layers (since QGIS 3.34)
@ Vector
Vector layers.
@ VectorTile
Vector tile layers (since QGIS 3.32)
@ Mesh
Mesh layers.
@ Raster
Raster layers.
@ MeshDataset
Mesh datasets.
@ PointCloud
Point clouds (since QGIS 3.18)
@ FastExtent2D
Provider's 2D extent retrieval via QgsDataProvider::extent() is always guaranteed to be trivial/fast ...
QFlags< VectorTileProviderCapability > VectorTileProviderCapabilities
Vector tile data provider capabilities.
Definition: qgis.h:4518
QFlags< SublayerQueryFlag > SublayerQueryFlags
Sublayer query flags.
Definition: qgis.h:1097
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
RendererUsage
Usage of the renderer.
Definition: qgis.h:2803
QFlags< VectorTileProviderFlag > VectorTileProviderFlags
Vector tile data provider flags.
Definition: qgis.h:4500
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
This class represents a coordinate reference system (CRS).
QFlags< ReadFlag > ReadFlags
Class for storing the component parts of a RDBMS data source URI (e.g.
QByteArray encodedUri() const
Returns the complete encoded URI as a byte array.
void setEncodedUri(const QByteArray &uri)
Sets the complete encoded uri.
QString param(const QString &key) const
Returns a generic parameter value corresponding to the specified key.
void setParam(const QString &key, const QString &value)
Sets a generic parameter value on the URI.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
A structured metadata store for a map layer.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
Holds data provider key, description, and associated shared library file or function pointer informat...
QFlags< ProviderMetadataCapability > ProviderMetadataCapabilities
QFlags< ProviderCapability > ProviderCapabilities
Contains details about a sub layer available from a dataset.
void setUri(const QString &uri)
Sets the layer's uri.
void setType(Qgis::LayerType type)
Sets the layer type.
void setName(const QString &name)
Sets the layer's name.
void setProviderKey(const QString &key)
Sets the associated data provider key.
static QString suggestLayerNameFromFilePath(const QString &path)
Suggests a suitable layer name given only a file path.
The class is used as a container of context for various read/write operations on other objects.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Defines a set of tile matrices for multiple zoom levels.
Definition: qgstiles.h:250
int minimumZoom() const
Returns the minimum zoom level for tiles present in the set.
Definition: qgstiles.cpp:176
Stores coordinates of a tile in a tile matrix set.
Definition: qgstiles.h:38
int zoomLevel() const
Returns tile's zoom level (Z)
Definition: qgstiles.h:51
int column() const
Returns tile's column index (X)
Definition: qgstiles.h:47
int row() const
Returns tile's row index (Y)
Definition: qgstiles.h:49
Base class for vector tile layer data providers.
Encapsulates properties of a vector tile matrix set, including tile origins and scaling information.
Keeps track of raw tile data that need to be decoded.
QByteArray data
Raw tile data.
QgsTileXYZ id
Tile position in tile matrix set.
Utility class for reading and writing ESRI VTPK files.
Definition: qgsvtpktiles.h:38
QByteArray tileData(int z, int x, int y)
Returns the raw tile data for the matching tile.
QgsVectorTileMatrixSet matrixSet() const
Returns the vector tile matrix set representing the tiles.
#define QgsDebugError(str)
Definition: qgslogger.h:38
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
const QgsCoordinateReferenceSystem & crs
Setting options for creating vector data providers.