31 #include <cpl_string.h>    32 #include <gdalwarper.h>    40     const QgsRectangle &outputExtent, 
int nOutputColumns, 
int nOutputRows, 
const QVector<QgsRasterCalculatorEntry> &rasterEntries )
    41   : mFormulaString( formulaString )
    42   , mOutputFile( outputFile )
    43   , mOutputFormat( outputFormat )
    44   , mOutputRectangle( outputExtent )
    45   , mNumOutputColumns( nOutputColumns )
    46   , mNumOutputRows( nOutputRows )
    47   , mRasterEntries( rasterEntries )
    50   mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
    55   : mFormulaString( formulaString )
    56   , mOutputFile( outputFile )
    57   , mOutputFormat( outputFormat )
    58   , mOutputRectangle( outputExtent )
    59   , mOutputCrs( outputCrs )
    60   , mNumOutputColumns( nOutputColumns )
    61   , mNumOutputRows( nOutputRows )
    62   , mRasterEntries( rasterEntries )
    79   for ( 
const auto &entry : qgis::as_const( mRasterEntries ) )
    83       mLastError = QObject::tr( 
"No raster layer for entry %1" ).arg( entry.ref );
    86     if ( entry.bandNumber <= 0 || entry.bandNumber > entry.raster->bandCount() )
    88       mLastError = QObject::tr( 
"Band number %1 is not valid for entry %2" ).arg( entry.bandNumber ).arg( entry.ref );
    95   QList<const QgsRasterCalcNode *> nodeList;
    97     return processCalculationGPU( std::move( calcNode ), feedback );
   101   GDALDriverH outputDriver = openOutputDriver();
   104     mLastError = QObject::tr( 
"Could not obtain driver for %1" ).arg( mOutputFormat );
   109   if ( !outputDataset )
   111     mLastError = QObject::tr( 
"Could not create output %1" ).arg( mOutputFile );
   115   GDALSetProjection( outputDataset.get(), mOutputCrs.
toWkt().toLocal8Bit().data() );
   116   GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
   118   float outputNodataValue = -FLT_MAX;
   119   GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue );
   124   bool requiresMatrix = ! calcNode->findNodes( QgsRasterCalcNode::Type::tMatrix ).isEmpty();
   127   if ( ! requiresMatrix )
   130     std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
   131     std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries;
   132     for ( 
const auto &r : calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef ) )
   134       QString layerRef( r->toString().remove( 0, 1 ) );
   136       if ( ! inputBlocks.count( layerRef ) )
   138         for ( 
const auto &ref : mRasterEntries )
   140           if ( ref.ref == layerRef )
   142             uniqueRasterEntries[layerRef] = ref;
   143             inputBlocks[layerRef ] = qgis::make_unique<QgsRasterBlock>();
   150     QMap<QString, QgsRasterBlock * > _rasterData;
   152     std::vector<float> castedResult;
   153     castedResult.reserve( static_cast<size_t>( mNumOutputColumns ) );
   154     auto rowHeight = mOutputRectangle.
height() / mNumOutputRows;
   155     for ( 
size_t row = 0; row < static_cast<size_t>( mNumOutputRows ); ++row )
   159         feedback->
setProgress( 100.0 * static_cast< double >( row ) / mNumOutputRows );
   173       for ( 
auto &layerRef : inputBlocks )
   176         if ( uniqueRasterEntries[layerRef.first].raster->crs() != mOutputCrs )
   182           layerRef.second.reset( proj.
block( ref.
bandNumber, rect, mNumOutputColumns, 1 ) );
   194       for ( 
const auto &layerRef : inputBlocks )
   196         _rasterData.insert( layerRef.first, inputBlocks[layerRef.first].get() );
   199       if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
   202         for ( 
size_t i = 0; i < static_cast<size_t>( mNumOutputColumns ); i++ )
   204           castedResult[i] = 
static_cast<float>( resultMatrix.
data()[i] );
   206         if ( GDALRasterIO( outputRasterBand, GF_Write, 0, row, mNumOutputColumns, 1, castedResult.data(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
   208           QgsDebugMsg( QStringLiteral( 
"RasterIO error!" ) );
   220     QMap< QString, QgsRasterBlock * > inputBlocks;
   221     QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
   222     for ( ; it != mRasterEntries.constEnd(); ++it )
   225       std::unique_ptr< QgsRasterBlock > block;
   227       if ( it->raster->crs() != mOutputCrs )
   230         proj.
setCrs( it->raster->crs(), mOutputCrs );
   231         proj.
setInput( it->raster->dataProvider() );
   236         block.reset( proj.
block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
   239           qDeleteAll( inputBlocks );
   245         block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
   247       if ( block->isEmpty() )
   249         mLastError = QObject::tr( 
"Could not allocate required memory for %1" ).arg( it->ref );
   250         qDeleteAll( inputBlocks );
   253       inputBlocks.insert( it->ref, block.release() );
   260     for ( 
int i = 0; i < mNumOutputRows; ++i )
   264         feedback->
setProgress( 100.0 * static_cast< double >( i ) / mNumOutputRows );
   272       if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
   274         bool resultIsNumber = resultMatrix.
isNumber();
   275         float *calcData = 
new float[mNumOutputColumns];
   277         for ( 
int j = 0; j < mNumOutputColumns; ++j )
   279           calcData[j] = ( float )( resultIsNumber ? resultMatrix.
number() : resultMatrix.
data()[j] );
   283         if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
   285           QgsDebugMsg( QStringLiteral( 
"RasterIO error!" ) );
   300     qDeleteAll( inputBlocks );
   318   QString cExpression( calcNode->toString( 
true ) );
   320   QList<const QgsRasterCalcNode *> nodeList( calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef ) );
   321   QSet<QString> capturedTexts;
   322   for ( 
const auto &r : qgis::as_const( nodeList ) )
   324     QString s( r->toString().remove( 0, 1 ) );
   326     capturedTexts.insert( s );
   343   std::vector<LayerRef> inputRefs;
   344   size_t refCounter = 0;
   345   for ( 
const auto &r : capturedTexts )
   347     if ( r.startsWith( 
'"' ) )
   349     QStringList parts( r.split( 
'@' ) );
   350     if ( parts.count() != 2 )
   355     entry.band = parts[1].toInt( &ok );
   356     for ( 
const auto &ref : mRasterEntries )
   358       if ( ref.ref == entry.name )
   359         entry.layer = ref.raster;
   361     if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
   363     entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
   364     switch ( entry.layer->dataProvider()->dataType( entry.band ) )
   366       case Qgis::DataType::Byte:
   367         entry.typeName = QStringLiteral( 
"unsigned char" );
   369       case Qgis::DataType::UInt16:
   370         entry.typeName = QStringLiteral( 
"unsigned int" );
   372       case Qgis::DataType::Int16:
   373         entry.typeName = QStringLiteral( 
"short" );
   375       case Qgis::DataType::UInt32:
   376         entry.typeName = QStringLiteral( 
"unsigned int" );
   378       case Qgis::DataType::Int32:
   379         entry.typeName = QStringLiteral( 
"int" );
   381       case Qgis::DataType::Float32:
   382         entry.typeName = QStringLiteral( 
"float" );
   387       case Qgis::DataType::Float64:
   388         entry.typeName = QStringLiteral( 
"double" );
   393     entry.bufferSize = entry.dataSize * mNumOutputColumns;
   394     entry.index = refCounter;
   395     entry.varName = QStringLiteral( 
"input_raster_%1_band_%2" )
   398     inputRefs.push_back( entry );
   406   std::vector<cl::Buffer> inputBuffers;
   407   inputBuffers.reserve( inputRefs.size() );
   408   QStringList inputArgs;
   409   for ( 
const auto &ref : inputRefs )
   411     cExpression.replace( QStringLiteral( 
"\"%1\"" ).arg( ref.name ), QStringLiteral( 
"%1[i]" ).arg( ref.varName ) );
   412     inputArgs.append( QStringLiteral( 
"__global %1 *%2" )
   414                       .arg( ref.varName ) );
   415     inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize, 
nullptr, 
nullptr ) );
   421   QString programTemplate( R
"CL(   424   // Expression: ##EXPRESSION_ORIGINAL##   425   __kernel void rasterCalculator( ##INPUT##   426                             __global float *resultLine   429     // Get the index of the current element   430     const int i = get_global_id(0);   432     resultLine[i] = ##EXPRESSION##;   436   QStringList inputDesc;   437   for ( 
const auto &ref : inputRefs )
   439     inputDesc.append( QStringLiteral( 
"  // %1 = %2" ).arg( ref.varName ).arg( ref.name ) );
   441   programTemplate = programTemplate.replace( QStringLiteral( 
"##INPUT_DESC##" ), inputDesc.join( 
'\n' ) );
   442   programTemplate = programTemplate.replace( QStringLiteral( 
"##INPUT##" ), inputArgs.length() ? ( inputArgs.join( 
',' ).append( 
',' ) ) : QChar( 
' ' ) );
   443   programTemplate = programTemplate.replace( QStringLiteral( 
"##EXPRESSION##" ), cExpression );
   444   programTemplate = programTemplate.replace( QStringLiteral( 
"##EXPRESSION_ORIGINAL##" ), calcNode->toString( ) );
   453   Q_ASSERT( 
sizeof( 
float ) == 4 );
   454   std::size_t resultBufferSize( 4 * static_cast<size_t>( mNumOutputColumns ) );
   455   cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY,
   456                                resultBufferSize, 
nullptr, 
nullptr );
   458   auto kernel = cl::Kernel( program, 
"rasterCalculator" );
   460   for ( 
unsigned int i = 0; i < inputBuffers.size() ; i++ )
   462     kernel.setArg( i, inputBuffers.at( i ) );
   464   kernel.setArg( static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
   469   GDALDriverH outputDriver = openOutputDriver();
   472     mLastError = QObject::tr( 
"Could not obtain driver for %1" ).arg( mOutputFormat );
   477   if ( !outputDataset )
   479     mLastError = QObject::tr( 
"Could not create output %1" ).arg( mOutputFile );
   483   GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt().toLocal8Bit().data() );
   485   GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
   486   if ( !outputRasterBand )
   490   std::unique_ptr<QgsRasterBlock> block;
   493   auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
   494   for ( 
int line = 0; line < mNumOutputRows; line++ )
   503       feedback->
setProgress( 100.0 * static_cast< double >( line ) / mNumOutputRows );
   507     for ( 
const auto &ref : inputRefs )
   516       if ( ref.layer->crs() != mOutputCrs )
   519         proj.
setCrs( ref.layer->crs(), mOutputCrs );
   520         proj.
setInput( ref.layer->dataProvider() );
   522         block.reset( proj.
block( ref.band, rect, mNumOutputColumns, 1 ) );
   526         block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
   533       Q_ASSERT( ref.bufferSize == static_cast<size_t>( block->data().size( ) ) );
   534       queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0,
   535                                 ref.bufferSize, block->bits() );
   539     queue.enqueueNDRangeKernel(
   542       cl::NDRange( mNumOutputColumns )
   546     queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0,
   547                              resultBufferSize, resultLine.
get() );
   552     if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.
get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
   565   inputBuffers.clear();
   571 GDALDriverH QgsRasterCalculator::openOutputDriver()
   574   GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
   592   char **papszOptions = 
nullptr;
   593   gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
   594   if ( !outputDataset )
   600   double geotransform[6];
   601   outputGeoTransform( geotransform );
   602   GDALSetGeoTransform( outputDataset.get(), geotransform );
   604   return outputDataset;
   607 void QgsRasterCalculator::outputGeoTransform( 
double *transform )
 const   609   transform[0] = mOutputRectangle.xMinimum();
   610   transform[1] = mOutputRectangle.width() / mNumOutputColumns;
   612   transform[3] = mOutputRectangle.yMaximum();
   614   transform[5] = -mOutputRectangle.height() / mNumOutputRows;
   624   QVector<QgsRasterCalculatorEntry> availableEntries;
   630     entry.ref = QStringLiteral( 
"%1@%2" ).arg( entry.raster->name() ).arg( entry.bandNumber );
   634       for ( 
const auto &ref : qgis::as_const( availableEntries ) )
   637         if ( !( entry.raster && ref.raster ) )
   640         if ( ref.raster->publicSource() == entry.raster->publicSource() )
   643         if ( ref.ref == entry.ref )
   646           entry.ref = QStringLiteral( 
"%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
   654   QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
   655   for ( ; layerIt != layers.constEnd(); ++layerIt )
   661       for ( 
int i = 0; i < rlayer->
bandCount(); ++i )
   666         if ( ! uniqueRasterBandIdentifier( entry ) )
   668         availableEntries.push_back( entry );
   672   return availableEntries;
 A rectangle specified with double values. 
 
static QVector< QgsRasterCalculatorEntry > rasterEntries()
Creates a list of raster entries from the current project. 
 
Tiny smart-pointer-like wrapper around CPLMalloc and CPLFree: this is needed because OpenCL C++ API m...
 
bool isNumber() const
Returns true if matrix is 1x1 (=scalar number) 
 
Result processCalculation(QgsFeedback *feedback=nullptr)
Starts the calculation and writes a new raster. 
 
QString lastError() const
Returns a description of the last error encountered. 
 
QgsRasterLayer * raster
Raster layer associated with entry. 
 
int bandCount() const
Returns the number of bands in this layer. 
 
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
 
This class provides qgis with the ability to render raster datasets onto the mapcanvas. 
 
Error allocating memory for result. 
 
void setNodataValue(double d)
 
void setProgress(double progress)
Sets the current progress for the feedback object. 
 
void canceled()
Internal routines can connect to this signal if they use event loop. 
 
QgsRasterCalculator(const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, int nOutputColumns, int nOutputRows, const QVector< QgsRasterCalculatorEntry > &rasterEntries)
QgsRasterCalculator constructor. 
 
static bool available()
Checks whether a suitable OpenCL platform and device is available on this system and initialize the Q...
 
static bool supportsRasterCreate(GDALDriverH driver)
Reads whether a driver supports GDALCreate() for raster purposes. 
 
virtual QString name() const =0
Returns a provider name. 
 
static cl::Context context()
Context factory. 
 
int bandNumber
Band number for entry. 
 
User canceled calculation. 
 
void setCrs(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
Sets the source and destination CRS. 
 
Base class for feedback objects to be used for cancelation of something running in a worker thread...
 
QgsRasterDataProvider * dataProvider() override
Returns the layer's data provider, it may be null. 
 
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...
 
Error reading input layer. 
 
void setYMinimum(double y)
Set the minimum y value. 
 
static bool enabled()
Returns true if OpenCL is enabled in the user settings. 
 
Error creating output data file. 
 
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID. 
 
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size. 
 
Represents an individual raster layer/band number entry within a raster calculation. 
 
Result
Result of the calculation. 
 
void setPrecision(Precision precision)
 
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...
 
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
 
virtual bool setInput(QgsRasterInterface *input)
Set input. 
 
QgsRasterBlock * block(int bandNo, const QgsRectangle &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size. 
 
bool isCanceled() const
Tells whether the operation has been canceled already. 
 
void setYMaximum(double y)
Set the maximum y value. 
 
static QgsProject * instance()
Returns the QgsProject singleton instance. 
 
This class represents a coordinate reference system (CRS). 
 
QString toWkt() const
Returns a WKT representation of this CRS. 
 
double * data()
Returns data array (but not ownership) 
 
const QgsCoordinateReferenceSystem & outputCrs
 
static QgsRasterCalcNode * parseRasterCalcString(const QString &str, QString &parserErrorMsg)
 
double yMaximum() const
Returns the y maximum value (top side of rectangle). 
 
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset. 
 
Invalid band number for input. 
 
static cl::CommandQueue commandQueue()
Create an OpenCL command queue from the default context. 
 
Feedback object tailored for raster block reading. 
 
QgsCoordinateReferenceSystem crs
 
double height() const
Returns the height of the rectangle.