39#include <QtConcurrentRun>
41using namespace Qt::StringLiterals;
45QString QgsRasterizeAlgorithm::name()
const
47 return u
"rasterize"_s;
50QString QgsRasterizeAlgorithm::displayName()
const
52 return QObject::tr(
"Convert map to raster" );
55QStringList QgsRasterizeAlgorithm::tags()
const
57 return QObject::tr(
"layer,raster,convert,file,map themes,tiles,render" ).split(
',' );
65QString QgsRasterizeAlgorithm::group()
const
67 return QObject::tr(
"Raster tools" );
70QString QgsRasterizeAlgorithm::groupId()
const
72 return u
"rastertools"_s;
75void QgsRasterizeAlgorithm::initAlgorithm(
const QVariantMap & )
79 QObject::tr(
"Minimum extent to render" )
83 QObject::tr(
"Buffer around tiles in map units" ),
91 QObject::tr(
"Tile size" ),
98 u
"MAP_UNITS_PER_PIXEL"_s,
99 QObject::tr(
"Map units per pixel" ),
106 u
"MAKE_BACKGROUND_TRANSPARENT"_s,
107 QObject::tr(
"Make background transparent" ),
113 QObject::tr(
"Map theme to render" ),
119 QObject::tr(
"Layers to render" ),
126 QObject::tr(
"Output layer" )
130QString QgsRasterizeAlgorithm::shortDescription()
const
132 return QObject::tr(
"Renders the map canvas to a raster file." );
135QString QgsRasterizeAlgorithm::shortHelpString()
const
137 return QObject::tr(
"This algorithm rasterizes map canvas content.\n\n"
138 "A map theme can be selected to render a predetermined set of layers with a defined style for each layer. "
139 "Alternatively, a set of layers can be selected if no map theme is set. "
140 "If neither map theme nor layer is set, all the visible layers in the set extent will be rendered.\n\n"
141 "The minimum extent entered will internally be extended to a multiple of the tile size." );
144QgsRasterizeAlgorithm *QgsRasterizeAlgorithm::createInstance()
const
146 return new QgsRasterizeAlgorithm();
153 const QgsRectangle extent { parameterAsExtent( parameters, u
"EXTENT"_s, context, mCrs ) };
154 const int tileSize { parameterAsInt( parameters, u
"TILE_SIZE"_s, context ) };
159 const bool transparent { parameterAsBool( parameters, u
"MAKE_BACKGROUND_TRANSPARENT"_s, context ) };
160 const double mapUnitsPerPixel { parameterAsDouble( parameters, u
"MAP_UNITS_PER_PIXEL"_s, context ) };
161 if ( mapUnitsPerPixel <= 0 )
165 const double extentBuffer { parameterAsDouble( parameters, u
"EXTENT_BUFFER"_s, context ) };
166 const QString outputLayerFileName { parameterAsOutputLayer( parameters, u
"OUTPUT"_s, context ) };
168 int xTileCount {
static_cast<int>( ceil( extent.
width() / mapUnitsPerPixel / tileSize ) ) };
169 int yTileCount {
static_cast<int>( ceil( extent.
height() / mapUnitsPerPixel / tileSize ) ) };
170 int width { xTileCount * tileSize };
171 int height { yTileCount * tileSize };
172 int nBands { transparent ? 4 : 3 };
174 int64_t totalTiles = 0;
175 for (
auto &layer : std::as_const( mMapLayers ) )
179 if (
QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( ( layer.get() ) ) )
181 const QList<double> resolutions = rasterLayer->dataProvider()->nativeResolutions();
182 if ( resolutions.isEmpty() )
187 if ( totalTiles == 0 )
193 extentLayer = ct.transform( extent );
201 const double mapUnitsPerPixelLayer = extentLayer.
width() / width;
203 for ( i = 0; i < resolutions.size() && resolutions.at( i ) < mapUnitsPerPixelLayer; i++ )
207 if ( i == resolutions.size() || ( i > 0 && resolutions.at( i ) - mapUnitsPerPixelLayer > mapUnitsPerPixelLayer - resolutions.at( i - 1 ) ) )
212 const int nbTilesWidth = std::ceil( extentLayer.
width() / resolutions.at( i ) / 256 );
213 const int nbTilesHeight = std::ceil( extentLayer.
height() / resolutions.at( i ) / 256 );
214 totalTiles =
static_cast<int64_t
>( nbTilesWidth ) * nbTilesHeight;
216 feedback->
pushInfo( u
"%1"_s.arg( totalTiles ) );
221 feedback->
pushFormattedMessage( QObject::tr(
"Layer %1 will be skipped as the algorithm leads to bulk downloading behavior which is prohibited by the %2OpenStreetMap Foundation tile usage policy%3" ).arg( rasterLayer->name(), u
"<a href=\"https://operations.osmfoundation.org/policies/tiles/\">"_s, u
"</a>"_s ), QObject::tr(
"Layer %1 will be skipped as the algorithm leads to bulk downloading behavior which is prohibited by the %2OpenStreetMap Foundation tile usage policy%3" ).arg( rasterLayer->name(), QString(), QString() ) );
223 layer->deleteLater();
224 std::vector<std::unique_ptr<QgsMapLayer>>::iterator position = std::find( mMapLayers.begin(), mMapLayers.end(), layer );
225 if ( position != mMapLayers.end() )
227 mMapLayers.erase( position );
234 const QString driverName = parameterAsOutputRasterFormat( parameters, u
"OUTPUT"_s, context );
235 if ( driverName.isEmpty() )
240 GDALDriverH hOutputFileDriver = GDALGetDriverByName( driverName.toLocal8Bit().constData() );
241 if ( !hOutputFileDriver )
246 gdal::dataset_unique_ptr hOutputDataset( GDALCreate( hOutputFileDriver, outputLayerFileName.toUtf8().constData(), width, height, nBands, GDALDataType::GDT_Byte,
nullptr ) );
247 if ( !hOutputDataset )
252#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
253 const bool hasReportsDuringClose = GDALDatasetGetCloseReportsProgress( hOutputDataset.get() );
254 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
256 constexpr double maxProgressDuringBlockWriting = 100.0;
260 double geoTransform[6];
261 geoTransform[0] = extent.
xMinimum();
262 geoTransform[1] = mapUnitsPerPixel;
264 geoTransform[3] = extent.
yMaximum();
266 geoTransform[5] = -mapUnitsPerPixel;
267 GDALSetGeoTransform( hOutputDataset.get(), geoTransform );
269 mMapSettings.setOutputImageFormat( QImage::Format_ARGB32 );
270 mMapSettings.setDestinationCrs( mCrs );
276 mMapSettings.setExtentBuffer( extentBuffer );
279 QList<QgsMapLayer *> layers;
280 for (
const auto &lptr : mMapLayers )
282 layers.push_back( lptr.get() );
284 mMapSettings.setLayers( layers );
285 mMapSettings.setLayerStyleOverrides( mMapThemeStyleOverrides );
288 const double extentRatio { mapUnitsPerPixel * tileSize };
289 const int numTiles { xTileCount * yTileCount };
294 void operator()( uint8_t *ptr )
const
300 QAtomicInt rendered = 0;
301 QMutex rasterWriteLocker;
303 const auto renderJob = [&](
const int x,
const int y,
QgsMapSettings mapSettings ) {
304 QImage image { tileSize, tileSize, QImage::Format::Format_ARGB32 };
305 mapSettings.setOutputDpi( image.logicalDpiX() );
306 mapSettings.setOutputSize( image.size() );
307 QPainter painter { &image };
312 image.fill( transparent ? mapSettings.backgroundColor().rgba() : mapSettings.backgroundColor().rgb() );
314 extent.
xMinimum() + x * extentRatio,
315 extent.
yMaximum() - ( y + 1 ) * extentRatio,
316 extent.
xMinimum() + ( x + 1 ) * extentRatio,
321 job.waitForFinished();
324 if ( !hIntermediateDataset )
329 const int xOffset { x * tileSize };
330 const int yOffset { y * tileSize };
332 std::unique_ptr<uint8_t, CPLDelete> buffer(
static_cast<uint8_t *
>( CPLMalloc(
sizeof( uint8_t ) *
static_cast<size_t>( tileSize * tileSize * nBands ) ) ) );
333 CPLErr err = GDALDatasetRasterIO( hIntermediateDataset.get(), GF_Read, 0, 0, tileSize, tileSize, buffer.get(), tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
334 if ( err != CE_None )
340 QMutexLocker locker( &rasterWriteLocker );
341 err = GDALDatasetRasterIO( hOutputDataset.get(), GF_Write, xOffset, yOffset, tileSize, tileSize, buffer.get(), tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
343 feedback->
setProgress(
static_cast<double>( rendered ) / numTiles * maxProgressDuringBlockWriting );
345 if ( err != CE_None )
353 std::vector<QFuture<void>> futures;
355 for (
int x = 0; x < xTileCount; ++x )
357 for (
int y = 0; y < yTileCount; ++y )
363 futures.push_back( QtConcurrent::run( renderJob, x, y, mMapSettings ) );
367 for (
auto &f : futures )
372#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
373 if ( hasReportsDuringClose )
376 if ( GDALDatasetRunCloseWithoutDestroyingEx(
388 return { { u
"OUTPUT"_s, outputLayerFileName } };
396 const QString mapTheme { parameterAsString( parameters, u
"MAP_THEME"_s, context ) };
397 const QList<QgsMapLayer *> mapLayers { parameterAsLayerList( parameters, u
"LAYERS"_s, context ) };
403 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
407 else if ( !mapLayers.isEmpty() )
409 for (
const QgsMapLayer *ml : std::as_const( mapLayers ) )
411 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
415 if ( mMapLayers.size() == 0 )
417 QList<QgsMapLayer *> layers;
422 if ( nodeLayer->isVisible() && root->
layerOrder().contains( layer ) )
426 for (
const QgsMapLayer *ml : std::as_const( layers ) )
428 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
438 const bool transparent { parameterAsBool( parameters, u
"MAKE_BACKGROUND_TRANSPARENT"_s, context ) };
442 bgColor = QColor( red, green, blue, 0 );
446 bgColor = QColor( red, green, blue );
448 mMapSettings.setBackgroundColor( bgColor );
452 return mMapLayers.size() > 0;
@ MapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer).
@ Default
Allow raster-based rendering in situations where it is required for correct rendering or where it wil...
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
@ RequiresProject
The algorithm requires that a valid QgsProject is available from the processing context in order to e...
@ Double
Double/float values.
@ RenderMapTile
Draw map such that there are no problems between adjacent tiles.
@ Antialiasing
Enable anti-aliasing for map rendering.
@ HighQualityImageTransforms
Enable high quality image transformations, which results in better appearance of scaled or rotated ra...
Custom exception class for Coordinate Reference System related exceptions.
bool isCanceled() const
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
Utility class to map from GDALProgressFunc to QgsFeedback.
static int CPL_STDCALL progressCallback(double dfComplete, const char *pszMessage, void *pProgressArg)
GDAL progress callback.
static gdal::dataset_unique_ptr imageToMemoryDataset(const QImage &image)
Converts an image to a GDAL memory dataset by borrowing image data.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Layer tree node points to a map layer.
Namespace with helper functions for layer tree operations.
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
static bool isOpenStreetMapLayer(QgsMapLayer *layer)
Returns true if the layer is served by OpenStreetMap server.
Base class for all map layer types.
Job implementation that renders everything sequentially using a custom painter.
Contains configuration for rendering maps.
bool hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
QList< QgsMapLayer * > mapThemeVisibleLayers(const QString &name) const
Returns the list of layers that are visible for the specified map theme.
QMap< QString, QString > mapThemeStyleOverrides(const QString &name)
Gets layer style overrides (for QgsMapSettings) of the visible layers for given map theme.
virtual Qgis::ProcessingAlgorithmFlags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
QgsProject * project() const
Returns the project in which the algorithm is being executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
virtual void pushFormattedMessage(const QString &html, const QString &text)
Pushes a pre-formatted message from the algorithm.
A boolean parameter for processing algorithms.
A rectangular map extent parameter for processing algorithms.
A map theme parameter for processing algorithms, allowing users to select an existing map theme from ...
A parameter for processing algorithms which accepts multiple map layers.
A numeric parameter for processing algorithms.
A raster layer destination parameter, for specifying the destination path for a raster layer created ...
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
Reads an integer from the specified scope and key.
QgsMapThemeCollection * mapThemeCollection
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
QgsCoordinateReferenceSystem crs
Qgis::ScaleCalculationMethod scaleMethod
Represents a raster layer.
A rectangle specified with double values.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
#define MAXIMUM_OPENSTREETMAP_TILES_FETCH