Skip to content

Commit 5c00fdd

Browse files
committed
[FEATURE] add function to calculator to determine x/y coordinate of nth point of a line
(xat(i) / yat(i); indexs starts with 0; negative indizes apply to end of the line)
1 parent 576b05c commit 5c00fdd

File tree

4 files changed

+35
-13
lines changed

4 files changed

+35
-13
lines changed

src/app/qgsfieldcalculator.cpp

100644100755
+1-6
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,7 @@ void QgsFieldCalculator::accept()
155155
// block layerModified signals (that would trigger table update)
156156
mVectorLayer->blockSignals( true );
157157

158-
bool useGeometry =
159-
calcString.contains( "$area" ) ||
160-
calcString.contains( "$length" ) ||
161-
calcString.contains( "$perimeter" ) ||
162-
calcString.contains( "$x" ) ||
163-
calcString.contains( "$y" );
158+
bool useGeometry = searchTree->needsGeometry();
164159
int rownum = 1;
165160

166161
mVectorLayer->select( mVectorLayer->pendingAllAttributesList(), QgsRectangle(), useGeometry, false );

src/core/qgssearchstringlexer.ll

+2
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ string "'"{str_char}*"'"
100100
"lower" { yylval.op = QgsSearchTreeNode::opLOWER; return FUNCTION1;}
101101
"upper" { yylval.op = QgsSearchTreeNode::opUPPER; return FUNCTION1;}
102102
"length" { yylval.op = QgsSearchTreeNode::opSTRLEN; return FUNCTION1;}
103+
"xat" { yylval.op = QgsSearchTreeNode::opXAT; return FUNCTION1;}
104+
"yat" { yylval.op = QgsSearchTreeNode::opYAT; return FUNCTION1;}
103105
104106
"atan2" { yylval.op = QgsSearchTreeNode::opATAN2; return FUNCTION2;}
105107

src/core/qgssearchtreenode.cpp

100644100755
+30-7
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ QString QgsSearchTreeNode::makeSearchString()
205205
mOp == opASIN || mOp == opACOS || mOp == opATAN ||
206206
mOp == opTOINT || mOp == opTOREAL || mOp == opTOSTRING ||
207207
mOp == opLOWER || mOp == opUPPER || mOp == opSTRLEN ||
208-
mOp == opATAN2 || mOp == opREPLACE || mOp == opSUBSTR )
208+
mOp == opATAN2 || mOp == opREPLACE || mOp == opSUBSTR ||
209+
mOp == opXAT || mOp == opYAT )
209210
{
210211
// functions
211212
switch ( mOp )
@@ -226,6 +227,8 @@ QString QgsSearchTreeNode::makeSearchString()
226227
case opSTRLEN: str += "length"; break;
227228
case opREPLACE: str += "replace"; break;
228229
case opSUBSTR: str += "substr"; break;
230+
case opXAT: str += "xat"; break;
231+
case opYAT: str += "yat"; break;
229232
default: str += "?";
230233
}
231234
// currently all functions take one parameter
@@ -364,13 +367,15 @@ bool QgsSearchTreeNode::needsGeometry()
364367
{
365368
if ( mType == tOperator )
366369
{
367-
if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY )
370+
if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY || mOp == opXAT || mOp == opYAT )
368371
return true;
369372

370373
if ( mLeft && mLeft->needsGeometry() )
371374
return true;
375+
372376
if ( mRight && mRight->needsGeometry() )
373377
return true;
378+
374379
return false;
375380
}
376381
else
@@ -664,11 +669,11 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q
664669
return value2;
665670
}
666671

667-
if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY )
672+
if ( mOp == opLENGTH || mOp == opAREA || mOp == opPERIMETER || mOp == opX || mOp == opY || mOp == opXAT || mOp == opYAT )
668673
{
669674
if ( !f.geometry() )
670675
{
671-
return QgsSearchTreeValue( 2, "Geometry is 0" );
676+
return QgsSearchTreeValue( 2, QObject::tr( "Geometry is 0" ) );
672677
}
673678

674679
//check that we don't use area for lines or length for polygons
@@ -692,6 +697,24 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q
692697
{
693698
return QgsSearchTreeValue( f.geometry()->asPoint().y() );
694699
}
700+
if (( mOp == opXAT || mOp == opYAT ) && f.geometry()->type() == QGis::Line && value1.isNumeric() )
701+
{
702+
QgsPolyline p = f.geometry()->asPolyline();
703+
704+
int idx = value1.number();
705+
if ( idx < 0 )
706+
{
707+
idx += p.size();
708+
}
709+
710+
if ( idx < 0 || idx >= p.size() )
711+
{
712+
return QgsSearchTreeValue( 2, QObject::tr( "Index %1 out of range [0;%2[" ).arg( idx ).arg( p.size() ) );
713+
}
714+
715+
return QgsSearchTreeValue( mOp == opXAT ? p[idx].x() : p[idx].y() );
716+
}
717+
695718
return QgsSearchTreeValue( 0 );
696719
}
697720

@@ -735,7 +758,7 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q
735758
{
736759
if ( value1.isNumeric() && value2.isNumeric() )
737760
{
738-
return QgsSearchTreeValue( 5, "Operator doesn't match the argument types." );
761+
return QgsSearchTreeValue( 5, QObject::tr( "Operator doesn't match the argument types." ) );
739762
}
740763
else
741764
{
@@ -784,7 +807,7 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q
784807
case opMOD:
785808
// NOTE: we _might_ support float operators, like postgresql does
786809
// see 83c94a886c059 commit in postgresql git repo for more info
787-
return QgsSearchTreeValue( int(val1) % int(val2) );
810+
return QgsSearchTreeValue( int( val1 ) % int( val2 ) );
788811
case opDIV:
789812
if ( val2 == 0 )
790813
return QgsSearchTreeValue( 2, "" ); // division by zero
@@ -793,7 +816,7 @@ QgsSearchTreeValue QgsSearchTreeNode::valueAgainst( const QgsFieldMap& fields, Q
793816
case opPOW:
794817
if (( val1 == 0 && val2 < 0 ) || ( val2 < 0 && ( val2 - floor( val2 ) ) > 0 ) )
795818
{
796-
return QgsSearchTreeValue( 4, "Error in power function" );
819+
return QgsSearchTreeValue( 4, QObject::tr( "Error in power function" ) );
797820
}
798821
return QgsSearchTreeValue( pow( val1, val2 ) );
799822
case opSQRT:

src/core/qgssearchtreenode.h

+2
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class CORE_EXPORT QgsSearchTreeNode
8585
// coordinates
8686
opX,
8787
opY,
88+
opXAT,
89+
opYAT,
8890

8991
// measuring
9092
opLENGTH,

0 commit comments

Comments
 (0)