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 QgsProcessingAlgorithm::Flags QgsRasterizeAlgorithm::flags()
const
57 QString QgsRasterizeAlgorithm::group()
const
59 return QObject::tr(
"Raster tools" );
62 QString QgsRasterizeAlgorithm::groupId()
const
64 return QStringLiteral(
"rastertools" );
67 void QgsRasterizeAlgorithm::initAlgorithm(
const QVariantMap & )
70 QStringLiteral(
"EXTENT" ),
71 QObject::tr(
"Minimum extent to render" ) ) );
73 QStringLiteral(
"EXTENT_BUFFER" ),
74 QObject::tr(
"Buffer around tiles in map units" ),
75 QgsProcessingParameterNumber::Type::Double,
80 QStringLiteral(
"TILE_SIZE" ),
81 QObject::tr(
"Tile size" ),
82 QgsProcessingParameterNumber::Type::Integer,
87 QStringLiteral(
"MAP_UNITS_PER_PIXEL" ),
88 QObject::tr(
"Map units per pixel" ),
89 QgsProcessingParameterNumber::Type::Double,
94 QStringLiteral(
"MAKE_BACKGROUND_TRANSPARENT" ),
95 QObject::tr(
"Make background transparent" ),
99 QStringLiteral(
"MAP_THEME" ),
100 QObject::tr(
"Map theme to render" ),
101 QVariant(),
true ) );
105 QStringLiteral(
"LAYERS" ),
106 QObject::tr(
"Layers to render" ),
112 QStringLiteral(
"OUTPUT" ),
113 QObject::tr(
"Output layer" ) ) );
117 QString QgsRasterizeAlgorithm::shortDescription()
const
119 return QObject::tr( R
"(This algorithm rasterizes map canvas content.
120 A map theme can be selected to render a predetermined set of layers with a defined style for each layer.
121 Alternatively, a set of layers layer can be selected if no map theme is set.
122 If neither map theme nor layer is set all the current project layers will be
124 The minimum extent entered will internally be extended to be a multiple of the tile size.)" );
127 QString QgsRasterizeAlgorithm::shortHelpString() const
129 return QObject::tr( R
"(This algorithm renders the map canvas to a raster file.
130 It's possible to choose the following parameters:
131 - Map theme to render
133 - The minimum extent to render
136 - The output (can be saved to a file or to a temporary file and
137 automatically opened as layer in qgis)
141 QgsRasterizeAlgorithm *QgsRasterizeAlgorithm::createInstance() const
143 return new QgsRasterizeAlgorithm();
150 const QgsRectangle extent { parameterAsExtent( parameters, QStringLiteral(
"EXTENT" ), context, context.
project()->
crs() ) };
151 const int tileSize { parameterAsInt( parameters, QStringLiteral(
"TILE_SIZE" ), context ) };
152 const bool transparent { parameterAsBool( parameters, QStringLiteral(
"MAKE_BACKGROUND_TRANSPARENT" ), context ) };
153 const double mapUnitsPerPixel { parameterAsDouble( parameters, QStringLiteral(
"MAP_UNITS_PER_PIXEL" ), context ) };
154 const double extentBuffer { parameterAsDouble( parameters, QStringLiteral(
"EXTENT_BUFFER" ), context ) };
155 const QString outputLayerFileName { parameterAsOutputLayer( parameters, QStringLiteral(
"OUTPUT" ), context )};
157 int xTileCount {
static_cast<int>( ceil( extent.width() / mapUnitsPerPixel / tileSize ) )};
158 int yTileCount {
static_cast<int>( ceil( extent.height() / mapUnitsPerPixel / tileSize ) )};
159 int width { xTileCount * tileSize };
160 int height { yTileCount * tileSize };
161 int nBands { transparent ? 4 : 3 };
164 if ( driverName.isEmpty() )
169 GDALDriverH hOutputFileDriver = GDALGetDriverByName( driverName.toLocal8Bit().constData() );
170 if ( !hOutputFileDriver )
175 gdal::dataset_unique_ptr hOutputDataset( GDALCreate( hOutputFileDriver, outputLayerFileName.toLocal8Bit().constData(), width, height, nBands, GDALDataType::GDT_Byte,
nullptr ) );
176 if ( !hOutputDataset )
182 double geoTransform[6];
183 geoTransform[0] = extent.xMinimum();
184 geoTransform[1] = mapUnitsPerPixel;
186 geoTransform[3] = extent.yMaximum();
188 geoTransform[5] = - mapUnitsPerPixel;
189 GDALSetGeoTransform( hOutputDataset.get(), geoTransform );
191 int red = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorRedPart", 255 );
192 int green = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorGreenPart", 255 );
193 int blue = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorBluePart", 255 );
198 bgColor = QColor( red, green, blue, 0 );
202 bgColor = QColor( red, green, blue );
216 QList<QgsMapLayer *> layers;
217 for (
const auto &lptr : mMapLayers )
219 layers.push_back( lptr.get() );
225 const double extentRatio { mapUnitsPerPixel * tileSize };
226 const int numTiles { xTileCount * yTileCount };
227 const QString fileExtension { QFileInfo( outputLayerFileName ).suffix() };
232 void operator()( uint8_t *ptr )
const
238 QAtomicInt rendered = 0;
239 QMutex rasterWriteLocker;
241 const auto renderJob = [ & ](
const int x,
const int y,
QgsMapSettings mapSettings )
243 QImage image { tileSize, tileSize, QImage::Format::Format_ARGB32 };
246 QPainter painter { &image };
251 image.fill( transparent ? bgColor.rgba() : bgColor.rgb() );
253 extent.xMinimum() + x * extentRatio,
254 extent.yMaximum() - ( y + 1 ) * extentRatio,
255 extent.xMinimum() + ( x + 1 ) * extentRatio,
256 extent.yMaximum() - y * extentRatio
260 job.waitForFinished();
263 if ( !hIntermediateDataset )
268 const int xOffset { x * tileSize };
269 const int yOffset { y * tileSize };
271 std::unique_ptr<uint8_t, CPLDelete> buffer(
static_cast< uint8_t *
>( CPLMalloc(
sizeof( uint8_t ) *
static_cast<size_t>( tileSize * tileSize * nBands ) ) ) );
272 CPLErr err = GDALDatasetRasterIO( hIntermediateDataset.get(),
273 GF_Read, 0, 0, tileSize, tileSize,
275 tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
276 if ( err != CE_None )
282 QMutexLocker locker( &rasterWriteLocker );
283 err = GDALDatasetRasterIO( hOutputDataset.get(),
284 GF_Write, xOffset, yOffset, tileSize, tileSize,
286 tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
288 feedback->
setProgress(
static_cast<double>( rendered ) / numTiles * 100.0 );
290 if ( err != CE_None )
298 std::vector<QFuture<void>> futures;
300 for (
int x = 0; x < xTileCount; ++x )
302 for (
int y = 0; y < yTileCount; ++y )
308 futures.push_back( QtConcurrent::run( renderJob, x, y, mapSettings ) );
312 for (
auto &f : futures )
317 return { { QStringLiteral(
"OUTPUT" ), outputLayerFileName } };
325 const QString mapTheme { parameterAsString( parameters, QStringLiteral(
"MAP_THEME" ), context ) };
326 const QList<QgsMapLayer *> mapLayers { parameterAsLayerList( parameters, QStringLiteral(
"LAYERS" ), context ) };
332 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone( ) ) );
336 else if ( ! mapLayers.isEmpty() )
338 for (
const QgsMapLayer *ml : qgis::as_const( mapLayers ) )
340 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone( ) ) );
344 if ( mMapLayers.size() == 0 )
349 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone( ) ) );
352 return mMapLayers.size() > 0;