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 |
} |