QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
qgsalgorithmdownloadvectortiles.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmdownloadvectortiles.cpp
3 ---------------------
4 begin : May 2023
5 copyright : (C) 2023 by Alexander Bruy
6 email : alexander dot bruy at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgsmbtiles.h"
21#include "qgsvectortileloader.h"
22#include "qgsvectortilelayer.h"
23#include "qgsziputils.h"
24
26
27class SetStylePostProcessor : public QgsProcessingLayerPostProcessorInterface
28{
29 public:
30
31 SetStylePostProcessor( QDomDocument &doc )
32 : mDocument( doc )
33 {}
34
36 {
37 if ( QgsVectorTileLayer *tileLayer = qobject_cast< QgsVectorTileLayer * >( layer ) )
38 {
39 QString errorMsg;
40 tileLayer->importNamedStyle( mDocument, errorMsg );
41 tileLayer->triggerRepaint();
42 }
43 }
44
45 private:
46
47 QDomDocument mDocument;
48};
49
50QString QgsDownloadVectorTilesAlgorithm::name() const
51{
52 return QStringLiteral( "downloadvectortiles" );
53}
54
55QString QgsDownloadVectorTilesAlgorithm::displayName() const
56{
57 return QObject::tr( "Download vector tiles" );
58}
59
60QStringList QgsDownloadVectorTilesAlgorithm::tags() const
61{
62 return QObject::tr( "vector,split,field,unique" ).split( ',' );
63}
64
65QString QgsDownloadVectorTilesAlgorithm::group() const
66{
67 return QObject::tr( "Vector tiles" );
68}
69
70QString QgsDownloadVectorTilesAlgorithm::groupId() const
71{
72 return QStringLiteral( "vectortiles" );
73}
74
75QString QgsDownloadVectorTilesAlgorithm::shortHelpString() const
76{
77 return QObject::tr( "Downloads vector tiles of the input vector tile layer and saves them in the local vector tile file." );
78}
79
80QgsDownloadVectorTilesAlgorithm *QgsDownloadVectorTilesAlgorithm::createInstance() const
81{
82 return new QgsDownloadVectorTilesAlgorithm();
83}
84
85void QgsDownloadVectorTilesAlgorithm::initAlgorithm( const QVariantMap & )
86{
87 addParameter( new QgsProcessingParameterMapLayer( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QVariant(), false, QList<int>() << static_cast< int >( Qgis::ProcessingSourceType::VectorTile ) ) );
88 addParameter( new QgsProcessingParameterExtent( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ) ) );
89 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "MAX_ZOOM" ), QObject::tr( "Maximum zoom level to download" ), Qgis::ProcessingNumberParameterType::Integer, 10, false, 0 ) );
90 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "TILE_LIMIT" ), QObject::tr( "Tile limit" ), Qgis::ProcessingNumberParameterType::Integer, 100, false, 0 ) );
91 addParameter( new QgsProcessingParameterVectorTileDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Output" ) ) );
92}
93
94bool QgsDownloadVectorTilesAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
95{
96 QgsMapLayer *layer = parameterAsLayer( parameters, QStringLiteral( "INPUT" ), context );
97 if ( !layer )
98 throw QgsProcessingException( QObject::tr( "Invalid input layer" ) );
99
100 QgsVectorTileLayer *vtLayer = qobject_cast< QgsVectorTileLayer * >( layer );
101 mProvider.reset( qgis::down_cast< const QgsVectorTileDataProvider * >( vtLayer->dataProvider() )->clone() );
102 mTileMatrixSet = vtLayer->tileMatrixSet();
103 mSourceMinZoom = vtLayer->sourceMinZoom();
104 mLayerName = vtLayer->name();
105
106 mExtent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, layer->crs() );
107
108 mMaxZoom = parameterAsInt( parameters, QStringLiteral( "MAX_ZOOM" ), context );
109 if ( mMaxZoom > vtLayer->sourceMaxZoom() )
110 {
111 throw QgsProcessingException( QObject::tr( "Requested maximum zoom level is bigger than available zoom level in the source layer. Please, select zoom level lower or equal to %1." ).arg( vtLayer->sourceMaxZoom() ) );
112 }
113
114 mTileLimit = static_cast< long long >( parameterAsInt( parameters, QStringLiteral( "TILE_LIMIT" ), context ) );
115
116 mStyleDocument = QDomDocument( QStringLiteral( "qgis" ) );
117 QString errorMsg;
118 vtLayer->exportNamedStyle( mStyleDocument, errorMsg );
119 if ( !errorMsg.isEmpty() )
120 {
121 feedback->pushWarning( QObject::tr( "Failed to get layer style: %1" ).arg( errorMsg ) );
122 }
123
124 return true;
125}
126
127QVariantMap QgsDownloadVectorTilesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
128{
129 const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
130
131 // count total number of tiles in the requested extent and zoom levels to see if it exceeds the tile limit
132 long long tileCount = 0;
133 QMap<int, QgsTileRange> tileRanges;
134 for ( int i = 0; i <= mMaxZoom; i++ )
135 {
136 QgsTileMatrix tileMatrix = mTileMatrixSet.tileMatrix( i );
137 QgsTileRange tileRange = tileMatrix.tileRangeFromExtent( mExtent );
138 tileRanges.insert( i, tileRange );
139 tileCount += static_cast< long long >( tileRange.endColumn() - tileRange.startColumn() + 1 ) * ( tileRange.endRow() - tileRange.startRow() + 1 );
140 }
141 if ( tileCount > mTileLimit )
142 {
143 throw QgsProcessingException( QObject::tr( "Requested number of tiles %1 exceeds limit of %2 tiles. Please, select a smaller extent, reduce maximum zoom level or increase tile limit." ).arg( tileCount ).arg( mTileLimit ) );
144 }
145
146 std::unique_ptr<QgsMbTiles> writer = std::make_unique<QgsMbTiles>( outputFile );
147 if ( !writer->create() )
148 {
149 throw QgsProcessingException( QObject::tr( "Failed to create MBTiles file %1" ).arg( outputFile ) );
150 }
151 writer->setMetadataValue( "format", "pbf" );
152 writer->setMetadataValue( "name", mLayerName );
153 writer->setMetadataValue( "minzoom", QString::number( mSourceMinZoom ) );
154 writer->setMetadataValue( "maxzoom", QString::number( mMaxZoom ) );
155 writer->setMetadataValue( "crs", mTileMatrixSet.rootMatrix().crs().authid() );
156 try
157 {
158 QgsCoordinateTransform ct( mTileMatrixSet.rootMatrix().crs(), QgsCoordinateReferenceSystem( "EPSG:4326" ), context.transformContext() );
159 ct.setBallparkTransformsAreAppropriate( true );
160 QgsRectangle wgsExtent = ct.transformBoundingBox( mExtent );
161 QString boundsStr = QString( "%1,%2,%3,%4" )
162 .arg( wgsExtent.xMinimum() ).arg( wgsExtent.yMinimum() )
163 .arg( wgsExtent.xMaximum() ).arg( wgsExtent.yMaximum() );
164 writer->setMetadataValue( "bounds", boundsStr );
165 }
166 catch ( const QgsCsException & )
167 {
168 // bounds won't be written (not a problem - it is an optional value)
169 }
170
171 QgsProcessingMultiStepFeedback multiStepFeedback( mMaxZoom + 1, feedback );
172
173 std::unique_ptr<QgsVectorTileLoader> loader;
174 QList<QgsVectorTileRawData> rawTiles;
175
176 QMap<int, QgsTileRange>::const_iterator it = tileRanges.constBegin();
177 while ( it != tileRanges.constEnd() )
178 {
179 if ( feedback->isCanceled() )
180 break;
181
182 multiStepFeedback.setCurrentStep( it.key() );
183
184 QgsTileMatrix tileMatrix = mTileMatrixSet.tileMatrix( it.key() );
185 tileCount = static_cast< long long >( it.value().endColumn() - it.value().startColumn() + 1 ) * ( it.value().endRow() - it.value().startRow() + 1 );
186
187 const QPointF viewCenter = tileMatrix.mapToTileCoordinates( mExtent.center() );
188
189 long long tileNumber = 0;
190 rawTiles = QgsVectorTileLoader::blockingFetchTileRawData( mProvider.get(), mTileMatrixSet, viewCenter, it.value(), it.key(), &multiStepFeedback, Qgis::RendererUsage::Export );
191 for ( const QgsVectorTileRawData &rawTile : std::as_const( rawTiles ) )
192 {
193 if ( feedback->isCanceled() )
194 break;
195
196 // TODO: at the moment, it handles single source only of tiles
197 // takes the first one
198 const QByteArray data = rawTile.data.first();
199
200 if ( !data.isEmpty() )
201 {
202 QByteArray gzipTileData;
203 QgsZipUtils::encodeGzip( data, gzipTileData );
204 int rowTMS = pow( 2, rawTile.id.zoomLevel() ) - rawTile.id.row() - 1;
205 writer->setTileData( rawTile.id.zoomLevel(), rawTile.id.column(), rowTMS, gzipTileData );
206 }
207
208 multiStepFeedback.setProgress( 100.0 * ( tileNumber++ ) / tileCount );
209 }
210
211 ++it;
212 }
213
214 if ( context.willLoadLayerOnCompletion( outputFile ) )
215 {
216 context.layerToLoadOnCompletionDetails( outputFile ).setPostProcessor( new SetStylePostProcessor( mStyleDocument ) );
217 }
218
219 QVariantMap results;
220 results.insert( QStringLiteral( "OUTPUT" ), outputFile );
221 return results;
222}
223
@ VectorTile
Vector tile layers.
@ Export
Renderer used for printing or exporting to a file.
This class represents a coordinate reference system (CRS).
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
Base class for all map layer types.
Definition qgsmaplayer.h:76
QString name
Definition qgsmaplayer.h:80
virtual void exportNamedStyle(QDomDocument &doc, QString &errorMsg, const QgsReadWriteContext &context=QgsReadWriteContext(), QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) const
Export the properties of this layer as named style in a QDomDocument.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
void setPostProcessor(QgsProcessingLayerPostProcessorInterface *processor)
Sets the layer post-processor.
Contains information about the context in which a processing algorithm is executed.
QgsProcessingContext::LayerDetails & layerToLoadOnCompletionDetails(const QString &layer)
Returns a reference to the details for a given layer which is loaded on completion of the algorithm o...
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
bool willLoadLayerOnCompletion(const QString &layer) const
Returns true if the given layer (by ID or datasource) will be loaded into the current project upon co...
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
An interface for layer post-processing handlers for execution following a processing algorithm operat...
virtual void postProcessLayer(QgsMapLayer *layer, QgsProcessingContext &context, QgsProcessingFeedback *feedback)=0
Post-processes the specified layer, following successful execution of a processing algorithm.
Processing feedback object for multi-step operations.
A rectangular map extent parameter for processing algorithms.
A map layer parameter for processing algorithms.
A numeric parameter for processing algorithms.
A vector tile layer destination parameter, for specifying the destination path for a vector tile laye...
A rectangle specified with double values.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Defines a matrix of tiles for a single zoom level: it is defined by its size (width *.
Definition qgstiles.h:136
QPointF mapToTileCoordinates(const QgsPointXY &mapPoint) const
Returns row/column coordinates (floating point number) from the given point in map coordinates.
Definition qgstiles.cpp:121
QgsTileRange tileRangeFromExtent(const QgsRectangle &mExtent) const
Returns tile range that fully covers the given extent.
Definition qgstiles.cpp:97
Range of tiles in a tile matrix to be rendered.
Definition qgstiles.h:99
int endColumn() const
Returns index of the last column in the range.
Definition qgstiles.h:111
int endRow() const
Returns index of the last row in the range.
Definition qgstiles.h:115
int startRow() const
Returns index of the first row in the range.
Definition qgstiles.h:113
int startColumn() const
Returns index of the first column in the range.
Definition qgstiles.h:109
Implements a map layer that is dedicated to rendering of vector tiles.
int sourceMinZoom() const
Returns minimum zoom level at which source has any valid tiles (negative = unconstrained)
int sourceMaxZoom() const
Returns maximum zoom level at which source has any valid tiles (negative = unconstrained)
QgsDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
QgsVectorTileMatrixSet & tileMatrixSet()
Returns the vector tile matrix set.
static QList< QgsVectorTileRawData > blockingFetchTileRawData(const QgsVectorTileDataProvider *provider, const QgsTileMatrixSet &tileMatrixSet, const QPointF &viewCenter, const QgsTileRange &range, int zoomLevel, QgsFeedback *feedback=nullptr, Qgis::RendererUsage usage=Qgis::RendererUsage::Unknown)
Returns raw tile data for the specified range of tiles. Blocks the caller until all tiles are fetched...
Keeps track of raw tile data from one or more sources that need to be decoded.
CORE_EXPORT bool encodeGzip(const QByteArray &bytesIn, QByteArray &bytesOut)
Encodes gzip byte stream, returns true on success.