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