diff --git a/resources/context_help/QgsFieldCalculator-en_US b/resources/context_help/QgsFieldCalculator-en_US new file mode 100644 index 00000000000..c4c19cb9f20 --- /dev/null +++ b/resources/context_help/QgsFieldCalculator-en_US @@ -0,0 +1,71 @@ +

Field Calculator

+The field calculator allows you to update fields with expressions. + +

Supported Operations

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperationDescription
+ column_name
+ "column_name" +
value of field column_name
'string'literal string value
numbernumber
NULLnull value
a OR ba or b are true.
a AND ba and b are true.
NOT ainverted boolean value of a
a IS NULLa has no value
a IS NOT NULLa has a value
a IN ( value, [, value] )a is one of the listed values
a NOT IN ( value, [, value] )a is not one of the listed values
a = ba and b are equal
+ a != b
+ a <> b +
a and b are not equal
a <= ba is less or equal b
a >= ba is greater or equal b
a > ba is greater than b
a < ba is less than b
a ~ ba matches regular expression b
a LIKE ba is like b
a ILIKE ba is like b (case insensitive)
sqrt(a)square root
sin(a)sinus of a
cos(a)cosinus of b
tan(a)tangens of a
asin(a)arcussinus of a
acos(a)arcuscosinus of a
atan(a)arcustangens of a
to int(a)convert string a to integer
to real(a)convert string a to real
to string(a)convert number a to string
lower(a)convert string a to lower case
upper(a)convert string a to upper case
length(a)length of string a
atan2(y,x)arcustangens of y/x using the signs of the two arguments to determine the quadrant of the result.
replace(a,replacethis,withthat)replace replacethis with withthat in string a
substr(a,from,len)len characters of string a starting from from (first character index is 1)
a || bconcatenate strings a and b
$rownumnumber current row
$areaarea of polygon
$lengtharea of line
$idfeature id
a ^ ba raised to the power of b
a * ba multiplied by b
a * ba divided by b
a + ba plus b
a - ba minus b
+apositive sign
-anegative value of a
diff --git a/src/app/qgsfieldcalculator.cpp b/src/app/qgsfieldcalculator.cpp index 8956d6993b1..1b9bad38222 100644 --- a/src/app/qgsfieldcalculator.cpp +++ b/src/app/qgsfieldcalculator.cpp @@ -36,8 +36,6 @@ QgsFieldCalculator::QgsFieldCalculator( QgsVectorLayer* vl ): QDialog(), mVector mOuputFieldWidthSpinBox->setValue( 10 ); mOutputFieldPrecisionSpinBox->setValue( 3 ); - - //disable ok button until there is text for output field and expression mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( false ); diff --git a/src/app/qgsfieldcalculator.h b/src/app/qgsfieldcalculator.h index eaa74a55009..9ef89697d30 100644 --- a/src/app/qgsfieldcalculator.h +++ b/src/app/qgsfieldcalculator.h @@ -17,6 +17,7 @@ #define QGSFIELDCALCULATOR_H #include "ui_qgsfieldcalculatorbase.h" +#include "qgscontexthelp.h" class QgsVectorLayer; @@ -61,6 +62,8 @@ class QgsFieldCalculator: public QDialog, private Ui::QgsFieldCalculatorBase void on_mExpressionTextEdit_textChanged(); void on_mOutputFieldTypeComboBox_activated( int index ); + void on_mButtonBox_helpRequested() { QgsContextHelp::run( metaObject()->className() ); } + private: //default constructor forbidden QgsFieldCalculator(); diff --git a/src/core/qgssearchstringlexer.ll b/src/core/qgssearchstringlexer.ll index 7a83a314309..e766a5179b7 100644 --- a/src/core/qgssearchstringlexer.ll +++ b/src/core/qgssearchstringlexer.ll @@ -83,18 +83,24 @@ string "'"{str_char}*"'" "LIKE" { yylval.op = QgsSearchTreeNode::opLike; return COMPARISON; } "ILIKE" { yylval.op = QgsSearchTreeNode::opILike; return COMPARISON; } -"sqrt" { yylval.op = QgsSearchTreeNode::opSQRT; return FUNCTION;} -"sin" { yylval.op = QgsSearchTreeNode::opSIN; return FUNCTION;} -"cos" { yylval.op = QgsSearchTreeNode::opCOS; return FUNCTION;} -"tan" { yylval.op = QgsSearchTreeNode::opTAN; return FUNCTION;} -"asin" { yylval.op = QgsSearchTreeNode::opASIN; return FUNCTION;} -"acos" { yylval.op = QgsSearchTreeNode::opACOS; return FUNCTION;} -"atan" { yylval.op = QgsSearchTreeNode::opATAN; return FUNCTION;} -"to int" { yylval.op = QgsSearchTreeNode::opTOINT; return FUNCTION;} -"to real" { yylval.op = QgsSearchTreeNode::opTOREAL; return FUNCTION;} -"to string" { yylval.op = QgsSearchTreeNode::opTOSTRING; return FUNCTION;} -"lower" { yylval.op = QgsSearchTreeNode::opLOWER; return FUNCTION;} -"upper" { yylval.op = QgsSearchTreeNode::opUPPER; return FUNCTION;} +"sqrt" { yylval.op = QgsSearchTreeNode::opSQRT; return FUNCTION1;} +"sin" { yylval.op = QgsSearchTreeNode::opSIN; return FUNCTION1;} +"cos" { yylval.op = QgsSearchTreeNode::opCOS; return FUNCTION1;} +"tan" { yylval.op = QgsSearchTreeNode::opTAN; return FUNCTION1;} +"asin" { yylval.op = QgsSearchTreeNode::opASIN; return FUNCTION1;} +"acos" { yylval.op = QgsSearchTreeNode::opACOS; return FUNCTION1;} +"atan" { yylval.op = QgsSearchTreeNode::opATAN; return FUNCTION1;} +"to int" { yylval.op = QgsSearchTreeNode::opTOINT; return FUNCTION1;} +"to real" { yylval.op = QgsSearchTreeNode::opTOREAL; return FUNCTION1;} +"to string" { yylval.op = QgsSearchTreeNode::opTOSTRING; return FUNCTION1;} +"lower" { yylval.op = QgsSearchTreeNode::opLOWER; return FUNCTION1;} +"upper" { yylval.op = QgsSearchTreeNode::opUPPER; return FUNCTION1;} +"length" { yylval.op = QgsSearchTreeNode::opSTRLEN; return FUNCTION1;} + +"atan2" { yylval.op = QgsSearchTreeNode::opATAN2; return FUNCTION2;} + +"replace" { yylval.op = QgsSearchTreeNode::opREPLACE; return FUNCTION3;} +"substr" { yylval.op = QgsSearchTreeNode::opSUBSTR; return FUNCTION3;} "||" { return CONCAT; } diff --git a/src/core/qgssearchstringparser.yy b/src/core/qgssearchstringparser.yy index 6c9a8a1870d..1ba37c1a28d 100644 --- a/src/core/qgssearchstringparser.yy +++ b/src/core/qgssearchstringparser.yy @@ -61,7 +61,9 @@ void addToTmpNodes(QgsSearchTreeNode* node); %token NUMBER %token COMPARISON -%token FUNCTION +%token FUNCTION1 +%token FUNCTION2 +%token FUNCTION3 %token CONCAT %token IS %token IN @@ -135,11 +137,28 @@ comp_predicate: scalar_exp_list: scalar_exp_list ',' scalar_exp { $$ = $1; $1->append($3); joinTmpNodes($1,$1,$3); } - | scalar_exp { $$ = new QgsSearchTreeNode( QgsSearchTreeNode::tNodeList ); $$->append($1); joinTmpNodes($$,$1,0); } + | scalar_exp + { + $$ = new QgsSearchTreeNode( QgsSearchTreeNode::tNodeList ); + $$->append($1); + joinTmpNodes($$,$1,0); + } ; scalar_exp: - FUNCTION '(' scalar_exp ')' { $$ = new QgsSearchTreeNode($1, $3, 0); joinTmpNodes($$, $3, 0);} + FUNCTION1 '(' scalar_exp ')' { $$ = new QgsSearchTreeNode($1, $3, 0); joinTmpNodes($$, $3, 0); } + | FUNCTION2 '(' scalar_exp ',' scalar_exp ')' { $$ = new QgsSearchTreeNode($1, $3, $5); joinTmpNodes($$, $3, $5); } + | FUNCTION3 '(' scalar_exp ',' scalar_exp ',' scalar_exp ')' + { + QgsSearchTreeNode *args = new QgsSearchTreeNode( QgsSearchTreeNode::tNodeList ); + args->append($3); + args->append($5); + args->append($7); + + $$ = new QgsSearchTreeNode($1, args, 0); + joinTmpNodes($$, $3, $5); + joinTmpNodes($$, $$, $7); + } | scalar_exp '^' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opPOW, $1, $3); joinTmpNodes($$,$1,$3); } | scalar_exp '*' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opMUL, $1, $3); joinTmpNodes($$,$1,$3); } | scalar_exp '/' scalar_exp { $$ = new QgsSearchTreeNode(QgsSearchTreeNode::opDIV, $1, $3); joinTmpNodes($$,$1,$3); } diff --git a/src/core/qgssearchtreenode.cpp b/src/core/qgssearchtreenode.cpp index da6a3238957..b2b9c32e8e7 100644 --- a/src/core/qgssearchtreenode.cpp +++ b/src/core/qgssearchtreenode.cpp @@ -115,8 +115,10 @@ QgsSearchTreeNode::QgsSearchTreeNode( const QgsSearchTreeNode& node ) else mRight = NULL; - foreach( QgsSearchTreeNode *lnode, node.mNodeList ) - mNodeList.append( new QgsSearchTreeNode( *lnode ) ); + foreach( QgsSearchTreeNode * lnode, node.mNodeList ) + { + mNodeList.append( new QgsSearchTreeNode( *lnode ) ); + } init(); } @@ -209,7 +211,8 @@ QString QgsSearchTreeNode::makeSearchString() if ( mOp == opSQRT || mOp == opSIN || mOp == opCOS || mOp == opTAN || mOp == opASIN || mOp == opACOS || mOp == opATAN || mOp == opTOINT || mOp == opTOREAL || mOp == opTOSTRING || - mOp == opLOWER || mOp == opUPPER ) + mOp == opLOWER || mOp == opUPPER || mOp == opSTRLEN || + mOp == opATAN2 || mOp == opREPLACE || mOp == opSUBSTR ) { // functions switch ( mOp ) @@ -226,6 +229,10 @@ QString QgsSearchTreeNode::makeSearchString() case opTOSTRING: str += "to string"; break; case opLOWER: str += "lower"; break; case opUPPER: str += "upper"; break; + case opATAN2: str += "atan2"; break; + case opSTRLEN: str += "length"; break; + case opREPLACE: str += "replace"; break; + case opSUBSTR: str += "substr"; break; default: str += "?"; } // currently all functions take one parameter @@ -306,7 +313,7 @@ QString QgsSearchTreeNode::makeSearchString() else if ( mType == tNodeList ) { QStringList items; - foreach( QgsSearchTreeNode *node, mNodeList ) + foreach( QgsSearchTreeNode * node, mNodeList ) { items << node->makeSearchString(); } @@ -461,7 +468,7 @@ bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, QgsFeature &f ) return false; } - foreach( QgsSearchTreeNode *node, mRight->mNodeList ) + foreach( QgsSearchTreeNode * node, mRight->mNodeList ) { if ( !getValue( value2, node, fields, f ) ) { @@ -480,7 +487,6 @@ bool QgsSearchTreeNode::checkAgainst( const QgsFieldMap& fields, QgsFeature &f ) return mOp == opNOTIN; } - break; case opRegexp: case opLike: @@ -545,7 +551,7 @@ bool QgsSearchTreeNode::getValue( QgsSearchTreeValue& value, value = node->valueAgainst( fields, f ); if ( value.isError() ) { - switch (( int )value.number() ) + switch (( int ) value.number() ) { case 1: mError = QObject::tr( "Referenced column wasn't found: %1" ).arg( value.string() ); @@ -587,7 +593,6 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q switch ( mType ) { - case tNumber: QgsDebugMsgLevel( "number: " + QString::number( mNumber ), 2 ); return QgsSearchTreeValue( mNumber ); @@ -637,13 +642,23 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q // arithmetic operators case tOperator: { - QgsSearchTreeValue value1, value2; + QgsSearchTreeValue value1, value2, value3; if ( mLeft ) { - if ( !getValue( value1, mLeft, fields, f ) ) return value1; + if ( mLeft->type() != tNodeList ) + { + if ( !getValue( value1, mLeft, fields, f ) ) return value1; + } + else + { + if ( mLeft->mNodeList.size() > 0 && !getValue( value1, mLeft->mNodeList[0], fields, f ) ) return value1; + if ( mLeft->mNodeList.size() > 1 && !getValue( value2, mLeft->mNodeList[1], fields, f ) ) return value2; + if ( mLeft->mNodeList.size() > 2 && !getValue( value3, mLeft->mNodeList[2], fields, f ) ) return value3; + } } if ( mRight ) { + Q_ASSERT( !mLeft || mLeft->type() != tNodeList ); if ( !getValue( value2, mRight, fields, f ) ) return value2; } @@ -716,6 +731,23 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q } } + // string operations + switch ( mOp ) + { + case opLOWER: + return QgsSearchTreeValue( value1.string().toLower() ); + case opUPPER: + return QgsSearchTreeValue( value1.string().toUpper() ); + case opSTRLEN: + return QgsSearchTreeValue( value1.string().length() ); + case opREPLACE: + return QgsSearchTreeValue( value1.string().replace( value2.string(), value3.string() ) ); + case opSUBSTR: + return QgsSearchTreeValue( value1.string().mid( value2.number() - 1, value3.number() ) ); + default: + break; + } + // for other operators, convert strings to numbers if needed double val1, val2; if ( value1.isNumeric() ) @@ -740,8 +772,6 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q return QgsSearchTreeValue( 2, "" ); // division by zero else return QgsSearchTreeValue( val1 / val2 ); - default: - return QgsSearchTreeValue( 3, QString::number( mOp ) ); // unknown operator case opPOW: if (( val1 == 0 && val2 < 0 ) || ( val2 < 0 && ( val2 - floor( val2 ) ) > 0 ) ) { @@ -762,16 +792,17 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q return QgsSearchTreeValue( acos( val1 ) ); case opATAN: return QgsSearchTreeValue( atan( val1 ) ); + case opATAN2: + return QgsSearchTreeValue( atan2( val1, val2 ) ); case opTOINT: return QgsSearchTreeValue( int( val1 ) ); case opTOREAL: return QgsSearchTreeValue( val1 ); case opTOSTRING: return QgsSearchTreeValue( QString::number( val1 ) ); - case opLOWER: - return QgsSearchTreeValue( value1.string().toLower() ); - case opUPPER: - return QgsSearchTreeValue( value1.string().toUpper() ); + + default: + return QgsSearchTreeValue( 3, QString::number( mOp ) ); // unknown operator } } @@ -806,8 +837,10 @@ void QgsSearchTreeNode::append( QgsSearchTreeNode *node ) void QgsSearchTreeNode::append( QList nodes ) { - foreach( QgsSearchTreeNode *node, nodes ) - mNodeList.append( node ); + foreach( QgsSearchTreeNode * node, nodes ) + { + mNodeList.append( node ); + } } int QgsSearchTreeValue::compare( QgsSearchTreeValue& value1, QgsSearchTreeValue& value2, Qt::CaseSensitivity cs ) diff --git a/src/core/qgssearchtreenode.h b/src/core/qgssearchtreenode.h index ae087cb4fa6..ac76b78a234 100644 --- a/src/core/qgssearchtreenode.h +++ b/src/core/qgssearchtreenode.h @@ -74,6 +74,7 @@ class CORE_EXPORT QgsSearchTreeNode opASIN, opACOS, opATAN, + opATAN2, // conversion opTOINT, @@ -106,6 +107,9 @@ class CORE_EXPORT QgsSearchTreeNode opCONCAT, opLOWER, opUPPER, + opREPLACE, + opSTRLEN, + opSUBSTR, opROWNUM }; diff --git a/src/ui/qgsfieldcalculatorbase.ui b/src/ui/qgsfieldcalculatorbase.ui index f96dea29e59..1e1f6195c92 100644 --- a/src/ui/qgsfieldcalculatorbase.ui +++ b/src/ui/qgsfieldcalculatorbase.ui @@ -305,7 +305,7 @@ Qt::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok