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.