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();
680 GDALDriverH 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;
716 void 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;