QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsrastercalculator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrastercalculator.cpp - description
3 -----------------------
4 begin : September 28th, 2010
5 copyright : (C) 2010 by Marco Hugentobler
6 email : marco dot hugentobler at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgsrastercalculator.h"
19
20#include <cpl_string.h>
21#include <gdalwarper.h>
22
23#include "qgsfeedback.h"
24#include "qgsgdalutils.h"
25#include "qgsogrutils.h"
26#include "qgsproject.h"
28#include "qgsrasterinterface.h"
29#include "qgsrasterlayer.h"
30#include "qgsrastermatrix.h"
31#include "qgsrasterprojector.h"
32
33#include <QFile>
34
35#ifdef HAVE_OPENCL
36#include "qgsopenclutils.h"
37#include "qgsgdalutils.h"
38#endif
39
40
41//
42// global callback function
43//
44int CPL_STDCALL GdalProgressCallback( double dfComplete, const char *pszMessage, void *pProgressArg )
45{
46 Q_UNUSED( pszMessage )
47
48 static double sDfLastComplete = -1.0;
49
50 QgsFeedback *feedback = static_cast<QgsFeedback *>( pProgressArg );
51
52 if ( sDfLastComplete > dfComplete )
53 {
54 if ( sDfLastComplete >= 1.0 )
55 sDfLastComplete = -1.0;
56 else
57 sDfLastComplete = dfComplete;
58 }
59
60 if ( std::floor( sDfLastComplete * 10 ) != std::floor( dfComplete * 10 ) )
61 {
62 if ( feedback )
63 feedback->setProgress( dfComplete * 100 );
64 }
65 sDfLastComplete = dfComplete;
66
67 if ( feedback && feedback->isCanceled() )
68 return false;
69
70 return true;
71}
72
73QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries, const QgsCoordinateTransformContext &transformContext )
74 : mFormulaString( formulaString )
75 , mOutputFile( outputFile )
76 , mOutputFormat( outputFormat )
77 , mOutputRectangle( outputExtent )
78 , mNumOutputColumns( nOutputColumns )
79 , mNumOutputRows( nOutputRows )
80 , mRasterEntries( rasterEntries )
81 , mTransformContext( transformContext )
82{
83 //default to first layer's crs
84 if ( !mRasterEntries.isEmpty() )
85 {
86 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
87 }
88}
89
90QgsRasterCalculator::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 )
91 : mFormulaString( formulaString )
92 , mOutputFile( outputFile )
93 , mOutputFormat( outputFormat )
94 , mOutputRectangle( outputExtent )
95 , mOutputCrs( outputCrs )
96 , mNumOutputColumns( nOutputColumns )
97 , mNumOutputRows( nOutputRows )
98 , mRasterEntries( rasterEntries )
99 , mTransformContext( transformContext )
100{
101}
102
103// Deprecated!
104QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries )
105 : mFormulaString( formulaString )
106 , mOutputFile( outputFile )
107 , mOutputFormat( outputFormat )
108 , mOutputRectangle( outputExtent )
109 , mNumOutputColumns( nOutputColumns )
110 , mNumOutputRows( nOutputRows )
111 , mRasterEntries( rasterEntries )
112{
113 //default to first layer's crs
114 if ( !mRasterEntries.isEmpty() )
115 {
116 mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
117 }
118 mTransformContext = QgsProject::instance()->transformContext();
119}
120
121
122// Deprecated!
123QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &outputCrs, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries )
124 : mFormulaString( formulaString )
125 , mOutputFile( outputFile )
126 , mOutputFormat( outputFormat )
127 , mOutputRectangle( outputExtent )
128 , mOutputCrs( outputCrs )
129 , mNumOutputColumns( nOutputColumns )
130 , mNumOutputRows( nOutputRows )
131 , mRasterEntries( rasterEntries )
132 , mTransformContext( QgsProject::instance()->transformContext() )
133{
134}
135
137{
138 mLastError.clear();
139
140 //prepare search string / tree
141 std::unique_ptr<QgsRasterCalcNode> calcNode( QgsRasterCalcNode::parseRasterCalcString( mFormulaString, mLastError ) );
142 if ( !calcNode )
143 {
144 //error
146 }
147
148 // Check input layers and bands
149 for ( const auto &entry : std::as_const( mRasterEntries ) )
150 {
151 if ( !entry.raster ) // no raster layer in entry
152 {
153 mLastError = QObject::tr( "No raster layer for entry %1" ).arg( entry.ref );
155 }
156 if ( entry.bandNumber <= 0 || entry.bandNumber > entry.raster->bandCount() )
157 {
158 mLastError = QObject::tr( "Band number %1 is not valid for entry %2" ).arg( entry.bandNumber ).arg( entry.ref );
160 }
161 }
162
163 // Check if we need to read the raster as a whole (which is memory inefficient
164 // and not interruptible by the user) by checking if any raster matrix nodes are
165 // in the expression
166 bool requiresMatrix = !calcNode->findNodes( QgsRasterCalcNode::Type::tMatrix ).isEmpty();
167
168#ifdef HAVE_OPENCL
169 // Check for matrix nodes, GPU implementation does not support them
170 if ( QgsOpenClUtils::enabled() && QgsOpenClUtils::available() && !requiresMatrix )
171 {
172 return processCalculationGPU( std::move( calcNode ), feedback );
173 }
174#endif
175
176 //open output dataset for writing
177 GDALDriverH outputDriver = openOutputDriver();
178 if ( !outputDriver )
179 {
180 mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
182 }
183
184 gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
185 if ( !outputDataset )
186 {
187 mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
189 }
190
191 GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLocal8Bit().data() );
192 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
193
194 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
195
196 // Take the fast route (process one line at a time) if we can
197 if ( !requiresMatrix )
198 {
199 // Map of raster names -> blocks
200 std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
201 std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries;
202 const QList<const QgsRasterCalcNode *> rasterRefNodes = calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef );
203 for ( const QgsRasterCalcNode *r : rasterRefNodes )
204 {
205 QString layerRef( r->toString().remove( 0, 1 ) );
206 layerRef.chop( 1 );
207 if ( !inputBlocks.count( layerRef ) )
208 {
209 for ( const QgsRasterCalculatorEntry &ref : std::as_const( mRasterEntries ) )
210 {
211 if ( ref.ref == layerRef )
212 {
213 uniqueRasterEntries[layerRef] = ref;
214 inputBlocks[layerRef] = std::make_unique<QgsRasterBlock>();
215 }
216 }
217 }
218 }
219
220 //read / write line by line
221 QMap<QString, QgsRasterBlock *> _rasterData;
222 // Cast to float
223 std::vector<float> castedResult( static_cast<size_t>( mNumOutputColumns ), 0 );
224 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
225 for ( size_t row = 0; row < static_cast<size_t>( mNumOutputRows ); ++row )
226 {
227 if ( feedback )
228 {
229 feedback->setProgress( 100.0 * static_cast<double>( row ) / mNumOutputRows );
230 }
231
232 if ( feedback && feedback->isCanceled() )
233 {
234 break;
235 }
236
237 // Calculates the rect for a single row read
238 QgsRectangle rect( mOutputRectangle );
239 rect.setYMaximum( rect.yMaximum() - rowHeight * row );
240 rect.setYMinimum( rect.yMaximum() - rowHeight );
241
242 // Read rows into input blocks
243 for ( auto &layerRef : inputBlocks )
244 {
245 QgsRasterCalculatorEntry ref = uniqueRasterEntries[layerRef.first];
246 if ( ref.raster->crs() != mOutputCrs )
247 {
249 proj.setCrs( ref.raster->crs(), mOutputCrs, mTransformContext );
250 proj.setInput( ref.raster->dataProvider() );
252 layerRef.second.reset( proj.block( ref.bandNumber, rect, mNumOutputColumns, 1 ) );
253 }
254 else
255 {
256 layerRef.second.reset( ref.raster->dataProvider()->block( ref.bandNumber, rect, mNumOutputColumns, 1 ) );
257 }
258 }
259
260 // 1 row X mNumOutputColumns matrix
261 QgsRasterMatrix resultMatrix( mNumOutputColumns, 1, nullptr, mNoDataValue );
262
263 _rasterData.clear();
264 for ( const auto &layerRef : inputBlocks )
265 {
266 _rasterData.insert( layerRef.first, layerRef.second.get() );
267 }
268
269 if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
270 {
271 std::copy( resultMatrix.data(), resultMatrix.data() + mNumOutputColumns, castedResult.begin() );
272 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, row, mNumOutputColumns, 1, castedResult.data(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
273 {
274 QgsDebugError( QStringLiteral( "RasterIO error!" ) );
275 }
276 }
277 else
278 {
279 //delete the dataset without closing (because it is faster)
280 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
282 }
283 }
284
285 if ( feedback )
286 {
287 feedback->setProgress( 100.0 );
288 }
289 }
290 else // Original code (memory inefficient route)
291 {
292 QMap<QString, QgsRasterBlock *> inputBlocks;
293 QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
294 for ( ; it != mRasterEntries.constEnd(); ++it )
295 {
296 std::unique_ptr<QgsRasterBlock> block;
297 // if crs transform needed
298 if ( it->raster->crs() != mOutputCrs )
299 {
301 proj.setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
302 proj.setInput( it->raster->dataProvider() );
304
305 QgsRasterBlockFeedback *rasterBlockFeedback = new QgsRasterBlockFeedback();
306 QObject::connect( feedback, &QgsFeedback::canceled, rasterBlockFeedback, &QgsRasterBlockFeedback::cancel );
307 block.reset( proj.block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
308 if ( rasterBlockFeedback->isCanceled() )
309 {
310 qDeleteAll( inputBlocks );
312 }
313 }
314 else
315 {
316 block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
317 }
318 if ( block->isEmpty() )
319 {
320 mLastError = QObject::tr( "Could not allocate required memory for %1" ).arg( it->ref );
321 qDeleteAll( inputBlocks );
323 }
324 inputBlocks.insert( it->ref, block.release() );
325 }
326
327 QgsRasterMatrix resultMatrix;
328 resultMatrix.setNodataValue( mNoDataValue );
329
330 //read / write line by line
331 for ( int i = 0; i < mNumOutputRows; ++i )
332 {
333 if ( feedback )
334 {
335 feedback->setProgress( 100.0 * static_cast<double>( i ) / mNumOutputRows );
336 }
337
338 if ( feedback && feedback->isCanceled() )
339 {
340 break;
341 }
342
343 if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
344 {
345 bool resultIsNumber = resultMatrix.isNumber();
346 float *calcData = new float[mNumOutputColumns];
347
348 for ( int j = 0; j < mNumOutputColumns; ++j )
349 {
350 calcData[j] = ( float ) ( resultIsNumber ? resultMatrix.number() : resultMatrix.data()[j] );
351 }
352
353 //write scanline to the dataset
354 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
355 {
356 QgsDebugError( QStringLiteral( "RasterIO error!" ) );
357 }
358
359 delete[] calcData;
360 }
361 else
362 {
363 qDeleteAll( inputBlocks );
364 inputBlocks.clear();
365 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
367 }
368 }
369
370 if ( feedback )
371 {
372 feedback->setProgress( 100.0 );
373 }
374
375 //close datasets and release memory
376 calcNode.reset();
377 qDeleteAll( inputBlocks );
378 inputBlocks.clear();
379 }
380
381 if ( feedback && feedback->isCanceled() )
382 {
383 //delete the dataset without closing (because it is faster)
384 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
386 }
387
388 GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
389
391}
392
393#ifdef HAVE_OPENCL
394QgsRasterCalculator::Result QgsRasterCalculator::processCalculationGPU( std::unique_ptr<QgsRasterCalcNode> calcNode, QgsFeedback *feedback )
395{
396 QString cExpression( calcNode->toString( true ) );
397
398 QList<const QgsRasterCalcNode *> nodeList( calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef ) );
399 QSet<QString> capturedTexts;
400 for ( const auto &r : std::as_const( nodeList ) )
401 {
402 QString s( r->toString().remove( 0, 1 ) );
403 s.chop( 1 );
404 capturedTexts.insert( s );
405 }
406
407 // Extract all references
408 struct LayerRef
409 {
410 QString name;
411 int band;
412 QgsRasterLayer *layer = nullptr;
413 QString varName;
414 QString typeName;
415 size_t index;
416 size_t bufferSize;
417 size_t dataSize;
418 };
419
420 // Collects all layers, band, name, varName and size information
421 std::vector<LayerRef> inputRefs;
422 size_t refCounter = 0;
423 for ( const auto &r : capturedTexts )
424 {
425 if ( r.startsWith( '"' ) )
426 continue;
427 QStringList parts( r.split( '@' ) );
428 if ( parts.count() != 2 )
429 continue;
430 bool ok = false;
431 LayerRef entry;
432 entry.name = r;
433 entry.band = parts[1].toInt( &ok );
434 for ( const auto &ref : std::as_const( mRasterEntries ) )
435 {
436 if ( ref.ref == entry.name )
437 entry.layer = ref.raster;
438 }
439 if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
440 continue;
441 entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
442 switch ( entry.layer->dataProvider()->dataType( entry.band ) )
443 {
445 entry.typeName = QStringLiteral( "unsigned char" );
446 break;
448 entry.typeName = QStringLiteral( "char" );
449 break;
451 entry.typeName = QStringLiteral( "unsigned short" );
452 break;
454 entry.typeName = QStringLiteral( "short" );
455 break;
457 entry.typeName = QStringLiteral( "unsigned int" );
458 break;
460 entry.typeName = QStringLiteral( "int" );
461 break;
463 entry.typeName = QStringLiteral( "float" );
464 break;
465 // FIXME: not sure all OpenCL implementations support double
466 // maybe safer to fall back to the CPU implementation
467 // after a compatibility check
469 entry.typeName = QStringLiteral( "double" );
470 break;
479 }
480 entry.bufferSize = entry.dataSize * mNumOutputColumns;
481 entry.index = refCounter;
482 entry.varName = QStringLiteral( "input_raster_%1_band_%2" )
483 .arg( refCounter++ )
484 .arg( entry.band );
485 inputRefs.push_back( entry );
486 }
487
488 // May throw an openCL exception
489 try
490 {
491 // Prepare context and queue
492 cl::Context ctx( QgsOpenClUtils::context() );
493 cl::CommandQueue queue( QgsOpenClUtils::commandQueue() );
494
495 // Create the C expression
496 std::vector<cl::Buffer> inputBuffers;
497 inputBuffers.reserve( inputRefs.size() );
498 QStringList inputArgs;
499 for ( const auto &ref : inputRefs )
500 {
501 cExpression.replace( QStringLiteral( "\"%1\"" ).arg( ref.name ), QStringLiteral( "%1[i]" ).arg( ref.varName ) );
502 inputArgs.append( QStringLiteral( "__global %1 *%2" )
503 .arg( ref.typeName, ref.varName ) );
504 inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize, nullptr, nullptr ) );
505 }
506
507 //qDebug() << cExpression;
508
509 // Create the program
510 QString programTemplate( R"CL(
511 // Inputs:
512 ##INPUT_DESC##
513 // Expression: ##EXPRESSION_ORIGINAL##
514 __kernel void rasterCalculator( ##INPUT##
515 __global float *resultLine
516 )
517 {
518 // Get the index of the current element
519 const int i = get_global_id(0);
520 // Check for nodata in input
521 if ( ##INPUT_NODATA_CHECK## )
522 resultLine[i] = -FLT_MAX;
523 // Expression
524 else
525 resultLine[i] = ##EXPRESSION##;
526 }
527 )CL" );
528
529 QStringList inputDesc;
530 QStringList inputNoDataCheck;
531 for ( const auto &ref : inputRefs )
532 {
533 inputDesc.append( QStringLiteral( " // %1 = %2" ).arg( ref.varName, ref.name ) );
534 if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
535 {
536 inputNoDataCheck.append( QStringLiteral( "(float) %1[i] == (float) %2" ).arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ), 'g', 10 ) ) );
537 }
538 }
539
540 programTemplate = programTemplate.replace( QLatin1String( "##INPUT_NODATA_CHECK##" ), inputNoDataCheck.isEmpty() ? QStringLiteral( "false" ) : inputNoDataCheck.join( QLatin1String( " || " ) ) );
541 programTemplate = programTemplate.replace( QLatin1String( "##INPUT_DESC##" ), inputDesc.join( '\n' ) );
542 programTemplate = programTemplate.replace( QLatin1String( "##INPUT##" ), !inputArgs.isEmpty() ? ( inputArgs.join( ',' ).append( ',' ) ) : QChar( ' ' ) );
543 programTemplate = programTemplate.replace( QLatin1String( "##EXPRESSION##" ), cExpression );
544 programTemplate = programTemplate.replace( QLatin1String( "##EXPRESSION_ORIGINAL##" ), calcNode->toString() );
545
546 // qDebug() << programTemplate;
547
548 // Create a program from the kernel source
549 cl::Program program( QgsOpenClUtils::buildProgram( programTemplate, QgsOpenClUtils::ExceptionBehavior::Throw ) );
550
551 // Create the buffers, output is float32 (4 bytes)
552 // We assume size of float = 4 because that's the size used by OpenCL and IEEE 754
553 Q_ASSERT( sizeof( float ) == 4 );
554 std::size_t resultBufferSize( 4 * static_cast<size_t>( mNumOutputColumns ) );
555 cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, resultBufferSize, nullptr, nullptr );
556
557 auto kernel = cl::Kernel( program, "rasterCalculator" );
558
559 for ( unsigned int i = 0; i < inputBuffers.size(); i++ )
560 {
561 kernel.setArg( i, inputBuffers.at( i ) );
562 }
563 kernel.setArg( static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
564
565 QgsOpenClUtils::CPLAllocator<float> resultLine( static_cast<size_t>( mNumOutputColumns ) );
566
567 //open output dataset for writing
568 GDALDriverH outputDriver = openOutputDriver();
569 if ( !outputDriver )
570 {
571 mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
573 }
574
575 gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
576 if ( !outputDataset )
577 {
578 mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
580 }
581
582 GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLocal8Bit().data() );
583
584
585 GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
586 if ( !outputRasterBand )
588
589 GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
590
591 // Input block (buffer)
592 std::unique_ptr<QgsRasterBlock> block;
593
594 // Run kernel on all scanlines
595 auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
596 for ( int line = 0; line < mNumOutputRows; line++ )
597 {
598 if ( feedback && feedback->isCanceled() )
599 {
600 break;
601 }
602
603 if ( feedback )
604 {
605 feedback->setProgress( 100.0 * static_cast<double>( line ) / mNumOutputRows );
606 }
607
608 // Read lines from rasters into the buffers
609 for ( const auto &ref : inputRefs )
610 {
611 // Read one row
612 QgsRectangle rect( mOutputRectangle );
613 rect.setYMaximum( rect.yMaximum() - rowHeight * line );
614 rect.setYMinimum( rect.yMaximum() - rowHeight );
615
616 // TODO: check if this is too slow
617 // if crs transform needed
618 if ( ref.layer->crs() != mOutputCrs )
619 {
620 QgsRasterProjector proj;
621 proj.setCrs( ref.layer->crs(), mOutputCrs, ref.layer->transformContext() );
622 proj.setInput( ref.layer->dataProvider() );
624 block.reset( proj.block( ref.band, rect, mNumOutputColumns, 1 ) );
625 }
626 else
627 {
628 block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
629 }
630
631 //for ( int i = 0; i < mNumOutputColumns; i++ )
632 // qDebug() << "Input: " << line << i << ref.varName << " = " << block->value( 0, i );
633 //qDebug() << "Writing buffer " << ref.index;
634
635 Q_ASSERT( ref.bufferSize == static_cast<size_t>( block->data().size() ) );
636 queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0, ref.bufferSize, block->bits() );
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, resultBufferSize, resultLine.get() );
647
648 //for ( int i = 0; i < mNumOutputColumns; i++ )
649 // qDebug() << "Output: " << line << i << " = " << resultLine[i];
650
651 if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
652 {
654 }
655 }
656
657 if ( feedback && feedback->isCanceled() )
658 {
659 //delete the dataset without closing (because it is faster)
660 gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
662 }
663
664 inputBuffers.clear();
665
666 GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
667 }
668 catch ( cl::Error &e )
669 {
670 mLastError = e.what();
672 }
673
675}
676#endif
677
678GDALDriverH QgsRasterCalculator::openOutputDriver()
679{
680 //open driver
681 GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
682
683 if ( !outputDriver )
684 {
685 return outputDriver; //return nullptr, driver does not exist
686 }
687
688 if ( !QgsGdalUtils::supportsRasterCreate( outputDriver ) )
689 {
690 return nullptr; //driver exist, but it does not support the create operation
691 }
692
693 return outputDriver;
694}
695
696gdal::dataset_unique_ptr QgsRasterCalculator::openOutputFile( GDALDriverH outputDriver )
697{
698 //open output file
699 char **papszOptions = QgsGdalUtils::papszFromStringList( mCreationOptions );
700 gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
701 CSLDestroy( papszOptions );
702 if ( !outputDataset )
703 {
704 return nullptr;
705 }
706
707 //assign georef information
708 double geotransform[6];
709 outputGeoTransform( geotransform );
710 GDALSetGeoTransform( outputDataset.get(), geotransform );
711
712 return outputDataset;
713}
714
715void QgsRasterCalculator::outputGeoTransform( double *transform ) const
716{
717 transform[0] = mOutputRectangle.xMinimum();
718 transform[1] = mOutputRectangle.width() / mNumOutputColumns;
719 transform[2] = 0;
720 transform[3] = mOutputRectangle.yMaximum();
721 transform[4] = 0;
722 transform[5] = -mOutputRectangle.height() / mNumOutputRows;
723}
724
726{
727 return mLastError;
728}
729
730QVector<QgsRasterCalculatorEntry> QgsRasterCalculatorEntry::rasterEntries()
731{
732 QVector<QgsRasterCalculatorEntry> availableEntries;
733 const QMap<QString, QgsMapLayer *> &layers = QgsProject::instance()->mapLayers();
734
735 auto uniqueRasterBandIdentifier = [&]( QgsRasterCalculatorEntry &entry ) -> bool {
736 unsigned int i( 1 );
737 entry.ref = QStringLiteral( "%1@%2" ).arg( entry.raster ? entry.raster->name() : QString() ).arg( entry.bandNumber );
738 while ( true )
739 {
740 bool unique( true );
741 for ( const auto &ref : std::as_const( availableEntries ) )
742 {
743 // Safety belt
744 if ( !( entry.raster && ref.raster ) )
745 continue;
746 // Check if is another band of the same raster
747 if ( ref.raster->publicSource() == entry.raster->publicSource() )
748 {
749 if ( ref.bandNumber != entry.bandNumber )
750 {
751 continue;
752 }
753 else // a layer with the same data source was already added to the list
754 {
755 return false;
756 }
757 }
758 // If same name but different source
759 if ( ref.ref == entry.ref )
760 {
761 unique = false;
762 entry.ref = QStringLiteral( "%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
763 }
764 }
765 if ( unique )
766 return true;
767 }
768 };
769
770 QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
771 for ( ; layerIt != layers.constEnd(); ++layerIt )
772 {
773 QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
774 if ( rlayer && rlayer->dataProvider() && ( rlayer->dataProvider()->capabilities() & Qgis::RasterInterfaceCapability::Size ) )
775 {
776 //get number of bands
777 for ( int i = 0; i < rlayer->bandCount(); ++i )
778 {
780 entry.raster = rlayer;
781 entry.bandNumber = i + 1;
782 if ( !uniqueRasterBandIdentifier( entry ) )
783 break;
784 availableEntries.push_back( entry );
785 }
786 }
787 }
788 return availableEntries;
789}
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
Definition qgis.h:4855
@ CInt32
Complex Int32.
Definition qgis.h:383
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:380
@ CFloat64
Complex Float64.
Definition qgis.h:385
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:377
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:387
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:375
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:376
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:374
@ UnknownDataType
Unknown or unspecified type.
Definition qgis.h:373
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:386
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:379
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:381
@ CFloat32
Complex Float32.
Definition qgis.h:384
@ CInt16
Complex Int16.
Definition qgis.h:382
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:378
@ PreferredGdal
Preferred format for conversion of CRS to WKT for use with the GDAL library.
Definition qgis.h:2441
Represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void canceled()
Internal routines can connect to this signal if they use event loop.
void cancel()
Tells the internal routines that the current operation should be canceled. This should be run by the ...
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
static bool supportsRasterCreate(GDALDriverH driver)
Reads whether a driver supports GDALCreate() for raster purposes.
static char ** papszFromStringList(const QStringList &list)
Helper function.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:87
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:109
static QgsProject * instance()
Returns the QgsProject singleton instance.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
Feedback object tailored for raster block reading.
Represents a node in a raster calculator.
static QgsRasterCalcNode * parseRasterCalcString(const QString &str, QString &parserErrorMsg)
Represents an individual raster layer/band number entry within a raster calculation.
QgsRasterLayer * raster
Raster layer associated with entry.
static QVector< QgsRasterCalculatorEntry > rasterEntries()
Creates a list of raster entries from the current project.
int bandNumber
Band number for entry.
QString ref
Name of entry.
QgsRasterCalculator(const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &outputCrs, int nOutputColumns, int nOutputRows, const QVector< QgsRasterCalculatorEntry > &rasterEntries, const QgsCoordinateTransformContext &transformContext)
QgsRasterCalculator constructor.
QString lastError() const
Returns a description of the last error encountered.
Result processCalculation(QgsFeedback *feedback=nullptr)
Starts the calculation and writes a new raster.
Result
Result of the calculation.
@ Canceled
User canceled calculation.
@ InputLayerError
Error reading input layer.
@ BandError
Invalid band number for input.
@ Success
Calculation successful.
@ CreateOutputError
Error creating output data file.
@ ParserError
Error parsing formula.
@ CalculationError
Error occurred while performing calculation.
@ MemoryError
Error allocating memory for result.
QgsRasterBlock * block(int bandNo, const QgsRectangle &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
virtual Qgis::RasterInterfaceCapabilities capabilities() const
Returns the capabilities supported by the interface.
virtual bool setInput(QgsRasterInterface *input)
Set input.
Represents a raster layer.
int bandCount() const
Returns the number of bands in this layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Represents a matrix in a raster calculator operation.
bool isNumber() const
Returns true if matrix is 1x1 (=scalar number).
void setNodataValue(double d)
double * data()
Returns data array (but not ownership).
double number() const
Implements approximate projection support for optimised raster transformation.
void setPrecision(Precision precision)
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
@ Exact
Exact, precise but slow.
Q_DECL_DEPRECATED void setCrs(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
Sets the source and destination CRS.
A rectangle specified with double values.
void setYMinimum(double y)
Set the minimum y value.
void setYMaximum(double y)
Set the maximum y value.
double yMaximum
void CORE_EXPORT fast_delete_and_close(dataset_unique_ptr &dataset, GDALDriverH driver, const QString &path)
Performs a fast close of an unwanted GDAL dataset handle by deleting the underlying data store.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
#define QgsDebugError(str)
Definition qgslogger.h:57
int CPL_STDCALL GdalProgressCallback(double dfComplete, const char *pszMessage, void *pProgressArg)