QGIS API Documentation 3.99.0-Master (752b475928d)
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
29#include "moc_qgsvtpkvectortiledataprovider.cpp"
30
32
33
34QString QgsVtpkVectorTileDataProvider::DATA_PROVIDER_KEY = QStringLiteral( "vtpkvectortiles" );
35QString QgsVtpkVectorTileDataProvider::DATA_PROVIDER_DESCRIPTION = QObject::tr( "VTPK Vector Tiles data provider" );
36
37
38QgsVtpkVectorTileDataProvider::QgsVtpkVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, Qgis::DataProviderReadFlags flags )
39 : QgsVectorTileDataProvider( uri, providerOptions, flags )
40{
41 QgsDataSourceUri dsUri;
42 dsUri.setEncodedUri( dataSourceUri() );
43 const QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
44
45 QgsVtpkTiles reader( sourcePath );
46 if ( !reader.open() )
47 {
48 QgsDebugError( QStringLiteral( "failed to open VTPK file: " ) + sourcePath );
49 mIsValid = false;
50 return;
51 }
52
53 const QVariantMap metadata = reader.metadata();
54 const QString format = metadata.value( QStringLiteral( "tileInfo" ) ).toMap().value( QStringLiteral( "format" ) ).toString();
55 if ( format != QLatin1String( "pbf" ) )
56 {
57 QgsDebugError( QStringLiteral( "Cannot open VTPK for vector tiles. Format = " ) + format );
58 mIsValid = false;
59 return;
60 }
61
62 mMatrixSet = reader.matrixSet();
63 mCrs = mMatrixSet.crs();
64 mExtent = reader.extent( transformContext() );
65 mLayerMetadata = reader.layerMetadata();
66 mStyleDefinition = reader.styleDefinition();
67 mSpriteDefinition = reader.spriteDefinition();
68 if ( !mSpriteDefinition.isEmpty() )
69 {
70 mSpriteImage = reader.spriteImage();
71 }
72
73 mIsValid = true;
74}
75
76QgsVtpkVectorTileDataProvider::QgsVtpkVectorTileDataProvider( const QgsVtpkVectorTileDataProvider &other )
78{
79 mIsValid = other.mIsValid;
80 mCrs = other.mCrs;;
81 mExtent = other.mExtent;
82 mMatrixSet = other.mMatrixSet;
83 mLayerMetadata = other.mLayerMetadata;
84 mStyleDefinition = other.mStyleDefinition;
85 mSpriteDefinition = other.mSpriteDefinition;
86 mSpriteImage = other.mSpriteImage;
87}
88
89Qgis::DataProviderFlags QgsVtpkVectorTileDataProvider::flags() const
90{
92}
93
94Qgis::VectorTileProviderFlags QgsVtpkVectorTileDataProvider::providerFlags() const
95{
97}
98
99Qgis::VectorTileProviderCapabilities QgsVtpkVectorTileDataProvider::providerCapabilities() const
100{
102}
103
104QString QgsVtpkVectorTileDataProvider::name() const
105{
107
108 return DATA_PROVIDER_KEY;
109}
110
111QString QgsVtpkVectorTileDataProvider::description() const
112{
114
115 return DATA_PROVIDER_DESCRIPTION;
116}
117
118QgsVectorTileDataProvider *QgsVtpkVectorTileDataProvider::clone() const
119{
121 return new QgsVtpkVectorTileDataProvider( *this );
122}
123
124QString QgsVtpkVectorTileDataProvider::sourcePath() const
125{
127
128 QgsDataSourceUri dsUri;
129 dsUri.setEncodedUri( dataSourceUri() );
130 return dsUri.param( QStringLiteral( "url" ) );
131}
132
133bool QgsVtpkVectorTileDataProvider::isValid() const
134{
136
137 return mIsValid;
138}
139
140QgsCoordinateReferenceSystem QgsVtpkVectorTileDataProvider::crs() const
141{
143
144 return mCrs;
145}
146
147QgsRectangle QgsVtpkVectorTileDataProvider::extent() const
148{
150
151 return mExtent;
152}
153
154QgsLayerMetadata QgsVtpkVectorTileDataProvider::layerMetadata() const
155{
156 return mLayerMetadata;
157}
158
159const QgsVectorTileMatrixSet &QgsVtpkVectorTileDataProvider::tileMatrixSet() const
160{
162
163 return mMatrixSet;
164}
165
166QVariantMap QgsVtpkVectorTileDataProvider::styleDefinition() const
167{
169
170 return mStyleDefinition;
171}
172
173QVariantMap QgsVtpkVectorTileDataProvider::spriteDefinition() const
174{
176
177 return mSpriteDefinition;
178}
179
180QImage QgsVtpkVectorTileDataProvider::spriteImage() const
181{
183
184 return mSpriteImage;
185}
186
187QgsVectorTileRawData QgsVtpkVectorTileDataProvider::readTile( const QgsTileMatrixSet &, const QgsTileXYZ &id, QgsFeedback *feedback ) const
188{
190
192 if ( mShared->getCachedTileData( data, id ) )
193 return data;
194
195 QgsDataSourceUri dsUri;
196 dsUri.setEncodedUri( dataSourceUri() );
197 QgsVtpkTiles reader( dsUri.param( QStringLiteral( "url" ) ) );
198 reader.open();
199 const QgsVectorTileRawData rawData = loadFromVtpk( reader, id, feedback );
200 mShared->storeCachedTileData( rawData );
201 return rawData;
202}
203
204QList<QgsVectorTileRawData> QgsVtpkVectorTileDataProvider::readTiles( const QgsTileMatrixSet &, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback, Qgis::RendererUsage ) const
205{
207
208 QgsDataSourceUri dsUri;
209 dsUri.setEncodedUri( dataSourceUri() );
210
211 // defer actual creation of reader until we need it -- maybe everything is already present in the cache!
212 std::unique_ptr< QgsVtpkTiles > reader;
213
214 QList<QgsVectorTileRawData> rawTiles;
215 QSet< QgsTileXYZ > fetchedTiles;
216 rawTiles.reserve( tiles.size() );
217 fetchedTiles.reserve( tiles.size() );
218 for ( QgsTileXYZ id : std::as_const( tiles ) )
219 {
220 if ( feedback && feedback->isCanceled() )
221 break;
222
223 if ( fetchedTiles.contains( id ) )
224 continue;
225
227 if ( mShared->getCachedTileData( data, id ) )
228 {
229 rawTiles.append( data );
230 fetchedTiles.insert( data.id );
231 }
232 else
233 {
234 if ( !reader )
235 {
236 reader = std::make_unique< QgsVtpkTiles >( dsUri.param( QStringLiteral( "url" ) ) );
237 reader->open();
238 }
239 const QgsVectorTileRawData rawData = loadFromVtpk( *reader, id, feedback );
240 if ( !rawData.data.isEmpty() && !fetchedTiles.contains( rawData.id ) )
241 {
242 rawTiles.append( rawData );
243 fetchedTiles.insert( rawData.id );
244 mShared->storeCachedTileData( rawData );
245 }
246 }
247 }
248 return rawTiles;
249}
250
251QString QgsVtpkVectorTileDataProvider::htmlMetadata() const
252{
253 QString metadata;
254
255 QgsDataSourceUri dsUri;
256 dsUri.setEncodedUri( dataSourceUri() );
257 QgsVtpkTiles reader( dsUri.param( QStringLiteral( "url" ) ) );
258 reader.open();
259
260 if ( !reader.rootTileMap().isEmpty() )
261 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "VTPK storage" ) % QStringLiteral( "</td><td>" ) % tr( "Indexed VTPK (tilemap is present)" ) % QStringLiteral( "</td></tr>\n" );
262 else
263 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "VTPK storage" ) % QStringLiteral( "</td><td>" ) % tr( "Flat VTPK (no tilemap)" ) % QStringLiteral( "</td></tr>\n" );
264
265 if ( reader.metadata().contains( QStringLiteral( "minLOD" ) ) )
266 {
267 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" );
268 }
269
270 return metadata;
271}
272
273QgsVectorTileRawData QgsVtpkVectorTileDataProvider::loadFromVtpk( QgsVtpkTiles &vtpkTileReader, const QgsTileXYZ &id, QgsFeedback * )
274{
275 QgsTileXYZ requestedTile = id;
276 QByteArray tileData = vtpkTileReader.tileData( requestedTile.zoomLevel(), requestedTile.column(), requestedTile.row() );
277 // I **think** here ESRI software will detect a zero size tile and automatically fallback to lower zoom level tiles
278 // I.e. they treat EVERY vtpk a bit like an indexed VTPK, but without the up-front tilemap information.
279 // See https://github.com/qgis/QGIS/issues/52872
280 while ( !tileData.isNull() && tileData.size() == 0 && requestedTile.zoomLevel() > vtpkTileReader.matrixSet().minimumZoom() )
281 {
282 requestedTile = QgsTileXYZ( requestedTile.column() / 2, requestedTile.row() / 2, requestedTile.zoomLevel() - 1 );
283 tileData = vtpkTileReader.tileData( requestedTile.zoomLevel(), requestedTile.column(), requestedTile.row() );
284 }
285
286 if ( tileData.isNull() )
287 return QgsVectorTileRawData();
288
289 QgsVectorTileRawData res( id, tileData );
290 res.tileGeometryId = requestedTile;
291 return res;
292}
293
294
295//
296// QgsVtpkVectorTileDataProviderMetadata
297//
298
299QgsVtpkVectorTileDataProviderMetadata::QgsVtpkVectorTileDataProviderMetadata()
300 : QgsProviderMetadata( QgsVtpkVectorTileDataProvider::DATA_PROVIDER_KEY, QgsVtpkVectorTileDataProvider::DATA_PROVIDER_DESCRIPTION )
301{
302}
303
304QgsProviderMetadata::ProviderMetadataCapabilities QgsVtpkVectorTileDataProviderMetadata::capabilities() const
305{
306 return ProviderMetadataCapability::LayerTypesForUri
307 | ProviderMetadataCapability::PriorityForUri
308 | ProviderMetadataCapability::QuerySublayers;
309}
310
311QgsVtpkVectorTileDataProvider *QgsVtpkVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
312{
313 return new QgsVtpkVectorTileDataProvider( uri, options, flags );
314}
315
316QIcon QgsVtpkVectorTileDataProviderMetadata::icon() const
317{
318 return QgsApplication::getThemeIcon( QStringLiteral( "mIconVectorTileLayer.svg" ) );
319}
320
321QgsProviderMetadata::ProviderCapabilities QgsVtpkVectorTileDataProviderMetadata::providerCapabilities() const
322{
323 return FileBasedUris;
324}
325
326QString QgsVtpkVectorTileDataProviderMetadata::filters( Qgis::FileFilterType type )
327{
328 switch ( type )
329 {
336 return QString();
337
339 return QObject::tr( "VTPK Vector Tiles" ) + QStringLiteral( " (*.vtpk *.VTPK)" );
340 }
341 return QString();
342}
343
344QList<QgsProviderSublayerDetails> QgsVtpkVectorTileDataProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags, QgsFeedback * ) const
345{
346 QString fileName;
347 const QFileInfo fi( uri );
348 if ( fi.isFile() )
349 {
350 fileName = uri;
351 }
352 else
353 {
354 const QVariantMap parts = decodeUri( uri );
355 fileName = parts.value( QStringLiteral( "path" ) ).toString();
356 }
357
358 if ( fileName.isEmpty() )
359 return {};
360
361 if ( QFileInfo( fileName ).suffix().compare( QLatin1String( "vtpk" ), Qt::CaseInsensitive ) == 0 )
362 {
363 QVariantMap parts;
364 parts.insert( QStringLiteral( "path" ), fileName );
365
367 details.setUri( encodeUri( parts ) );
368 details.setProviderKey( key() );
371 return {details};
372 }
373 else
374 {
375 return {};
376 }
377}
378
379int QgsVtpkVectorTileDataProviderMetadata::priorityForUri( const QString &uri ) const
380{
381 if ( validLayerTypesForUri( uri ).contains( Qgis::LayerType::VectorTile ) )
382 return 100;
383
384 return 0;
385}
386
387QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::validLayerTypesForUri( const QString &uri ) const
388{
389 const QFileInfo fi( uri );
390 if ( fi.isFile() && fi.suffix().compare( QLatin1String( "vtpk" ), Qt::CaseInsensitive ) == 0 )
391 {
393 }
394
395 const QVariantMap parts = decodeUri( uri );
396 if ( parts.value( QStringLiteral( "path" ) ).toString().endsWith( ".vtpk", Qt::CaseSensitivity::CaseInsensitive ) )
398
399 return {};
400}
401
402QVariantMap QgsVtpkVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
403{
404 QgsDataSourceUri dsUri;
405 dsUri.setEncodedUri( uri );
406
407 QVariantMap uriComponents;
408 uriComponents.insert( QStringLiteral( "type" ), QStringLiteral( "vtpk" ) );
409 uriComponents.insert( QStringLiteral( "path" ), dsUri.param( QStringLiteral( "url" ) ) );
410
411 return uriComponents;
412}
413
414QString QgsVtpkVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
415{
416 QgsDataSourceUri dsUri;
417 dsUri.setParam( QStringLiteral( "type" ), QStringLiteral( "vtpk" ) );
418 dsUri.setParam( QStringLiteral( "url" ), parts.value( parts.contains( QStringLiteral( "path" ) ) ? QStringLiteral( "path" ) : QStringLiteral( "url" ) ).toString() );
419 return dsUri.encodedUri();
420}
421
422QString QgsVtpkVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
423{
424 QVariantMap parts = decodeUri( uri );
425
426 const QString originalPath = parts.value( QStringLiteral( "path" ) ).toString();
427 parts.insert( QStringLiteral( "path" ), context.pathResolver().writePath( originalPath ) );
428
429 return encodeUri( parts );
430}
431
432QString QgsVtpkVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
433{
434 QVariantMap parts = decodeUri( uri );
435
436 const QString originalPath = parts.value( QStringLiteral( "path" ) ).toString();
437 parts.insert( QStringLiteral( "path" ), context.pathResolver().readPath( originalPath ) );
438
439 return encodeUri( parts );
440}
441
442QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::supportedLayerTypes() const
443{
445}
446
447
449
450
@ ReadLayerMetadata
Provider can read layer metadata from data store. See QgsDataProvider::layerMetadata().
Definition qgis.h:5589
@ AlwaysUseTileMatrixSetFromProvider
Vector tile layer must always use the tile matrix set from the data provider, and should never store,...
Definition qgis.h:5571
QFlags< DataProviderFlag > DataProviderFlags
Data provider flags.
Definition qgis.h:2315
FileFilterType
Type of file filters.
Definition qgis.h:1354
@ TiledScene
Tiled scene layers.
Definition qgis.h:1361
@ Vector
Vector layers.
Definition qgis.h:1355
@ VectorTile
Vector tile layers.
Definition qgis.h:1360
@ Mesh
Mesh layers.
Definition qgis.h:1357
@ Raster
Raster layers.
Definition qgis.h:1356
@ MeshDataset
Mesh datasets.
Definition qgis.h:1358
@ PointCloud
Point clouds.
Definition qgis.h:1359
@ FastExtent2D
Provider's 2D extent retrieval via QgsDataProvider::extent() is always guaranteed to be trivial/fast ...
Definition qgis.h:2310
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:486
QFlags< VectorTileProviderCapability > VectorTileProviderCapabilities
Vector tile data provider capabilities.
Definition qgis.h:5598
QFlags< SublayerQueryFlag > SublayerQueryFlags
Sublayer query flags.
Definition qgis.h:1399
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:195
RendererUsage
Usage of the renderer.
Definition qgis.h:3445
QFlags< VectorTileProviderFlag > VectorTileProviderFlags
Vector tile data provider flags.
Definition qgis.h:5580
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: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.
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:274
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:39
int zoomLevel() const
Returns tile's zoom level (Z).
Definition qgstiles.h:52
int column() const
Returns tile's column index (X).
Definition qgstiles.h:48
int row() const
Returns tile's row index (Y).
Definition qgstiles.h:50
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:57
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.