31#include <cpl_string.h> 
   32#include <gdalwarper.h> 
   45  Q_UNUSED( pszMessage )
 
   47  static double sDfLastComplete = -1.0;
 
   51  if ( sDfLastComplete > dfComplete )
 
   53    if ( sDfLastComplete >= 1.0 )
 
   54      sDfLastComplete = -1.0;
 
   56      sDfLastComplete = dfComplete;
 
   59  if ( std::floor( sDfLastComplete * 10 ) != std::floor( dfComplete * 10 ) )
 
   64  sDfLastComplete = dfComplete;
 
 
   73  : mFormulaString( formulaString )
 
   74  , mOutputFile( outputFile )
 
   75  , mOutputFormat( outputFormat )
 
   76  , mOutputRectangle( outputExtent )
 
   77  , mNumOutputColumns( nOutputColumns )
 
   78  , mNumOutputRows( nOutputRows )
 
   79  , mRasterEntries( rasterEntries )
 
   80  , mTransformContext( transformContext )
 
   83  if ( !mRasterEntries.isEmpty() )
 
   85    mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
 
 
   90  : mFormulaString( formulaString )
 
   91  , mOutputFile( outputFile )
 
   92  , mOutputFormat( outputFormat )
 
   93  , mOutputRectangle( outputExtent )
 
   95  , mNumOutputColumns( nOutputColumns )
 
   96  , mNumOutputRows( nOutputRows )
 
   97  , mRasterEntries( rasterEntries )
 
   98  , mTransformContext( transformContext )
 
 
  104  : mFormulaString( formulaString )
 
  105  , mOutputFile( outputFile )
 
  106  , mOutputFormat( outputFormat )
 
  107  , mOutputRectangle( outputExtent )
 
  108  , mNumOutputColumns( nOutputColumns )
 
  109  , mNumOutputRows( nOutputRows )
 
  110  , mRasterEntries( rasterEntries )
 
  113  if ( !mRasterEntries.isEmpty() )
 
  115    mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
 
 
  123  : mFormulaString( formulaString )
 
  124  , mOutputFile( outputFile )
 
  125  , mOutputFormat( outputFormat )
 
  126  , mOutputRectangle( outputExtent )
 
  128  , mNumOutputColumns( nOutputColumns )
 
  129  , mNumOutputRows( nOutputRows )
 
  130  , mRasterEntries( rasterEntries )
 
  131  , mTransformContext( 
QgsProject::instance()->transformContext() )
 
 
  148  for ( 
const auto &entry : std::as_const( mRasterEntries ) )
 
  152      mLastError = QObject::tr( 
"No raster layer for entry %1" ).arg( entry.ref );
 
  155    if ( entry.bandNumber <= 0 || entry.bandNumber > entry.raster->bandCount() )
 
  157      mLastError = QObject::tr( 
"Band number %1 is not valid for entry %2" ).arg( entry.bandNumber ).arg( entry.ref );
 
  171    return processCalculationGPU( std::move( calcNode ), feedback );
 
  176  GDALDriverH outputDriver = openOutputDriver();
 
  179    mLastError = QObject::tr( 
"Could not obtain driver for %1" ).arg( mOutputFormat );
 
  184  if ( !outputDataset )
 
  186    mLastError = QObject::tr( 
"Could not create output %1" ).arg( mOutputFile );
 
  191  GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
 
  193  GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
 
  196  if ( !requiresMatrix )
 
  199    std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
 
  200    std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries;
 
  204      QString layerRef( r->toString().remove( 0, 1 ) );
 
  206      if ( !inputBlocks.count( layerRef ) )
 
  210          if ( ref.ref == layerRef )
 
  212            uniqueRasterEntries[layerRef] = ref;
 
  213            inputBlocks[layerRef] = std::make_unique<QgsRasterBlock>();
 
  220    QMap<QString, QgsRasterBlock *> _rasterData;
 
  222    std::vector<float> castedResult( 
static_cast<size_t>( mNumOutputColumns ), 0 );
 
  223    auto rowHeight = mOutputRectangle.
height() / mNumOutputRows;
 
  224    for ( 
size_t row = 0; row < static_cast<size_t>( mNumOutputRows ); ++row )
 
  228        feedback->
setProgress( 100.0 * 
static_cast<double>( row ) / mNumOutputRows );
 
  242      for ( 
auto &layerRef : inputBlocks )
 
  251          layerRef.second.reset( proj.
block( ref.
bandNumber, rect, mNumOutputColumns, 1 ) );
 
  260      QgsRasterMatrix resultMatrix( mNumOutputColumns, 1, 
nullptr, mNoDataValue );
 
  263      for ( 
const auto &layerRef : inputBlocks )
 
  265        _rasterData.insert( layerRef.first, layerRef.second.get() );
 
  268      if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
 
  270        std::copy( resultMatrix.
data(), resultMatrix.
data() + mNumOutputColumns, castedResult.begin() );
 
  271        if ( GDALRasterIO( outputRasterBand, GF_Write, 0, row, mNumOutputColumns, 1, castedResult.data(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
 
  291    QMap<QString, QgsRasterBlock *> inputBlocks;
 
  292    QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
 
  293    for ( ; it != mRasterEntries.constEnd(); ++it )
 
  295      std::unique_ptr<QgsRasterBlock> block;
 
  297      if ( it->raster->crs() != mOutputCrs )
 
  300        proj.
setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
 
  301        proj.
setInput( it->raster->dataProvider() );
 
  306        block.reset( proj.
block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
 
  309          qDeleteAll( inputBlocks );
 
  315        block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
 
  317      if ( block->isEmpty() )
 
  319        mLastError = QObject::tr( 
"Could not allocate required memory for %1" ).arg( it->ref );
 
  320        qDeleteAll( inputBlocks );
 
  323      inputBlocks.insert( it->ref, block.release() );
 
  330    for ( 
int i = 0; i < mNumOutputRows; ++i )
 
  334        feedback->
setProgress( 100.0 * 
static_cast<double>( i ) / mNumOutputRows );
 
  342      if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
 
  344        bool resultIsNumber = resultMatrix.
isNumber();
 
  345        float *calcData = 
new float[mNumOutputColumns];
 
  347        for ( 
int j = 0; j < mNumOutputColumns; ++j )
 
  349          calcData[j] = ( float ) ( resultIsNumber ? resultMatrix.
number() : resultMatrix.
data()[j] );
 
  353        if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
 
  362        qDeleteAll( inputBlocks );
 
  376    qDeleteAll( inputBlocks );
 
  387  GDALComputeRasterStatistics( outputRasterBand, 
true, 
nullptr, 
nullptr, 
nullptr, 
nullptr, 
GdalProgressCallback, feedback );
 
 
  395  QString cExpression( calcNode->toString( 
true ) );
 
  398  QSet<QString> capturedTexts;
 
  399  for ( 
const auto &r : std::as_const( nodeList ) )
 
  401    QString s( r->toString().remove( 0, 1 ) );
 
  403    capturedTexts.insert( s );
 
  420  std::vector<LayerRef> inputRefs;
 
  421  size_t refCounter = 0;
 
  422  for ( 
const auto &r : capturedTexts )
 
  424    if ( r.startsWith( 
'"' ) )
 
  426    QStringList parts( r.split( 
'@' ) );
 
  427    if ( parts.count() != 2 )
 
  432    entry.band = parts[1].toInt( &ok );
 
  433    for ( 
const auto &ref : std::as_const( mRasterEntries ) )
 
  435      if ( ref.ref == entry.name )
 
  436        entry.layer = ref.raster;
 
  438    if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
 
  440    entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
 
  441    switch ( entry.layer->dataProvider()->dataType( entry.band ) )
 
  444        entry.typeName = QStringLiteral( 
"unsigned char" );
 
  447        entry.typeName = QStringLiteral( 
"char" );
 
  450        entry.typeName = QStringLiteral( 
"unsigned short" );
 
  453        entry.typeName = QStringLiteral( 
"short" );
 
  456        entry.typeName = QStringLiteral( 
"unsigned int" );
 
  459        entry.typeName = QStringLiteral( 
"int" );
 
  462        entry.typeName = QStringLiteral( 
"float" );
 
  468        entry.typeName = QStringLiteral( 
"double" );
 
  479    entry.bufferSize = entry.dataSize * mNumOutputColumns;
 
  480    entry.index = refCounter;
 
  481    entry.varName = QStringLiteral( 
"input_raster_%1_band_%2" )
 
  484    inputRefs.push_back( entry );
 
  495    std::vector<cl::Buffer> inputBuffers;
 
  496    inputBuffers.reserve( inputRefs.size() );
 
  497    QStringList inputArgs;
 
  498    for ( 
const auto &ref : inputRefs )
 
  500      cExpression.replace( QStringLiteral( 
"\"%1\"" ).arg( ref.name ), QStringLiteral( 
"%1[i]" ).arg( ref.varName ) );
 
  501      inputArgs.append( QStringLiteral( 
"__global %1 *%2" )
 
  502                          .arg( ref.typeName, ref.varName ) );
 
  503      inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize, 
nullptr, 
nullptr ) );
 
  509    QString programTemplate( R
"CL( 
  512    // Expression: ##EXPRESSION_ORIGINAL## 
  513    __kernel void rasterCalculator( ##INPUT## 
  514                              __global float *resultLine 
  517      // Get the index of the current element 
  518      const int i = get_global_id(0); 
  519      // Check for nodata in input 
  520      if ( ##INPUT_NODATA_CHECK## ) 
  521        resultLine[i] = -FLT_MAX; 
  524        resultLine[i] = ##EXPRESSION##; 
  528    QStringList inputDesc; 
  529    QStringList inputNoDataCheck; 
  530    for ( 
const auto &ref : inputRefs )
 
  532      inputDesc.append( QStringLiteral( 
"  // %1 = %2" ).arg( ref.varName, ref.name ) );
 
  533      if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
 
  535        inputNoDataCheck.append( QStringLiteral( 
"(float) %1[i] == (float) %2" ).arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ), 
'g', 10 ) ) );
 
  539    programTemplate = programTemplate.replace( QLatin1String( 
"##INPUT_NODATA_CHECK##" ), inputNoDataCheck.isEmpty() ? QStringLiteral( 
"false" ) : inputNoDataCheck.join( QLatin1String( 
" || " ) ) );
 
  540    programTemplate = programTemplate.replace( QLatin1String( 
"##INPUT_DESC##" ), inputDesc.join( 
'\n' ) );
 
  541    programTemplate = programTemplate.replace( QLatin1String( 
"##INPUT##" ), !inputArgs.isEmpty() ? ( inputArgs.join( 
',' ).append( 
',' ) ) : QChar( 
' ' ) );
 
  542    programTemplate = programTemplate.replace( QLatin1String( 
"##EXPRESSION##" ), cExpression );
 
  543    programTemplate = programTemplate.replace( QLatin1String( 
"##EXPRESSION_ORIGINAL##" ), calcNode->toString() );
 
  552    Q_ASSERT( 
sizeof( 
float ) == 4 );
 
  553    std::size_t resultBufferSize( 4 * 
static_cast<size_t>( mNumOutputColumns ) );
 
  554    cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, resultBufferSize, 
nullptr, 
nullptr );
 
  556    auto kernel = cl::Kernel( program, 
"rasterCalculator" );
 
  558    for ( 
unsigned int i = 0; i < inputBuffers.size(); i++ )
 
  560      kernel.setArg( i, inputBuffers.at( i ) );
 
  562    kernel.setArg( 
static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
 
  567    GDALDriverH outputDriver = openOutputDriver();
 
  570      mLastError = QObject::tr( 
"Could not obtain driver for %1" ).arg( mOutputFormat );
 
  575    if ( !outputDataset )
 
  577      mLastError = QObject::tr( 
"Could not create output %1" ).arg( mOutputFile );
 
  584    GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
 
  585    if ( !outputRasterBand )
 
  588    GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
 
  591    std::unique_ptr<QgsRasterBlock> block;
 
  594    auto rowHeight = mOutputRectangle.
height() / mNumOutputRows;
 
  595    for ( 
int line = 0; line < mNumOutputRows; line++ )
 
  604        feedback->
setProgress( 100.0 * 
static_cast<double>( line ) / mNumOutputRows );
 
  608      for ( 
const auto &ref : inputRefs )
 
  612        rect.setYMaximum( rect.yMaximum() - rowHeight * line );
 
  613        rect.setYMinimum( rect.yMaximum() - rowHeight );
 
  617        if ( ref.layer->crs() != mOutputCrs )
 
  620          proj.
setCrs( ref.layer->crs(), mOutputCrs, ref.layer->transformContext() );
 
  621          proj.
setInput( ref.layer->dataProvider() );
 
  623          block.reset( proj.
block( ref.band, rect, mNumOutputColumns, 1 ) );
 
  627          block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
 
  634        Q_ASSERT( ref.bufferSize == 
static_cast<size_t>( block->data().size() ) );
 
  635        queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0, ref.bufferSize, block->bits() );
 
  638      queue.enqueueNDRangeKernel(
 
  641        cl::NDRange( mNumOutputColumns )
 
  645      queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, resultBufferSize, resultLine.get() );
 
  650      if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
 
  663    inputBuffers.clear();
 
  665    GDALComputeRasterStatistics( outputRasterBand, 
true, 
nullptr, 
nullptr, 
nullptr, 
nullptr, 
GdalProgressCallback, feedback );
 
  667  catch ( cl::Error &e )
 
  669    mLastError = e.what();
 
  677GDALDriverH QgsRasterCalculator::openOutputDriver()
 
  680  GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
 
  699  gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
 
  700  CSLDestroy( papszOptions );
 
  701  if ( !outputDataset )
 
  707  double geotransform[6];
 
  708  outputGeoTransform( geotransform );
 
  709  GDALSetGeoTransform( outputDataset.get(), geotransform );
 
  711  return outputDataset;
 
  714void QgsRasterCalculator::outputGeoTransform( 
double *transform )
 const 
  716  transform[0] = mOutputRectangle.
xMinimum();
 
  717  transform[1] = mOutputRectangle.
width() / mNumOutputColumns;
 
  719  transform[3] = mOutputRectangle.
yMaximum();
 
  721  transform[5] = -mOutputRectangle.
height() / mNumOutputRows;
 
  731  QVector<QgsRasterCalculatorEntry> availableEntries;
 
  736    entry.ref = QStringLiteral( 
"%1@%2" ).arg( entry.raster->name() ).arg( entry.bandNumber );
 
  740      for ( 
const auto &
ref : std::as_const( availableEntries ) )
 
  743        if ( !( entry.raster && 
ref.raster ) )
 
  746        if ( 
ref.raster->publicSource() == entry.raster->publicSource() )
 
  748          if ( 
ref.bandNumber != entry.bandNumber )
 
  758        if ( 
ref.ref == entry.ref )
 
  761          entry.ref = QStringLiteral( 
"%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
 
  769  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
 
  770  for ( ; layerIt != layers.constEnd(); ++layerIt )
 
  772    QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
 
  776      for ( 
int i = 0; i < rlayer->
bandCount(); ++i )
 
  781        if ( !uniqueRasterBandIdentifier( entry ) )
 
  783        availableEntries.push_back( entry );
 
  787  return availableEntries;
 
 
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
 
@ Float32
Thirty two bit floating point (float)
 
@ CFloat64
Complex Float64.
 
@ Int16
Sixteen bit signed integer (qint16)
 
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
 
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30)
 
@ UInt16
Sixteen bit unsigned integer (quint16)
 
@ Byte
Eight bit unsigned integer (quint8)
 
@ UnknownDataType
Unknown or unspecified type.
 
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
 
@ Int32
Thirty two bit signed integer (qint32)
 
@ Float64
Sixty four bit floating point (double)
 
@ CFloat32
Complex Float32.
 
@ UInt32
Thirty two bit unsigned integer (quint32)
 
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
 
Represents a coordinate reference system (CRS).
 
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
 
Contains information about the context in which a coordinate transform is executed.
 
Base class for feedback objects to be used for cancellation of something running in a worker thread.
 
bool isCanceled() const
Tells whether the operation has been canceled already.
 
void canceled()
Internal routines can connect to this signal if they use event loop.
 
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
 
void setProgress(double progress)
Sets the current progress for the feedback object.
 
static bool supportsRasterCreate(GDALDriverH driver)
Reads whether a driver supports GDALCreate() for raster purposes.
 
static char ** papszFromStringList(const QStringList &list)
Helper function.
 
QgsCoordinateReferenceSystem crs
 
static Q_DECL_DEPRECATED cl::Program buildProgram(const cl::Context &context, const QString &source, ExceptionBehavior exceptionBehavior=Catch)
Build the program from source in the given context and depending on exceptionBehavior can throw or ca...
 
static cl::Context context()
Context factory.
 
static bool enabled()
Returns true if OpenCL is enabled in the user settings.
 
static bool available()
Checks whether a suitable OpenCL platform and device is available on this system and initialize the Q...
 
static cl::CommandQueue commandQueue()
Create an OpenCL command queue from the default context.
 
@ Throw
Write errors in the message log and re-throw exceptions.
 
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
 
static QgsProject * instance()
Returns the QgsProject singleton instance.
 
QgsCoordinateTransformContext transformContext
 
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
 
Feedback object tailored for raster block reading.
 
Represents a node in a raster calculator.
 
static QgsRasterCalcNode * parseRasterCalcString(const QString &str, QString &parserErrorMsg)
 
Represents an individual raster layer/band number entry within a raster calculation.
 
QgsRasterLayer * raster
Raster layer associated with entry.
 
static QVector< QgsRasterCalculatorEntry > rasterEntries()
Creates a list of raster entries from the current project.
 
int bandNumber
Band number for entry.
 
QString ref
Name of entry.
 
QgsRasterCalculator(const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &outputCrs, int nOutputColumns, int nOutputRows, const QVector< QgsRasterCalculatorEntry > &rasterEntries, const QgsCoordinateTransformContext &transformContext)
QgsRasterCalculator constructor.
 
QString lastError() const
Returns a description of the last error encountered.
 
Result processCalculation(QgsFeedback *feedback=nullptr)
Starts the calculation and writes a new raster.
 
Result
Result of the calculation.
 
@ Canceled
User canceled calculation.
 
@ InputLayerError
Error reading input layer.
 
@ BandError
Invalid band number for input.
 
@ Success
Calculation successful.
 
@ CreateOutputError
Error creating output data file.
 
@ ParserError
Error parsing formula.
 
@ CalculationError
Error occurred while performing calculation.
 
@ MemoryError
Error allocating memory for result.
 
QgsRasterBlock * block(int bandNo, const QgsRectangle &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
 
virtual Qgis::RasterInterfaceCapabilities capabilities() const
Returns the capabilities supported by the interface.
 
virtual bool setInput(QgsRasterInterface *input)
Set input.
 
Represents a raster layer.
 
int bandCount() const
Returns the number of bands in this layer.
 
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
 
Represents a matrix in a raster calculator operation.
 
bool isNumber() const
Returns true if matrix is 1x1 (=scalar number)
 
void setNodataValue(double d)
 
double * data()
Returns data array (but not ownership)
 
Implements approximate projection support for optimised raster transformation.
 
void setPrecision(Precision precision)
 
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
 
@ Exact
Exact, precise but slow.
 
Q_DECL_DEPRECATED void setCrs(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
Sets the source and destination CRS.
 
A rectangle specified with double values.
 
void setYMinimum(double y)
Set the minimum y value.
 
void setYMaximum(double y)
Set the maximum y value.
 
void CORE_EXPORT fast_delete_and_close(dataset_unique_ptr &dataset, GDALDriverH driver, const QString &path)
Performs a fast close of an unwanted GDAL dataset handle by deleting the underlying data store.
 
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
 
#define QgsDebugError(str)
 
int CPL_STDCALL GdalProgressCallback(double dfComplete, const char *pszMessage, void *pProgressArg)
 
const QgsCoordinateReferenceSystem & outputCrs
 
Tiny smart-pointer-like wrapper around CPLMalloc and CPLFree: this is needed because OpenCL C++ API m...