20#include <cpl_string.h>
21#include <gdalwarper.h>
46 Q_UNUSED( pszMessage )
48 static double sDfLastComplete = -1.0;
52 if ( sDfLastComplete > dfComplete )
54 if ( sDfLastComplete >= 1.0 )
55 sDfLastComplete = -1.0;
57 sDfLastComplete = dfComplete;
60 if ( std::floor( sDfLastComplete * 10 ) != std::floor( dfComplete * 10 ) )
65 sDfLastComplete = dfComplete;
74 : mFormulaString( formulaString )
75 , mOutputFile( outputFile )
76 , mOutputFormat( outputFormat )
77 , mOutputRectangle( outputExtent )
78 , mNumOutputColumns( nOutputColumns )
79 , mNumOutputRows( nOutputRows )
80 , mRasterEntries( rasterEntries )
81 , mTransformContext( transformContext )
84 if ( !mRasterEntries.isEmpty() )
86 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
91 : mFormulaString( formulaString )
92 , mOutputFile( outputFile )
93 , mOutputFormat( outputFormat )
94 , mOutputRectangle( outputExtent )
95 , mOutputCrs( outputCrs )
96 , mNumOutputColumns( nOutputColumns )
97 , mNumOutputRows( nOutputRows )
98 , mRasterEntries( rasterEntries )
99 , mTransformContext( transformContext )
105 : mFormulaString( formulaString )
106 , mOutputFile( outputFile )
107 , mOutputFormat( outputFormat )
108 , mOutputRectangle( outputExtent )
109 , mNumOutputColumns( nOutputColumns )
110 , mNumOutputRows( nOutputRows )
111 , mRasterEntries( rasterEntries )
114 if ( !mRasterEntries.isEmpty() )
116 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
124 : mFormulaString( formulaString )
125 , mOutputFile( outputFile )
126 , mOutputFormat( outputFormat )
127 , mOutputRectangle( outputExtent )
128 , mOutputCrs( outputCrs )
129 , mNumOutputColumns( nOutputColumns )
130 , mNumOutputRows( nOutputRows )
131 , mRasterEntries( rasterEntries )
132 , mTransformContext(
QgsProject::instance()->transformContext() )
149 for (
const auto &entry : std::as_const( mRasterEntries ) )
153 mLastError = QObject::tr(
"No raster layer for entry %1" ).arg( entry.ref );
156 if ( entry.bandNumber <= 0 || entry.bandNumber > entry.raster->bandCount() )
158 mLastError = QObject::tr(
"Band number %1 is not valid for entry %2" ).arg( entry.bandNumber ).arg( entry.ref );
172 return processCalculationGPU( std::move( calcNode ), feedback );
177 GDALDriverH outputDriver = openOutputDriver();
180 mLastError = QObject::tr(
"Could not obtain driver for %1" ).arg( mOutputFormat );
185 if ( !outputDataset )
187 mLastError = QObject::tr(
"Could not create output %1" ).arg( mOutputFile );
192 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
194 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
197 if ( !requiresMatrix )
200 std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
201 std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries;
205 QString layerRef( r->toString().remove( 0, 1 ) );
207 if ( !inputBlocks.count( layerRef ) )
211 if ( ref.ref == layerRef )
213 uniqueRasterEntries[layerRef] = ref;
214 inputBlocks[layerRef] = std::make_unique<QgsRasterBlock>();
221 QMap<QString, QgsRasterBlock *> _rasterData;
223 std::vector<float> castedResult(
static_cast<size_t>( mNumOutputColumns ), 0 );
224 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
225 for (
size_t row = 0; row < static_cast<size_t>( mNumOutputRows ); ++row )
229 feedback->
setProgress( 100.0 *
static_cast<double>( row ) / mNumOutputRows );
243 for (
auto &layerRef : inputBlocks )
252 layerRef.second.reset( proj.
block( ref.
bandNumber, rect, mNumOutputColumns, 1 ) );
261 QgsRasterMatrix resultMatrix( mNumOutputColumns, 1,
nullptr, mNoDataValue );
264 for (
const auto &layerRef : inputBlocks )
266 _rasterData.insert( layerRef.first, layerRef.second.get() );
269 if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
271 std::copy( resultMatrix.
data(), resultMatrix.
data() + mNumOutputColumns, castedResult.begin() );
272 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, row, mNumOutputColumns, 1, castedResult.data(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
292 QMap<QString, QgsRasterBlock *> inputBlocks;
293 QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
294 for ( ; it != mRasterEntries.constEnd(); ++it )
296 std::unique_ptr<QgsRasterBlock> block;
298 if ( it->raster->crs() != mOutputCrs )
301 proj.
setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
302 proj.
setInput( it->raster->dataProvider() );
307 block.reset( proj.
block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
310 qDeleteAll( inputBlocks );
316 block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
318 if ( block->isEmpty() )
320 mLastError = QObject::tr(
"Could not allocate required memory for %1" ).arg( it->ref );
321 qDeleteAll( inputBlocks );
324 inputBlocks.insert( it->ref, block.release() );
331 for (
int i = 0; i < mNumOutputRows; ++i )
335 feedback->
setProgress( 100.0 *
static_cast<double>( i ) / mNumOutputRows );
343 if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
345 bool resultIsNumber = resultMatrix.
isNumber();
346 float *calcData =
new float[mNumOutputColumns];
348 for (
int j = 0; j < mNumOutputColumns; ++j )
350 calcData[j] = ( float ) ( resultIsNumber ? resultMatrix.
number() : resultMatrix.
data()[j] );
354 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
363 qDeleteAll( inputBlocks );
377 qDeleteAll( inputBlocks );
388 GDALComputeRasterStatistics( outputRasterBand,
true,
nullptr,
nullptr,
nullptr,
nullptr,
GdalProgressCallback, feedback );
396 QString cExpression( calcNode->toString(
true ) );
399 QSet<QString> capturedTexts;
400 for (
const auto &r : std::as_const( nodeList ) )
402 QString s( r->toString().remove( 0, 1 ) );
404 capturedTexts.insert( s );
412 QgsRasterLayer *layer =
nullptr;
421 std::vector<LayerRef> inputRefs;
422 size_t refCounter = 0;
423 for (
const auto &r : capturedTexts )
425 if ( r.startsWith(
'"' ) )
427 QStringList parts( r.split(
'@' ) );
428 if ( parts.count() != 2 )
433 entry.band = parts[1].toInt( &ok );
434 for (
const auto &ref : std::as_const( mRasterEntries ) )
436 if ( ref.ref == entry.name )
437 entry.layer = ref.raster;
439 if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
441 entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
442 switch ( entry.layer->dataProvider()->dataType( entry.band ) )
445 entry.typeName = QStringLiteral(
"unsigned char" );
448 entry.typeName = QStringLiteral(
"char" );
451 entry.typeName = QStringLiteral(
"unsigned short" );
454 entry.typeName = QStringLiteral(
"short" );
457 entry.typeName = QStringLiteral(
"unsigned int" );
460 entry.typeName = QStringLiteral(
"int" );
463 entry.typeName = QStringLiteral(
"float" );
469 entry.typeName = QStringLiteral(
"double" );
480 entry.bufferSize = entry.dataSize * mNumOutputColumns;
481 entry.index = refCounter;
482 entry.varName = QStringLiteral(
"input_raster_%1_band_%2" )
485 inputRefs.push_back( entry );
496 std::vector<cl::Buffer> inputBuffers;
497 inputBuffers.reserve( inputRefs.size() );
498 QStringList inputArgs;
499 for (
const auto &ref : inputRefs )
501 cExpression.replace( QStringLiteral(
"\"%1\"" ).arg( ref.name ), QStringLiteral(
"%1[i]" ).arg( ref.varName ) );
502 inputArgs.append( QStringLiteral(
"__global %1 *%2" )
503 .arg( ref.typeName, ref.varName ) );
504 inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize,
nullptr,
nullptr ) );
510 QString programTemplate( R
"CL(
513 // Expression: ##EXPRESSION_ORIGINAL##
514 __kernel void rasterCalculator( ##INPUT##
515 __global float *resultLine
518 // Get the index of the current element
519 const int i = get_global_id(0);
520 // Check for nodata in input
521 if ( ##INPUT_NODATA_CHECK## )
522 resultLine[i] = -FLT_MAX;
525 resultLine[i] = ##EXPRESSION##;
529 QStringList inputDesc;
530 QStringList inputNoDataCheck;
531 for (
const auto &ref : inputRefs )
533 inputDesc.append( QStringLiteral(
" // %1 = %2" ).arg( ref.varName, ref.name ) );
534 if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
536 inputNoDataCheck.append( QStringLiteral(
"(float) %1[i] == (float) %2" ).arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ),
'g', 10 ) ) );
540 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT_NODATA_CHECK##" ), inputNoDataCheck.isEmpty() ? QStringLiteral(
"false" ) : inputNoDataCheck.join( QLatin1String(
" || " ) ) );
541 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT_DESC##" ), inputDesc.join(
'\n' ) );
542 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT##" ), !inputArgs.isEmpty() ? ( inputArgs.join(
',' ).append(
',' ) ) : QChar(
' ' ) );
543 programTemplate = programTemplate.replace( QLatin1String(
"##EXPRESSION##" ), cExpression );
544 programTemplate = programTemplate.replace( QLatin1String(
"##EXPRESSION_ORIGINAL##" ), calcNode->toString() );
553 Q_ASSERT(
sizeof(
float ) == 4 );
554 std::size_t resultBufferSize( 4 *
static_cast<size_t>( mNumOutputColumns ) );
555 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, resultBufferSize,
nullptr,
nullptr );
557 auto kernel = cl::Kernel( program,
"rasterCalculator" );
559 for (
unsigned int i = 0; i < inputBuffers.size(); i++ )
561 kernel.setArg( i, inputBuffers.at( i ) );
563 kernel.setArg(
static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
565 QgsOpenClUtils::CPLAllocator<float> resultLine(
static_cast<size_t>( mNumOutputColumns ) );
568 GDALDriverH outputDriver = openOutputDriver();
571 mLastError = QObject::tr(
"Could not obtain driver for %1" ).arg( mOutputFormat );
576 if ( !outputDataset )
578 mLastError = QObject::tr(
"Could not create output %1" ).arg( mOutputFile );
585 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
586 if ( !outputRasterBand )
589 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
592 std::unique_ptr<QgsRasterBlock> block;
595 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
596 for (
int line = 0; line < mNumOutputRows; line++ )
605 feedback->
setProgress( 100.0 *
static_cast<double>( line ) / mNumOutputRows );
609 for (
const auto &ref : inputRefs )
612 QgsRectangle rect( mOutputRectangle );
613 rect.setYMaximum( rect.yMaximum() - rowHeight * line );
614 rect.setYMinimum( rect.yMaximum() - rowHeight );
618 if ( ref.layer->crs() != mOutputCrs )
620 QgsRasterProjector proj;
621 proj.
setCrs( ref.layer->crs(), mOutputCrs, ref.layer->transformContext() );
622 proj.
setInput( ref.layer->dataProvider() );
624 block.reset( proj.
block( ref.band, rect, mNumOutputColumns, 1 ) );
628 block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
635 Q_ASSERT( ref.bufferSize ==
static_cast<size_t>( block->data().size() ) );
636 queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0, ref.bufferSize, block->bits() );
639 queue.enqueueNDRangeKernel(
642 cl::NDRange( mNumOutputColumns )
646 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, resultBufferSize, resultLine.get() );
651 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
664 inputBuffers.clear();
666 GDALComputeRasterStatistics( outputRasterBand,
true,
nullptr,
nullptr,
nullptr,
nullptr,
GdalProgressCallback, feedback );
668 catch ( cl::Error &e )
670 mLastError = e.what();
678GDALDriverH QgsRasterCalculator::openOutputDriver()
681 GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
700 gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
701 CSLDestroy( papszOptions );
702 if ( !outputDataset )
708 double geotransform[6];
709 outputGeoTransform( geotransform );
710 GDALSetGeoTransform( outputDataset.get(), geotransform );
712 return outputDataset;
715void QgsRasterCalculator::outputGeoTransform(
double *transform )
const
717 transform[0] = mOutputRectangle.xMinimum();
718 transform[1] = mOutputRectangle.width() / mNumOutputColumns;
720 transform[3] = mOutputRectangle.yMaximum();
722 transform[5] = -mOutputRectangle.height() / mNumOutputRows;
732 QVector<QgsRasterCalculatorEntry> availableEntries;
737 entry.ref = QStringLiteral(
"%1@%2" ).arg( entry.raster ? entry.raster->name() : QString() ).arg( entry.bandNumber );
741 for (
const auto &
ref : std::as_const( availableEntries ) )
744 if ( !( entry.raster &&
ref.raster ) )
747 if (
ref.raster->publicSource() == entry.raster->publicSource() )
749 if (
ref.bandNumber != entry.bandNumber )
759 if (
ref.ref == entry.ref )
762 entry.ref = QStringLiteral(
"%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
770 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
771 for ( ; layerIt != layers.constEnd(); ++layerIt )
773 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
777 for (
int i = 0; i < rlayer->
bandCount(); ++i )
782 if ( !uniqueRasterBandIdentifier( entry ) )
784 availableEntries.push_back( entry );
788 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.
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)