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;