QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
qgsvectortileutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectortileutils.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
16#include "qgsvectortileutils.h"
17
18#include <math.h>
19
20#include <QPolygon>
21
24#include "qgsfields.h"
25#include "qgslogger.h"
26#include "qgsmaptopixel.h"
27#include "qgsrectangle.h"
28#include "qgsvectorlayer.h"
29
30#include "qgsvectortileloader.h"
32#include "qgsvectortilelayer.h"
37#include "qgsjsonutils.h"
38
39
41{
42 QgsRectangle r = tm.tileExtent( id );
43 QgsPointXY p00a = mtp.transform( ct.transform( r.xMinimum(), r.yMinimum() ) );
44 QgsPointXY p11a = mtp.transform( ct.transform( r.xMaximum(), r.yMaximum() ) );
45 QgsPointXY p01a = mtp.transform( ct.transform( r.xMinimum(), r.yMaximum() ) );
46 QgsPointXY p10a = mtp.transform( ct.transform( r.xMaximum(), r.yMinimum() ) );
47 QPolygon path;
48 path << p00a.toQPointF().toPoint();
49 path << p01a.toQPointF().toPoint();
50 path << p11a.toQPointF().toPoint();
51 path << p10a.toQPointF().toPoint();
52 return path;
53}
54
56{
57 QgsFields fields;
58 QStringList fieldsSorted = qgis::setToList( flds );
59 std::sort( fieldsSorted.begin(), fieldsSorted.end() );
60 for ( const QString &fieldName : std::as_const( fieldsSorted ) )
61 {
62 fields.append( QgsField( fieldName, QVariant::String ) );
63 }
64 return fields;
65}
66
67double QgsVectorTileUtils::scaleToZoom( double mapScale, double z0Scale )
68{
69 double s0 = z0Scale;
70 double tileZoom2 = log( s0 / mapScale ) / log( 2 );
71 tileZoom2 -= 1; // TODO: it seems that map scale is double (is that because of high-dpi screen?)
72 return tileZoom2;
73}
74
75int QgsVectorTileUtils::scaleToZoomLevel( double mapScale, int sourceMinZoom, int sourceMaxZoom, double z0Scale )
76{
77 int tileZoom = static_cast<int>( round( scaleToZoom( mapScale, z0Scale ) ) );
78
79 if ( tileZoom < sourceMinZoom )
80 tileZoom = sourceMinZoom;
81 if ( tileZoom > sourceMaxZoom )
82 tileZoom = sourceMaxZoom;
83
84 return tileZoom;
85}
86
88{
89 QgsVectorTileMVTDecoder decoder( mvt->tileMatrixSet() );
90 const QgsVectorTileRawData rawTile = mvt->getRawTile( tileID );
91 decoder.decode( rawTile );
92 QSet<QString> fieldNames = qgis::listToSet( decoder.layerFieldNames( layerName ) );
93 fieldNames << QStringLiteral( "_geom_type" );
94 QMap<QString, QgsFields> perLayerFields;
95 QgsFields fields = QgsVectorTileUtils::makeQgisFields( fieldNames );
96 perLayerFields[layerName] = fields;
97 QgsVectorTileFeatures data = decoder.layerFeatures( perLayerFields, QgsCoordinateTransform() );
98 QgsFeatureList featuresList = data[layerName].toList();
99
100 // turn all geometries to geom. collections (otherwise they won't be accepted by memory provider)
101 for ( int i = 0; i < featuresList.count(); ++i )
102 {
103 QgsGeometry g = featuresList[i].geometry();
105 const QgsAbstractGeometry *gg = g.constGet();
106 if ( const QgsGeometryCollection *ggc = qgsgeometry_cast<const QgsGeometryCollection *>( gg ) )
107 {
108 for ( int k = 0; k < ggc->numGeometries(); ++k )
109 gc->addGeometry( ggc->geometryN( k )->clone() );
110 }
111 else
112 gc->addGeometry( gg->clone() );
113 featuresList[i].setGeometry( QgsGeometry( gc ) );
114 }
115
116 QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( "GeometryCollection" ), layerName, QStringLiteral( "memory" ) );
117 vl->dataProvider()->addAttributes( fields.toList() );
118 vl->updateFields();
119 bool res = vl->dataProvider()->addFeatures( featuresList );
120 Q_UNUSED( res )
121 Q_ASSERT( res );
122 Q_ASSERT( featuresList.count() == vl->featureCount() );
123 vl->updateExtents();
124 QgsDebugMsgLevel( QStringLiteral( "Layer %1 features %2" ).arg( layerName ).arg( vl->featureCount() ), 2 );
125 return vl;
126}
127
128
129QString QgsVectorTileUtils::formatXYZUrlTemplate( const QString &url, QgsTileXYZ tile, const QgsTileMatrix &tileMatrix )
130{
131 QString turl( url );
132
133 turl.replace( QLatin1String( "{x}" ), QString::number( tile.column() ), Qt::CaseInsensitive );
134 if ( turl.contains( QLatin1String( "{-y}" ) ) )
135 {
136 turl.replace( QLatin1String( "{-y}" ), QString::number( tileMatrix.matrixHeight() - tile.row() - 1 ), Qt::CaseInsensitive );
137 }
138 else
139 {
140 turl.replace( QLatin1String( "{y}" ), QString::number( tile.row() ), Qt::CaseInsensitive );
141 }
142 turl.replace( QLatin1String( "{z}" ), QString::number( tile.zoomLevel() ), Qt::CaseInsensitive );
143 return turl;
144}
145
147{
148 return url.contains( QStringLiteral( "{x}" ) ) &&
149 ( url.contains( QStringLiteral( "{y}" ) ) || url.contains( QStringLiteral( "{-y}" ) ) ) &&
150 url.contains( QStringLiteral( "{z}" ) );
151}
152
155{
156 QPointF center;
158 {
159 QPointF p1( req1.column() + 0.5, req1.row() + 0.5 );
160 QPointF p2( req2.column() + 0.5, req2.row() + 0.5 );
161 // using chessboard distance (loading order more natural than euclidean/manhattan distance)
162 double d1 = std::max( std::fabs( center.x() - p1.x() ), std::fabs( center.y() - p1.y() ) );
163 double d2 = std::max( std::fabs( center.x() - p2.x() ), std::fabs( center.y() - p2.y() ) );
164 return d1 < d2;
165 }
166};
167
168void QgsVectorTileUtils::sortTilesByDistanceFromCenter( QVector<QgsTileXYZ> &tiles, QPointF center )
169{
171 cmp.center = center;
172 std::sort( tiles.begin(), tiles.end(), cmp );
173}
174
175void QgsVectorTileUtils::loadSprites( const QVariantMap &styleDefinition, QgsMapBoxGlStyleConversionContext &context, const QString &styleUrl )
176{
177 if ( styleDefinition.contains( QStringLiteral( "sprite" ) ) && ( context.spriteDefinitions().empty() || context.spriteImage().isNull() ) )
178 {
179 // retrieve sprite definition
180 QString spriteUriBase;
181 if ( styleDefinition.value( QStringLiteral( "sprite" ) ).toString().startsWith( QLatin1String( "http" ) ) )
182 {
183 spriteUriBase = styleDefinition.value( QStringLiteral( "sprite" ) ).toString();
184 }
185 else
186 {
187 spriteUriBase = styleUrl + '/' + styleDefinition.value( QStringLiteral( "sprite" ) ).toString();
188 }
189
190 for ( int resolution = 2; resolution > 0; resolution-- )
191 {
192 QUrl spriteUrl = QUrl( spriteUriBase );
193 spriteUrl.setPath( spriteUrl.path() + QStringLiteral( "%1.json" ).arg( resolution > 1 ? QStringLiteral( "@%1x" ).arg( resolution ) : QString() ) );
194 QNetworkRequest request = QNetworkRequest( spriteUrl );
195 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
196 QgsBlockingNetworkRequest networkRequest;
197 switch ( networkRequest.get( request ) )
198 {
200 {
201 const QgsNetworkReplyContent content = networkRequest.reply();
202 const QVariantMap spriteDefinition = QgsJsonUtils::parseJson( content.content() ).toMap();
203
204 // retrieve sprite images
205 QUrl spriteUrl = QUrl( spriteUriBase );
206 spriteUrl.setPath( spriteUrl.path() + QStringLiteral( "%1.png" ).arg( resolution > 1 ? QStringLiteral( "@%1x" ).arg( resolution ) : QString() ) );
207 QNetworkRequest request = QNetworkRequest( spriteUrl );
208 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsVectorTileLayer" ) )
209 QgsBlockingNetworkRequest networkRequest;
210 switch ( networkRequest.get( request ) )
211 {
213 {
214 const QgsNetworkReplyContent imageContent = networkRequest.reply();
215 const QImage spriteImage( QImage::fromData( imageContent.content() ) );
216 context.setSprites( spriteImage, spriteDefinition );
217 break;
218 }
219
223 break;
224 }
225
226 break;
227 }
228
232 break;
233 }
234
235 if ( !context.spriteDefinitions().isEmpty() )
236 break;
237 }
238 }
239}
240
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...
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr)
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...
Class for doing transforms between two map coordinate systems.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:53
Container of fields for a vector layer.
Definition: qgsfields.h:45
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfields.cpp:212
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
Geometry collection.
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.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
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)
Sets the sprite image and definitions JSON to use during conversion.
QImage spriteImage() const
Returns the sprite image to use during conversion, or an invalid image if this is not set.
QVariantMap spriteDefinitions() const
Returns the sprite definitions to use during conversion.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Definition: qgsmaptopixel.h:90
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QByteArray content() const
Returns the reply content.
A class to represent a 2D point.
Definition: qgspointxy.h:59
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:169
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition: qgstiles.h:134
QgsRectangle tileExtent(QgsTileXYZ id) const
Returns extent of the given tile in this matrix.
Definition: qgstiles.cpp:81
int matrixHeight() const
Returns number of rows of the tile matrix.
Definition: qgstiles.h:186
Stores coordinates of a tile in a tile matrix set.
Definition: qgstiles.h:38
int zoomLevel() const
Returns tile's zoom level (Z)
Definition: qgstiles.h:51
int column() const
Returns tile's column index (X)
Definition: qgstiles.h:47
int row() const
Returns tile's row index (Y)
Definition: qgstiles.h:49
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 data sets.
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.
This class is 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 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} for TM...
static void sortTilesByDistanceFromCenter(QVector< QgsTileXYZ > &tiles, QPointF center)
Orders tile requests according to the distance from view center (given in tile matrix coords)
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 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.
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:920
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#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.