38#include <QtConcurrent>
42QString QgsRasterizeAlgorithm::name()
const
44 return QStringLiteral(
"rasterize" );
47QString QgsRasterizeAlgorithm::displayName()
const
49 return QObject::tr(
"Convert map to raster" );
52QStringList QgsRasterizeAlgorithm::tags()
const
54 return QObject::tr(
"layer,raster,convert,file,map themes,tiles,render" ).split(
',' );
62QString QgsRasterizeAlgorithm::group()
const
64 return QObject::tr(
"Raster tools" );
67QString QgsRasterizeAlgorithm::groupId()
const
69 return QStringLiteral(
"rastertools" );
72void QgsRasterizeAlgorithm::initAlgorithm(
const QVariantMap & )
75 QStringLiteral(
"EXTENT" ),
76 QObject::tr(
"Minimum extent to render" )
79 QStringLiteral(
"EXTENT_BUFFER" ),
80 QObject::tr(
"Buffer around tiles in map units" ),
87 QStringLiteral(
"TILE_SIZE" ),
88 QObject::tr(
"Tile size" ),
95 QStringLiteral(
"MAP_UNITS_PER_PIXEL" ),
96 QObject::tr(
"Map units per pixel" ),
103 QStringLiteral(
"MAKE_BACKGROUND_TRANSPARENT" ),
104 QObject::tr(
"Make background transparent" ),
109 QStringLiteral(
"MAP_THEME" ),
110 QObject::tr(
"Map theme to render" ),
115 QStringLiteral(
"LAYERS" ),
116 QObject::tr(
"Layers to render" ),
122 QStringLiteral(
"OUTPUT" ),
123 QObject::tr(
"Output layer" )
127QString QgsRasterizeAlgorithm::shortDescription()
const
129 return QObject::tr(
"Renders the map canvas to a raster file." );
132QString QgsRasterizeAlgorithm::shortHelpString()
const
134 return QObject::tr(
"This algorithm rasterizes map canvas content.\n\n"
135 "A map theme can be selected to render a predetermined set of layers with a defined style for each layer. "
136 "Alternatively, a set of layers can be selected if no map theme is set. "
137 "If neither map theme nor layer is set, all the visible layers in the set extent will be rendered.\n\n"
138 "The minimum extent entered will internally be extended to a multiple of the tile size." );
141QgsRasterizeAlgorithm *QgsRasterizeAlgorithm::createInstance()
const
143 return new QgsRasterizeAlgorithm();
150 const QgsRectangle extent { parameterAsExtent( parameters, QStringLiteral(
"EXTENT" ), context, mCrs ) };
151 const int tileSize { parameterAsInt( parameters, QStringLiteral(
"TILE_SIZE" ), context ) };
156 const bool transparent { parameterAsBool( parameters, QStringLiteral(
"MAKE_BACKGROUND_TRANSPARENT" ), context ) };
157 const double mapUnitsPerPixel { parameterAsDouble( parameters, QStringLiteral(
"MAP_UNITS_PER_PIXEL" ), context ) };
158 if ( mapUnitsPerPixel <= 0 )
162 const double extentBuffer { parameterAsDouble( parameters, QStringLiteral(
"EXTENT_BUFFER" ), context ) };
163 const QString outputLayerFileName { parameterAsOutputLayer( parameters, QStringLiteral(
"OUTPUT" ), context ) };
165 int xTileCount {
static_cast<int>( ceil( extent.
width() / mapUnitsPerPixel / tileSize ) ) };
166 int yTileCount {
static_cast<int>( ceil( extent.
height() / mapUnitsPerPixel / tileSize ) ) };
167 int width { xTileCount * tileSize };
168 int height { yTileCount * tileSize };
169 int nBands { transparent ? 4 : 3 };
171 int64_t totalTiles = 0;
172 for (
auto &layer : std::as_const( mMapLayers ) )
176 if (
QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( ( layer.get() ) ) )
178 const QList<double> resolutions = rasterLayer->dataProvider()->nativeResolutions();
179 if ( resolutions.isEmpty() )
184 if ( totalTiles == 0 )
190 extentLayer = ct.transform( extent );
198 const double mapUnitsPerPixelLayer = extentLayer.
width() / width;
200 for ( i = 0; i < resolutions.size() && resolutions.at( i ) < mapUnitsPerPixelLayer; i++ )
204 if ( i == resolutions.size() || ( i > 0 && resolutions.at( i ) - mapUnitsPerPixelLayer > mapUnitsPerPixelLayer - resolutions.at( i - 1 ) ) )
209 const int nbTilesWidth = std::ceil( extentLayer.
width() / resolutions.at( i ) / 256 );
210 const int nbTilesHeight = std::ceil( extentLayer.
height() / resolutions.at( i ) / 256 );
211 totalTiles =
static_cast<int64_t
>( nbTilesWidth ) * nbTilesHeight;
213 feedback->
pushInfo( QStringLiteral(
"%1" ).arg( totalTiles ) );
218 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(), QStringLiteral(
"<a href=\"https://operations.osmfoundation.org/policies/tiles/\">" ), QStringLiteral(
"</a>" ) ), 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() ) );
220 layer->deleteLater();
221 std::vector<std::unique_ptr<QgsMapLayer>>::iterator position = std::find( mMapLayers.begin(), mMapLayers.end(), layer );
222 if ( position != mMapLayers.end() )
224 mMapLayers.erase( position );
232 if ( driverName.isEmpty() )
237 GDALDriverH hOutputFileDriver = GDALGetDriverByName( driverName.toLocal8Bit().constData() );
238 if ( !hOutputFileDriver )
243 gdal::dataset_unique_ptr hOutputDataset( GDALCreate( hOutputFileDriver, outputLayerFileName.toUtf8().constData(), width, height, nBands, GDALDataType::GDT_Byte,
nullptr ) );
244 if ( !hOutputDataset )
250 double geoTransform[6];
251 geoTransform[0] = extent.
xMinimum();
252 geoTransform[1] = mapUnitsPerPixel;
254 geoTransform[3] = extent.
yMaximum();
256 geoTransform[5] = -mapUnitsPerPixel;
257 GDALSetGeoTransform( hOutputDataset.get(), geoTransform );
259 mMapSettings.setOutputImageFormat( QImage::Format_ARGB32 );
260 mMapSettings.setDestinationCrs( mCrs );
266 mMapSettings.setExtentBuffer( extentBuffer );
269 QList<QgsMapLayer *> layers;
270 for (
const auto &lptr : mMapLayers )
272 layers.push_back( lptr.get() );
274 mMapSettings.setLayers( layers );
275 mMapSettings.setLayerStyleOverrides( mMapThemeStyleOverrides );
278 const double extentRatio { mapUnitsPerPixel * tileSize };
279 const int numTiles { xTileCount * yTileCount };
284 void operator()( uint8_t *ptr )
const
290 QAtomicInt rendered = 0;
291 QMutex rasterWriteLocker;
293 const auto renderJob = [&](
const int x,
const int y,
QgsMapSettings mapSettings ) {
294 QImage image { tileSize, tileSize, QImage::Format::Format_ARGB32 };
295 mapSettings.setOutputDpi( image.logicalDpiX() );
296 mapSettings.setOutputSize( image.size() );
297 QPainter painter { &image };
302 image.fill( transparent ? mapSettings.backgroundColor().rgba() : mapSettings.backgroundColor().rgb() );
304 extent.
xMinimum() + x * extentRatio,
305 extent.
yMaximum() - ( y + 1 ) * extentRatio,
306 extent.
xMinimum() + ( x + 1 ) * extentRatio,
311 job.waitForFinished();
314 if ( !hIntermediateDataset )
319 const int xOffset { x * tileSize };
320 const int yOffset { y * tileSize };
322 std::unique_ptr<uint8_t, CPLDelete> buffer(
static_cast<uint8_t *
>( CPLMalloc(
sizeof( uint8_t ) *
static_cast<size_t>( tileSize * tileSize * nBands ) ) ) );
323 CPLErr err = GDALDatasetRasterIO( hIntermediateDataset.get(), GF_Read, 0, 0, tileSize, tileSize, buffer.get(), tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
324 if ( err != CE_None )
330 QMutexLocker locker( &rasterWriteLocker );
331 err = GDALDatasetRasterIO( hOutputDataset.get(), GF_Write, xOffset, yOffset, tileSize, tileSize, buffer.get(), tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
333 feedback->
setProgress(
static_cast<double>( rendered ) / numTiles * 100.0 );
335 if ( err != CE_None )
343 std::vector<QFuture<void>> futures;
345 for (
int x = 0; x < xTileCount; ++x )
347 for (
int y = 0; y < yTileCount; ++y )
353 futures.push_back( QtConcurrent::run( renderJob, x, y, mMapSettings ) );
357 for (
auto &f : futures )
362 return { { QStringLiteral(
"OUTPUT" ), outputLayerFileName } };
370 const QString mapTheme { parameterAsString( parameters, QStringLiteral(
"MAP_THEME" ), context ) };
371 const QList<QgsMapLayer *> mapLayers { parameterAsLayerList( parameters, QStringLiteral(
"LAYERS" ), context ) };
377 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
381 else if ( !mapLayers.isEmpty() )
383 for (
const QgsMapLayer *ml : std::as_const( mapLayers ) )
385 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
389 if ( mMapLayers.size() == 0 )
391 QList<QgsMapLayer *> layers;
396 if ( nodeLayer->isVisible() && root->
layerOrder().contains( layer ) )
400 for (
const QgsMapLayer *ml : std::as_const( layers ) )
402 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
408 int red = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorRedPart", 255 );
409 int green = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorGreenPart", 255 );
410 int blue = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorBluePart", 255 );
412 const bool transparent { parameterAsBool( parameters, QStringLiteral(
"MAKE_BACKGROUND_TRANSPARENT" ), context ) };
416 bgColor = QColor( red, green, blue, 0 );
420 bgColor = QColor( red, green, blue );
422 mMapSettings.setBackgroundColor( bgColor );
426 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.
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
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
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