28 #include <nlohmann/json.hpp>
45 mErrorMessage = tr(
"Invalid min. zoom level" );
50 mErrorMessage = tr(
"Invalid max. zoom level" );
54 std::unique_ptr<QgsMbTiles> mbtiles;
59 QString sourceType = dsUri.
param( QStringLiteral(
"type" ) );
60 QString sourcePath = dsUri.
param( QStringLiteral(
"url" ) );
61 if ( sourceType == QStringLiteral(
"xyz" ) )
64 sourcePath = QUrl( sourcePath ).toLocalFile();
68 mErrorMessage = tr(
"Invalid template for XYZ: " ) + sourcePath;
72 else if ( sourceType == QStringLiteral(
"mbtiles" ) )
78 mErrorMessage = tr(
"Unsupported source type for writing: " ) + sourceType;
88 mErrorMessage = tr(
"Failed to calculate output extent" );
94 int tilesToCreate = 0;
95 for (
int zoomLevel = mMinZoom; zoomLevel <= mMaxZoom; ++zoomLevel )
100 tilesToCreate += ( tileRange.
endRow() - tileRange.
startRow() + 1 ) *
104 if ( tilesToCreate == 0 )
106 mErrorMessage = tr(
"No tiles to generate" );
112 if ( !mbtiles->create() )
114 mErrorMessage = tr(
"Failed to create MBTiles file: " ) + sourcePath;
119 mbtiles->setMetadataValue(
"format",
"pbf" );
120 mbtiles->setMetadataValue(
"json", mbtilesJsonSchema() );
123 const QStringList metaKeys = mMetadata.keys();
124 for (
const QString &key : metaKeys )
126 mbtiles->setMetadataValue( key, mMetadata[key].toString() );
130 if ( !mMetadata.contains(
"name" ) )
131 mbtiles->setMetadataValue(
"name",
"unnamed" );
132 if ( !mMetadata.contains(
"minzoom" ) )
133 mbtiles->setMetadataValue(
"minzoom", QString::number( mMinZoom ) );
134 if ( !mMetadata.contains(
"maxzoom" ) )
135 mbtiles->setMetadataValue(
"maxzoom", QString::number( mMaxZoom ) );
136 if ( !mMetadata.contains(
"bounds" ) )
142 QString boundsStr = QString(
"%1,%2,%3,%4" )
145 mbtiles->setMetadataValue(
"bounds", boundsStr );
154 int tilesCreated = 0;
155 for (
int zoomLevel = mMinZoom; zoomLevel <= mMaxZoom; ++zoomLevel )
160 for (
int row = tileRange.
startRow(); row <= tileRange.
endRow(); ++row )
168 for (
const Layer &layer : qgis::as_const( mLayers ) )
170 if ( ( layer.minZoom() >= 0 && zoomLevel < layer.minZoom() ) ||
171 ( layer.maxZoom() >= 0 && zoomLevel > layer.maxZoom() ) )
174 encoder.
addLayer( layer.layer(), feedback, layer.filterExpression(), layer.layerName() );
179 mErrorMessage = tr(
"Operation has been canceled" );
183 QByteArray tileData = encoder.
encode();
188 feedback->
setProgress(
static_cast<double>( tilesCreated ) / tilesToCreate * 100 );
191 if ( tileData.isEmpty() )
197 if ( sourceType == QStringLiteral(
"xyz" ) )
199 if ( !writeTileFileXYZ( sourcePath, tileID, tileMatrix, tileData ) )
204 QByteArray gzipTileData;
206 int rowTMS = pow( 2, tileID.
zoomLevel() ) - tileID.
row() - 1;
207 mbtiles->setTileData( tileID.
zoomLevel(), tileID.
column(), rowTMS, gzipTileData );
221 for (
const Layer &layer : mLayers )
232 QgsDebugMsg(
"Failed to reproject layer extent to destination CRS" );
238 bool QgsVectorTileWriter::writeTileFileXYZ(
const QString &sourcePath,
QgsTileXYZ tileID,
const QgsTileMatrix &tileMatrix,
const QByteArray &tileData )
243 QFileInfo fi( filePath );
244 QDir fileDir = fi.dir();
245 if ( !fileDir.exists() )
247 if ( !fileDir.mkpath(
"." ) )
249 mErrorMessage = tr(
"Cannot create directory " ) + fileDir.path();
255 if ( !f.open( QIODevice::WriteOnly ) )
257 mErrorMessage = tr(
"Cannot open file for writing " ) + filePath;
267 QString QgsVectorTileWriter::mbtilesJsonSchema()
269 QVariantList arrayLayers;
270 for (
const Layer &layer : qgis::as_const( mLayers ) )
275 QVariantMap fieldsObj;
276 for (
const QgsField &field : fields )
278 QString fieldTypeStr;
279 if ( field.type() == QVariant::Bool )
280 fieldTypeStr = QStringLiteral(
"Boolean" );
281 else if ( field.type() == QVariant::Int || field.type() == QVariant::Double )
282 fieldTypeStr = QStringLiteral(
"Number" );
284 fieldTypeStr = QStringLiteral(
"String" );
286 fieldsObj[field.name()] = fieldTypeStr;
289 QVariantMap layerObj;
290 layerObj[
"id"] = vl->
name();
291 layerObj[
"fields"] = fieldsObj;
292 arrayLayers.append( layerObj );
296 rootObj[
"vector_layers"] = arrayLayers;