20#include <cpl_string.h>
21#include <gdalwarper.h>
36using namespace Qt::StringLiterals;
49 Q_UNUSED( pszMessage )
51 static double sDfLastComplete = -1.0;
55 if ( sDfLastComplete > dfComplete )
57 if ( sDfLastComplete >= 1.0 )
58 sDfLastComplete = -1.0;
60 sDfLastComplete = dfComplete;
63 if ( std::floor( sDfLastComplete * 10 ) != std::floor( dfComplete * 10 ) )
68 sDfLastComplete = dfComplete;
77 const QString &formulaString,
78 const QString &outputFile,
79 const QString &outputFormat,
83 const QVector<QgsRasterCalculatorEntry> &rasterEntries,
86 : mFormulaString( formulaString )
87 , mOutputFile( outputFile )
88 , mOutputFormat( outputFormat )
89 , mOutputRectangle( outputExtent )
90 , mNumOutputColumns( nOutputColumns )
91 , mNumOutputRows( nOutputRows )
92 , mRasterEntries( rasterEntries )
93 , mTransformContext( transformContext )
96 if ( !mRasterEntries.isEmpty() )
98 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
103 const QString &formulaString,
104 const QString &outputFile,
105 const QString &outputFormat,
110 const QVector<QgsRasterCalculatorEntry> &rasterEntries,
113 : mFormulaString( formulaString )
114 , mOutputFile( outputFile )
115 , mOutputFormat( outputFormat )
116 , mOutputRectangle( outputExtent )
117 , mOutputCrs( outputCrs )
118 , mNumOutputColumns( nOutputColumns )
119 , mNumOutputRows( nOutputRows )
120 , mRasterEntries( rasterEntries )
121 , mTransformContext( transformContext )
126 const QString &formulaString,
const QString &outputFile,
const QString &outputFormat,
const QgsRectangle &outputExtent,
int nOutputColumns,
int nOutputRows,
const QVector<QgsRasterCalculatorEntry> &rasterEntries
128 : mFormulaString( formulaString )
129 , mOutputFile( outputFile )
130 , mOutputFormat( outputFormat )
131 , mOutputRectangle( outputExtent )
132 , mNumOutputColumns( nOutputColumns )
133 , mNumOutputRows( nOutputRows )
134 , mRasterEntries( rasterEntries )
137 if ( !mRasterEntries.isEmpty() )
139 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
147 const QString &formulaString,
148 const QString &outputFile,
149 const QString &outputFormat,
154 const QVector<QgsRasterCalculatorEntry> &rasterEntries
156 : mFormulaString( formulaString )
157 , mOutputFile( outputFile )
158 , mOutputFormat( outputFormat )
159 , mOutputRectangle( outputExtent )
160 , mOutputCrs( outputCrs )
161 , mNumOutputColumns( nOutputColumns )
162 , mNumOutputRows( nOutputRows )
163 , mRasterEntries( rasterEntries )
164 , mTransformContext(
QgsProject::instance()->transformContext() )
180 for (
const auto &entry : std::as_const( mRasterEntries ) )
184 mLastError = QObject::tr(
"No raster layer for entry %1" ).arg( entry.ref );
187 if ( entry.bandNumber <= 0 || entry.bandNumber > entry.raster->bandCount() )
189 mLastError = QObject::tr(
"Band number %1 is not valid for entry %2" ).arg( entry.bandNumber ).arg( entry.ref );
203 return processCalculationGPU( std::move( calcNode ), feedback );
208 GDALDriverH outputDriver = openOutputDriver();
211 mLastError = QObject::tr(
"Could not obtain driver for %1" ).arg( mOutputFormat );
216 if ( !outputDataset )
218 mLastError = QObject::tr(
"Could not create output %1" ).arg( mOutputFile );
223 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
225 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
227#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
228 const bool hasReportsDuringClose = GDALDatasetGetCloseReportsProgress( outputDataset.get() );
229 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
231 constexpr double maxProgressDuringBlockWriting = 100.0;
235 if ( !requiresMatrix )
238 std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
239 std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries;
243 QString layerRef( r->toString().remove( 0, 1 ) );
245 if ( !inputBlocks.count( layerRef ) )
249 if ( ref.ref == layerRef )
251 uniqueRasterEntries[layerRef] = ref;
252 inputBlocks[layerRef] = std::make_unique<QgsRasterBlock>();
259 QMap<QString, QgsRasterBlock *> _rasterData;
261 std::vector<float> castedResult(
static_cast<size_t>( mNumOutputColumns ), 0 );
262 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
263 for (
size_t row = 0; row < static_cast<size_t>( mNumOutputRows ); ++row )
267 feedback->
setProgress( maxProgressDuringBlockWriting *
static_cast<double>( row ) / mNumOutputRows );
281 for (
auto &layerRef : inputBlocks )
290 layerRef.second.reset( proj.
block( ref.
bandNumber, rect, mNumOutputColumns, 1 ) );
299 QgsRasterMatrix resultMatrix( mNumOutputColumns, 1,
nullptr, mNoDataValue );
302 for (
const auto &layerRef : inputBlocks )
304 _rasterData.insert( layerRef.first, layerRef.second.get() );
307 if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
309 std::copy( resultMatrix.
data(), resultMatrix.
data() + mNumOutputColumns, castedResult.begin() );
310 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, row, mNumOutputColumns, 1, castedResult.data(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
325 feedback->
setProgress( maxProgressDuringBlockWriting );
330 QMap<QString, QgsRasterBlock *> inputBlocks;
331 QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
332 for ( ; it != mRasterEntries.constEnd(); ++it )
334 std::unique_ptr<QgsRasterBlock> block;
336 if ( it->raster->crs() != mOutputCrs )
339 proj.
setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
340 proj.
setInput( it->raster->dataProvider() );
345 block.reset( proj.
block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
348 qDeleteAll( inputBlocks );
354 block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
356 if ( block->isEmpty() )
358 mLastError = QObject::tr(
"Could not allocate required memory for %1" ).arg( it->ref );
359 qDeleteAll( inputBlocks );
362 inputBlocks.insert( it->ref, block.release() );
369 for (
int i = 0; i < mNumOutputRows; ++i )
373 feedback->
setProgress( maxProgressDuringBlockWriting *
static_cast<double>( i ) / mNumOutputRows );
381 if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
383 bool resultIsNumber = resultMatrix.
isNumber();
384 float *calcData =
new float[mNumOutputColumns];
386 for (
int j = 0; j < mNumOutputColumns; ++j )
388 calcData[j] = ( float ) ( resultIsNumber ? resultMatrix.
number() : resultMatrix.
data()[j] );
392 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
401 qDeleteAll( inputBlocks );
410 feedback->
setProgress( maxProgressDuringBlockWriting );
415 qDeleteAll( inputBlocks );
426 GDALComputeRasterStatistics( outputRasterBand,
true,
nullptr,
nullptr,
nullptr,
nullptr,
GdalProgressCallback, feedback );
428#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
429 if ( hasReportsDuringClose && feedback )
445 QString cExpression( calcNode->toString(
true ) );
448 QSet<QString> capturedTexts;
449 for (
const auto &r : std::as_const( nodeList ) )
451 QString s( r->toString().remove( 0, 1 ) );
453 capturedTexts.insert( s );
461 QgsRasterLayer *layer =
nullptr;
470 std::vector<LayerRef> inputRefs;
471 size_t refCounter = 0;
472 for (
const auto &r : capturedTexts )
474 if ( r.startsWith(
'"' ) )
476 QStringList parts( r.split(
'@' ) );
477 if ( parts.count() != 2 )
482 entry.band = parts[1].toInt( &ok );
483 for (
const auto &ref : std::as_const( mRasterEntries ) )
485 if ( ref.ref == entry.name )
486 entry.layer = ref.raster;
488 if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
490 entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
491 switch ( entry.layer->dataProvider()->dataType( entry.band ) )
494 entry.typeName = u
"unsigned char"_s;
497 entry.typeName = u
"char"_s;
500 entry.typeName = u
"unsigned short"_s;
503 entry.typeName = u
"short"_s;
506 entry.typeName = u
"unsigned int"_s;
509 entry.typeName = u
"int"_s;
512 entry.typeName = u
"float"_s;
518 entry.typeName = u
"double"_s;
529 entry.bufferSize = entry.dataSize * mNumOutputColumns;
530 entry.index = refCounter;
531 entry.varName = u
"input_raster_%1_band_%2"_s.arg( refCounter++ ).arg( entry.band );
532 inputRefs.push_back( entry );
543 std::vector<cl::Buffer> inputBuffers;
544 inputBuffers.reserve( inputRefs.size() );
545 QStringList inputArgs;
546 for (
const auto &ref : inputRefs )
548 cExpression.replace( u
"\"%1\""_s.arg( ref.name ), u
"%1[i]"_s.arg( ref.varName ) );
549 inputArgs.append( u
"__global %1 *%2"_s.arg( ref.typeName, ref.varName ) );
550 inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize,
nullptr,
nullptr ) );
556 QString programTemplate( R
"CL(
559 // Expression: ##EXPRESSION_ORIGINAL##
560 __kernel void rasterCalculator( ##INPUT##
561 __global float *resultLine
564 // Get the index of the current element
565 const int i = get_global_id(0);
566 // Check for nodata in input
567 if ( ##INPUT_NODATA_CHECK## )
568 resultLine[i] = -FLT_MAX;
571 resultLine[i] = ##EXPRESSION##;
575 QStringList inputDesc;
576 QStringList inputNoDataCheck;
577 for (
const auto &ref : inputRefs )
579 inputDesc.append( u
" // %1 = %2"_s.arg( ref.varName, ref.name ) );
580 if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
582 inputNoDataCheck.append( u
"(float) %1[i] == (float) %2"_s.arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ),
'g', 10 ) ) );
586 programTemplate = programTemplate.replace(
"##INPUT_NODATA_CHECK##"_L1, inputNoDataCheck.isEmpty() ? u
"false"_s : inputNoDataCheck.join(
" || "_L1 ) );
587 programTemplate = programTemplate.replace(
"##INPUT_DESC##"_L1, inputDesc.join(
'\n' ) );
588 programTemplate = programTemplate.replace(
"##INPUT##"_L1, !inputArgs.isEmpty() ? ( inputArgs.join(
',' ).append(
',' ) ) : QChar(
' ' ) );
589 programTemplate = programTemplate.replace(
"##EXPRESSION##"_L1, cExpression );
590 programTemplate = programTemplate.replace(
"##EXPRESSION_ORIGINAL##"_L1, calcNode->toString() );
600 catch ( cl::Error &e )
602 mLastError = QObject::tr(
"Error compiling OpenCL kernel: %1" ).arg( e.what() );
608 Q_ASSERT(
sizeof(
float ) == 4 );
609 std::size_t resultBufferSize( 4 *
static_cast<size_t>( mNumOutputColumns ) );
610 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, resultBufferSize,
nullptr,
nullptr );
612 auto kernel = cl::Kernel( program,
"rasterCalculator" );
614 for (
unsigned int i = 0; i < inputBuffers.size(); i++ )
616 kernel.setArg( i, inputBuffers.at( i ) );
618 kernel.setArg(
static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
620 QgsOpenClUtils::CPLAllocator<float> resultLine(
static_cast<size_t>( mNumOutputColumns ) );
623 GDALDriverH outputDriver = openOutputDriver();
626 mLastError = QObject::tr(
"Could not obtain driver for %1" ).arg( mOutputFormat );
631 if ( !outputDataset )
633 mLastError = QObject::tr(
"Could not create output %1" ).arg( mOutputFile );
637#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
638 const bool hasReportsDuringClose = GDALDatasetGetCloseReportsProgress( outputDataset.get() );
639 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
641 constexpr double maxProgressDuringBlockWriting = 100.0;
647 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
648 if ( !outputRasterBand )
651 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
654 std::unique_ptr<QgsRasterBlock> block;
657 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
658 for (
int line = 0; line < mNumOutputRows; line++ )
667 feedback->
setProgress( maxProgressDuringBlockWriting *
static_cast<double>( line ) / mNumOutputRows );
671 for (
const auto &ref : inputRefs )
674 QgsRectangle rect( mOutputRectangle );
675 rect.setYMaximum( rect.yMaximum() - rowHeight * line );
676 rect.setYMinimum( rect.yMaximum() - rowHeight );
680 if ( ref.layer->crs() != mOutputCrs )
682 QgsRasterProjector proj;
683 proj.
setCrs( ref.layer->crs(), mOutputCrs, ref.layer->transformContext() );
684 proj.
setInput( ref.layer->dataProvider() );
686 block.reset( proj.
block( ref.band, rect, mNumOutputColumns, 1 ) );
690 block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
697 Q_ASSERT( ref.bufferSize ==
static_cast<size_t>( block->data().size() ) );
698 queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0, ref.bufferSize, block->bits() );
701 queue.enqueueNDRangeKernel( kernel, 0, cl::NDRange( mNumOutputColumns ) );
704 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, resultBufferSize, resultLine.get() );
709 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
722 inputBuffers.clear();
724 GDALComputeRasterStatistics( outputRasterBand,
true,
nullptr,
nullptr,
nullptr,
nullptr,
GdalProgressCallback, feedback );
726#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
727 if ( hasReportsDuringClose && feedback )
729 QgsGdalProgressAdapter progress( feedback, maxProgressDuringBlockWriting );
737 catch ( cl::Error &e )
739 mLastError = e.what();
747GDALDriverH QgsRasterCalculator::openOutputDriver()
750 GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
769 gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
770 CSLDestroy( papszOptions );
771 if ( !outputDataset )
777 double geotransform[6];
778 outputGeoTransform( geotransform );
779 GDALSetGeoTransform( outputDataset.get(), geotransform );
781 return outputDataset;
784void QgsRasterCalculator::outputGeoTransform(
double *transform )
const
786 transform[0] = mOutputRectangle.xMinimum();
787 transform[1] = mOutputRectangle.width() / mNumOutputColumns;
789 transform[3] = mOutputRectangle.yMaximum();
791 transform[5] = -mOutputRectangle.height() / mNumOutputRows;
801 QVector<QgsRasterCalculatorEntry> availableEntries;
806 entry.ref = u
"%1@%2"_s.arg( entry.raster ? entry.raster->name() : QString() ).arg( entry.bandNumber );
810 for (
const auto &
ref : std::as_const( availableEntries ) )
813 if ( !( entry.raster &&
ref.raster ) )
816 if (
ref.raster->publicSource() == entry.raster->publicSource() )
818 if (
ref.bandNumber != entry.bandNumber )
828 if (
ref.ref == entry.ref )
831 entry.ref = u
"%1_%2@%3"_s.arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
839 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
840 for ( ; layerIt != layers.constEnd(); ++layerIt )
842 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
846 for (
int i = 0; i < rlayer->
bandCount(); ++i )
851 if ( !uniqueRasterBandIdentifier( entry ) )
853 availableEntries.push_back( entry );
857 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).
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.
Utility class to map from GDALProgressFunc to QgsFeedback.
static int CPL_STDCALL progressCallback(double dfComplete, const char *pszMessage, void *pProgressArg)
GDAL progress callback.
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.
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.
@ OpenCLKernelBuildError
Error building OpenCL kernel.
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)