QGIS API Documentation 3.99.0-Master (357b655ed83)
Loading...
Searching...
No Matches
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
18#include "qgsapplication.h"
19#include "qgslogger.h"
21#include "qgsproviderutils.h"
22#include "qgsthreadingutils.h"
23#include "qgsvectortileloader.h"
24#include "qgsvtpktiles.h"
25
26#include <QFileInfo>
27#include <QIcon>
28#include <QString>
29
30#include "moc_qgsvtpkvectortiledataprovider.cpp"
31
32using namespace Qt::StringLiterals;
33
35
36
37QString QgsVtpkVectorTileDataProvider::DATA_PROVIDER_KEY = u"vtpkvectortiles"_s;
38QString QgsVtpkVectorTileDataProvider::DATA_PROVIDER_DESCRIPTION = QObject::tr( "VTPK Vector Tiles data provider" );
39
40
41QgsVtpkVectorTileDataProvider::QgsVtpkVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, Qgis::DataProviderReadFlags flags )
42 : QgsVectorTileDataProvider( uri, providerOptions, flags )
43{
44 QgsDataSourceUri dsUri;
45 dsUri.setEncodedUri( dataSourceUri() );
46 const QString sourcePath = dsUri.param( u"url"_s );
47
48 QgsVtpkTiles reader( sourcePath );
49 if ( !reader.open() )
50 {
51 QgsDebugError( u"failed to open VTPK file: "_s + sourcePath );
52 mIsValid = false;
53 return;
54 }
55
56 const QVariantMap metadata = reader.metadata();
57 const QString format = metadata.value( u"tileInfo"_s ).toMap().value( u"format"_s ).toString();
58 if ( format != "pbf"_L1 )
59 {
60 QgsDebugError( u"Cannot open VTPK for vector tiles. Format = "_s + format );
61 mIsValid = false;
62 return;
63 }
64
65 mMatrixSet = reader.matrixSet();
66 mCrs = mMatrixSet.crs();
67 mExtent = reader.extent( transformContext() );
68 mLayerMetadata = reader.layerMetadata();
69 mStyleDefinition = reader.styleDefinition();
70 mSpriteDefinition = reader.spriteDefinition();
71 if ( !mSpriteDefinition.isEmpty() )
72 {
73 mSpriteImage = reader.spriteImage();
74 }
75
76 mIsValid = true;
77}
78
79QgsVtpkVectorTileDataProvider::QgsVtpkVectorTileDataProvider( const QgsVtpkVectorTileDataProvider &other )
81{
82 mIsValid = other.mIsValid;
83 mCrs = other.mCrs;;
84 mExtent = other.mExtent;
85 mMatrixSet = other.mMatrixSet;
86 mLayerMetadata = other.mLayerMetadata;
87 mStyleDefinition = other.mStyleDefinition;
88 mSpriteDefinition = other.mSpriteDefinition;
89 mSpriteImage = other.mSpriteImage;
90}
91
92Qgis::DataProviderFlags QgsVtpkVectorTileDataProvider::flags() const
93{
95}
96
97Qgis::VectorTileProviderFlags QgsVtpkVectorTileDataProvider::providerFlags() const
98{
100}
101
102Qgis::VectorTileProviderCapabilities QgsVtpkVectorTileDataProvider::providerCapabilities() const
103{
105}
106
107QString QgsVtpkVectorTileDataProvider::name() const
108{
110
111 return DATA_PROVIDER_KEY;
112}
113
114QString QgsVtpkVectorTileDataProvider::description() const
115{
117
118 return DATA_PROVIDER_DESCRIPTION;
119}
120
121QgsVectorTileDataProvider *QgsVtpkVectorTileDataProvider::clone() const
122{
124 return new QgsVtpkVectorTileDataProvider( *this );
125}
126
127QString QgsVtpkVectorTileDataProvider::sourcePath() const
128{
130
131 QgsDataSourceUri dsUri;
132 dsUri.setEncodedUri( dataSourceUri() );
133 return dsUri.param( u"url"_s );
134}
135
136bool QgsVtpkVectorTileDataProvider::isValid() const
137{
139
140 return mIsValid;
141}
142
143QgsCoordinateReferenceSystem QgsVtpkVectorTileDataProvider::crs() const
144{
146
147 return mCrs;
148}
149
150QgsRectangle QgsVtpkVectorTileDataProvider::extent() const
151{
153
154 return mExtent;
155}
156
157QgsLayerMetadata QgsVtpkVectorTileDataProvider::layerMetadata() const
158{
159 return mLayerMetadata;
160}
161
162const QgsVectorTileMatrixSet &QgsVtpkVectorTileDataProvider::tileMatrixSet() const
163{
165
166 return mMatrixSet;
167}
168
169QVariantMap QgsVtpkVectorTileDataProvider::styleDefinition() const
170{
172
173 return mStyleDefinition;
174}
175
176QVariantMap QgsVtpkVectorTileDataProvider::spriteDefinition() const
177{
179
180 return mSpriteDefinition;
181}
182
183QImage QgsVtpkVectorTileDataProvider::spriteImage() const
184{
186
187 return mSpriteImage;
188}
189
190QgsVectorTileRawData QgsVtpkVectorTileDataProvider::readTile( const QgsTileMatrixSet &, const QgsTileXYZ &id, QgsFeedback *feedback ) const
191{
193
195 if ( mShared->getCachedTileData( data, id ) )
196 return data;
197
198 QgsDataSourceUri dsUri;
199 dsUri.setEncodedUri( dataSourceUri() );
200 QgsVtpkTiles reader( dsUri.param( u"url"_s ) );
201 reader.open();
202 const QgsVectorTileRawData rawData = loadFromVtpk( reader, id, feedback );
203 mShared->storeCachedTileData( rawData );
204 return rawData;
205}
206
207QList<QgsVectorTileRawData> QgsVtpkVectorTileDataProvider::readTiles( const QgsTileMatrixSet &, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback, Qgis::RendererUsage ) const
208{
210
211 QgsDataSourceUri dsUri;
212 dsUri.setEncodedUri( dataSourceUri() );
213
214 // defer actual creation of reader until we need it -- maybe everything is already present in the cache!
215 std::unique_ptr< QgsVtpkTiles > reader;
216
217 QList<QgsVectorTileRawData> rawTiles;
218 QSet< QgsTileXYZ > fetchedTiles;
219 rawTiles.reserve( tiles.size() );
220 fetchedTiles.reserve( tiles.size() );
221 for ( QgsTileXYZ id : std::as_const( tiles ) )
222 {
223 if ( feedback && feedback->isCanceled() )
224 break;
225
226 if ( fetchedTiles.contains( id ) )
227 continue;
228
230 if ( mShared->getCachedTileData( data, id ) )
231 {
232 rawTiles.append( data );
233 fetchedTiles.insert( data.id );
234 }
235 else
236 {
237 if ( !reader )
238 {
239 reader = std::make_unique< QgsVtpkTiles >( dsUri.param( u"url"_s ) );
240 reader->open();
241 }
242 const QgsVectorTileRawData rawData = loadFromVtpk( *reader, id, feedback );
243 if ( !rawData.data.isEmpty() && !fetchedTiles.contains( rawData.id ) )
244 {
245 rawTiles.append( rawData );
246 fetchedTiles.insert( rawData.id );
247 mShared->storeCachedTileData( rawData );
248 }
249 }
250 }
251 return rawTiles;
252}
253
254QString QgsVtpkVectorTileDataProvider::htmlMetadata() const
255{
256 QString metadata;
257
258 QgsDataSourceUri dsUri;
259 dsUri.setEncodedUri( dataSourceUri() );
260 QgsVtpkTiles reader( dsUri.param( u"url"_s ) );
261 reader.open();
262
263 if ( !reader.rootTileMap().isEmpty() )
264 metadata += u"<tr><td class=\"highlight\">"_s % tr( "VTPK storage" ) % u"</td><td>"_s % tr( "Indexed VTPK (tilemap is present)" ) % u"</td></tr>\n"_s;
265 else
266 metadata += u"<tr><td class=\"highlight\">"_s % tr( "VTPK storage" ) % u"</td><td>"_s % tr( "Flat VTPK (no tilemap)" ) % u"</td></tr>\n"_s;
267
268 if ( reader.metadata().contains( u"minLOD"_s ) )
269 {
270 metadata += u"<tr><td class=\"highlight\">"_s % tr( "Tile detail levels" ) % u"</td><td>"_s % u"%1 - %2"_s.arg( reader.metadata().value( u"minLOD"_s ).toInt() ).arg( reader.metadata().value( u"maxLOD"_s ).toInt() ) % u"</td></tr>\n"_s;
271 }
272
273 return metadata;
274}
275
276QgsVectorTileRawData QgsVtpkVectorTileDataProvider::loadFromVtpk( QgsVtpkTiles &vtpkTileReader, const QgsTileXYZ &id, QgsFeedback * )
277{
278 QgsTileXYZ requestedTile = id;
279 QByteArray tileData = vtpkTileReader.tileData( requestedTile.zoomLevel(), requestedTile.column(), requestedTile.row() );
280 // I **think** here ESRI software will detect a zero size tile and automatically fallback to lower zoom level tiles
281 // I.e. they treat EVERY vtpk a bit like an indexed VTPK, but without the up-front tilemap information.
282 // See https://github.com/qgis/QGIS/issues/52872
283 while ( !tileData.isNull() && tileData.size() == 0 && requestedTile.zoomLevel() > vtpkTileReader.matrixSet().minimumZoom() )
284 {
285 requestedTile = QgsTileXYZ( requestedTile.column() / 2, requestedTile.row() / 2, requestedTile.zoomLevel() - 1 );
286 tileData = vtpkTileReader.tileData( requestedTile.zoomLevel(), requestedTile.column(), requestedTile.row() );
287 }
288
289 if ( tileData.isNull() )
290 return QgsVectorTileRawData();
291
292 QgsVectorTileRawData res( id, tileData );
293 res.tileGeometryId = requestedTile;
294 return res;
295}
296
297
298//
299// QgsVtpkVectorTileDataProviderMetadata
300//
301
302QgsVtpkVectorTileDataProviderMetadata::QgsVtpkVectorTileDataProviderMetadata()
303 : QgsProviderMetadata( QgsVtpkVectorTileDataProvider::DATA_PROVIDER_KEY, QgsVtpkVectorTileDataProvider::DATA_PROVIDER_DESCRIPTION )
304{
305}
306
307QgsProviderMetadata::ProviderMetadataCapabilities QgsVtpkVectorTileDataProviderMetadata::capabilities() const
308{
309 return ProviderMetadataCapability::LayerTypesForUri
310 | ProviderMetadataCapability::PriorityForUri
311 | ProviderMetadataCapability::QuerySublayers;
312}
313
314QgsVtpkVectorTileDataProvider *QgsVtpkVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
315{
316 return new QgsVtpkVectorTileDataProvider( uri, options, flags );
317}
318
319QIcon QgsVtpkVectorTileDataProviderMetadata::icon() const
320{
321 return QgsApplication::getThemeIcon( u"mIconVectorTileLayer.svg"_s );
322}
323
324QgsProviderMetadata::ProviderCapabilities QgsVtpkVectorTileDataProviderMetadata::providerCapabilities() const
325{
326 return FileBasedUris;
327}
328
329QString QgsVtpkVectorTileDataProviderMetadata::filters( Qgis::FileFilterType type )
330{
331 switch ( type )
332 {
339 return QString();
340
342 return QObject::tr( "VTPK Vector Tiles" ) + u" (*.vtpk *.VTPK)"_s;
343 }
344 return QString();
345}
346
347QList<QgsProviderSublayerDetails> QgsVtpkVectorTileDataProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags, QgsFeedback * ) const
348{
349 QString fileName;
350 const QFileInfo fi( uri );
351 if ( fi.isFile() )
352 {
353 fileName = uri;
354 }
355 else
356 {
357 const QVariantMap parts = decodeUri( uri );
358 fileName = parts.value( u"path"_s ).toString();
359 }
360
361 if ( fileName.isEmpty() )
362 return {};
363
364 if ( QFileInfo( fileName ).suffix().compare( "vtpk"_L1, Qt::CaseInsensitive ) == 0 )
365 {
366 QVariantMap parts;
367 parts.insert( u"path"_s, fileName );
368
370 details.setUri( encodeUri( parts ) );
371 details.setProviderKey( key() );
374 return {details};
375 }
376 else
377 {
378 return {};
379 }
380}
381
382int QgsVtpkVectorTileDataProviderMetadata::priorityForUri( const QString &uri ) const
383{
384 if ( validLayerTypesForUri( uri ).contains( Qgis::LayerType::VectorTile ) )
385 return 100;
386
387 return 0;
388}
389
390QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::validLayerTypesForUri( const QString &uri ) const
391{
392 const QFileInfo fi( uri );
393 if ( fi.isFile() && fi.suffix().compare( "vtpk"_L1, Qt::CaseInsensitive ) == 0 )
394 {
396 }
397
398 const QVariantMap parts = decodeUri( uri );
399 if ( parts.value( u"path"_s ).toString().endsWith( ".vtpk", Qt::CaseSensitivity::CaseInsensitive ) )
401
402 return {};
403}
404
405QVariantMap QgsVtpkVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
406{
407 QgsDataSourceUri dsUri;
408 dsUri.setEncodedUri( uri );
409
410 QVariantMap uriComponents;
411 uriComponents.insert( u"type"_s, u"vtpk"_s );
412 uriComponents.insert( u"path"_s, dsUri.param( u"url"_s ) );
413
414 return uriComponents;
415}
416
417QString QgsVtpkVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
418{
419 QgsDataSourceUri dsUri;
420 dsUri.setParam( u"type"_s, u"vtpk"_s );
421 dsUri.setParam( u"url"_s, parts.value( parts.contains( u"path"_s ) ? u"path"_s : u"url"_s ).toString() );
422 return dsUri.encodedUri();
423}
424
425QString QgsVtpkVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
426{
427 QVariantMap parts = decodeUri( uri );
428
429 const QString originalPath = parts.value( u"path"_s ).toString();
430 parts.insert( u"path"_s, context.pathResolver().writePath( originalPath ) );
431
432 return encodeUri( parts );
433}
434
435QString QgsVtpkVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
436{
437 QVariantMap parts = decodeUri( uri );
438
439 const QString originalPath = parts.value( u"path"_s ).toString();
440 parts.insert( u"path"_s, context.pathResolver().readPath( originalPath ) );
441
442 return encodeUri( parts );
443}
444
445QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::supportedLayerTypes() const
446{
448}
449
450
452
453
@ ReadLayerMetadata
Provider can read layer metadata from data store. See QgsDataProvider::layerMetadata().
Definition qgis.h:5890
@ AlwaysUseTileMatrixSetFromProvider
Vector tile layer must always use the tile matrix set from the data provider, and should never store,...
Definition qgis.h:5872
QFlags< DataProviderFlag > DataProviderFlags
Data provider flags.
Definition qgis.h:2373
FileFilterType
Type of file filters.
Definition qgis.h:1412
@ TiledScene
Tiled scene layers.
Definition qgis.h:1419
@ Vector
Vector layers.
Definition qgis.h:1413
@ VectorTile
Vector tile layers.
Definition qgis.h:1418
@ Mesh
Mesh layers.
Definition qgis.h:1415
@ Raster
Raster layers.
Definition qgis.h:1414
@ MeshDataset
Mesh datasets.
Definition qgis.h:1416
@ PointCloud
Point clouds.
Definition qgis.h:1417
@ FastExtent2D
Provider's 2D extent retrieval via QgsDataProvider::extent() is always guaranteed to be trivial/fast ...
Definition qgis.h:2368
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:505
QFlags< VectorTileProviderCapability > VectorTileProviderCapabilities
Vector tile data provider capabilities.
Definition qgis.h:5899
QFlags< SublayerQueryFlag > SublayerQueryFlags
Sublayer query flags.
Definition qgis.h:1457
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:198
RendererUsage
Usage of the renderer.
Definition qgis.h:3516
QFlags< VectorTileProviderFlag > VectorTileProviderFlags
Vector tile data provider flags.
Definition qgis.h:5881
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Represents a coordinate reference system (CRS).
Stores the component parts of a 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:55
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.
A container for the context for various read/write operations on objects.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
A rectangle specified with double values.
Defines a set of tile matrices for multiple zoom levels.
Definition qgstiles.h:278
int minimumZoom() const
Returns the minimum zoom level for tiles present in the set.
Definition qgstiles.cpp:180
Stores coordinates of a tile in a tile matrix set.
Definition qgstiles.h:43
int zoomLevel() const
Returns tile's zoom level (Z).
Definition qgstiles.h:56
int column() const
Returns tile's column index (X).
Definition qgstiles.h:52
int row() const
Returns tile's row index (Y).
Definition qgstiles.h:54
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 from one or more sources that need to be decoded.
QMap< QString, QByteArray > data
Raw tile data by source ID.
QgsTileXYZ id
Tile position in tile matrix set.
Utility class for reading and writing ESRI VTPK files.
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:59
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.