QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsxyzvectortiledataprovider.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsxyzvectortiledataprovider.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 "qgsthreadingutils.h"
18#include "qgstiles.h"
19#include "qgsvectortileloader.h"
20#include "qgsvectortileutils.h"
23#include "qgsapplication.h"
24#include "qgsauthmanager.h"
25#include "qgsmessagelog.h"
27#include "qgslogger.h"
28#include <QIcon>
29#include <QNetworkRequest>
30
32
33QString QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_KEY = QStringLiteral( "xyzvectortiles" );
34QString QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_DESCRIPTION = QObject::tr( "XYZ Vector Tiles data provider" );
35
36//
37// QgsXyzVectorTileDataProviderBase
38//
39
40QgsXyzVectorTileDataProviderBase::QgsXyzVectorTileDataProviderBase( const QString &uri, const ProviderOptions &providerOptions, ReadFlags flags )
41 : QgsVectorTileDataProvider( uri, providerOptions, flags )
42{
43 QgsDataSourceUri dsUri;
44 dsUri.setEncodedUri( uri );
45 mAuthCfg = dsUri.authConfigId();
46 mHeaders = dsUri.httpHeaders();
47}
48
49QgsXyzVectorTileDataProviderBase::QgsXyzVectorTileDataProviderBase( const QgsXyzVectorTileDataProviderBase &other )
51{
52 mAuthCfg = other.mAuthCfg;
53 mHeaders = other.mHeaders;
54}
55
56bool QgsXyzVectorTileDataProviderBase::supportsAsync() const
57{
58 return true;
59}
60
61QgsVectorTileRawData QgsXyzVectorTileDataProviderBase::readTile( const QgsTileMatrixSet &set, const QgsTileXYZ &id, QgsFeedback *feedback ) const
62{
63 return QgsVectorTileRawData( id, loadFromNetwork( id, set.tileMatrix( id.zoomLevel() ), sourcePath(), mAuthCfg, mHeaders, feedback ) );
64}
65
66QList<QgsVectorTileRawData> QgsXyzVectorTileDataProviderBase::readTiles( const QgsTileMatrixSet &set, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback, Qgis::RendererUsage usage ) const
67{
68 QList<QgsVectorTileRawData> rawTiles;
69 rawTiles.reserve( tiles.size() );
70 const QString source = sourcePath();
71 for ( QgsTileXYZ id : std::as_const( tiles ) )
72 {
73 if ( feedback && feedback->isCanceled() )
74 break;
75
76 const QByteArray rawData = loadFromNetwork( id, set.tileMatrix( id.zoomLevel() ), source, mAuthCfg, mHeaders, feedback, usage );
77 if ( !rawData.isEmpty() )
78 {
79 rawTiles.append( QgsVectorTileRawData( id, rawData ) );
80 }
81 }
82 return rawTiles;
83}
84
85QNetworkRequest QgsXyzVectorTileDataProviderBase::tileRequest( const QgsTileMatrixSet &set, const QgsTileXYZ &id, Qgis::RendererUsage usage ) const
86{
87 QString urlTemplate = sourcePath();
88
89 if ( urlTemplate.contains( QLatin1String( "{usage}" ) ) )
90 {
91 switch ( usage )
92 {
94 urlTemplate.replace( QLatin1String( "{usage}" ), QLatin1String( "view" ) );
95 break;
97 urlTemplate.replace( QLatin1String( "{usage}" ), QLatin1String( "export" ) );
98 break;
100 urlTemplate.replace( QLatin1String( "{usage}" ), QString() );
101 break;
102 }
103 }
104
105 const QString url = QgsVectorTileUtils::formatXYZUrlTemplate( urlTemplate, id, set.tileMatrix( id.zoomLevel() ) );
106
107 QNetworkRequest request( url );
108 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsXyzVectorTileDataProvider" ) );
109 QgsSetRequestInitiatorId( request, id.toString() );
110
111 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 1 ), id.column() );
112 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 2 ), id.row() );
113 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 3 ), id.zoomLevel() );
114
115 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
116 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
117
118 mHeaders.updateNetworkRequest( request );
119
120 if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg ) )
121 {
122 QgsMessageLog::logMessage( tr( "network request update failed for authentication config" ), tr( "Network" ) );
123 }
124
125 return request;
126}
127
128QByteArray QgsXyzVectorTileDataProviderBase::loadFromNetwork( const QgsTileXYZ &id, const QgsTileMatrix &tileMatrix, const QString &requestUrl, const QString &authid, const QgsHttpHeaders &headers, QgsFeedback *feedback, Qgis::RendererUsage usage )
129{
130 QString url = QgsVectorTileUtils::formatXYZUrlTemplate( requestUrl, id, tileMatrix );
131
132 if ( url.contains( QLatin1String( "{usage}" ) ) )
133 {
134 switch ( usage )
135 {
137 url.replace( QLatin1String( "{usage}" ), QLatin1String( "view" ) );
138 break;
140 url.replace( QLatin1String( "{usage}" ), QLatin1String( "export" ) );
141 break;
143 url.replace( QLatin1String( "{usage}" ), QString() );
144 break;
145 }
146 }
147
148 QNetworkRequest nr;
149 nr.setUrl( QUrl( url ) );
150
151 headers.updateNetworkRequest( nr );
152
154 req.setAuthCfg( authid );
155 QgsDebugMsgLevel( QStringLiteral( "Blocking request: " ) + url, 2 );
156 QgsBlockingNetworkRequest::ErrorCode errCode = req.get( nr, false, feedback );
157 if ( errCode != QgsBlockingNetworkRequest::NoError )
158 {
159 QgsDebugError( QStringLiteral( "Request failed: " ) + url );
160 return QByteArray();
161 }
162 QgsNetworkReplyContent reply = req.reply();
163 QgsDebugMsgLevel( QStringLiteral( "Request successful, content size %1" ).arg( reply.content().size() ), 2 );
164 return reply.content();
165}
166
167
168//
169// QgsXyzVectorTileDataProviderMetadata
170//
171
172
173QgsXyzVectorTileDataProviderMetadata::QgsXyzVectorTileDataProviderMetadata()
174 : QgsProviderMetadata( QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_KEY, QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_DESCRIPTION )
175{
176}
177
178QgsXyzVectorTileDataProvider *QgsXyzVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
179{
180 return new QgsXyzVectorTileDataProvider( uri, options, flags );
181}
182
183QIcon QgsXyzVectorTileDataProviderMetadata::icon() const
184{
185 return QgsApplication::getThemeIcon( QStringLiteral( "mIconVectorTileLayer.svg" ) );
186}
187
188QgsProviderMetadata::ProviderCapabilities QgsXyzVectorTileDataProviderMetadata::providerCapabilities() const
189{
190 return FileBasedUris;
191}
192
193QVariantMap QgsXyzVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
194{
195 QgsDataSourceUri dsUri;
196 dsUri.setEncodedUri( uri );
197
198 QVariantMap uriComponents;
199 uriComponents.insert( QStringLiteral( "type" ), QStringLiteral( "xyz" ) );
200
201 if ( uriComponents[ QStringLiteral( "type" ) ] == QLatin1String( "mbtiles" ) ||
202 ( uriComponents[ QStringLiteral( "type" ) ] == QLatin1String( "xyz" ) &&
203 !dsUri.param( QStringLiteral( "url" ) ).startsWith( QLatin1String( "http" ) ) ) )
204 {
205 uriComponents.insert( QStringLiteral( "path" ), dsUri.param( QStringLiteral( "url" ) ) );
206 }
207 else
208 {
209 uriComponents.insert( QStringLiteral( "url" ), dsUri.param( QStringLiteral( "url" ) ) );
210 }
211
212 if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
213 uriComponents.insert( QStringLiteral( "zmin" ), dsUri.param( QStringLiteral( "zmin" ) ) );
214 if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
215 uriComponents.insert( QStringLiteral( "zmax" ), dsUri.param( QStringLiteral( "zmax" ) ) );
216
217 dsUri.httpHeaders().updateMap( uriComponents );
218
219 if ( dsUri.hasParam( QStringLiteral( "styleUrl" ) ) )
220 uriComponents.insert( QStringLiteral( "styleUrl" ), dsUri.param( QStringLiteral( "styleUrl" ) ) );
221
222 const QString authcfg = dsUri.authConfigId();
223 if ( !authcfg.isEmpty() )
224 uriComponents.insert( QStringLiteral( "authcfg" ), authcfg );
225
226 return uriComponents;
227}
228
229QString QgsXyzVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
230{
231 QgsDataSourceUri dsUri;
232 dsUri.setParam( QStringLiteral( "type" ), QStringLiteral( "xyz" ) );
233 dsUri.setParam( QStringLiteral( "url" ), parts.value( parts.contains( QStringLiteral( "path" ) ) ? QStringLiteral( "path" ) : QStringLiteral( "url" ) ).toString() );
234
235 if ( parts.contains( QStringLiteral( "zmin" ) ) )
236 dsUri.setParam( QStringLiteral( "zmin" ), parts[ QStringLiteral( "zmin" ) ].toString() );
237 if ( parts.contains( QStringLiteral( "zmax" ) ) )
238 dsUri.setParam( QStringLiteral( "zmax" ), parts[ QStringLiteral( "zmax" ) ].toString() );
239
240 dsUri.httpHeaders().setFromMap( parts );
241
242 if ( parts.contains( QStringLiteral( "styleUrl" ) ) )
243 dsUri.setParam( QStringLiteral( "styleUrl" ), parts[ QStringLiteral( "styleUrl" ) ].toString() );
244
245 if ( parts.contains( QStringLiteral( "authcfg" ) ) )
246 dsUri.setAuthConfigId( parts[ QStringLiteral( "authcfg" ) ].toString() );
247
248 return dsUri.encodedUri();
249}
250
251QString QgsXyzVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
252{
253 QgsDataSourceUri dsUri;
254 dsUri.setEncodedUri( uri );
255
256 QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
257
258 const QUrl sourceUrl( sourcePath );
259 if ( sourceUrl.isLocalFile() )
260 {
261 // relative path will become "file:./x.txt"
262 const QString relSrcUrl = context.pathResolver().writePath( sourceUrl.toLocalFile() );
263 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
264 dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
265 return dsUri.encodedUri();
266 }
267
268 return uri;
269}
270
271QString QgsXyzVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
272{
273 QgsDataSourceUri dsUri;
274 dsUri.setEncodedUri( uri );
275
276 QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
277
278 const QUrl sourceUrl( sourcePath );
279 if ( sourceUrl.isLocalFile() ) // file-based URL? convert to relative path
280 {
281 const QString absSrcUrl = context.pathResolver().readPath( sourceUrl.toLocalFile() );
282 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
283 dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
284 return dsUri.encodedUri();
285 }
286
287 return uri;
288}
289
290QList<Qgis::LayerType> QgsXyzVectorTileDataProviderMetadata::supportedLayerTypes() const
291{
293}
294
295
296//
297// QgsXyzVectorTileDataProvider
298//
299
300QgsXyzVectorTileDataProvider::QgsXyzVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, ReadFlags flags )
301 : QgsXyzVectorTileDataProviderBase( uri, providerOptions, flags )
302{
303 QgsDataSourceUri dsUri;
304 dsUri.setEncodedUri( uri );
305
306 const QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
307 if ( !QgsVectorTileUtils::checkXYZUrlTemplate( sourcePath ) )
308 {
309 QgsDebugError( QStringLiteral( "Invalid format of URL for XYZ source: " ) + sourcePath );
310 mIsValid = false;
311 return;
312 }
313
314 int zMin = 0;
315 if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
316 zMin = dsUri.param( QStringLiteral( "zmin" ) ).toInt();
317
318 int zMax = 14;
319 if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
320 zMax = dsUri.param( QStringLiteral( "zmax" ) ).toInt();
321
322 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( zMin, zMax );
323 mExtent = QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 );
324
325 mIsValid = true;
326}
327
328QgsXyzVectorTileDataProvider::QgsXyzVectorTileDataProvider( const QgsXyzVectorTileDataProvider &other )
329 : QgsXyzVectorTileDataProviderBase( other )
330{
331 mIsValid = other.mIsValid;
332 mExtent = other.mExtent;
333 mMatrixSet = other.mMatrixSet;
334}
335
336Qgis::DataProviderFlags QgsXyzVectorTileDataProvider::flags() const
337{
339}
340
341QString QgsXyzVectorTileDataProvider::name() const
342{
344
345 return XYZ_DATA_PROVIDER_KEY;
346}
347
348QString QgsXyzVectorTileDataProvider::description() const
349{
351
352 return XYZ_DATA_PROVIDER_DESCRIPTION;
353}
354
355QgsVectorTileDataProvider *QgsXyzVectorTileDataProvider::clone() const
356{
358
359 return new QgsXyzVectorTileDataProvider( *this );
360}
361
362bool QgsXyzVectorTileDataProvider::isValid() const
363{
365
366 return mIsValid;
367}
368
369QgsRectangle QgsXyzVectorTileDataProvider::extent() const
370{
372
373 return mExtent;
374}
375
377{
379
380 return QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) );
381}
382
383const QgsVectorTileMatrixSet &QgsXyzVectorTileDataProvider::tileMatrixSet() const
384{
386
387 return mMatrixSet;
388}
389
390QString QgsXyzVectorTileDataProvider::sourcePath() const
391{
393
394 QgsDataSourceUri dsUri;
395 dsUri.setEncodedUri( dataSourceUri() );
396 return dsUri.param( QStringLiteral( "url" ) );
397}
398
400
401
402
QFlags< DataProviderFlag > DataProviderFlags
Data provider flags.
Definition: qgis.h:1853
@ FastExtent2D
Provider's 2D extent retrieval via QgsDataProvider::extent() is always guaranteed to be trivial/fast ...
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
RendererUsage
Usage of the renderer.
Definition: qgis.h:2803
@ Export
Renderer used for printing or exporting to a file.
@ View
Renderer used for displaying on screen.
@ Unknown
Renderer used for unknown usage.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkRequest with an authentication config.
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
Performs a "get" operation on the specified request.
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
@ NoError
No error was encountered.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get(), post(), head() or put() request has been mad...
This class represents a coordinate reference system (CRS).
QFlags< ReadFlag > ReadFlags
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.
bool hasParam(const QString &key) const
Returns true if a parameter with the specified key exists.
int removeParam(const QString &key)
Removes a generic parameter by key.
void setEncodedUri(const QByteArray &uri)
Sets the complete encoded uri.
void setAuthConfigId(const QString &authcfg)
Sets the authentication configuration ID for the URI.
QgsHttpHeaders httpHeaders() const
Returns http headers.
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.
QString authConfigId() const
Returns any associated authentication configuration ID stored in 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
This class implements simple http header management.
bool updateMap(QVariantMap &map) const
Updates a map by adding all the HTTP headers.
bool updateNetworkRequest(QNetworkRequest &request) const
Updates a request by adding all the HTTP headers.
void setFromMap(const QVariantMap &map)
Loads headers from the map.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QByteArray content() const
Returns the reply content.
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< ProviderCapability > ProviderCapabilities
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.
Definition: qgsrectangle.h:42
Defines a set of tile matrices for multiple zoom levels.
Definition: qgstiles.h:250
QgsTileMatrix tileMatrix(int zoom) const
Returns the tile matrix corresponding to the specified zoom.
Definition: qgstiles.cpp:156
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition: qgstiles.h:134
Stores coordinates of a tile in a tile matrix set.
Definition: qgstiles.h:38
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 that need to be decoded.
static bool checkXYZUrlTemplate(const QString &url)
Checks whether the URL template string is correct (contains {x}, {y} / {-y}, {z} placeholders)
static QString formatXYZUrlTemplate(const QString &url, QgsTileXYZ tile, const QgsTileMatrix &tileMatrix)
Returns formatted tile URL string replacing {x}, {y}, {z} placeholders (or {-y} instead of {y} for TM...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38
#define QgsSetRequestInitiatorClass(request, _class)
#define QgsSetRequestInitiatorId(request, str)
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
const QgsCoordinateReferenceSystem & crs
Setting options for creating vector data providers.