QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
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#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
132QgsCoordinateReferenceSystem QgsVtpkVectorTileDataProvider::crs() const
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, Qgis::RendererUsage ) 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 {
328 return QString();
329
331 return QObject::tr( "VTPK Vector Tiles" ) + QStringLiteral( " (*.vtpk *.VTPK)" );
332 }
333 return QString();
334}
335
336QList<QgsProviderSublayerDetails> QgsVtpkVectorTileDataProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags, QgsFeedback * ) const
337{
338 QString fileName;
339 const QFileInfo fi( uri );
340 if ( fi.isFile() )
341 {
342 fileName = uri;
343 }
344 else
345 {
346 const QVariantMap parts = decodeUri( uri );
347 fileName = parts.value( QStringLiteral( "path" ) ).toString();
348 }
349
350 if ( fileName.isEmpty() )
351 return {};
352
353 if ( QFileInfo( fileName ).suffix().compare( QLatin1String( "vtpk" ), Qt::CaseInsensitive ) == 0 )
354 {
355 QVariantMap parts;
356 parts.insert( QStringLiteral( "path" ), fileName );
357
359 details.setUri( encodeUri( parts ) );
360 details.setProviderKey( key() );
363 return {details};
364 }
365 else
366 {
367 return {};
368 }
369}
370
371int QgsVtpkVectorTileDataProviderMetadata::priorityForUri( const QString &uri ) const
372{
373 if ( validLayerTypesForUri( uri ).contains( Qgis::LayerType::VectorTile ) )
374 return 100;
375
376 return 0;
377}
378
379QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::validLayerTypesForUri( const QString &uri ) const
380{
381 const QFileInfo fi( uri );
382 if ( fi.isFile() && fi.suffix().compare( QLatin1String( "vtpk" ), Qt::CaseInsensitive ) == 0 )
383 {
385 }
386
387 const QVariantMap parts = decodeUri( uri );
388 if ( parts.value( QStringLiteral( "path" ) ).toString().endsWith( ".vtpk", Qt::CaseSensitivity::CaseInsensitive ) )
390
391 return {};
392}
393
394QVariantMap QgsVtpkVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
395{
396 QgsDataSourceUri dsUri;
397 dsUri.setEncodedUri( uri );
398
399 QVariantMap uriComponents;
400 uriComponents.insert( QStringLiteral( "type" ), QStringLiteral( "vtpk" ) );
401 uriComponents.insert( QStringLiteral( "path" ), dsUri.param( QStringLiteral( "url" ) ) );
402
403 return uriComponents;
404}
405
406QString QgsVtpkVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
407{
408 QgsDataSourceUri dsUri;
409 dsUri.setParam( QStringLiteral( "type" ), QStringLiteral( "vtpk" ) );
410 dsUri.setParam( QStringLiteral( "url" ), parts.value( parts.contains( QStringLiteral( "path" ) ) ? QStringLiteral( "path" ) : QStringLiteral( "url" ) ).toString() );
411 return dsUri.encodedUri();
412}
413
414QString QgsVtpkVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
415{
416 QVariantMap parts = decodeUri( uri );
417
418 const QString originalPath = parts.value( QStringLiteral( "path" ) ).toString();
419 parts.insert( QStringLiteral( "path" ), context.pathResolver().writePath( originalPath ) );
420
421 return encodeUri( parts );
422}
423
424QString QgsVtpkVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
425{
426 QVariantMap parts = decodeUri( uri );
427
428 const QString originalPath = parts.value( QStringLiteral( "path" ) ).toString();
429 parts.insert( QStringLiteral( "path" ), context.pathResolver().readPath( originalPath ) );
430
431 return encodeUri( parts );
432}
433
434QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::supportedLayerTypes() const
435{
437}
438
439
441
442
@ 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:997
@ 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)
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
RendererUsage
Usage of the renderer.
Definition qgis.h:2548
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
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.
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.
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
Setting options for creating vector data providers.