Clean up and extend expression geometry functions:

- New expression functions for area(geom), perimeter(geom),
point_n(geom), start_point(geom), end_point(geom), make_point(x,y)
- Add new variant to length() function which takes a geometry object,
allows for length(geom) evaluation.
- Rename x_at, y_at to $x_at, $y_at (alias old names) to reflect that
these only work on current feature geometry
- Add x(geom), y(geom) functions which return x and y coordinate
for point geometries or centroid x/y for non-point geometries
(fix #11008)
This commit is contained in:
Nyall Dawson 2015-09-18 05:41:38 +10:00
parent d82a018f4d
commit 55027e545f
13 changed files with 291 additions and 39 deletions

View File

@ -1,7 +1,7 @@
{
"function": "x_at",
"function": "$x_at",
"description": "Retrieves a x coordinate of the current feature's geometry.",
"arguments": [ {"arg":"i","description":"index of point of a line (indices start at 0; negative values apply from the last index)"}],
"examples": [ { "expression":"x_at(1)", "returns":"5"}
"examples": [ { "expression":"$x_at(1)", "returns":"5"}
]
}

View File

@ -1,7 +1,7 @@
{
"function": "y_at",
"function": "$y_at",
"description": "Retrieves a y coordinate of the current feature's geometry.",
"arguments": [ {"arg":"i","description":"index of point of a line (indices start at 0; negative values apply from the last index)"}],
"examples": [ { "expression":"y_at(1)", "returns":"2"}
"examples": [ { "expression":"$y_at(1)", "returns":"2"}
]
}

View File

@ -0,0 +1,7 @@
{
"function": "area",
"description": "Returns the area of a geometry polygon object. Calculations are in the Spatial Reference System of this geometry.",
"arguments": [ {"arg":"geometry","description":"polygon geometry object"}],
"examples": [ { "expression":"area(geom_from_wkt('POLYGON((0 0, 4 0, 4 2, 0 2, 0 0))'))", "returns":"8.0"}]
}

View File

@ -0,0 +1,6 @@
{
"function": "end_point",
"description": "Returns the last node from a geometry.",
"arguments": [ {"arg":"geometry","description":"geometry object"} ],
"examples": [ { "expression":"geom_to_wkt(end_point(geom_from_wkt('LINESTRING(4 0, 4 2, 0 2)')))", "returns":"'Point (0 2)'"}]
}

View File

@ -1,7 +1,15 @@
{
"function": "length",
"description": "Returns the length of a string.",
"arguments": [ {"arg":"string","description":"string to count length of"}],
"examples": [ { "expression":"length('hello')", "returns":"5"}
]
"description": "Returns the number of characters in a string or the length of a geometry linestring.",
"variants": [
{ "variant": "String variant",
"variant_description": "Returns the number of characters in a string.",
"arguments": [ {"arg":"string","description":"string to count length of"} ],
"examples": [ { "expression":"length('hello')", "returns":"5"} ] },
{
"variant": "Geometry variant",
"variant_description": "Calculate the length of a geometry line object. Calculations are in the Spatial Reference System of this geometry.",
"arguments": [ {"arg":"geometry","description":"line geometry object"}],
"examples": [ { "expression":"length(geom_from_wkt('LINESTRING(0 0, 4 0)'))", "returns":"4.0"}]
}]
}

View File

@ -0,0 +1,7 @@
{
"function": "make_point",
"description": "Creates a point geometry from an x and y value.",
"arguments": [ {"arg":"x","description":"x coordinate of point"},
{"arg":"y","description":"y coordinate of point"} ],
"examples": [ { "expression":"geom_to_wkt(make_point(2,4))", "returns":"'Point (2 4)'"}]
}

View File

@ -0,0 +1,7 @@
{
"function": "perimeter",
"description": "Returns the perimeter of a geometry polygon object. Calculations are in the Spatial Reference System of this geometry.",
"arguments": [ {"arg":"geometry","description":"polygon geometry object"}],
"examples": [ { "expression":"perimeter(geom_from_wkt('POLYGON((0 0, 4 0, 4 2, 0 2, 0 0))'))", "returns":"12.0"}]
}

View File

@ -0,0 +1,7 @@
{
"function": "point_n",
"description": "Returns a specific node from a geometry.",
"arguments": [ {"arg":"geometry","description":"geometry object"},
{"arg":"index","description":"index of node to return, where 1 is the first node"} ],
"examples": [ { "expression":"geom_to_wkt(point_n(geom_from_wkt('POLYGON((0 0, 4 0, 4 2, 0 2, 0 0))'),2))", "returns":"'Point (4 0)'"}]
}

View File

@ -0,0 +1,6 @@
{
"function": "start_point",
"description": "Returns the first node from a geometry.",
"arguments": [ {"arg":"geometry","description":"geometry object"} ],
"examples": [ { "expression":"geom_to_wkt(start_point(geom_from_wkt('LINESTRING(4 0, 4 2, 0 2)')))", "returns":"'Point (4 0)'"}]
}

View File

@ -0,0 +1,7 @@
{
"function": "x",
"description": "Returns the x coordinate of a point geometry, or the x-coordinate of the centroid for a non-point geometry.",
"arguments": [ {"arg":"geom","description":"a geometry"}],
"examples": [ { "expression":"x( geom_from_wkt( 'POINT(2 5)' ) )", "returns":"2"}
]
}

View File

@ -0,0 +1,7 @@
{
"function": "y",
"description": "Returns the y coordinate of a point geometry, or the y-coordinate of the centroid for a non-point geometry.",
"arguments": [ {"arg":"geom","description":"a geometry"}],
"examples": [ { "expression":"y( geom_from_wkt( 'POINT(2 5)' ) )", "returns":"5"}
]
}

View File

@ -38,6 +38,8 @@
#include "qgsexpressioncontext.h"
#include "qgsproject.h"
#include "qgsstringutils.h"
#include "qgsgeometrycollectionv2.h"
#include "qgspointv2.h"
// from parser
extern QgsExpression::Node* parseExpression( const QString& str, QString& parserErrorMsg );
@ -771,9 +773,22 @@ static QVariant fcnWordwrap( const QVariantList& values, const QgsExpressionCont
static QVariant fcnLength( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
// two variants, one for geometry, one for string
if ( values.at( 0 ).canConvert<QgsGeometry>() )
{
//geometry variant
QgsGeometry geom = getGeometry( values.at( 0 ), parent );
if ( geom.type() != QGis::Line )
return QVariant();
return QVariant( geom.length() );
}
//otherwise fall back to string variant
QString str = getStringValue( values.at( 0 ), parent );
return QVariant( str.length() );
}
static QVariant fcnReplace( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QString str = getStringValue( values.at( 0 ), parent );
@ -1171,6 +1186,7 @@ static QVariant fcnX( const QVariantList&, const QgsExpressionContext* context,
return g->asPoint().x();
}
}
static QVariant fcnY( const QVariantList&, const QgsExpressionContext* context, QgsExpression* )
{
FEAT_FROM_CONTEXT( context, f );
@ -1185,6 +1201,106 @@ static QVariant fcnY( const QVariantList&, const QgsExpressionContext* context,
}
}
static QVariant fcnGeomX( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QgsGeometry geom = getGeometry( values.at( 0 ), parent );
if ( geom.isEmpty() )
return QVariant();
//if single point, return the point's x coordinate
if ( geom.type() == QGis::Point && !geom.isMultipart() )
{
return geom.asPoint().x();
}
//otherwise return centroid x
QgsGeometry* centroid = geom.centroid();
QVariant result( centroid->asPoint().x() );
delete centroid;
return result;
}
static QVariant fcnGeomY( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QgsGeometry geom = getGeometry( values.at( 0 ), parent );
if ( geom.isEmpty() )
return QVariant();
//if single point, return the point's y coordinate
if ( geom.type() == QGis::Point && !geom.isMultipart() )
{
return geom.asPoint().y();
}
//otherwise return centroid y
QgsGeometry* centroid = geom.centroid();
QVariant result( centroid->asPoint().y() );
delete centroid;
return result;
}
static QVariant fcnPointN( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QgsGeometry geom = getGeometry( values.at( 0 ), parent );
if ( geom.isEmpty() )
return QVariant();
//idx is 1 based
int idx = getIntValue( values.at( 1 ), parent ) - 1;
QgsVertexId vId;
if ( idx < 0 || !geom.vertexIdFromVertexNr( idx, vId ) )
{
parent->setEvalErrorString( QObject::tr( "Point index is out of range" ) );
return QVariant();
}
QgsPointV2 point = geom.geometry()->vertexAt( vId );
return QVariant::fromValue( QgsGeometry( new QgsPointV2( point ) ) );
}
static QVariant fcnStartPoint( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QgsGeometry geom = getGeometry( values.at( 0 ), parent );
if ( geom.isEmpty() )
return QVariant();
QgsVertexId vId;
if ( !geom.vertexIdFromVertexNr( 0, vId ) )
{
return QVariant();
}
QgsPointV2 point = geom.geometry()->vertexAt( vId );
return QVariant::fromValue( QgsGeometry( new QgsPointV2( point ) ) );
}
static QVariant fcnEndPoint( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QgsGeometry geom = getGeometry( values.at( 0 ), parent );
if ( geom.isEmpty() )
return QVariant();
QgsVertexId vId;
if ( !geom.vertexIdFromVertexNr( geom.geometry()->nCoordinates() - 1, vId ) )
{
return QVariant();
}
QgsPointV2 point = geom.geometry()->vertexAt( vId );
return QVariant::fromValue( QgsGeometry( new QgsPointV2( point ) ) );
}
static QVariant fcnMakePoint( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
double x = getDoubleValue( values.at( 0 ), parent );
double y = getDoubleValue( values.at( 1 ), parent );
return QVariant::fromValue( QgsGeometry( new QgsPointV2( x, y ) ) );
}
static QVariant pointAt( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent ) // helper function
{
FEAT_FROM_CONTEXT( context, f );
@ -1251,6 +1367,17 @@ static QVariant fcnGeomArea( const QVariantList&, const QgsExpressionContext* co
QgsDistanceArea* calc = parent->geomCalculator();
return QVariant( calc->measure( f.constGeometry() ) );
}
static QVariant fcnArea( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QgsGeometry geom = getGeometry( values.at( 0 ), parent );
if ( geom.type() != QGis::Polygon )
return QVariant();
return QVariant( geom.area() );
}
static QVariant fcnGeomLength( const QVariantList&, const QgsExpressionContext* context, QgsExpression* parent )
{
FEAT_FROM_CONTEXT( context, f );
@ -1258,6 +1385,7 @@ static QVariant fcnGeomLength( const QVariantList&, const QgsExpressionContext*
QgsDistanceArea* calc = parent->geomCalculator();
return QVariant( calc->measure( f.constGeometry() ) );
}
static QVariant fcnGeomPerimeter( const QVariantList&, const QgsExpressionContext* context, QgsExpression* parent )
{
FEAT_FROM_CONTEXT( context, f );
@ -1265,10 +1393,22 @@ static QVariant fcnGeomPerimeter( const QVariantList&, const QgsExpressionContex
QgsDistanceArea* calc = parent->geomCalculator();
return QVariant( calc->measurePerimeter( f.constGeometry() ) );
}
static QVariant fcnPerimeter( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QgsGeometry geom = getGeometry( values.at( 0 ), parent );
if ( geom.type() != QGis::Polygon )
return QVariant();
//length for polygons = perimeter
return QVariant( geom.length() );
}
static QVariant fcnGeomNumPoints( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
{
QgsGeometry geom = getGeometry( values.at( 0 ), parent );
return QVariant( geom.geometry()->nCoordinates() );
return QVariant( geom.isEmpty() ? 0 : geom.geometry()->nCoordinates() );
}
static QVariant fcnBounds( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
@ -1916,9 +2056,10 @@ const QStringList& QgsExpression::BuiltinFunctions()
<< "color_rgb" << "color_rgba" << "ramp_color"
<< "color_hsl" << "color_hsla" << "color_hsv" << "color_hsva"
<< "color_cymk" << "color_cymka"
<< "xat" << "yat" << "$area"
<< "$length" << "$perimeter" << "$x" << "$y" << "num_points"
<< "x_at" << "xat" << "y_at" << "yat" << "x_min" << "xmin" << "x_max" << "xmax"
<< "xat" << "yat" << "$area" << "area" << "perimeter"
<< "$length" << "$perimeter" << "x" << "y" << "$x" << "$y" << "num_points"
<< "point_n" << "start_point" << "end_point" << "make_point"
<< "$x_at" << "x_at" << "xat" << "$y_at" << "y_at" << "yat" << "x_min" << "xmin" << "x_max" << "xmax"
<< "y_min" << "ymin" << "y_max" << "ymax" << "geom_from_wkt" << "geomFromWKT"
<< "geom_from_gml" << "geomFromGML" << "intersects_bbox" << "bbox"
<< "disjoint" << "intersects" << "touches" << "crosses" << "contains"
@ -1975,7 +2116,7 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "to_time", 1, fcnToTime, "Conversions", QString(), false, QStringList(), false, QStringList() << "totime" )
<< new StaticFunction( "to_interval", 1, fcnToInterval, "Conversions", QString(), false, QStringList(), false, QStringList() << "tointerval" )
<< new StaticFunction( "coalesce", -1, fcnCoalesce, "Conditionals", QString(), false, QStringList(), false, QStringList(), true )
<< new StaticFunction( "if", 3, fcnIf, "Conditionals", "", False, QStringList(), true )
<< new StaticFunction( "if", 3, fcnIf, "Conditionals", QString(), False, QStringList(), true )
<< new StaticFunction( "regexp_match", 2, fcnRegexpMatch, "Conditionals" )
<< new StaticFunction( "now", 0, fcnNow, "Date and Time", QString(), false, QStringList(), false, QStringList() << "$now" )
<< new StaticFunction( "age", 2, fcnAge, "Date and Time" )
@ -2018,18 +2159,26 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "color_hsva", 4, fncColorHsva, "Color" )
<< new StaticFunction( "color_cmyk", 4, fcnColorCmyk, "Color" )
<< new StaticFunction( "color_cmyka", 5, fncColorCmyka, "Color" )
<< new StaticFunction( "$geometry", 0, fcnGeometry, "GeometryGroup", "", true )
<< new StaticFunction( "$area", 0, fcnGeomArea, "GeometryGroup", "", true )
<< new StaticFunction( "$length", 0, fcnGeomLength, "GeometryGroup", "", true )
<< new StaticFunction( "$perimeter", 0, fcnGeomPerimeter, "GeometryGroup", "", true )
<< new StaticFunction( "$x", 0, fcnX, "GeometryGroup", "", true )
<< new StaticFunction( "$y", 0, fcnY, "GeometryGroup", "", true )
<< new StaticFunction( "x_at", 1, fcnXat, "GeometryGroup", "", true, QStringList(), false, QStringList() << "xat" )
<< new StaticFunction( "y_at", 1, fcnYat, "GeometryGroup", "", true, QStringList(), false, QStringList() << "yat" )
<< new StaticFunction( "x_min", 1, fcnXMin, "GeometryGroup", "", true, QStringList(), false, QStringList() << "xmin" )
<< new StaticFunction( "x_max", 1, fcnXMax, "GeometryGroup", "", true, QStringList(), false, QStringList() << "xmax" )
<< new StaticFunction( "y_min", 1, fcnYMin, "GeometryGroup", "", true, QStringList(), false, QStringList() << "ymin" )
<< new StaticFunction( "y_max", 1, fcnYMax, "GeometryGroup", "", true, QStringList(), false, QStringList() << "ymax" )
<< new StaticFunction( "$geometry", 0, fcnGeometry, "GeometryGroup", QString(), true )
<< new StaticFunction( "$area", 0, fcnGeomArea, "GeometryGroup", QString(), true )
<< new StaticFunction( "area", 1, fcnArea, "GeometryGroup" )
<< new StaticFunction( "$length", 0, fcnGeomLength, "GeometryGroup", QString(), true )
<< new StaticFunction( "$perimeter", 0, fcnGeomPerimeter, "GeometryGroup", QString(), true )
<< new StaticFunction( "perimeter", 1, fcnPerimeter, "GeometryGroup" )
<< new StaticFunction( "$x", 0, fcnX, "GeometryGroup", QString(), true )
<< new StaticFunction( "$y", 0, fcnY, "GeometryGroup", QString(), true )
<< new StaticFunction( "x", 1, fcnGeomX, "GeometryGroup" )
<< new StaticFunction( "y", 1, fcnGeomY, "GeometryGroup" )
<< new StaticFunction( "point_n", 2, fcnPointN, "GeometryGroup" )
<< new StaticFunction( "start_point", 1, fcnStartPoint, "GeometryGroup" )
<< new StaticFunction( "end_point", 1, fcnEndPoint, "GeometryGroup" )
<< new StaticFunction( "make_point", 2, fcnMakePoint, "GeometryGroup" )
<< new StaticFunction( "$x_at", 1, fcnXat, "GeometryGroup", QString(), true, QStringList(), false, QStringList() << "xat" << "x_at" )
<< new StaticFunction( "$y_at", 1, fcnYat, "GeometryGroup", QString(), true, QStringList(), false, QStringList() << "yat" << "y_at" )
<< new StaticFunction( "x_min", 1, fcnXMin, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "xmin" )
<< new StaticFunction( "x_max", 1, fcnXMax, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "xmax" )
<< new StaticFunction( "y_min", 1, fcnYMin, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "ymin" )
<< new StaticFunction( "y_max", 1, fcnYMax, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "ymax" )
<< new StaticFunction( "geom_from_wkt", 1, fcnGeomFromWKT, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "geomFromWKT" )
<< new StaticFunction( "geom_from_gml", 1, fcnGeomFromGML, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "geomFromGML" )
<< new StaticFunction( "intersects_bbox", 2, fcnBbox, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "bbox" )
@ -2042,10 +2191,10 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "within", 2, fcnWithin, "GeometryGroup" )
<< new StaticFunction( "buffer", -1, fcnBuffer, "GeometryGroup" )
<< new StaticFunction( "centroid", 1, fcnCentroid, "GeometryGroup" )
<< new StaticFunction( "bounds", 1, fcnBounds, "GeometryGroup", "", true )
<< new StaticFunction( "num_points", 1, fcnGeomNumPoints, "GeometryGroup", "", true )
<< new StaticFunction( "bounds_width", 1, fcnBoundsWidth, "GeometryGroup", "", true )
<< new StaticFunction( "bounds_height", 1, fcnBoundsHeight, "GeometryGroup", "", true )
<< new StaticFunction( "bounds", 1, fcnBounds, "GeometryGroup" )
<< new StaticFunction( "num_points", 1, fcnGeomNumPoints, "GeometryGroup" )
<< new StaticFunction( "bounds_width", 1, fcnBoundsWidth, "GeometryGroup" )
<< new StaticFunction( "bounds_height", 1, fcnBoundsHeight, "GeometryGroup" )
<< new StaticFunction( "convex_hull", 1, fcnConvexHull, "GeometryGroup", QString(), false, QStringList(), false, QStringList() << "convexHull" )
<< new StaticFunction( "difference", 2, fcnDifference, "GeometryGroup" )
<< new StaticFunction( "distance", 2, fcnDistance, "GeometryGroup" )

View File

@ -101,7 +101,7 @@ class TestQgsExpression: public QObject
QTest::newRow( "invalid binary operator" ) << "1+" << false;
QTest::newRow( "invalid function no params" ) << "cos" << false;
QTest::newRow( "invalid function not known" ) << "coz(1)" << false;
QTest::newRow( "invalid operator IN" ) << "x in y" << false;
QTest::newRow( "invalid operator IN" ) << "n in m" << false;
QTest::newRow( "empty node list" ) << "1 in ()" << false;
QTest::newRow( "invalid sqrt params" ) << "sqrt(2,4)" << false;
QTest::newRow( "special column as function" ) << "$id()" << false;
@ -117,23 +117,23 @@ class TestQgsExpression: public QObject
QTest::newRow( "unary minus" ) << "-(-3)" << true;
QTest::newRow( "function" ) << "cos(0)" << true;
QTest::newRow( "function2" ) << "atan2(0,1)" << true;
QTest::newRow( "operator IN" ) << "x in (a,b)" << true;
QTest::newRow( "operator IN" ) << "n in (a,b)" << true;
QTest::newRow( "pow" ) << "2 ^ 8" << true;
QTest::newRow( "$id" ) << "$id + 1" << true;
QTest::newRow( "arithmetics" ) << "1+2*3" << true;
QTest::newRow( "logic" ) << "be or not be" << true;
QTest::newRow( "conditions +1" ) << "case when x then y end" << true;
QTest::newRow( "conditions +2" ) << "case when x then y else z end" << true;
QTest::newRow( "conditions +3" ) << "case when x then y when a then b end" << true;
QTest::newRow( "conditions +4" ) << "case when x then y when a then b else z end" << true;
QTest::newRow( "conditions +1" ) << "case when n then m end" << true;
QTest::newRow( "conditions +2" ) << "case when n then m else o end" << true;
QTest::newRow( "conditions +3" ) << "case when n then m when a then b end" << true;
QTest::newRow( "conditions +4" ) << "case when n then ym when a then b else z end" << true;
QTest::newRow( "conditions -1" ) << "case end" << false;
QTest::newRow( "conditions -2" ) << "when x then y" << false;
QTest::newRow( "conditions -2" ) << "when n then m" << false;
QTest::newRow( "conditions -3" ) << "case" << false;
QTest::newRow( "conditions -4" ) << "case when x y end" << false;
QTest::newRow( "conditions -5" ) << "case y end" << false;
QTest::newRow( "conditions -4" ) << "case when n m end" << false;
QTest::newRow( "conditions -5" ) << "case m end" << false;
}
void parsing()
{
@ -398,6 +398,36 @@ class TestQgsExpression: public QObject
// geometry functions
QTest::newRow( "num_points" ) << "num_points(geom_from_wkt('GEOMETRYCOLLECTION(LINESTRING(0 0, 1 0),POINT(6 5))'))" << false << QVariant( 3 );
QTest::newRow( "length line" ) << "length(geom_from_wkt('LINESTRING(0 0, 4 0)'))" << false << QVariant( 4.0 );
QTest::newRow( "length polygon" ) << "length(geom_from_wkt('POLYGON((0 0, 4 0, 4 2, 0 2, 0 0))'))" << false << QVariant();
QTest::newRow( "length point" ) << "length(geom_from_wkt('POINT(0 0)'))" << false << QVariant();
QTest::newRow( "area polygon" ) << "area(geom_from_wkt('POLYGON((0 0, 4 0, 4 2, 0 2, 0 0))'))" << false << QVariant( 8.0 );
QTest::newRow( "area line" ) << "area(geom_from_wkt('LINESTRING(0 0, 4 0)'))" << false << QVariant();
QTest::newRow( "area point" ) << "area(geom_from_wkt('POINT(0 0)'))" << false << QVariant();
QTest::newRow( "perimeter polygon" ) << "perimeter(geom_from_wkt('POLYGON((0 0, 4 0, 4 2, 0 2, 0 0))'))" << false << QVariant( 12.0 );
QTest::newRow( "perimeter line" ) << "perimeter(geom_from_wkt('LINESTRING(0 0, 4 0)'))" << false << QVariant();
QTest::newRow( "perimeter point" ) << "perimeter(geom_from_wkt('POINT(0 0)'))" << false << QVariant();
QTest::newRow( "point_n point" ) << "geom_to_wkt(point_n(geom_from_wkt('POINT(0 0)'),1))" << false << QVariant( "Point (0 0)" );
QTest::newRow( "point_n bad index" ) << "geom_to_wkt(point_n(geom_from_wkt('POINT(0 0)'),0))" << true << QVariant();
QTest::newRow( "point_n bad index" ) << "geom_to_wkt(point_n(geom_from_wkt('POINT(0 0)'),2))" << true << QVariant();
QTest::newRow( "point_n multipoint" ) << "geom_to_wkt(point_n(geom_from_wkt('MULTIPOINT((0 0), (1 1), (2 2))'),2))" << false << QVariant( "Point (1 1)" );
QTest::newRow( "point_n line" ) << "geom_to_wkt(point_n(geom_from_wkt('LINESTRING(0 0, 1 1, 2 2)'),3))" << false << QVariant( "Point (2 2)" );
QTest::newRow( "point_n polygon" ) << "geom_to_wkt(point_n(geom_from_wkt('POLYGON((0 0, 4 0, 4 2, 0 2, 0 0))'),3))" << false << QVariant( "Point (4 2)" );
QTest::newRow( "start_point point" ) << "geom_to_wkt(start_point(geom_from_wkt('POINT(2 0)')))" << false << QVariant( "Point (2 0)" );
QTest::newRow( "start_point multipoint" ) << "geom_to_wkt(start_point(geom_from_wkt('MULTIPOINT((3 3), (1 1), (2 2))')))" << false << QVariant( "Point (3 3)" );
QTest::newRow( "start_point line" ) << "geom_to_wkt(start_point(geom_from_wkt('LINESTRING(4 1, 1 1, 2 2)')))" << false << QVariant( "Point (4 1)" );
QTest::newRow( "start_point polygon" ) << "geom_to_wkt(start_point(geom_from_wkt('POLYGON((-1 -1, 4 0, 4 2, 0 2, -1 -1))')))" << false << QVariant( "Point (-1 -1)" );
QTest::newRow( "end_point point" ) << "geom_to_wkt(end_point(geom_from_wkt('POINT(2 0)')))" << false << QVariant( "Point (2 0)" );
QTest::newRow( "end_point multipoint" ) << "geom_to_wkt(end_point(geom_from_wkt('MULTIPOINT((3 3), (1 1), (2 2))')))" << false << QVariant( "Point (2 2)" );
QTest::newRow( "end_point line" ) << "geom_to_wkt(end_point(geom_from_wkt('LINESTRING(4 1, 1 1, 2 2)')))" << false << QVariant( "Point (2 2)" );
QTest::newRow( "end_point polygon" ) << "geom_to_wkt(end_point(geom_from_wkt('POLYGON((-1 -1, 4 0, 4 2, 0 2, -1 -1))')))" << false << QVariant( "Point (-1 -1)" );
QTest::newRow( "make_point" ) << "geom_to_wkt(make_point(2.2,4.4))" << false << QVariant( "Point (2.2 4.4)" );
QTest::newRow( "x point" ) << "x(make_point(2.2,4.4))" << false << QVariant( 2.2 );
QTest::newRow( "y point" ) << "y(make_point(2.2,4.4))" << false << QVariant( 4.4 );
QTest::newRow( "x line" ) << "x(geom_from_wkt('LINESTRING(2 0,2 2, 3 2, 3 0)'))" << false << QVariant( 2.5 );
QTest::newRow( "x line" ) << "y(geom_from_wkt('LINESTRING(2 0,2 2, 3 2, 3 0)'))" << false << QVariant( 1.2 );
QTest::newRow( "x polygon" ) << "x(geom_from_wkt('POLYGON((2 0,2 2, 3 2, 3 0, 2 0))'))" << false << QVariant( 2.5 );
QTest::newRow( "x polygon" ) << "y(geom_from_wkt('POLYGON((2 0,2 2, 3 2, 3 0, 2 0))'))" << false << QVariant( 1.0 );
// string functions
QTest::newRow( "lower" ) << "lower('HeLLo')" << false << QVariant( "hello" );
@ -786,7 +816,7 @@ class TestQgsExpression: public QObject
QTest::addColumn<bool>( "needsGeom" );
// literal evaluation
QTest::newRow( "x > 0" ) << "x > 0" << false;
QTest::newRow( "n > 0" ) << "n > 0" << false;
QTest::newRow( "1 = 1" ) << "1 = 1" << false;
QTest::newRow( "$x > 0" ) << "$x > 0" << true;
QTest::newRow( "xat(0) > 0" ) << "xat(0) > 0" << true;
@ -942,6 +972,17 @@ class TestQgsExpression: public QObject
QVariant vVertices = exp10.evaluate( &fPolygon );
QCOMPARE( vVertices.toInt(), 5 );
QgsExpression exp11( "length($geometry)" );
QVariant vLengthLine = exp11.evaluate( &fPolyline );
QCOMPARE( vLengthLine.toDouble(), 10.0 );
QgsExpression exp12( "area($geometry)" );
QVariant vAreaPoly = exp12.evaluate( &fPolygon );
QCOMPARE( vAreaPoly.toDouble(), 40.0 );
QgsExpression exp13( "perimeter($geometry)" );
QVariant vPerimeterPoly = exp13.evaluate( &fPolygon );
QCOMPARE( vPerimeterPoly.toDouble(), 26.0 );
Q_NOWARN_DEPRECATED_POP