1 /***************************************************************************
2   qgsvectortilewriter.h
3   --------------------------------------
4   Date                 : April 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 QGSVECTORTILEWRITER_H
17 #define QGSVECTORTILEWRITER_H
18 
19 #include <QCoreApplication>
20 #include "qgstiles.h"
21 #include "qgsrectangle.h"
22 #include "qgscoordinatetransformcontext.h"
23 #include "qgscoordinatereferencesystem.h"
24 
25 class QgsFeedback;
26 class QgsTileMatrix;
27 class QgsTileXYZ;
28 class QgsVectorLayer;
29 
30 
31 /**
32  * \ingroup core
33  * \brief Takes care of writing vector tiles. The intended use is to set up the class
34  * by setting the destination URI, extent, zoom level range and input vector
35  * layers. Then with a call to writeTiles() the data gets written. In case
36  * of a failure, errorMessage() returns the cause of the problem during writing.
37  *
38  * The syntax of destination URIs is the same like the data source string
39  * used by vector tile layers: parameters are encoded as a HTTP query string,
40  * where "type" key determines the type of the destination container,
41  * the "url" key is normally the path. Currently supported types:
42  *
43  * - "xyz" - tile data written as local files, using a template where {x},{y},{z}
44  *   are replaced by the actual tile column, row and zoom level numbers, e.g.:
45  *   file:///home/qgis/tiles/{z}/{x}/{y}.pbf
46  * - "mbtiles" - tile data written to a new MBTiles file, the "url" key should
47  *   be ordinary file system path, e.g.: /home/qgis/output.mbtiles
48  *
49  * Currently the writer only support MVT encoding of data.
50  *
51  * Metadata support: it is possible to pass a QVariantMap with metadata. This
52  * is backend dependent. Currently only "mbtiles" source type supports writing
53  * of metadata. The key-value pairs will be written to the "metadata" table
54  * in the MBTiles file. Some useful metadata keys listed here, but see MBTiles spec
55  * for more details:
56  *
57  * - "name" - human-readable name of the tileset
58  * - "bounds" - extent in WGS 84: "minlon,minlat,maxlon,maxlat"
59  * - "center" - default view of the map: "longitude,latitude,zoomlevel"
60  * - "minzoom" - lowest zoom level
61  * - "maxzoom" - highest zoom level
62  * - "attribution" - string that explains the sources of data
63  * - "description" - descriptions of the content
64  * - "type" - whether this is an overlay or a basemap
65  * - "version" - version of the tileset
66  *
67  * Vector tile writer always writes "format" and "json" metadata. If not specified
68  * in metadata by the client, tile writer also writes "name", "bounds", "minzoom"
69  * and "maxzoom".
70  *
71  *
72  * \since QGIS 3.14
73  */
74 class CORE_EXPORT QgsVectorTileWriter
75 {
76     Q_DECLARE_TR_FUNCTIONS( QgsVectorTileWriter )
77 
78   public:
79     QgsVectorTileWriter();
80 
81     /**
82      * \ingroup core
83      * \brief Configuration of a single input vector layer to be included in the output
84      * \since QGIS 3.14
85      */
86     class Layer
87     {
88       public:
89         //! Constructs an entry for a vector layer
Layer(QgsVectorLayer * layer)90         explicit Layer( QgsVectorLayer *layer )
91           : mLayer( layer )
92         {
93         }
94 
95         //! Returns vector layer of this entry
layer()96         QgsVectorLayer *layer() const { return mLayer; }
97 
98         //! Returns filter expression. If not empty, only features matching the expression will be used
filterExpression()99         QString filterExpression() const { return mFilterExpression; }
100         //! Sets filter expression. If not empty, only features matching the expression will be used
setFilterExpression(const QString & expr)101         void setFilterExpression( const QString &expr ) { mFilterExpression = expr; }
102 
103         //! Returns layer name in the output. If not set, layer()->name() will be used.
layerName()104         QString layerName() const { return mLayerName; }
105         //! Sets layer name in the output. If not set, layer()->name() will be used.
setLayerName(const QString & name)106         void setLayerName( const QString &name ) { mLayerName = name; }
107 
108         //! Returns minimum zoom level at which this layer will be used. Negative value means no min. zoom level
minZoom()109         int minZoom() const { return mMinZoom; }
110         //! Sets minimum zoom level at which this layer will be used. Negative value means no min. zoom level
setMinZoom(int minzoom)111         void setMinZoom( int minzoom ) { mMinZoom = minzoom; }
112 
113         //! Returns maximum zoom level at which this layer will be used. Negative value means no max. zoom level
maxZoom()114         int maxZoom() const { return mMaxZoom; }
115         //! Sets maximum zoom level at which this layer will be used. Negative value means no max. zoom level
setMaxZoom(int maxzoom)116         void setMaxZoom( int maxzoom ) { mMaxZoom = maxzoom; }
117 
118       private:
119         QgsVectorLayer *mLayer;
120         QString mFilterExpression;
121         QString mLayerName;
122         int mMinZoom = -1;
123         int mMaxZoom = -1;
124     };
125 
126     /**
127      * Sets where and how the vector tiles will be written.
128      * See the class description about the syntax of destination URIs.
129      */
setDestinationUri(const QString & uri)130     void setDestinationUri( const QString &uri ) { mDestinationUri = uri; }
131 
132     /**
133      * Sets extent of vector tile output.
134      * If unset, it will use the full extent of all input layers combined
135      */
setExtent(const QgsRectangle & extent)136     void setExtent( const QgsRectangle &extent ) { mExtent = extent; }
137 
138     //! Sets the minimum zoom level of tiles. Allowed values are in interval [0,24]
setMinZoom(int minZoom)139     void setMinZoom( int minZoom ) { mMinZoom = minZoom; }
140     //! Sets the maximum zoom level of tiles. Allowed values are in interval [0,24]
setMaxZoom(int maxZoom)141     void setMaxZoom( int maxZoom ) { mMaxZoom = maxZoom; }
142 
143     //! Sets vector layers and their configuration for output of vector tiles
setLayers(const QList<QgsVectorTileWriter::Layer> & layers)144     void setLayers( const QList<QgsVectorTileWriter::Layer> &layers ) { mLayers = layers; }
145 
146     //! Sets that will be written to the output dataset. See class description for more on metadata support
setMetadata(const QVariantMap & metadata)147     void setMetadata( const QVariantMap &metadata ) { mMetadata = metadata; }
148 
149     //! Sets coordinate transform context for transforms between layers and tile matrix CRS
setTransformContext(const QgsCoordinateTransformContext & transformContext)150     void setTransformContext( const QgsCoordinateTransformContext &transformContext ) { mTransformContext = transformContext; }
151 
152     /**
153      * Sets zoom level 0 tile matrix
154      */
155     bool setRootTileMatrix( const QgsTileMatrix &tileMatrix );
156 
157     /**
158      * Writes vector tiles according to the configuration.
159      * Returns TRUE on success (upon failure one can get error cause using errorMessage())
160      *
161      * If a pointer to a feedback object is provided, it can be used to track progress or
162      * provide cancellation functionality.
163      */
164     bool writeTiles( QgsFeedback *feedback = nullptr );
165 
166     /**
167      * Returns error message related to the previous call to writeTiles(). Will return
168      * an empty string if writing was successful.
169      */
errorMessage()170     QString errorMessage() const { return mErrorMessage; }
171 
172     //! Returns calculated extent that combines extent of all input layers
173     QgsRectangle fullExtent() const;
174 
175     /**
176      * Encodes single MVT tile
177      *
178      * \param tileID Tile identifier
179      * \param feedback  optional, provide cancellation functionality
180      * \param resolution the resolution of coordinates of geometries within the tile. The default is 4096
181      * \param buffer size of the buffer zone around tile edges in integer tile coordinates. The default is 256 (~6%)
182      *
183      * Returns a QByteArray with the encoded data
184      *
185      * \since QGIS 3.22
186     */
187     QByteArray writeSingleTile( QgsTileXYZ tileID, QgsFeedback *feedback = nullptr, int buffer = 256, int resolution = 4096 ) const;
188 
189   private:
190     bool writeTileFileXYZ( const QString &sourcePath, QgsTileXYZ tileID, const QgsTileMatrix &tileMatrix, const QByteArray &tileData );
191     QString mbtilesJsonSchema();
192 
193   private:
194     QgsTileMatrix mRootTileMatrix;
195     QgsRectangle mExtent;
196     int mMinZoom = 0;
197     int mMaxZoom = 4;
198     QList<Layer> mLayers;
199     QString mDestinationUri;
200     QVariantMap mMetadata;
201     QgsCoordinateTransformContext mTransformContext;
202 
203     QString mErrorMessage;
204 };
205 
206 #endif // QGSVECTORTILEWRITER_H
207