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