1 /***************************************************************************
2   qgsvectortilelayer.h
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 #ifndef QGSVECTORTILELAYER_H
17 #define QGSVECTORTILELAYER_H
18 
19 #include "qgis_core.h"
20 #include "qgis_sip.h"
21 
22 #include "qgsmaplayer.h"
23 
24 class QgsVectorTileLabeling;
25 class QgsVectorTileRenderer;
26 
27 class QgsTileXYZ;
28 
29 /**
30  * \ingroup core
31  * \brief Implements a map layer that is dedicated to rendering of vector tiles.
32  * Vector tiles compared to "ordinary" vector layers are pre-processed data
33  * optimized for fast rendering. A dataset is provided with a series of zoom levels
34  * for different map scales. Each zoom level has a matrix of tiles that contain
35  * actual data. A single vector tile may be a a file stored on a local drive,
36  * requested over HTTP request or retrieved from a database.
37  *
38  * Content of a vector tile is divided into one or more named sub-layers. Each such
39  * sub-layer may contain many features which consist of geometry and attributes.
40  * Contrary to traditional vector layers, these sub-layers do not need to have a rigid
41  * schema where geometry type and attributes are the same for all features. A single
42  * sub-layer may have multiple geometry types in a single tile or have some attributes
43  * defined only at particular zoom levels.
44  *
45  * Vector tile layer currently does not use the concept of data providers that other
46  * layer types use. The process of rendering of vector tiles looks like this:
47  *
48  * +--------+                +------+                 +---------+
49  * |  DATA  |                |  RAW |                 | DECODED |
50  * |        | --> LOADER --> |      | --> DECODER --> |         | --> RENDERER
51  * | SOURCE |                | TILE |                 |  TILE   |
52  * +--------+                +------+                 +---------+
53  *
54  * Data source is a place from where tiles are fetched from (URL for HTTP access, local
55  * files, MBTiles file, GeoPackage file or others. Loader (QgsVectorTileLoader) class
56  * takes care of loading data from the data source. The "raw tile" data is just a blob
57  * (QByteArray) that is encoded in some way. There are multiple ways how vector tiles
58  * are encoded just like there are different formats how to store images. For example,
59  * tiles can be encoded using Mapbox Vector Tiles (MVT) format or in GeoJSON. Decoder
60  * (QgsVectorTileDecoder) takes care of decoding raw tile data into QgsFeature objects.
61  * A decoded tile is essentially an array of vector features for each sub-layer found
62  * in the tile - this is what vector tile renderer (QgsVectorTileRenderer) expects
63  * and does the map rendering.
64  *
65  * To construct a vector tile layer, it is best to use QgsDataSourceUri class and set
66  * the following parameters to get a valid encoded URI:
67  *
68  * - "type" - what kind of data source will be used
69  * - "url" - URL or path of the data source (specific to each data source type, see below)
70  *
71  * Currently supported data source types:
72  *
73  * - "xyz" - the "url" should be a template like http://example.com/{z}/{x}/{y}.pbf where
74  *   {x},{y},{z} will be replaced by tile coordinates
75  * - "mbtiles" - tiles read from a MBTiles file (a SQLite database)
76  *
77  * Currently supported decoders:
78  *
79  * - MVT - following Mapbox Vector Tiles specification
80  *
81  * \since QGIS 3.14
82  */
83 class CORE_EXPORT QgsVectorTileLayer : public QgsMapLayer
84 {
85     Q_OBJECT
86 
87   public:
88 
89 
90     /**
91      * Setting options for loading vector tile layers.
92      *
93      * \since QGIS 3.22
94      */
95     struct LayerOptions
96     {
97 
98       /**
99        * Constructor for LayerOptions with optional \a transformContext.
100        */
101       explicit LayerOptions( const QgsCoordinateTransformContext &transformContext = QgsCoordinateTransformContext( ) )
transformContextLayerOptions102         : transformContext( transformContext )
103       {}
104 
105       //! Coordinate transform context
106       QgsCoordinateTransformContext transformContext;
107     };
108 
109     //! Constructs a new vector tile layer
110     explicit QgsVectorTileLayer( const QString &path = QString(), const QString &baseName = QString(), const QgsVectorTileLayer::LayerOptions &options = QgsVectorTileLayer::LayerOptions() );
111     ~QgsVectorTileLayer() override;
112 
113 #ifdef SIP_RUN
114     SIP_PYOBJECT __repr__();
115     % MethodCode
116     QString str = QStringLiteral( "<QgsVectorTileLayer: '%1'>" ).arg( sipCpp->name() );
117     sipRes = PyUnicode_FromString( str.toUtf8().constData() );
118     % End
119 #endif
120 
121     // implementation of virtual functions from QgsMapLayer
122 
123     QgsVectorTileLayer *clone() const override SIP_FACTORY;
124     QgsDataProvider *dataProvider() override;
125     const QgsDataProvider *dataProvider() const override SIP_SKIP;
126     QgsMapLayerRenderer *createMapRenderer( QgsRenderContext &rendererContext ) override SIP_FACTORY;
127     bool readXml( const QDomNode &layerNode, QgsReadWriteContext &context ) override;
128     bool writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const override;
129     bool readSymbology( const QDomNode &node, QString &errorMessage,
130                         QgsReadWriteContext &context, StyleCategories categories = AllStyleCategories ) override;
131     bool writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context,
132                          StyleCategories categories = AllStyleCategories ) const override;
133     void setTransformContext( const QgsCoordinateTransformContext &transformContext ) override;
134     QString loadDefaultStyle( bool &resultFlag SIP_OUT ) override;
135 
136     /**
137      * Loads the default style for the layer, and returns TRUE if the style was
138      * successfully loaded.
139      *
140      * The \a error string will be filled with a translated error message if an error
141      * occurs during the style load. The \a warnings list will be populated with any
142      * warning messages generated during the style load (e.g. default style properties
143      * which could not be converted).
144      *
145      * \since QGIS 3.16
146      */
147     bool loadDefaultStyle( QString &error, QStringList &warnings ) SIP_SKIP;
148 
149     QString loadDefaultMetadata( bool &resultFlag SIP_OUT ) override;
150 
151     QString encodedSource( const QString &source, const QgsReadWriteContext &context ) const FINAL;
152     QString decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const FINAL;
153     QString htmlMetadata() const override;
154 
155     // new methods
156 
157     //! Returns type of the data source
sourceType()158     QString sourceType() const { return mSourceType; }
159     //! Returns URL/path of the data source (syntax different to each data source type)
sourcePath()160     QString sourcePath() const { return mSourcePath; }
161 
162     //! Returns minimum zoom level at which source has any valid tiles (negative = unconstrained)
sourceMinZoom()163     int sourceMinZoom() const { return mSourceMinZoom; }
164     //! Returns maximum zoom level at which source has any valid tiles (negative = unconstrained)
sourceMaxZoom()165     int sourceMaxZoom() const { return mSourceMaxZoom; }
166 
167     /**
168      * Fetches raw tile data for the give tile coordinates. If failed to fetch tile data,
169      * it will return an empty byte array.
170      *
171      * \note This call may issue a network request (depending on the source type) and will block
172      * the caller until the request is finished.
173      */
174     QByteArray getRawTile( QgsTileXYZ tileID ) SIP_SKIP;
175 
176     /**
177      * Sets renderer for the map layer.
178      * \note Takes ownership of the passed renderer
179      */
180     void setRenderer( QgsVectorTileRenderer *r SIP_TRANSFER );
181     //! Returns currently assigned renderer
182     QgsVectorTileRenderer *renderer() const;
183 
184     /**
185      * Sets labeling for the map layer.
186      * \note Takes ownership of the passed labeling
187      */
188     void setLabeling( QgsVectorTileLabeling *labeling SIP_TRANSFER );
189     //! Returns currently assigned labeling
190     QgsVectorTileLabeling *labeling() const;
191 
192     //! Sets whether to render also borders of tiles (useful for debugging)
setTileBorderRenderingEnabled(bool enabled)193     void setTileBorderRenderingEnabled( bool enabled ) { mTileBorderRendering = enabled; }
194     //! Returns whether to render also borders of tiles (useful for debugging)
isTileBorderRenderingEnabled()195     bool isTileBorderRenderingEnabled() const { return mTileBorderRendering; }
196 
197   private:
198     bool loadDataSource();
199 
200   private:
201     //! Type of the data source
202     QString mSourceType;
203     //! URL/Path of the data source
204     QString mSourcePath;
205     //! Minimum zoom level at which source has any valid tiles (negative = unconstrained)
206     int mSourceMinZoom = -1;
207     //! Maximum zoom level at which source has any valid tiles (negative = unconstrained)
208     int mSourceMaxZoom = -1;
209 
210     //! Renderer assigned to the layer to draw map
211     std::unique_ptr<QgsVectorTileRenderer> mRenderer;
212     //! Labeling assigned to the layer to produce labels
213     std::unique_ptr<QgsVectorTileLabeling> mLabeling;
214     //! Whether we draw borders of tiles
215     bool mTileBorderRendering = false;
216 
217     QVariantMap mArcgisLayerConfiguration;
218 
219     QgsCoordinateTransformContext mTransformContext;
220 
221     std::unique_ptr< QgsDataProvider > mDataProvider;
222 
223     bool setupArcgisVectorTileServiceConnection( const QString &uri, const QgsDataSourceUri &dataSourceUri );
224 
225     void setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider,
226                                const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags ) override;
227 
228 };
229 
230 #ifndef SIP_RUN
231 ///@cond PRIVATE
232 
233 /**
234  * A minimal data provider for vector tile layers.
235  *
236  * \since QGIS 3.22
237  */
238 class QgsVectorTileDataProvider : public QgsDataProvider
239 {
240     Q_OBJECT
241 
242   public:
243     QgsVectorTileDataProvider( const QgsDataProvider::ProviderOptions &providerOptions,
244                                QgsDataProvider::ReadFlags flags );
245     QgsCoordinateReferenceSystem crs() const override;
246     QString name() const override;
247     QString description() const override;
248     QgsRectangle extent() const override;
249     bool isValid() const override;
250 
251 };
252 ///@endcond
253 #endif
254 
255 
256 #endif // QGSVECTORTILELAYER_H
257