QGIS API Documentation 3.31.0-Master (9f23a2c1dc)
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::VectorTileProviderFlags QgsVtpkVectorTileDataProvider::providerFlags() const
87{
89}
90
91Qgis::VectorTileProviderCapabilities QgsVtpkVectorTileDataProvider::providerCapabilities() const
92{
94}
95
96QString QgsVtpkVectorTileDataProvider::name() const
97{
99
100 return DATA_PROVIDER_KEY;
101}
102
103QString QgsVtpkVectorTileDataProvider::description() const
104{
106
107 return DATA_PROVIDER_DESCRIPTION;
108}
109
110QgsVectorTileDataProvider *QgsVtpkVectorTileDataProvider::clone() const
111{
113 return new QgsVtpkVectorTileDataProvider( *this );
114}
115
116QString QgsVtpkVectorTileDataProvider::sourcePath() const
117{
119
120 QgsDataSourceUri dsUri;
121 dsUri.setEncodedUri( dataSourceUri() );
122 return dsUri.param( QStringLiteral( "url" ) );
123}
124
125bool QgsVtpkVectorTileDataProvider::isValid() const
126{
128
129 return mIsValid;
130}
131
133{
135
136 return mCrs;
137}
138
139QgsRectangle QgsVtpkVectorTileDataProvider::extent() const
140{
142
143 return mExtent;
144}
145
146QgsLayerMetadata QgsVtpkVectorTileDataProvider::layerMetadata() const
147{
148 return mLayerMetadata;
149}
150
151const QgsVectorTileMatrixSet &QgsVtpkVectorTileDataProvider::tileMatrixSet() const
152{
154
155 return mMatrixSet;
156}
157
158QVariantMap QgsVtpkVectorTileDataProvider::styleDefinition() const
159{
161
162 return mStyleDefinition;
163}
164
165QVariantMap QgsVtpkVectorTileDataProvider::spriteDefinition() const
166{
168
169 return mSpriteDefinition;
170}
171
172QImage QgsVtpkVectorTileDataProvider::spriteImage() const
173{
175
176 return mSpriteImage;
177}
178
179QgsVectorTileRawData QgsVtpkVectorTileDataProvider::readTile( const QgsTileMatrixSet &, const QgsTileXYZ &id, QgsFeedback *feedback ) const
180{
182
184 if ( mShared->getCachedTileData( data, id ) )
185 return data;
186
187 QgsDataSourceUri dsUri;
188 dsUri.setEncodedUri( dataSourceUri() );
189 QgsVtpkTiles reader( dsUri.param( QStringLiteral( "url" ) ) );
190 reader.open();
191 const QgsVectorTileRawData rawData = loadFromVtpk( reader, id, feedback );
192 mShared->storeCachedTileData( rawData );
193 return rawData;
194}
195
196QList<QgsVectorTileRawData> QgsVtpkVectorTileDataProvider::readTiles( const QgsTileMatrixSet &, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback ) const
197{
199
200 QgsDataSourceUri dsUri;
201 dsUri.setEncodedUri( dataSourceUri() );
202
203 // defer actual creation of reader until we need it -- maybe everything is already present in the cache!
204 std::unique_ptr< QgsVtpkTiles > reader;
205
206 QList<QgsVectorTileRawData> rawTiles;
207 QSet< QgsTileXYZ > fetchedTiles;
208 rawTiles.reserve( tiles.size() );
209 fetchedTiles.reserve( tiles.size() );
210 for ( QgsTileXYZ id : std::as_const( tiles ) )
211 {
212 if ( feedback && feedback->isCanceled() )
213 break;
214
215 if ( fetchedTiles.contains( id ) )
216 continue;
217
219 if ( mShared->getCachedTileData( data, id ) )
220 {
221 rawTiles.append( data );
222 fetchedTiles.insert( data.id );
223 }
224 else
225 {
226 if ( !reader )
227 {
228 reader = std::make_unique< QgsVtpkTiles >( dsUri.param( QStringLiteral( "url" ) ) );
229 reader->open();
230 }
231 const QgsVectorTileRawData rawData = loadFromVtpk( *reader, id, feedback );
232 if ( !rawData.data.isEmpty() && !fetchedTiles.contains( rawData.id ) )
233 {
234 rawTiles.append( rawData );
235 fetchedTiles.insert( rawData.id );
236 mShared->storeCachedTileData( rawData );
237 }
238 }
239 }
240 return rawTiles;
241}
242
243QString QgsVtpkVectorTileDataProvider::htmlMetadata() const
244{
245 QString metadata;
246
247 QgsDataSourceUri dsUri;
248 dsUri.setEncodedUri( dataSourceUri() );
249 QgsVtpkTiles reader( dsUri.param( QStringLiteral( "url" ) ) );
250 reader.open();
251
252 if ( !reader.rootTileMap().isEmpty() )
253 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "VTPK storage" ) % QStringLiteral( "</td><td>" ) % tr( "Indexed VTPK (tilemap is present)" ) % QStringLiteral( "</td></tr>\n" );
254 else
255 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "VTPK storage" ) % QStringLiteral( "</td><td>" ) % tr( "Flat VTPK (no tilemap)" ) % QStringLiteral( "</td></tr>\n" );
256
257 if ( reader.metadata().contains( QStringLiteral( "minLOD" ) ) )
258 {
259 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" );
260 }
261
262 return metadata;
263}
264
265QgsVectorTileRawData QgsVtpkVectorTileDataProvider::loadFromVtpk( QgsVtpkTiles &vtpkTileReader, const QgsTileXYZ &id, QgsFeedback * )
266{
267 QgsTileXYZ requestedTile = id;
268 QByteArray tileData = vtpkTileReader.tileData( requestedTile.zoomLevel(), requestedTile.column(), requestedTile.row() );
269 // I **think** here ESRI software will detect a zero size tile and automatically fallback to lower zoom level tiles
270 // I.e. they treat EVERY vtpk a bit like an indexed VTPK, but without the up-front tilemap information.
271 // See https://github.com/qgis/QGIS/issues/52872
272 while ( !tileData.isNull() && tileData.size() == 0 && requestedTile.zoomLevel() > vtpkTileReader.matrixSet().minimumZoom() )
273 {
274 requestedTile = QgsTileXYZ( requestedTile.column() / 2, requestedTile.row() / 2, requestedTile.zoomLevel() - 1 );
275 tileData = vtpkTileReader.tileData( requestedTile.zoomLevel(), requestedTile.column(), requestedTile.row() );
276 }
277
278 if ( tileData.isNull() )
279 return QgsVectorTileRawData();
280
281 QgsVectorTileRawData res( id, tileData );
282 res.tileGeometryId = requestedTile;
283 return res;
284}
285
286
287//
288// QgsVtpkVectorTileDataProviderMetadata
289//
290
291QgsVtpkVectorTileDataProviderMetadata::QgsVtpkVectorTileDataProviderMetadata()
292 : QgsProviderMetadata( QgsVtpkVectorTileDataProvider::DATA_PROVIDER_KEY, QgsVtpkVectorTileDataProvider::DATA_PROVIDER_DESCRIPTION )
293{
294}
295
296QgsProviderMetadata::ProviderMetadataCapabilities QgsVtpkVectorTileDataProviderMetadata::capabilities() const
297{
298 return ProviderMetadataCapability::LayerTypesForUri
299 | ProviderMetadataCapability::PriorityForUri
300 | ProviderMetadataCapability::QuerySublayers;
301}
302
303QgsVtpkVectorTileDataProvider *QgsVtpkVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
304{
305 return new QgsVtpkVectorTileDataProvider( uri, options, flags );
306}
307
308QIcon QgsVtpkVectorTileDataProviderMetadata::icon() const
309{
310 return QgsApplication::getThemeIcon( QStringLiteral( "mIconVectorTileLayer.svg" ) );
311}
312
313QgsProviderMetadata::ProviderCapabilities QgsVtpkVectorTileDataProviderMetadata::providerCapabilities() const
314{
315 return FileBasedUris;
316}
317
318QString QgsVtpkVectorTileDataProviderMetadata::filters( Qgis::FileFilterType type )
319{
320 switch ( type )
321 {
322 case Qgis::FileFilterType::Vector:
323 case Qgis::FileFilterType::Raster:
324 case Qgis::FileFilterType::Mesh:
325 case Qgis::FileFilterType::MeshDataset:
326 case Qgis::FileFilterType::PointCloud:
327 return QString();
328
330 return QObject::tr( "VTPK Vector Tiles" ) + QStringLiteral( " (*.vtpk *.VTPK)" );
331 }
332 return QString();
333}
334
335QList<QgsProviderSublayerDetails> QgsVtpkVectorTileDataProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags, QgsFeedback * ) const
336{
337 QString fileName;
338 const QFileInfo fi( uri );
339 if ( fi.isFile() )
340 {
341 fileName = uri;
342 }
343 else
344 {
345 const QVariantMap parts = decodeUri( uri );
346 fileName = parts.value( QStringLiteral( "path" ) ).toString();
347 }
348
349 if ( fileName.isEmpty() )
350 return {};
351
352 if ( QFileInfo( fileName ).suffix().compare( QLatin1String( "vtpk" ), Qt::CaseInsensitive ) == 0 )
353 {
354 QVariantMap parts;
355 parts.insert( QStringLiteral( "path" ), fileName );
356
358 details.setUri( encodeUri( parts ) );
359 details.setProviderKey( key() );
360 details.setType( Qgis::LayerType::VectorTile );
362 return {details};
363 }
364 else
365 {
366 return {};
367 }
368}
369
370int QgsVtpkVectorTileDataProviderMetadata::priorityForUri( const QString &uri ) const
371{
372 if ( validLayerTypesForUri( uri ).contains( Qgis::LayerType::VectorTile ) )
373 return 100;
374
375 return 0;
376}
377
378QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::validLayerTypesForUri( const QString &uri ) const
379{
380 const QFileInfo fi( uri );
381 if ( fi.isFile() && fi.suffix().compare( QLatin1String( "vtpk" ), Qt::CaseInsensitive ) == 0 )
382 {
383 return { Qgis::LayerType::VectorTile };
384 }
385
386 const QVariantMap parts = decodeUri( uri );
387 if ( parts.value( QStringLiteral( "path" ) ).toString().endsWith( ".vtpk", Qt::CaseSensitivity::CaseInsensitive ) )
388 return { Qgis::LayerType::VectorTile };
389
390 return {};
391}
392
393QVariantMap QgsVtpkVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
394{
395 QgsDataSourceUri dsUri;
396 dsUri.setEncodedUri( uri );
397
398 QVariantMap uriComponents;
399 uriComponents.insert( QStringLiteral( "type" ), QStringLiteral( "vtpk" ) );
400 uriComponents.insert( QStringLiteral( "path" ), dsUri.param( QStringLiteral( "url" ) ) );
401
402 return uriComponents;
403}
404
405QString QgsVtpkVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
406{
407 QgsDataSourceUri dsUri;
408 dsUri.setParam( QStringLiteral( "type" ), QStringLiteral( "vtpk" ) );
409 dsUri.setParam( QStringLiteral( "url" ), parts.value( parts.contains( QStringLiteral( "path" ) ) ? QStringLiteral( "path" ) : QStringLiteral( "url" ) ).toString() );
410 return dsUri.encodedUri();
411}
412
413QString QgsVtpkVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
414{
415 QVariantMap parts = decodeUri( uri );
416
417 const QString originalPath = parts.value( QStringLiteral( "path" ) ).toString();
418 parts.insert( QStringLiteral( "path" ), context.pathResolver().writePath( originalPath ) );
419
420 return encodeUri( parts );
421}
422
423QString QgsVtpkVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
424{
425 QVariantMap parts = decodeUri( uri );
426
427 const QString originalPath = parts.value( QStringLiteral( "path" ) ).toString();
428 parts.insert( QStringLiteral( "path" ), context.pathResolver().readPath( originalPath ) );
429
430 return encodeUri( parts );
431}
432
433QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::supportedLayerTypes() const
434{
435 return { Qgis::LayerType::VectorTile };
436}
437
438
440
441
@ 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,...
FileFilterType
Type of file filters.
Definition: qgis.h:929
@ VectorTile
Vector tile layers (since QGIS 3.32)
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).
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:45
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
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...
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.