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 )
85 : mFormulaString( formulaString )
86 , mOutputFile( outputFile )
87 , mOutputFormat( outputFormat )
88 , mOutputRectangle( outputExtent )
90 , mNumOutputColumns( nOutputColumns )
91 , mNumOutputRows( nOutputRows )
92 , mRasterEntries( rasterEntries )
93 , mTransformContext( transformContext )
99 : mFormulaString( formulaString )
100 , mOutputFile( outputFile )
101 , mOutputFormat( outputFormat )
102 , mOutputRectangle( outputExtent )
103 , mNumOutputColumns( nOutputColumns )
104 , mNumOutputRows( nOutputRows )
105 , mRasterEntries( rasterEntries )
108 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
115 : mFormulaString( formulaString )
116 , mOutputFile( outputFile )
117 , mOutputFormat( outputFormat )
118 , mOutputRectangle( outputExtent )
120 , mNumOutputColumns( nOutputColumns )
121 , mNumOutputRows( nOutputRows )
122 , mRasterEntries( rasterEntries )
123 , mTransformContext(
QgsProject::instance()->transformContext() )
140 for (
const auto &entry : std::as_const( mRasterEntries ) )
144 mLastError = QObject::tr(
"No raster layer for entry %1" ).arg( entry.ref );
147 if ( entry.bandNumber <= 0 || entry.bandNumber > entry.raster->bandCount() )
149 mLastError = QObject::tr(
"Band number %1 is not valid for entry %2" ).arg( entry.bandNumber ).arg( entry.ref );
163 return processCalculationGPU( std::move( calcNode ), feedback );
168 GDALDriverH outputDriver = openOutputDriver();
171 mLastError = QObject::tr(
"Could not obtain driver for %1" ).arg( mOutputFormat );
176 if ( !outputDataset )
178 mLastError = QObject::tr(
"Could not create output %1" ).arg( mOutputFile );
183 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
185 float outputNodataValue = -FLT_MAX;
186 GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue );
190 if ( !requiresMatrix )
193 std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
194 std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries;
198 QString layerRef( r->toString().remove( 0, 1 ) );
200 if ( !inputBlocks.count( layerRef ) )
204 if ( ref.ref == layerRef )
206 uniqueRasterEntries[layerRef] = ref;
207 inputBlocks[layerRef] = std::make_unique<QgsRasterBlock>();
214 QMap<QString, QgsRasterBlock *> _rasterData;
216 std::vector<float> castedResult(
static_cast<size_t>( mNumOutputColumns ), 0 );
217 auto rowHeight = mOutputRectangle.
height() / mNumOutputRows;
218 for (
size_t row = 0; row < static_cast<size_t>( mNumOutputRows ); ++row )
222 feedback->
setProgress( 100.0 *
static_cast<double>( row ) / mNumOutputRows );
236 for (
auto &layerRef : inputBlocks )
245 layerRef.second.reset( proj.
block( ref.
bandNumber, rect, mNumOutputColumns, 1 ) );
254 QgsRasterMatrix resultMatrix( mNumOutputColumns, 1,
nullptr, outputNodataValue );
257 for (
const auto &layerRef : inputBlocks )
259 _rasterData.insert( layerRef.first, layerRef.second.get() );
262 if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
264 std::copy( resultMatrix.
data(), resultMatrix.
data() + mNumOutputColumns, castedResult.begin() );
265 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, row, mNumOutputColumns, 1, castedResult.data(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
285 QMap<QString, QgsRasterBlock *> inputBlocks;
286 QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
287 for ( ; it != mRasterEntries.constEnd(); ++it )
289 std::unique_ptr<QgsRasterBlock> block;
291 if ( it->raster->crs() != mOutputCrs )
294 proj.
setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
295 proj.
setInput( it->raster->dataProvider() );
300 block.reset( proj.
block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
303 qDeleteAll( inputBlocks );
309 block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
311 if ( block->isEmpty() )
313 mLastError = QObject::tr(
"Could not allocate required memory for %1" ).arg( it->ref );
314 qDeleteAll( inputBlocks );
317 inputBlocks.insert( it->ref, block.release() );
324 for (
int i = 0; i < mNumOutputRows; ++i )
328 feedback->
setProgress( 100.0 *
static_cast<double>( i ) / mNumOutputRows );
336 if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
338 bool resultIsNumber = resultMatrix.
isNumber();
339 float *calcData =
new float[mNumOutputColumns];
341 for (
int j = 0; j < mNumOutputColumns; ++j )
343 calcData[j] = ( float ) ( resultIsNumber ? resultMatrix.
number() : resultMatrix.
data()[j] );
347 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
356 qDeleteAll( inputBlocks );
370 qDeleteAll( inputBlocks );
381 GDALComputeRasterStatistics( outputRasterBand,
true,
nullptr,
nullptr,
nullptr,
nullptr,
GdalProgressCallback, feedback );
389 QString cExpression( calcNode->toString(
true ) );
392 QSet<QString> capturedTexts;
393 for (
const auto &r : std::as_const( nodeList ) )
395 QString s( r->toString().remove( 0, 1 ) );
397 capturedTexts.insert( s );
414 std::vector<LayerRef> inputRefs;
415 size_t refCounter = 0;
416 for (
const auto &r : capturedTexts )
418 if ( r.startsWith(
'"' ) )
420 QStringList parts( r.split(
'@' ) );
421 if ( parts.count() != 2 )
426 entry.band = parts[1].toInt( &ok );
427 for (
const auto &ref : std::as_const( mRasterEntries ) )
429 if ( ref.ref == entry.name )
430 entry.layer = ref.raster;
432 if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
434 entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
435 switch ( entry.layer->dataProvider()->dataType( entry.band ) )
438 entry.typeName = QStringLiteral(
"unsigned char" );
441 entry.typeName = QStringLiteral(
"signed char" );
444 entry.typeName = QStringLiteral(
"unsigned int" );
447 entry.typeName = QStringLiteral(
"short" );
450 entry.typeName = QStringLiteral(
"unsigned int" );
453 entry.typeName = QStringLiteral(
"int" );
456 entry.typeName = QStringLiteral(
"float" );
462 entry.typeName = QStringLiteral(
"double" );
473 entry.bufferSize = entry.dataSize * mNumOutputColumns;
474 entry.index = refCounter;
475 entry.varName = QStringLiteral(
"input_raster_%1_band_%2" )
478 inputRefs.push_back( entry );
489 std::vector<cl::Buffer> inputBuffers;
490 inputBuffers.reserve( inputRefs.size() );
491 QStringList inputArgs;
492 for (
const auto &ref : inputRefs )
494 cExpression.replace( QStringLiteral(
"\"%1\"" ).arg( ref.name ), QStringLiteral(
"%1[i]" ).arg( ref.varName ) );
495 inputArgs.append( QStringLiteral(
"__global %1 *%2" )
496 .arg( ref.typeName, ref.varName ) );
497 inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize,
nullptr,
nullptr ) );
503 QString programTemplate( R
"CL(
506 // Expression: ##EXPRESSION_ORIGINAL##
507 __kernel void rasterCalculator( ##INPUT##
508 __global float *resultLine
511 // Get the index of the current element
512 const int i = get_global_id(0);
513 // Check for nodata in input
514 if ( ##INPUT_NODATA_CHECK## )
515 resultLine[i] = -FLT_MAX;
518 resultLine[i] = ##EXPRESSION##;
522 QStringList inputDesc;
523 QStringList inputNoDataCheck;
524 for (
const auto &ref : inputRefs )
526 inputDesc.append( QStringLiteral(
" // %1 = %2" ).arg( ref.varName, ref.name ) );
527 if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
529 inputNoDataCheck.append( QStringLiteral(
"(float) %1[i] == (float) %2" ).arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ),
'g', 10 ) ) );
533 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT_NODATA_CHECK##" ), inputNoDataCheck.isEmpty() ? QStringLiteral(
"false" ) : inputNoDataCheck.join( QLatin1String(
" || " ) ) );
534 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT_DESC##" ), inputDesc.join(
'\n' ) );
535 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT##" ), !inputArgs.isEmpty() ? ( inputArgs.join(
',' ).append(
',' ) ) : QChar(
' ' ) );
536 programTemplate = programTemplate.replace( QLatin1String(
"##EXPRESSION##" ), cExpression );
537 programTemplate = programTemplate.replace( QLatin1String(
"##EXPRESSION_ORIGINAL##" ), calcNode->toString() );
546 Q_ASSERT(
sizeof(
float ) == 4 );
547 std::size_t resultBufferSize( 4 *
static_cast<size_t>( mNumOutputColumns ) );
548 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, resultBufferSize,
nullptr,
nullptr );
550 auto kernel = cl::Kernel( program,
"rasterCalculator" );
552 for (
unsigned int i = 0; i < inputBuffers.size(); i++ )
554 kernel.setArg( i, inputBuffers.at( i ) );
556 kernel.setArg(
static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
561 GDALDriverH outputDriver = openOutputDriver();
564 mLastError = QObject::tr(
"Could not obtain driver for %1" ).arg( mOutputFormat );
569 if ( !outputDataset )
571 mLastError = QObject::tr(
"Could not create output %1" ).arg( mOutputFile );
578 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
579 if ( !outputRasterBand )
582 const float outputNodataValue = -FLT_MAX;
583 GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue );
586 std::unique_ptr<QgsRasterBlock> block;
589 auto rowHeight = mOutputRectangle.
height() / mNumOutputRows;
590 for (
int line = 0; line < mNumOutputRows; line++ )
599 feedback->
setProgress( 100.0 *
static_cast<double>( line ) / mNumOutputRows );
603 for (
const auto &ref : inputRefs )
607 rect.setYMaximum( rect.yMaximum() - rowHeight * line );
608 rect.setYMinimum( rect.yMaximum() - rowHeight );
612 if ( ref.layer->crs() != mOutputCrs )
615 proj.
setCrs( ref.layer->crs(), mOutputCrs, ref.layer->transformContext() );
616 proj.
setInput( ref.layer->dataProvider() );
618 block.reset( proj.
block( ref.band, rect, mNumOutputColumns, 1 ) );
622 block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
629 Q_ASSERT( ref.bufferSize ==
static_cast<size_t>( block->data().size() ) );
630 queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0, ref.bufferSize, block->bits() );
633 queue.enqueueNDRangeKernel(
636 cl::NDRange( mNumOutputColumns )
640 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, resultBufferSize, resultLine.get() );
645 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
658 inputBuffers.clear();
660 GDALComputeRasterStatistics( outputRasterBand,
true,
nullptr,
nullptr,
nullptr,
nullptr,
GdalProgressCallback, feedback );
662 catch ( cl::Error &e )
664 mLastError = e.what();
672GDALDriverH QgsRasterCalculator::openOutputDriver()
675 GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
693 char **papszOptions =
nullptr;
694 gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
695 if ( !outputDataset )
701 double geotransform[6];
702 outputGeoTransform( geotransform );
703 GDALSetGeoTransform( outputDataset.get(), geotransform );
705 return outputDataset;
708void QgsRasterCalculator::outputGeoTransform(
double *transform )
const
710 transform[0] = mOutputRectangle.
xMinimum();
711 transform[1] = mOutputRectangle.
width() / mNumOutputColumns;
713 transform[3] = mOutputRectangle.
yMaximum();
715 transform[5] = -mOutputRectangle.
height() / mNumOutputRows;
725 QVector<QgsRasterCalculatorEntry> availableEntries;
730 entry.ref = QStringLiteral(
"%1@%2" ).arg( entry.raster->name() ).arg( entry.bandNumber );
734 for (
const auto &
ref : std::as_const( availableEntries ) )
737 if ( !( entry.raster &&
ref.raster ) )
740 if (
ref.raster->publicSource() == entry.raster->publicSource() )
742 if (
ref.bandNumber != entry.bandNumber )
752 if (
ref.ref == entry.ref )
755 entry.ref = QStringLiteral(
"%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
763 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
764 for ( ; layerIt != layers.constEnd(); ++layerIt )
766 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
770 for (
int i = 0; i < rlayer->
bandCount(); ++i )
775 if ( !uniqueRasterBandIdentifier( entry ) )
777 availableEntries.push_back( entry );
781 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.
This class 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.
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, 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.
@ InputLayerError
Error reading input layer.
@ ParserError
Error parsing formula.
@ CalculationError
Error occurred while performing calculation.
@ MemoryError
Error allocating memory for result.
@ Canceled
User canceled calculation.
@ Success
Calculation successful.
@ BandError
Invalid band number for input.
@ CreateOutputError
Error creating output data file.
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...