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.
@ 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...