QGIS API Documentation 3.41.0-Master (88383c3d16f)
Loading...
Searching...
No Matches
qgsmbtilesvectortiledataprovider.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmbtilesvectortiledataprovider.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_qgsmbtilesvectortiledataprovider.cpp"
18#include "qgsthreadingutils.h"
19#include "qgsmbtiles.h"
20#include "qgstiles.h"
21#include "qgsvectortileloader.h"
22#include "qgsziputils.h"
23#include "qgslogger.h"
24#include "qgsapplication.h"
26#include "qgsproviderutils.h"
28
29#include <QIcon>
30#include <QFileInfo>
31
33
34QString QgsMbTilesVectorTileDataProvider::MB_TILES_VECTOR_TILE_DATA_PROVIDER_KEY = QStringLiteral( "mbtilesvectortiles" );
35QString QgsMbTilesVectorTileDataProvider::MB_TILES_VECTOR_TILE_DATA_PROVIDER_DESCRIPTION = QObject::tr( "MBTile Vector Tiles data provider" );
36
37QgsMbTilesVectorTileDataProvider::QgsMbTilesVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, Qgis::DataProviderReadFlags flags )
38 : QgsVectorTileDataProvider( uri, providerOptions, flags )
39{
40 QgsDataSourceUri dsUri;
41 dsUri.setEncodedUri( uri );
42 const QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
43
44 QgsMbTiles reader( sourcePath );
45 if ( !reader.open() )
46 {
47 QgsDebugError( QStringLiteral( "failed to open MBTiles file: " ) + sourcePath );
48 mIsValid = false;
49 return;
50 }
51
52 const QString format = reader.metadataValue( QStringLiteral( "format" ) );
53 if ( format != QLatin1String( "pbf" ) )
54 {
55 QgsDebugError( QStringLiteral( "Cannot open MBTiles for vector tiles. Format = " ) + format );
56 mIsValid = false;
57 return;
58 }
59
60 QgsDebugMsgLevel( QStringLiteral( "name: " ) + reader.metadataValue( QStringLiteral( "name" ) ), 2 );
61
62 bool minZoomOk, maxZoomOk;
63 const int minZoom = reader.metadataValue( QStringLiteral( "minzoom" ) ).toInt( &minZoomOk );
64 const int maxZoom = reader.metadataValue( QStringLiteral( "maxzoom" ) ).toInt( &maxZoomOk );
65 if ( minZoomOk && maxZoomOk )
66 {
67 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( minZoom, maxZoom );
68 }
69 else if ( minZoomOk )
70 {
71 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( minZoom, 99 );
72 }
73 else if ( maxZoomOk )
74 {
75 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( 0, maxZoom );
76 }
77 else
78 {
80 }
81
82 QgsDebugMsgLevel( QStringLiteral( "zoom range: %1 - %2" ).arg( mMatrixSet.minimumZoom() ).arg( mMatrixSet.maximumZoom() ), 2 );
83
84 QgsRectangle r = reader.extent();
85 QgsCoordinateTransform ct( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ),
86 QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) ), transformContext() );
87 ct.setBallparkTransformsAreAppropriate( true );
88 try
89 {
90 mExtent = ct.transformBoundingBox( r );
91 }
92 catch ( QgsCsException & )
93 {
94 QgsDebugError( QStringLiteral( "Could not transform layer extent to layer CRS" ) );
95 }
96
97 mIsValid = true;
98}
99
100QgsMbTilesVectorTileDataProvider::QgsMbTilesVectorTileDataProvider( const QgsMbTilesVectorTileDataProvider &other )
102{
103 mIsValid = other.mIsValid;
104 mExtent = other.mExtent;
105 mMatrixSet = other.mMatrixSet;
106}
107
108Qgis::DataProviderFlags QgsMbTilesVectorTileDataProvider::flags() const
109{
111}
112
113QString QgsMbTilesVectorTileDataProvider::name() const
114{
116
117 return MB_TILES_VECTOR_TILE_DATA_PROVIDER_KEY;
118}
119
120QString QgsMbTilesVectorTileDataProvider::description() const
121{
123
124 return MB_TILES_VECTOR_TILE_DATA_PROVIDER_DESCRIPTION;
125}
126
127QgsVectorTileDataProvider *QgsMbTilesVectorTileDataProvider::clone() const
128{
130 return new QgsMbTilesVectorTileDataProvider( *this );
131}
132
133QString QgsMbTilesVectorTileDataProvider::sourcePath() const
134{
136
137 QgsDataSourceUri dsUri;
138 dsUri.setEncodedUri( dataSourceUri() );
139 return dsUri.param( QStringLiteral( "url" ) );
140}
141
142bool QgsMbTilesVectorTileDataProvider::isValid() const
143{
145
146 return mIsValid;
147}
148
149QgsRectangle QgsMbTilesVectorTileDataProvider::extent() const
150{
152
153 return mExtent;
154}
155
156QgsCoordinateReferenceSystem QgsMbTilesVectorTileDataProvider::crs() const
157{
159
160 return QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) );
161}
162
163const QgsVectorTileMatrixSet &QgsMbTilesVectorTileDataProvider::tileMatrixSet() const
164{
166
167 return mMatrixSet;
168}
169
170QgsVectorTileRawData QgsMbTilesVectorTileDataProvider::readTile( const QgsTileMatrixSet &, const QgsTileXYZ &id, QgsFeedback *feedback ) const
171{
173
174 QgsDataSourceUri dsUri;
175 dsUri.setEncodedUri( dataSourceUri() );
176
177 QgsMbTiles mbReader( dsUri.param( QStringLiteral( "url" ) ) );
178 mbReader.open();
179 return QgsVectorTileRawData( id, loadFromMBTiles( mbReader, id, feedback ) );
180}
181
182QList<QgsVectorTileRawData> QgsMbTilesVectorTileDataProvider::readTiles( const QgsTileMatrixSet &, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback, Qgis::RendererUsage ) const
183{
185
186 QgsDataSourceUri dsUri;
187 dsUri.setEncodedUri( dataSourceUri() );
188
189 QgsMbTiles mbReader( dsUri.param( QStringLiteral( "url" ) ) );
190 mbReader.open();
191
192 QList<QgsVectorTileRawData> rawTiles;
193 rawTiles.reserve( tiles.size() );
194 for ( QgsTileXYZ id : std::as_const( tiles ) )
195 {
196 if ( feedback && feedback->isCanceled() )
197 break;
198
199 const QByteArray rawData = loadFromMBTiles( mbReader, id, feedback );
200 if ( !rawData.isEmpty() )
201 {
202 rawTiles.append( QgsVectorTileRawData( id, rawData ) );
203 }
204 }
205 return rawTiles;
206}
207
208QByteArray QgsMbTilesVectorTileDataProvider::loadFromMBTiles( QgsMbTiles &mbTileReader, const QgsTileXYZ &id, QgsFeedback *feedback )
209{
210 // MBTiles uses TMS specs with Y starting at the bottom while XYZ uses Y starting at the top
211 const int rowTMS = static_cast<int>( pow( 2, id.zoomLevel() ) - id.row() - 1 );
212 QByteArray gzippedTileData = mbTileReader.tileData( id.zoomLevel(), id.column(), rowTMS );
213 if ( gzippedTileData.isEmpty() )
214 {
215 return QByteArray();
216 }
217
218 if ( feedback && feedback->isCanceled() )
219 return QByteArray();
220
221 QByteArray data;
222 if ( !QgsZipUtils::decodeGzip( gzippedTileData, data ) )
223 {
224 QgsDebugError( QStringLiteral( "Failed to decompress tile " ) + id.toString() );
225 return QByteArray();
226 }
227
228 QgsDebugMsgLevel( QStringLiteral( "Tile blob size %1 -> uncompressed size %2" ).arg( gzippedTileData.size() ).arg( data.size() ), 2 );
229 return data;
230}
231
232
233//
234// QgsMbTilesVectorTileDataProviderMetadata
235//
236
237QgsMbTilesVectorTileDataProviderMetadata::QgsMbTilesVectorTileDataProviderMetadata()
238 : QgsProviderMetadata( QgsMbTilesVectorTileDataProvider::MB_TILES_VECTOR_TILE_DATA_PROVIDER_KEY,
239 QgsMbTilesVectorTileDataProvider::MB_TILES_VECTOR_TILE_DATA_PROVIDER_DESCRIPTION )
240{
241}
242
243QgsProviderMetadata::ProviderMetadataCapabilities QgsMbTilesVectorTileDataProviderMetadata::capabilities() const
244{
245 return ProviderMetadataCapability::LayerTypesForUri
246 | ProviderMetadataCapability::PriorityForUri
247 | ProviderMetadataCapability::QuerySublayers;
248}
249
250QgsMbTilesVectorTileDataProvider *QgsMbTilesVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
251{
252 return new QgsMbTilesVectorTileDataProvider( uri, options, flags );
253}
254
255QIcon QgsMbTilesVectorTileDataProviderMetadata::icon() const
256{
257 return QgsApplication::getThemeIcon( QStringLiteral( "mIconVectorTileLayer.svg" ) );
258}
259
260QgsProviderMetadata::ProviderCapabilities QgsMbTilesVectorTileDataProviderMetadata::providerCapabilities() const
261{
262 return FileBasedUris;
263}
264
265QString QgsMbTilesVectorTileDataProviderMetadata::filters( Qgis::FileFilterType type )
266{
267 switch ( type )
268 {
275 return QString();
276
278 return QObject::tr( "Mbtiles Vector Tiles" ) + QStringLiteral( " (*.mbtiles *.MBTILES)" );
279 }
280 return QString();
281}
282
283QList<QgsProviderSublayerDetails> QgsMbTilesVectorTileDataProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags, QgsFeedback * ) const
284{
285 QString fileName;
286 const QFileInfo fi( uri );
287 if ( fi.isFile() )
288 {
289 fileName = uri;
290 }
291 else
292 {
293 const QVariantMap parts = decodeUri( uri );
294 fileName = parts.value( QStringLiteral( "path" ) ).toString();
295 }
296
297 if ( fileName.isEmpty() )
298 return {};
299
300 if ( QFileInfo( fileName ).suffix().compare( QLatin1String( "mbtiles" ), Qt::CaseInsensitive ) == 0 )
301 {
302 QVariantMap parts;
303 parts.insert( QStringLiteral( "path" ), fileName );
304
306 {
307 // fast scan -- assume vector tile are available
309 details.setUri( encodeUri( parts ) );
310 details.setProviderKey( key() );
312 details.setSkippedContainerScan( true );
314 return {details};
315 }
316 else
317 {
318 // slower scan, check actual mbtiles format
319 QgsMbTiles reader( fileName );
320 if ( reader.open() )
321 {
322 if ( reader.metadataValue( "format" ) == QLatin1String( "pbf" ) )
323 {
325 details.setUri( encodeUri( parts ) );
326 details.setProviderKey( key() );
329 return {details};
330 }
331 }
332 }
333 }
334 return {};
335}
336
337int QgsMbTilesVectorTileDataProviderMetadata::priorityForUri( const QString &uri ) const
338{
339 if ( validLayerTypesForUri( uri ).contains( Qgis::LayerType::VectorTile ) )
340 return 100;
341
342 return 0;
343}
344
345QList<Qgis::LayerType> QgsMbTilesVectorTileDataProviderMetadata::validLayerTypesForUri( const QString &uri ) const
346{
347 const QFileInfo fi( uri );
348 if ( fi.isFile() && fi.suffix().compare( QLatin1String( "mbtiles" ), Qt::CaseInsensitive ) == 0 )
349 {
351 }
352
353 const QVariantMap parts = decodeUri( uri );
354 if ( parts.value( QStringLiteral( "path" ) ).toString().endsWith( ".mbtiles", Qt::CaseSensitivity::CaseInsensitive ) )
356
357 return {};
358}
359
360QVariantMap QgsMbTilesVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
361{
362 QgsDataSourceUri dsUri;
363 dsUri.setEncodedUri( uri );
364
365 QVariantMap uriComponents;
366 uriComponents.insert( QStringLiteral( "type" ), QStringLiteral( "mbtiles" ) );
367 uriComponents.insert( QStringLiteral( "path" ), dsUri.param( QStringLiteral( "url" ) ) );
368
369 return uriComponents;
370}
371
372QString QgsMbTilesVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
373{
374 QgsDataSourceUri dsUri;
375 dsUri.setParam( QStringLiteral( "type" ), QStringLiteral( "mbtiles" ) );
376 dsUri.setParam( QStringLiteral( "url" ), parts.value( parts.contains( QStringLiteral( "path" ) ) ? QStringLiteral( "path" ) : QStringLiteral( "url" ) ).toString() );
377 return dsUri.encodedUri();
378}
379
380QString QgsMbTilesVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
381{
382 QVariantMap parts = decodeUri( uri );
383
384 const QString originalPath = parts.value( QStringLiteral( "path" ) ).toString();
385 parts.insert( QStringLiteral( "path" ), context.pathResolver().writePath( originalPath ) );
386
387 return encodeUri( parts );
388}
389
390QString QgsMbTilesVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
391{
392 QVariantMap parts = decodeUri( uri );
393
394 const QString originalPath = parts.value( QStringLiteral( "path" ) ).toString();
395 parts.insert( QStringLiteral( "path" ), context.pathResolver().readPath( originalPath ) );
396
397 return encodeUri( parts );
398}
399
400QList<Qgis::LayerType> QgsMbTilesVectorTileDataProviderMetadata::supportedLayerTypes() const
401{
403}
404
405
407
408
QFlags< DataProviderFlag > DataProviderFlags
Data provider flags.
Definition qgis.h:2230
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 ...
@ FastScan
Indicates that the provider must scan for sublayers using the fastest possible approach – e....
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:450
QFlags< SublayerQueryFlag > SublayerQueryFlags
Sublayer query flags.
Definition qgis.h:1329
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
RendererUsage
Usage of the renderer.
Definition qgis.h:3279
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 doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
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
Utility class for reading and writing MBTiles files (which are SQLite3 databases).
Definition qgsmbtiles.h:39
QByteArray tileData(int z, int x, int y) const
Returns raw tile data for given tile.
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.
void setSkippedContainerScan(bool skipped)
Set to true if the layer is a potential dataset container and an in-depth scan of its contents was sk...
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
Stores coordinates of a tile in a tile matrix set.
Definition qgstiles.h:40
Base class for vector tile layer data providers.
Encapsulates properties of a vector tile matrix set, including tile origins and scaling information.
static QgsVectorTileMatrixSet fromWebMercator(int minimumZoom=0, int maximumZoom=14)
Returns a vector tile structure corresponding to the standard web mercator/GoogleCRS84Quad setup.
Keeps track of raw tile data from one or more sources that need to be decoded.
CORE_EXPORT bool decodeGzip(const QByteArray &bytesIn, QByteArray &bytesOut)
Decodes gzip byte stream, returns true on success.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
#define QgsDebugError(str)
Definition qgslogger.h:40
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.