QGIS API Documentation 3.31.0-Master (9f23a2c1dc)
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"
22#include "qgsapplication.h"
23#include "qgsauthmanager.h"
24#include "qgsmessagelog.h"
26#include "qgslogger.h"
27#include <QIcon>
28#include <QNetworkRequest>
29
31
32QString QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_KEY = QStringLiteral( "xyzvectortiles" );
33QString QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_DESCRIPTION = QObject::tr( "XYZ Vector Tiles data provider" );
34
35//
36// QgsXyzVectorTileDataProviderBase
37//
38
39QgsXyzVectorTileDataProviderBase::QgsXyzVectorTileDataProviderBase( const QString &uri, const ProviderOptions &providerOptions, ReadFlags flags )
40 : QgsVectorTileDataProvider( uri, providerOptions, flags )
41{
42 QgsDataSourceUri dsUri;
43 dsUri.setEncodedUri( uri );
44 mAuthCfg = dsUri.authConfigId();
45 mHeaders = dsUri.httpHeaders();
46}
47
48QgsXyzVectorTileDataProviderBase::QgsXyzVectorTileDataProviderBase( const QgsXyzVectorTileDataProviderBase &other )
50{
51 mAuthCfg = other.mAuthCfg;
52 mHeaders = other.mHeaders;
53}
54
55bool QgsXyzVectorTileDataProviderBase::supportsAsync() const
56{
57 return true;
58}
59
60QgsVectorTileRawData QgsXyzVectorTileDataProviderBase::readTile( const QgsTileMatrixSet &set, const QgsTileXYZ &id, QgsFeedback *feedback ) const
61{
62 return QgsVectorTileRawData( id, loadFromNetwork( id, set.tileMatrix( id.zoomLevel() ), sourcePath(), mAuthCfg, mHeaders, feedback ) );
63}
64
65QList<QgsVectorTileRawData> QgsXyzVectorTileDataProviderBase::readTiles( const QgsTileMatrixSet &set, const QVector<QgsTileXYZ> &tiles, QgsFeedback *feedback ) const
66{
67 QList<QgsVectorTileRawData> rawTiles;
68 rawTiles.reserve( tiles.size() );
69 const QString source = sourcePath();
70 for ( QgsTileXYZ id : std::as_const( tiles ) )
71 {
72 if ( feedback && feedback->isCanceled() )
73 break;
74
75 const QByteArray rawData = loadFromNetwork( id, set.tileMatrix( id.zoomLevel() ), source, mAuthCfg, mHeaders, feedback );
76 if ( !rawData.isEmpty() )
77 {
78 rawTiles.append( QgsVectorTileRawData( id, rawData ) );
79 }
80 }
81 return rawTiles;
82}
83
84QNetworkRequest QgsXyzVectorTileDataProviderBase::tileRequest( const QgsTileMatrixSet &set, const QgsTileXYZ &id, Qgis::RendererUsage usage ) const
85{
86 QString urlTemplate = sourcePath();
87
88 if ( urlTemplate.contains( QLatin1String( "{usage}" ) ) )
89 {
90 switch ( usage )
91 {
93 urlTemplate.replace( QLatin1String( "{usage}" ), QLatin1String( "view" ) );
94 break;
96 urlTemplate.replace( QLatin1String( "{usage}" ), QLatin1String( "export" ) );
97 break;
99 urlTemplate.replace( QLatin1String( "{usage}" ), QString() );
100 break;
101 }
102 }
103
104 const QString url = QgsVectorTileUtils::formatXYZUrlTemplate( urlTemplate, id, set.tileMatrix( id.zoomLevel() ) );
105
106 QNetworkRequest request( url );
107 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsXyzVectorTileDataProvider" ) );
108 QgsSetRequestInitiatorId( request, id.toString() );
109
110 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 1 ), id.column() );
111 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 2 ), id.row() );
112 request.setAttribute( static_cast<QNetworkRequest::Attribute>( QNetworkRequest::User + 3 ), id.zoomLevel() );
113
114 request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
115 request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true );
116
117 mHeaders.updateNetworkRequest( request );
118
119 if ( !mAuthCfg.isEmpty() && !QgsApplication::authManager()->updateNetworkRequest( request, mAuthCfg ) )
120 {
121 QgsMessageLog::logMessage( tr( "network request update failed for authentication config" ), tr( "Network" ) );
122 }
123
124 return request;
125}
126
127QByteArray QgsXyzVectorTileDataProviderBase::loadFromNetwork( const QgsTileXYZ &id, const QgsTileMatrix &tileMatrix, const QString &requestUrl, const QString &authid, const QgsHttpHeaders &headers, QgsFeedback *feedback )
128{
129 QString url = QgsVectorTileUtils::formatXYZUrlTemplate( requestUrl, id, tileMatrix );
130 QNetworkRequest nr;
131 nr.setUrl( QUrl( url ) );
132
133 headers.updateNetworkRequest( nr );
134
136 req.setAuthCfg( authid );
137 QgsDebugMsgLevel( QStringLiteral( "Blocking request: " ) + url, 2 );
138 QgsBlockingNetworkRequest::ErrorCode errCode = req.get( nr, false, feedback );
139 if ( errCode != QgsBlockingNetworkRequest::NoError )
140 {
141 QgsDebugError( QStringLiteral( "Request failed: " ) + url );
142 return QByteArray();
143 }
144 QgsNetworkReplyContent reply = req.reply();
145 QgsDebugMsgLevel( QStringLiteral( "Request successful, content size %1" ).arg( reply.content().size() ), 2 );
146 return reply.content();
147}
148
149
150//
151// QgsXyzVectorTileDataProviderMetadata
152//
153
154
155QgsXyzVectorTileDataProviderMetadata::QgsXyzVectorTileDataProviderMetadata()
156 : QgsProviderMetadata( QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_KEY, QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_DESCRIPTION )
157{
158}
159
160QgsXyzVectorTileDataProvider *QgsXyzVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
161{
162 return new QgsXyzVectorTileDataProvider( uri, options, flags );
163}
164
165QIcon QgsXyzVectorTileDataProviderMetadata::icon() const
166{
167 return QgsApplication::getThemeIcon( QStringLiteral( "mIconVectorTileLayer.svg" ) );
168}
169
170QgsProviderMetadata::ProviderCapabilities QgsXyzVectorTileDataProviderMetadata::providerCapabilities() const
171{
172 return FileBasedUris;
173}
174
175QVariantMap QgsXyzVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
176{
177 QgsDataSourceUri dsUri;
178 dsUri.setEncodedUri( uri );
179
180 QVariantMap uriComponents;
181 uriComponents.insert( QStringLiteral( "type" ), QStringLiteral( "xyz" ) );
182
183 if ( uriComponents[ QStringLiteral( "type" ) ] == QLatin1String( "mbtiles" ) ||
184 ( uriComponents[ QStringLiteral( "type" ) ] == QLatin1String( "xyz" ) &&
185 !dsUri.param( QStringLiteral( "url" ) ).startsWith( QLatin1String( "http" ) ) ) )
186 {
187 uriComponents.insert( QStringLiteral( "path" ), dsUri.param( QStringLiteral( "url" ) ) );
188 }
189 else
190 {
191 uriComponents.insert( QStringLiteral( "url" ), dsUri.param( QStringLiteral( "url" ) ) );
192 }
193
194 if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
195 uriComponents.insert( QStringLiteral( "zmin" ), dsUri.param( QStringLiteral( "zmin" ) ) );
196 if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
197 uriComponents.insert( QStringLiteral( "zmax" ), dsUri.param( QStringLiteral( "zmax" ) ) );
198
199 dsUri.httpHeaders().updateMap( uriComponents );
200
201 if ( dsUri.hasParam( QStringLiteral( "styleUrl" ) ) )
202 uriComponents.insert( QStringLiteral( "styleUrl" ), dsUri.param( QStringLiteral( "styleUrl" ) ) );
203
204 const QString authcfg = dsUri.authConfigId();
205 if ( !authcfg.isEmpty() )
206 uriComponents.insert( QStringLiteral( "authcfg" ), authcfg );
207
208 return uriComponents;
209}
210
211QString QgsXyzVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
212{
213 QgsDataSourceUri dsUri;
214 dsUri.setParam( QStringLiteral( "type" ), QStringLiteral( "xyz" ) );
215 dsUri.setParam( QStringLiteral( "url" ), parts.value( parts.contains( QStringLiteral( "path" ) ) ? QStringLiteral( "path" ) : QStringLiteral( "url" ) ).toString() );
216
217 if ( parts.contains( QStringLiteral( "zmin" ) ) )
218 dsUri.setParam( QStringLiteral( "zmin" ), parts[ QStringLiteral( "zmin" ) ].toString() );
219 if ( parts.contains( QStringLiteral( "zmax" ) ) )
220 dsUri.setParam( QStringLiteral( "zmax" ), parts[ QStringLiteral( "zmax" ) ].toString() );
221
222 dsUri.httpHeaders().setFromMap( parts );
223
224 if ( parts.contains( QStringLiteral( "styleUrl" ) ) )
225 dsUri.setParam( QStringLiteral( "styleUrl" ), parts[ QStringLiteral( "styleUrl" ) ].toString() );
226
227 if ( parts.contains( QStringLiteral( "authcfg" ) ) )
228 dsUri.setAuthConfigId( parts[ QStringLiteral( "authcfg" ) ].toString() );
229
230 return dsUri.encodedUri();
231}
232
233QString QgsXyzVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
234{
235 QgsDataSourceUri dsUri;
236 dsUri.setEncodedUri( uri );
237
238 QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
239
240 const QUrl sourceUrl( sourcePath );
241 if ( sourceUrl.isLocalFile() )
242 {
243 // relative path will become "file:./x.txt"
244 const QString relSrcUrl = context.pathResolver().writePath( sourceUrl.toLocalFile() );
245 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
246 dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
247 return dsUri.encodedUri();
248 }
249
250 return uri;
251}
252
253QString QgsXyzVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
254{
255 QgsDataSourceUri dsUri;
256 dsUri.setEncodedUri( uri );
257
258 QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
259
260 const QUrl sourceUrl( sourcePath );
261 if ( sourceUrl.isLocalFile() ) // file-based URL? convert to relative path
262 {
263 const QString absSrcUrl = context.pathResolver().readPath( sourceUrl.toLocalFile() );
264 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
265 dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
266 return dsUri.encodedUri();
267 }
268
269 return uri;
270}
271
272QList<Qgis::LayerType> QgsXyzVectorTileDataProviderMetadata::supportedLayerTypes() const
273{
274 return { Qgis::LayerType::VectorTile };
275}
276
277
278//
279// QgsXyzVectorTileDataProvider
280//
281
282QgsXyzVectorTileDataProvider::QgsXyzVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, ReadFlags flags )
283 : QgsXyzVectorTileDataProviderBase( uri, providerOptions, flags )
284{
285 QgsDataSourceUri dsUri;
286 dsUri.setEncodedUri( uri );
287
288 const QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
289 if ( !QgsVectorTileUtils::checkXYZUrlTemplate( sourcePath ) )
290 {
291 QgsDebugError( QStringLiteral( "Invalid format of URL for XYZ source: " ) + sourcePath );
292 mIsValid = false;
293 return;
294 }
295
296 int zMin = 0;
297 if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
298 zMin = dsUri.param( QStringLiteral( "zmin" ) ).toInt();
299
300 int zMax = 14;
301 if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
302 zMax = dsUri.param( QStringLiteral( "zmax" ) ).toInt();
303
304 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( zMin, zMax );
305 mExtent = QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 );
306
307 mIsValid = true;
308}
309
310QgsXyzVectorTileDataProvider::QgsXyzVectorTileDataProvider( const QgsXyzVectorTileDataProvider &other )
311 : QgsXyzVectorTileDataProviderBase( other )
312{
313 mIsValid = other.mIsValid;
314 mExtent = other.mExtent;
315 mMatrixSet = other.mMatrixSet;
316}
317
318QString QgsXyzVectorTileDataProvider::name() const
319{
321
322 return XYZ_DATA_PROVIDER_KEY;
323}
324
325QString QgsXyzVectorTileDataProvider::description() const
326{
328
329 return XYZ_DATA_PROVIDER_DESCRIPTION;
330}
331
332QgsVectorTileDataProvider *QgsXyzVectorTileDataProvider::clone() const
333{
335
336 return new QgsXyzVectorTileDataProvider( *this );
337}
338
339bool QgsXyzVectorTileDataProvider::isValid() const
340{
342
343 return mIsValid;
344}
345
346QgsRectangle QgsXyzVectorTileDataProvider::extent() const
347{
349
350 return mExtent;
351}
352
354{
356
357 return QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) );
358}
359
360const QgsVectorTileMatrixSet &QgsXyzVectorTileDataProvider::tileMatrixSet() const
361{
363
364 return mMatrixSet;
365}
366
367QString QgsXyzVectorTileDataProvider::sourcePath() const
368{
370
371 QgsDataSourceUri dsUri;
372 dsUri.setEncodedUri( dataSourceUri() );
373 return dsUri.param( QStringLiteral( "url" ) );
374}
375
377
378
379
RendererUsage
Usage of the renderer.
Definition: qgis.h:2320
@ 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).
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:45
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
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...
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.