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 float outputNodataValue = -FLT_MAX;
194 GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue );
198 if ( !requiresMatrix )
201 std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
202 std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries;
206 QString layerRef( r->toString().remove( 0, 1 ) );
208 if ( !inputBlocks.count( layerRef ) )
212 if ( ref.ref == layerRef )
214 uniqueRasterEntries[layerRef] = ref;
215 inputBlocks[layerRef] = std::make_unique<QgsRasterBlock>();
222 QMap<QString, QgsRasterBlock *> _rasterData;
224 std::vector<float> castedResult(
static_cast<size_t>( mNumOutputColumns ), 0 );
225 auto rowHeight = mOutputRectangle.
height() / mNumOutputRows;
226 for (
size_t row = 0; row < static_cast<size_t>( mNumOutputRows ); ++row )
230 feedback->
setProgress( 100.0 *
static_cast<double>( row ) / mNumOutputRows );
244 for (
auto &layerRef : inputBlocks )
253 layerRef.second.reset( proj.
block( ref.
bandNumber, rect, mNumOutputColumns, 1 ) );
262 QgsRasterMatrix resultMatrix( mNumOutputColumns, 1,
nullptr, outputNodataValue );
265 for (
const auto &layerRef : inputBlocks )
267 _rasterData.insert( layerRef.first, layerRef.second.get() );
270 if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
272 std::copy( resultMatrix.
data(), resultMatrix.
data() + mNumOutputColumns, castedResult.begin() );
273 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, row, mNumOutputColumns, 1, castedResult.data(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
293 QMap<QString, QgsRasterBlock *> inputBlocks;
294 QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
295 for ( ; it != mRasterEntries.constEnd(); ++it )
297 std::unique_ptr<QgsRasterBlock> block;
299 if ( it->raster->crs() != mOutputCrs )
302 proj.
setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
303 proj.
setInput( it->raster->dataProvider() );
308 block.reset( proj.
block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
311 qDeleteAll( inputBlocks );
317 block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
319 if ( block->isEmpty() )
321 mLastError = QObject::tr(
"Could not allocate required memory for %1" ).arg( it->ref );
322 qDeleteAll( inputBlocks );
325 inputBlocks.insert( it->ref, block.release() );
332 for (
int i = 0; i < mNumOutputRows; ++i )
336 feedback->
setProgress( 100.0 *
static_cast<double>( i ) / mNumOutputRows );
344 if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
346 bool resultIsNumber = resultMatrix.
isNumber();
347 float *calcData =
new float[mNumOutputColumns];
349 for (
int j = 0; j < mNumOutputColumns; ++j )
351 calcData[j] = ( float ) ( resultIsNumber ? resultMatrix.
number() : resultMatrix.
data()[j] );
355 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
364 qDeleteAll( inputBlocks );
378 qDeleteAll( inputBlocks );
389 GDALComputeRasterStatistics( outputRasterBand,
true,
nullptr,
nullptr,
nullptr,
nullptr,
GdalProgressCallback, feedback );
397 QString cExpression( calcNode->toString(
true ) );
400 QSet<QString> capturedTexts;
401 for (
const auto &r : std::as_const( nodeList ) )
403 QString s( r->toString().remove( 0, 1 ) );
405 capturedTexts.insert( s );
422 std::vector<LayerRef> inputRefs;
423 size_t refCounter = 0;
424 for (
const auto &r : capturedTexts )
426 if ( r.startsWith(
'"' ) )
428 QStringList parts( r.split(
'@' ) );
429 if ( parts.count() != 2 )
434 entry.band = parts[1].toInt( &ok );
435 for (
const auto &ref : std::as_const( mRasterEntries ) )
437 if ( ref.ref == entry.name )
438 entry.layer = ref.raster;
440 if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
442 entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
443 switch ( entry.layer->dataProvider()->dataType( entry.band ) )
446 entry.typeName = QStringLiteral(
"unsigned char" );
449 entry.typeName = QStringLiteral(
"signed char" );
452 entry.typeName = QStringLiteral(
"unsigned int" );
455 entry.typeName = QStringLiteral(
"short" );
458 entry.typeName = QStringLiteral(
"unsigned int" );
461 entry.typeName = QStringLiteral(
"int" );
464 entry.typeName = QStringLiteral(
"float" );
470 entry.typeName = QStringLiteral(
"double" );
481 entry.bufferSize = entry.dataSize * mNumOutputColumns;
482 entry.index = refCounter;
483 entry.varName = QStringLiteral(
"input_raster_%1_band_%2" )
486 inputRefs.push_back( entry );
497 std::vector<cl::Buffer> inputBuffers;
498 inputBuffers.reserve( inputRefs.size() );
499 QStringList inputArgs;
500 for (
const auto &ref : inputRefs )
502 cExpression.replace( QStringLiteral(
"\"%1\"" ).arg( ref.name ), QStringLiteral(
"%1[i]" ).arg( ref.varName ) );
503 inputArgs.append( QStringLiteral(
"__global %1 *%2" )
504 .arg( ref.typeName, ref.varName ) );
505 inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize,
nullptr,
nullptr ) );
511 QString programTemplate( R
"CL(
514 // Expression: ##EXPRESSION_ORIGINAL##
515 __kernel void rasterCalculator( ##INPUT##
516 __global float *resultLine
519 // Get the index of the current element
520 const int i = get_global_id(0);
521 // Check for nodata in input
522 if ( ##INPUT_NODATA_CHECK## )
523 resultLine[i] = -FLT_MAX;
526 resultLine[i] = ##EXPRESSION##;
530 QStringList inputDesc;
531 QStringList inputNoDataCheck;
532 for (
const auto &ref : inputRefs )
534 inputDesc.append( QStringLiteral(
" // %1 = %2" ).arg( ref.varName, ref.name ) );
535 if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
537 inputNoDataCheck.append( QStringLiteral(
"(float) %1[i] == (float) %2" ).arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ),
'g', 10 ) ) );
541 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT_NODATA_CHECK##" ), inputNoDataCheck.isEmpty() ? QStringLiteral(
"false" ) : inputNoDataCheck.join( QLatin1String(
" || " ) ) );
542 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT_DESC##" ), inputDesc.join(
'\n' ) );
543 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT##" ), !inputArgs.isEmpty() ? ( inputArgs.join(
',' ).append(
',' ) ) : QChar(
' ' ) );
544 programTemplate = programTemplate.replace( QLatin1String(
"##EXPRESSION##" ), cExpression );
545 programTemplate = programTemplate.replace( QLatin1String(
"##EXPRESSION_ORIGINAL##" ), calcNode->toString() );
554 Q_ASSERT(
sizeof(
float ) == 4 );
555 std::size_t resultBufferSize( 4 *
static_cast<size_t>( mNumOutputColumns ) );
556 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, resultBufferSize,
nullptr,
nullptr );
558 auto kernel = cl::Kernel( program,
"rasterCalculator" );
560 for (
unsigned int i = 0; i < inputBuffers.size(); i++ )
562 kernel.setArg( i, inputBuffers.at( i ) );
564 kernel.setArg(
static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
569 GDALDriverH outputDriver = openOutputDriver();
572 mLastError = QObject::tr(
"Could not obtain driver for %1" ).arg( mOutputFormat );
577 if ( !outputDataset )
579 mLastError = QObject::tr(
"Could not create output %1" ).arg( mOutputFile );
586 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
587 if ( !outputRasterBand )
590 const float outputNodataValue = -FLT_MAX;
591 GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue );
594 std::unique_ptr<QgsRasterBlock> block;
597 auto rowHeight = mOutputRectangle.
height() / mNumOutputRows;
598 for (
int line = 0; line < mNumOutputRows; line++ )
607 feedback->
setProgress( 100.0 *
static_cast<double>( line ) / mNumOutputRows );
611 for (
const auto &ref : inputRefs )
615 rect.setYMaximum( rect.yMaximum() - rowHeight * line );
616 rect.setYMinimum( rect.yMaximum() - rowHeight );
620 if ( ref.layer->crs() != mOutputCrs )
623 proj.
setCrs( ref.layer->crs(), mOutputCrs, ref.layer->transformContext() );
624 proj.
setInput( ref.layer->dataProvider() );
626 block.reset( proj.
block( ref.band, rect, mNumOutputColumns, 1 ) );
630 block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
637 Q_ASSERT( ref.bufferSize ==
static_cast<size_t>( block->data().size() ) );
638 queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0, ref.bufferSize, block->bits() );
641 queue.enqueueNDRangeKernel(
644 cl::NDRange( mNumOutputColumns )
648 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, resultBufferSize, resultLine.get() );
653 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
666 inputBuffers.clear();
668 GDALComputeRasterStatistics( outputRasterBand,
true,
nullptr,
nullptr,
nullptr,
nullptr,
GdalProgressCallback, feedback );
670 catch ( cl::Error &e )
672 mLastError = e.what();
680GDALDriverH QgsRasterCalculator::openOutputDriver()
683 GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
701 char **papszOptions =
nullptr;
702 gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
703 if ( !outputDataset )
709 double geotransform[6];
710 outputGeoTransform( geotransform );
711 GDALSetGeoTransform( outputDataset.get(), geotransform );
713 return outputDataset;
716void QgsRasterCalculator::outputGeoTransform(
double *transform )
const
718 transform[0] = mOutputRectangle.
xMinimum();
719 transform[1] = mOutputRectangle.
width() / mNumOutputColumns;
721 transform[3] = mOutputRectangle.
yMaximum();
723 transform[5] = -mOutputRectangle.
height() / mNumOutputRows;
733 QVector<QgsRasterCalculatorEntry> availableEntries;
738 entry.ref = QStringLiteral(
"%1@%2" ).arg( entry.raster->name() ).arg( entry.bandNumber );
742 for (
const auto &
ref : std::as_const( availableEntries ) )
745 if ( !( entry.raster &&
ref.raster ) )
748 if (
ref.raster->publicSource() == entry.raster->publicSource() )
750 if (
ref.bandNumber != entry.bandNumber )
760 if (
ref.ref == entry.ref )
763 entry.ref = QStringLiteral(
"%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
771 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
772 for ( ; layerIt != layers.constEnd(); ++layerIt )
774 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
778 for (
int i = 0; i < rlayer->
bandCount(); ++i )
783 if ( !uniqueRasterBandIdentifier( entry ) )
785 availableEntries.push_back( entry );
789 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, 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...