QGIS API Documentation 3.99.0-Master (e9821da5c6b)
Loading...
Searching...
No Matches
qgsrastercalculator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrastercalculator.cpp - description
3 -----------------------
4 begin : September 28th, 2010
5 copyright : (C) 2010 by Marco Hugentobler
6 email : marco dot hugentobler at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgsrastercalculator.h"
19
20#include <cpl_string.h>
21#include <gdalwarper.h>
22
23#include "qgsfeedback.h"
24#include "qgsgdalutils.h"
25#include "qgsogrutils.h"
26#include "qgsproject.h"
28#include "qgsrasterinterface.h"
29#include "qgsrasterlayer.h"
30#include "qgsrastermatrix.h"
31#include "qgsrasterprojector.h"
32
33#include <QFile>
34#include <QString>
35
36using namespace Qt::StringLiterals;
37
38#ifdef HAVE_OPENCL
39#include "qgsopenclutils.h"
40#include "qgsgdalutils.h"
41#endif
42
43
44//
45// global callback function
46//
47int CPL_STDCALL GdalProgressCallback( double dfComplete, const char *pszMessage, void *pProgressArg )
48{
49 Q_UNUSED( pszMessage )
50
51 static double sDfLastComplete = -1.0;
52
53 QgsFeedback *feedback = static_cast<QgsFeedback *>( pProgressArg );
54
55 if ( sDfLastComplete > dfComplete )
56 {
57 if ( sDfLastComplete >= 1.0 )
58 sDfLastComplete = -1.0;
59 else
60 sDfLastComplete = dfComplete;
61 }
62
63 if ( std::floor( sDfLastComplete * 10 ) != std::floor( dfComplete * 10 ) )
64 {
65 if ( feedback )
66 feedback->setProgress( dfComplete * 100 );
67 }
68 sDfLastComplete = dfComplete;
69
70 if ( feedback && feedback->isCanceled() )
71 return false;
72
73 return true;
74}
75
76QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries, const QgsCoordinateTransformContext &transformContext )
77 : mFormulaString( formulaString )
78 , mOutputFile( outputFile )
79 , mOutputFormat( outputFormat )
80 , mOutputRectangle( outputExtent )
81 , mNumOutputColumns( nOutputColumns )
82 , mNumOutputRows( nOutputRows )
83 , mRasterEntries( rasterEntries )
84 , mTransformContext( transformContext )
85{
86 //default to first layer's crs
87 if ( !mRasterEntries.isEmpty() )
88 {
89 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
90 }
91}
92
93QgsRasterCalculator::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 )
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 )
103{
104}
105
106// Deprecated!
107QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries )
108 : mFormulaString( formulaString )
109 , mOutputFile( outputFile )
110 , mOutputFormat( outputFormat )
111 , mOutputRectangle( outputExtent )
112 , mNumOutputColumns( nOutputColumns )
113 , mNumOutputRows( nOutputRows )
114 , mRasterEntries( rasterEntries )
115{
116 //default to first layer's crs
117 if ( !mRasterEntries.isEmpty() )
118 {
119 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
120 }
121 mTransformContext = QgsProject::instance()->transformContext();
122}
123
124
125// Deprecated!
126QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &outputCrs, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries )
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() )
136{
137}
138
140{
141 mLastError.clear();
142
143 //prepare search string / tree
144 std::unique_ptr<QgsRasterCalcNode> calcNode( QgsRasterCalcNode::parseRasterCalcString( mFormulaString, mLastError ) );
145 if ( !calcNode )
146 {
147 //error
149 }
150
151 // Check input layers and bands
152 for ( const auto &entry : std::as_const( mRasterEntries ) )
153 {
154 if ( !entry.raster ) // no raster layer in entry
155 {
156 mLastError = QObject::tr( "No raster layer for entry %1" ).arg( entry.ref );
158 }
159 if ( entry.bandNumber <= 0 || entry.bandNumber > entry.raster->bandCount() )
160 {
161 mLastError = QObject::tr( "Band number %1 is not valid for entry %2" ).arg( entry.bandNumber ).arg( entry.ref );
163 }
164 }
165
166 // Check if we need to read the raster as a whole (which is memory inefficient
167 // and not interruptible by the user) by checking if any raster matrix nodes are
168 // in the expression
169 bool requiresMatrix = !calcNode->findNodes( QgsRasterCalcNode::Type::tMatrix ).isEmpty();
170
171#ifdef HAVE_OPENCL
172 // Check for matrix nodes, GPU implementation does not support them
173 if ( QgsOpenClUtils::enabled() && QgsOpenClUtils::available() && !requiresMatrix )
174 {
175 return processCalculationGPU( std::move( calcNode ), feedback );
176 }
177#endif
178
179 //open output dataset for writing
180 GDALDriverH outputDriver = openOutputDriver();
181 if ( !outputDriver )
182 {
183 mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
185 }
186
187 gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
188 if ( !outputDataset )
189 {
190 mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
192 }
193
194 GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLocal8Bit().data() );
195 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
196
197 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
198
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;
202#else
203 constexpr double maxProgressDuringBlockWriting = 100.0;
204#endif
205
206 // Take the fast route (process one line at a time) if we can
207 if ( !requiresMatrix )
208 {
209 // Map of raster names -> blocks
210 std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
211 std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries;
212 const QList<const QgsRasterCalcNode *> rasterRefNodes = calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef );
213 for ( const QgsRasterCalcNode *r : rasterRefNodes )
214 {
215 QString layerRef( r->toString().remove( 0, 1 ) );
216 layerRef.chop( 1 );
217 if ( !inputBlocks.count( layerRef ) )
218 {
219 for ( const QgsRasterCalculatorEntry &ref : std::as_const( mRasterEntries ) )
220 {
221 if ( ref.ref == layerRef )
222 {
223 uniqueRasterEntries[layerRef] = ref;
224 inputBlocks[layerRef] = std::make_unique<QgsRasterBlock>();
225 }
226 }
227 }
228 }
229
230 //read / write line by line
231 QMap<QString, QgsRasterBlock *> _rasterData;
232 // Cast to float
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 )
236 {
237 if ( feedback )
238 {
239 feedback->setProgress( maxProgressDuringBlockWriting * static_cast<double>( row ) / mNumOutputRows );
240 }
241
242 if ( feedback && feedback->isCanceled() )
243 {
244 break;
245 }
246
247 // Calculates the rect for a single row read
248 QgsRectangle rect( mOutputRectangle );
249 rect.setYMaximum( rect.yMaximum() - rowHeight * row );
250 rect.setYMinimum( rect.yMaximum() - rowHeight );
251
252 // Read rows into input blocks
253 for ( auto &layerRef : inputBlocks )
254 {
255 QgsRasterCalculatorEntry ref = uniqueRasterEntries[layerRef.first];
256 if ( ref.raster->crs() != mOutputCrs )
257 {
259 proj.setCrs( ref.raster->crs(), mOutputCrs, mTransformContext );
260 proj.setInput( ref.raster->dataProvider() );
262 layerRef.second.reset( proj.block( ref.bandNumber, rect, mNumOutputColumns, 1 ) );
263 }
264 else
265 {
266 layerRef.second.reset( ref.raster->dataProvider()->block( ref.bandNumber, rect, mNumOutputColumns, 1 ) );
267 }
268 }
269
270 // 1 row X mNumOutputColumns matrix
271 QgsRasterMatrix resultMatrix( mNumOutputColumns, 1, nullptr, mNoDataValue );
272
273 _rasterData.clear();
274 for ( const auto &layerRef : inputBlocks )
275 {
276 _rasterData.insert( layerRef.first, layerRef.second.get() );
277 }
278
279 if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
280 {
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 )
283 {
284 QgsDebugError( u"RasterIO error!"_s );
285 }
286 }
287 else
288 {
289 //delete the dataset without closing (because it is faster)
290 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
292 }
293 }
294
295 if ( feedback )
296 {
297 feedback->setProgress( maxProgressDuringBlockWriting );
298 }
299 }
300 else // Original code (memory inefficient route)
301 {
302 QMap<QString, QgsRasterBlock *> inputBlocks;
303 QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
304 for ( ; it != mRasterEntries.constEnd(); ++it )
305 {
306 std::unique_ptr<QgsRasterBlock> block;
307 // if crs transform needed
308 if ( it->raster->crs() != mOutputCrs )
309 {
311 proj.setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
312 proj.setInput( it->raster->dataProvider() );
314
315 QgsRasterBlockFeedback *rasterBlockFeedback = new QgsRasterBlockFeedback();
316 QObject::connect( feedback, &QgsFeedback::canceled, rasterBlockFeedback, &QgsRasterBlockFeedback::cancel );
317 block.reset( proj.block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
318 if ( rasterBlockFeedback->isCanceled() )
319 {
320 qDeleteAll( inputBlocks );
322 }
323 }
324 else
325 {
326 block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
327 }
328 if ( block->isEmpty() )
329 {
330 mLastError = QObject::tr( "Could not allocate required memory for %1" ).arg( it->ref );
331 qDeleteAll( inputBlocks );
333 }
334 inputBlocks.insert( it->ref, block.release() );
335 }
336
337 QgsRasterMatrix resultMatrix;
338 resultMatrix.setNodataValue( mNoDataValue );
339
340 //read / write line by line
341 for ( int i = 0; i < mNumOutputRows; ++i )
342 {
343 if ( feedback )
344 {
345 feedback->setProgress( maxProgressDuringBlockWriting * static_cast<double>( i ) / mNumOutputRows );
346 }
347
348 if ( feedback && feedback->isCanceled() )
349 {
350 break;
351 }
352
353 if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
354 {
355 bool resultIsNumber = resultMatrix.isNumber();
356 float *calcData = new float[mNumOutputColumns];
357
358 for ( int j = 0; j < mNumOutputColumns; ++j )
359 {
360 calcData[j] = ( float ) ( resultIsNumber ? resultMatrix.number() : resultMatrix.data()[j] );
361 }
362
363 //write scanline to the dataset
364 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
365 {
366 QgsDebugError( u"RasterIO error!"_s );
367 }
368
369 delete[] calcData;
370 }
371 else
372 {
373 qDeleteAll( inputBlocks );
374 inputBlocks.clear();
375 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
377 }
378 }
379
380 if ( feedback )
381 {
382 feedback->setProgress( maxProgressDuringBlockWriting );
383 }
384
385 //close datasets and release memory
386 calcNode.reset();
387 qDeleteAll( inputBlocks );
388 inputBlocks.clear();
389 }
390
391 if ( feedback && feedback->isCanceled() )
392 {
393 //delete the dataset without closing (because it is faster)
394 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
396 }
397
398 GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
399
400#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
401 if ( hasReportsDuringClose && feedback )
402 {
403 QgsGdalProgressAdapter progress( feedback, maxProgressDuringBlockWriting );
404 if ( GDALDatasetRunCloseWithoutDestroyingEx(
405 outputDataset.get(), QgsGdalProgressAdapter::progressCallback, &progress
406 )
407 != CE_None )
408 {
410 }
411 }
412#endif
413
415}
416
417#ifdef HAVE_OPENCL
418QgsRasterCalculator::Result QgsRasterCalculator::processCalculationGPU( std::unique_ptr<QgsRasterCalcNode> calcNode, QgsFeedback *feedback )
419{
420 QString cExpression( calcNode->toString( true ) );
421
422 QList<const QgsRasterCalcNode *> nodeList( calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef ) );
423 QSet<QString> capturedTexts;
424 for ( const auto &r : std::as_const( nodeList ) )
425 {
426 QString s( r->toString().remove( 0, 1 ) );
427 s.chop( 1 );
428 capturedTexts.insert( s );
429 }
430
431 // Extract all references
432 struct LayerRef
433 {
434 QString name;
435 int band;
436 QgsRasterLayer *layer = nullptr;
437 QString varName;
438 QString typeName;
439 size_t index;
440 size_t bufferSize;
441 size_t dataSize;
442 };
443
444 // Collects all layers, band, name, varName and size information
445 std::vector<LayerRef> inputRefs;
446 size_t refCounter = 0;
447 for ( const auto &r : capturedTexts )
448 {
449 if ( r.startsWith( '"' ) )
450 continue;
451 QStringList parts( r.split( '@' ) );
452 if ( parts.count() != 2 )
453 continue;
454 bool ok = false;
455 LayerRef entry;
456 entry.name = r;
457 entry.band = parts[1].toInt( &ok );
458 for ( const auto &ref : std::as_const( mRasterEntries ) )
459 {
460 if ( ref.ref == entry.name )
461 entry.layer = ref.raster;
462 }
463 if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
464 continue;
465 entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
466 switch ( entry.layer->dataProvider()->dataType( entry.band ) )
467 {
469 entry.typeName = u"unsigned char"_s;
470 break;
472 entry.typeName = u"char"_s;
473 break;
475 entry.typeName = u"unsigned short"_s;
476 break;
478 entry.typeName = u"short"_s;
479 break;
481 entry.typeName = u"unsigned int"_s;
482 break;
484 entry.typeName = u"int"_s;
485 break;
487 entry.typeName = u"float"_s;
488 break;
489 // FIXME: not sure all OpenCL implementations support double
490 // maybe safer to fall back to the CPU implementation
491 // after a compatibility check
493 entry.typeName = u"double"_s;
494 break;
503 }
504 entry.bufferSize = entry.dataSize * mNumOutputColumns;
505 entry.index = refCounter;
506 entry.varName = u"input_raster_%1_band_%2"_s
507 .arg( refCounter++ )
508 .arg( entry.band );
509 inputRefs.push_back( entry );
510 }
511
512 // May throw an openCL exception
513 try
514 {
515 // Prepare context and queue
516 cl::Context ctx( QgsOpenClUtils::context() );
517 cl::CommandQueue queue( QgsOpenClUtils::commandQueue() );
518
519 // Create the C expression
520 std::vector<cl::Buffer> inputBuffers;
521 inputBuffers.reserve( inputRefs.size() );
522 QStringList inputArgs;
523 for ( const auto &ref : inputRefs )
524 {
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 ) );
529 }
530
531 //qDebug() << cExpression;
532
533 // Create the program
534 QString programTemplate( R"CL(
535 // Inputs:
536 ##INPUT_DESC##
537 // Expression: ##EXPRESSION_ORIGINAL##
538 __kernel void rasterCalculator( ##INPUT##
539 __global float *resultLine
540 )
541 {
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;
547 // Expression
548 else
549 resultLine[i] = ##EXPRESSION##;
550 }
551 )CL" );
552
553 QStringList inputDesc;
554 QStringList inputNoDataCheck;
555 for ( const auto &ref : inputRefs )
556 {
557 inputDesc.append( u" // %1 = %2"_s.arg( ref.varName, ref.name ) );
558 if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
559 {
560 inputNoDataCheck.append( u"(float) %1[i] == (float) %2"_s.arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ), 'g', 10 ) ) );
561 }
562 }
563
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() );
569
570 // qDebug() << programTemplate;
571
572 // Create a program from the kernel source
573 cl::Program program( QgsOpenClUtils::buildProgram( programTemplate, QgsOpenClUtils::ExceptionBehavior::Throw ) );
574
575 // Create the buffers, output is float32 (4 bytes)
576 // We assume size of float = 4 because that's the size used by OpenCL and IEEE 754
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 );
580
581 auto kernel = cl::Kernel( program, "rasterCalculator" );
582
583 for ( unsigned int i = 0; i < inputBuffers.size(); i++ )
584 {
585 kernel.setArg( i, inputBuffers.at( i ) );
586 }
587 kernel.setArg( static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
588
589 QgsOpenClUtils::CPLAllocator<float> resultLine( static_cast<size_t>( mNumOutputColumns ) );
590
591 //open output dataset for writing
592 GDALDriverH outputDriver = openOutputDriver();
593 if ( !outputDriver )
594 {
595 mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
597 }
598
599 gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
600 if ( !outputDataset )
601 {
602 mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
604 }
605
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;
609#else
610 constexpr double maxProgressDuringBlockWriting = 100.0;
611#endif
612
613 GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLocal8Bit().data() );
614
615
616 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
617 if ( !outputRasterBand )
619
620 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
621
622 // Input block (buffer)
623 std::unique_ptr<QgsRasterBlock> block;
624
625 // Run kernel on all scanlines
626 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
627 for ( int line = 0; line < mNumOutputRows; line++ )
628 {
629 if ( feedback && feedback->isCanceled() )
630 {
631 break;
632 }
633
634 if ( feedback )
635 {
636 feedback->setProgress( maxProgressDuringBlockWriting * static_cast<double>( line ) / mNumOutputRows );
637 }
638
639 // Read lines from rasters into the buffers
640 for ( const auto &ref : inputRefs )
641 {
642 // Read one row
643 QgsRectangle rect( mOutputRectangle );
644 rect.setYMaximum( rect.yMaximum() - rowHeight * line );
645 rect.setYMinimum( rect.yMaximum() - rowHeight );
646
647 // TODO: check if this is too slow
648 // if crs transform needed
649 if ( ref.layer->crs() != mOutputCrs )
650 {
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 ) );
656 }
657 else
658 {
659 block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
660 }
661
662 //for ( int i = 0; i < mNumOutputColumns; i++ )
663 // qDebug() << "Input: " << line << i << ref.varName << " = " << block->value( 0, i );
664 //qDebug() << "Writing buffer " << ref.index;
665
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() );
668 }
669 // Run the kernel
670 queue.enqueueNDRangeKernel(
671 kernel,
672 0,
673 cl::NDRange( mNumOutputColumns )
674 );
675
676 // Write the result
677 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, resultBufferSize, resultLine.get() );
678
679 //for ( int i = 0; i < mNumOutputColumns; i++ )
680 // qDebug() << "Output: " << line << i << " = " << resultLine[i];
681
682 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
683 {
685 }
686 }
687
688 if ( feedback && feedback->isCanceled() )
689 {
690 //delete the dataset without closing (because it is faster)
691 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
693 }
694
695 inputBuffers.clear();
696
697 GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
698
699#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION( 3, 13, 0 )
700 if ( hasReportsDuringClose && feedback )
701 {
702 QgsGdalProgressAdapter progress( feedback, maxProgressDuringBlockWriting );
703 if ( GDALDatasetRunCloseWithoutDestroyingEx(
704 outputDataset.get(), QgsGdalProgressAdapter::progressCallback, &progress
705 )
706 != CE_None )
707 {
709 }
710 }
711#endif
712 }
713 catch ( cl::Error &e )
714 {
715 mLastError = e.what();
717 }
718
720}
721#endif
722
723GDALDriverH QgsRasterCalculator::openOutputDriver()
724{
725 //open driver
726 GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
727
728 if ( !outputDriver )
729 {
730 return outputDriver; //return nullptr, driver does not exist
731 }
732
733 if ( !QgsGdalUtils::supportsRasterCreate( outputDriver ) )
734 {
735 return nullptr; //driver exist, but it does not support the create operation
736 }
737
738 return outputDriver;
739}
740
741gdal::dataset_unique_ptr QgsRasterCalculator::openOutputFile( GDALDriverH outputDriver )
742{
743 //open output file
744 char **papszOptions = QgsGdalUtils::papszFromStringList( mCreationOptions );
745 gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
746 CSLDestroy( papszOptions );
747 if ( !outputDataset )
748 {
749 return nullptr;
750 }
751
752 //assign georef information
753 double geotransform[6];
754 outputGeoTransform( geotransform );
755 GDALSetGeoTransform( outputDataset.get(), geotransform );
756
757 return outputDataset;
758}
759
760void QgsRasterCalculator::outputGeoTransform( double *transform ) const
761{
762 transform[0] = mOutputRectangle.xMinimum();
763 transform[1] = mOutputRectangle.width() / mNumOutputColumns;
764 transform[2] = 0;
765 transform[3] = mOutputRectangle.yMaximum();
766 transform[4] = 0;
767 transform[5] = -mOutputRectangle.height() / mNumOutputRows;
768}
769
771{
772 return mLastError;
773}
774
775QVector<QgsRasterCalculatorEntry> QgsRasterCalculatorEntry::rasterEntries()
776{
777 QVector<QgsRasterCalculatorEntry> availableEntries;
778 const QMap<QString, QgsMapLayer *> &layers = QgsProject::instance()->mapLayers();
779
780 auto uniqueRasterBandIdentifier = [&]( QgsRasterCalculatorEntry &entry ) -> bool {
781 unsigned int i( 1 );
782 entry.ref = u"%1@%2"_s.arg( entry.raster ? entry.raster->name() : QString() ).arg( entry.bandNumber );
783 while ( true )
784 {
785 bool unique( true );
786 for ( const auto &ref : std::as_const( availableEntries ) )
787 {
788 // Safety belt
789 if ( !( entry.raster && ref.raster ) )
790 continue;
791 // Check if is another band of the same raster
792 if ( ref.raster->publicSource() == entry.raster->publicSource() )
793 {
794 if ( ref.bandNumber != entry.bandNumber )
795 {
796 continue;
797 }
798 else // a layer with the same data source was already added to the list
799 {
800 return false;
801 }
802 }
803 // If same name but different source
804 if ( ref.ref == entry.ref )
805 {
806 unique = false;
807 entry.ref = u"%1_%2@%3"_s.arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
808 }
809 }
810 if ( unique )
811 return true;
812 }
813 };
814
815 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
816 for ( ; layerIt != layers.constEnd(); ++layerIt )
817 {
818 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
819 if ( rlayer && rlayer->dataProvider() && ( rlayer->dataProvider()->capabilities() & Qgis::RasterInterfaceCapability::Size ) )
820 {
821 //get number of bands
822 for ( int i = 0; i < rlayer->bandCount(); ++i )
823 {
825 entry.raster = rlayer;
826 entry.bandNumber = i + 1;
827 if ( !uniqueRasterBandIdentifier( entry ) )
828 break;
829 availableEntries.push_back( entry );
830 }
831 }
832 }
833 return availableEntries;
834}
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
Definition qgis.h:4951
@ CInt32
Complex Int32.
Definition qgis.h:390
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:387
@ CFloat64
Complex Float64.
Definition qgis.h:392
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:384
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:394
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:382
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:383
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:381
@ UnknownDataType
Unknown or unspecified type.
Definition qgis.h:380
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:393
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:386
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:388
@ CFloat32
Complex Float32.
Definition qgis.h:391
@ CInt16
Complex Int16.
Definition qgis.h:389
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:385
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
Definition qgis.h:2499
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.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:55
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.
Definition qgsfeedback.h:63
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
Definition qgsmaplayer.h:90
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,...
Definition qgsproject.h:113
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).
double number() const
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.
double yMaximum
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)
Definition qgslogger.h:59
int CPL_STDCALL GdalProgressCallback(double dfComplete, const char *pszMessage, void *pProgressArg)