38#include <QJsonDocument>
43using namespace Qt::StringLiterals;
47 QgsVectorTileProviderConnection::Data data = QgsVectorTileProviderConnection::decodedUri( uri );
48 if ( forceUpdate || ( data.url.isEmpty() && !data.styleUrl.isEmpty() ) )
50 const QMap<QString, QString> sources = QgsVectorTileUtils::parseStyleSourceUrl( data.styleUrl, data.httpHeaders, data.authCfg );
51 QMap<QString, QString>::const_iterator it = sources.constBegin();
53 for ( ; it != sources.constEnd(); ++it )
56 QString urlKey = u
"url"_s;
57 QString nameKey = u
"urlName"_s;
60 urlKey.append( QString(
"_%1" ).arg( i ) );
61 nameKey.append( QString(
"_%1" ).arg( i ) );
63 uri.append( QString(
"&%1=%2" ).arg( nameKey, it.key() ) );
64 uri.append( QString(
"&%1=%2" ).arg( urlKey, it.value() ) );
69QMap<QString, QString> QgsVectorTileUtils::parseStyleSourceUrl(
const QString &styleUrl,
const QgsHttpHeaders &headers,
const QString &authCfg )
72 nr.setUrl( QUrl( styleUrl ) );
81 return QMap<QString, QString>();
83 QgsNetworkReplyContent reply = req.
reply();
86 const QJsonDocument doc = QJsonDocument::fromJson( reply.
content(), &err );
89 QgsDebugError( u
"Could not load style: %1"_s.arg( err.errorString() ) );
91 else if ( !doc.isObject() )
93 QgsDebugError( u
"Could not read style, JSON object is expected"_s );
97 QMap<QString, QString> sources;
98 QJsonObject sourcesData = doc.object().value( u
"sources"_s ).toObject();
99 if ( sourcesData.count() == 0 )
105 QJsonObject::const_iterator it = sourcesData.constBegin();
106 for ( ; it != sourcesData.constEnd(); ++it )
108 const QString sourceName = it.key();
109 const QJsonObject sourceData = it.value().toObject();
110 if ( sourceData.value( u
"type"_s ).toString() !=
"vector"_L1 )
118 if ( sourceData.contains( u
"tiles"_s ) )
120 tiles = sourceData[
"tiles"].toArray().toVariantList();
121 tilesFrom = styleUrl;
123 else if ( sourceData.contains( u
"url"_s ) )
125 tiles = parseStyleSourceContentUrl( sourceData.value( u
"url"_s ).toString(), headers, authCfg );
126 tilesFrom = sourceData.value( u
"url"_s ).toString();
130 QgsDebugError( u
"Could not read source %1"_s.arg( sourceName ) );
133 if ( !tiles.isEmpty() )
137 QString tile = tiles[rand() % tiles.count()].toString();
138 QUrl tileUrl( tile );
139 if ( tileUrl.isRelative() )
141 QUrl tilesFromUrl( tilesFrom );
142 if ( tile.startsWith(
"/" ) )
144 tile = u
"%1://%2%3"_s.arg( tilesFromUrl.scheme(), tilesFromUrl.host(), tile );
148 const QString fileName = tilesFromUrl.fileName();
149 if ( !fileName.isEmpty() && fileName.indexOf(
"." ) >= 0 )
151 tilesFrom = tilesFrom.mid( 0, tilesFrom.length() - fileName.length() );
153 tile = u
"%1/%2"_s.arg( tilesFrom, tile );
156 sources.insert( sourceName, tile );
160 QgsDebugError( u
"Could not read source %1, not tiles found"_s.arg( sourceName ) );
166 return QMap<QString, QString>();
169QVariantList QgsVectorTileUtils::parseStyleSourceContentUrl(
const QString &sourceUrl,
const QgsHttpHeaders &headers,
const QString &authCfg )
172 nr.setUrl( QUrl( sourceUrl ) );
175 QgsBlockingNetworkRequest req;
181 return QVariantList();
183 QgsNetworkReplyContent reply = req.
reply();
186 const QJsonDocument doc = QJsonDocument::fromJson( reply.
content(), &err );
189 QgsDebugError( u
"Could not load style: %1"_s.arg( err.errorString() ) );
191 else if ( !doc.isObject() )
193 QgsDebugError( u
"Could not read style, JSON object is expected"_s );
197 return doc.object().value( u
"tiles"_s ).toArray().toVariantList();
199 return QVariantList();
221 QStringList fieldsSorted = qgis::setToList( flds );
222 std::sort( fieldsSorted.begin(), fieldsSorted.end() );
223 for (
const QString &fieldName : std::as_const( fieldsSorted ) )
233 double tileZoom2 = log( s0 / mapScale ) / log( 2 );
240 int tileZoom =
static_cast<int>( round(
scaleToZoom( mapScale, z0Scale ) ) );
242 if ( tileZoom < sourceMinZoom )
243 tileZoom = sourceMinZoom;
244 if ( tileZoom > sourceMaxZoom )
245 tileZoom = sourceMaxZoom;
254 decoder.
decode( rawTile );
255 QSet<QString> fieldNames = qgis::listToSet( decoder.
layerFieldNames( layerName ) );
256 fieldNames << u
"_geom_type"_s;
257 QMap<QString, QgsFields> perLayerFields;
259 perLayerFields[layerName] = fields;
264 for (
int i = 0; i < featuresList.count(); ++i )
271 for (
int k = 0; k < ggc->numGeometries(); ++k )
296 turl.replace(
"{x}"_L1, QString::number( tile.
column() ), Qt::CaseInsensitive );
297 if ( turl.contains(
"{-y}"_L1 ) )
299 turl.replace(
"{-y}"_L1, QString::number( tileMatrix.
matrixHeight() - tile.
row() - 1 ), Qt::CaseInsensitive );
303 turl.replace(
"{y}"_L1, QString::number( tile.
row() ), Qt::CaseInsensitive );
305 turl.replace(
"{z}"_L1, QString::number( tile.
zoomLevel() ), Qt::CaseInsensitive );
311 return url.contains( u
"{x}"_s ) && ( url.contains( u
"{y}"_s ) || url.contains( u
"{-y}"_s ) ) && url.contains( u
"{z}"_s );
320 QPointF p1( req1.
column() + 0.5, req1.
row() + 0.5 );
321 QPointF p2( req2.
column() + 0.5, req2.
row() + 0.5 );
323 double d1 = std::max( std::fabs(
center.x() - p1.x() ), std::fabs(
center.y() - p1.y() ) );
324 double d2 = std::max( std::fabs(
center.x() - p2.x() ), std::fabs(
center.y() - p2.y() ) );
333 std::sort( tiles.begin(), tiles.end(), cmp );
338 if ( styleDefinition.contains( u
"sprite"_s ) && ( context.
spriteCategories().isEmpty() ) )
340 auto prepareSpriteUrl = [](
const QString &sprite,
const QString &styleUrl ) {
341 if ( sprite.startsWith(
"http"_L1 ) )
345 else if ( sprite.startsWith(
"mapbox://"_L1 ) )
347 const QUrl url( styleUrl );
348 const QUrlQuery query( url.query() );
349 if ( query.hasQueryItem( u
"access_token"_s ) )
351 return resolveMapboxUri( sprite, query.queryItemValue( u
"access_token"_s ) );
354 else if ( sprite.startsWith(
'/'_L1 ) )
356 const QUrl url( styleUrl );
357 return u
"%1://%2%3"_s.arg( url.scheme(), url.host(), sprite );
360 return u
"%1/%2"_s.arg( styleUrl, sprite );
364 QMap<QString, QString> sprites;
365 const QVariant spriteVariant = styleDefinition.value( u
"sprite"_s );
366 if ( spriteVariant.userType() == QMetaType::Type::QVariantList )
368 const QVariantList spriteList = spriteVariant.toList();
369 for (
const QVariant &spriteItem : spriteList )
371 QVariantMap spriteMap = spriteItem.toMap();
372 if ( spriteMap.contains( u
"id"_s ) && spriteMap.contains(
"url" ) )
374 sprites[spriteMap.value( u
"id"_s ).toString()] = prepareSpriteUrl( spriteMap.value( u
"url"_s ).toString(), styleUrl );
380 sprites[
""] = prepareSpriteUrl( spriteVariant.toString(), styleUrl );
383 if ( sprites.isEmpty() )
388 QMap<QString, QString>::const_iterator spritesIterator = sprites.constBegin();
389 while ( spritesIterator != sprites.end() )
391 for (
int resolution = 2; resolution > 0; resolution-- )
393 QUrl spriteUrl = QUrl( spritesIterator.value() );
394 spriteUrl.setPath( spriteUrl.path() + u
"%1.json"_s.arg( resolution > 1 ? u
"@%1x"_s.arg( resolution ) : QString() ) );
395 QNetworkRequest request = QNetworkRequest( spriteUrl );
397 switch ( networkRequest.
get( request ) )
405 QUrl spriteUrl = QUrl( spritesIterator.value() );
406 spriteUrl.setPath( spriteUrl.path() + u
"%1.png"_s.arg( resolution > 1 ? u
"@%1x"_s.arg( resolution ) : QString() ) );
407 QNetworkRequest request = QNetworkRequest( spriteUrl );
409 switch ( networkRequest.
get( request ) )
414 const QImage spriteImage( QImage::fromData( imageContent.
content() ) );
415 context.
setSprites( spriteImage, spriteDefinition, spritesIterator.key() );
445 const QUrl url( uri );
446 if ( url.scheme() !=
"mapbox"_L1 )
451 const QString host = url.host();
452 if ( host ==
"styles"_L1 )
455 return u
"https://api.mapbox.com/styles/v1%1?access_token=%2"_s.arg( url.path(), accessToken );
457 else if ( host ==
"sprites" )
460 return u
"https://api.mapbox.com/styles/v1%1/sprite?access_token=%2"_s.arg( url.path(), accessToken );
462 else if ( host ==
"fonts" )
465 return u
"https://api.mapbox.com/fonts/v1%1?access_token=%2"_s.arg( url.path(), accessToken );
471 return u
"https://api.mapbox.com/v4/%1.json?secure&access_token=%2"_s.arg( host, accessToken );
Abstract base class for all geometries.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
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.
@ NetworkError
A network error occurred.
@ ServerExceptionError
An exception was raised by the server.
@ NoError
No error was encountered.
@ TimeoutError
Timeout was reached before a reply was received.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get(), post(), head() or put() request has been mad...
Encapsulate a field in an attribute table or data source.
Container of fields for a vector layer.
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
virtual bool addGeometry(QgsAbstractGeometry *g)
Adds a geometry and takes ownership. Returns true in case of success.
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QVariant parseJson(const std::string &jsonString)
Converts JSON jsonString to a QVariant, in case of parsing error an invalid QVariant is returned and ...
Context for a MapBox GL style conversion operation.
void setSprites(const QImage &image, const QVariantMap &definitions, const QString &category=QString())
Sets the sprite image and definitions JSON for a given category to use during conversion.
QVariantMap spriteDefinitions(const QString &category=QString()) const
Returns the sprite definitions for a given category to use during conversion.
QStringList spriteCategories() const
Returns the list of sprite categories to use during conversion, or an empty list of none is set.
Perform transforms between map coordinates and device coordinates.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QByteArray content() const
Returns the reply content.
QPointF toQPointF() const
Converts a point to a QPointF.
A rectangle specified with double values.
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
QgsRectangle tileExtent(QgsTileXYZ id) const
Returns extent of the given tile in this matrix.
int matrixHeight() const
Returns number of rows of the tile matrix.
Stores coordinates of a tile in a tile matrix set.
int zoomLevel() const
Returns tile's zoom level (Z).
int column() const
Returns tile's column index (X).
int row() const
Returns tile's row index (Y).
bool addFeatures(QgsFeatureList &flist, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
virtual bool addAttributes(const QList< QgsField > &attributes)
Adds new attributes to the provider.
Represents a vector layer which manages a vector based dataset.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void updateFields()
Will regenerate the fields property of this layer by obtaining all fields from the dataProvider,...
virtual void updateExtents(bool force=false)
Update the extents for the layer.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
Implements a map layer that is dedicated to rendering of vector tiles.
QgsVectorTileRawData getRawTile(QgsTileXYZ tileID)
Fetches raw tile data for the give tile coordinates.
QgsVectorTileMatrixSet & tileMatrixSet()
Returns the vector tile matrix set.
Responsible for decoding raw tile data written with Mapbox Vector Tiles encoding.
QStringList layerFieldNames(const QString &layerName) const
Returns a list of all field names in a tile. It can only be called after a successful decode().
bool decode(const QgsVectorTileRawData &rawTileData)
Tries to decode raw tile data, returns true on success.
QgsVectorTileFeatures layerFeatures(const QMap< QString, QgsFields > &perLayerFields, const QgsCoordinateTransform &ct, const QSet< QString > *layerSubset=nullptr) const
Returns decoded features grouped by sub-layers.
Keeps track of raw tile data from one or more sources that need to be decoded.
static QgsVectorLayer * makeVectorLayerForTile(QgsVectorTileLayer *mvt, QgsTileXYZ tileID, const QString &layerName)
Returns a temporary vector layer for given sub-layer of tile in vector tile layer.
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}...
static void sortTilesByDistanceFromCenter(QVector< QgsTileXYZ > &tiles, QPointF center)
Orders tile requests according to the distance from view center (given in tile matrix coords).
static QString resolveMapboxUri(const QString &uri, const QString &accessToken)
Resolves a URI which uses the mapbox:// protocol to a standard HTTPS URL.
static QPolygon tilePolygon(QgsTileXYZ id, const QgsCoordinateTransform &ct, const QgsTileMatrix &tm, const QgsMapToPixel &mtp)
Returns polygon (made by four corners of the tile) in screen coordinates.
static double scaleToZoom(double mapScale, double z0Scale=559082264.0287178)
Finds zoom level given map scale denominator.
static int scaleToZoomLevel(double mapScale, int sourceMinZoom, int sourceMaxZoom, double z0Scale=559082264.0287178)
Finds the best fitting zoom level given a map scale denominator and allowed zoom level range.
static void updateUriSources(QString &uri SIP_INOUT, bool forceUpdate=false)
Parses the style URL to update the source URLs in the uri.
static QgsFields makeQgisFields(const QSet< QString > &flds)
Returns QgsFields instance based on the set of field names.
static void loadSprites(const QVariantMap &styleDefinition, QgsMapBoxGlStyleConversionContext &context, const QString &styleUrl=QString())
Downloads the sprite image and sets it to the conversion context.
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QList< QgsFeature > QgsFeatureList
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
#define QgsSetRequestInitiatorClass(request, _class)
QMap< QString, QVector< QgsFeature > > QgsVectorTileFeatures
Features of a vector tile, grouped by sub-layer names (key of the map).
a helper class for ordering tile requests according to the distance from view center
bool operator()(QgsTileXYZ req1, QgsTileXYZ req2)
QPointF center
Center in tile matrix (!) coordinates.