Statistics
| Branch: | Tag: | Revision:

qgis / src / analysis / raster / qgsrastercalculator.cpp @ master

History | View | Annotate | Download (27.4 KB)

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

    
40
//
41
// global callback function
42
//
43
int CPL_STDCALL GdalProgressCallback( double dfComplete, const char *pszMessage, void *pProgressArg )
44
{
45
  Q_UNUSED( pszMessage )
46

    
47
  static double sDfLastComplete = -1.0;
48

    
49
  QgsFeedback *feedback = static_cast<QgsFeedback *>( pProgressArg );
50

    
51
  if ( sDfLastComplete > dfComplete )
52
  {
53
    if ( sDfLastComplete >= 1.0 )
54
      sDfLastComplete = -1.0;
55
    else
56
      sDfLastComplete = dfComplete;
57
  }
58

    
59
  if ( std::floor( sDfLastComplete * 10 ) != std::floor( dfComplete * 10 ) )
60
  {
61
    if ( feedback )
62
      feedback->setProgress( dfComplete * 100 );
63
  }
64
  sDfLastComplete = dfComplete;
65

    
66
  if ( feedback && feedback->isCanceled() )
67
    return false;
68

    
69
  return true;
70
}
71

    
72
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 )
73
  : mFormulaString( formulaString )
74
  , mOutputFile( outputFile )
75
  , mOutputFormat( outputFormat )
76
  , mOutputRectangle( outputExtent )
77
  , mNumOutputColumns( nOutputColumns )
78
  , mNumOutputRows( nOutputRows )
79
  , mRasterEntries( rasterEntries )
80
  , mTransformContext( transformContext )
81
{
82
  //default to first layer's crs
83
  if ( !mRasterEntries.isEmpty() )
84
  {
85
    mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
86
  }
87
}
88

    
89
QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &outputCrs, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries, const QgsCoordinateTransformContext &transformContext )
90
  : mFormulaString( formulaString )
91
  , mOutputFile( outputFile )
92
  , mOutputFormat( outputFormat )
93
  , mOutputRectangle( outputExtent )
94
  , mOutputCrs( outputCrs )
95
  , mNumOutputColumns( nOutputColumns )
96
  , mNumOutputRows( nOutputRows )
97
  , mRasterEntries( rasterEntries )
98
  , mTransformContext( transformContext )
99
{
100
}
101

    
102
// Deprecated!
103
QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries )
104
  : mFormulaString( formulaString )
105
  , mOutputFile( outputFile )
106
  , mOutputFormat( outputFormat )
107
  , mOutputRectangle( outputExtent )
108
  , mNumOutputColumns( nOutputColumns )
109
  , mNumOutputRows( nOutputRows )
110
  , mRasterEntries( rasterEntries )
111
{
112
  //default to first layer's crs
113
  if ( !mRasterEntries.isEmpty() )
114
  {
115
    mOutputCrs = mRasterEntries.at( 0 ).raster->crs();
116
  }
117
  mTransformContext = QgsProject::instance()->transformContext();
118
}
119

    
120

    
121
// Deprecated!
122
QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat, const QgsRectangle &outputExtent, const QgsCoordinateReferenceSystem &outputCrs, int nOutputColumns, int nOutputRows, const QVector<QgsRasterCalculatorEntry> &rasterEntries )
123
  : mFormulaString( formulaString )
124
  , mOutputFile( outputFile )
125
  , mOutputFormat( outputFormat )
126
  , mOutputRectangle( outputExtent )
127
  , mOutputCrs( outputCrs )
128
  , mNumOutputColumns( nOutputColumns )
129
  , mNumOutputRows( nOutputRows )
130
  , mRasterEntries( rasterEntries )
131
  , mTransformContext( QgsProject::instance()->transformContext() )
132
{
133
}
134

    
135
QgsRasterCalculator::Result QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
136
{
137
  mLastError.clear();
138

    
139
  //prepare search string / tree
140
  std::unique_ptr<QgsRasterCalcNode> calcNode( QgsRasterCalcNode::parseRasterCalcString( mFormulaString, mLastError ) );
141
  if ( !calcNode )
142
  {
143
    //error
144
    return QgsRasterCalculator::Result::ParserError;
145
  }
146

    
147
  // Check input layers and bands
148
  for ( const auto &entry : std::as_const( mRasterEntries ) )
149
  {
150
    if ( !entry.raster ) // no raster layer in entry
151
    {
152
      mLastError = QObject::tr( "No raster layer for entry %1" ).arg( entry.ref );
153
      return QgsRasterCalculator::Result::InputLayerError;
154
    }
155
    if ( entry.bandNumber <= 0 || entry.bandNumber > entry.raster->bandCount() )
156
    {
157
      mLastError = QObject::tr( "Band number %1 is not valid for entry %2" ).arg( entry.bandNumber ).arg( entry.ref );
158
      return QgsRasterCalculator::Result::BandError;
159
    }
160
  }
161

    
162
  // Check if we need to read the raster as a whole (which is memory inefficient
163
  // and not interruptible by the user) by checking if any raster matrix nodes are
164
  // in the expression
165
  bool requiresMatrix = !calcNode->findNodes( QgsRasterCalcNode::Type::tMatrix ).isEmpty();
166

    
167
#ifdef HAVE_OPENCL
168
  // Check for matrix nodes, GPU implementation does not support them
169
  if ( QgsOpenClUtils::enabled() && QgsOpenClUtils::available() && !requiresMatrix )
170
  {
171
    return processCalculationGPU( std::move( calcNode ), feedback );
172
  }
173
#endif
174

    
175
  //open output dataset for writing
176
  GDALDriverH outputDriver = openOutputDriver();
177
  if ( !outputDriver )
178
  {
179
    mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
180
    return QgsRasterCalculator::Result::CreateOutputError;
181
  }
182

    
183
  gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
184
  if ( !outputDataset )
185
  {
186
    mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
187
    return QgsRasterCalculator::Result::CreateOutputError;
188
  }
189

    
190
  GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLocal8Bit().data() );
191
  GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
192

    
193
  GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
194

    
195
  // Take the fast route (process one line at a time) if we can
196
  if ( !requiresMatrix )
197
  {
198
    // Map of raster names -> blocks
199
    std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
200
    std::map<QString, QgsRasterCalculatorEntry> uniqueRasterEntries;
201
    const QList<const QgsRasterCalcNode *> rasterRefNodes = calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef );
202
    for ( const QgsRasterCalcNode *r : rasterRefNodes )
203
    {
204
      QString layerRef( r->toString().remove( 0, 1 ) );
205
      layerRef.chop( 1 );
206
      if ( !inputBlocks.count( layerRef ) )
207
      {
208
        for ( const QgsRasterCalculatorEntry &ref : std::as_const( mRasterEntries ) )
209
        {
210
          if ( ref.ref == layerRef )
211
          {
212
            uniqueRasterEntries[layerRef] = ref;
213
            inputBlocks[layerRef] = std::make_unique<QgsRasterBlock>();
214
          }
215
        }
216
      }
217
    }
218

    
219
    //read / write line by line
220
    QMap<QString, QgsRasterBlock *> _rasterData;
221
    // Cast to float
222
    std::vector<float> castedResult( static_cast<size_t>( mNumOutputColumns ), 0 );
223
    auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
224
    for ( size_t row = 0; row < static_cast<size_t>( mNumOutputRows ); ++row )
225
    {
226
      if ( feedback )
227
      {
228
        feedback->setProgress( 100.0 * static_cast<double>( row ) / mNumOutputRows );
229
      }
230

    
231
      if ( feedback && feedback->isCanceled() )
232
      {
233
        break;
234
      }
235

    
236
      // Calculates the rect for a single row read
237
      QgsRectangle rect( mOutputRectangle );
238
      rect.setYMaximum( rect.yMaximum() - rowHeight * row );
239
      rect.setYMinimum( rect.yMaximum() - rowHeight );
240

    
241
      // Read rows into input blocks
242
      for ( auto &layerRef : inputBlocks )
243
      {
244
        QgsRasterCalculatorEntry ref = uniqueRasterEntries[layerRef.first];
245
        if ( ref.raster->crs() != mOutputCrs )
246
        {
247
          QgsRasterProjector proj;
248
          proj.setCrs( ref.raster->crs(), mOutputCrs, mTransformContext );
249
          proj.setInput( ref.raster->dataProvider() );
250
          proj.setPrecision( QgsRasterProjector::Exact );
251
          layerRef.second.reset( proj.block( ref.bandNumber, rect, mNumOutputColumns, 1 ) );
252
        }
253
        else
254
        {
255
          layerRef.second.reset( ref.raster->dataProvider()->block( ref.bandNumber, rect, mNumOutputColumns, 1 ) );
256
        }
257
      }
258

    
259
      // 1 row X mNumOutputColumns matrix
260
      QgsRasterMatrix resultMatrix( mNumOutputColumns, 1, nullptr, mNoDataValue );
261

    
262
      _rasterData.clear();
263
      for ( const auto &layerRef : inputBlocks )
264
      {
265
        _rasterData.insert( layerRef.first, layerRef.second.get() );
266
      }
267

    
268
      if ( calcNode->calculate( _rasterData, resultMatrix, 0 ) )
269
      {
270
        std::copy( resultMatrix.data(), resultMatrix.data() + mNumOutputColumns, castedResult.begin() );
271
        if ( GDALRasterIO( outputRasterBand, GF_Write, 0, row, mNumOutputColumns, 1, castedResult.data(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
272
        {
273
          QgsDebugError( QStringLiteral( "RasterIO error!" ) );
274
        }
275
      }
276
      else
277
      {
278
        //delete the dataset without closing (because it is faster)
279
        gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
280
        return QgsRasterCalculator::Result::CalculationError;
281
      }
282
    }
283

    
284
    if ( feedback )
285
    {
286
      feedback->setProgress( 100.0 );
287
    }
288
  }
289
  else // Original code (memory inefficient route)
290
  {
291
    QMap<QString, QgsRasterBlock *> inputBlocks;
292
    QVector<QgsRasterCalculatorEntry>::const_iterator it = mRasterEntries.constBegin();
293
    for ( ; it != mRasterEntries.constEnd(); ++it )
294
    {
295
      std::unique_ptr<QgsRasterBlock> block;
296
      // if crs transform needed
297
      if ( it->raster->crs() != mOutputCrs )
298
      {
299
        QgsRasterProjector proj;
300
        proj.setCrs( it->raster->crs(), mOutputCrs, it->raster->transformContext() );
301
        proj.setInput( it->raster->dataProvider() );
302
        proj.setPrecision( QgsRasterProjector::Exact );
303

    
304
        QgsRasterBlockFeedback *rasterBlockFeedback = new QgsRasterBlockFeedback();
305
        QObject::connect( feedback, &QgsFeedback::canceled, rasterBlockFeedback, &QgsRasterBlockFeedback::cancel );
306
        block.reset( proj.block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows, rasterBlockFeedback ) );
307
        if ( rasterBlockFeedback->isCanceled() )
308
        {
309
          qDeleteAll( inputBlocks );
310
          return QgsRasterCalculator::Result::Canceled;
311
        }
312
      }
313
      else
314
      {
315
        block.reset( it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows ) );
316
      }
317
      if ( block->isEmpty() )
318
      {
319
        mLastError = QObject::tr( "Could not allocate required memory for %1" ).arg( it->ref );
320
        qDeleteAll( inputBlocks );
321
        return QgsRasterCalculator::Result::MemoryError;
322
      }
323
      inputBlocks.insert( it->ref, block.release() );
324
    }
325

    
326
    QgsRasterMatrix resultMatrix;
327
    resultMatrix.setNodataValue( mNoDataValue );
328

    
329
    //read / write line by line
330
    for ( int i = 0; i < mNumOutputRows; ++i )
331
    {
332
      if ( feedback )
333
      {
334
        feedback->setProgress( 100.0 * static_cast<double>( i ) / mNumOutputRows );
335
      }
336

    
337
      if ( feedback && feedback->isCanceled() )
338
      {
339
        break;
340
      }
341

    
342
      if ( calcNode->calculate( inputBlocks, resultMatrix, i ) )
343
      {
344
        bool resultIsNumber = resultMatrix.isNumber();
345
        float *calcData = new float[mNumOutputColumns];
346

    
347
        for ( int j = 0; j < mNumOutputColumns; ++j )
348
        {
349
          calcData[j] = ( float ) ( resultIsNumber ? resultMatrix.number() : resultMatrix.data()[j] );
350
        }
351

    
352
        //write scanline to the dataset
353
        if ( GDALRasterIO( outputRasterBand, GF_Write, 0, i, mNumOutputColumns, 1, calcData, mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
354
        {
355
          QgsDebugError( QStringLiteral( "RasterIO error!" ) );
356
        }
357

    
358
        delete[] calcData;
359
      }
360
      else
361
      {
362
        qDeleteAll( inputBlocks );
363
        inputBlocks.clear();
364
        gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
365
        return QgsRasterCalculator::Result::CalculationError;
366
      }
367
    }
368

    
369
    if ( feedback )
370
    {
371
      feedback->setProgress( 100.0 );
372
    }
373

    
374
    //close datasets and release memory
375
    calcNode.reset();
376
    qDeleteAll( inputBlocks );
377
    inputBlocks.clear();
378
  }
379

    
380
  if ( feedback && feedback->isCanceled() )
381
  {
382
    //delete the dataset without closing (because it is faster)
383
    gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
384
    return QgsRasterCalculator::Result::Canceled;
385
  }
386

    
387
  GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
388

    
389
  return QgsRasterCalculator::Result::Success;
390
}
391

    
392
#ifdef HAVE_OPENCL
393
QgsRasterCalculator::Result QgsRasterCalculator::processCalculationGPU( std::unique_ptr<QgsRasterCalcNode> calcNode, QgsFeedback *feedback )
394
{
395
  QString cExpression( calcNode->toString( true ) );
396

    
397
  QList<const QgsRasterCalcNode *> nodeList( calcNode->findNodes( QgsRasterCalcNode::Type::tRasterRef ) );
398
  QSet<QString> capturedTexts;
399
  for ( const auto &r : std::as_const( nodeList ) )
400
  {
401
    QString s( r->toString().remove( 0, 1 ) );
402
    s.chop( 1 );
403
    capturedTexts.insert( s );
404
  }
405

    
406
  // Extract all references
407
  struct LayerRef
408
  {
409
      QString name;
410
      int band;
411
      QgsRasterLayer *layer = nullptr;
412
      QString varName;
413
      QString typeName;
414
      size_t index;
415
      size_t bufferSize;
416
      size_t dataSize;
417
  };
418

    
419
  // Collects all layers, band, name, varName and size information
420
  std::vector<LayerRef> inputRefs;
421
  size_t refCounter = 0;
422
  for ( const auto &r : capturedTexts )
423
  {
424
    if ( r.startsWith( '"' ) )
425
      continue;
426
    QStringList parts( r.split( '@' ) );
427
    if ( parts.count() != 2 )
428
      continue;
429
    bool ok = false;
430
    LayerRef entry;
431
    entry.name = r;
432
    entry.band = parts[1].toInt( &ok );
433
    for ( const auto &ref : std::as_const( mRasterEntries ) )
434
    {
435
      if ( ref.ref == entry.name )
436
        entry.layer = ref.raster;
437
    }
438
    if ( !( entry.layer && entry.layer->dataProvider() && ok ) )
439
      continue;
440
    entry.dataSize = entry.layer->dataProvider()->dataTypeSize( entry.band );
441
    switch ( entry.layer->dataProvider()->dataType( entry.band ) )
442
    {
443
      case Qgis::DataType::Byte:
444
        entry.typeName = QStringLiteral( "unsigned char" );
445
        break;
446
      case Qgis::DataType::Int8:
447
        entry.typeName = QStringLiteral( "char" );
448
        break;
449
      case Qgis::DataType::UInt16:
450
        entry.typeName = QStringLiteral( "unsigned short" );
451
        break;
452
      case Qgis::DataType::Int16:
453
        entry.typeName = QStringLiteral( "short" );
454
        break;
455
      case Qgis::DataType::UInt32:
456
        entry.typeName = QStringLiteral( "unsigned int" );
457
        break;
458
      case Qgis::DataType::Int32:
459
        entry.typeName = QStringLiteral( "int" );
460
        break;
461
      case Qgis::DataType::Float32:
462
        entry.typeName = QStringLiteral( "float" );
463
        break;
464
      // FIXME: not sure all OpenCL implementations support double
465
      //        maybe safer to fall back to the CPU implementation
466
      //        after a compatibility check
467
      case Qgis::DataType::Float64:
468
        entry.typeName = QStringLiteral( "double" );
469
        break;
470
      case Qgis::DataType::CInt16:
471
      case Qgis::DataType::CInt32:
472
      case Qgis::DataType::CFloat32:
473
      case Qgis::DataType::CFloat64:
474
      case Qgis::DataType::ARGB32:
475
      case Qgis::DataType::ARGB32_Premultiplied:
476
      case Qgis::DataType::UnknownDataType:
477
        return QgsRasterCalculator::Result::BandError;
478
    }
479
    entry.bufferSize = entry.dataSize * mNumOutputColumns;
480
    entry.index = refCounter;
481
    entry.varName = QStringLiteral( "input_raster_%1_band_%2" )
482
                      .arg( refCounter++ )
483
                      .arg( entry.band );
484
    inputRefs.push_back( entry );
485
  }
486

    
487
  // May throw an openCL exception
488
  try
489
  {
490
    // Prepare context and queue
491
    cl::Context ctx( QgsOpenClUtils::context() );
492
    cl::CommandQueue queue( QgsOpenClUtils::commandQueue() );
493

    
494
    // Create the C expression
495
    std::vector<cl::Buffer> inputBuffers;
496
    inputBuffers.reserve( inputRefs.size() );
497
    QStringList inputArgs;
498
    for ( const auto &ref : inputRefs )
499
    {
500
      cExpression.replace( QStringLiteral( "\"%1\"" ).arg( ref.name ), QStringLiteral( "%1[i]" ).arg( ref.varName ) );
501
      inputArgs.append( QStringLiteral( "__global %1 *%2" )
502
                          .arg( ref.typeName, ref.varName ) );
503
      inputBuffers.push_back( cl::Buffer( ctx, CL_MEM_READ_ONLY, ref.bufferSize, nullptr, nullptr ) );
504
    }
505

    
506
    //qDebug() << cExpression;
507

    
508
    // Create the program
509
    QString programTemplate( R"CL(
510
    // Inputs:
511
    ##INPUT_DESC##
512
    // Expression: ##EXPRESSION_ORIGINAL##
513
    __kernel void rasterCalculator( ##INPUT##
514
                              __global float *resultLine
515
                            )
516
    {
517
      // Get the index of the current element
518
      const int i = get_global_id(0);
519
      // Check for nodata in input
520
      if ( ##INPUT_NODATA_CHECK## )
521
        resultLine[i] = -FLT_MAX;
522
      // Expression
523
      else
524
        resultLine[i] = ##EXPRESSION##;
525
    }
526
    )CL" );
527

    
528
    QStringList inputDesc;
529
    QStringList inputNoDataCheck;
530
    for ( const auto &ref : inputRefs )
531
    {
532
      inputDesc.append( QStringLiteral( "  // %1 = %2" ).arg( ref.varName, ref.name ) );
533
      if ( ref.layer->dataProvider()->sourceHasNoDataValue( ref.band ) )
534
      {
535
        inputNoDataCheck.append( QStringLiteral( "(float) %1[i] == (float) %2" ).arg( ref.varName, QString::number( ref.layer->dataProvider()->sourceNoDataValue( ref.band ), 'g', 10 ) ) );
536
      }
537
    }
538

    
539
    programTemplate = programTemplate.replace( QLatin1String( "##INPUT_NODATA_CHECK##" ), inputNoDataCheck.isEmpty() ? QStringLiteral( "false" ) : inputNoDataCheck.join( QLatin1String( " || " ) ) );
540
    programTemplate = programTemplate.replace( QLatin1String( "##INPUT_DESC##" ), inputDesc.join( '\n' ) );
541
    programTemplate = programTemplate.replace( QLatin1String( "##INPUT##" ), !inputArgs.isEmpty() ? ( inputArgs.join( ',' ).append( ',' ) ) : QChar( ' ' ) );
542
    programTemplate = programTemplate.replace( QLatin1String( "##EXPRESSION##" ), cExpression );
543
    programTemplate = programTemplate.replace( QLatin1String( "##EXPRESSION_ORIGINAL##" ), calcNode->toString() );
544

    
545
    // qDebug() << programTemplate;
546

    
547
    // Create a program from the kernel source
548
    cl::Program program( QgsOpenClUtils::buildProgram( programTemplate, QgsOpenClUtils::ExceptionBehavior::Throw ) );
549

    
550
    // Create the buffers, output is float32 (4 bytes)
551
    // We assume size of float = 4 because that's the size used by OpenCL and IEEE 754
552
    Q_ASSERT( sizeof( float ) == 4 );
553
    std::size_t resultBufferSize( 4 * static_cast<size_t>( mNumOutputColumns ) );
554
    cl::Buffer resultLineBuffer( ctx, CL_MEM_WRITE_ONLY, resultBufferSize, nullptr, nullptr );
555

    
556
    auto kernel = cl::Kernel( program, "rasterCalculator" );
557

    
558
    for ( unsigned int i = 0; i < inputBuffers.size(); i++ )
559
    {
560
      kernel.setArg( i, inputBuffers.at( i ) );
561
    }
562
    kernel.setArg( static_cast<unsigned int>( inputBuffers.size() ), resultLineBuffer );
563

    
564
    QgsOpenClUtils::CPLAllocator<float> resultLine( static_cast<size_t>( mNumOutputColumns ) );
565

    
566
    //open output dataset for writing
567
    GDALDriverH outputDriver = openOutputDriver();
568
    if ( !outputDriver )
569
    {
570
      mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
571
      return QgsRasterCalculator::Result::CreateOutputError;
572
    }
573

    
574
    gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
575
    if ( !outputDataset )
576
    {
577
      mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
578
      return QgsRasterCalculator::Result::CreateOutputError;
579
    }
580

    
581
    GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt( Qgis::CrsWktVariant::PreferredGdal ).toLocal8Bit().data() );
582

    
583

    
584
    GDALRasterBandH outputRasterBand = GDALGetRasterBand( outputDataset.get(), 1 );
585
    if ( !outputRasterBand )
586
      return QgsRasterCalculator::Result::BandError;
587

    
588
    GDALSetRasterNoDataValue( outputRasterBand, mNoDataValue );
589

    
590
    // Input block (buffer)
591
    std::unique_ptr<QgsRasterBlock> block;
592

    
593
    // Run kernel on all scanlines
594
    auto rowHeight = mOutputRectangle.height() / mNumOutputRows;
595
    for ( int line = 0; line < mNumOutputRows; line++ )
596
    {
597
      if ( feedback && feedback->isCanceled() )
598
      {
599
        break;
600
      }
601

    
602
      if ( feedback )
603
      {
604
        feedback->setProgress( 100.0 * static_cast<double>( line ) / mNumOutputRows );
605
      }
606

    
607
      // Read lines from rasters into the buffers
608
      for ( const auto &ref : inputRefs )
609
      {
610
        // Read one row
611
        QgsRectangle rect( mOutputRectangle );
612
        rect.setYMaximum( rect.yMaximum() - rowHeight * line );
613
        rect.setYMinimum( rect.yMaximum() - rowHeight );
614

    
615
        // TODO: check if this is too slow
616
        // if crs transform needed
617
        if ( ref.layer->crs() != mOutputCrs )
618
        {
619
          QgsRasterProjector proj;
620
          proj.setCrs( ref.layer->crs(), mOutputCrs, ref.layer->transformContext() );
621
          proj.setInput( ref.layer->dataProvider() );
622
          proj.setPrecision( QgsRasterProjector::Exact );
623
          block.reset( proj.block( ref.band, rect, mNumOutputColumns, 1 ) );
624
        }
625
        else
626
        {
627
          block.reset( ref.layer->dataProvider()->block( ref.band, rect, mNumOutputColumns, 1 ) );
628
        }
629

    
630
        //for ( int i = 0; i < mNumOutputColumns; i++ )
631
        //  qDebug() << "Input: " << line << i << ref.varName << " = " << block->value( 0, i );
632
        //qDebug() << "Writing buffer " << ref.index;
633

    
634
        Q_ASSERT( ref.bufferSize == static_cast<size_t>( block->data().size() ) );
635
        queue.enqueueWriteBuffer( inputBuffers[ref.index], CL_TRUE, 0, ref.bufferSize, block->bits() );
636
      }
637
      // Run the kernel
638
      queue.enqueueNDRangeKernel(
639
        kernel,
640
        0,
641
        cl::NDRange( mNumOutputColumns )
642
      );
643

    
644
      // Write the result
645
      queue.enqueueReadBuffer( resultLineBuffer, CL_TRUE, 0, resultBufferSize, resultLine.get() );
646

    
647
      //for ( int i = 0; i < mNumOutputColumns; i++ )
648
      //  qDebug() << "Output: " << line << i << " = " << resultLine[i];
649

    
650
      if ( GDALRasterIO( outputRasterBand, GF_Write, 0, line, mNumOutputColumns, 1, resultLine.get(), mNumOutputColumns, 1, GDT_Float32, 0, 0 ) != CE_None )
651
      {
652
        return QgsRasterCalculator::Result::CreateOutputError;
653
      }
654
    }
655

    
656
    if ( feedback && feedback->isCanceled() )
657
    {
658
      //delete the dataset without closing (because it is faster)
659
      gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
660
      return QgsRasterCalculator::Result::Canceled;
661
    }
662

    
663
    inputBuffers.clear();
664

    
665
    GDALComputeRasterStatistics( outputRasterBand, true, nullptr, nullptr, nullptr, nullptr, GdalProgressCallback, feedback );
666
  }
667
  catch ( cl::Error &e )
668
  {
669
    mLastError = e.what();
670
    return QgsRasterCalculator::Result::CreateOutputError;
671
  }
672

    
673
  return QgsRasterCalculator::Result::Success;
674
}
675
#endif
676

    
677
GDALDriverH QgsRasterCalculator::openOutputDriver()
678
{
679
  //open driver
680
  GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
681

    
682
  if ( !outputDriver )
683
  {
684
    return outputDriver; //return nullptr, driver does not exist
685
  }
686

    
687
  if ( !QgsGdalUtils::supportsRasterCreate( outputDriver ) )
688
  {
689
    return nullptr; //driver exist, but it does not support the create operation
690
  }
691

    
692
  return outputDriver;
693
}
694

    
695
gdal::dataset_unique_ptr QgsRasterCalculator::openOutputFile( GDALDriverH outputDriver )
696
{
697
  //open output file
698
  char **papszOptions = QgsGdalUtils::papszFromStringList( mCreationOptions );
699
  gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), mNumOutputColumns, mNumOutputRows, 1, GDT_Float32, papszOptions ) );
700
  CSLDestroy( papszOptions );
701
  if ( !outputDataset )
702
  {
703
    return nullptr;
704
  }
705

    
706
  //assign georef information
707
  double geotransform[6];
708
  outputGeoTransform( geotransform );
709
  GDALSetGeoTransform( outputDataset.get(), geotransform );
710

    
711
  return outputDataset;
712
}
713

    
714
void QgsRasterCalculator::outputGeoTransform( double *transform ) const
715
{
716
  transform[0] = mOutputRectangle.xMinimum();
717
  transform[1] = mOutputRectangle.width() / mNumOutputColumns;
718
  transform[2] = 0;
719
  transform[3] = mOutputRectangle.yMaximum();
720
  transform[4] = 0;
721
  transform[5] = -mOutputRectangle.height() / mNumOutputRows;
722
}
723

    
724
QString QgsRasterCalculator::lastError() const
725
{
726
  return mLastError;
727
}
728

    
729
QVector<QgsRasterCalculatorEntry> QgsRasterCalculatorEntry::rasterEntries()
730
{
731
  QVector<QgsRasterCalculatorEntry> availableEntries;
732
  const QMap<QString, QgsMapLayer *> &layers = QgsProject::instance()->mapLayers();
733

    
734
  auto uniqueRasterBandIdentifier = [&]( QgsRasterCalculatorEntry &entry ) -> bool {
735
    unsigned int i( 1 );
736
    entry.ref = QStringLiteral( "%1@%2" ).arg( entry.raster->name() ).arg( entry.bandNumber );
737
    while ( true )
738
    {
739
      bool unique( true );
740
      for ( const auto &ref : std::as_const( availableEntries ) )
741
      {
742
        // Safety belt
743
        if ( !( entry.raster && ref.raster ) )
744
          continue;
745
        // Check if is another band of the same raster
746
        if ( ref.raster->publicSource() == entry.raster->publicSource() )
747
        {
748
          if ( ref.bandNumber != entry.bandNumber )
749
          {
750
            continue;
751
          }
752
          else // a layer with the same data source was already added to the list
753
          {
754
            return false;
755
          }
756
        }
757
        // If same name but different source
758
        if ( ref.ref == entry.ref )
759
        {
760
          unique = false;
761
          entry.ref = QStringLiteral( "%1_%2@%3" ).arg( entry.raster->name() ).arg( i++ ).arg( entry.bandNumber );
762
        }
763
      }
764
      if ( unique )
765
        return true;
766
    }
767
  };
768

    
769
  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
770
  for ( ; layerIt != layers.constEnd(); ++layerIt )
771
  {
772
    QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layerIt.value() );
773
    if ( rlayer && rlayer->dataProvider() && ( rlayer->dataProvider()->capabilities() & Qgis::RasterInterfaceCapability::Size ) )
774
    {
775
      //get number of bands
776
      for ( int i = 0; i < rlayer->bandCount(); ++i )
777
      {
778
        QgsRasterCalculatorEntry entry;
779
        entry.raster = rlayer;
780
        entry.bandNumber = i + 1;
781
        if ( !uniqueRasterBandIdentifier( entry ) )
782
          break;
783
        availableEntries.push_back( entry );
784
      }
785
    }
786
  }
787
  return availableEntries;
788
}