34 #include <QtConcurrent>
38 QString QgsRasterizeAlgorithm::name()
const
40 return QStringLiteral(
"rasterize" );
43 QString QgsRasterizeAlgorithm::displayName()
const
45 return QObject::tr(
"Convert map to raster" );
48 QStringList QgsRasterizeAlgorithm::tags()
const
50 return QObject::tr(
"layer,raster,convert,file,map themes,tiles,render" ).split(
',' );
53 QgsProcessingAlgorithm::Flags QgsRasterizeAlgorithm::flags()
const
58 QString QgsRasterizeAlgorithm::group()
const
60 return QObject::tr(
"Raster tools" );
63 QString QgsRasterizeAlgorithm::groupId()
const
65 return QStringLiteral(
"rastertools" );
68 void QgsRasterizeAlgorithm::initAlgorithm(
const QVariantMap & )
71 QStringLiteral(
"EXTENT" ),
72 QObject::tr(
"Minimum extent to render" ) ) );
74 QStringLiteral(
"EXTENT_BUFFER" ),
75 QObject::tr(
"Buffer around tiles in map units" ),
76 QgsProcessingParameterNumber::Type::Double,
81 QStringLiteral(
"TILE_SIZE" ),
82 QObject::tr(
"Tile size" ),
83 QgsProcessingParameterNumber::Type::Integer,
88 QStringLiteral(
"MAP_UNITS_PER_PIXEL" ),
89 QObject::tr(
"Map units per pixel" ),
90 QgsProcessingParameterNumber::Type::Double,
95 QStringLiteral(
"MAKE_BACKGROUND_TRANSPARENT" ),
96 QObject::tr(
"Make background transparent" ),
100 QStringLiteral(
"MAP_THEME" ),
101 QObject::tr(
"Map theme to render" ),
102 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(
"Renders the map canvas to a raster file." );
122 QString QgsRasterizeAlgorithm::shortHelpString()
const
124 return QObject::tr(
"This algorithm rasterizes map canvas content.\n\n"
125 "A map theme can be selected to render a predetermined set of layers with a defined style for each layer. "
126 "Alternatively, a set of layers can be selected if no map theme is set. "
127 "If neither map theme nor layer is set, all the visible layers in the set extent will be rendered.\n\n"
128 "The minimum extent entered will internally be extended to a multiple of the tile size." );
131 QgsRasterizeAlgorithm *QgsRasterizeAlgorithm::createInstance()
const
133 return new QgsRasterizeAlgorithm();
140 const QgsRectangle extent { parameterAsExtent( parameters, QStringLiteral(
"EXTENT" ), context, context.
project()->
crs() ) };
141 const int tileSize { parameterAsInt( parameters, QStringLiteral(
"TILE_SIZE" ), context ) };
142 const bool transparent { parameterAsBool( parameters, QStringLiteral(
"MAKE_BACKGROUND_TRANSPARENT" ), context ) };
143 const double mapUnitsPerPixel { parameterAsDouble( parameters, QStringLiteral(
"MAP_UNITS_PER_PIXEL" ), context ) };
144 const double extentBuffer { parameterAsDouble( parameters, QStringLiteral(
"EXTENT_BUFFER" ), context ) };
145 const QString outputLayerFileName { parameterAsOutputLayer( parameters, QStringLiteral(
"OUTPUT" ), context )};
147 int xTileCount {
static_cast<int>( ceil( extent.width() / mapUnitsPerPixel / tileSize ) )};
148 int yTileCount {
static_cast<int>( ceil( extent.height() / mapUnitsPerPixel / tileSize ) )};
149 int width { xTileCount * tileSize };
150 int height { yTileCount * tileSize };
151 int nBands { transparent ? 4 : 3 };
154 if ( driverName.isEmpty() )
159 GDALDriverH hOutputFileDriver = GDALGetDriverByName( driverName.toLocal8Bit().constData() );
160 if ( !hOutputFileDriver )
165 gdal::dataset_unique_ptr hOutputDataset( GDALCreate( hOutputFileDriver, outputLayerFileName.toLocal8Bit().constData(), width, height, nBands, GDALDataType::GDT_Byte,
nullptr ) );
166 if ( !hOutputDataset )
172 double geoTransform[6];
173 geoTransform[0] = extent.xMinimum();
174 geoTransform[1] = mapUnitsPerPixel;
176 geoTransform[3] = extent.yMaximum();
178 geoTransform[5] = - mapUnitsPerPixel;
179 GDALSetGeoTransform( hOutputDataset.get(), geoTransform );
181 int red = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorRedPart", 255 );
182 int green = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorGreenPart", 255 );
183 int blue = context.
project()->
readNumEntry( QStringLiteral(
"Gui" ),
"/CanvasColorBluePart", 255 );
188 bgColor = QColor( red, green, blue, 0 );
192 bgColor = QColor( red, green, blue );
207 QList<QgsMapLayer *> layers;
208 for (
const auto &lptr : mMapLayers )
210 layers.push_back( lptr.get() );
216 const double extentRatio { mapUnitsPerPixel * tileSize };
217 const int numTiles { xTileCount * yTileCount };
222 void operator()( uint8_t *ptr )
const
228 QAtomicInt rendered = 0;
229 QMutex rasterWriteLocker;
231 const auto renderJob = [ & ](
const int x,
const int y,
QgsMapSettings mapSettings )
233 QImage image { tileSize, tileSize, QImage::Format::Format_ARGB32 };
236 QPainter painter { &image };
241 image.fill( transparent ? bgColor.rgba() : bgColor.rgb() );
243 extent.xMinimum() + x * extentRatio,
244 extent.yMaximum() - ( y + 1 ) * extentRatio,
245 extent.xMinimum() + ( x + 1 ) * extentRatio,
246 extent.yMaximum() - y * extentRatio
250 job.waitForFinished();
253 if ( !hIntermediateDataset )
258 const int xOffset { x * tileSize };
259 const int yOffset { y * tileSize };
261 std::unique_ptr<uint8_t, CPLDelete> buffer(
static_cast< uint8_t *
>( CPLMalloc(
sizeof( uint8_t ) *
static_cast<size_t>( tileSize * tileSize * nBands ) ) ) );
262 CPLErr err = GDALDatasetRasterIO( hIntermediateDataset.get(),
263 GF_Read, 0, 0, tileSize, tileSize,
265 tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
266 if ( err != CE_None )
272 QMutexLocker locker( &rasterWriteLocker );
273 err = GDALDatasetRasterIO( hOutputDataset.get(),
274 GF_Write, xOffset, yOffset, tileSize, tileSize,
276 tileSize, tileSize, GDT_Byte, nBands,
nullptr, 0, 0, 0 );
278 feedback->
setProgress(
static_cast<double>( rendered ) / numTiles * 100.0 );
280 if ( err != CE_None )
288 std::vector<QFuture<void>> futures;
290 for (
int x = 0; x < xTileCount; ++x )
292 for (
int y = 0; y < yTileCount; ++y )
298 futures.push_back( QtConcurrent::run( renderJob, x, y, mapSettings ) );
302 for (
auto &f : futures )
307 return { { QStringLiteral(
"OUTPUT" ), outputLayerFileName } };
315 const QString mapTheme { parameterAsString( parameters, QStringLiteral(
"MAP_THEME" ), context ) };
316 const QList<QgsMapLayer *> mapLayers { parameterAsLayerList( parameters, QStringLiteral(
"LAYERS" ), context ) };
322 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone( ) ) );
326 else if ( ! mapLayers.isEmpty() )
328 for (
const QgsMapLayer *ml : std::as_const( mapLayers ) )
330 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone( ) ) );
334 if ( mMapLayers.size() == 0 )
336 QList<QgsMapLayer *> layers;
341 if ( nodeLayer->isVisible() && root->
layerOrder().contains( layer ) )
345 for (
const QgsMapLayer *ml : std::as_const( layers ) )
347 mMapLayers.push_back( std::unique_ptr<QgsMapLayer>( ml->clone( ) ) );
350 return mMapLayers.size() > 0;