QGIS API Documentation 3.43.0-Master (c67cf405802)
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 "qgsgdalutils.h"
19#include "qgsrastercalculator.h"
21#include "qgsrasterinterface.h"
22#include "qgsrasterlayer.h"
23#include "qgsrastermatrix.h"
24#include "qgsrasterprojector.h"
25#include "qgsfeedback.h"
26#include "qgsogrutils.h"
27#include "qgsproject.h"
28
29#include <QFile>
30
31#include <cpl_string.h>
32#include <gdalwarper.h>
33
34#ifdef HAVE_OPENCL
35#include "qgsopenclutils.h"
36#include "qgsgdalutils.h"
37#endif
38
39
40//
41// global callback function
42//
43int CPL_STDCALL GdalProgressCallback( double dfComplete, const char *pszMessage, void *pProgressArg )
44{
45 Q_UNUSED( pszMessage )
46
47 static double sDfLastComplete = -1.0;
48
49 QgsFeedback *feedback = static_cast<QgsFeedback *>( pProgressArg );
50
51 if ( sDfLastComplete > dfComplete )
52 {
53 if ( sDfLastComplete >= 1.0 )
54 sDfLastComplete = -1.0;
55 else
56 sDfLastComplete = dfComplete;
57 }
58
59 if ( std::floor( sDfLastComplete * 10 ) != std::floor( dfComplete * 10 ) )
60 {
61 if ( feedback )
62 feedback->setProgress( dfComplete * 100 );
63 }
64 sDfLastComplete = dfComplete;
65
66 if ( feedback && feedback->isCanceled() )
67 return false;
68
69 return true;
70}
71
72QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries, const QgsCoordinateTransformContext &transformContext )
73 : mFormulaString( formulaString )
74 , mOutputFile( outputFile )
75 , mOutputFormat( outputFormat )
76 , mOutputRectangle( outputExtent )
77 , mNumOutputColumns( nOutputColumns )
78 , mNumOutputRows( nOutputRows )
79 , mRasterEntries( rasterEntries )
80 , mTransformContext( transformContext )
81{
82 //default to first layer's crs
83 if ( !mRasterEntries.isEmpty() )
84 {
85 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
86 }
87}
88
89QgsRasterCalculator::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 )
90 : mFormulaString( formulaString )
91 , mOutputFile( outputFile )
92 , mOutputFormat( outputFormat )
93 , mOutputRectangle( outputExtent )
94 , mOutputCrs( outputCrs )
95 , mNumOutputColumns( nOutputColumns )
96 , mNumOutputRows( nOutputRows )
97 , mRasterEntries( rasterEntries )
98 , mTransformContext( transformContext )
99{
100}
101
102// Deprecated!
103QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries )
104 : mFormulaString( formulaString )
105 , mOutputFile( outputFile )
106 , mOutputFormat( outputFormat )
107 , mOutputRectangle( outputExtent )
108 , mNumOutputColumns( nOutputColumns )
109 , mNumOutputRows( nOutputRows )
110 , mRasterEntries( rasterEntries )
111{
112 //default to first layer's crs
113 if ( !mRasterEntries.isEmpty() )
114 {
115 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
116 }
117 mTransformContext = QgsProject::instance()->transformContext();
118}
119
120
121// Deprecated!
122QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &outputCrs, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries )
123 : mFormulaString( formulaString )
124 , mOutputFile( outputFile )
125 , mOutputFormat( outputFormat )
126 , mOutputRectangle( outputExtent )
127 , mOutputCrs( outputCrs )
128 , mNumOutputColumns( nOutputColumns )
129 , mNumOutputRows( nOutputRows )
130 , mRasterEntries( rasterEntries )
131 , mTransformContext( QgsProject::instance()->transformContext() )
132{
133}
134
136{
137 mLastError.clear();
138
139 //prepare search string / tree
140 std::unique_ptr<QgsRasterCalcNode> calcNode( QgsRasterCalcNode::parseRasterCalcString( mFormulaString, mLastError ) );
141 if ( !calcNode )
142 {
143 //error
144 return ParserError;
145 }
146
147 // Check input layers and bands
148 for ( const auto &entry : std::as_const( mRasterEntries ) )
149 {
150 if ( !entry.raster ) // no raster layer in entry
151 {
152 mLastError = QObject::tr( "No raster layer for entry %1" ).arg( entry.ref );
153 return InputLayerError;
154 }
155 if ( entry.bandNumber <= 0 || entry.bandNumber > entry.raster->bandCount() )
156 {
157 mLastError = QObject::tr( "Band number %1 is not valid for entry %2" ).arg( entry.bandNumber ).arg( entry.ref );
158 return BandError;
159 }
160 }
161
162 // Check if we need to read the raster as a whole (which is memory inefficient
163 // and not interruptible by the user) by checking if any raster matrix nodes are
164 // in the expression
165 bool requiresMatrix = !calcNode->findNodes( QgsRasterCalcNode::Type::tMatrix ).isEmpty();
166
167#ifdef HAVE_OPENCL
168 // Check for matrix nodes, GPU implementation does not support them
169 if ( QgsOpenClUtils::enabled() && QgsOpenClUtils::available() && !requiresMatrix )
170 {
171 return processCalculationGPU( std::move( calcNode ), feedback );
172 }
173#endif
174
175 //open output dataset for writing
176 GDALDriverH outputDriver = openOutputDriver();
177 if ( !outputDriver )
178 {
179 mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
180 return CreateOutputError;
181 }
182
183 gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
184 if ( !outputDataset )
185 {
186 mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
187 return CreateOutputError;
188 }
189
190 GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLocal8Bit().data() );
191 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
192
193 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
194
195 // Take the fast route (process one line at a time) if we can
196 if ( !requiresMatrix )
197 {
198 // Map of raster names -> blocks
199 std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
200 std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries;
201 const QList<const QgsRasterCalcNode *> rasterRefNodes = calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef );
202 for ( const QgsRasterCalcNode *r : rasterRefNodes )
203 {
204 QString layerRef( r->toString().remove( 0, 1 ) );
205 layerRef.chop( 1 );
206 if ( !inputBlocks.count( layerRef ) )
207 {
208 for ( const QgsRasterCalculatorEntry &ref : std::as_const( mRasterEntries ) )
209 {
210 if ( ref.ref == layerRef )
211 {
212 uniqueRasterEntries[layerRef] = ref;
213 inputBlocks[layerRef] = std::make_unique<QgsRasterBlock>();
214 }
215 }
216 }
217 }
218
219 //read / write line by line
220 QMap<QString, QgsRasterBlock *> _rasterData;
221 // Cast to float
222 std::vector<float> castedResult( static_cast<size_t>( mNumOutputColumns ), 0 );
223 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
224 for ( size_t row = 0; row < static_cast<size_t>( mNumOutputRows ); ++row )
225 {
226 if ( feedback )
227 {
228 feedback->setProgress( 100.0 * static_cast<double>( row ) / mNumOutputRows );
229 }
230
231 if ( feedback && feedback->isCanceled() )
232 {
233 break;
234 }
235
236 // Calculates the rect for a single row read
237 QgsRectangle rect( mOutputRectangle );
238 rect.setYMaximum( rect.yMaximum() - rowHeight * row );
239 rect.setYMinimum( rect.yMaximum() - rowHeight );
240
241 // Read rows into input blocks
242 for ( auto &layerRef : inputBlocks )
243 {
244 QgsRasterCalculatorEntry ref = uniqueRasterEntries[layerRef.first];
245 if ( ref.raster->crs() != mOutputCrs )
246 {
248 proj.setCrs( ref.raster->crs(), mOutputCrs, mTransformContext );
249 proj.setInput( ref.raster->dataProvider() );
251 layerRef.second.reset( proj.block( ref.bandNumber, rect, mNumOutputColumns, 1 ) );
252 }
253 else
254 {
255 layerRef.second.reset( ref.raster->dataProvider()->block( ref.bandNumber, rect, mNumOutputColumns, 1 ) );
256 }
257 }
258
259 // 1 row X mNumOutputColumns matrix
260 QgsRasterMatrix resultMatrix( mNumOutputColumns, 1, nullptr, mNoDataValue );
261
262 _rasterData.clear();
263 for ( const auto &layerRef : inputBlocks )
264 {
265 _rasterData.insert( layerRef.first, layerRef.second.get() );
266 }
267
268 if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
269 {
270 std::copy( resultMatrix.data(), resultMatrix.data() + mNumOutputColumns, castedResult.begin() );
271 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, row, mNumOutputColumns, 1, castedResult.data(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
272 {
273 QgsDebugError( QStringLiteral( "RasterIO error!" ) );
274 }
275 }
276 else
277 {
278 //delete the dataset without closing (because it is faster)
279 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
280 return CalculationError;
281 }
282 }
283
284 if ( feedback )
285 {
286 feedback->setProgress( 100.0 );
287 }
288 }
289 else // Original code (memory inefficient route)
290 {
291 QMap<QString, QgsRasterBlock *> inputBlocks;
292 QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
293 for ( ; it != mRasterEntries.constEnd(); ++it )
294 {
295 std::unique_ptr<QgsRasterBlock> block;
296 // if crs transform needed
297 if ( it->raster->crs() != mOutputCrs )
298 {
300 proj.setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
301 proj.setInput( it->raster->dataProvider() );
303
304 QgsRasterBlockFeedback *rasterBlockFeedback = new QgsRasterBlockFeedback();
305 QObject::connect( feedback, &QgsFeedback::canceled, rasterBlockFeedback, &QgsRasterBlockFeedback::cancel );
306 block.reset( proj.block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
307 if ( rasterBlockFeedback->isCanceled() )
308 {
309 qDeleteAll( inputBlocks );
310 return Canceled;
311 }
312 }
313 else
314 {
315 block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
316 }
317 if ( block->isEmpty() )
318 {
319 mLastError = QObject::tr( "Could not allocate required memory for %1" ).arg( it->ref );
320 qDeleteAll( inputBlocks );
321 return MemoryError;
322 }
323 inputBlocks.insert( it->ref, block.release() );
324 }
325
326 QgsRasterMatrix resultMatrix;
327 resultMatrix.setNodataValue( mNoDataValue );
328
329 //read / write line by line
330 for ( int i = 0; i < mNumOutputRows; ++i )
331 {
332 if ( feedback )
333 {
334 feedback->setProgress( 100.0 * static_cast<double>( i ) / mNumOutputRows );
335 }
336
337 if ( feedback && feedback->isCanceled() )
338 {
339 break;
340 }
341
342 if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
343 {
344 bool resultIsNumber = resultMatrix.isNumber();
345 float *calcData = new float[mNumOutputColumns];
346
347 for ( int j = 0; j < mNumOutputColumns; ++j )
348 {
349 calcData[j] = ( float ) ( resultIsNumber ? resultMatrix.number() : resultMatrix.data()[j] );
350 }
351
352 //write scanline to the dataset
353 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
354 {
355 QgsDebugError( QStringLiteral( "RasterIO error!" ) );
356 }
357
358 delete[] calcData;
359 }
360 else
361 {
362 qDeleteAll( inputBlocks );
363 inputBlocks.clear();
364 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
365 return CalculationError;
366 }
367 }
368
369 if ( feedback )
370 {
371 feedback->setProgress( 100.0 );
372 }
373
374 //close datasets and release memory
375 calcNode.reset();
376 qDeleteAll( inputBlocks );
377 inputBlocks.clear();
378 }
379
380 if ( feedback && feedback->isCanceled() )
381 {
382 //delete the dataset without closing (because it is faster)
383 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
384 return Canceled;
385 }
386
387 GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
388
389 return Success;
390}
391
392#ifdef HAVE_OPENCL
393QgsRasterCalculator::Result QgsRasterCalculator::processCalculationGPU( std::unique_ptr<QgsRasterCalcNode> calcNode, QgsFeedback *feedback )
394{
395 QString cExpression( calcNode->toString( true ) );
396
397 QList<const QgsRasterCalcNode *> nodeList( calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef ) );
398 QSet<QString> capturedTexts;
399 for ( const auto &r : std::as_const( nodeList ) )
400 {
401 QString s( r->toString().remove( 0, 1 ) );
402 s.chop( 1 );
403 capturedTexts.insert( s );
404 }
405
406 // Extract all references
407 struct LayerRef
408 {
409 QString name;
410 int band;
411 QgsRasterLayer *layer = nullptr;
412 QString varName;
413 QString typeName;
414 size_t index;
415 size_t bufferSize;
416 size_t dataSize;
417 };
418
419 // Collects all layers, band, name, varName and size information
420 std::vector<LayerRef> inputRefs;
421 size_t refCounter = 0;
422 for ( const auto &r : capturedTexts )
423 {
424 if ( r.startsWith( '"' ) )
425 continue;
426 QStringList parts( r.split( '@' ) );
427 if ( parts.count() != 2 )
428 continue;
429 bool ok = false;
430 LayerRef entry;
431 entry.name = r;
432 entry.band = parts[1].toInt( &ok );
433 for ( const auto &ref : std::as_const( mRasterEntries ) )
434 {
435 if ( ref.ref == entry.name )
436 entry.layer = ref.raster;
437 }
438 if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
439 continue;
440 entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
441 switch ( entry.layer->dataProvider()->dataType( entry.band ) )
442 {
444 entry.typeName = QStringLiteral( "unsigned char" );
445 break;
447 entry.typeName = QStringLiteral( "char" );
448 break;
450 entry.typeName = QStringLiteral( "unsigned short" );
451 break;
453 entry.typeName = QStringLiteral( "short" );
454 break;
456 entry.typeName = QStringLiteral( "unsigned int" );
457 break;
459 entry.typeName = QStringLiteral( "int" );
460 break;
462 entry.typeName = QStringLiteral( "float" );
463 break;
464 // FIXME: not sure all OpenCL implementations support double
465 // maybe safer to fall back to the CPU implementation
466 // after a compatibility check
468 entry.typeName = QStringLiteral( "double" );
469 break;
477 return BandError;
478 }
479 entry.bufferSize = entry.dataSize * mNumOutputColumns;
480 entry.index = refCounter;
481 entry.varName = QStringLiteral( "input_raster_%1_band_%2" )
482 .arg( refCounter++ )
483 .arg( entry.band );
484 inputRefs.push_back( entry );
485 }
486
487 // May throw an openCL exception
488 try
489 {
490 // Prepare context and queue
491 cl::Context ctx( QgsOpenClUtils::context() );
492 cl::CommandQueue queue( QgsOpenClUtils::commandQueue() );
493
494 // Create the C expression
495 std::vector<cl::Buffer> inputBuffers;
496 inputBuffers.reserve( inputRefs.size() );
497 QStringList inputArgs;
498 for ( const auto &ref : inputRefs )
499 {
500 cExpression.replace( QStringLiteral( "\"%1\"" ).arg( ref.name ), QStringLiteral( "%1[i]" ).arg( ref.varName ) );
501 inputArgs.append( QStringLiteral( "__global %1 *%2" )
502 .arg( ref.typeName, ref.varName ) );
503 inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize, nullptr, nullptr ) );
504 }
505
506 //qDebug() << cExpression;
507
508 // Create the program
509 QString programTemplate( R"CL(
510 // Inputs:
511 ##INPUT_DESC##
512 // Expression: ##EXPRESSION_ORIGINAL##
513 __kernel void rasterCalculator( ##INPUT##
514 __global float *resultLine
515 )
516 {
517 // Get the index of the current element
518 const int i = get_global_id(0);
519 // Check for nodata in input
520 if ( ##INPUT_NODATA_CHECK## )
521 resultLine[i] = -FLT_MAX;
522 // Expression
523 else
524 resultLine[i] = ##EXPRESSION##;
525 }
526 )CL" );
527
528 QStringList inputDesc;
529 QStringList inputNoDataCheck;
530 for ( const auto &ref : inputRefs )
531 {
532 inputDesc.append( QStringLiteral( " // %1 = %2" ).arg( ref.varName, ref.name ) );
533 if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
534 {
535 inputNoDataCheck.append( QStringLiteral( "(float) %1[i] == (float) %2" ).arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ), 'g', 10 ) ) );
536 }
537 }
538
539 programTemplate = programTemplate.replace( QLatin1String( "##INPUT_NODATA_CHECK##" ), inputNoDataCheck.isEmpty() ? QStringLiteral( "false" ) : inputNoDataCheck.join( QLatin1String( " || " ) ) );
540 programTemplate = programTemplate.replace( QLatin1String( "##INPUT_DESC##" ), inputDesc.join( '\n' ) );
541 programTemplate = programTemplate.replace( QLatin1String( "##INPUT##" ), !inputArgs.isEmpty() ? ( inputArgs.join( ',' ).append( ',' ) ) : QChar( ' ' ) );
542 programTemplate = programTemplate.replace( QLatin1String( "##EXPRESSION##" ), cExpression );
543 programTemplate = programTemplate.replace( QLatin1String( "##EXPRESSION_ORIGINAL##" ), calcNode->toString() );
544
545 // qDebug() << programTemplate;
546
547 // Create a program from the kernel source
548 cl::Program program( QgsOpenClUtils::buildProgram( programTemplate, QgsOpenClUtils::ExceptionBehavior::Throw ) );
549
550 // Create the buffers, output is float32 (4 bytes)
551 // We assume size of float = 4 because that's the size used by OpenCL and IEEE 754
552 Q_ASSERT( sizeof( float ) == 4 );
553 std::size_t resultBufferSize( 4 * static_cast<size_t>( mNumOutputColumns ) );
554 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, resultBufferSize, nullptr, nullptr );
555
556 auto kernel = cl::Kernel( program, "rasterCalculator" );
557
558 for ( unsigned int i = 0; i < inputBuffers.size(); i++ )
559 {
560 kernel.setArg( i, inputBuffers.at( i ) );
561 }
562 kernel.setArg( static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
563
564 QgsOpenClUtils::CPLAllocator<float> resultLine( static_cast<size_t>( mNumOutputColumns ) );
565
566 //open output dataset for writing
567 GDALDriverH outputDriver = openOutputDriver();
568 if ( !outputDriver )
569 {
570 mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
571 return CreateOutputError;
572 }
573
574 gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
575 if ( !outputDataset )
576 {
577 mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
578 return CreateOutputError;
579 }
580
581 GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLocal8Bit().data() );
582
583
584 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
585 if ( !outputRasterBand )
586 return BandError;
587
588 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
589
590 // Input block (buffer)
591 std::unique_ptr<QgsRasterBlock> block;
592
593 // Run kernel on all scanlines
594 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
595 for ( int line = 0; line < mNumOutputRows; line++ )
596 {
597 if ( feedback && feedback->isCanceled() )
598 {
599 break;
600 }
601
602 if ( feedback )
603 {
604 feedback->setProgress( 100.0 * static_cast<double>( line ) / mNumOutputRows );
605 }
606
607 // Read lines from rasters into the buffers
608 for ( const auto &ref : inputRefs )
609 {
610 // Read one row
611 QgsRectangle rect( mOutputRectangle );
612 rect.setYMaximum( rect.yMaximum() - rowHeight * line );
613 rect.setYMinimum( rect.yMaximum() - rowHeight );
614
615 // TODO: check if this is too slow
616 // if crs transform needed
617 if ( ref.layer->crs() != mOutputCrs )
618 {
620 proj.setCrs( ref.layer->crs(), mOutputCrs, ref.layer->transformContext() );
621 proj.setInput( ref.layer->dataProvider() );
623 block.reset( proj.block( ref.band, rect, mNumOutputColumns, 1 ) );
624 }
625 else
626 {
627 block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
628 }
629
630 //for ( int i = 0; i < mNumOutputColumns; i++ )
631 // qDebug() << "Input: " << line << i << ref.varName << " = " << block->value( 0, i );
632 //qDebug() << "Writing buffer " << ref.index;
633
634 Q_ASSERT( ref.bufferSize == static_cast<size_t>( block->data().size() ) );
635 queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0, ref.bufferSize, block->bits() );
636 }
637 // Run the kernel
638 queue.enqueueNDRangeKernel(
639 kernel,
640 0,
641 cl::NDRange( mNumOutputColumns )
642 );
643
644 // Write the result
645 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, resultBufferSize, resultLine.get() );
646
647 //for ( int i = 0; i < mNumOutputColumns; i++ )
648 // qDebug() << "Output: " << line << i << " = " << resultLine[i];
649
650 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
651 {
652 return CreateOutputError;
653 }
654 }
655
656 if ( feedback && feedback->isCanceled() )
657 {
658 //delete the dataset without closing (because it is faster)
659 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
660 return Canceled;
661 }
662
663 inputBuffers.clear();
664
665 GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
666 }
667 catch ( cl::Error &e )
668 {
669 mLastError = e.what();
670 return CreateOutputError;
671 }
672
673 return Success;
674}
675#endif
676
677GDALDriverH QgsRasterCalculator::openOutputDriver()
678{
679 //open driver
680 GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
681
682 if ( !outputDriver )
683 {
684 return outputDriver; //return nullptr, driver does not exist
685 }
686
687 if ( !QgsGdalUtils::supportsRasterCreate( outputDriver ) )
688 {
689 return nullptr; //driver exist, but it does not support the create operation
690 }
691
692 return outputDriver;
693}
694
695gdal::dataset_unique_ptr QgsRasterCalculator::openOutputFile( GDALDriverH outputDriver )
696{
697 //open output file
698 char **papszOptions = QgsGdalUtils::papszFromStringList( mCreateOptions );
699 gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
700 CSLDestroy( papszOptions );
701 if ( !outputDataset )
702 {
703 return nullptr;
704 }
705
706 //assign georef information
707 double geotransform[6];
708 outputGeoTransform( geotransform );
709 GDALSetGeoTransform( outputDataset.get(), geotransform );
710
711 return outputDataset;
712}
713
714void QgsRasterCalculator::outputGeoTransform( double *transform ) const
715{
716 transform[0] = mOutputRectangle.xMinimum();
717 transform[1] = mOutputRectangle.width() / mNumOutputColumns;
718 transform[2] = 0;
719 transform[3] = mOutputRectangle.yMaximum();
720 transform[4] = 0;
721 transform[5] = -mOutputRectangle.height() / mNumOutputRows;
722}
723
725{
726 return mLastError;
727}
728
729QVector<QgsRasterCalculatorEntry> QgsRasterCalculatorEntry::rasterEntries()
730{
731 QVector<QgsRasterCalculatorEntry> availableEntries;
732 const QMap<QString, QgsMapLayer *> &layers = QgsProject::instance()->mapLayers();
733
734 auto uniqueRasterBandIdentifier = [&]( QgsRasterCalculatorEntry &entry ) -> bool {
735 unsigned int i( 1 );
736 entry.ref = QStringLiteral( "%1@%2" ).arg( entry.raster->name() ).arg( entry.bandNumber );
737 while ( true )
738 {
739 bool unique( true );
740 for ( const auto &ref : std::as_const( availableEntries ) )
741 {
742 // Safety belt
743 if ( !( entry.raster && ref.raster ) )
744 continue;
745 // Check if is another band of the same raster
746 if ( ref.raster->publicSource() == entry.raster->publicSource() )
747 {
748 if ( ref.bandNumber != entry.bandNumber )
749 {
750 continue;
751 }
752 else // a layer with the same data source was already added to the list
753 {
754 return false;
755 }
756 }
757 // If same name but different source
758 if ( ref.ref == entry.ref )
759 {
760 unique = false;
761 entry.ref = QStringLiteral( "%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
762 }
763 }
764 if ( unique )
765 return true;
766 }
767 };
768
769 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
770 for ( ; layerIt != layers.constEnd(); ++layerIt )
771 {
772 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
773 if ( rlayer && rlayer->dataProvider() && ( rlayer->dataProvider()->capabilities() & Qgis::RasterInterfaceCapability::Size ) )
774 {
775 //get number of bands
776 for ( int i = 0; i < rlayer->bandCount(); ++i )
777 {
779 entry.raster = rlayer;
780 entry.bandNumber = i + 1;
781 if ( !uniqueRasterBandIdentifier( entry ) )
782 break;
783 availableEntries.push_back( entry );
784 }
785 }
786 }
787 return availableEntries;
788}
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
@ CInt32
Complex Int32.
@ 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.
@ CInt16
Complex Int16.
@ 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).
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.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
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:61
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:84
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:107
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:113
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)
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.
double xMinimum
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:40
int CPL_STDCALL GdalProgressCallback(double dfComplete, const char *pszMessage, void *pProgressArg)
const QgsCoordinateReferenceSystem & outputCrs
const QString & typeName
Tiny smart-pointer-like wrapper around CPLMalloc and CPLFree: this is needed because OpenCL C++ API m...