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