QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsrastercalcnode.cpp
Go to the documentation of this file.
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
20 : mNumber( number )
21{
22}
23
25 : mType( tMatrix )
26 , mMatrix( matrix )
27{
28
29}
30
32 : mType( tOperator )
33 , mLeft( left )
34 , mRight( right )
35 , mOperator( op )
36{
37}
38
39QgsRasterCalcNode::QgsRasterCalcNode( QString functionName, QVector <QgsRasterCalcNode *> functionArgs )
40 : mType( tFunction )
41 , mFunctionName( functionName )
42 , mFunctionArgs( functionArgs )
43{
44}
45
46QgsRasterCalcNode::QgsRasterCalcNode( const QString &rasterName )
47 : mType( tRasterRef )
48 , mRasterName( rasterName )
49{
50 if ( mRasterName.startsWith( '"' ) && mRasterName.endsWith( '"' ) )
51 mRasterName = mRasterName.mid( 1, mRasterName.size() - 2 );
52}
53
55{
56 delete mLeft;
57 delete mRight;
58 for ( int i = 0; i < mFunctionArgs.size(); ++i )
59 {
60 if ( mFunctionArgs.at( i ) )
61 delete mFunctionArgs.at( i );
62 }
63}
64
65bool QgsRasterCalcNode::calculate( QMap<QString, QgsRasterBlock * > &rasterData, QgsRasterMatrix &result, int row ) const
66{
67 //if type is raster ref: return a copy of the corresponding matrix
68
69 //if type is operator, call the proper matrix operations
70 if ( mType == tRasterRef )
71 {
72 const QMap<QString, QgsRasterBlock *>::iterator it = rasterData.find( mRasterName );
73 if ( it == rasterData.end() )
74 {
75 QgsDebugError( QStringLiteral( "Error: could not find raster data for \"%1\"" ).arg( mRasterName ) );
76 return false;
77 }
78
79 const int nRows = ( row >= 0 ? 1 : ( *it )->height() );
80 const int startRow = ( row >= 0 ? row : 0 );
81 const int endRow = startRow + nRows;
82 const int nCols = ( *it )->width();
83 const int nEntries = nCols * nRows;
84 double *data = new double[nEntries];
85
86 //convert input raster values to double, also convert input no data to result no data
87
88 int outRow = 0;
89 bool isNoData = false;
90 for ( int dataRow = startRow; dataRow < endRow ; ++dataRow, ++outRow )
91 {
92 for ( int dataCol = 0; dataCol < nCols; ++dataCol )
93 {
94 const double value = ( *it )->valueAndNoData( dataRow, dataCol, isNoData );
95 data[ dataCol + nCols * outRow] = isNoData ? result.nodataValue() : value;
96 }
97 }
98 result.setData( nCols, nRows, data, result.nodataValue() );
99 return true;
100 }
101 else if ( mType == tOperator )
102 {
103 QgsRasterMatrix leftMatrix( result.nColumns(), result.nRows(), nullptr, result.nodataValue() );
104 QgsRasterMatrix rightMatrix( result.nColumns(), result.nRows(), nullptr, result.nodataValue() );
105
106 if ( !mLeft || !mLeft->calculate( rasterData, leftMatrix, row ) )
107 {
108 return false;
109 }
110 if ( mRight && !mRight->calculate( rasterData, rightMatrix, row ) )
111 {
112 return false;
113 }
114
115 switch ( mOperator )
116 {
117 case opPLUS:
118 leftMatrix.add( rightMatrix );
119 break;
120 case opMINUS:
121 leftMatrix.subtract( rightMatrix );
122 break;
123 case opMUL:
124 leftMatrix.multiply( rightMatrix );
125 break;
126 case opDIV:
127 leftMatrix.divide( rightMatrix );
128 break;
129 case opPOW:
130 leftMatrix.power( rightMatrix );
131 break;
132 case opEQ:
133 leftMatrix.equal( rightMatrix );
134 break;
135 case opNE:
136 leftMatrix.notEqual( rightMatrix );
137 break;
138 case opGT:
139 leftMatrix.greaterThan( rightMatrix );
140 break;
141 case opLT:
142 leftMatrix.lesserThan( rightMatrix );
143 break;
144 case opGE:
145 leftMatrix.greaterEqual( rightMatrix );
146 break;
147 case opLE:
148 leftMatrix.lesserEqual( rightMatrix );
149 break;
150 case opAND:
151 leftMatrix.logicalAnd( rightMatrix );
152 break;
153 case opOR:
154 leftMatrix.logicalOr( rightMatrix );
155 break;
156 case opMIN:
157 leftMatrix.min( rightMatrix );
158 break;
159 case opMAX:
160 leftMatrix.max( rightMatrix );
161 break;
162 case opSQRT:
163 leftMatrix.squareRoot();
164 break;
165 case opSIN:
166 leftMatrix.sinus();
167 break;
168 case opCOS:
169 leftMatrix.cosinus();
170 break;
171 case opTAN:
172 leftMatrix.tangens();
173 break;
174 case opASIN:
175 leftMatrix.asinus();
176 break;
177 case opACOS:
178 leftMatrix.acosinus();
179 break;
180 case opATAN:
181 leftMatrix.atangens();
182 break;
183 case opSIGN:
184 leftMatrix.changeSign();
185 break;
186 case opLOG:
187 leftMatrix.log();
188 break;
189 case opLOG10:
190 leftMatrix.log10();
191 break;
192 case opABS:
193 leftMatrix.absoluteValue();
194 break;
195 default:
196 return false;
197 }
198 const int newNColumns = leftMatrix.nColumns();
199 const int newNRows = leftMatrix.nRows();
200 result.setData( newNColumns, newNRows, leftMatrix.takeData(), leftMatrix.nodataValue() );
201 return true;
202 }
203 else if ( mType == tNumber )
204 {
205 const size_t nEntries = static_cast<size_t>( result.nColumns() * result.nRows() );
206 double *data = new double[ nEntries ];
207 std::fill( data, data + nEntries, mNumber );
208 result.setData( result.nColumns(), 1, data, result.nodataValue() );
209
210 return true;
211 }
212 else if ( mType == tMatrix )
213 {
214 const int nEntries = mMatrix->nColumns() * mMatrix->nRows();
215 double *data = new double[nEntries];
216 for ( int i = 0; i < nEntries; ++i )
217 {
218 data[i] = mMatrix->data()[i] == mMatrix->nodataValue() ? result.nodataValue() : mMatrix->data()[i];
219 }
220 result.setData( mMatrix->nColumns(), mMatrix->nRows(), data, result.nodataValue() );
221 return true;
222 }
223 else if ( mType == tFunction )
224 {
225 std::vector< std::unique_ptr< QgsRasterMatrix > > matrixContainer;
226 for ( int i = 0; i < mFunctionArgs.size(); ++i )
227 {
228 std::unique_ptr< QgsRasterMatrix > singleMatrix = std::make_unique< QgsRasterMatrix >( result.nColumns(), result.nRows(), nullptr, result.nodataValue() );
229 if ( !mFunctionArgs.at( i ) || !mFunctionArgs.at( i )->calculate( rasterData, *singleMatrix, row ) )
230 {
231 return false;
232 }
233 matrixContainer.emplace_back( std::move( singleMatrix ) );
234 }
235 evaluateFunction( matrixContainer, result );
236 return true;
237 }
238 return false;
239}
240
241QString QgsRasterCalcNode::toString( bool cStyle ) const
242{
243 QString result;
244 QString left;
245 QString right;
246 if ( mLeft )
247 left = mLeft->toString( cStyle );
248 if ( mRight )
249 right = mRight->toString( cStyle );
250
251 switch ( mType )
252 {
253 case tOperator:
254 switch ( mOperator )
255 {
256 case opPLUS:
257 result = QStringLiteral( "( %1 + %2 )" ).arg( left ).arg( right );
258 break;
259 case opMINUS:
260 result = QStringLiteral( "( %1 - %2 )" ).arg( left ).arg( right );
261 break;
262 case opSIGN:
263 result = QStringLiteral( "-%1" ).arg( left );
264 break;
265 case opMUL:
266 result = QStringLiteral( "%1 * %2" ).arg( left ).arg( right );
267 break;
268 case opDIV:
269 result = QStringLiteral( "%1 / %2" ).arg( left ).arg( right );
270 break;
271 case opPOW:
272 if ( cStyle )
273 result = QStringLiteral( "pow( %1, %2 )" ).arg( left ).arg( right );
274 else
275 result = QStringLiteral( "%1^%2" ).arg( left ).arg( right );
276 break;
277 case opEQ:
278 if ( cStyle )
279 result = QStringLiteral( "( float ) ( %1 == %2 )" ).arg( left ).arg( right );
280 else
281 result = QStringLiteral( "%1 = %2" ).arg( left ).arg( right );
282 break;
283 case opNE:
284 if ( cStyle )
285 result = QStringLiteral( "( float ) ( %1 != %2 )" ).arg( left ).arg( right );
286 else
287 result = QStringLiteral( "%1 != %2" ).arg( left ).arg( right );
288 break;
289 case opGT:
290 if ( cStyle )
291 result = QStringLiteral( "( float ) ( %1 > %2 )" ).arg( left ).arg( right );
292 else
293 result = QStringLiteral( "%1 > %2" ).arg( left ).arg( right );
294 break;
295 case opLT:
296 if ( cStyle )
297 result = QStringLiteral( "( float ) ( %1 < %2 )" ).arg( left ).arg( right );
298 else
299 result = QStringLiteral( "%1 < %2" ).arg( left ).arg( right );
300 break;
301 case opGE:
302 if ( cStyle )
303 result = QStringLiteral( "( float ) ( %1 >= %2 )" ).arg( left ).arg( right );
304 else
305 result = QStringLiteral( "%1 >= %2" ).arg( left ).arg( right );
306 break;
307 case opLE:
308 if ( cStyle )
309 result = QStringLiteral( "( float ) ( %1 <= %2 )" ).arg( left ).arg( right );
310 else
311 result = QStringLiteral( "%1 <= %2" ).arg( left ).arg( right );
312 break;
313 case opAND:
314 if ( cStyle )
315 result = QStringLiteral( "( float ) ( %1 && %2 )" ).arg( left ).arg( right );
316 else
317 result = QStringLiteral( "%1 AND %2" ).arg( left ).arg( right );
318 break;
319 case opOR:
320 if ( cStyle )
321 result = QStringLiteral( "( float ) ( %1 || %2 )" ).arg( left ).arg( right );
322 else
323 result = QStringLiteral( "%1 OR %2" ).arg( left ).arg( right );
324 break;
325 case opSQRT:
326 result = QStringLiteral( "sqrt( %1 )" ).arg( left );
327 break;
328 case opSIN:
329 result = QStringLiteral( "sin( %1 )" ).arg( left );
330 break;
331 case opCOS:
332 result = QStringLiteral( "cos( %1 )" ).arg( left );
333 break;
334 case opTAN:
335 result = QStringLiteral( "tan( %1 )" ).arg( left );
336 break;
337 case opASIN:
338 result = QStringLiteral( "asin( %1 )" ).arg( left );
339 break;
340 case opACOS:
341 result = QStringLiteral( "acos( %1 )" ).arg( left );
342 break;
343 case opATAN:
344 result = QStringLiteral( "atan( %1 )" ).arg( left );
345 break;
346 case opLOG:
347 result = QStringLiteral( "log( %1 )" ).arg( left );
348 break;
349 case opLOG10:
350 result = QStringLiteral( "log10( %1 )" ).arg( left );
351 break;
352 case opABS:
353 if ( cStyle )
354 result = QStringLiteral( "fabs( %1 )" ).arg( left );
355 else
356 // Call the floating point version
357 result = QStringLiteral( "abs( %1 )" ).arg( left );
358 break;
359 case opMIN:
360 if ( cStyle )
361 result = QStringLiteral( "min( ( float ) ( %1 ), ( float ) ( %2 ) )" ).arg( left ).arg( right );
362 else
363 result = QStringLiteral( "min( %1, %2 )" ).arg( left ).arg( right );
364 break;
365 case opMAX:
366 if ( cStyle )
367 result = QStringLiteral( "max( ( float ) ( %1 ), ( float ) ( %2 ) )" ).arg( left ).arg( right );
368 else
369 result = QStringLiteral( "max( %1, %2 )" ).arg( left ).arg( right );
370 break;
371 case opNONE:
372 break;
373 }
374 break;
375 case tRasterRef:
376 if ( cStyle )
377 result = QStringLiteral( "( float ) \"%1\"" ).arg( mRasterName );
378 else
379 result = QStringLiteral( "\"%1\"" ).arg( mRasterName );
380 break;
381 case tNumber:
382 result = QString::number( mNumber );
383 if ( cStyle )
384 {
385 result = QStringLiteral( "( float ) %1" ).arg( result );
386 }
387 break;
388 case tMatrix:
389 break;
390 case tFunction:
391 if ( mFunctionName == "if" )
392 {
393 const QString argOne = mFunctionArgs.at( 0 )->toString( cStyle );
394 const QString argTwo = mFunctionArgs.at( 1 )->toString( cStyle );
395 const QString argThree = mFunctionArgs.at( 2 )->toString( cStyle );
396 if ( cStyle )
397 result = QStringLiteral( " ( %1 ) ? ( %2 ) : ( %3 ) " ).arg( argOne, argTwo, argThree );
398 else
399 result = QStringLiteral( "if( %1 , %2 , %3 )" ).arg( argOne, argTwo, argThree );
400 }
401 break;
402 }
403 return result;
404}
405
406QList<const QgsRasterCalcNode *> QgsRasterCalcNode::findNodes( const QgsRasterCalcNode::Type type ) const
407{
408 QList<const QgsRasterCalcNode *> nodeList;
409 if ( mType == type )
410 nodeList.push_back( this );
411 if ( mLeft )
412 nodeList.append( mLeft->findNodes( type ) );
413 if ( mRight )
414 nodeList.append( mRight->findNodes( type ) );
415
416 for ( QgsRasterCalcNode *node : mFunctionArgs )
417 nodeList.append( node->findNodes( type ) );
418
419 return nodeList;
420}
421
422QgsRasterCalcNode *QgsRasterCalcNode::parseRasterCalcString( const QString &str, QString &parserErrorMsg )
423{
424 extern QgsRasterCalcNode *localParseRasterCalcString( const QString & str, QString & parserErrorMsg );
425 return localParseRasterCalcString( str, parserErrorMsg );
426}
427
429{
430 QStringList referencedRasters;
431
432 QStringList rasterRef = this->cleanRasterReferences();
433 for ( const auto &i : rasterRef )
434 {
435 if ( referencedRasters.contains( i.mid( 0, i.lastIndexOf( "@" ) ) ) ) continue;
436 referencedRasters << i.mid( 0, i.lastIndexOf( "@" ) );
437 }
438
439 return referencedRasters;
440}
441
443{
444 QStringList rasterReferences;
445 const QList<const QgsRasterCalcNode *> rasterRefNodes = this->findNodes( QgsRasterCalcNode::Type::tRasterRef );
446
447 for ( const QgsRasterCalcNode *r : rasterRefNodes )
448 {
449
450 QString layerRef( r->toString() );
451 if ( layerRef.at( 0 ) == QLatin1String( "\"" ) && layerRef.at( layerRef.size() - 1 ) == QLatin1String( "\"" ) )
452 {
453 layerRef.remove( 0, 1 );
454 layerRef.chop( 1 );
455
456 }
457 layerRef.remove( QChar( '\\' ), Qt::CaseInsensitive );
458 rasterReferences << layerRef;
459 }
460
461 return rasterReferences;
462}
463
464QgsRasterMatrix QgsRasterCalcNode::evaluateFunction( const std::vector< std::unique_ptr< QgsRasterMatrix > > &matrixVector, QgsRasterMatrix &result ) const
465{
466
467 if ( mFunctionName == "if" )
468 {
469 //scalar condition
470 if ( matrixVector.at( 0 )->isNumber() )
471 {
472 result = ( matrixVector.at( 0 )->data() ? * matrixVector.at( 1 ) : * matrixVector.at( 2 ) );
473 return result;
474 }
475 int nCols = matrixVector.at( 0 )->nColumns();
476 int nRows = matrixVector.at( 0 )->nRows();
477 int nEntries = nCols * nRows;
478 std::unique_ptr< double[] > dataResult = std::make_unique< double[] >( nEntries );
479 double *dataResultRawPtr = dataResult.get();
480
481 double *condition = matrixVector.at( 0 )->data();
482 double *firstOption = matrixVector.at( 1 )->data();
483 double *secondOption = matrixVector.at( 2 )->data();
484
485 bool isFirstOptionNumber = matrixVector.at( 1 )->isNumber();
486 bool isSecondCOptionNumber = matrixVector.at( 2 )->isNumber();
487 double noDataValueCondition = matrixVector.at( 0 )->nodataValue();
488
489 for ( int i = 0; i < nEntries; ++i )
490 {
491 if ( condition[i] == noDataValueCondition )
492 {
493 dataResultRawPtr[i] = result.nodataValue();
494 continue;
495 }
496 else if ( condition[i] != 0 )
497 {
498 dataResultRawPtr[i] = isFirstOptionNumber ? firstOption[0] : firstOption[i];
499 continue;
500 }
501 dataResultRawPtr[i] = isSecondCOptionNumber ? secondOption[0] : secondOption[i];
502 }
503
504 result.setData( nCols, nRows, dataResult.release(), result.nodataValue() );
505 }
506 return result;
507}
Represents a node in a raster calculator.
QString toString(bool cStyle=false) const
Returns a string representation of the expression.
Operator
possible operators
QgsRasterCalcNode()=default
Constructor for QgsRasterCalcNode.
QList< const QgsRasterCalcNode * > findNodes(const QgsRasterCalcNode::Type type) const
Returns a list of nodes of a specific type.
static QgsRasterCalcNode * parseRasterCalcString(const QString &str, QString &parserErrorMsg)
QStringList referencedLayerNames()
Returns a list of raster layer names that are referenced in the formula without the quotation marks.
bool calculate(QMap< QString, QgsRasterBlock * > &rasterData, QgsRasterMatrix &result, int row=-1) const
Calculates result of raster calculation (might be real matrix or single number).
QStringList cleanRasterReferences()
Returns a list of raster layer references that are addressed in the formula, without quotation marks.
Type
defines possible types of node
Represents a matrix in a raster calculator operation.
bool equal(const QgsRasterMatrix &other)
bool max(const QgsRasterMatrix &other)
Calculates the maximum value between two matrices.
bool greaterEqual(const QgsRasterMatrix &other)
double * takeData()
Returns data and ownership.
bool notEqual(const QgsRasterMatrix &other)
bool greaterThan(const QgsRasterMatrix &other)
bool logicalOr(const QgsRasterMatrix &other)
bool lesserEqual(const QgsRasterMatrix &other)
bool absoluteValue()
Calculates the absolute value.
bool logicalAnd(const QgsRasterMatrix &other)
bool lesserThan(const QgsRasterMatrix &other)
bool add(const QgsRasterMatrix &other)
Adds another matrix to this one.
bool multiply(const QgsRasterMatrix &other)
int nRows() const
double * data()
Returns data array (but not ownership)
bool power(const QgsRasterMatrix &other)
double nodataValue() const
bool min(const QgsRasterMatrix &other)
Calculates the minimum value between two matrices.
bool divide(const QgsRasterMatrix &other)
int nColumns() const
void setData(int cols, int rows, double *data, double nodataValue)
bool subtract(const QgsRasterMatrix &other)
Subtracts another matrix from this one.
#define str(x)
Definition: qgis.cpp:38
#define QgsDebugError(str)
Definition: qgslogger.h:38