Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (16 KB)

1
/***************************************************************************
2
    qgsrastercalcnode.cpp
3
    ---------------------
4
    begin                : October 2010
5
    copyright            : (C) 2010 by Marco Hugentobler
6
    email                : marco dot hugentobler at sourcepole dot ch
7
 ***************************************************************************
8
 *                                                                         *
9
 *   This program is free software; you can redistribute it and/or modify  *
10
 *   it under the terms of the GNU General Public License as published by  *
11
 *   the Free Software Foundation; either version 2 of the License, or     *
12
 *   (at your option) any later version.                                   *
13
 *                                                                         *
14
 ***************************************************************************/
15
#include "qgsrastercalcnode.h"
16
#include "qgsrasterblock.h"
17
#include "qgsrastermatrix.h"
18

    
19
QgsRasterCalcNode::QgsRasterCalcNode( double number )
20
  : mNumber( number )
21
{
22
}
23

    
24
QgsRasterCalcNode::QgsRasterCalcNode( QgsRasterMatrix *matrix )
25
  : mType( tMatrix )
26
  , mMatrix( matrix )
27
{
28
}
29

    
30
QgsRasterCalcNode::QgsRasterCalcNode( Operator op, QgsRasterCalcNode *left, QgsRasterCalcNode *right )
31
  : mType( tOperator )
32
  , mLeft( left )
33
  , mRight( right )
34
  , mOperator( op )
35
{
36
}
37

    
38
QgsRasterCalcNode::QgsRasterCalcNode( QString functionName, QVector<QgsRasterCalcNode *> functionArgs )
39
  : mType( tFunction )
40
  , mFunctionName( functionName )
41
  , mFunctionArgs( functionArgs )
42
{
43
}
44

    
45
QgsRasterCalcNode::QgsRasterCalcNode( const QString &rasterName )
46
  : mType( tRasterRef )
47
  , mRasterName( rasterName )
48
{
49
  if ( mRasterName.startsWith( '"' ) && mRasterName.endsWith( '"' ) )
50
    mRasterName = mRasterName.mid( 1, mRasterName.size() - 2 );
51
}
52

    
53
QgsRasterCalcNode::~QgsRasterCalcNode()
54
{
55
  delete mLeft;
56
  delete mRight;
57
  for ( int i = 0; i < mFunctionArgs.size(); ++i )
58
  {
59
    if ( mFunctionArgs.at( i ) )
60
      delete mFunctionArgs.at( i );
61
  }
62
}
63

    
64
bool QgsRasterCalcNode::calculate( QMap<QString, QgsRasterBlock *> &rasterData, QgsRasterMatrix &result, int row ) const
65
{
66
  //if type is raster ref: return a copy of the corresponding matrix
67

    
68
  //if type is operator, call the proper matrix operations
69
  if ( mType == tRasterRef )
70
  {
71
    const QMap<QString, QgsRasterBlock *>::iterator it = rasterData.find( mRasterName );
72
    if ( it == rasterData.end() )
73
    {
74
      QgsDebugError( QStringLiteral( "Error: could not find raster data for \"%1\"" ).arg( mRasterName ) );
75
      return false;
76
    }
77

    
78
    const int nRows = ( row >= 0 ? 1 : ( *it )->height() );
79
    const int startRow = ( row >= 0 ? row : 0 );
80
    const int endRow = startRow + nRows;
81
    const int nCols = ( *it )->width();
82
    const int nEntries = nCols * nRows;
83
    double *data = new double[nEntries];
84

    
85
    //convert input raster values to double, also convert input no data to result no data
86

    
87
    int outRow = 0;
88
    bool isNoData = false;
89
    for ( int dataRow = startRow; dataRow < endRow; ++dataRow, ++outRow )
90
    {
91
      for ( int dataCol = 0; dataCol < nCols; ++dataCol )
92
      {
93
        const double value = ( *it )->valueAndNoData( dataRow, dataCol, isNoData );
94
        data[dataCol + nCols * outRow] = isNoData ? result.nodataValue() : value;
95
      }
96
    }
97
    result.setData( nCols, nRows, data, result.nodataValue() );
98
    return true;
99
  }
100
  else if ( mType == tOperator )
101
  {
102
    QgsRasterMatrix leftMatrix( result.nColumns(), result.nRows(), nullptr, result.nodataValue() );
103
    QgsRasterMatrix rightMatrix( result.nColumns(), result.nRows(), nullptr, result.nodataValue() );
104

    
105
    if ( !mLeft || !mLeft->calculate( rasterData, leftMatrix, row ) )
106
    {
107
      return false;
108
    }
109
    if ( mRight && !mRight->calculate( rasterData, rightMatrix, row ) )
110
    {
111
      return false;
112
    }
113

    
114
    switch ( mOperator )
115
    {
116
      case opPLUS:
117
        leftMatrix.add( rightMatrix );
118
        break;
119
      case opMINUS:
120
        leftMatrix.subtract( rightMatrix );
121
        break;
122
      case opMUL:
123
        leftMatrix.multiply( rightMatrix );
124
        break;
125
      case opDIV:
126
        leftMatrix.divide( rightMatrix );
127
        break;
128
      case opPOW:
129
        leftMatrix.power( rightMatrix );
130
        break;
131
      case opEQ:
132
        leftMatrix.equal( rightMatrix );
133
        break;
134
      case opNE:
135
        leftMatrix.notEqual( rightMatrix );
136
        break;
137
      case opGT:
138
        leftMatrix.greaterThan( rightMatrix );
139
        break;
140
      case opLT:
141
        leftMatrix.lesserThan( rightMatrix );
142
        break;
143
      case opGE:
144
        leftMatrix.greaterEqual( rightMatrix );
145
        break;
146
      case opLE:
147
        leftMatrix.lesserEqual( rightMatrix );
148
        break;
149
      case opAND:
150
        leftMatrix.logicalAnd( rightMatrix );
151
        break;
152
      case opOR:
153
        leftMatrix.logicalOr( rightMatrix );
154
        break;
155
      case opMIN:
156
        leftMatrix.min( rightMatrix );
157
        break;
158
      case opMAX:
159
        leftMatrix.max( rightMatrix );
160
        break;
161
      case opSQRT:
162
        leftMatrix.squareRoot();
163
        break;
164
      case opSIN:
165
        leftMatrix.sinus();
166
        break;
167
      case opCOS:
168
        leftMatrix.cosinus();
169
        break;
170
      case opTAN:
171
        leftMatrix.tangens();
172
        break;
173
      case opASIN:
174
        leftMatrix.asinus();
175
        break;
176
      case opACOS:
177
        leftMatrix.acosinus();
178
        break;
179
      case opATAN:
180
        leftMatrix.atangens();
181
        break;
182
      case opSIGN:
183
        leftMatrix.changeSign();
184
        break;
185
      case opLOG:
186
        leftMatrix.log();
187
        break;
188
      case opLOG10:
189
        leftMatrix.log10();
190
        break;
191
      case opABS:
192
        leftMatrix.absoluteValue();
193
        break;
194
      default:
195
        return false;
196
    }
197
    const int newNColumns = leftMatrix.nColumns();
198
    const int newNRows = leftMatrix.nRows();
199
    result.setData( newNColumns, newNRows, leftMatrix.takeData(), leftMatrix.nodataValue() );
200
    return true;
201
  }
202
  else if ( mType == tNumber )
203
  {
204
    const size_t nEntries = static_cast<size_t>( result.nColumns() * result.nRows() );
205
    double *data = new double[nEntries];
206
    std::fill( data, data + nEntries, mNumber );
207
    result.setData( result.nColumns(), 1, data, result.nodataValue() );
208

    
209
    return true;
210
  }
211
  else if ( mType == tMatrix )
212
  {
213
    const int nEntries = mMatrix->nColumns() * mMatrix->nRows();
214
    double *data = new double[nEntries];
215
    for ( int i = 0; i < nEntries; ++i )
216
    {
217
      data[i] = mMatrix->data()[i] == mMatrix->nodataValue() ? result.nodataValue() : mMatrix->data()[i];
218
    }
219
    result.setData( mMatrix->nColumns(), mMatrix->nRows(), data, result.nodataValue() );
220
    return true;
221
  }
222
  else if ( mType == tFunction )
223
  {
224
    std::vector<std::unique_ptr<QgsRasterMatrix>> matrixContainer;
225
    for ( int i = 0; i < mFunctionArgs.size(); ++i )
226
    {
227
      std::unique_ptr<QgsRasterMatrix> singleMatrix = std::make_unique<QgsRasterMatrix>( result.nColumns(), result.nRows(), nullptr, result.nodataValue() );
228
      if ( !mFunctionArgs.at( i ) || !mFunctionArgs.at( i )->calculate( rasterData, *singleMatrix, row ) )
229
      {
230
        return false;
231
      }
232
      matrixContainer.emplace_back( std::move( singleMatrix ) );
233
    }
234
    evaluateFunction( matrixContainer, result );
235
    return true;
236
  }
237
  return false;
238
}
239

    
240
QString QgsRasterCalcNode::toString( bool cStyle ) const
241
{
242
  QString result;
243
  QString left;
244
  QString right;
245
  if ( mLeft )
246
    left = mLeft->toString( cStyle );
247
  if ( mRight )
248
    right = mRight->toString( cStyle );
249

    
250
  switch ( mType )
251
  {
252
    case tOperator:
253
      switch ( mOperator )
254
      {
255
        case opPLUS:
256
          result = QStringLiteral( "( %1 + %2 )" ).arg( left ).arg( right );
257
          break;
258
        case opMINUS:
259
          result = QStringLiteral( "( %1 - %2 )" ).arg( left ).arg( right );
260
          break;
261
        case opSIGN:
262
          result = QStringLiteral( "-%1" ).arg( left );
263
          break;
264
        case opMUL:
265
          result = QStringLiteral( "%1 * %2" ).arg( left ).arg( right );
266
          break;
267
        case opDIV:
268
          result = QStringLiteral( "%1 / %2" ).arg( left ).arg( right );
269
          break;
270
        case opPOW:
271
          if ( cStyle )
272
            result = QStringLiteral( "pow( %1, %2 )" ).arg( left ).arg( right );
273
          else
274
            result = QStringLiteral( "%1^%2" ).arg( left ).arg( right );
275
          break;
276
        case opEQ:
277
          if ( cStyle )
278
            result = QStringLiteral( "( float ) ( %1 == %2 )" ).arg( left ).arg( right );
279
          else
280
            result = QStringLiteral( "%1 = %2" ).arg( left ).arg( right );
281
          break;
282
        case opNE:
283
          if ( cStyle )
284
            result = QStringLiteral( "( float ) ( %1 != %2 )" ).arg( left ).arg( right );
285
          else
286
            result = QStringLiteral( "%1 != %2" ).arg( left ).arg( right );
287
          break;
288
        case opGT:
289
          if ( cStyle )
290
            result = QStringLiteral( "( float ) ( %1 > %2 )" ).arg( left ).arg( right );
291
          else
292
            result = QStringLiteral( "%1 > %2" ).arg( left ).arg( right );
293
          break;
294
        case opLT:
295
          if ( cStyle )
296
            result = QStringLiteral( "( float ) ( %1 < %2 )" ).arg( left ).arg( right );
297
          else
298
            result = QStringLiteral( "%1 < %2" ).arg( left ).arg( right );
299
          break;
300
        case opGE:
301
          if ( cStyle )
302
            result = QStringLiteral( "( float ) ( %1 >= %2 )" ).arg( left ).arg( right );
303
          else
304
            result = QStringLiteral( "%1 >= %2" ).arg( left ).arg( right );
305
          break;
306
        case opLE:
307
          if ( cStyle )
308
            result = QStringLiteral( "( float ) ( %1 <= %2 )" ).arg( left ).arg( right );
309
          else
310
            result = QStringLiteral( "%1 <= %2" ).arg( left ).arg( right );
311
          break;
312
        case opAND:
313
          if ( cStyle )
314
            result = QStringLiteral( "( float ) ( %1 && %2 )" ).arg( left ).arg( right );
315
          else
316
            result = QStringLiteral( "%1 AND %2" ).arg( left ).arg( right );
317
          break;
318
        case opOR:
319
          if ( cStyle )
320
            result = QStringLiteral( "( float ) ( %1 || %2 )" ).arg( left ).arg( right );
321
          else
322
            result = QStringLiteral( "%1 OR %2" ).arg( left ).arg( right );
323
          break;
324
        case opSQRT:
325
          result = QStringLiteral( "sqrt( %1 )" ).arg( left );
326
          break;
327
        case opSIN:
328
          result = QStringLiteral( "sin( %1 )" ).arg( left );
329
          break;
330
        case opCOS:
331
          result = QStringLiteral( "cos( %1 )" ).arg( left );
332
          break;
333
        case opTAN:
334
          result = QStringLiteral( "tan( %1 )" ).arg( left );
335
          break;
336
        case opASIN:
337
          result = QStringLiteral( "asin( %1 )" ).arg( left );
338
          break;
339
        case opACOS:
340
          result = QStringLiteral( "acos( %1 )" ).arg( left );
341
          break;
342
        case opATAN:
343
          result = QStringLiteral( "atan( %1 )" ).arg( left );
344
          break;
345
        case opLOG:
346
          result = QStringLiteral( "log( %1 )" ).arg( left );
347
          break;
348
        case opLOG10:
349
          result = QStringLiteral( "log10( %1 )" ).arg( left );
350
          break;
351
        case opABS:
352
          if ( cStyle )
353
            result = QStringLiteral( "fabs( %1 )" ).arg( left );
354
          else
355
            // Call the floating point version
356
            result = QStringLiteral( "abs( %1 )" ).arg( left );
357
          break;
358
        case opMIN:
359
          if ( cStyle )
360
            result = QStringLiteral( "min( ( float ) ( %1 ), ( float ) ( %2 ) )" ).arg( left ).arg( right );
361
          else
362
            result = QStringLiteral( "min( %1, %2 )" ).arg( left ).arg( right );
363
          break;
364
        case opMAX:
365
          if ( cStyle )
366
            result = QStringLiteral( "max( ( float ) ( %1 ), ( float ) ( %2 ) )" ).arg( left ).arg( right );
367
          else
368
            result = QStringLiteral( "max( %1, %2 )" ).arg( left ).arg( right );
369
          break;
370
        case opNONE:
371
          break;
372
      }
373
      break;
374
    case tRasterRef:
375
      if ( cStyle )
376
        result = QStringLiteral( "( float ) \"%1\"" ).arg( mRasterName );
377
      else
378
        result = QStringLiteral( "\"%1\"" ).arg( mRasterName );
379
      break;
380
    case tNumber:
381
      result = QString::number( mNumber );
382
      if ( cStyle )
383
      {
384
        result = QStringLiteral( "( float ) %1" ).arg( result );
385
      }
386
      break;
387
    case tMatrix:
388
      break;
389
    case tFunction:
390
      if ( mFunctionName == "if" )
391
      {
392
        const QString argOne = mFunctionArgs.at( 0 )->toString( cStyle );
393
        const QString argTwo = mFunctionArgs.at( 1 )->toString( cStyle );
394
        const QString argThree = mFunctionArgs.at( 2 )->toString( cStyle );
395
        if ( cStyle )
396
          result = QStringLiteral( " ( %1 ) ? ( %2 ) : ( %3 ) " ).arg( argOne, argTwo, argThree );
397
        else
398
          result = QStringLiteral( "if( %1 , %2 , %3 )" ).arg( argOne, argTwo, argThree );
399
      }
400
      break;
401
  }
402
  return result;
403
}
404

    
405
QList<const QgsRasterCalcNode *> QgsRasterCalcNode::findNodes( const QgsRasterCalcNode::Type type ) const
406
{
407
  QList<const QgsRasterCalcNode *> nodeList;
408
  if ( mType == type )
409
    nodeList.push_back( this );
410
  if ( mLeft )
411
    nodeList.append( mLeft->findNodes( type ) );
412
  if ( mRight )
413
    nodeList.append( mRight->findNodes( type ) );
414

    
415
  for ( QgsRasterCalcNode *node : mFunctionArgs )
416
    nodeList.append( node->findNodes( type ) );
417

    
418
  return nodeList;
419
}
420

    
421
QgsRasterCalcNode *QgsRasterCalcNode::parseRasterCalcString( const QString &str, QString &parserErrorMsg )
422
{
423
  extern QgsRasterCalcNode *localParseRasterCalcString( const QString &str, QString &parserErrorMsg );
424
  return localParseRasterCalcString( str, parserErrorMsg );
425
}
426

    
427
QStringList QgsRasterCalcNode::referencedLayerNames() const
428
{
429
  QStringList referencedRasters;
430

    
431
  QStringList rasterRef = this->cleanRasterReferences();
432
  for ( const auto &i : rasterRef )
433
  {
434
    if ( referencedRasters.contains( i.mid( 0, i.lastIndexOf( "@" ) ) ) )
435
      continue;
436
    referencedRasters << i.mid( 0, i.lastIndexOf( "@" ) );
437
  }
438

    
439
  return referencedRasters;
440
}
441

    
442
QStringList QgsRasterCalcNode::cleanRasterReferences() const
443
{
444
  QStringList rasterReferences;
445
  const QList<const QgsRasterCalcNode *> rasterRefNodes = this->findNodes( QgsRasterCalcNode::Type::tRasterRef );
446

    
447
  for ( const QgsRasterCalcNode *r : rasterRefNodes )
448
  {
449
    QString layerRef( r->toString() );
450
    if ( layerRef.at( 0 ) == QLatin1String( "\"" ) && layerRef.at( layerRef.size() - 1 ) == QLatin1String( "\"" ) )
451
    {
452
      layerRef.remove( 0, 1 );
453
      layerRef.chop( 1 );
454
    }
455
    layerRef.remove( QChar( '\\' ), Qt::CaseInsensitive );
456
    rasterReferences << layerRef;
457
  }
458

    
459
  return rasterReferences;
460
}
461

    
462
QgsRasterMatrix QgsRasterCalcNode::evaluateFunction( const std::vector<std::unique_ptr<QgsRasterMatrix>> &matrixVector, QgsRasterMatrix &result ) const
463
{
464
  if ( mFunctionName == "if" )
465
  {
466
    //scalar condition
467
    if ( matrixVector.at( 0 )->isNumber() )
468
    {
469
      result = ( matrixVector.at( 0 )->data() ? *matrixVector.at( 1 ) : *matrixVector.at( 2 ) );
470
      return result;
471
    }
472
    int nCols = matrixVector.at( 0 )->nColumns();
473
    int nRows = matrixVector.at( 0 )->nRows();
474
    int nEntries = nCols * nRows;
475
    std::unique_ptr<double[]> dataResult = std::make_unique<double[]>( nEntries );
476
    double *dataResultRawPtr = dataResult.get();
477

    
478
    double *condition = matrixVector.at( 0 )->data();
479
    double *firstOption = matrixVector.at( 1 )->data();
480
    double *secondOption = matrixVector.at( 2 )->data();
481

    
482
    bool isFirstOptionNumber = matrixVector.at( 1 )->isNumber();
483
    bool isSecondCOptionNumber = matrixVector.at( 2 )->isNumber();
484
    double noDataValueCondition = matrixVector.at( 0 )->nodataValue();
485

    
486
    for ( int i = 0; i < nEntries; ++i )
487
    {
488
      if ( condition[i] == noDataValueCondition )
489
      {
490
        dataResultRawPtr[i] = result.nodataValue();
491
        continue;
492
      }
493
      else if ( condition[i] != 0 )
494
      {
495
        dataResultRawPtr[i] = isFirstOptionNumber ? firstOption[0] : firstOption[i];
496
        continue;
497
      }
498
      dataResultRawPtr[i] = isSecondCOptionNumber ? secondOption[0] : secondOption[i];
499
    }
500

    
501
    result.setData( nCols, nRows, dataResult.release(), result.nodataValue() );
502
  }
503
  return result;
504
}