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