From d730c97f46e4eeff0ef5ad5d577e33c48d0cef2a Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti <elpaso@itopen.it> Date: Thu, 26 Sep 2019 18:00:09 +0200 Subject: [PATCH] Fix multiple raster calc issues Fixes #32023 Raster calculator change sign does not work when OpenCL is on Fixes #32025 QGIS Raster Calculator outputs nodata only rasters Bonus: three new operators with full test coverage - ABS - MIN - MAX --- .../raster/qgsrastercalcnode.sip.in | 3 + .../raster/qgsrastermatrix.sip.in | 8 +- src/analysis/raster/qgsrastercalclexer.ll | 3 + src/analysis/raster/qgsrastercalcnode.cpp | 44 +++- src/analysis/raster/qgsrastercalcnode.h | 3 + src/analysis/raster/qgsrastercalcparser.yy | 1 + src/analysis/raster/qgsrastercalculator.cpp | 4 +- src/analysis/raster/qgsrastermatrix.cpp | 23 ++ src/analysis/raster/qgsrastermatrix.h | 8 +- src/app/qgsrastercalcdialog.cpp | 18 ++ src/app/qgsrastercalcdialog.h | 3 + src/ui/qgsrastercalcdialogbase.ui | 205 ++++++++++-------- .../src/analysis/testqgsrastercalculator.cpp | 68 ++++-- 13 files changed, 273 insertions(+), 118 deletions(-) diff --git a/python/analysis/auto_generated/raster/qgsrastercalcnode.sip.in b/python/analysis/auto_generated/raster/qgsrastercalcnode.sip.in index 61eb0f3d07e..f6e01da8a28 100644 --- a/python/analysis/auto_generated/raster/qgsrastercalcnode.sip.in +++ b/python/analysis/auto_generated/raster/qgsrastercalcnode.sip.in @@ -50,6 +50,9 @@ class QgsRasterCalcNode opSIGN, opLOG, opLOG10, + opABS, + opMAX, + opMIN, opNONE, }; diff --git a/python/analysis/auto_generated/raster/qgsrastermatrix.sip.in b/python/analysis/auto_generated/raster/qgsrastermatrix.sip.in index cc5c9bbc556..d492e6d1f83 100644 --- a/python/analysis/auto_generated/raster/qgsrastermatrix.sip.in +++ b/python/analysis/auto_generated/raster/qgsrastermatrix.sip.in @@ -31,7 +31,9 @@ class QgsRasterMatrix opGE, opLE, opAND, - opOR + opOR, + opMIN, + opMAX }; enum OneArgOperator @@ -46,6 +48,7 @@ class QgsRasterMatrix opSIGN, opLOG, opLOG10, + opABS, }; QgsRasterMatrix(); @@ -91,6 +94,8 @@ Subtracts another matrix from this one bool lesserEqual( const QgsRasterMatrix &other ); bool logicalAnd( const QgsRasterMatrix &other ); bool logicalOr( const QgsRasterMatrix &other ); + bool max( const QgsRasterMatrix &other ); + bool min( const QgsRasterMatrix &other ); bool squareRoot(); bool sinus(); @@ -102,6 +107,7 @@ Subtracts another matrix from this one bool changeSign(); bool log(); bool log10(); + bool absoluteValue(); }; diff --git a/src/analysis/raster/qgsrastercalclexer.ll b/src/analysis/raster/qgsrastercalclexer.ll index a54b40b2b36..fcc753f625f 100644 --- a/src/analysis/raster/qgsrastercalclexer.ll +++ b/src/analysis/raster/qgsrastercalclexer.ll @@ -62,6 +62,9 @@ raster_band_ref_quoted \"(\\.|[^"])*\" "atan" { rasterlval.op = QgsRasterCalcNode::opATAN; return FUNCTION;} "ln" { rasterlval.op = QgsRasterCalcNode::opLOG; return FUNCTION;} "log10" { rasterlval.op = QgsRasterCalcNode::opLOG10; return FUNCTION;} +"abs" { rasterlval.op = QgsRasterCalcNode::opABS; return FUNCTION;} +"min" { rasterlval.op = QgsRasterCalcNode::opMIN; return FUNCTION;} +"max" { rasterlval.op = QgsRasterCalcNode::opMAX; return FUNCTION;} "AND" { return AND; } "OR" { return OR; } diff --git a/src/analysis/raster/qgsrastercalcnode.cpp b/src/analysis/raster/qgsrastercalcnode.cpp index 78354f71d6b..bd06d3ed1f0 100644 --- a/src/analysis/raster/qgsrastercalcnode.cpp +++ b/src/analysis/raster/qgsrastercalcnode.cpp @@ -87,9 +87,8 @@ bool QgsRasterCalcNode::calculate( QMap<QString, QgsRasterBlock * > &rasterData, } else if ( mType == tOperator ) { - QgsRasterMatrix leftMatrix, rightMatrix; - leftMatrix.setNodataValue( result.nodataValue() ); - rightMatrix.setNodataValue( result.nodataValue() ); + QgsRasterMatrix leftMatrix( result.nColumns(), result.nRows(), nullptr, result.nodataValue() ); + QgsRasterMatrix rightMatrix( result.nColumns(), result.nRows(), nullptr, result.nodataValue() ); if ( !mLeft || !mLeft->calculate( rasterData, leftMatrix, row ) ) { @@ -141,6 +140,12 @@ bool QgsRasterCalcNode::calculate( QMap<QString, QgsRasterBlock * > &rasterData, case opOR: leftMatrix.logicalOr( rightMatrix ); break; + case opMIN: + leftMatrix.min( rightMatrix ); + break; + case opMAX: + leftMatrix.max( rightMatrix ); + break; case opSQRT: leftMatrix.squareRoot(); break; @@ -171,6 +176,9 @@ bool QgsRasterCalcNode::calculate( QMap<QString, QgsRasterBlock * > &rasterData, case opLOG10: leftMatrix.log10(); break; + case opABS: + leftMatrix.absoluteValue(); + break; default: return false; } @@ -181,9 +189,10 @@ bool QgsRasterCalcNode::calculate( QMap<QString, QgsRasterBlock * > &rasterData, } else if ( mType == tNumber ) { - double *data = new double[1]; - data[0] = mNumber; - result.setData( 1, 1, data, result.nodataValue() ); + size_t nEntries = static_cast<size_t>( result.nColumns() * result.nRows() ); + std::vector<double> *data = new std::vector<double>( nEntries ); + std::fill( std::begin( *data ), std::end( *data ), mNumber ); + result.setData( result.nColumns(), 1, data->data(), result.nodataValue() ); return true; } else if ( mType == tMatrix ) @@ -219,9 +228,11 @@ QString QgsRasterCalcNode::toString( bool cStyle ) const result = QStringLiteral( "( %1 + %2 )" ).arg( left ).arg( right ); break; case opMINUS: - case opSIGN: result = QStringLiteral( "( %1 - %2 )" ).arg( left ).arg( right ); break; + case opSIGN: + result = QStringLiteral( "-%1" ).arg( left ); + break; case opMUL: result = QStringLiteral( "%1 * %2" ).arg( left ).arg( right ); break; @@ -309,6 +320,25 @@ QString QgsRasterCalcNode::toString( bool cStyle ) const case opLOG10: result = QStringLiteral( "log10( %1 )" ).arg( left ); break; + case opABS: + if ( cStyle ) + result = QStringLiteral( "fabs( %1 )" ).arg( left ); + else + // Call the floating point version + result = QStringLiteral( "abs( %1 )" ).arg( left ); + break; + case opMIN: + if ( cStyle ) + result = QStringLiteral( "min( ( float ) ( %1 ), ( float ) ( %2 ) )" ).arg( left ).arg( right ); + else + result = QStringLiteral( "min( %1, %2 )" ).arg( left ).arg( right ); + break; + case opMAX: + if ( cStyle ) + result = QStringLiteral( "max( ( float ) ( %1 ), ( float ) ( %2 ) )" ).arg( left ).arg( right ); + else + result = QStringLiteral( "max( %1, %2 )" ).arg( left ).arg( right ); + break; case opNONE: break; } diff --git a/src/analysis/raster/qgsrastercalcnode.h b/src/analysis/raster/qgsrastercalcnode.h index 27bd0ccee38..d3655fe222f 100644 --- a/src/analysis/raster/qgsrastercalcnode.h +++ b/src/analysis/raster/qgsrastercalcnode.h @@ -70,6 +70,9 @@ class ANALYSIS_EXPORT QgsRasterCalcNode opSIGN, // change sign opLOG, opLOG10, + opABS, + opMAX, + opMIN, opNONE, }; diff --git a/src/analysis/raster/qgsrastercalcparser.yy b/src/analysis/raster/qgsrastercalcparser.yy index fed84a45114..c598aaa9eb9 100644 --- a/src/analysis/raster/qgsrastercalcparser.yy +++ b/src/analysis/raster/qgsrastercalcparser.yy @@ -79,6 +79,7 @@ root: raster_exp{} raster_exp: FUNCTION '(' raster_exp ')' { $$ = new QgsRasterCalcNode($1, $3, 0); joinTmpNodes($$, $3, 0);} + | FUNCTION '(' raster_exp ',' raster_exp ')' { $$ = new QgsRasterCalcNode($1, $3, $5); joinTmpNodes($$, $3, $5);} | raster_exp AND raster_exp { $$ = new QgsRasterCalcNode( QgsRasterCalcNode::opAND, $1, $3 ); joinTmpNodes($$,$1,$3); } | raster_exp OR raster_exp { $$ = new QgsRasterCalcNode( QgsRasterCalcNode::opOR, $1, $3 ); joinTmpNodes($$,$1,$3); } | raster_exp '=' raster_exp { $$ = new QgsRasterCalcNode( QgsRasterCalcNode::opEQ, $1, $3 ); joinTmpNodes($$,$1,$3); } diff --git a/src/analysis/raster/qgsrastercalculator.cpp b/src/analysis/raster/qgsrastercalculator.cpp index 14d3303b133..7806587b223 100644 --- a/src/analysis/raster/qgsrastercalculator.cpp +++ b/src/analysis/raster/qgsrastercalculator.cpp @@ -220,8 +220,8 @@ QgsRasterCalculator::Result QgsRasterCalculator::processCalculation( QgsFeedback } } - QgsRasterMatrix resultMatrix; - resultMatrix.setNodataValue( outputNodataValue ); + // 1 row X mNumOutputColumns matrix + QgsRasterMatrix resultMatrix( mNumOutputColumns, 1, nullptr, outputNodataValue ); _rasterData.clear(); for ( const auto &layerRef : inputBlocks ) diff --git a/src/analysis/raster/qgsrastermatrix.cpp b/src/analysis/raster/qgsrastermatrix.cpp index 7cedc9112d7..544d2a24d96 100644 --- a/src/analysis/raster/qgsrastermatrix.cpp +++ b/src/analysis/raster/qgsrastermatrix.cpp @@ -18,6 +18,7 @@ #include "qgsrastermatrix.h" #include <cstring> #include <cmath> +#include<algorithm> QgsRasterMatrix::QgsRasterMatrix( int nCols, int nRows, double *data, double nodataValue ) : mColumns( nCols ) @@ -132,6 +133,16 @@ bool QgsRasterMatrix::logicalOr( const QgsRasterMatrix &other ) return twoArgumentOperation( opOR, other ); } +bool QgsRasterMatrix::max( const QgsRasterMatrix &other ) +{ + return twoArgumentOperation( opMAX, other ); +} + +bool QgsRasterMatrix::min( const QgsRasterMatrix &other ) +{ + return twoArgumentOperation( opMIN, other ); +} + bool QgsRasterMatrix::squareRoot() { return oneArgumentOperation( opSQRT ); @@ -182,6 +193,11 @@ bool QgsRasterMatrix::log10() return oneArgumentOperation( opLOG10 ); } +bool QgsRasterMatrix::absoluteValue() +{ + return oneArgumentOperation( opABS ); +} + bool QgsRasterMatrix::oneArgumentOperation( OneArgOperator op ) { if ( !mData ) @@ -249,6 +265,9 @@ bool QgsRasterMatrix::oneArgumentOperation( OneArgOperator op ) mData[i] = ::log10( value ); } break; + case opABS: + mData[i] = ::fabs( value ); + break; } } } @@ -299,6 +318,10 @@ double QgsRasterMatrix::calculateTwoArgumentOp( TwoArgOperator op, double arg1, return ( arg1 && arg2 ? 1.0 : 0.0 ); case opOR: return ( arg1 || arg2 ? 1.0 : 0.0 ); + case opMAX: + return std::max( arg1, arg2 ); + case opMIN: + return std::min( arg1, arg2 ); } return mNodataValue; } diff --git a/src/analysis/raster/qgsrastermatrix.h b/src/analysis/raster/qgsrastermatrix.h index 6b92f760fdd..9df4f0c2aa6 100644 --- a/src/analysis/raster/qgsrastermatrix.h +++ b/src/analysis/raster/qgsrastermatrix.h @@ -43,7 +43,9 @@ class ANALYSIS_EXPORT QgsRasterMatrix opGE, // >= opLE, // <= opAND, - opOR + opOR, + opMIN, + opMAX }; enum OneArgOperator @@ -58,6 +60,7 @@ class ANALYSIS_EXPORT QgsRasterMatrix opSIGN, opLOG, opLOG10, + opABS, }; //! Takes ownership of data array @@ -108,6 +111,8 @@ class ANALYSIS_EXPORT QgsRasterMatrix bool lesserEqual( const QgsRasterMatrix &other ); bool logicalAnd( const QgsRasterMatrix &other ); bool logicalOr( const QgsRasterMatrix &other ); + bool max( const QgsRasterMatrix &other ); + bool min( const QgsRasterMatrix &other ); bool squareRoot(); bool sinus(); @@ -119,6 +124,7 @@ class ANALYSIS_EXPORT QgsRasterMatrix bool changeSign(); bool log(); bool log10(); + bool absoluteValue(); private: int mColumns = 0; diff --git a/src/app/qgsrastercalcdialog.cpp b/src/app/qgsrastercalcdialog.cpp index 770f8c18ced..6fed53bb0ab 100644 --- a/src/app/qgsrastercalcdialog.cpp +++ b/src/app/qgsrastercalcdialog.cpp @@ -69,6 +69,9 @@ QgsRasterCalcDialog::QgsRasterCalcDialog( QgsRasterLayer *rasterLayer, QWidget * connect( mLesserEqualButton, &QPushButton::clicked, this, &QgsRasterCalcDialog::mLesserEqualButton_clicked ); connect( mGreaterEqualButton, &QPushButton::clicked, this, &QgsRasterCalcDialog::mGreaterEqualButton_clicked ); connect( mAndButton, &QPushButton::clicked, this, &QgsRasterCalcDialog::mAndButton_clicked ); + connect( mAbsButton, &QPushButton::clicked, this, &QgsRasterCalcDialog::mAbsButton_clicked ); + connect( mMinButton, &QPushButton::clicked, this, &QgsRasterCalcDialog::mMinButton_clicked ); + connect( mMaxButton, &QPushButton::clicked, this, &QgsRasterCalcDialog::mMaxButton_clicked ); connect( mOrButton, &QPushButton::clicked, this, &QgsRasterCalcDialog::mOrButton_clicked ); connect( mButtonBox, &QDialogButtonBox::helpRequested, this, &QgsRasterCalcDialog::showHelp ); @@ -474,6 +477,21 @@ void QgsRasterCalcDialog::mOrButton_clicked() mExpressionTextEdit->insertPlainText( QStringLiteral( " OR " ) ); } +void QgsRasterCalcDialog::mAbsButton_clicked() +{ + mExpressionTextEdit->insertPlainText( QStringLiteral( " ABS ( " ) ); +} + +void QgsRasterCalcDialog::mMinButton_clicked() +{ + mExpressionTextEdit->insertPlainText( QStringLiteral( " MIN ( " ) ); +} + +void QgsRasterCalcDialog::mMaxButton_clicked() +{ + mExpressionTextEdit->insertPlainText( QStringLiteral( " MAX ( " ) ); +} + QString QgsRasterCalcDialog::quoteBandEntry( const QString &layerName ) { // '"' -> '\\"' diff --git a/src/app/qgsrastercalcdialog.h b/src/app/qgsrastercalcdialog.h index 02ca756c3df..b5f06359106 100644 --- a/src/app/qgsrastercalcdialog.h +++ b/src/app/qgsrastercalcdialog.h @@ -91,6 +91,9 @@ class APP_EXPORT QgsRasterCalcDialog: public QDialog, private Ui::QgsRasterCalcD void mGreaterEqualButton_clicked(); void mAndButton_clicked(); void mOrButton_clicked(); + void mAbsButton_clicked(); + void mMinButton_clicked(); + void mMaxButton_clicked(); private: //! Sets the extent and size of the output diff --git a/src/ui/qgsrastercalcdialogbase.ui b/src/ui/qgsrastercalcdialogbase.ui index 71a1db08ac6..945ceeab9eb 100644 --- a/src/ui/qgsrastercalcdialogbase.ui +++ b/src/ui/qgsrastercalcdialogbase.ui @@ -260,24 +260,17 @@ <property name="bottomMargin"> <number>0</number> </property> - <item row="1" column="6"> - <widget class="QPushButton" name="mASinButton"> + <item row="2" column="11"> + <widget class="QPushButton" name="mOrButton"> <property name="text"> - <string>asin</string> + <string>OR</string> </property> </widget> </item> - <item row="0" column="4"> - <widget class="QPushButton" name="mCosButton"> + <item row="1" column="10"> + <widget class="QPushButton" name="mLnButton"> <property name="text"> - <string>cos</string> - </property> - </widget> - </item> - <item row="2" column="4"> - <widget class="QPushButton" name="mNotEqualButton"> - <property name="text"> - <string>!=</string> + <string>ln</string> </property> </widget> </item> @@ -288,24 +281,31 @@ </property> </widget> </item> - <item row="0" column="6"> - <widget class="QPushButton" name="mSinButton"> + <item row="2" column="10"> + <widget class="QPushButton" name="mAndButton"> <property name="text"> - <string>sin</string> + <string>AND</string> </property> </widget> </item> - <item row="1" column="8"> - <widget class="QPushButton" name="mATanButton"> + <item row="0" column="10"> + <widget class="QPushButton" name="mLogButton"> <property name="text"> - <string>atan</string> + <string>log10</string> </property> </widget> </item> - <item row="2" column="2"> - <widget class="QPushButton" name="mEqualButton"> + <item row="2" column="0"> + <widget class="QPushButton" name="mLessButton"> <property name="text"> - <string>=</string> + <string><</string> + </property> + </widget> + </item> + <item row="2" column="8"> + <widget class="QPushButton" name="mGreaterEqualButton"> + <property name="text"> + <string>>=</string> </property> </widget> </item> @@ -316,17 +316,31 @@ </property> </widget> </item> - <item row="1" column="4"> - <widget class="QPushButton" name="mACosButton"> + <item row="0" column="0"> + <widget class="QPushButton" name="mPlusPushButton"> <property name="text"> - <string>acos</string> + <string>+</string> </property> </widget> </item> - <item row="2" column="10"> - <widget class="QPushButton" name="mAndButton"> + <item row="0" column="1"> + <widget class="QPushButton" name="mMultiplyPushButton"> <property name="text"> - <string>AND</string> + <string>*</string> + </property> + </widget> + </item> + <item row="0" column="4"> + <widget class="QPushButton" name="mCosButton"> + <property name="text"> + <string>cos</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QPushButton" name="mGreaterButton"> + <property name="text"> + <string>></string> </property> </widget> </item> @@ -343,80 +357,31 @@ </property> </spacer> </item> - <item row="2" column="8"> - <widget class="QPushButton" name="mGreaterEqualButton"> + <item row="1" column="8"> + <widget class="QPushButton" name="mATanButton"> <property name="text"> - <string>>=</string> + <string>atan</string> </property> </widget> </item> - <item row="1" column="0"> - <widget class="QPushButton" name="mMinusPushButton"> + <item row="1" column="4"> + <widget class="QPushButton" name="mACosButton"> <property name="text"> - <string>-</string> + <string>acos</string> </property> </widget> </item> - <item row="1" column="1"> - <widget class="QPushButton" name="mDividePushButton"> + <item row="1" column="6"> + <widget class="QPushButton" name="mASinButton"> <property name="text"> - <string>/</string> + <string>asin</string> </property> </widget> </item> - <item row="2" column="1"> - <widget class="QPushButton" name="mGreaterButton"> + <item row="2" column="4"> + <widget class="QPushButton" name="mNotEqualButton"> <property name="text"> - <string>></string> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QPushButton" name="mLessButton"> - <property name="text"> - <string><</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QPushButton" name="mMultiplyPushButton"> - <property name="text"> - <string>*</string> - </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QPushButton" name="mPlusPushButton"> - <property name="text"> - <string>+</string> - </property> - </widget> - </item> - <item row="2" column="11"> - <widget class="QPushButton" name="mOrButton"> - <property name="text"> - <string>OR</string> - </property> - </widget> - </item> - <item row="0" column="10"> - <widget class="QPushButton" name="mLogButton"> - <property name="text"> - <string>log10</string> - </property> - </widget> - </item> - <item row="1" column="10"> - <widget class="QPushButton" name="mLnButton"> - <property name="text"> - <string>ln</string> - </property> - </widget> - </item> - <item row="0" column="11"> - <widget class="QPushButton" name="mOpenBracketPushButton"> - <property name="text"> - <string>(</string> + <string>!=</string> </property> </widget> </item> @@ -427,10 +392,31 @@ </property> </widget> </item> - <item row="0" column="2"> - <widget class="QPushButton" name="mSqrtButton"> + <item row="2" column="2"> + <widget class="QPushButton" name="mEqualButton"> <property name="text"> - <string>sqrt</string> + <string>=</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QPushButton" name="mMinusPushButton"> + <property name="text"> + <string>-</string> + </property> + </widget> + </item> + <item row="0" column="11"> + <widget class="QPushButton" name="mOpenBracketPushButton"> + <property name="text"> + <string>(</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QPushButton" name="mDividePushButton"> + <property name="text"> + <string>/</string> </property> </widget> </item> @@ -441,6 +427,41 @@ </property> </widget> </item> + <item row="0" column="6"> + <widget class="QPushButton" name="mSinButton"> + <property name="text"> + <string>sin</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="mSqrtButton"> + <property name="text"> + <string>sqrt</string> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QPushButton" name="mAbsButton"> + <property name="text"> + <string>abs</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QPushButton" name="mMinButton"> + <property name="text"> + <string>min</string> + </property> + </widget> + </item> + <item row="3" column="2"> + <widget class="QPushButton" name="mMaxButton"> + <property name="text"> + <string>max</string> + </property> + </widget> + </item> </layout> </widget> </item> diff --git a/tests/src/analysis/testqgsrastercalculator.cpp b/tests/src/analysis/testqgsrastercalculator.cpp index a112bc607e4..4082ac08e22 100644 --- a/tests/src/analysis/testqgsrastercalculator.cpp +++ b/tests/src/analysis/testqgsrastercalculator.cpp @@ -173,7 +173,7 @@ void TestQgsRasterCalculator::dualOp() QgsRasterCalcNode node( op, new QgsRasterCalcNode( left ), new QgsRasterCalcNode( right ) ); - QgsRasterMatrix result; + QgsRasterMatrix result( 1, 1, nullptr, -999 ); result.setNodataValue( -9999 ); QMap<QString, QgsRasterBlock *> rasterData; @@ -223,8 +223,7 @@ void TestQgsRasterCalculator::singleOp() QgsRasterCalcNode node( op, new QgsRasterCalcNode( value ), nullptr ); - QgsRasterMatrix result; - result.setNodataValue( -9999 ); + QgsRasterMatrix result( 1, 1, nullptr, -9999 ); QMap<QString, QgsRasterBlock *> rasterData; QVERIFY( node.calculate( rasterData, result ) ); @@ -249,8 +248,7 @@ void TestQgsRasterCalculator::singleOpMatrices() QgsRasterCalcNode node( QgsRasterCalcNode::opSIGN, new QgsRasterCalcNode( &m ), nullptr ); - QgsRasterMatrix result; - result.setNodataValue( -9999 ); + QgsRasterMatrix result( 1, 1, nullptr, -9999 ); QMap<QString, QgsRasterBlock *> rasterData; QVERIFY( node.calculate( rasterData, result ) ); @@ -278,8 +276,7 @@ void TestQgsRasterCalculator::dualOpNumberMatrix() QgsRasterCalcNode node( QgsRasterCalcNode::opPLUS, new QgsRasterCalcNode( 5.0 ), new QgsRasterCalcNode( &m ) ); - QgsRasterMatrix result; - result.setNodataValue( -9999 ); + QgsRasterMatrix result( 1, 1, nullptr, -9999 ); QMap<QString, QgsRasterBlock *> rasterData; QVERIFY( node.calculate( rasterData, result ) ); @@ -312,8 +309,7 @@ void TestQgsRasterCalculator::dualOpMatrixNumber() QgsRasterCalcNode node( QgsRasterCalcNode::opPLUS, new QgsRasterCalcNode( &m ), new QgsRasterCalcNode( 5.0 ) ); - QgsRasterMatrix result; - result.setNodataValue( -9999 ); + QgsRasterMatrix result( 1, 1, nullptr, -9999 ); QMap<QString, QgsRasterBlock *> rasterData; QVERIFY( node.calculate( rasterData, result ) ); @@ -354,8 +350,7 @@ void TestQgsRasterCalculator::dualOpMatrixMatrix() QgsRasterCalcNode node( QgsRasterCalcNode::opPLUS, new QgsRasterCalcNode( &m1 ), new QgsRasterCalcNode( &m2 ) ); - QgsRasterMatrix result; - result.setNodataValue( -9999 ); + QgsRasterMatrix result( 1, 1, nullptr, -9999 ); QMap<QString, QgsRasterBlock *> rasterData; QVERIFY( node.calculate( rasterData, result ) ); @@ -373,8 +368,7 @@ void TestQgsRasterCalculator::rasterRefOp() // test single op run on raster ref QgsRasterCalcNode node( QgsRasterCalcNode::opSIGN, new QgsRasterCalcNode( QStringLiteral( "raster" ) ), nullptr ); - QgsRasterMatrix result; - result.setNodataValue( -9999 ); + QgsRasterMatrix result( 1, 1, nullptr, -9999 ); QMap<QString, QgsRasterBlock *> rasterData; //first test invalid raster ref @@ -427,8 +421,7 @@ void TestQgsRasterCalculator::dualOpRasterRaster() QgsRasterCalcNode node( QgsRasterCalcNode::opPLUS, new QgsRasterCalcNode( QStringLiteral( "raster1" ) ), new QgsRasterCalcNode( QStringLiteral( "raster2" ) ) ); - QgsRasterMatrix result; - result.setNodataValue( -9999 ); + QgsRasterMatrix result( 1, 1, nullptr, -9999 ); QVERIFY( node.calculate( rasterData, result ) ); QCOMPARE( result.data()[0], 0.0 ); @@ -587,6 +580,18 @@ void TestQgsRasterCalculator::findNodes() QVERIFY( ! node ); QVERIFY( ! errorString.isEmpty() ); + // Test new abs, min, max + errorString = QString(); + node = QgsRasterCalcNode::parseRasterCalcString( QStringLiteral( "abs(2)" ), errorString ); + QVERIFY( node ); + QVERIFY( errorString.isEmpty() ); + node = QgsRasterCalcNode::parseRasterCalcString( QStringLiteral( "min(-1,1)" ), errorString ); + QVERIFY( node ); + QVERIFY( errorString.isEmpty() ); + node = QgsRasterCalcNode::parseRasterCalcString( QStringLiteral( "max(-1,1)" ), errorString ); + QVERIFY( node ); + QVERIFY( errorString.isEmpty() ); + } void TestQgsRasterCalculator::testRasterEntries() @@ -728,6 +733,16 @@ void TestQgsRasterCalculator::toString() QCOMPARE( _test( QStringLiteral( "0.5 * ( 1.4 * (\"raster@1\" + 2) )" ), true ), QString( "( float ) ( 0.5 ) * ( float ) ( 1.4 ) * ( \"raster@1\" + ( float ) ( 2 ) )" ) ); QCOMPARE( _test( QStringLiteral( "0.5 * ( 1 > 0 )" ), false ), QString( "0.5 * 1 > 0" ) ); QCOMPARE( _test( QStringLiteral( "0.5 * ( 1 > 0 )" ), true ), QString( "( float ) ( 0.5 ) * ( float ) ( ( float ) ( 1 ) > ( float ) ( 0 ) )" ) ); + // Test negative numbers + QCOMPARE( _test( QStringLiteral( "0.5 * ( -1 > 0 )" ), false ), QString( "0.5 * -1 > 0" ) ); + QCOMPARE( _test( QStringLiteral( "0.5 * ( -1 > 0 )" ), true ), QString( "( float ) ( 0.5 ) * ( float ) ( -( float ) ( 1 ) > ( float ) ( 0 ) )" ) ); + // Test new functions + QCOMPARE( _test( QStringLiteral( "0.5 * abs( -1 )" ), false ), QString( "0.5 * abs( -1 )" ) ); + QCOMPARE( _test( QStringLiteral( "0.5 * abs( -1 )" ), true ), QString( "( float ) ( 0.5 ) * fabs( -( float ) ( 1 ) )" ) ); + QCOMPARE( _test( QStringLiteral( "0.5 * min( -1, 1 )" ), false ), QString( "0.5 * min( -1, 1 )" ) ); + QCOMPARE( _test( QStringLiteral( "0.5 * min( -1, 1 )" ), true ), QString( "( float ) ( 0.5 ) * min( ( float ) ( -( float ) ( 1 ) ), ( float ) ( ( float ) ( 1 ) ) )" ) ); + QCOMPARE( _test( QStringLiteral( "0.5 * max( -1, 1 )" ), false ), QString( "0.5 * max( -1, 1 )" ) ); + QCOMPARE( _test( QStringLiteral( "0.5 * max( -1, 1 )" ), true ), QString( "( float ) ( 0.5 ) * max( ( float ) ( -( float ) ( 1 ) ), ( float ) ( ( float ) ( 1 ) ) )" ) ); } void TestQgsRasterCalculator::calcFormulasWithReprojectedLayers() @@ -753,6 +768,8 @@ void TestQgsRasterCalculator::calcFormulasWithReprojectedLayers() auto _chk = [ = ]( const QString & formula, const std::vector<float> &values, bool useOpenCL ) { + qDebug() << formula; + #ifdef HAVE_OPENCL if ( ! QgsOpenClUtils::available() ) return ; @@ -798,6 +815,27 @@ void TestQgsRasterCalculator::calcFormulasWithReprojectedLayers() _chk( QStringLiteral( "\"landsat@1\" * ( \"landsat@1\" > 124 )" ), {125.0, 125.0, 0.0, 125.0, 125.0, 0.0}, false ); _chk( QStringLiteral( "\"landsat@1\" * ( \"landsat@1\" > 124 )" ), {125.0, 125.0, 0.0, 125.0, 125.0, 0.0}, true ); + // Test negative numbers + _chk( QStringLiteral( "-2.5" ), { -2.5, -2.5, -2.5, -2.5, -2.5, -2.5 }, false ); + _chk( QStringLiteral( "- 2.5" ), { -2.5, -2.5, -2.5, -2.5, -2.5, -2.5 }, false ); + _chk( QStringLiteral( "-2.5" ), { -2.5, -2.5, -2.5, -2.5, -2.5, -2.5 }, true ); + _chk( QStringLiteral( "- 2.5" ), { -2.5, -2.5, -2.5, -2.5, -2.5, -2.5 }, true ); + _chk( QStringLiteral( "-\"landsat@1\"" ), {-125, -125, -124, -125, -125, -124}, false ); + _chk( QStringLiteral( "-\"landsat@1\"" ), {-125, -125, -124, -125, -125, -124}, true ); + + // Test abs, min and max + // landsat values: 125 125 124 125 125 124 + // landsat_4326 values: 139 138 140 139 141 137 + _chk( QStringLiteral( "abs(-123)" ), {123, 123, 123, 123, 123, 123}, false ); + _chk( QStringLiteral( "abs(-\"landsat@1\")" ), {125, 125, 124, 125, 125, 124}, true ); + _chk( QStringLiteral( "abs(-123)" ), {123, 123, 123, 123, 123, 123}, false ); + _chk( QStringLiteral( "abs(-\"landsat@1\")" ), {125, 125, 124, 125, 125, 124}, true ); + _chk( QStringLiteral( "-\"landsat_4326@2\" + 15" ), {-124, -123, -125, -124, -126, -122}, false ); + _chk( QStringLiteral( "min(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-125, -125, -125, -125, -126, -124}, false ); + _chk( QStringLiteral( "min(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-125, -125, -125, -125, -126, -124}, true ); + _chk( QStringLiteral( "max(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-124, -123, -124, -124, -125, -122}, false ); + _chk( QStringLiteral( "max(-\"landsat@1\", -\"landsat_4326@2\" + 15 )" ), {-124, -123, -124, -124, -125, -122}, true ); + }