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 : mFormulaString( formulaString )
78 , mOutputFile( outputFile )
79 , mOutputFormat( outputFormat )
80 , mOutputRectangle( outputExtent )
81 , mNumOutputColumns( nOutputColumns )
82 , mNumOutputRows( nOutputRows )
83 , mRasterEntries( rasterEntries )
84 , mTransformContext( transformContext )
87 if ( !mRasterEntries.isEmpty() )
89 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
94 : mFormulaString( formulaString )
95 , mOutputFile( outputFile )
96 , mOutputFormat( outputFormat )
97 , mOutputRectangle( outputExtent )
98 , mOutputCrs( outputCrs )
99 , mNumOutputColumns( nOutputColumns )
100 , mNumOutputRows( nOutputRows )
101 , mRasterEntries( rasterEntries )
102 , mTransformContext( transformContext )
108 : mFormulaString( formulaString )
109 , mOutputFile( outputFile )
110 , mOutputFormat( outputFormat )
111 , mOutputRectangle( outputExtent )
112 , mNumOutputColumns( nOutputColumns )
113 , mNumOutputRows( nOutputRows )
114 , mRasterEntries( rasterEntries )
117 if ( !mRasterEntries.isEmpty() )
119 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
127 : mFormulaString( formulaString )
128 , mOutputFile( outputFile )
129 , mOutputFormat( outputFormat )
130 , mOutputRectangle( outputExtent )
131 , mOutputCrs( outputCrs )
132 , mNumOutputColumns( nOutputColumns )
133 , mNumOutputRows( nOutputRows )
134 , mRasterEntries( rasterEntries )
135 , mTransformContext(
QgsProject::instance()->transformContext() )
152 for (
const auto &entry : std::as_const( mRasterEntries ) )
156 mLastError = QObject::tr(
"No raster layer for entry %1" ).arg( entry.ref );
159 if ( entry.bandNumber <= 0 || entry.bandNumber > entry.raster->bandCount() )
161 mLastError = QObject::tr(
"Band number %1 is not valid for entry %2" ).arg( entry.bandNumber ).arg( entry.ref );
175 return processCalculationGPU( std::move( calcNode ), feedback );
180 GDALDriverH outputDriver = openOutputDriver();
183 mLastError = QObject::tr(
"Could not obtain driver for %1" ).arg( mOutputFormat );
188 if ( !outputDataset )
190 mLastError = QObject::tr(
"Could not create output %1" ).arg( mOutputFile );
195 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
197 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
199#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
200 const bool hasReportsDuringClose = GDALDatasetGetCloseReportsProgress( outputDataset.get() );
201 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
203 constexpr double maxProgressDuringBlockWriting = 100.0;
207 if ( !requiresMatrix )
210 std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
211 std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries;
215 QString layerRef( r->toString().remove( 0, 1 ) );
217 if ( !inputBlocks.count( layerRef ) )
221 if ( ref.ref == layerRef )
223 uniqueRasterEntries[layerRef] = ref;
224 inputBlocks[layerRef] = std::make_unique<QgsRasterBlock>();
231 QMap<QString, QgsRasterBlock *> _rasterData;
233 std::vector<float> castedResult(
static_cast<size_t>( mNumOutputColumns ), 0 );
234 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
235 for (
size_t row = 0; row < static_cast<size_t>( mNumOutputRows ); ++row )
239 feedback->
setProgress( maxProgressDuringBlockWriting *
static_cast<double>( row ) / mNumOutputRows );
253 for (
auto &layerRef : inputBlocks )
262 layerRef.second.reset( proj.
block( ref.
bandNumber, rect, mNumOutputColumns, 1 ) );
271 QgsRasterMatrix resultMatrix( mNumOutputColumns, 1,
nullptr, mNoDataValue );
274 for (
const auto &layerRef : inputBlocks )
276 _rasterData.insert( layerRef.first, layerRef.second.get() );
279 if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
281 std::copy( resultMatrix.
data(), resultMatrix.
data() + mNumOutputColumns, castedResult.begin() );
282 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, row, mNumOutputColumns, 1, castedResult.data(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
297 feedback->
setProgress( maxProgressDuringBlockWriting );
302 QMap<QString, QgsRasterBlock *> inputBlocks;
303 QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
304 for ( ; it != mRasterEntries.constEnd(); ++it )
306 std::unique_ptr<QgsRasterBlock> block;
308 if ( it->raster->crs() != mOutputCrs )
311 proj.
setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
312 proj.
setInput( it->raster->dataProvider() );
317 block.reset( proj.
block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
320 qDeleteAll( inputBlocks );
326 block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
328 if ( block->isEmpty() )
330 mLastError = QObject::tr(
"Could not allocate required memory for %1" ).arg( it->ref );
331 qDeleteAll( inputBlocks );
334 inputBlocks.insert( it->ref, block.release() );
341 for (
int i = 0; i < mNumOutputRows; ++i )
345 feedback->
setProgress( maxProgressDuringBlockWriting *
static_cast<double>( i ) / mNumOutputRows );
353 if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
355 bool resultIsNumber = resultMatrix.
isNumber();
356 float *calcData =
new float[mNumOutputColumns];
358 for (
int j = 0; j < mNumOutputColumns; ++j )
360 calcData[j] = ( float ) ( resultIsNumber ? resultMatrix.
number() : resultMatrix.
data()[j] );
364 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
373 qDeleteAll( inputBlocks );
382 feedback->
setProgress( maxProgressDuringBlockWriting );
387 qDeleteAll( inputBlocks );
398 GDALComputeRasterStatistics( outputRasterBand,
true,
nullptr,
nullptr,
nullptr,
nullptr,
GdalProgressCallback, feedback );
400#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
401 if ( hasReportsDuringClose && feedback )
404 if ( GDALDatasetRunCloseWithoutDestroyingEx(
420 QString cExpression( calcNode->toString(
true ) );
423 QSet<QString> capturedTexts;
424 for (
const auto &r : std::as_const( nodeList ) )
426 QString s( r->toString().remove( 0, 1 ) );
428 capturedTexts.insert( s );
436 QgsRasterLayer *layer =
nullptr;
445 std::vector<LayerRef> inputRefs;
446 size_t refCounter = 0;
447 for (
const auto &r : capturedTexts )
449 if ( r.startsWith(
'"' ) )
451 QStringList parts( r.split(
'@' ) );
452 if ( parts.count() != 2 )
457 entry.band = parts[1].toInt( &ok );
458 for (
const auto &ref : std::as_const( mRasterEntries ) )
460 if ( ref.ref == entry.name )
461 entry.layer = ref.raster;
463 if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
465 entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
466 switch ( entry.layer->dataProvider()->dataType( entry.band ) )
469 entry.typeName = u
"unsigned char"_s;
472 entry.typeName = u
"char"_s;
475 entry.typeName = u
"unsigned short"_s;
478 entry.typeName = u
"short"_s;
481 entry.typeName = u
"unsigned int"_s;
484 entry.typeName = u
"int"_s;
487 entry.typeName = u
"float"_s;
493 entry.typeName = u
"double"_s;
504 entry.bufferSize = entry.dataSize * mNumOutputColumns;
505 entry.index = refCounter;
506 entry.varName = u
"input_raster_%1_band_%2"_s
509 inputRefs.push_back( entry );
520 std::vector<cl::Buffer> inputBuffers;
521 inputBuffers.reserve( inputRefs.size() );
522 QStringList inputArgs;
523 for (
const auto &ref : inputRefs )
525 cExpression.replace( u
"\"%1\""_s.arg( ref.name ), u
"%1[i]"_s.arg( ref.varName ) );
526 inputArgs.append( u
"__global %1 *%2"_s
527 .arg( ref.typeName, ref.varName ) );
528 inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize,
nullptr,
nullptr ) );
534 QString programTemplate( R
"CL(
537 // Expression: ##EXPRESSION_ORIGINAL##
538 __kernel void rasterCalculator( ##INPUT##
539 __global float *resultLine
542 // Get the index of the current element
543 const int i = get_global_id(0);
544 // Check for nodata in input
545 if ( ##INPUT_NODATA_CHECK## )
546 resultLine[i] = -FLT_MAX;
549 resultLine[i] = ##EXPRESSION##;
553 QStringList inputDesc;
554 QStringList inputNoDataCheck;
555 for (
const auto &ref : inputRefs )
557 inputDesc.append( u
" // %1 = %2"_s.arg( ref.varName, ref.name ) );
558 if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
560 inputNoDataCheck.append( u
"(float) %1[i] == (float) %2"_s.arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ),
'g', 10 ) ) );
564 programTemplate = programTemplate.replace(
"##INPUT_NODATA_CHECK##"_L1, inputNoDataCheck.isEmpty() ? u
"false"_s : inputNoDataCheck.join(
" || "_L1 ) );
565 programTemplate = programTemplate.replace(
"##INPUT_DESC##"_L1, inputDesc.join(
'\n' ) );
566 programTemplate = programTemplate.replace(
"##INPUT##"_L1, !inputArgs.isEmpty() ? ( inputArgs.join(
',' ).append(
',' ) ) : QChar(
' ' ) );
567 programTemplate = programTemplate.replace(
"##EXPRESSION##"_L1, cExpression );
568 programTemplate = programTemplate.replace(
"##EXPRESSION_ORIGINAL##"_L1, calcNode->toString() );
577 Q_ASSERT(
sizeof(
float ) == 4 );
578 std::size_t resultBufferSize( 4 *
static_cast<size_t>( mNumOutputColumns ) );
579 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, resultBufferSize,
nullptr,
nullptr );
581 auto kernel = cl::Kernel( program,
"rasterCalculator" );
583 for (
unsigned int i = 0; i < inputBuffers.size(); i++ )
585 kernel.setArg( i, inputBuffers.at( i ) );
587 kernel.setArg(
static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
589 QgsOpenClUtils::CPLAllocator<float> resultLine(
static_cast<size_t>( mNumOutputColumns ) );
592 GDALDriverH outputDriver = openOutputDriver();
595 mLastError = QObject::tr(
"Could not obtain driver for %1" ).arg( mOutputFormat );
600 if ( !outputDataset )
602 mLastError = QObject::tr(
"Could not create output %1" ).arg( mOutputFile );
606#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
607 const bool hasReportsDuringClose = GDALDatasetGetCloseReportsProgress( outputDataset.get() );
608 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
610 constexpr double maxProgressDuringBlockWriting = 100.0;
616 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
617 if ( !outputRasterBand )
620 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
623 std::unique_ptr<QgsRasterBlock> block;
626 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
627 for (
int line = 0; line < mNumOutputRows; line++ )
636 feedback->
setProgress( maxProgressDuringBlockWriting *
static_cast<double>( line ) / mNumOutputRows );
640 for (
const auto &ref : inputRefs )
643 QgsRectangle rect( mOutputRectangle );
644 rect.setYMaximum( rect.yMaximum() - rowHeight * line );
645 rect.setYMinimum( rect.yMaximum() - rowHeight );
649 if ( ref.layer->crs() != mOutputCrs )
651 QgsRasterProjector proj;
652 proj.
setCrs( ref.layer->crs(), mOutputCrs, ref.layer->transformContext() );
653 proj.
setInput( ref.layer->dataProvider() );
655 block.reset( proj.
block( ref.band, rect, mNumOutputColumns, 1 ) );
659 block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
666 Q_ASSERT( ref.bufferSize ==
static_cast<size_t>( block->data().size() ) );
667 queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0, ref.bufferSize, block->bits() );
670 queue.enqueueNDRangeKernel(
673 cl::NDRange( mNumOutputColumns )
677 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, resultBufferSize, resultLine.get() );
682 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
695 inputBuffers.clear();
697 GDALComputeRasterStatistics( outputRasterBand,
true,
nullptr,
nullptr,
nullptr,
nullptr,
GdalProgressCallback, feedback );
699#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
700 if ( hasReportsDuringClose && feedback )
702 QgsGdalProgressAdapter progress( feedback, maxProgressDuringBlockWriting );
703 if ( GDALDatasetRunCloseWithoutDestroyingEx(
713 catch ( cl::Error &e )
715 mLastError = e.what();
723GDALDriverH QgsRasterCalculator::openOutputDriver()
726 GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
745 gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
746 CSLDestroy( papszOptions );
747 if ( !outputDataset )
753 double geotransform[6];
754 outputGeoTransform( geotransform );
755 GDALSetGeoTransform( outputDataset.get(), geotransform );
757 return outputDataset;
760void QgsRasterCalculator::outputGeoTransform(
double *transform )
const
762 transform[0] = mOutputRectangle.xMinimum();
763 transform[1] = mOutputRectangle.width() / mNumOutputColumns;
765 transform[3] = mOutputRectangle.yMaximum();
767 transform[5] = -mOutputRectangle.height() / mNumOutputRows;
777 QVector<QgsRasterCalculatorEntry> availableEntries;
782 entry.ref = u
"%1@%2"_s.arg( entry.raster ? entry.raster->name() : QString() ).arg( entry.bandNumber );
786 for (
const auto &
ref : std::as_const( availableEntries ) )
789 if ( !( entry.raster &&
ref.raster ) )
792 if (
ref.raster->publicSource() == entry.raster->publicSource() )
794 if (
ref.bandNumber != entry.bandNumber )
804 if (
ref.ref == entry.ref )
807 entry.ref = u
"%1_%2@%3"_s.arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
815 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
816 for ( ; layerIt != layers.constEnd(); ++layerIt )
818 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
822 for (
int i = 0; i < rlayer->
bandCount(); ++i )
827 if ( !uniqueRasterBandIdentifier( entry ) )
829 availableEntries.push_back( entry );
833 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.
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)