QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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,
44 const char *pszMessage,
45 void *pProgressArg )
46{
47 Q_UNUSED( pszMessage )
48
49 static double sDfLastComplete = -1.0;
50
51 QgsFeedback *feedback = static_cast<QgsFeedback *>( pProgressArg );
52
53 if ( sDfLastComplete > dfComplete )
54 {
55 if ( sDfLastComplete >= 1.0 )
56 sDfLastComplete = -1.0;
57 else
58 sDfLastComplete = dfComplete;
59 }
60
61 if ( std::floor( sDfLastComplete * 10 ) != std::floor( dfComplete * 10 ) )
62 {
63 if ( feedback )
64 feedback->setProgress( dfComplete * 100 );
65 }
66 sDfLastComplete = dfComplete;
67
68 if ( feedback && feedback->isCanceled() )
69 return false;
70
71 return true;
72}
73
74QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries, const QgsCoordinateTransformContext &transformContext )
75 : mFormulaString( formulaString )
76 , mOutputFile( outputFile )
77 , mOutputFormat( outputFormat )
78 , mOutputRectangle( outputExtent )
79 , mNumOutputColumns( nOutputColumns )
80 , mNumOutputRows( nOutputRows )
81 , mRasterEntries( rasterEntries )
82 , mTransformContext( transformContext )
83{
84
85}
86
87QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat,
88 const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &outputCrs, int nOutputColumns, int nOutputRows,
89 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
103// Deprecated!
104QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat,
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 )
113{
114 //default to first layer's crs
115 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
116 mTransformContext = QgsProject::instance()->transformContext();
117}
118
119
120// Deprecated!
121QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat,
122 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( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toLocal8Bit().data() );
191 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
192
193 float outputNodataValue = -FLT_MAX;
194 GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue );
195
196
197 // Take the fast route (process one line at a time) if we can
198 if ( ! requiresMatrix )
199 {
200 // Map of raster names -> blocks
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 );
204 for ( const QgsRasterCalcNode *r : rasterRefNodes )
205 {
206 QString layerRef( r->toString().remove( 0, 1 ) );
207 layerRef.chop( 1 );
208 if ( ! inputBlocks.count( layerRef ) )
209 {
210 for ( const QgsRasterCalculatorEntry &ref : std::as_const( mRasterEntries ) )
211 {
212 if ( ref.ref == layerRef )
213 {
214 uniqueRasterEntries[layerRef] = ref;
215 inputBlocks[layerRef ] = std::make_unique<QgsRasterBlock>();
216 }
217 }
218 }
219 }
220
221 //read / write line by line
222 QMap<QString, QgsRasterBlock * > _rasterData;
223 // Cast to float
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 )
227 {
228 if ( feedback )
229 {
230 feedback->setProgress( 100.0 * static_cast< double >( row ) / mNumOutputRows );
231 }
232
233 if ( feedback && feedback->isCanceled() )
234 {
235 break;
236 }
237
238 // Calculates the rect for a single row read
239 QgsRectangle rect( mOutputRectangle );
240 rect.setYMaximum( rect.yMaximum() - rowHeight * row );
241 rect.setYMinimum( rect.yMaximum() - rowHeight );
242
243 // Read rows into input blocks
244 for ( auto &layerRef : inputBlocks )
245 {
246 QgsRasterCalculatorEntry ref = uniqueRasterEntries[layerRef.first];
247 if ( ref.raster->crs() != mOutputCrs )
248 {
250 proj.setCrs( ref.raster->crs(), mOutputCrs, mTransformContext );
251 proj.setInput( ref.raster->dataProvider() );
253 layerRef.second.reset( proj.block( ref.bandNumber, rect, mNumOutputColumns, 1 ) );
254 }
255 else
256 {
257 layerRef.second.reset( ref.raster->dataProvider()->block( ref.bandNumber, rect, mNumOutputColumns, 1 ) );
258 }
259 }
260
261 // 1 row X mNumOutputColumns matrix
262 QgsRasterMatrix resultMatrix( mNumOutputColumns, 1, nullptr, outputNodataValue );
263
264 _rasterData.clear();
265 for ( const auto &layerRef : inputBlocks )
266 {
267 _rasterData.insert( layerRef.first, layerRef.second.get() );
268 }
269
270 if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
271 {
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 )
274 {
275 QgsDebugMsg( QStringLiteral( "RasterIO error!" ) );
276 }
277 }
278 else
279 {
280 //delete the dataset without closing (because it is faster)
281 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
282 return CalculationError;
283 }
284 }
285
286 if ( feedback )
287 {
288 feedback->setProgress( 100.0 );
289 }
290 }
291 else // Original code (memory inefficient route)
292 {
293 QMap< QString, QgsRasterBlock * > inputBlocks;
294 QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
295 for ( ; it != mRasterEntries.constEnd(); ++it )
296 {
297
298 std::unique_ptr< QgsRasterBlock > block;
299 // if crs transform needed
300 if ( it->raster->crs() != mOutputCrs )
301 {
303 proj.setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
304 proj.setInput( it->raster->dataProvider() );
306
307 QgsRasterBlockFeedback *rasterBlockFeedback = new QgsRasterBlockFeedback();
308 QObject::connect( feedback, &QgsFeedback::canceled, rasterBlockFeedback, &QgsRasterBlockFeedback::cancel );
309 block.reset( proj.block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
310 if ( rasterBlockFeedback->isCanceled() )
311 {
312 qDeleteAll( inputBlocks );
313 return Canceled;
314 }
315 }
316 else
317 {
318 block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
319 }
320 if ( block->isEmpty() )
321 {
322 mLastError = QObject::tr( "Could not allocate required memory for %1" ).arg( it->ref );
323 qDeleteAll( inputBlocks );
324 return MemoryError;
325 }
326 inputBlocks.insert( it->ref, block.release() );
327 }
328
329 QgsRasterMatrix resultMatrix;
330 resultMatrix.setNodataValue( outputNodataValue );
331
332 //read / write line by line
333 for ( int i = 0; i < mNumOutputRows; ++i )
334 {
335 if ( feedback )
336 {
337 feedback->setProgress( 100.0 * static_cast< double >( i ) / mNumOutputRows );
338 }
339
340 if ( feedback && feedback->isCanceled() )
341 {
342 break;
343 }
344
345 if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
346 {
347 bool resultIsNumber = resultMatrix.isNumber();
348 float *calcData = new float[mNumOutputColumns];
349
350 for ( int j = 0; j < mNumOutputColumns; ++j )
351 {
352 calcData[j] = ( float )( resultIsNumber ? resultMatrix.number() : resultMatrix.data()[j] );
353 }
354
355 //write scanline to the dataset
356 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
357 {
358 QgsDebugMsg( QStringLiteral( "RasterIO error!" ) );
359 }
360
361 delete[] calcData;
362 }
363 else
364 {
365 qDeleteAll( inputBlocks );
366 inputBlocks.clear();
367 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
368 return CalculationError;
369 }
370
371 }
372
373 if ( feedback )
374 {
375 feedback->setProgress( 100.0 );
376 }
377
378 //close datasets and release memory
379 calcNode.reset();
380 qDeleteAll( inputBlocks );
381 inputBlocks.clear();
382
383 }
384
385 if ( feedback && feedback->isCanceled() )
386 {
387 //delete the dataset without closing (because it is faster)
388 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
389 return Canceled;
390 }
391
392 GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
393
394 return Success;
395}
396
397#ifdef HAVE_OPENCL
398QgsRasterCalculator::Result QgsRasterCalculator::processCalculationGPU( std::unique_ptr< QgsRasterCalcNode > calcNode, QgsFeedback *feedback )
399{
400
401 QString cExpression( calcNode->toString( true ) );
402
403 QList<const QgsRasterCalcNode *> nodeList( calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef ) );
404 QSet<QString> capturedTexts;
405 for ( const auto &r : std::as_const( nodeList ) )
406 {
407 QString s( r->toString().remove( 0, 1 ) );
408 s.chop( 1 );
409 capturedTexts.insert( s );
410 }
411
412 // Extract all references
413 struct LayerRef
414 {
415 QString name;
416 int band;
417 QgsRasterLayer *layer = nullptr;
418 QString varName;
419 QString typeName;
420 size_t index;
421 size_t bufferSize;
422 size_t dataSize;
423 };
424
425 // Collects all layers, band, name, varName and size information
426 std::vector<LayerRef> inputRefs;
427 size_t refCounter = 0;
428 for ( const auto &r : capturedTexts )
429 {
430 if ( r.startsWith( '"' ) )
431 continue;
432 QStringList parts( r.split( '@' ) );
433 if ( parts.count() != 2 )
434 continue;
435 bool ok = false;
436 LayerRef entry;
437 entry.name = r;
438 entry.band = parts[1].toInt( &ok );
439 for ( const auto &ref : std::as_const( mRasterEntries ) )
440 {
441 if ( ref.ref == entry.name )
442 entry.layer = ref.raster;
443 }
444 if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
445 continue;
446 entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
447 switch ( entry.layer->dataProvider()->dataType( entry.band ) )
448 {
450 entry.typeName = QStringLiteral( "unsigned char" );
451 break;
453 entry.typeName = QStringLiteral( "unsigned int" );
454 break;
456 entry.typeName = QStringLiteral( "short" );
457 break;
459 entry.typeName = QStringLiteral( "unsigned int" );
460 break;
462 entry.typeName = QStringLiteral( "int" );
463 break;
465 entry.typeName = QStringLiteral( "float" );
466 break;
467 // FIXME: not sure all OpenCL implementations support double
468 // maybe safer to fall back to the CPU implementation
469 // after a compatibility check
471 entry.typeName = QStringLiteral( "double" );
472 break;
473 default:
474 return BandError;
475 }
476 entry.bufferSize = entry.dataSize * mNumOutputColumns;
477 entry.index = refCounter;
478 entry.varName = QStringLiteral( "input_raster_%1_band_%2" )
479 .arg( refCounter++ )
480 .arg( entry.band );
481 inputRefs.push_back( entry );
482 }
483
484 // May throw an openCL exception
485 try
486 {
487 // Prepare context and queue
488 cl::Context ctx( QgsOpenClUtils::context() );
489 cl::CommandQueue queue( QgsOpenClUtils::commandQueue() );
490
491 // Create the C expression
492 std::vector<cl::Buffer> inputBuffers;
493 inputBuffers.reserve( inputRefs.size() );
494 QStringList inputArgs;
495 for ( const auto &ref : inputRefs )
496 {
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 ) );
501 }
502
503 //qDebug() << cExpression;
504
505 // Create the program
506 QString programTemplate( R"CL(
507 // Inputs:
508 ##INPUT_DESC##
509 // Expression: ##EXPRESSION_ORIGINAL##
510 __kernel void rasterCalculator( ##INPUT##
511 __global float *resultLine
512 )
513 {
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;
519 // Expression
520 else
521 resultLine[i] = ##EXPRESSION##;
522 }
523 )CL" );
524
525 QStringList inputDesc;
526 QStringList inputNoDataCheck;
527 for ( const auto &ref : inputRefs )
528 {
529 inputDesc.append( QStringLiteral( " // %1 = %2" ).arg( ref.varName, ref.name ) );
530 if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
531 {
532 inputNoDataCheck.append( QStringLiteral( "(float) %1[i] == (float) %2" ).arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ), 'g', 10 ) ) );
533 }
534 }
535
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( ) );
541
542 //qDebug() << programTemplate;
543
544 // Create a program from the kernel source
545 cl::Program program( QgsOpenClUtils::buildProgram( programTemplate, QgsOpenClUtils::ExceptionBehavior::Throw ) );
546
547 // Create the buffers, output is float32 (4 bytes)
548 // We assume size of float = 4 because that's the size used by OpenCL and IEEE 754
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 );
553
554 auto kernel = cl::Kernel( program, "rasterCalculator" );
555
556 for ( unsigned int i = 0; i < inputBuffers.size() ; i++ )
557 {
558 kernel.setArg( i, inputBuffers.at( i ) );
559 }
560 kernel.setArg( static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
561
562 QgsOpenClUtils::CPLAllocator<float> resultLine( static_cast<size_t>( mNumOutputColumns ) );
563
564 //open output dataset for writing
565 GDALDriverH outputDriver = openOutputDriver();
566 if ( !outputDriver )
567 {
568 mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
569 return CreateOutputError;
570 }
571
572 gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
573 if ( !outputDataset )
574 {
575 mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
576 return CreateOutputError;
577 }
578
579 GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED_GDAL ).toLocal8Bit().data() );
580
581
582 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
583 if ( !outputRasterBand )
584 return BandError;
585
586 const float outputNodataValue = -FLT_MAX;
587 GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue );
588
589 // Input block (buffer)
590 std::unique_ptr<QgsRasterBlock> block;
591
592 // Run kernel on all scanlines
593 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
594 for ( int line = 0; line < mNumOutputRows; line++ )
595 {
596 if ( feedback && feedback->isCanceled() )
597 {
598 break;
599 }
600
601 if ( feedback )
602 {
603 feedback->setProgress( 100.0 * static_cast< double >( line ) / mNumOutputRows );
604 }
605
606 // Read lines from rasters into the buffers
607 for ( const auto &ref : inputRefs )
608 {
609 // Read one row
610 QgsRectangle rect( mOutputRectangle );
611 rect.setYMaximum( rect.yMaximum() - rowHeight * line );
612 rect.setYMinimum( rect.yMaximum() - rowHeight );
613
614 // TODO: check if this is too slow
615 // if crs transform needed
616 if ( ref.layer->crs() != mOutputCrs )
617 {
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 ) );
623 }
624 else
625 {
626 block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
627 }
628
629 //for ( int i = 0; i < mNumOutputColumns; i++ )
630 // qDebug() << "Input: " << line << i << ref.varName << " = " << block->value( 0, i );
631 //qDebug() << "Writing buffer " << ref.index;
632
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() );
636
637 }
638 // Run the kernel
639 queue.enqueueNDRangeKernel(
640 kernel,
641 0,
642 cl::NDRange( mNumOutputColumns )
643 );
644
645 // Write the result
646 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0,
647 resultBufferSize, resultLine.get() );
648
649 //for ( int i = 0; i < mNumOutputColumns; i++ )
650 // qDebug() << "Output: " << line << i << " = " << resultLine[i];
651
652 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
653 {
654 return CreateOutputError;
655 }
656 }
657
658 if ( feedback && feedback->isCanceled() )
659 {
660 //delete the dataset without closing (because it is faster)
661 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
662 return Canceled;
663 }
664
665 inputBuffers.clear();
666
667 GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
668
669 }
670 catch ( cl::Error &e )
671 {
672 mLastError = e.what();
673 return CreateOutputError;
674 }
675
676 return Success;
677}
678#endif
679
680GDALDriverH QgsRasterCalculator::openOutputDriver()
681{
682 //open driver
683 GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
684
685 if ( !outputDriver )
686 {
687 return outputDriver; //return nullptr, driver does not exist
688 }
689
690 if ( !QgsGdalUtils::supportsRasterCreate( outputDriver ) )
691 {
692 return nullptr; //driver exist, but it does not support the create operation
693 }
694
695 return outputDriver;
696}
697
698gdal::dataset_unique_ptr QgsRasterCalculator::openOutputFile( GDALDriverH outputDriver )
699{
700 //open output file
701 char **papszOptions = nullptr;
702 gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
703 if ( !outputDataset )
704 {
705 return nullptr;
706 }
707
708 //assign georef information
709 double geotransform[6];
710 outputGeoTransform( geotransform );
711 GDALSetGeoTransform( outputDataset.get(), geotransform );
712
713 return outputDataset;
714}
715
716void QgsRasterCalculator::outputGeoTransform( double *transform ) const
717{
718 transform[0] = mOutputRectangle.xMinimum();
719 transform[1] = mOutputRectangle.width() / mNumOutputColumns;
720 transform[2] = 0;
721 transform[3] = mOutputRectangle.yMaximum();
722 transform[4] = 0;
723 transform[5] = -mOutputRectangle.height() / mNumOutputRows;
724}
725
727{
728 return mLastError;
729}
730
731QVector<QgsRasterCalculatorEntry> QgsRasterCalculatorEntry::rasterEntries()
732{
733 QVector<QgsRasterCalculatorEntry> availableEntries;
734 const QMap<QString, QgsMapLayer *> &layers = QgsProject::instance()->mapLayers();
735
736 auto uniqueRasterBandIdentifier = [ & ]( QgsRasterCalculatorEntry & entry ) -> bool
737 {
738 unsigned int i( 1 );
739 entry.ref = QStringLiteral( "%1@%2" ).arg( entry.raster->name() ).arg( entry.bandNumber );
740 while ( true )
741 {
742 bool unique( true );
743 for ( const auto &ref : std::as_const( availableEntries ) )
744 {
745 // Safety belt
746 if ( !( entry.raster && ref.raster ) )
747 continue;
748 // Check if is another band of the same raster
749 if ( ref.raster->publicSource() == entry.raster->publicSource() )
750 {
751 if ( ref.bandNumber != entry.bandNumber )
752 {
753 continue;
754 }
755 else // a layer with the same data source was already added to the list
756 {
757 return false;
758 }
759 }
760 // If same name but different source
761 if ( ref.ref == entry.ref )
762 {
763 unique = false;
764 entry.ref = QStringLiteral( "%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
765 }
766 }
767 if ( unique )
768 return true;
769 }
770 };
771
772 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
773 for ( ; layerIt != layers.constEnd(); ++layerIt )
774 {
775 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
776 if ( rlayer && rlayer->dataProvider() && ( rlayer->dataProvider()->capabilities() & QgsRasterDataProvider::Size ) )
777 {
778 //get number of bands
779 for ( int i = 0; i < rlayer->bandCount(); ++i )
780 {
782 entry.raster = rlayer;
783 entry.bandNumber = i + 1;
784 if ( ! uniqueRasterBandIdentifier( entry ) )
785 break;
786 availableEntries.push_back( entry );
787 }
788 }
789 }
790 return availableEntries;
791}
@ 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.
Definition: qgsfeedback.h:45
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 ...
Definition: qgsfeedback.h:108
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
static bool supportsRasterCreate(GDALDriverH driver)
Reads whether a driver supports GDALCreate() for raster purposes.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
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,...
Definition: qgsproject.h:104
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:110
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)
double number() const
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.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
Definition: qgsrectangle.h:161
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
Definition: qgsrectangle.h:166
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
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.
Definition: qgsogrutils.cpp:81
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
Definition: qgsogrutils.h:140
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
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...