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 & )
89QString QgsRasterizeAlgorithm::shortDescription()
const
91 return QObject::tr(
"Renders the map canvas to a raster file." );
94QString QgsRasterizeAlgorithm::shortHelpString()
const
97 "This algorithm rasterizes map canvas content.\n\n"
98 "A map theme can be selected to render a predetermined set of layers with a defined style for each layer. "
99 "Alternatively, a set of layers can be selected if no map theme is set. "
100 "If neither map theme nor layer is set, all the visible layers in the set extent will be rendered.\n\n"
101 "The minimum extent entered will internally be extended to a multiple of the tile size."
105QgsRasterizeAlgorithm *QgsRasterizeAlgorithm::createInstance()
const
107 return new QgsRasterizeAlgorithm();
114 const QgsRectangle extent { parameterAsExtent( parameters, u
"EXTENT"_s, context, mCrs ) };
115 const int tileSize { parameterAsInt( parameters, u
"TILE_SIZE"_s, context ) };
120 const bool transparent { parameterAsBool( parameters, u
"MAKE_BACKGROUND_TRANSPARENT"_s, context ) };
121 const double mapUnitsPerPixel { parameterAsDouble( parameters, u
"MAP_UNITS_PER_PIXEL"_s, context ) };
122 if ( mapUnitsPerPixel <= 0 )
126 const double extentBuffer { parameterAsDouble( parameters, u
"EXTENT_BUFFER"_s, context ) };
127 const QString outputLayerFileName { parameterAsOutputLayer( parameters, u
"OUTPUT"_s, context ) };
129 int xTileCount {
static_cast<int>( ceil( extent.
width() / mapUnitsPerPixel / tileSize ) ) };
130 int yTileCount {
static_cast<int>( ceil( extent.
height() / mapUnitsPerPixel / tileSize ) ) };
131 int width { xTileCount * tileSize };
132 int height { yTileCount * tileSize };
133 int nBands { transparent ? 4 : 3 };
135 int64_t totalTiles = 0;
136 for (
auto &layer : std::as_const( mMapLayers ) )
140 if (
QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( ( layer.get() ) ) )
142 const QList<double> resolutions = rasterLayer->dataProvider()->nativeResolutions();
143 if ( resolutions.isEmpty() )
148 if ( totalTiles == 0 )
154 extentLayer = ct.transform( extent );
162 const double mapUnitsPerPixelLayer = extentLayer.
width() / width;
164 for ( i = 0; i < resolutions.size() && resolutions.at( i ) < mapUnitsPerPixelLayer; i++ )
168 if ( i == resolutions.size() || ( i > 0 && resolutions.at( i ) - mapUnitsPerPixelLayer > mapUnitsPerPixelLayer - resolutions.at( i - 1 ) ) )
173 const int nbTilesWidth = std::ceil( extentLayer.
width() / resolutions.at( i ) / 256 );
174 const int nbTilesHeight = std::ceil( extentLayer.
height() / resolutions.at( i ) / 256 );
175 totalTiles =
static_cast<int64_t
>( nbTilesWidth ) * nbTilesHeight;
177 feedback->
pushInfo( u
"%1"_s.arg( totalTiles ) );
183 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" )
184 .arg( rasterLayer->name(), u
"<a href=\"https://operations.osmfoundation.org/policies/tiles/\">"_s, u
"</a>"_s ),
185 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" )
186 .arg( rasterLayer->name(), QString(), QString() )
189 layer->deleteLater();
190 std::vector<std::unique_ptr<QgsMapLayer>>::iterator position = std::find( mMapLayers.begin(), mMapLayers.end(), layer );
191 if ( position != mMapLayers.end() )
193 mMapLayers.erase( position );
200 const QString driverName = parameterAsOutputRasterFormat( parameters, u
"OUTPUT"_s, context );
201 if ( driverName.isEmpty() )
206 GDALDriverH hOutputFileDriver = GDALGetDriverByName( driverName.toLocal8Bit().constData() );
207 if ( !hOutputFileDriver )
212 gdal::dataset_unique_ptr hOutputDataset( GDALCreate( hOutputFileDriver, outputLayerFileName.toUtf8().constData(), width, height, nBands, GDALDataType::GDT_Byte,
nullptr ) );
213 if ( !hOutputDataset )
218#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
219 const bool hasReportsDuringClose = GDALDatasetGetCloseReportsProgress( hOutputDataset.get() );
220 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
222 constexpr double maxProgressDuringBlockWriting = 100.0;
226 double geoTransform[6];
227 geoTransform[0] = extent.
xMinimum();
228 geoTransform[1] = mapUnitsPerPixel;
230 geoTransform[3] = extent.
yMaximum();
232 geoTransform[5] = -mapUnitsPerPixel;
233 GDALSetGeoTransform( hOutputDataset.get(), geoTransform );
236 mMapSettings.setOutputImageFormat( QImage::Format_ARGB32 );
237 mMapSettings.setDestinationCrs( mCrs );
243 mMapSettings.setEllipsoid( context.
ellipsoid() );
244 mMapSettings.setExtentBuffer( extentBuffer );
247 QList<QgsMapLayer *> layers;
248 for (
const auto &lptr : mMapLayers )
250 layers.push_back( lptr.get() );
252 mMapSettings.setLayers( layers );
253 mMapSettings.setLayerStyleOverrides( mMapThemeStyleOverrides );
256 const double extentRatio { mapUnitsPerPixel * tileSize };
257 const int numTiles { xTileCount * yTileCount };
262 void operator()( uint8_t *ptr )
const { CPLFree( ptr ); }
265 QAtomicInt rendered = 0;
266 QMutex rasterWriteLocker;
268 const auto renderJob = [&](
const int x,
const int y,
QgsMapSettings mapSettings ) {
269 QImage image { tileSize, tileSize, QImage::Format::Format_ARGB32 };
270 mapSettings.setOutputDpi( image.logicalDpiX() );
271 mapSettings.setOutputSize( image.size() );
272 QPainter painter { &image };
277 image.fill( transparent ? mapSettings.backgroundColor().rgba() : mapSettings.backgroundColor().rgb() );
278 mapSettings.setExtent(
283 job.waitForFinished();
286 if ( !hIntermediateDataset )
291 const int xOffset { x * tileSize };
292 const int yOffset { y * tileSize };
294 std::unique_ptr<uint8_t, CPLDelete> buffer(
static_cast<uint8_t *
>( CPLMalloc(
sizeof( uint8_t ) *
static_cast<size_t>( tileSize * tileSize * nBands ) ) ) );
295 CPLErr err = GDALDatasetRasterIO( hIntermediateDataset.get(), GF_Read, 0, 0, tileSize, tileSize, buffer.get(), tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
296 if ( err != CE_None )
302 QMutexLocker locker( &rasterWriteLocker );
303 err = GDALDatasetRasterIO( hOutputDataset.get(), GF_Write, xOffset, yOffset, tileSize, tileSize, buffer.get(), tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
305 feedback->
setProgress(
static_cast<double>( rendered ) / numTiles * maxProgressDuringBlockWriting );
307 if ( err != CE_None )
315 std::vector<QFuture<void>> futures;
317 for (
int x = 0; x < xTileCount; ++x )
319 for (
int y = 0; y < yTileCount; ++y )
325 futures.push_back( QtConcurrent::run( renderJob, x, y, mMapSettings ) );
329 for (
auto &f : futures )
334#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
335 if ( hasReportsDuringClose )
347 return { { u
"OUTPUT"_s, outputLayerFileName } };
356 const QString mapTheme { parameterAsString( parameters, u
"MAP_THEME"_s, context ) };
357 const QList<QgsMapLayer *> mapLayers { parameterAsLayerList( parameters, u
"LAYERS"_s, context ) };
363 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
367 else if ( !mapLayers.isEmpty() )
369 for (
const QgsMapLayer *ml : std::as_const( mapLayers ) )
371 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
375 if ( mMapLayers.empty() && project )
377 QList<QgsMapLayer *> layers;
382 if ( nodeLayer->isVisible() && root->
layerOrder().contains( layer ) )
386 for (
const QgsMapLayer *ml : std::as_const( layers ) )
388 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
394 int red = project ? project->
readNumEntry( u
"Gui"_s,
"/CanvasColorRedPart", 255 ) : 255;
395 int green = project ? project->
readNumEntry( u
"Gui"_s,
"/CanvasColorGreenPart", 255 ) : 255;
396 int blue = project ? project->
readNumEntry( u
"Gui"_s,
"/CanvasColorBluePart", 255 ) : 255;
398 const bool transparent { parameterAsBool( parameters, u
"MAKE_BACKGROUND_TRANSPARENT"_s, context ) };
402 bgColor = QColor( red, green, blue, 0 );
406 bgColor = QColor( red, green, blue );
408 mMapSettings.setBackgroundColor( bgColor );
412 mMapSettings.setScaleMethod( project->
scaleMethod() );
415 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.
@ Export
Renderer used for printing or exporting to a file.
@ 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...
Represents a coordinate reference system (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.
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.
QString ellipsoid() const
Returns the ellipsoid to use for distance and area calculations.
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 ...
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
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