QGIS API Documentation 3.99.0-Master (357b655ed83)
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
18#include "qgsapplication.h"
20#include "qgslogger.h"
21#include "qgsmbtiles.h"
23#include "qgsproviderutils.h"
24#include "qgsthreadingutils.h"
25#include "qgstiles.h"
26#include "qgsvectortileloader.h"
27#include "qgsziputils.h"
28
29#include <QFileInfo>
30#include <QIcon>
31#include <QString>
32
33#include "moc_qgsmbtilesvectortiledataprovider.cpp"
34
35using namespace Qt::StringLiterals;
36
38
39QString QgsMbTilesVectorTileDataProvider::MB_TILES_VECTOR_TILE_DATA_PROVIDER_KEY = u"mbtilesvectortiles"_s;
40QString QgsMbTilesVectorTileDataProvider::MB_TILES_VECTOR_TILE_DATA_PROVIDER_DESCRIPTION = QObject::tr( "MBTile Vector Tiles data provider" );
41
42QgsMbTilesVectorTileDataProvider::QgsMbTilesVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, Qgis::DataProviderReadFlags flags )
43 : QgsVectorTileDataProvider( uri, providerOptions, flags )
44{
45 QgsDataSourceUri dsUri;
46 dsUri.setEncodedUri( uri );
47 const QString sourcePath = dsUri.param( u"url"_s );
48
49 QgsMbTiles reader( sourcePath );
50 if ( !reader.open() )
51 {
52 QgsDebugError( u"failed to open MBTiles file: "_s + sourcePath );
53 mIsValid = false;
54 return;
55 }
56
57 const QString format = reader.metadataValue( u"format"_s );
58 if ( format != "pbf"_L1 )
59 {
60 QgsDebugError( u"Cannot open MBTiles for vector tiles. Format = "_s + format );
61 mIsValid = false;
62 return;
63 }
64
65 QgsDebugMsgLevel( u"name: "_s + reader.metadataValue( u"name"_s ), 2 );
66
67 bool minZoomOk, maxZoomOk;
68 const int minZoom = reader.metadataValue( u"minzoom"_s ).toInt( &minZoomOk );
69 const int maxZoom = reader.metadataValue( u"maxzoom"_s ).toInt( &maxZoomOk );
70 if ( minZoomOk && maxZoomOk )
71 {
72 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( minZoom, maxZoom );
73 }
74 else if ( minZoomOk )
75 {
76 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( minZoom, 99 );
77 }
78 else if ( maxZoomOk )
79 {
80 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( 0, maxZoom );
81 }
82 else
83 {
85 }
86
87 QgsDebugMsgLevel( u"zoom range: %1 - %2"_s.arg( mMatrixSet.minimumZoom() ).arg( mMatrixSet.maximumZoom() ), 2 );
88
89 QgsRectangle r = reader.extent();
91 QgsCoordinateReferenceSystem( u"EPSG:3857"_s ), transformContext() );
92 ct.setBallparkTransformsAreAppropriate( true );
93 try
94 {
95 mExtent = ct.transformBoundingBox( r );
96 }
97 catch ( QgsCsException & )
98 {
99 QgsDebugError( u"Could not transform layer extent to layer CRS"_s );
100 }
101
102 mIsValid = true;
103}
104
105QgsMbTilesVectorTileDataProvider::QgsMbTilesVectorTileDataProvider( const QgsMbTilesVectorTileDataProvider &other )
107{
108 mIsValid = other.mIsValid;
109 mExtent = other.mExtent;
110 mMatrixSet = other.mMatrixSet;
111}
112
113Qgis::DataProviderFlags QgsMbTilesVectorTileDataProvider::flags() const
114{
116}
117
118QString QgsMbTilesVectorTileDataProvider::name() const
119{
121
122 return MB_TILES_VECTOR_TILE_DATA_PROVIDER_KEY;
123}
124
125QString QgsMbTilesVectorTileDataProvider::description() const
126{
128
129 return MB_TILES_VECTOR_TILE_DATA_PROVIDER_DESCRIPTION;
130}
131
132QgsVectorTileDataProvider *QgsMbTilesVectorTileDataProvider::clone() const
133{
135 return new QgsMbTilesVectorTileDataProvider( *this );
136}
137
138QString QgsMbTilesVectorTileDataProvider::sourcePath() const
139{
141
142 QgsDataSourceUri dsUri;
143 dsUri.setEncodedUri( dataSourceUri() );
144 return dsUri.param( u"url"_s );
145}
146
147bool QgsMbTilesVectorTileDataProvider::isValid() const
148{
150
151 return mIsValid;
152}
153
154QgsRectangle QgsMbTilesVectorTileDataProvider::extent() const
155{
157
158 return mExtent;
159}
160
161QgsCoordinateReferenceSystem QgsMbTilesVectorTileDataProvider::crs() const
162{
164
165 return QgsCoordinateReferenceSystem( u"EPSG:3857"_s );
166}
167
168const QgsVectorTileMatrixSet &QgsMbTilesVectorTileDataProvider::tileMatrixSet() const
169{
171
172 return mMatrixSet;
173}
174
175QgsVectorTileRawData QgsMbTilesVectorTileDataProvider::readTile( const QgsTileMatrixSet &, const QgsTileXYZ &id, QgsFeedback *feedback ) const
176{
178
179 QgsDataSourceUri dsUri;
180 dsUri.setEncodedUri( dataSourceUri() );
181
182 QgsMbTiles mbReader( dsUri.param( u"url"_s ) );
183 mbReader.open();
184 return QgsVectorTileRawData( id, loadFromMBTiles( mbReader, id, feedback ) );
185}
186
187QList<QgsVectorTileRawData> QgsMbTilesVectorTileDataProvider::readTiles( const QgsTileMatrixSet &, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback, Qgis::RendererUsage ) const
188{
190
191 QgsDataSourceUri dsUri;
192 dsUri.setEncodedUri( dataSourceUri() );
193
194 QgsMbTiles mbReader( dsUri.param( u"url"_s ) );
195 mbReader.open();
196
197 QList<QgsVectorTileRawData> rawTiles;
198 rawTiles.reserve( tiles.size() );
199 for ( QgsTileXYZ id : std::as_const( tiles ) )
200 {
201 if ( feedback && feedback->isCanceled() )
202 break;
203
204 const QByteArray rawData = loadFromMBTiles( mbReader, id, feedback );
205 if ( !rawData.isEmpty() )
206 {
207 rawTiles.append( QgsVectorTileRawData( id, rawData ) );
208 }
209 }
210 return rawTiles;
211}
212
213QByteArray QgsMbTilesVectorTileDataProvider::loadFromMBTiles( QgsMbTiles &mbTileReader, const QgsTileXYZ &id, QgsFeedback *feedback )
214{
215 // MBTiles uses TMS specs with Y starting at the bottom while XYZ uses Y starting at the top
216 const int rowTMS = static_cast<int>( pow( 2, id.zoomLevel() ) - id.row() - 1 );
217 QByteArray gzippedTileData = mbTileReader.tileData( id.zoomLevel(), id.column(), rowTMS );
218 if ( gzippedTileData.isEmpty() )
219 {
220 return QByteArray();
221 }
222
223 if ( feedback && feedback->isCanceled() )
224 return QByteArray();
225
226 QByteArray data;
227 if ( !QgsZipUtils::decodeGzip( gzippedTileData, data ) )
228 {
229 QgsDebugError( u"Failed to decompress tile "_s + id.toString() );
230 return QByteArray();
231 }
232
233 QgsDebugMsgLevel( u"Tile blob size %1 -> uncompressed size %2"_s.arg( gzippedTileData.size() ).arg( data.size() ), 2 );
234 return data;
235}
236
237
238//
239// QgsMbTilesVectorTileDataProviderMetadata
240//
241
242QgsMbTilesVectorTileDataProviderMetadata::QgsMbTilesVectorTileDataProviderMetadata()
243 : QgsProviderMetadata( QgsMbTilesVectorTileDataProvider::MB_TILES_VECTOR_TILE_DATA_PROVIDER_KEY,
244 QgsMbTilesVectorTileDataProvider::MB_TILES_VECTOR_TILE_DATA_PROVIDER_DESCRIPTION )
245{
246}
247
248QgsProviderMetadata::ProviderMetadataCapabilities QgsMbTilesVectorTileDataProviderMetadata::capabilities() const
249{
250 return ProviderMetadataCapability::LayerTypesForUri
251 | ProviderMetadataCapability::PriorityForUri
252 | ProviderMetadataCapability::QuerySublayers;
253}
254
255QgsMbTilesVectorTileDataProvider *QgsMbTilesVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
256{
257 return new QgsMbTilesVectorTileDataProvider( uri, options, flags );
258}
259
260QIcon QgsMbTilesVectorTileDataProviderMetadata::icon() const
261{
262 return QgsApplication::getThemeIcon( u"mIconVectorTileLayer.svg"_s );
263}
264
265QgsProviderMetadata::ProviderCapabilities QgsMbTilesVectorTileDataProviderMetadata::providerCapabilities() const
266{
267 return FileBasedUris;
268}
269
270QString QgsMbTilesVectorTileDataProviderMetadata::filters( Qgis::FileFilterType type )
271{
272 switch ( type )
273 {
280 return QString();
281
283 return QObject::tr( "Mbtiles Vector Tiles" ) + u" (*.mbtiles *.MBTILES)"_s;
284 }
285 return QString();
286}
287
288QList<QgsProviderSublayerDetails> QgsMbTilesVectorTileDataProviderMetadata::querySublayers( const QString &uri, Qgis::SublayerQueryFlags flags, QgsFeedback * ) const
289{
290 QString fileName;
291 const QFileInfo fi( uri );
292 if ( fi.isFile() )
293 {
294 fileName = uri;
295 }
296 else
297 {
298 const QVariantMap parts = decodeUri( uri );
299 fileName = parts.value( u"path"_s ).toString();
300 }
301
302 if ( fileName.isEmpty() )
303 return {};
304
305 if ( QFileInfo( fileName ).suffix().compare( "mbtiles"_L1, Qt::CaseInsensitive ) == 0 )
306 {
307 QVariantMap parts;
308 parts.insert( u"path"_s, fileName );
309
311 {
312 // fast scan -- assume vector tile are available
314 details.setUri( encodeUri( parts ) );
315 details.setProviderKey( key() );
317 details.setSkippedContainerScan( true );
319 return {details};
320 }
321 else
322 {
323 // slower scan, check actual mbtiles format
324 QgsMbTiles reader( fileName );
325 if ( reader.open() )
326 {
327 if ( reader.metadataValue( "format" ) == "pbf"_L1 )
328 {
330 details.setUri( encodeUri( parts ) );
331 details.setProviderKey( key() );
334 return {details};
335 }
336 }
337 }
338 }
339 return {};
340}
341
342int QgsMbTilesVectorTileDataProviderMetadata::priorityForUri( const QString &uri ) const
343{
344 if ( validLayerTypesForUri( uri ).contains( Qgis::LayerType::VectorTile ) )
345 return 100;
346
347 return 0;
348}
349
350QList<Qgis::LayerType> QgsMbTilesVectorTileDataProviderMetadata::validLayerTypesForUri( const QString &uri ) const
351{
352 const QFileInfo fi( uri );
353 if ( fi.isFile() && fi.suffix().compare( "mbtiles"_L1, Qt::CaseInsensitive ) == 0 )
354 {
356 }
357
358 const QVariantMap parts = decodeUri( uri );
359 if ( parts.value( u"path"_s ).toString().endsWith( ".mbtiles", Qt::CaseSensitivity::CaseInsensitive ) )
361
362 return {};
363}
364
365QVariantMap QgsMbTilesVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
366{
367 QgsDataSourceUri dsUri;
368 dsUri.setEncodedUri( uri );
369
370 QVariantMap uriComponents;
371 uriComponents.insert( u"type"_s, u"mbtiles"_s );
372 uriComponents.insert( u"path"_s, dsUri.param( u"url"_s ) );
373
374 return uriComponents;
375}
376
377QString QgsMbTilesVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
378{
379 QgsDataSourceUri dsUri;
380 dsUri.setParam( u"type"_s, u"mbtiles"_s );
381 dsUri.setParam( u"url"_s, parts.value( parts.contains( u"path"_s ) ? u"path"_s : u"url"_s ).toString() );
382 return dsUri.encodedUri();
383}
384
385QString QgsMbTilesVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
386{
387 QVariantMap parts = decodeUri( uri );
388
389 const QString originalPath = parts.value( u"path"_s ).toString();
390 parts.insert( u"path"_s, context.pathResolver().writePath( originalPath ) );
391
392 return encodeUri( parts );
393}
394
395QString QgsMbTilesVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
396{
397 QVariantMap parts = decodeUri( uri );
398
399 const QString originalPath = parts.value( u"path"_s ).toString();
400 parts.insert( u"path"_s, context.pathResolver().readPath( originalPath ) );
401
402 return encodeUri( parts );
403}
404
405QList<Qgis::LayerType> QgsMbTilesVectorTileDataProviderMetadata::supportedLayerTypes() const
406{
408}
409
410
412
413
QFlags< DataProviderFlag > DataProviderFlags
Data provider flags.
Definition qgis.h:2373
FileFilterType
Type of file filters.
Definition qgis.h:1412
@ TiledScene
Tiled scene layers.
Definition qgis.h:1419
@ Vector
Vector layers.
Definition qgis.h:1413
@ VectorTile
Vector tile layers.
Definition qgis.h:1418
@ Mesh
Mesh layers.
Definition qgis.h:1415
@ Raster
Raster layers.
Definition qgis.h:1414
@ MeshDataset
Mesh datasets.
Definition qgis.h:1416
@ PointCloud
Point clouds.
Definition qgis.h:1417
@ FastExtent2D
Provider's 2D extent retrieval via QgsDataProvider::extent() is always guaranteed to be trivial/fast ...
Definition qgis.h:2368
@ FastScan
Indicates that the provider must scan for sublayers using the fastest possible approach – e....
Definition qgis.h:1450
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:505
QFlags< SublayerQueryFlag > SublayerQueryFlags
Sublayer query flags.
Definition qgis.h:1457
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:198
RendererUsage
Usage of the renderer.
Definition qgis.h:3516
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).
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
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:55
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.
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:278
Stores coordinates of a tile in a tile matrix set.
Definition qgstiles.h:43
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.
static bool decodeGzip(const QByteArray &bytesIn, QByteArray &bytesOut)
Decodes gzip byte stream, returns true on success.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.