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