QGIS API Documentation 3.99.0-Master (357b655ed83)
Loading...
Searching...
No Matches
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
18#include "qgsapplication.h"
19#include "qgsauthmanager.h"
21#include "qgslogger.h"
22#include "qgsmessagelog.h"
25#include "qgsthreadingutils.h"
26#include "qgstiles.h"
28#include "qgsvectortileloader.h"
29#include "qgsvectortileutils.h"
30
31#include <QIcon>
32#include <QNetworkRequest>
33#include <QString>
34
35#include "moc_qgsxyzvectortiledataprovider.cpp"
36
37using namespace Qt::StringLiterals;
38
40
41QString QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_KEY = u"xyzvectortiles"_s;
42QString QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_DESCRIPTION = QObject::tr( "XYZ Vector Tiles data provider" );
43
44//
45// QgsXyzVectorTileDataProviderBase
46//
47
48QgsXyzVectorTileDataProviderBase::QgsXyzVectorTileDataProviderBase( const QString &uri, const ProviderOptions &providerOptions, Qgis::DataProviderReadFlags flags )
49 : QgsVectorTileDataProvider( uri, providerOptions, flags )
50{
51 QgsDataSourceUri dsUri;
52 dsUri.setEncodedUri( uri );
53 mAuthCfg = dsUri.authConfigId();
54 mHeaders = dsUri.httpHeaders();
55}
56
57QgsXyzVectorTileDataProviderBase::QgsXyzVectorTileDataProviderBase( const QgsXyzVectorTileDataProviderBase &other )
59{
60 mAuthCfg = other.mAuthCfg;
61 mHeaders = other.mHeaders;
62}
63
64bool QgsXyzVectorTileDataProviderBase::supportsAsync() const
65{
66 return true;
67}
68
69QgsVectorTileRawData QgsXyzVectorTileDataProviderBase::readTile( const QgsTileMatrixSet &set, const QgsTileXYZ &id, QgsFeedback *feedback ) const
70{
71 return QgsVectorTileRawData( id, loadFromNetwork( id, set.tileMatrix( id.zoomLevel() ), sourcePath(), mAuthCfg, mHeaders, feedback ) );
72}
73
74QList<QgsVectorTileRawData> QgsXyzVectorTileDataProviderBase::readTiles( const QgsTileMatrixSet &set, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback, Qgis::RendererUsage usage ) const
75{
76 QList<QgsVectorTileRawData> rawTiles;
77 rawTiles.reserve( tiles.size() );
78 for ( QgsTileXYZ id : std::as_const( tiles ) )
79 {
80 QMap<QString, QByteArray> data;
81 const QgsStringMap sources = sourcePaths();
82 QgsStringMap::const_iterator it = sources.constBegin();
83 for ( ; it != sources.constEnd(); ++it )
84 {
85 if ( feedback && feedback->isCanceled() )
86 break;
87
88 const QByteArray rawData = loadFromNetwork( id, set.tileMatrix( id.zoomLevel() ), it.value(), mAuthCfg, mHeaders, feedback, usage );
89 if ( !rawData.isEmpty() )
90 {
91 data[it.key()] = rawData;
92 }
93 }
94 rawTiles.append( QgsVectorTileRawData( id, data ) );
95 }
96 return rawTiles;
97}
98
99QList<QNetworkRequest> QgsXyzVectorTileDataProviderBase::tileRequests( const QgsTileMatrixSet &set, const QgsTileXYZ &id, Qgis::RendererUsage usage ) const
100{
101 QList<QNetworkRequest> requests;
102
103 const QgsStringMap sourcesPaths = sourcePaths();
104
105 QgsStringMap::const_iterator it = sourcesPaths.constBegin();
106
107 for ( ; it != sourcesPaths.constEnd(); ++it )
108 {
109 QString urlTemplate = it.value();
110 QString layerName = it.key();
111
112 if ( urlTemplate.contains( "{usage}"_L1 ) )
113 {
114 switch ( usage )
115 {
117 urlTemplate.replace( "{usage}"_L1, "view"_L1 );
118 break;
120 urlTemplate.replace( "{usage}"_L1, "export"_L1 );
121 break;
123 urlTemplate.replace( "{usage}"_L1, QString() );
124 break;
125 }
126 }
127
128 const QString url = QgsVectorTileUtils::formatXYZUrlTemplate( urlTemplate, id, set.tileMatrix( id.zoomLevel() ) );
129
130 QNetworkRequest request( url );
131 QgsSetRequestInitiatorClass( request, u"QgsXyzVectorTileDataProvider"_s );
132 QgsSetRequestInitiatorId( request, id.toString() );
133
134 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_COLUMN ), id.column() );
135 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_ROW ), id.row() );
136 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_ZOOM ), id.zoomLevel() );
137 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QgsVectorTileDataProvider::DATA_SOURCE_ID ), layerName );
138
139 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
140 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
141
142 mHeaders.updateNetworkRequest( request );
143
144 if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg ) )
145 {
146 QgsMessageLog::logMessage( tr( "network request update failed for authentication config" ), tr( "Network" ) );
147 }
148
149 requests << request;
150 }
151
152 return requests;
153}
154
155QByteArray QgsXyzVectorTileDataProviderBase::loadFromNetwork( const QgsTileXYZ &id, const QgsTileMatrix &tileMatrix, const QString &requestUrl, const QString &authid, const QgsHttpHeaders &headers, QgsFeedback *feedback, Qgis::RendererUsage usage )
156{
157 QString url = QgsVectorTileUtils::formatXYZUrlTemplate( requestUrl, id, tileMatrix );
158
159 if ( url.contains( "{usage}"_L1 ) )
160 {
161 switch ( usage )
162 {
164 url.replace( "{usage}"_L1, "view"_L1 );
165 break;
167 url.replace( "{usage}"_L1, "export"_L1 );
168 break;
170 url.replace( "{usage}"_L1, QString() );
171 break;
172 }
173 }
174
175 QNetworkRequest nr;
176 nr.setUrl( QUrl( url ) );
177
178 headers.updateNetworkRequest( nr );
179
181 req.setAuthCfg( authid );
182 QgsDebugMsgLevel( u"Blocking request: "_s + url, 2 );
183 QgsBlockingNetworkRequest::ErrorCode errCode = req.get( nr, false, feedback );
184 if ( errCode != QgsBlockingNetworkRequest::NoError )
185 {
186 QgsDebugError( u"Request failed: "_s + url );
187 return QByteArray();
188 }
189 QgsNetworkReplyContent reply = req.reply();
190 QgsDebugMsgLevel( u"Request successful, content size %1"_s.arg( reply.content().size() ), 2 );
191 return reply.content();
192}
193
194
195//
196// QgsXyzVectorTileDataProviderMetadata
197//
198
199
200QgsXyzVectorTileDataProviderMetadata::QgsXyzVectorTileDataProviderMetadata()
201 : QgsProviderMetadata( QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_KEY, QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_DESCRIPTION )
202{
203}
204
205QgsXyzVectorTileDataProvider *QgsXyzVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
206{
207 return new QgsXyzVectorTileDataProvider( uri, options, flags );
208}
209
210QIcon QgsXyzVectorTileDataProviderMetadata::icon() const
211{
212 return QgsApplication::getThemeIcon( u"mIconVectorTileLayer.svg"_s );
213}
214
215QgsProviderMetadata::ProviderCapabilities QgsXyzVectorTileDataProviderMetadata::providerCapabilities() const
216{
217 return FileBasedUris;
218}
219
220QVariantMap QgsXyzVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
221{
222 QgsDataSourceUri dsUri;
223 dsUri.setEncodedUri( uri );
224
225 QVariantMap uriComponents;
226 uriComponents.insert( u"type"_s, u"xyz"_s );
227
228 if ( uriComponents[ u"type"_s ] == "mbtiles"_L1 ||
229 ( uriComponents[ u"type"_s ] == "xyz"_L1 &&
230 !dsUri.param( u"url"_s ).startsWith( "http"_L1 ) ) )
231 {
232 uriComponents.insert( u"path"_s, dsUri.param( u"url"_s ) );
233 }
234 else
235 {
236 uriComponents.insert( u"url"_s, dsUri.param( u"url"_s ) );
237 if ( dsUri.hasParam( u"urlName"_s ) )
238 uriComponents.insert( u"urlName"_s, dsUri.param( u"urlName"_s ) );
239 }
240 int i = 2;
241 while ( true )
242 {
243 QString url = dsUri.param( u"url_%2"_s.arg( i ) );
244 QString urlName = dsUri.param( u"urlName_%2"_s.arg( i ) );
245 if ( url.isEmpty() || urlName.isEmpty() )
246 break;
247 uriComponents.insert( u"urlName_%2"_s.arg( i ), urlName );
248 uriComponents.insert( u"url_%2"_s.arg( i ), url );
249 i++;
250 }
251
252
253 if ( dsUri.hasParam( u"zmin"_s ) )
254 uriComponents.insert( u"zmin"_s, dsUri.param( u"zmin"_s ) );
255 if ( dsUri.hasParam( u"zmax"_s ) )
256 uriComponents.insert( u"zmax"_s, dsUri.param( u"zmax"_s ) );
257
258 dsUri.httpHeaders().updateMap( uriComponents );
259
260 if ( dsUri.hasParam( u"styleUrl"_s ) )
261 uriComponents.insert( u"styleUrl"_s, dsUri.param( u"styleUrl"_s ) );
262
263 const QString authcfg = dsUri.authConfigId();
264 if ( !authcfg.isEmpty() )
265 uriComponents.insert( u"authcfg"_s, authcfg );
266
267 return uriComponents;
268}
269
270QString QgsXyzVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
271{
272 QgsDataSourceUri dsUri;
273 dsUri.setParam( u"type"_s, u"xyz"_s );
274 dsUri.setParam( u"url"_s, parts.value( parts.contains( u"path"_s ) ? u"path"_s : u"url"_s ).toString() );
275 if ( parts.contains( u"urlName"_s ) )
276 dsUri.setParam( u"urlName"_s, parts[ u"urlName"_s ].toString() );
277
278 int i = 2;
279 while ( true )
280 {
281 QString urlNameKey = u"urlName_%2"_s.arg( i );
282 QString urlKey = u"url_%2"_s.arg( i );
283
284 if ( !parts.contains( urlNameKey ) || !parts.contains( urlKey ) )
285 break;
286 QString url = dsUri.param( u"url_%2"_s.arg( i ) );
287 QString urlName = dsUri.param( u"urlName_%2"_s.arg( i ) );
288 if ( url.isEmpty() || urlName.isEmpty() )
289 break;
290
291 dsUri.setParam( urlNameKey, parts[ urlNameKey ].toString() );
292 dsUri.setParam( urlKey, parts[ urlKey ].toString() );
293 i++;
294 }
295
296 if ( parts.contains( u"zmin"_s ) )
297 dsUri.setParam( u"zmin"_s, parts[ u"zmin"_s ].toString() );
298 if ( parts.contains( u"zmax"_s ) )
299 dsUri.setParam( u"zmax"_s, parts[ u"zmax"_s ].toString() );
300
301 dsUri.httpHeaders().setFromMap( parts );
302
303 if ( parts.contains( u"styleUrl"_s ) )
304 dsUri.setParam( u"styleUrl"_s, parts[ u"styleUrl"_s ].toString() );
305
306 if ( parts.contains( u"authcfg"_s ) )
307 dsUri.setAuthConfigId( parts[ u"authcfg"_s ].toString() );
308
309 return dsUri.encodedUri();
310}
311
312QString QgsXyzVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
313{
314 QgsDataSourceUri dsUri;
315 dsUri.setEncodedUri( uri );
316
317 QString sourcePath = dsUri.param( u"url"_s );
318
319 const QUrl sourceUrl( sourcePath );
320 if ( sourceUrl.isLocalFile() )
321 {
322 // relative path will become "file:./x.txt"
323 const QString relSrcUrl = context.pathResolver().writePath( sourceUrl.toLocalFile() );
324 dsUri.removeParam( u"url"_s ); // needed because setParam() would insert second "url" key
325 dsUri.setParam( u"url"_s, QUrl::fromLocalFile( relSrcUrl ).toString( QUrl::DecodeReserved ) );
326 return dsUri.encodedUri();
327 }
328
329 return uri;
330}
331
332QString QgsXyzVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
333{
334 QgsDataSourceUri dsUri;
335 dsUri.setEncodedUri( uri );
336
337 QString sourcePath = dsUri.param( u"url"_s );
338
339 const QUrl sourceUrl( sourcePath );
340 if ( sourceUrl.isLocalFile() ) // file-based URL? convert to relative path
341 {
342 const QString absSrcUrl = context.pathResolver().readPath( sourceUrl.toLocalFile() );
343 dsUri.removeParam( u"url"_s ); // needed because setParam() would insert second "url" key
344 dsUri.setParam( u"url"_s, QUrl::fromLocalFile( absSrcUrl ).toString( QUrl::DecodeReserved ) );
345 return dsUri.encodedUri();
346 }
347
348 return uri;
349}
350
351QList<Qgis::LayerType> QgsXyzVectorTileDataProviderMetadata::supportedLayerTypes() const
352{
354}
355
356
357//
358// QgsXyzVectorTileDataProvider
359//
360
361QgsXyzVectorTileDataProvider::QgsXyzVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, Qgis::DataProviderReadFlags flags )
362 : QgsXyzVectorTileDataProviderBase( uri, providerOptions, flags )
363{
364 QgsDataSourceUri dsUri;
365 dsUri.setEncodedUri( uri );
366
367 const QString sourcePath = dsUri.param( u"url"_s );
368 if ( !QgsVectorTileUtils::checkXYZUrlTemplate( sourcePath ) )
369 {
370 QgsDebugError( u"Invalid format of URL for XYZ source: "_s + sourcePath );
371 mIsValid = false;
372 return;
373 }
374
375 int zMin = 0;
376 if ( dsUri.hasParam( u"zmin"_s ) )
377 zMin = dsUri.param( u"zmin"_s ).toInt();
378
379 int zMax = 14;
380 if ( dsUri.hasParam( u"zmax"_s ) )
381 zMax = dsUri.param( u"zmax"_s ).toInt();
382
383 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( zMin, zMax );
384 mExtent = QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 );
385
386 mIsValid = true;
387}
388
389QgsXyzVectorTileDataProvider::QgsXyzVectorTileDataProvider( const QgsXyzVectorTileDataProvider &other )
390 : QgsXyzVectorTileDataProviderBase( other )
391{
392 mIsValid = other.mIsValid;
393 mExtent = other.mExtent;
394 mMatrixSet = other.mMatrixSet;
395}
396
397Qgis::DataProviderFlags QgsXyzVectorTileDataProvider::flags() const
398{
400}
401
402QString QgsXyzVectorTileDataProvider::name() const
403{
405
406 return XYZ_DATA_PROVIDER_KEY;
407}
408
409QString QgsXyzVectorTileDataProvider::description() const
410{
412
413 return XYZ_DATA_PROVIDER_DESCRIPTION;
414}
415
416QgsVectorTileDataProvider *QgsXyzVectorTileDataProvider::clone() const
417{
419
420 return new QgsXyzVectorTileDataProvider( *this );
421}
422
423bool QgsXyzVectorTileDataProvider::isValid() const
424{
426
427 return mIsValid;
428}
429
430QgsRectangle QgsXyzVectorTileDataProvider::extent() const
431{
433
434 return mExtent;
435}
436
437QgsCoordinateReferenceSystem QgsXyzVectorTileDataProvider::crs() const
438{
440
441 return QgsCoordinateReferenceSystem( u"EPSG:3857"_s );
442}
443
444const QgsVectorTileMatrixSet &QgsXyzVectorTileDataProvider::tileMatrixSet() const
445{
447
448 return mMatrixSet;
449}
450
451QString QgsXyzVectorTileDataProvider::sourcePath() const
452{
454
455 QgsDataSourceUri dsUri;
456 dsUri.setEncodedUri( dataSourceUri() );
457 return dsUri.param( u"url"_s );
458}
459
460QgsStringMap QgsXyzVectorTileDataProvider::sourcePaths() const
461{
463
464 QgsDataSourceUri dsUri;
465 dsUri.setEncodedUri( dataSourceUri() );
466
467 QgsStringMap paths = { { dsUri.param( u"urlName"_s ), dsUri.param( u"url"_s ) } };
468
469 int i = 2;
470 while ( true )
471 {
472 QString url = dsUri.param( u"url_%2"_s.arg( i ) );
473 QString urlName = dsUri.param( u"urlName_%2"_s.arg( i ) );
474 if ( url.isEmpty() || urlName.isEmpty() )
475 break;
476
477 paths.insert( urlName, url );
478 i++;
479 }
480
481 return paths;
482}
483
485
486
487
QFlags< DataProviderFlag > DataProviderFlags
Data provider flags.
Definition qgis.h:2373
@ FastExtent2D
Provider's 2D extent retrieval via QgsDataProvider::extent() is always guaranteed to be trivial/fast ...
Definition qgis.h:2368
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:505
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:198
RendererUsage
Usage of the renderer.
Definition qgis.h:3516
@ Export
Renderer used for printing or exporting to a file.
Definition qgis.h:3518
@ View
Renderer used for displaying on screen.
Definition qgis.h:3517
@ Unknown
Renderer used for unknown usage.
Definition qgis.h:3519
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.
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr, RequestFlags requestFlags=QgsBlockingNetworkRequest::RequestFlags())
Performs a "get" operation on the specified 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...
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.
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:55
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
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
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
QgsTileMatrix tileMatrix(int zoom) const
Returns the tile matrix corresponding to the specified zoom.
Definition qgstiles.cpp:160
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition qgstiles.h:162
Stores coordinates of a tile in a tile matrix set.
Definition qgstiles.h:43
Base class for vector tile layer data providers.
static int DATA_ZOOM
Role to set zoom attribute in the request so it can be retrieved later.
static int DATA_ROW
Role to set row attribute in the request so it can be retrieved later.
static int DATA_SOURCE_ID
Role to set source ID attribute in the request so it can be retrieved later.
static int DATA_COLUMN
Role to set column attribute in the request so it can be retrieved later.
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 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}...
QMap< QString, QString > QgsStringMap
Definition qgis.h:7448
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
#define QgsSetRequestInitiatorClass(request, _class)
#define QgsSetRequestInitiatorId(request, str)
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.