33 #include <QtConcurrent>
37 QString QgsRasterizeAlgorithm::name()
const
39 return QStringLiteral(
"rasterize" );
42 QString QgsRasterizeAlgorithm::displayName()
const
44 return QObject::tr(
"Convert map to raster" );
47 QStringList QgsRasterizeAlgorithm::tags()
const
49 return QObject::tr(
"layer,raster,convert,file,map themes,tiles,render" ).split(
',' );
52 QString QgsRasterizeAlgorithm::group()
const
54 return QObject::tr(
"Raster tools" );
57 QString QgsRasterizeAlgorithm::groupId()
const
59 return QStringLiteral(
"rastertools" );
62 void QgsRasterizeAlgorithm::initAlgorithm(
const QVariantMap & )
65 QStringLiteral(
"EXTENT" ),
66 QObject::tr(
"Minimum extent to render" ) ) );
68 QStringLiteral(
"EXTENT_BUFFER" ),
69 QObject::tr(
"Buffer around tiles in map units" ),
70 QgsProcessingParameterNumber::Type::Double,
75 QStringLiteral(
"TILE_SIZE" ),
76 QObject::tr(
"Tile size" ),
77 QgsProcessingParameterNumber::Type::Integer,
82 QStringLiteral(
"MAP_UNITS_PER_PIXEL" ),
83 QObject::tr(
"Map units per pixel" ),
84 QgsProcessingParameterNumber::Type::Double,
89 QStringLiteral(
"MAKE_BACKGROUND_TRANSPARENT" ),
90 QObject::tr(
"Make background transparent" ),
94 QStringLiteral(
"MAP_THEME" ),
95 QObject::tr(
"Map theme to render" ),
100 QStringLiteral(
"LAYERS" ),
101 QObject::tr(
"Layers to render" ),
107 QStringLiteral(
"OUTPUT" ),
108 QObject::tr(
"Output layer" ) ) );
112 QString QgsRasterizeAlgorithm::shortDescription()
const
114 return QObject::tr( R
"(This algorithm rasterizes map canvas content.
115 A map theme can be selected to render a predetermined set of layers with a defined style for each layer.
116 Alternatively, a set of layers layer can be selected if no map theme is set.
117 If neither map theme nor layer is set all the current project layers will be
119 The minimum extent entered will internally be extended to be a multiple of the tile size.)" );
122 QString QgsRasterizeAlgorithm::shortHelpString() const
124 return QObject::tr( R
"(This algorithm renders the map canvas to a raster file.
125 It's possible to choose the following parameters:
126 - Map theme to render
128 - The minimum extent to render
131 - The output (can be saved to a file or to a temporary file and
132 automatically opened as layer in qgis)
136 QgsRasterizeAlgorithm *QgsRasterizeAlgorithm::createInstance() const
138 return new QgsRasterizeAlgorithm();
145 const QgsRectangle extent { parameterAsExtent( parameters, QStringLiteral(
"EXTENT" ), context, context.
project()->
crs() ) };
146 const int tileSize { parameterAsInt( parameters, QStringLiteral(
"TILE_SIZE" ), context ) };
147 const bool transparent { parameterAsBool( parameters, QStringLiteral(
"MAKE_BACKGROUND_TRANSPARENT" ), context ) };
148 const double mapUnitsPerPixel { parameterAsDouble( parameters, QStringLiteral(
"MAP_UNITS_PER_PIXEL" ), context ) };
149 const double extentBuffer { parameterAsDouble( parameters, QStringLiteral(
"EXTENT_BUFFER" ), context ) };
150 const QString outputLayerFileName { parameterAsOutputLayer( parameters, QStringLiteral(
"OUTPUT" ), context )};
152 int xTileCount {
static_cast<int>( ceil( extent.width() / mapUnitsPerPixel / tileSize ) )};
153 int yTileCount {
static_cast<int>( ceil( extent.height() / mapUnitsPerPixel / tileSize ) )};
154 int width { xTileCount * tileSize };
155 int height { yTileCount * tileSize };
156 int nBands { transparent ? 4 : 3 };
159 if ( driverName.isEmpty() )
164 GDALDriverH hOutputFileDriver = GDALGetDriverByName( driverName.toLocal8Bit().constData() );
165 if ( !hOutputFileDriver )
170 gdal::dataset_unique_ptr hOutputDataset( GDALCreate( hOutputFileDriver, outputLayerFileName.toLocal8Bit().constData(), width, height, nBands, GDALDataType::GDT_Byte,
nullptr ) );
171 if ( !hOutputDataset )
177 double geoTransform[6];
178 geoTransform[0] = extent.xMinimum();
179 geoTransform[1] = mapUnitsPerPixel;
181 geoTransform[3] = extent.yMaximum();
183 geoTransform[5] = - mapUnitsPerPixel;
184 GDALSetGeoTransform( hOutputDataset.get(), geoTransform );
186 int red = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorRedPart", 255 );
187 int green = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorGreenPart", 255 );
188 int blue = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorBluePart", 255 );
193 bgColor = QColor( red, green, blue, 0 );
197 bgColor = QColor( red, green, blue );
211 QList<QgsMapLayer *> layers;
212 for (
const auto &lptr : mMapLayers )
214 layers.push_back( lptr.get() );
220 const double extentRatio { mapUnitsPerPixel * tileSize };
221 const int numTiles { xTileCount * yTileCount };
222 const QString fileExtension { QFileInfo( outputLayerFileName ).suffix() };
227 void operator()( uint8_t *ptr )
const
233 QAtomicInt rendered = 0;
234 QMutex rasterWriteLocker;
236 const auto renderJob = [ & ](
const int x,
const int y,
QgsMapSettings mapSettings )
238 QImage image { tileSize, tileSize, QImage::Format::Format_ARGB32 };
241 QPainter painter { &image };
246 image.fill( transparent ? bgColor.rgba() : bgColor.rgb() );
248 extent.xMinimum() + x * extentRatio,
249 extent.yMaximum() - ( y + 1 ) * extentRatio,
250 extent.xMinimum() + ( x + 1 ) * extentRatio,
251 extent.yMaximum() - y * extentRatio
255 job.waitForFinished();
258 if ( !hIntermediateDataset )
263 const int xOffset { x * tileSize };
264 const int yOffset { y * tileSize };
266 std::unique_ptr<uint8_t, CPLDelete> buffer(
static_cast< uint8_t *
>( CPLMalloc(
sizeof( uint8_t ) *
static_cast<size_t>( tileSize * tileSize * nBands ) ) ) );
267 CPLErr err = GDALDatasetRasterIO( hIntermediateDataset.get(),
268 GF_Read, 0, 0, tileSize, tileSize,
270 tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
271 if ( err != CE_None )
277 QMutexLocker locker( &rasterWriteLocker );
278 err = GDALDatasetRasterIO( hOutputDataset.get(),
279 GF_Write, xOffset, yOffset, tileSize, tileSize,
281 tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
283 feedback->
setProgress(
static_cast<double>( rendered ) / numTiles * 100.0 );
285 if ( err != CE_None )
293 std::vector<QFuture<void>> futures;
295 for (
int x = 0; x < xTileCount; ++x )
297 for (
int y = 0; y < yTileCount; ++y )
303 futures.push_back( QtConcurrent::run( renderJob, x, y, mapSettings ) );
307 for (
auto &f : futures )
312 return { { QStringLiteral(
"OUTPUT" ), outputLayerFileName } };
320 const QString mapTheme { parameterAsString( parameters, QStringLiteral(
"MAP_THEME" ), context ) };
321 const QList<QgsMapLayer *> mapLayers { parameterAsLayerList( parameters, QStringLiteral(
"LAYERS" ), context ) };
327 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone( ) ) );
331 else if ( ! mapLayers.isEmpty() )
333 for (
const QgsMapLayer *ml : qgis::as_const( mapLayers ) )
335 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone( ) ) );
339 if ( mMapLayers.size() == 0 )
344 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone( ) ) );
347 return mMapLayers.size() > 0;