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();
113 QGS_MARK_ALGORITHM_SOURCE
116 const QgsRectangle extent { parameterAsExtent( parameters, u
"EXTENT"_s, context, mCrs ) };
117 const int tileSize { parameterAsInt( parameters, u
"TILE_SIZE"_s, context ) };
122 const bool transparent { parameterAsBool( parameters, u
"MAKE_BACKGROUND_TRANSPARENT"_s, context ) };
123 const double mapUnitsPerPixel { parameterAsDouble( parameters, u
"MAP_UNITS_PER_PIXEL"_s, context ) };
124 if ( mapUnitsPerPixel <= 0 )
128 const double extentBuffer { parameterAsDouble( parameters, u
"EXTENT_BUFFER"_s, context ) };
129 const QString outputLayerFileName { parameterAsOutputLayer( parameters, u
"OUTPUT"_s, context ) };
131 int xTileCount {
static_cast<int>( ceil( extent.width() / mapUnitsPerPixel / tileSize ) ) };
132 int yTileCount {
static_cast<int>( ceil( extent.height() / mapUnitsPerPixel / tileSize ) ) };
133 int width { xTileCount * tileSize };
134 int height { yTileCount * tileSize };
135 int nBands { transparent ? 4 : 3 };
137 int64_t totalTiles = 0;
138 for (
auto &layer : std::as_const( mMapLayers ) )
142 if (
QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( ( layer.get() ) ) )
144 const QList<double> resolutions = rasterLayer->dataProvider()->nativeResolutions();
145 if ( resolutions.isEmpty() )
150 if ( totalTiles == 0 )
156 extentLayer = ct.transform( extent );
164 const double mapUnitsPerPixelLayer = extentLayer.
width() / width;
166 for ( i = 0; i < resolutions.size() && resolutions.at( i ) < mapUnitsPerPixelLayer; i++ )
170 if ( i == resolutions.size() || ( i > 0 && resolutions.at( i ) - mapUnitsPerPixelLayer > mapUnitsPerPixelLayer - resolutions.at( i - 1 ) ) )
175 const int nbTilesWidth = std::ceil( extentLayer.
width() / resolutions.at( i ) / 256 );
176 const int nbTilesHeight = std::ceil( extentLayer.
height() / resolutions.at( i ) / 256 );
177 totalTiles =
static_cast<int64_t
>( nbTilesWidth ) * nbTilesHeight;
179 feedback->
pushInfo( u
"%1"_s.arg( totalTiles ) );
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(), u
"<a href=\"https://operations.osmfoundation.org/policies/tiles/\">"_s, u
"</a>"_s ),
187 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" )
188 .arg( rasterLayer->name(), QString(), QString() )
191 layer->deleteLater();
192 std::vector<std::unique_ptr<QgsMapLayer>>::iterator position = std::find( mMapLayers.begin(), mMapLayers.end(), layer );
193 if ( position != mMapLayers.end() )
195 mMapLayers.erase( position );
202 const QString driverName = parameterAsOutputRasterFormat( parameters, u
"OUTPUT"_s, context );
203 if ( driverName.isEmpty() )
208 GDALDriverH hOutputFileDriver = GDALGetDriverByName( driverName.toLocal8Bit().constData() );
209 if ( !hOutputFileDriver )
214 gdal::dataset_unique_ptr hOutputDataset( GDALCreate( hOutputFileDriver, outputLayerFileName.toUtf8().constData(), width, height, nBands, GDALDataType::GDT_Byte,
nullptr ) );
215 if ( !hOutputDataset )
220#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
221 const bool hasReportsDuringClose = GDALDatasetGetCloseReportsProgress( hOutputDataset.get() );
222 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
224 constexpr double maxProgressDuringBlockWriting = 100.0;
228 double geoTransform[6];
229 geoTransform[0] = extent.xMinimum();
230 geoTransform[1] = mapUnitsPerPixel;
232 geoTransform[3] = extent.yMaximum();
234 geoTransform[5] = -mapUnitsPerPixel;
235 GDALSetGeoTransform( hOutputDataset.get(), geoTransform );
238 mMapSettings.setOutputImageFormat( QImage::Format_ARGB32 );
239 mMapSettings.setDestinationCrs( mCrs );
245 mMapSettings.setEllipsoid( context.
ellipsoid() );
246 mMapSettings.setExtentBuffer( extentBuffer );
249 QList<QgsMapLayer *> layers;
250 for (
const auto &lptr : mMapLayers )
252 layers.push_back( lptr.get() );
254 mMapSettings.setLayers( layers );
255 mMapSettings.setLayerStyleOverrides( mMapThemeStyleOverrides );
258 const double extentRatio { mapUnitsPerPixel * tileSize };
259 const int numTiles { xTileCount * yTileCount };
264 void operator()( uint8_t *ptr )
const { CPLFree( ptr ); }
267 QAtomicInt rendered = 0;
268 QMutex rasterWriteLocker;
270 const auto renderJob = [&](
const int x,
const int y,
QgsMapSettings mapSettings ) {
271 QImage image { tileSize, tileSize, QImage::Format::Format_ARGB32 };
272 mapSettings.setOutputDpi( image.logicalDpiX() );
273 mapSettings.setOutputSize( image.size() );
274 QPainter painter { &image };
279 image.fill( transparent ? mapSettings.backgroundColor().rgba() : mapSettings.backgroundColor().rgb() );
280 mapSettings.setExtent(
281 QgsRectangle( extent.xMinimum() + x * extentRatio, extent.yMaximum() - ( y + 1 ) * extentRatio, extent.xMinimum() + ( x + 1 ) * extentRatio, extent.yMaximum() - y * extentRatio )
285 job.waitForFinished();
288 if ( !hIntermediateDataset )
293 const int xOffset { x * tileSize };
294 const int yOffset { y * tileSize };
296 std::unique_ptr<uint8_t, CPLDelete> buffer(
static_cast<uint8_t *
>( CPLMalloc(
sizeof( uint8_t ) *
static_cast<size_t>( tileSize * tileSize * nBands ) ) ) );
297 CPLErr err = GDALDatasetRasterIO( hIntermediateDataset.get(), GF_Read, 0, 0, tileSize, tileSize, buffer.get(), tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
298 if ( err != CE_None )
304 QMutexLocker locker( &rasterWriteLocker );
305 err = GDALDatasetRasterIO( hOutputDataset.get(), GF_Write, xOffset, yOffset, tileSize, tileSize, buffer.get(), tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
307 feedback->
setProgress(
static_cast<double>( rendered ) / numTiles * maxProgressDuringBlockWriting );
309 if ( err != CE_None )
317 std::vector<QFuture<void>> futures;
319 for (
int x = 0; x < xTileCount; ++x )
321 for (
int y = 0; y < yTileCount; ++y )
327 futures.push_back( QtConcurrent::run( renderJob, x, y, mMapSettings ) );
331 for (
auto &f : futures )
336#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
337 if ( hasReportsDuringClose )
349 return { { u
"OUTPUT"_s, outputLayerFileName } };
358 const QString mapTheme { parameterAsString( parameters, u
"MAP_THEME"_s, context ) };
359 const QList<QgsMapLayer *> mapLayers { parameterAsLayerList( parameters, u
"LAYERS"_s, context ) };
365 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
369 else if ( !mapLayers.isEmpty() )
371 for (
const QgsMapLayer *ml : std::as_const( mapLayers ) )
373 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
377 if ( mMapLayers.empty() && project )
379 QList<QgsMapLayer *> layers;
384 if ( nodeLayer->isVisible() && root->
layerOrder().contains( layer ) )
388 for (
const QgsMapLayer *ml : std::as_const( layers ) )
390 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone() ) );
396 int red = project ? project->
readNumEntry( u
"Gui"_s,
"/CanvasColorRedPart", 255 ) : 255;
397 int green = project ? project->
readNumEntry( u
"Gui"_s,
"/CanvasColorGreenPart", 255 ) : 255;
398 int blue = project ? project->
readNumEntry( u
"Gui"_s,
"/CanvasColorBluePart", 255 ) : 255;
400 const bool transparent { parameterAsBool( parameters, u
"MAKE_BACKGROUND_TRANSPARENT"_s, context ) };
404 bgColor = QColor( red, green, blue, 0 );
408 bgColor = QColor( red, green, blue );
410 mMapSettings.setBackgroundColor( bgColor );
414 mMapSettings.setScaleMethod( project->
scaleMethod() );
417 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