36#include <QtConcurrent>
40QString QgsRasterizeAlgorithm::name()
const
42 return QStringLiteral(
"rasterize" );
45QString QgsRasterizeAlgorithm::displayName()
const
47 return QObject::tr(
"Convert map to raster" );
50QStringList QgsRasterizeAlgorithm::tags()
const
52 return QObject::tr(
"layer,raster,convert,file,map themes,tiles,render" ).split(
',' );
60QString QgsRasterizeAlgorithm::group()
const
62 return QObject::tr(
"Raster tools" );
65QString QgsRasterizeAlgorithm::groupId()
const
67 return QStringLiteral(
"rastertools" );
70void QgsRasterizeAlgorithm::initAlgorithm(
const QVariantMap & )
73 QStringLiteral(
"EXTENT" ),
74 QObject::tr(
"Minimum extent to render" )
77 QStringLiteral(
"EXTENT_BUFFER" ),
78 QObject::tr(
"Buffer around tiles in map units" ),
85 QStringLiteral(
"TILE_SIZE" ),
86 QObject::tr(
"Tile size" ),
93 QStringLiteral(
"MAP_UNITS_PER_PIXEL" ),
94 QObject::tr(
"Map units per pixel" ),
101 QStringLiteral(
"MAKE_BACKGROUND_TRANSPARENT" ),
102 QObject::tr(
"Make background transparent" ),
107 QStringLiteral(
"MAP_THEME" ),
108 QObject::tr(
"Map theme to render" ),
113 QStringLiteral(
"LAYERS" ),
114 QObject::tr(
"Layers to render" ),
120 QStringLiteral(
"OUTPUT" ),
121 QObject::tr(
"Output layer" )
125QString QgsRasterizeAlgorithm::shortDescription()
const
127 return QObject::tr(
"Renders the map canvas to a raster file." );
130QString QgsRasterizeAlgorithm::shortHelpString()
const
132 return QObject::tr(
"This algorithm rasterizes map canvas content.\n\n"
133 "A map theme can be selected to render a predetermined set of layers with a defined style for each layer. "
134 "Alternatively, a set of layers can be selected if no map theme is set. "
135 "If neither map theme nor layer is set, all the visible layers in the set extent will be rendered.\n\n"
136 "The minimum extent entered will internally be extended to a multiple of the tile size." );
139QgsRasterizeAlgorithm *QgsRasterizeAlgorithm::createInstance()
const
141 return new QgsRasterizeAlgorithm();
148 const QgsRectangle extent { parameterAsExtent( parameters, QStringLiteral(
"EXTENT" ), context, context.
project()->
crs() ) };
149 const int tileSize { parameterAsInt( parameters, QStringLiteral(
"TILE_SIZE" ), context ) };
150 const bool transparent { parameterAsBool( parameters, QStringLiteral(
"MAKE_BACKGROUND_TRANSPARENT" ), context ) };
151 const double mapUnitsPerPixel { parameterAsDouble( parameters, QStringLiteral(
"MAP_UNITS_PER_PIXEL" ), context ) };
152 const double extentBuffer { parameterAsDouble( parameters, QStringLiteral(
"EXTENT_BUFFER" ), context ) };
153 const QString outputLayerFileName { parameterAsOutputLayer( parameters, QStringLiteral(
"OUTPUT" ), context ) };
155 int xTileCount {
static_cast<int>( ceil( extent.width() / mapUnitsPerPixel / tileSize ) ) };
156 int yTileCount {
static_cast<int>( ceil( extent.height() / mapUnitsPerPixel / tileSize ) ) };
157 int width { xTileCount * tileSize };
158 int height { yTileCount * tileSize };
159 int nBands { transparent ? 4 : 3 };
161 int64_t totalTiles = 0;
162 for (
auto &layer : std::as_const( mMapLayers ) )
166 if (
QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( ( layer.get() ) ) )
168 const QList<double> resolutions = rasterLayer->dataProvider()->nativeResolutions();
169 if ( resolutions.isEmpty() )
174 if ( totalTiles == 0 )
180 extentLayer = ct.transform( extent );
188 const double mapUnitsPerPixelLayer = extentLayer.
width() / width;
190 for ( i = 0; i < resolutions.size() && resolutions.at( i ) < mapUnitsPerPixelLayer; i++ )
194 if ( i == resolutions.size() || ( i > 0 && resolutions.at( i ) - mapUnitsPerPixelLayer > mapUnitsPerPixelLayer - resolutions.at( i - 1 ) ) )
199 const int nbTilesWidth = std::ceil( extentLayer.
width() / resolutions.at( i ) / 256 );
200 const int nbTilesHeight = std::ceil( extentLayer.
height() / resolutions.at( i ) / 256 );
201 totalTiles =
static_cast<int64_t
>( nbTilesWidth ) * nbTilesHeight;
203 feedback->
pushInfo( QStringLiteral(
"%1" ).arg( totalTiles ) );
208 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() ) );
210 layer->deleteLater();
211 std::vector<std::unique_ptr<QgsMapLayer>>::iterator position = std::find( mMapLayers.begin(), mMapLayers.end(), layer );
212 if ( position != mMapLayers.end() )
214 mMapLayers.erase( position );
222 if ( driverName.isEmpty() )
227 GDALDriverH hOutputFileDriver = GDALGetDriverByName( driverName.toLocal8Bit().constData() );
228 if ( !hOutputFileDriver )
233 gdal::dataset_unique_ptr hOutputDataset( GDALCreate( hOutputFileDriver, outputLayerFileName.toUtf8().constData(), width, height, nBands, GDALDataType::GDT_Byte,
nullptr ) );
234 if ( !hOutputDataset )
240 double geoTransform[6];
241 geoTransform[0] = extent.xMinimum();
242 geoTransform[1] = mapUnitsPerPixel;
244 geoTransform[3] = extent.yMaximum();
246 geoTransform[5] = -mapUnitsPerPixel;
247 GDALSetGeoTransform( hOutputDataset.get(), geoTransform );
249 int red = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorRedPart", 255 );
250 int green = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorGreenPart", 255 );
251 int blue = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorBluePart", 255 );
256 bgColor = QColor( red, green, blue, 0 );
260 bgColor = QColor( red, green, blue );
275 QList<QgsMapLayer *> layers;
276 for (
const auto &lptr : mMapLayers )
278 layers.push_back( lptr.get() );
284 const double extentRatio { mapUnitsPerPixel * tileSize };
285 const int numTiles { xTileCount * yTileCount };
290 void operator()( uint8_t *ptr )
const
296 QAtomicInt rendered = 0;
297 QMutex rasterWriteLocker;
299 const auto renderJob = [&](
const int x,
const int y,
QgsMapSettings mapSettings ) {
300 QImage image { tileSize, tileSize, QImage::Format::Format_ARGB32 };
303 QPainter painter { &image };
308 image.fill( transparent ? bgColor.rgba() : bgColor.rgb() );
310 extent.xMinimum() + x * extentRatio,
311 extent.yMaximum() - ( y + 1 ) * extentRatio,
312 extent.xMinimum() + ( x + 1 ) * extentRatio,
313 extent.yMaximum() - y * extentRatio
317 job.waitForFinished();
320 if ( !hIntermediateDataset )
325 const int xOffset { x * tileSize };
326 const int yOffset { y * tileSize };
328 std::unique_ptr<uint8_t, CPLDelete> buffer(
static_cast<uint8_t *
>( CPLMalloc(
sizeof( uint8_t ) *
static_cast<size_t>( tileSize * tileSize * nBands ) ) ) );
329 CPLErr err = GDALDatasetRasterIO( hIntermediateDataset.get(), GF_Read, 0, 0, tileSize, tileSize, buffer.get(), tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
330 if ( err != CE_None )
336 QMutexLocker locker( &rasterWriteLocker );
337 err = GDALDatasetRasterIO( hOutputDataset.get(), GF_Write, xOffset, yOffset, tileSize, tileSize, buffer.get(), tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
339 feedback->
setProgress(
static_cast<double>( rendered ) / numTiles * 100.0 );
341 if ( err != CE_None )
349 std::vector<QFuture<void>> futures;
351 for (
int x = 0; x < xTileCount; ++x )
353 for (
int y = 0; y < yTileCount; ++y )
359 futures.push_back( QtConcurrent::run( renderJob, x, y, mapSettings ) );
363 for (
auto &f : futures )
368 return { { QStringLiteral(
"OUTPUT" ), outputLayerFileName } };
376 const QString mapTheme { parameterAsString( parameters, QStringLiteral(
"MAP_THEME" ), context ) };
377 const QList<QgsMapLayer *> mapLayers { parameterAsLayerList( parameters, QStringLiteral(
"LAYERS" ), context ) };
383 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
387 else if ( !mapLayers.isEmpty() )
389 for (
const QgsMapLayer *ml : std::as_const( mapLayers ) )
391 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
395 if ( mMapLayers.size() == 0 )
397 QList<QgsMapLayer *> layers;
402 if ( nodeLayer->isVisible() && root->
layerOrder().contains( layer ) )
406 for (
const QgsMapLayer *ml : std::as_const( layers ) )
408 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
411 return mMapLayers.size() > 0;
@ MapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer)
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.
@ UseAdvancedEffects
Enable layer opacity and blending effects.
@ HighQualityImageTransforms
Enable high quality image transformations, which results in better appearance of scaled or rotated ra...
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
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.
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.
The QgsMapSettings class contains configuration for rendering of the map.
void setLayers(const QList< QgsMapLayer * > &layers)
Sets the list of layers to render in the map.
void setOutputDpi(double dpi)
Sets the dpi (dots per inch) used for conversion between real world units (e.g.
void setOutputImageFormat(QImage::Format format)
sets format of internal QImage
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Sets the map of map layer style overrides (key: layer ID, value: style name) where a different style ...
void setExtent(const QgsRectangle &rect, bool magnified=true)
Sets the coordinates of the rectangle which should be rendered.
void setExtentBuffer(double buffer)
Sets the buffer in map units to use around the visible extent for rendering symbols whose correspondi...
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the coordinate transform context, which stores various information regarding which datum transfo...
void setOutputSize(QSize size)
Sets the size of the resulting map image, in pixels.
void setBackgroundColor(const QColor &color)
Sets the background color of the map.
void setFlag(Qgis::MapSettingsFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
void setDestinationCrs(const QgsCoordinateReferenceSystem &crs)
Sets the destination crs (coordinate reference system) for the map render.
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
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