19#include <nlohmann/json.hpp>
38using namespace Qt::StringLiterals;
50 mRootTileMatrix = tileMatrix;
61 mErrorMessage = tr(
"Invalid min. zoom level" );
66 mErrorMessage = tr(
"Invalid max. zoom level" );
70 std::unique_ptr<QgsMbTiles> mbtiles;
75 QString sourceType = dsUri.
param( u
"type"_s );
76 QString sourcePath = dsUri.
param( u
"url"_s );
77 if ( sourceType ==
"xyz"_L1 )
80 sourcePath = QUrl( sourcePath ).toLocalFile();
84 mErrorMessage = tr(
"Invalid template for XYZ: " ) + sourcePath;
88 else if ( sourceType ==
"mbtiles"_L1 )
90 mbtiles = std::make_unique<QgsMbTiles>( sourcePath );
94 mErrorMessage = tr(
"Unsupported source type for writing: " ) + sourceType;
104 mErrorMessage = tr(
"Failed to calculate output extent" );
110 int tilesToCreate = 0;
111 for (
int zoomLevel = mMinZoom; zoomLevel <= mMaxZoom; ++zoomLevel )
116 tilesToCreate += ( tileRange.
endRow() - tileRange.
startRow() + 1 ) *
120 if ( tilesToCreate == 0 )
122 mErrorMessage = tr(
"No tiles to generate" );
128 if ( !mbtiles->create() )
130 mErrorMessage = tr(
"Failed to create MBTiles file: " ) + sourcePath;
135 mbtiles->setMetadataValue(
"format",
"pbf" );
136 mbtiles->setMetadataValue(
"json", mbtilesJsonSchema() );
139 const QStringList metaKeys = mMetadata.keys();
140 for (
const QString &key : metaKeys )
142 mbtiles->setMetadataValue( key, mMetadata[key].toString() );
146 if ( !mMetadata.contains(
"name" ) )
147 mbtiles->setMetadataValue(
"name",
"unnamed" );
148 if ( !mMetadata.contains(
"minzoom" ) )
149 mbtiles->setMetadataValue(
"minzoom", QString::number( mMinZoom ) );
150 if ( !mMetadata.contains(
"maxzoom" ) )
151 mbtiles->setMetadataValue(
"maxzoom", QString::number( mMaxZoom ) );
152 if ( !mMetadata.contains(
"bounds" ) )
159 QString boundsStr = QString(
"%1,%2,%3,%4" )
162 mbtiles->setMetadataValue(
"bounds", boundsStr );
169 if ( !mMetadata.contains(
"crs" ) )
170 mbtiles->setMetadataValue(
"crs", mRootTileMatrix.crs().authid() );
173 int tilesCreated = 0;
174 for (
int zoomLevel = mMinZoom; zoomLevel <= mMaxZoom; ++zoomLevel )
179 for (
int row = tileRange.
startRow(); row <= tileRange.
endRow(); ++row )
187 for (
const Layer &layer : std::as_const( mLayers ) )
189 if ( ( layer.minZoom() >= 0 && zoomLevel < layer.minZoom() ) ||
190 ( layer.maxZoom() >= 0 && zoomLevel > layer.maxZoom() ) )
193 encoder.
addLayer( layer.layer(), feedback, layer.filterExpression(), layer.layerName() );
198 mErrorMessage = tr(
"Operation has been canceled" );
202 QByteArray tileData = encoder.
encode();
207 feedback->
setProgress(
static_cast<double>( tilesCreated ) / tilesToCreate * 100 );
210 if ( tileData.isEmpty() )
216 if ( sourceType ==
"xyz"_L1 )
218 if ( !writeTileFileXYZ( sourcePath, tileID, tileMatrix, tileData ) )
223 QByteArray gzipTileData;
225 int rowTMS = pow( 2, tileID.
zoomLevel() ) - tileID.
row() - 1;
226 mbtiles->setTileData( tileID.
zoomLevel(), tileID.
column(), rowTMS, gzipTileData );
239 for (
const Layer &layer : mLayers )
251 QgsDebugError(
"Failed to reproject layer extent to destination CRS" );
257bool QgsVectorTileWriter::writeTileFileXYZ(
const QString &sourcePath,
QgsTileXYZ tileID,
const QgsTileMatrix &tileMatrix,
const QByteArray &tileData )
262 QFileInfo fi( filePath );
263 QDir fileDir = fi.dir();
264 if ( !fileDir.exists() )
266 if ( !fileDir.mkpath(
"." ) )
268 mErrorMessage = tr(
"Cannot create directory " ) + fileDir.path();
274 if ( !f.open( QIODevice::WriteOnly ) )
276 mErrorMessage = tr(
"Cannot open file for writing " ) + filePath;
286QString QgsVectorTileWriter::mbtilesJsonSchema()
const
288 QVariantList arrayLayers;
289 for (
const Layer &layer : std::as_const( mLayers ) )
291 QgsVectorLayer *vl = layer.layer();
292 const QgsFields fields = vl->
fields();
294 QVariantMap fieldsObj;
295 for (
const QgsField &field : fields )
297 QString fieldTypeStr;
298 if ( field.type() == QMetaType::Type::Bool )
299 fieldTypeStr = u
"Boolean"_s;
300 else if ( field.type() == QMetaType::Type::Int || field.type() == QMetaType::Type::Double )
301 fieldTypeStr = u
"Number"_s;
303 fieldTypeStr = u
"String"_s;
305 fieldsObj[field.name()] = fieldTypeStr;
308 QVariantMap layerObj;
309 layerObj[
"id"] = vl->
name();
310 layerObj[
"fields"] = fieldsObj;
311 arrayLayers.append( layerObj );
315 rootObj[
"vector_layers"] = arrayLayers;
331 if ( ( layer.minZoom() >= 0 && zoomLevel < layer.minZoom() ) ||
332 ( layer.maxZoom() >= 0 && zoomLevel > layer.maxZoom() ) )
335 encoder.
addLayer( layer.layer(), feedback, layer.filterExpression(), layer.layerName() );
Represents a coordinate reference system (CRS).
Custom exception class for Coordinate Reference System related exceptions.
Stores the component parts of a data source URI (e.g.
void setEncodedUri(const QByteArray &uri)
Sets the complete encoded uri.
QString param(const QString &key) const
Returns a generic parameter value corresponding to the specified key.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
static json jsonFromVariant(const QVariant &v)
Converts a QVariant v to a json object.
QgsCoordinateReferenceSystem crs
A rectangle specified with double values.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
static QgsTileMatrix fromWebMercator(int zoomLevel)
Returns a tile matrix for the usual web mercator.
bool isRootTileMatrix() const
Returns the root status of the tile matrix (zoom level == 0).
static QgsTileMatrix fromTileMatrix(int zoomLevel, const QgsTileMatrix &tileMatrix)
Returns a tile matrix based on another one.
A range of tiles in a tile matrix.
int endColumn() const
Returns index of the last column in the range.
int endRow() const
Returns index of the last row in the range.
int startRow() const
Returns index of the first row in the range.
int startColumn() const
Returns index of the first column in the range.
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).
Represents a vector layer which manages a vector based dataset.
QgsRectangle extent() const final
Returns the extent of the layer.
Handles conversion of vector features to Mapbox vector tiles encoding.
void addLayer(QgsVectorLayer *layer, QgsFeedback *feedback=nullptr, QString filterExpression=QString(), QString layerName=QString())
Fetches data from vector layer for the given tile, does reprojection and clipping.
void setTileBuffer(int buffer)
Sets size of the buffer zone around tile edges in integer tile coordinates.
void setResolution(int extent)
Sets the resolution of coordinates of geometries within the tile.
QByteArray encode() const
Encodes MVT using data stored previously with addLayer() calls.
void setTransformContext(const QgsCoordinateTransformContext &transformContext)
Sets coordinate transform context for transforms between layers and tile matrix CRS.
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}...
Configuration of a single input vector layer to be included in the output.
bool setRootTileMatrix(const QgsTileMatrix &tileMatrix)
Sets zoom level 0 tile matrix.
QgsRectangle fullExtent() const
Returns calculated extent that combines extent of all input layers.
QByteArray writeSingleTile(QgsTileXYZ tileID, QgsFeedback *feedback=nullptr, int buffer=256, int resolution=4096) const
Encodes single MVT tile.
bool writeTiles(QgsFeedback *feedback=nullptr)
Writes vector tiles according to the configuration.
static bool encodeGzip(const QByteArray &bytesIn, QByteArray &bytesOut)
Encodes gzip byte stream, returns true on success.
#define QgsDebugError(str)