QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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(
156 const QgsTileXYZ &id, const QgsTileMatrix &tileMatrix, const QString &requestUrl, const QString &authid, const QgsHttpHeaders &headers, QgsFeedback *feedback, Qgis::RendererUsage usage
157)
158{
159 QString url = QgsVectorTileUtils::formatXYZUrlTemplate( requestUrl, id, tileMatrix );
160
161 if ( url.contains( "{usage}"_L1 ) )
162 {
163 switch ( usage )
164 {
166 url.replace( "{usage}"_L1, "view"_L1 );
167 break;
169 url.replace( "{usage}"_L1, "export"_L1 );
170 break;
172 url.replace( "{usage}"_L1, QString() );
173 break;
174 }
175 }
176
177 QNetworkRequest nr;
178 nr.setUrl( QUrl( url ) );
179
180 headers.updateNetworkRequest( nr );
181
183 req.setAuthCfg( authid );
184 QgsDebugMsgLevel( u"Blocking request: "_s + url, 2 );
185 QgsBlockingNetworkRequest::ErrorCode errCode = req.get( nr, false, feedback );
186 if ( errCode != QgsBlockingNetworkRequest::NoError )
187 {
188 QgsDebugError( u"Request failed: "_s + url );
189 return QByteArray();
190 }
191 QgsNetworkReplyContent reply = req.reply();
192 QgsDebugMsgLevel( u"Request successful, content size %1"_s.arg( reply.content().size() ), 2 );
193 return reply.content();
194}
195
196
197//
198// QgsXyzVectorTileDataProviderMetadata
199//
200
201
202QgsXyzVectorTileDataProviderMetadata::QgsXyzVectorTileDataProviderMetadata()
203 : QgsProviderMetadata( QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_KEY, QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_DESCRIPTION )
204{}
205
206QgsXyzVectorTileDataProvider *QgsXyzVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
207{
208 return new QgsXyzVectorTileDataProvider( uri, options, flags );
209}
210
211QIcon QgsXyzVectorTileDataProviderMetadata::icon() const
212{
213 return QgsApplication::getThemeIcon( u"mIconVectorTileLayer.svg"_s );
214}
215
216QgsProviderMetadata::ProviderCapabilities QgsXyzVectorTileDataProviderMetadata::providerCapabilities() const
217{
218 return FileBasedUris;
219}
220
221QVariantMap QgsXyzVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
222{
223 QgsDataSourceUri dsUri;
224 dsUri.setEncodedUri( uri );
225
226 QVariantMap uriComponents;
227 uriComponents.insert( u"type"_s, u"xyz"_s );
228
229 if ( uriComponents[u"type"_s] == "mbtiles"_L1 || ( uriComponents[u"type"_s] == "xyz"_L1 && !dsUri.param( u"url"_s ).startsWith( "http"_L1 ) ) )
230 {
231 uriComponents.insert( u"path"_s, dsUri.param( u"url"_s ) );
232 }
233 else
234 {
235 uriComponents.insert( u"url"_s, dsUri.param( u"url"_s ) );
236 if ( dsUri.hasParam( u"urlName"_s ) )
237 uriComponents.insert( u"urlName"_s, dsUri.param( u"urlName"_s ) );
238 }
239 int i = 2;
240 while ( true )
241 {
242 QString url = dsUri.param( u"url_%2"_s.arg( i ) );
243 QString urlName = dsUri.param( u"urlName_%2"_s.arg( i ) );
244 if ( url.isEmpty() || urlName.isEmpty() )
245 break;
246 uriComponents.insert( u"urlName_%2"_s.arg( i ), urlName );
247 uriComponents.insert( u"url_%2"_s.arg( i ), url );
248 i++;
249 }
250
251
252 if ( dsUri.hasParam( u"zmin"_s ) )
253 uriComponents.insert( u"zmin"_s, dsUri.param( u"zmin"_s ) );
254 if ( dsUri.hasParam( u"zmax"_s ) )
255 uriComponents.insert( u"zmax"_s, dsUri.param( u"zmax"_s ) );
256
257 dsUri.httpHeaders().updateMap( uriComponents );
258
259 if ( dsUri.hasParam( u"styleUrl"_s ) )
260 uriComponents.insert( u"styleUrl"_s, dsUri.param( u"styleUrl"_s ) );
261
262 const QString authcfg = dsUri.authConfigId();
263 if ( !authcfg.isEmpty() )
264 uriComponents.insert( u"authcfg"_s, authcfg );
265
266 return uriComponents;
267}
268
269QString QgsXyzVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
270{
271 QgsDataSourceUri dsUri;
272 dsUri.setParam( u"type"_s, u"xyz"_s );
273 dsUri.setParam( u"url"_s, parts.value( parts.contains( u"path"_s ) ? u"path"_s : u"url"_s ).toString() );
274 if ( parts.contains( u"urlName"_s ) )
275 dsUri.setParam( u"urlName"_s, parts[u"urlName"_s].toString() );
276
277 int i = 2;
278 while ( true )
279 {
280 QString urlNameKey = u"urlName_%2"_s.arg( i );
281 QString urlKey = u"url_%2"_s.arg( i );
282
283 if ( !parts.contains( urlNameKey ) || !parts.contains( urlKey ) )
284 break;
285 QString url = dsUri.param( u"url_%2"_s.arg( i ) );
286 QString urlName = dsUri.param( u"urlName_%2"_s.arg( i ) );
287 if ( url.isEmpty() || urlName.isEmpty() )
288 break;
289
290 dsUri.setParam( urlNameKey, parts[urlNameKey].toString() );
291 dsUri.setParam( urlKey, parts[urlKey].toString() );
292 i++;
293 }
294
295 if ( parts.contains( u"zmin"_s ) )
296 dsUri.setParam( u"zmin"_s, parts[u"zmin"_s].toString() );
297 if ( parts.contains( u"zmax"_s ) )
298 dsUri.setParam( u"zmax"_s, parts[u"zmax"_s].toString() );
299
300 dsUri.httpHeaders().setFromMap( parts );
301
302 if ( parts.contains( u"styleUrl"_s ) )
303 dsUri.setParam( u"styleUrl"_s, parts[u"styleUrl"_s].toString() );
304
305 if ( parts.contains( u"authcfg"_s ) )
306 dsUri.setAuthConfigId( parts[u"authcfg"_s].toString() );
307
308 return dsUri.encodedUri();
309}
310
311QString QgsXyzVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
312{
313 QgsDataSourceUri dsUri;
314 dsUri.setEncodedUri( uri );
315
316 QString sourcePath = dsUri.param( u"url"_s );
317
318 const QUrl sourceUrl( sourcePath );
319 if ( sourceUrl.isLocalFile() )
320 {
321 // relative path will become "file:./x.txt"
322 const QString relSrcUrl = context.pathResolver().writePath( sourceUrl.toLocalFile() );
323 dsUri.removeParam( u"url"_s ); // needed because setParam() would insert second "url" key
324 dsUri.setParam( u"url"_s, QUrl::fromLocalFile( relSrcUrl ).toString( QUrl::DecodeReserved ) );
325 return dsUri.encodedUri();
326 }
327
328 return uri;
329}
330
331QString QgsXyzVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
332{
333 QgsDataSourceUri dsUri;
334 dsUri.setEncodedUri( uri );
335
336 QString sourcePath = dsUri.param( u"url"_s );
337
338 const QUrl sourceUrl( sourcePath );
339 if ( sourceUrl.isLocalFile() ) // file-based URL? convert to relative path
340 {
341 const QString absSrcUrl = context.pathResolver().readPath( sourceUrl.toLocalFile() );
342 dsUri.removeParam( u"url"_s ); // needed because setParam() would insert second "url" key
343 dsUri.setParam( u"url"_s, QUrl::fromLocalFile( absSrcUrl ).toString( QUrl::DecodeReserved ) );
344 return dsUri.encodedUri();
345 }
346
347 return uri;
348}
349
350QList<Qgis::LayerType> QgsXyzVectorTileDataProviderMetadata::supportedLayerTypes() const
351{
353}
354
355
356//
357// QgsXyzVectorTileDataProvider
358//
359
360QgsXyzVectorTileDataProvider::QgsXyzVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, Qgis::DataProviderReadFlags flags )
361 : QgsXyzVectorTileDataProviderBase( uri, providerOptions, flags )
362{
363 QgsDataSourceUri dsUri;
364 dsUri.setEncodedUri( uri );
365
366 const QString sourcePath = dsUri.param( u"url"_s );
367 if ( !QgsVectorTileUtils::checkXYZUrlTemplate( sourcePath ) )
368 {
369 QgsDebugError( u"Invalid format of URL for XYZ source: "_s + sourcePath );
370 mIsValid = false;
371 return;
372 }
373
374 int zMin = 0;
375 if ( dsUri.hasParam( u"zmin"_s ) )
376 zMin = dsUri.param( u"zmin"_s ).toInt();
377
378 int zMax = 14;
379 if ( dsUri.hasParam( u"zmax"_s ) )
380 zMax = dsUri.param( u"zmax"_s ).toInt();
381
382 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( zMin, zMax );
383 mExtent = QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 );
384
385 mIsValid = true;
386}
387
388QgsXyzVectorTileDataProvider::QgsXyzVectorTileDataProvider( const QgsXyzVectorTileDataProvider &other )
389 : QgsXyzVectorTileDataProviderBase( other )
390{
391 mIsValid = other.mIsValid;
392 mExtent = other.mExtent;
393 mMatrixSet = other.mMatrixSet;
394}
395
396Qgis::DataProviderFlags QgsXyzVectorTileDataProvider::flags() const
397{
399}
400
401QString QgsXyzVectorTileDataProvider::name() const
402{
404
405 return XYZ_DATA_PROVIDER_KEY;
406}
407
408QString QgsXyzVectorTileDataProvider::description() const
409{
411
412 return XYZ_DATA_PROVIDER_DESCRIPTION;
413}
414
415QgsVectorTileDataProvider *QgsXyzVectorTileDataProvider::clone() const
416{
418
419 return new QgsXyzVectorTileDataProvider( *this );
420}
421
422bool QgsXyzVectorTileDataProvider::isValid() const
423{
425
426 return mIsValid;
427}
428
429QgsRectangle QgsXyzVectorTileDataProvider::extent() const
430{
432
433 return mExtent;
434}
435
436QgsCoordinateReferenceSystem QgsXyzVectorTileDataProvider::crs() const
437{
439
440 return QgsCoordinateReferenceSystem( u"EPSG:3857"_s );
441}
442
443const QgsVectorTileMatrixSet &QgsXyzVectorTileDataProvider::tileMatrixSet() const
444{
446
447 return mMatrixSet;
448}
449
450QString QgsXyzVectorTileDataProvider::sourcePath() const
451{
453
454 QgsDataSourceUri dsUri;
455 dsUri.setEncodedUri( dataSourceUri() );
456 return dsUri.param( u"url"_s );
457}
458
459QgsStringMap QgsXyzVectorTileDataProvider::sourcePaths() const
460{
462
463 QgsDataSourceUri dsUri;
464 dsUri.setEncodedUri( dataSourceUri() );
465
466 QgsStringMap paths = { { dsUri.param( u"urlName"_s ), dsUri.param( u"url"_s ) } };
467
468 int i = 2;
469 while ( true )
470 {
471 QString url = dsUri.param( u"url_%2"_s.arg( i ) );
472 QString urlName = dsUri.param( u"urlName_%2"_s.arg( i ) );
473 if ( url.isEmpty() || urlName.isEmpty() )
474 break;
475
476 paths.insert( urlName, url );
477 i++;
478 }
479
480 return paths;
481}
482
QFlags< DataProviderFlag > DataProviderFlags
Data provider flags.
Definition qgis.h:2397
@ FastExtent2D
Provider's 2D extent retrieval via QgsDataProvider::extent() is always guaranteed to be trivial/fast ...
Definition qgis.h:2392
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:512
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:211
RendererUsage
Usage of the renderer.
Definition qgis.h:3559
@ Export
Renderer used for printing or exporting to a file.
Definition qgis.h:3561
@ View
Renderer used for displaying on screen.
Definition qgis.h:3560
@ Unknown
Renderer used for unknown usage.
Definition qgis.h:3562
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:56
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(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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:284
QgsTileMatrix tileMatrix(int zoom) const
Returns the tile matrix corresponding to the specified zoom.
Definition qgstiles.cpp:162
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition qgstiles.h:171
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:7475
#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.