31#include <cpl_string.h>
32#include <gdalwarper.h>
44 const char *pszMessage,
47 Q_UNUSED( pszMessage )
49 static double sDfLastComplete = -1.0;
53 if ( sDfLastComplete > dfComplete )
55 if ( sDfLastComplete >= 1.0 )
56 sDfLastComplete = -1.0;
58 sDfLastComplete = dfComplete;
61 if ( std::floor( sDfLastComplete * 10 ) != std::floor( dfComplete * 10 ) )
66 sDfLastComplete = dfComplete;
75 : mFormulaString( formulaString )
76 , mOutputFile( outputFile )
77 , mOutputFormat( outputFormat )
78 , mOutputRectangle( outputExtent )
79 , mNumOutputColumns( nOutputColumns )
80 , mNumOutputRows( nOutputRows )
81 , mRasterEntries( rasterEntries )
82 , mTransformContext( transformContext )
90 : mFormulaString( formulaString )
91 , mOutputFile( outputFile )
92 , mOutputFormat( outputFormat )
93 , mOutputRectangle( outputExtent )
95 , mNumOutputColumns( nOutputColumns )
96 , mNumOutputRows( nOutputRows )
97 , mRasterEntries( rasterEntries )
98 , mTransformContext( transformContext )
105 const QgsRectangle &outputExtent,
int nOutputColumns,
int nOutputRows,
const QVector<QgsRasterCalculatorEntry> &rasterEntries )
106 : mFormulaString( formulaString )
107 , mOutputFile( outputFile )
108 , mOutputFormat( outputFormat )
109 , mOutputRectangle( outputExtent )
110 , mNumOutputColumns( nOutputColumns )
111 , mNumOutputRows( nOutputRows )
112 , mRasterEntries( rasterEntries )
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 );
165 bool requiresMatrix = ! calcNode->findNodes( QgsRasterCalcNode::Type::tMatrix ).isEmpty();
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;
203 const QList<const QgsRasterCalcNode *> rasterRefNodes = calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef );
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 )
275 QgsDebugMsg( QStringLiteral(
"RasterIO error!" ) );
293 QMap< QString, QgsRasterBlock * > inputBlocks;
294 QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
295 for ( ; it != mRasterEntries.constEnd(); ++it )
298 std::unique_ptr< QgsRasterBlock > block;
300 if ( it->raster->crs() != mOutputCrs )
303 proj.
setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
304 proj.
setInput( it->raster->dataProvider() );
309 block.reset( proj.
block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
312 qDeleteAll( inputBlocks );
318 block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
320 if ( block->isEmpty() )
322 mLastError = QObject::tr(
"Could not allocate required memory for %1" ).arg( it->ref );
323 qDeleteAll( inputBlocks );
326 inputBlocks.insert( it->ref, block.release() );
333 for (
int i = 0; i < mNumOutputRows; ++i )
337 feedback->
setProgress( 100.0 *
static_cast< double >( i ) / mNumOutputRows );
345 if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
347 bool resultIsNumber = resultMatrix.
isNumber();
348 float *calcData =
new float[mNumOutputColumns];
350 for (
int j = 0; j < mNumOutputColumns; ++j )
352 calcData[j] = ( float )( resultIsNumber ? resultMatrix.
number() : resultMatrix.
data()[j] );
356 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
358 QgsDebugMsg( QStringLiteral(
"RasterIO error!" ) );
365 qDeleteAll( inputBlocks );
380 qDeleteAll( inputBlocks );
392 GDALComputeRasterStatistics( outputRasterBand,
true,
nullptr,
nullptr,
nullptr,
nullptr,
GdalProgressCallback, feedback );
401 QString cExpression( calcNode->toString(
true ) );
403 QList<const QgsRasterCalcNode *> nodeList( calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef ) );
404 QSet<QString> capturedTexts;
405 for (
const auto &r : std::as_const( nodeList ) )
407 QString s( r->toString().remove( 0, 1 ) );
409 capturedTexts.insert( s );
426 std::vector<LayerRef> inputRefs;
427 size_t refCounter = 0;
428 for (
const auto &r : capturedTexts )
430 if ( r.startsWith(
'"' ) )
432 QStringList parts( r.split(
'@' ) );
433 if ( parts.count() != 2 )
438 entry.band = parts[1].toInt( &ok );
439 for (
const auto &ref : std::as_const( mRasterEntries ) )
441 if ( ref.ref == entry.name )
442 entry.layer = ref.raster;
444 if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
446 entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
447 switch ( entry.layer->dataProvider()->dataType( entry.band ) )
450 entry.typeName = QStringLiteral(
"unsigned char" );
453 entry.typeName = QStringLiteral(
"unsigned int" );
456 entry.typeName = QStringLiteral(
"short" );
459 entry.typeName = QStringLiteral(
"unsigned int" );
462 entry.typeName = QStringLiteral(
"int" );
465 entry.typeName = QStringLiteral(
"float" );
471 entry.typeName = QStringLiteral(
"double" );
476 entry.bufferSize = entry.dataSize * mNumOutputColumns;
477 entry.index = refCounter;
478 entry.varName = QStringLiteral(
"input_raster_%1_band_%2" )
481 inputRefs.push_back( entry );
492 std::vector<cl::Buffer> inputBuffers;
493 inputBuffers.reserve( inputRefs.size() );
494 QStringList inputArgs;
495 for (
const auto &ref : inputRefs )
497 cExpression.replace( QStringLiteral(
"\"%1\"" ).arg( ref.name ), QStringLiteral(
"%1[i]" ).arg( ref.varName ) );
498 inputArgs.append( QStringLiteral(
"__global %1 *%2" )
499 .arg( ref.typeName, ref.varName ) );
500 inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize,
nullptr,
nullptr ) );
506 QString programTemplate( R
"CL(
509 // Expression: ##EXPRESSION_ORIGINAL##
510 __kernel void rasterCalculator( ##INPUT##
511 __global float *resultLine
514 // Get the index of the current element
515 const int i = get_global_id(0);
516 // Check for nodata in input
517 if ( ##INPUT_NODATA_CHECK## )
518 resultLine[i] = -FLT_MAX;
521 resultLine[i] = ##EXPRESSION##;
525 QStringList inputDesc;
526 QStringList inputNoDataCheck;
527 for (
const auto &ref : inputRefs )
529 inputDesc.append( QStringLiteral(
" // %1 = %2" ).arg( ref.varName, ref.name ) );
530 if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
532 inputNoDataCheck.append( QStringLiteral(
"(float) %1[i] == (float) %2" ).arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ),
'g', 10 ) ) );
536 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT_NODATA_CHECK##" ), inputNoDataCheck.isEmpty() ? QStringLiteral(
"false" ) : inputNoDataCheck.join( QLatin1String(
" || " ) ) );
537 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT_DESC##" ), inputDesc.join(
'\n' ) );
538 programTemplate = programTemplate.replace( QLatin1String(
"##INPUT##" ), !inputArgs.isEmpty() ? ( inputArgs.join(
',' ).append(
',' ) ) : QChar(
' ' ) );
539 programTemplate = programTemplate.replace( QLatin1String(
"##EXPRESSION##" ), cExpression );
540 programTemplate = programTemplate.replace( QLatin1String(
"##EXPRESSION_ORIGINAL##" ), calcNode->toString( ) );
549 Q_ASSERT(
sizeof(
float ) == 4 );
550 std::size_t resultBufferSize( 4 *
static_cast<size_t>( mNumOutputColumns ) );
551 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY,
552 resultBufferSize,
nullptr,
nullptr );
554 auto kernel = cl::Kernel( program,
"rasterCalculator" );
556 for (
unsigned int i = 0; i < inputBuffers.size() ; i++ )
558 kernel.setArg( i, inputBuffers.at( i ) );
560 kernel.setArg(
static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
565 GDALDriverH outputDriver = openOutputDriver();
568 mLastError = QObject::tr(
"Could not obtain driver for %1" ).arg( mOutputFormat );
573 if ( !outputDataset )
575 mLastError = QObject::tr(
"Could not create output %1" ).arg( mOutputFile );
582 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
583 if ( !outputRasterBand )
586 const float outputNodataValue = -FLT_MAX;
587 GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue );
590 std::unique_ptr<QgsRasterBlock> block;
593 auto rowHeight = mOutputRectangle.
height() / mNumOutputRows;
594 for (
int line = 0; line < mNumOutputRows; line++ )
603 feedback->
setProgress( 100.0 *
static_cast< double >( line ) / mNumOutputRows );
607 for (
const auto &ref : inputRefs )
611 rect.setYMaximum( rect.yMaximum() - rowHeight * line );
612 rect.setYMinimum( rect.yMaximum() - rowHeight );
616 if ( ref.layer->crs() != mOutputCrs )
619 proj.
setCrs( ref.layer->crs(), mOutputCrs, ref.layer->transformContext() );
620 proj.
setInput( ref.layer->dataProvider() );
622 block.reset( proj.
block( ref.band, rect, mNumOutputColumns, 1 ) );
626 block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
633 Q_ASSERT( ref.bufferSize ==
static_cast<size_t>( block->data().size( ) ) );
634 queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0,
635 ref.bufferSize, block->bits() );
639 queue.enqueueNDRangeKernel(
642 cl::NDRange( mNumOutputColumns )
646 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0,
647 resultBufferSize, resultLine.get() );
652 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
665 inputBuffers.clear();
667 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;
739 entry.ref = QStringLiteral(
"%1@%2" ).arg( entry.raster->name() ).arg( entry.bandNumber );
743 for (
const auto &
ref : std::as_const( availableEntries ) )
746 if ( !( entry.raster &&
ref.raster ) )
749 if (
ref.raster->publicSource() == entry.raster->publicSource() )
751 if (
ref.bandNumber != entry.bandNumber )
761 if (
ref.ref == entry.ref )
764 entry.ref = QStringLiteral(
"%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
772 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
773 for ( ; layerIt != layers.constEnd(); ++layerIt )
775 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
779 for (
int i = 0; i < rlayer->
bandCount(); ++i )
784 if ( ! uniqueRasterBandIdentifier( entry ) )
786 availableEntries.push_back( entry );
790 return availableEntries;
@ Float32
Thirty two bit floating point (float)
@ Int16
Sixteen bit signed integer (qint16)
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ Int32
Thirty two bit signed integer (qint32)
@ Float64
Sixty four bit floating point (double)
@ UInt32
Thirty two bit unsigned integer (quint32)
This class represents a coordinate reference system (CRS).
@ WKT_PREFERRED_GDAL
Preferred format for conversion of CRS to WKT for use with the GDAL library.
QString toWkt(WktVariant variant=WKT1_GDAL, 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.
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 ...
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
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.
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, 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.
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
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)
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
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.
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
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.
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...