QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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 ;
85 mExtent = other.mExtent;
86 mMatrixSet = other.mMatrixSet;
87 mLayerMetadata = other.mLayerMetadata;
88 mStyleDefinition = other.mStyleDefinition;
89 mSpriteDefinition = other.mSpriteDefinition;
90 mSpriteImage = other.mSpriteImage;
91}
92
93Qgis::DataProviderFlags QgsVtpkVectorTileDataProvider::flags() const
94{
96}
97
98Qgis::VectorTileProviderFlags QgsVtpkVectorTileDataProvider::providerFlags() const
99{
101}
102
103Qgis::VectorTileProviderCapabilities QgsVtpkVectorTileDataProvider::providerCapabilities() const
104{
106}
107
108QString QgsVtpkVectorTileDataProvider::name() const
109{
111
112 return DATA_PROVIDER_KEY;
113}
114
115QString QgsVtpkVectorTileDataProvider::description() const
116{
118
119 return DATA_PROVIDER_DESCRIPTION;
120}
121
122QgsVectorTileDataProvider *QgsVtpkVectorTileDataProvider::clone() const
123{
125 return new QgsVtpkVectorTileDataProvider( *this );
126}
127
128QString QgsVtpkVectorTileDataProvider::sourcePath() const
129{
131
132 QgsDataSourceUri dsUri;
133 dsUri.setEncodedUri( dataSourceUri() );
134 return dsUri.param( u"url"_s );
135}
136
137bool QgsVtpkVectorTileDataProvider::isValid() const
138{
140
141 return mIsValid;
142}
143
144QgsCoordinateReferenceSystem QgsVtpkVectorTileDataProvider::crs() const
145{
147
148 return mCrs;
149}
150
151QgsRectangle QgsVtpkVectorTileDataProvider::extent() const
152{
154
155 return mExtent;
156}
157
158QgsLayerMetadata QgsVtpkVectorTileDataProvider::layerMetadata() const
159{
160 return mLayerMetadata;
161}
162
163const QgsVectorTileMatrixSet &QgsVtpkVectorTileDataProvider::tileMatrixSet() const
164{
166
167 return mMatrixSet;
168}
169
170QVariantMap QgsVtpkVectorTileDataProvider::styleDefinition() const
171{
173
174 return mStyleDefinition;
175}
176
177QVariantMap QgsVtpkVectorTileDataProvider::spriteDefinition() const
178{
180
181 return mSpriteDefinition;
182}
183
184QImage QgsVtpkVectorTileDataProvider::spriteImage() const
185{
187
188 return mSpriteImage;
189}
190
191QgsVectorTileRawData QgsVtpkVectorTileDataProvider::readTile( const QgsTileMatrixSet &, const QgsTileXYZ &id, QgsFeedback *feedback ) const
192{
194
196 if ( mShared->getCachedTileData( data, id ) )
197 return data;
198
199 QgsDataSourceUri dsUri;
200 dsUri.setEncodedUri( dataSourceUri() );
201 QgsVtpkTiles reader( dsUri.param( u"url"_s ) );
202 reader.open();
203 const QgsVectorTileRawData rawData = loadFromVtpk( reader, id, feedback );
204 mShared->storeCachedTileData( rawData );
205 return rawData;
206}
207
208QList<QgsVectorTileRawData> QgsVtpkVectorTileDataProvider::readTiles( const QgsTileMatrixSet &, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback, Qgis::RendererUsage ) const
209{
211
212 QgsDataSourceUri dsUri;
213 dsUri.setEncodedUri( dataSourceUri() );
214
215 // defer actual creation of reader until we need it -- maybe everything is already present in the cache!
216 std::unique_ptr< QgsVtpkTiles > reader;
217
218 QList<QgsVectorTileRawData> rawTiles;
219 QSet< QgsTileXYZ > fetchedTiles;
220 rawTiles.reserve( tiles.size() );
221 fetchedTiles.reserve( tiles.size() );
222 for ( QgsTileXYZ id : std::as_const( tiles ) )
223 {
224 if ( feedback && feedback->isCanceled() )
225 break;
226
227 if ( fetchedTiles.contains( id ) )
228 continue;
229
231 if ( mShared->getCachedTileData( data, id ) )
232 {
233 rawTiles.append( data );
234 fetchedTiles.insert( data.id );
235 }
236 else
237 {
238 if ( !reader )
239 {
240 reader = std::make_unique< QgsVtpkTiles >( dsUri.param( u"url"_s ) );
241 reader->open();
242 }
243 const QgsVectorTileRawData rawData = loadFromVtpk( *reader, id, feedback );
244 if ( !rawData.data.isEmpty() && !fetchedTiles.contains( rawData.id ) )
245 {
246 rawTiles.append( rawData );
247 fetchedTiles.insert( rawData.id );
248 mShared->storeCachedTileData( rawData );
249 }
250 }
251 }
252 return rawTiles;
253}
254
255QString QgsVtpkVectorTileDataProvider::htmlMetadata() const
256{
257 QString metadata;
258
259 QgsDataSourceUri dsUri;
260 dsUri.setEncodedUri( dataSourceUri() );
261 QgsVtpkTiles reader( dsUri.param( u"url"_s ) );
262 reader.open();
263
264 if ( !reader.rootTileMap().isEmpty() )
265 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;
266 else
267 metadata += u"<tr><td class=\"highlight\">"_s % tr( "VTPK storage" ) % u"</td><td>"_s % tr( "Flat VTPK (no tilemap)" ) % u"</td></tr>\n"_s;
268
269 if ( reader.metadata().contains( u"minLOD"_s ) )
270 {
271 metadata += u"<tr><td class=\"highlight\">"_s
272 % tr( "Tile detail levels" )
273 % u"</td><td>"_s
274 % u"%1 - %2"_s.arg( reader.metadata().value( u"minLOD"_s ).toInt() ).arg( reader.metadata().value( u"maxLOD"_s ).toInt() )
275 % u"</td></tr>\n"_s;
276 }
277
278 return metadata;
279}
280
281QgsVectorTileRawData QgsVtpkVectorTileDataProvider::loadFromVtpk( QgsVtpkTiles &vtpkTileReader, const QgsTileXYZ &id, QgsFeedback * )
282{
283 QgsTileXYZ requestedTile = id;
284 QByteArray tileData = vtpkTileReader.tileData( requestedTile.zoomLevel(), requestedTile.column(), requestedTile.row() );
285 // I **think** here ESRI software will detect a zero size tile and automatically fallback to lower zoom level tiles
286 // I.e. they treat EVERY vtpk a bit like an indexed VTPK, but without the up-front tilemap information.
287 // See https://github.com/qgis/QGIS/issues/52872
288 while ( !tileData.isNull() && tileData.size() == 0 && requestedTile.zoomLevel() > vtpkTileReader.matrixSet().minimumZoom() )
289 {
290 requestedTile = QgsTileXYZ( requestedTile.column() / 2, requestedTile.row() / 2, requestedTile.zoomLevel() - 1 );
291 tileData = vtpkTileReader.tileData( requestedTile.zoomLevel(), requestedTile.column(), requestedTile.row() );
292 }
293
294 if ( tileData.isNull() )
295 return QgsVectorTileRawData();
296
297 QgsVectorTileRawData res( id, tileData );
298 res.tileGeometryId = requestedTile;
299 return res;
300}
301
302
303//
304// QgsVtpkVectorTileDataProviderMetadata
305//
306
307QgsVtpkVectorTileDataProviderMetadata::QgsVtpkVectorTileDataProviderMetadata()
308 : QgsProviderMetadata( QgsVtpkVectorTileDataProvider::DATA_PROVIDER_KEY, QgsVtpkVectorTileDataProvider::DATA_PROVIDER_DESCRIPTION )
309{}
310
311QgsProviderMetadata::ProviderMetadataCapabilities QgsVtpkVectorTileDataProviderMetadata::capabilities() const
312{
313 return ProviderMetadataCapability::LayerTypesForUri | ProviderMetadataCapability::PriorityForUri | ProviderMetadataCapability::QuerySublayers;
314}
315
316QgsVtpkVectorTileDataProvider *QgsVtpkVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
317{
318 return new QgsVtpkVectorTileDataProvider( uri, options, flags );
319}
320
321QIcon QgsVtpkVectorTileDataProviderMetadata::icon() const
322{
323 return QgsApplication::getThemeIcon( u"mIconVectorTileLayer.svg"_s );
324}
325
326QgsProviderMetadata::ProviderCapabilities QgsVtpkVectorTileDataProviderMetadata::providerCapabilities() const
327{
328 return FileBasedUris;
329}
330
331QString QgsVtpkVectorTileDataProviderMetadata::filters( Qgis::FileFilterType type )
332{
333 switch ( type )
334 {
341 return QString();
342
344 return QObject::tr( "VTPK Vector Tiles" ) + u" (*.vtpk *.VTPK)"_s;
345 }
346 return QString();
347}
348
349QList<QgsProviderSublayerDetails> QgsVtpkVectorTileDataProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags, QgsFeedback * ) const
350{
351 QString fileName;
352 const QFileInfo fi( uri );
353 if ( fi.isFile() )
354 {
355 fileName = uri;
356 }
357 else
358 {
359 const QVariantMap parts = decodeUri( uri );
360 fileName = parts.value( u"path"_s ).toString();
361 }
362
363 if ( fileName.isEmpty() )
364 return {};
365
366 if ( QFileInfo( fileName ).suffix().compare( "vtpk"_L1, Qt::CaseInsensitive ) == 0 )
367 {
368 QVariantMap parts;
369 parts.insert( u"path"_s, fileName );
370
372 details.setUri( encodeUri( parts ) );
373 details.setProviderKey( key() );
376 return { details };
377 }
378 else
379 {
380 return {};
381 }
382}
383
384int QgsVtpkVectorTileDataProviderMetadata::priorityForUri( const QString &uri ) const
385{
386 if ( validLayerTypesForUri( uri ).contains( Qgis::LayerType::VectorTile ) )
387 return 100;
388
389 return 0;
390}
391
392QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::validLayerTypesForUri( const QString &uri ) const
393{
394 const QFileInfo fi( uri );
395 if ( fi.isFile() && fi.suffix().compare( "vtpk"_L1, Qt::CaseInsensitive ) == 0 )
396 {
398 }
399
400 const QVariantMap parts = decodeUri( uri );
401 if ( parts.value( u"path"_s ).toString().endsWith( ".vtpk", Qt::CaseSensitivity::CaseInsensitive ) )
403
404 return {};
405}
406
407QVariantMap QgsVtpkVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
408{
409 QgsDataSourceUri dsUri;
410 dsUri.setEncodedUri( uri );
411
412 QVariantMap uriComponents;
413 uriComponents.insert( u"type"_s, u"vtpk"_s );
414 uriComponents.insert( u"path"_s, dsUri.param( u"url"_s ) );
415
416 return uriComponents;
417}
418
419QString QgsVtpkVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
420{
421 QgsDataSourceUri dsUri;
422 dsUri.setParam( u"type"_s, u"vtpk"_s );
423 dsUri.setParam( u"url"_s, parts.value( parts.contains( u"path"_s ) ? u"path"_s : u"url"_s ).toString() );
424 return dsUri.encodedUri();
425}
426
427QString QgsVtpkVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
428{
429 QVariantMap parts = decodeUri( uri );
430
431 const QString originalPath = parts.value( u"path"_s ).toString();
432 parts.insert( u"path"_s, context.pathResolver().writePath( originalPath ) );
433
434 return encodeUri( parts );
435}
436
437QString QgsVtpkVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
438{
439 QVariantMap parts = decodeUri( uri );
440
441 const QString originalPath = parts.value( u"path"_s ).toString();
442 parts.insert( u"path"_s, context.pathResolver().readPath( originalPath ) );
443
444 return encodeUri( parts );
445}
446
447QList<Qgis::LayerType> QgsVtpkVectorTileDataProviderMetadata::supportedLayerTypes() const
448{
450}
451
452
@ ReadLayerMetadata
Provider can read layer metadata from data store. See QgsDataProvider::layerMetadata().
Definition qgis.h:5945
@ AlwaysUseTileMatrixSetFromProvider
Vector tile layer must always use the tile matrix set from the data provider, and should never store,...
Definition qgis.h:5927
QFlags< DataProviderFlag > DataProviderFlags
Data provider flags.
Definition qgis.h:2397
FileFilterType
Type of file filters.
Definition qgis.h:1419
@ TiledScene
Tiled scene layers.
Definition qgis.h:1426
@ Vector
Vector layers.
Definition qgis.h:1420
@ VectorTile
Vector tile layers.
Definition qgis.h:1425
@ Mesh
Mesh layers.
Definition qgis.h:1422
@ Raster
Raster layers.
Definition qgis.h:1421
@ MeshDataset
Mesh datasets.
Definition qgis.h:1423
@ PointCloud
Point clouds.
Definition qgis.h:1424
@ FastExtent2D
Provider's 2D extent retrieval via QgsDataProvider::extent() is always guaranteed to be trivial/fast ...
Definition qgis.h:2392
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:512
QFlags< VectorTileProviderCapability > VectorTileProviderCapabilities
Vector tile data provider capabilities.
Definition qgis.h:5954
QFlags< SublayerQueryFlag > SublayerQueryFlags
Sublayer query flags.
Definition qgis.h:1477
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:211
RendererUsage
Usage of the renderer.
Definition qgis.h:3559
QFlags< VectorTileProviderFlag > VectorTileProviderFlags
Vector tile data provider flags.
Definition qgis.h:5936
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:56
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:284
int minimumZoom() const
Returns the minimum zoom level for tiles present in the set.
Definition qgstiles.cpp:182
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:57
int column() const
Returns tile's column index (X).
Definition qgstiles.h:53
int row() const
Returns tile's row index (Y).
Definition qgstiles.h:55
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.