QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
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#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, Qgis::RendererUsage usage ) 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, usage );
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, Qgis::RendererUsage usage )
128{
129 QString url = QgsVectorTileUtils::formatXYZUrlTemplate( requestUrl, id, tileMatrix );
130
131 if ( url.contains( QLatin1String( "{usage}" ) ) )
132 {
133 switch ( usage )
134 {
136 url.replace( QLatin1String( "{usage}" ), QLatin1String( "view" ) );
137 break;
139 url.replace( QLatin1String( "{usage}" ), QLatin1String( "export" ) );
140 break;
142 url.replace( QLatin1String( "{usage}" ), QString() );
143 break;
144 }
145 }
146
147 QNetworkRequest nr;
148 nr.setUrl( QUrl( url ) );
149
150 headers.updateNetworkRequest( nr );
151
153 req.setAuthCfg( authid );
154 QgsDebugMsgLevel( QStringLiteral( "Blocking request: " ) + url, 2 );
155 QgsBlockingNetworkRequest::ErrorCode errCode = req.get( nr, false, feedback );
156 if ( errCode != QgsBlockingNetworkRequest::NoError )
157 {
158 QgsDebugError( QStringLiteral( "Request failed: " ) + url );
159 return QByteArray();
160 }
161 QgsNetworkReplyContent reply = req.reply();
162 QgsDebugMsgLevel( QStringLiteral( "Request successful, content size %1" ).arg( reply.content().size() ), 2 );
163 return reply.content();
164}
165
166
167//
168// QgsXyzVectorTileDataProviderMetadata
169//
170
171
172QgsXyzVectorTileDataProviderMetadata::QgsXyzVectorTileDataProviderMetadata()
173 : QgsProviderMetadata( QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_KEY, QgsXyzVectorTileDataProvider::XYZ_DATA_PROVIDER_DESCRIPTION )
174{
175}
176
177QgsXyzVectorTileDataProvider *QgsXyzVectorTileDataProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
178{
179 return new QgsXyzVectorTileDataProvider( uri, options, flags );
180}
181
182QIcon QgsXyzVectorTileDataProviderMetadata::icon() const
183{
184 return QgsApplication::getThemeIcon( QStringLiteral( "mIconVectorTileLayer.svg" ) );
185}
186
187QgsProviderMetadata::ProviderCapabilities QgsXyzVectorTileDataProviderMetadata::providerCapabilities() const
188{
189 return FileBasedUris;
190}
191
192QVariantMap QgsXyzVectorTileDataProviderMetadata::decodeUri( const QString &uri ) const
193{
194 QgsDataSourceUri dsUri;
195 dsUri.setEncodedUri( uri );
196
197 QVariantMap uriComponents;
198 uriComponents.insert( QStringLiteral( "type" ), QStringLiteral( "xyz" ) );
199
200 if ( uriComponents[ QStringLiteral( "type" ) ] == QLatin1String( "mbtiles" ) ||
201 ( uriComponents[ QStringLiteral( "type" ) ] == QLatin1String( "xyz" ) &&
202 !dsUri.param( QStringLiteral( "url" ) ).startsWith( QLatin1String( "http" ) ) ) )
203 {
204 uriComponents.insert( QStringLiteral( "path" ), dsUri.param( QStringLiteral( "url" ) ) );
205 }
206 else
207 {
208 uriComponents.insert( QStringLiteral( "url" ), dsUri.param( QStringLiteral( "url" ) ) );
209 }
210
211 if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
212 uriComponents.insert( QStringLiteral( "zmin" ), dsUri.param( QStringLiteral( "zmin" ) ) );
213 if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
214 uriComponents.insert( QStringLiteral( "zmax" ), dsUri.param( QStringLiteral( "zmax" ) ) );
215
216 dsUri.httpHeaders().updateMap( uriComponents );
217
218 if ( dsUri.hasParam( QStringLiteral( "styleUrl" ) ) )
219 uriComponents.insert( QStringLiteral( "styleUrl" ), dsUri.param( QStringLiteral( "styleUrl" ) ) );
220
221 const QString authcfg = dsUri.authConfigId();
222 if ( !authcfg.isEmpty() )
223 uriComponents.insert( QStringLiteral( "authcfg" ), authcfg );
224
225 return uriComponents;
226}
227
228QString QgsXyzVectorTileDataProviderMetadata::encodeUri( const QVariantMap &parts ) const
229{
230 QgsDataSourceUri dsUri;
231 dsUri.setParam( QStringLiteral( "type" ), QStringLiteral( "xyz" ) );
232 dsUri.setParam( QStringLiteral( "url" ), parts.value( parts.contains( QStringLiteral( "path" ) ) ? QStringLiteral( "path" ) : QStringLiteral( "url" ) ).toString() );
233
234 if ( parts.contains( QStringLiteral( "zmin" ) ) )
235 dsUri.setParam( QStringLiteral( "zmin" ), parts[ QStringLiteral( "zmin" ) ].toString() );
236 if ( parts.contains( QStringLiteral( "zmax" ) ) )
237 dsUri.setParam( QStringLiteral( "zmax" ), parts[ QStringLiteral( "zmax" ) ].toString() );
238
239 dsUri.httpHeaders().setFromMap( parts );
240
241 if ( parts.contains( QStringLiteral( "styleUrl" ) ) )
242 dsUri.setParam( QStringLiteral( "styleUrl" ), parts[ QStringLiteral( "styleUrl" ) ].toString() );
243
244 if ( parts.contains( QStringLiteral( "authcfg" ) ) )
245 dsUri.setAuthConfigId( parts[ QStringLiteral( "authcfg" ) ].toString() );
246
247 return dsUri.encodedUri();
248}
249
250QString QgsXyzVectorTileDataProviderMetadata::absoluteToRelativeUri( const QString &uri, const QgsReadWriteContext &context ) const
251{
252 QgsDataSourceUri dsUri;
253 dsUri.setEncodedUri( uri );
254
255 QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
256
257 const QUrl sourceUrl( sourcePath );
258 if ( sourceUrl.isLocalFile() )
259 {
260 // relative path will become "file:./x.txt"
261 const QString relSrcUrl = context.pathResolver().writePath( sourceUrl.toLocalFile() );
262 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
263 dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
264 return dsUri.encodedUri();
265 }
266
267 return uri;
268}
269
270QString QgsXyzVectorTileDataProviderMetadata::relativeToAbsoluteUri( const QString &uri, const QgsReadWriteContext &context ) const
271{
272 QgsDataSourceUri dsUri;
273 dsUri.setEncodedUri( uri );
274
275 QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
276
277 const QUrl sourceUrl( sourcePath );
278 if ( sourceUrl.isLocalFile() ) // file-based URL? convert to relative path
279 {
280 const QString absSrcUrl = context.pathResolver().readPath( sourceUrl.toLocalFile() );
281 dsUri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
282 dsUri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
283 return dsUri.encodedUri();
284 }
285
286 return uri;
287}
288
289QList<Qgis::LayerType> QgsXyzVectorTileDataProviderMetadata::supportedLayerTypes() const
290{
292}
293
294
295//
296// QgsXyzVectorTileDataProvider
297//
298
299QgsXyzVectorTileDataProvider::QgsXyzVectorTileDataProvider( const QString &uri, const ProviderOptions &providerOptions, ReadFlags flags )
300 : QgsXyzVectorTileDataProviderBase( uri, providerOptions, flags )
301{
302 QgsDataSourceUri dsUri;
303 dsUri.setEncodedUri( uri );
304
305 const QString sourcePath = dsUri.param( QStringLiteral( "url" ) );
306 if ( !QgsVectorTileUtils::checkXYZUrlTemplate( sourcePath ) )
307 {
308 QgsDebugError( QStringLiteral( "Invalid format of URL for XYZ source: " ) + sourcePath );
309 mIsValid = false;
310 return;
311 }
312
313 int zMin = 0;
314 if ( dsUri.hasParam( QStringLiteral( "zmin" ) ) )
315 zMin = dsUri.param( QStringLiteral( "zmin" ) ).toInt();
316
317 int zMax = 14;
318 if ( dsUri.hasParam( QStringLiteral( "zmax" ) ) )
319 zMax = dsUri.param( QStringLiteral( "zmax" ) ).toInt();
320
321 mMatrixSet = QgsVectorTileMatrixSet::fromWebMercator( zMin, zMax );
322 mExtent = QgsRectangle( -20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892 );
323
324 mIsValid = true;
325}
326
327QgsXyzVectorTileDataProvider::QgsXyzVectorTileDataProvider( const QgsXyzVectorTileDataProvider &other )
328 : QgsXyzVectorTileDataProviderBase( other )
329{
330 mIsValid = other.mIsValid;
331 mExtent = other.mExtent;
332 mMatrixSet = other.mMatrixSet;
333}
334
335QString QgsXyzVectorTileDataProvider::name() const
336{
338
339 return XYZ_DATA_PROVIDER_KEY;
340}
341
342QString QgsXyzVectorTileDataProvider::description() const
343{
345
346 return XYZ_DATA_PROVIDER_DESCRIPTION;
347}
348
349QgsVectorTileDataProvider *QgsXyzVectorTileDataProvider::clone() const
350{
352
353 return new QgsXyzVectorTileDataProvider( *this );
354}
355
356bool QgsXyzVectorTileDataProvider::isValid() const
357{
359
360 return mIsValid;
361}
362
363QgsRectangle QgsXyzVectorTileDataProvider::extent() const
364{
366
367 return mExtent;
368}
369
370QgsCoordinateReferenceSystem QgsXyzVectorTileDataProvider::crs() const
371{
373
374 return QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3857" ) );
375}
376
377const QgsVectorTileMatrixSet &QgsXyzVectorTileDataProvider::tileMatrixSet() const
378{
380
381 return mMatrixSet;
382}
383
384QString QgsXyzVectorTileDataProvider::sourcePath() const
385{
387
388 QgsDataSourceUri dsUri;
389 dsUri.setEncodedUri( dataSourceUri() );
390 return dsUri.param( QStringLiteral( "url" ) );
391}
392
394
395
396
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
RendererUsage
Usage of the renderer.
Definition qgis.h:2548
@ 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
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.
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
Setting options for creating vector data providers.