[QGIS Commit] r14533 - in trunk/qgis: resources/context_help src/app src/core src/ui

svn_qgis at osgeo.org svn_qgis at osgeo.org
Mon Nov 8 07:01:58 EST 2010


Author: jef
Date: 2010-11-08 04:01:58 -0800 (Mon, 08 Nov 2010)
New Revision: 14533

Added:
   trunk/qgis/resources/context_help/QgsFieldCalculator-en_US
Modified:
   trunk/qgis/src/app/qgsfieldcalculator.cpp
   trunk/qgis/src/app/qgsfieldcalculator.h
   trunk/qgis/src/core/qgssearchstringlexer.ll
   trunk/qgis/src/core/qgssearchstringparser.yy
   trunk/qgis/src/core/qgssearchtreenode.cpp
   trunk/qgis/src/core/qgssearchtreenode.h
   trunk/qgis/src/ui/qgsfieldcalculatorbase.ui
Log:
[FEATURE] add field calculator functions (implements #3177)
- add support for functions with 2 or three arguments
- add function atan2(y,x)
- add length(string) to determine string length
- add replace(string,from,to) to do string replaces
- add substr(string,from,length) to retrieve substrings
- add (preliminary) online help to field calculator with a listing of operations.


Added: trunk/qgis/resources/context_help/QgsFieldCalculator-en_US
===================================================================
--- trunk/qgis/resources/context_help/QgsFieldCalculator-en_US	                        (rev 0)
+++ trunk/qgis/resources/context_help/QgsFieldCalculator-en_US	2010-11-08 12:01:58 UTC (rev 14533)
@@ -0,0 +1,71 @@
+<h3>Field Calculator</h3>
+The field calculator allows you to update fields with expressions.
+
+<h4>Supported Operations</h4>
+
+<table border=1>
+<tr>
+  <th>Operation</th>
+  <th>Description</th>
+</tr>
+<tr>
+  <td>
+    <tt>column_name</tt><br>
+    <tt>"column_name"</tt>
+  </td>
+  <td>value of field <tt>column_name</tt></td>
+</tr>
+<tr><td>'string'</td><td>literal string value</td></tr>
+<tr><td><i>number</i></td><td>number</td></tr>
+<tr><td>NULL</td><td>null value</td></tr>
+<tr><td><tt>a</tt> OR <tt>b</tt></td><td><tt>a</tt> or <tt>b</tt> are true.</td></tr>
+<tr><td><tt>a</tt> AND <tt>b</tt></td><td><tt>a</tt> and <tt>b</tt> are true.</td></tr>
+<tr><td>NOT <tt>a</tt></td><td>inverted boolean value of <tt>a</tt></td></tr>
+<tr><td><tt>a</tt> IS NULL</td><td><tt>a</tt> has no value</td></tr>
+<tr><td><tt>a</tt> IS NOT NULL</td><td><tt>a</tt> has <tt>a</tt> value</td></tr>
+<tr><td><tt>a</tt> IN ( value, [, value] )</td><td><tt>a</tt> is one of the listed values</td></tr>
+<tr><td><tt>a</tt> NOT IN ( value, [, value] )</td><td><tt>a</tt> is not one of the listed values</td></tr>
+<tr><td><tt>a</tt> = <tt>b</tt></td><td><tt>a</tt> and <tt>b</tt> are equal</td><tr>
+<tr>
+  <td>
+    <tt>a</tt> != <tt>b</tt><br>
+    <tt>a</tt> &lt;&gt; <tt>b</tt>
+  </td>
+  <td><tt>a</tt> and <tt>b</tt> are not equal</td>
+</tr>
+<tr><td><tt>a</tt> &lt;= <tt>b</tt></td><td><tt>a</tt> is less or equal <tt>b</tt></td></tr>
+<tr><td><tt>a</tt> &gt;= <tt>b</tt></td><td><tt>a</tt> is greater or equal <tt>b</tt></td></tr>
+<tr><td><tt>a</tt> &gt; <tt>b</tt></td><td><tt>a</tt> is greater than <tt>b</tt></td></tr>
+<tr><td><tt>a</tt> &lt; <tt>b</tt></td><td><tt>a</tt> is less than <tt>b</tt></td></tr>
+<tr><td><tt>a</tt> ~ <tt>b</tt></td></td><td><tt>a</tt> matches regular expression <tt>b</tt></td></tr>
+<tr><td><tt>a</tt> LIKE <tt>b</tt></td><td><tt>a</tt> is like <tt>b</tt></td></tr>
+<tr><td><tt>a</tt> ILIKE <tt>b</tt></td><td><tt>a</tt> is like <tt>b</tt> (case insensitive)</td></tr>
+<tr><td>sqrt(<tt>a</tt>)</td><td>square root</td></tr>
+<tr><td>sin(<tt>a</tt>)</td><td>sinus of <tt>a</tt></td></tr>
+<tr><td>cos(<tt>a</tt>)</td><td>cosinus of <tt>b</tt></td></tr>
+<tr><td>tan(<tt>a</tt>)</td><td>tangens of <tt>a</tt></td></tr>
+<tr><td>asin(<tt>a</tt>)</td><td>arcussinus of <tt>a</tt></td></tr>
+<tr><td>acos(<tt>a</tt>)</td><td>arcuscosinus of <tt>a</tt></td></tr>
+<tr><td>atan(<tt>a</tt>)</td><td>arcustangens of <tt>a</tt></td></tr>
+<tr><td>to int(<tt>a</tt>)</td><td>convert string <tt>a</tt> to integer</td></tr>
+<tr><td>to real(<tt>a</tt>)</td><td>convert string <tt>a</tt> to real</td></tr>
+<tr><td>to string(<tt>a</tt>)</td><td>convert number <tt>a</tt> to string</td></tr>
+<tr><td>lower(<tt>a</tt>)</td><td>convert string <tt>a</tt> to lower case</td></tr>
+<tr><td>upper(<tt>a</tt>)</td><td>convert string <tt>a</tt> to upper case</td></tr>
+<tr><td>length(<tt>a</tt>)</td><td>length of string <tt>a</tt></td></tr>
+<tr><td>atan2(y,x)</td><td>arcustangens of y/x using the signs of the two arguments to determine the quadrant of the result.</td></tr>
+<tr><td>replace(<tt>a</tt>,replacethis,withthat)</td><td>replace replacethis with withthat in string <tt>a</tt></td></td>
+<tr><td>substr(<tt>a</tt>,from,len)</td><td>len characters of string <tt>a</tt> starting from from (first character index is 1)</td></td>
+<tr><td><tt>a</tt> || <tt>b</tt></td><td>concatenate strings <tt>a</tt> and <tt>b</tt></td></tr>
+<tr><td>$rownum</td><td>number current row</td></tr>
+<tr><td>$area</td><td>area of polygon</td></tr>
+<tr><td>$length</td><td>area of line</td></tr>
+<tr><td>$id</td><td>feature id</td></tr>
+<tr><td><tt>a</tt> ^ <tt>b</tt></td><td><tt>a</tt> raised to the power of <tt>b</tt></td></tr>
+<tr><td><tt>a</tt> * <tt>b</tt></td><td><tt>a</tt> multiplied by <tt>b</tt></td></tr>
+<tr><td><tt>a</tt> * <tt>b</tt></td><td><tt>a</tt> divided by <tt>b</tt></td></tr>
+<tr><td><tt>a</tt> + <tt>b</tt></td><td><tt>a</tt> plus <tt>b</tt></td></tr>
+<tr><td><tt>a</tt> - <tt>b</tt></td><td><tt>a</tt> minus <tt>b</tt></td></tr>
+<tr><td>+<tt>a</tt></td><td>positive sign</td></tr>
+<tr><td>-<tt>a</tt></td><td>negative value of <tt>a</tt></td></tr>
+</table>

Modified: trunk/qgis/src/app/qgsfieldcalculator.cpp
===================================================================
--- trunk/qgis/src/app/qgsfieldcalculator.cpp	2010-11-08 10:57:54 UTC (rev 14532)
+++ trunk/qgis/src/app/qgsfieldcalculator.cpp	2010-11-08 12:01:58 UTC (rev 14533)
@@ -36,8 +36,6 @@
   mOuputFieldWidthSpinBox->setValue( 10 );
   mOutputFieldPrecisionSpinBox->setValue( 3 );
 
-
-
   //disable ok button until there is text for output field and expression
   mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
 

Modified: trunk/qgis/src/app/qgsfieldcalculator.h
===================================================================
--- trunk/qgis/src/app/qgsfieldcalculator.h	2010-11-08 10:57:54 UTC (rev 14532)
+++ trunk/qgis/src/app/qgsfieldcalculator.h	2010-11-08 12:01:58 UTC (rev 14533)
@@ -17,6 +17,7 @@
 #define QGSFIELDCALCULATOR_H
 
 #include "ui_qgsfieldcalculatorbase.h"
+#include "qgscontexthelp.h"
 
 class QgsVectorLayer;
 
@@ -61,6 +62,8 @@
     void on_mExpressionTextEdit_textChanged();
     void on_mOutputFieldTypeComboBox_activated( int index );
 
+    void on_mButtonBox_helpRequested() { QgsContextHelp::run( metaObject()->className() ); }
+
   private:
     //default constructor forbidden
     QgsFieldCalculator();

Modified: trunk/qgis/src/core/qgssearchstringlexer.ll
===================================================================
--- trunk/qgis/src/core/qgssearchstringlexer.ll	2010-11-08 10:57:54 UTC (rev 14532)
+++ trunk/qgis/src/core/qgssearchstringlexer.ll	2010-11-08 12:01:58 UTC (rev 14533)
@@ -83,19 +83,25 @@
 "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; }
 
 [+-/*^]    { return yytext[0]; }

Modified: trunk/qgis/src/core/qgssearchstringparser.yy
===================================================================
--- trunk/qgis/src/core/qgssearchstringparser.yy	2010-11-08 10:57:54 UTC (rev 14532)
+++ trunk/qgis/src/core/qgssearchstringparser.yy	2010-11-08 12:01:58 UTC (rev 14533)
@@ -61,7 +61,9 @@
 
 %token <number> NUMBER
 %token <op> COMPARISON
-%token <op> FUNCTION
+%token <op> FUNCTION1
+%token <op> FUNCTION2
+%token <op> FUNCTION3
 %token CONCAT
 %token IS
 %token IN
@@ -135,11 +137,28 @@
 
 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); }

Modified: trunk/qgis/src/core/qgssearchtreenode.cpp
===================================================================
--- trunk/qgis/src/core/qgssearchtreenode.cpp	2010-11-08 10:57:54 UTC (rev 14532)
+++ trunk/qgis/src/core/qgssearchtreenode.cpp	2010-11-08 12:01:58 UTC (rev 14533)
@@ -115,8 +115,10 @@
   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 @@
     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 @@
         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 @@
   else if ( mType == tNodeList )
   {
     QStringList items;
-    foreach( QgsSearchTreeNode *node, mNodeList )
+    foreach( QgsSearchTreeNode * node, mNodeList )
     {
       items << node->makeSearchString();
     }
@@ -461,7 +468,7 @@
         return false;
       }
 
-      foreach( QgsSearchTreeNode *node, mRight->mNodeList )
+      foreach( QgsSearchTreeNode * node, mRight->mNodeList )
       {
         if ( !getValue( value2, node, fields, f ) )
         {
@@ -480,7 +487,6 @@
 
       return mOp == opNOTIN;
     }
-    break;
 
     case opRegexp:
     case opLike:
@@ -545,7 +551,7 @@
   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 @@
 
   switch ( mType )
   {
-
     case tNumber:
       QgsDebugMsgLevel( "number: " + QString::number( mNumber ), 2 );
       return QgsSearchTreeValue( mNumber );
@@ -637,13 +642,23 @@
     // 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 @@
         }
       }
 
+      // 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 @@
             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 @@
           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( QList<QgsSearchTreeNode *> 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 )

Modified: trunk/qgis/src/core/qgssearchtreenode.h
===================================================================
--- trunk/qgis/src/core/qgssearchtreenode.h	2010-11-08 10:57:54 UTC (rev 14532)
+++ trunk/qgis/src/core/qgssearchtreenode.h	2010-11-08 12:01:58 UTC (rev 14533)
@@ -74,6 +74,7 @@
       opASIN,
       opACOS,
       opATAN,
+      opATAN2,
 
       // conversion
       opTOINT,
@@ -106,6 +107,9 @@
       opCONCAT,
       opLOWER,
       opUPPER,
+      opREPLACE,
+      opSTRLEN,
+      opSUBSTR,
 
       opROWNUM
     };

Modified: trunk/qgis/src/ui/qgsfieldcalculatorbase.ui
===================================================================
--- trunk/qgis/src/ui/qgsfieldcalculatorbase.ui	2010-11-08 10:57:54 UTC (rev 14532)
+++ trunk/qgis/src/ui/qgsfieldcalculatorbase.ui	2010-11-08 12:01:58 UTC (rev 14533)
@@ -305,7 +305,7 @@
       <enum>Qt::Horizontal</enum>
      </property>
      <property name="standardButtons">
-      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok</set>
      </property>
     </widget>
    </item>



More information about the QGIS-commit mailing list