QGIS API Documentation 3.41.0-Master (02257426e5a)
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 "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 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 QgsDebugError( 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 std::unique_ptr<QgsRasterBlock> block;
298 // if crs transform needed
299 if ( it->raster->crs() != mOutputCrs )
300 {
302 proj.setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
303 proj.setInput( it->raster->dataProvider() );
305
306 QgsRasterBlockFeedback *rasterBlockFeedback = new QgsRasterBlockFeedback();
307 QObject::connect( feedback, &QgsFeedback::canceled, rasterBlockFeedback, &QgsRasterBlockFeedback::cancel );
308 block.reset( proj.block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
309 if ( rasterBlockFeedback->isCanceled() )
310 {
311 qDeleteAll( inputBlocks );
312 return Canceled;
313 }
314 }
315 else
316 {
317 block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
318 }
319 if ( block->isEmpty() )
320 {
321 mLastError = QObject::tr( "Could not allocate required memory for %1" ).arg( it->ref );
322 qDeleteAll( inputBlocks );
323 return MemoryError;
324 }
325 inputBlocks.insert( it->ref, block.release() );
326 }
327
328 QgsRasterMatrix resultMatrix;
329 resultMatrix.setNodataValue( outputNodataValue );
330
331 //read / write line by line
332 for ( int i = 0; i < mNumOutputRows; ++i )
333 {
334 if ( feedback )
335 {
336 feedback->setProgress( 100.0 * static_cast<double>( i ) / mNumOutputRows );
337 }
338
339 if ( feedback && feedback->isCanceled() )
340 {
341 break;
342 }
343
344 if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
345 {
346 bool resultIsNumber = resultMatrix.isNumber();
347 float *calcData = new float[mNumOutputColumns];
348
349 for ( int j = 0; j < mNumOutputColumns; ++j )
350 {
351 calcData[j] = ( float ) ( resultIsNumber ? resultMatrix.number() : resultMatrix.data()[j] );
352 }
353
354 //write scanline to the dataset
355 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
356 {
357 QgsDebugError( QStringLiteral( "RasterIO error!" ) );
358 }
359
360 delete[] calcData;
361 }
362 else
363 {
364 qDeleteAll( inputBlocks );
365 inputBlocks.clear();
366 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
367 return CalculationError;
368 }
369 }
370
371 if ( feedback )
372 {
373 feedback->setProgress( 100.0 );
374 }
375
376 //close datasets and release memory
377 calcNode.reset();
378 qDeleteAll( inputBlocks );
379 inputBlocks.clear();
380 }
381
382 if ( feedback && feedback->isCanceled() )
383 {
384 //delete the dataset without closing (because it is faster)
385 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
386 return Canceled;
387 }
388
389 GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
390
391 return Success;
392}
393
394#ifdef HAVE_OPENCL
395QgsRasterCalculator::Result QgsRasterCalculator::processCalculationGPU( std::unique_ptr<QgsRasterCalcNode> calcNode, QgsFeedback *feedback )
396{
397 QString cExpression( calcNode->toString( true ) );
398
399 QList<const QgsRasterCalcNode *> nodeList( calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef ) );
400 QSet<QString> capturedTexts;
401 for ( const auto &r : std::as_const( nodeList ) )
402 {
403 QString s( r->toString().remove( 0, 1 ) );
404 s.chop( 1 );
405 capturedTexts.insert( s );
406 }
407
408 // Extract all references
409 struct LayerRef
410 {
411 QString name;
412 int band;
413 QgsRasterLayer *layer = nullptr;
414 QString varName;
415 QString typeName;
416 size_t index;
417 size_t bufferSize;
418 size_t dataSize;
419 };
420
421 // Collects all layers, band, name, varName and size information
422 std::vector<LayerRef> inputRefs;
423 size_t refCounter = 0;
424 for ( const auto &r : capturedTexts )
425 {
426 if ( r.startsWith( '"' ) )
427 continue;
428 QStringList parts( r.split( '@' ) );
429 if ( parts.count() != 2 )
430 continue;
431 bool ok = false;
432 LayerRef entry;
433 entry.name = r;
434 entry.band = parts[1].toInt( &ok );
435 for ( const auto &ref : std::as_const( mRasterEntries ) )
436 {
437 if ( ref.ref == entry.name )
438 entry.layer = ref.raster;
439 }
440 if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
441 continue;
442 entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
443 switch ( entry.layer->dataProvider()->dataType( entry.band ) )
444 {
446 entry.typeName = QStringLiteral( "unsigned char" );
447 break;
449 entry.typeName = QStringLiteral( "signed char" );
450 break;
452 entry.typeName = QStringLiteral( "unsigned int" );
453 break;
455 entry.typeName = QStringLiteral( "short" );
456 break;
458 entry.typeName = QStringLiteral( "unsigned int" );
459 break;
461 entry.typeName = QStringLiteral( "int" );
462 break;
464 entry.typeName = QStringLiteral( "float" );
465 break;
466 // FIXME: not sure all OpenCL implementations support double
467 // maybe safer to fall back to the CPU implementation
468 // after a compatibility check
470 entry.typeName = QStringLiteral( "double" );
471 break;
479 return BandError;
480 }
481 entry.bufferSize = entry.dataSize * mNumOutputColumns;
482 entry.index = refCounter;
483 entry.varName = QStringLiteral( "input_raster_%1_band_%2" )
484 .arg( refCounter++ )
485 .arg( entry.band );
486 inputRefs.push_back( entry );
487 }
488
489 // May throw an openCL exception
490 try
491 {
492 // Prepare context and queue
493 cl::Context ctx( QgsOpenClUtils::context() );
494 cl::CommandQueue queue( QgsOpenClUtils::commandQueue() );
495
496 // Create the C expression
497 std::vector<cl::Buffer> inputBuffers;
498 inputBuffers.reserve( inputRefs.size() );
499 QStringList inputArgs;
500 for ( const auto &ref : inputRefs )
501 {
502 cExpression.replace( QStringLiteral( "\"%1\"" ).arg( ref.name ), QStringLiteral( "%1[i]" ).arg( ref.varName ) );
503 inputArgs.append( QStringLiteral( "__global %1 *%2" )
504 .arg( ref.typeName, ref.varName ) );
505 inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize, nullptr, nullptr ) );
506 }
507
508 //qDebug() << cExpression;
509
510 // Create the program
511 QString programTemplate( R"CL(
512 // Inputs:
513 ##INPUT_DESC##
514 // Expression: ##EXPRESSION_ORIGINAL##
515 __kernel void rasterCalculator( ##INPUT##
516 __global float *resultLine
517 )
518 {
519 // Get the index of the current element
520 const int i = get_global_id(0);
521 // Check for nodata in input
522 if ( ##INPUT_NODATA_CHECK## )
523 resultLine[i] = -FLT_MAX;
524 // Expression
525 else
526 resultLine[i] = ##EXPRESSION##;
527 }
528 )CL" );
529
530 QStringList inputDesc;
531 QStringList inputNoDataCheck;
532 for ( const auto &ref : inputRefs )
533 {
534 inputDesc.append( QStringLiteral( " // %1 = %2" ).arg( ref.varName, ref.name ) );
535 if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
536 {
537 inputNoDataCheck.append( QStringLiteral( "(float) %1[i] == (float) %2" ).arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ), 'g', 10 ) ) );
538 }
539 }
540
541 programTemplate = programTemplate.replace( QLatin1String( "##INPUT_NODATA_CHECK##" ), inputNoDataCheck.isEmpty() ? QStringLiteral( "false" ) : inputNoDataCheck.join( QLatin1String( " || " ) ) );
542 programTemplate = programTemplate.replace( QLatin1String( "##INPUT_DESC##" ), inputDesc.join( '\n' ) );
543 programTemplate = programTemplate.replace( QLatin1String( "##INPUT##" ), !inputArgs.isEmpty() ? ( inputArgs.join( ',' ).append( ',' ) ) : QChar( ' ' ) );
544 programTemplate = programTemplate.replace( QLatin1String( "##EXPRESSION##" ), cExpression );
545 programTemplate = programTemplate.replace( QLatin1String( "##EXPRESSION_ORIGINAL##" ), calcNode->toString() );
546
547 //qDebug() << programTemplate;
548
549 // Create a program from the kernel source
550 cl::Program program( QgsOpenClUtils::buildProgram( programTemplate, QgsOpenClUtils::ExceptionBehavior::Throw ) );
551
552 // Create the buffers, output is float32 (4 bytes)
553 // We assume size of float = 4 because that's the size used by OpenCL and IEEE 754
554 Q_ASSERT( sizeof( float ) == 4 );
555 std::size_t resultBufferSize( 4 * static_cast<size_t>( mNumOutputColumns ) );
556 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, resultBufferSize, nullptr, nullptr );
557
558 auto kernel = cl::Kernel( program, "rasterCalculator" );
559
560 for ( unsigned int i = 0; i < inputBuffers.size(); i++ )
561 {
562 kernel.setArg( i, inputBuffers.at( i ) );
563 }
564 kernel.setArg( static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
565
566 QgsOpenClUtils::CPLAllocator<float> resultLine( static_cast<size_t>( mNumOutputColumns ) );
567
568 //open output dataset for writing
569 GDALDriverH outputDriver = openOutputDriver();
570 if ( !outputDriver )
571 {
572 mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
573 return CreateOutputError;
574 }
575
576 gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
577 if ( !outputDataset )
578 {
579 mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
580 return CreateOutputError;
581 }
582
583 GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLocal8Bit().data() );
584
585
586 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
587 if ( !outputRasterBand )
588 return BandError;
589
590 const float outputNodataValue = -FLT_MAX;
591 GDALSetRasterNoDataValue( outputRasterBand, outputNodataValue );
592
593 // Input block (buffer)
594 std::unique_ptr<QgsRasterBlock> block;
595
596 // Run kernel on all scanlines
597 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
598 for ( int line = 0; line < mNumOutputRows; line++ )
599 {
600 if ( feedback && feedback->isCanceled() )
601 {
602 break;
603 }
604
605 if ( feedback )
606 {
607 feedback->setProgress( 100.0 * static_cast<double>( line ) / mNumOutputRows );
608 }
609
610 // Read lines from rasters into the buffers
611 for ( const auto &ref : inputRefs )
612 {
613 // Read one row
614 QgsRectangle rect( mOutputRectangle );
615 rect.setYMaximum( rect.yMaximum() - rowHeight * line );
616 rect.setYMinimum( rect.yMaximum() - rowHeight );
617
618 // TODO: check if this is too slow
619 // if crs transform needed
620 if ( ref.layer->crs() != mOutputCrs )
621 {
623 proj.setCrs( ref.layer->crs(), mOutputCrs, ref.layer->transformContext() );
624 proj.setInput( ref.layer->dataProvider() );
626 block.reset( proj.block( ref.band, rect, mNumOutputColumns, 1 ) );
627 }
628 else
629 {
630 block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
631 }
632
633 //for ( int i = 0; i < mNumOutputColumns; i++ )
634 // qDebug() << "Input: " << line << i << ref.varName << " = " << block->value( 0, i );
635 //qDebug() << "Writing buffer " << ref.index;
636
637 Q_ASSERT( ref.bufferSize == static_cast<size_t>( block->data().size() ) );
638 queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0, ref.bufferSize, block->bits() );
639 }
640 // Run the kernel
641 queue.enqueueNDRangeKernel(
642 kernel,
643 0,
644 cl::NDRange( mNumOutputColumns )
645 );
646
647 // Write the result
648 queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, resultBufferSize, resultLine.get() );
649
650 //for ( int i = 0; i < mNumOutputColumns; i++ )
651 // qDebug() << "Output: " << line << i << " = " << resultLine[i];
652
653 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
654 {
655 return CreateOutputError;
656 }
657 }
658
659 if ( feedback && feedback->isCanceled() )
660 {
661 //delete the dataset without closing (because it is faster)
662 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
663 return Canceled;
664 }
665
666 inputBuffers.clear();
667
668 GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
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 unsigned int i( 1 );
738 entry.ref = QStringLiteral( "%1@%2" ).arg( entry.raster->name() ).arg( entry.bandNumber );
739 while ( true )
740 {
741 bool unique( true );
742 for ( const auto &ref : std::as_const( availableEntries ) )
743 {
744 // Safety belt
745 if ( !( entry.raster && ref.raster ) )
746 continue;
747 // Check if is another band of the same raster
748 if ( ref.raster->publicSource() == entry.raster->publicSource() )
749 {
750 if ( ref.bandNumber != entry.bandNumber )
751 {
752 continue;
753 }
754 else // a layer with the same data source was already added to the list
755 {
756 return false;
757 }
758 }
759 // If same name but different source
760 if ( ref.ref == entry.ref )
761 {
762 unique = false;
763 entry.ref = QStringLiteral( "%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
764 }
765 }
766 if ( unique )
767 return true;
768 }
769 };
770
771 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
772 for ( ; layerIt != layers.constEnd(); ++layerIt )
773 {
774 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
775 if ( rlayer && rlayer->dataProvider() && ( rlayer->dataProvider()->capabilities() & Qgis::RasterInterfaceCapability::Size ) )
776 {
777 //get number of bands
778 for ( int i = 0; i < rlayer->bandCount(); ++i )
779 {
781 entry.raster = rlayer;
782 entry.bandNumber = i + 1;
783 if ( !uniqueRasterBandIdentifier( entry ) )
784 break;
785 availableEntries.push_back( entry );
786 }
787 }
788 }
789 return availableEntries;
790}
@ 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.
This class 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.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
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...