From 55027e545f6265aaeda47dc48923cd5be6a47d74 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 18 Sep 2015 05:41:38 +1000 Subject: [PATCH] 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) --- resources/function_help/json/{x_at => $x_at} | 4 +- resources/function_help/json/{y_at => $y_at} | 4 +- resources/function_help/json/area | 7 + resources/function_help/json/end_point | 6 + resources/function_help/json/length | 16 +- resources/function_help/json/make_point | 7 + resources/function_help/json/perimeter | 7 + resources/function_help/json/point_n | 7 + resources/function_help/json/start_point | 6 + resources/function_help/json/x | 7 + resources/function_help/json/y | 7 + src/core/qgsexpression.cpp | 191 +++++++++++++++++-- tests/src/core/testqgsexpression.cpp | 61 +++++- 13 files changed, 291 insertions(+), 39 deletions(-) rename resources/function_help/json/{x_at => $x_at} (73%) rename resources/function_help/json/{y_at => $y_at} (73%) create mode 100644 resources/function_help/json/area create mode 100644 resources/function_help/json/end_point create mode 100644 resources/function_help/json/make_point create mode 100644 resources/function_help/json/perimeter create mode 100644 resources/function_help/json/point_n create mode 100644 resources/function_help/json/start_point create mode 100644 resources/function_help/json/x create mode 100644 resources/function_help/json/y diff --git a/resources/function_help/json/x_at b/resources/function_help/json/$x_at similarity index 73% rename from resources/function_help/json/x_at rename to resources/function_help/json/$x_at index ccf5143bf7e..fd8272d2984 100644 --- a/resources/function_help/json/x_at +++ b/resources/function_help/json/$x_at @@ -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"} ] } diff --git a/resources/function_help/json/y_at b/resources/function_help/json/$y_at similarity index 73% rename from resources/function_help/json/y_at rename to resources/function_help/json/$y_at index 01fc278f935..6b8eb6c3c01 100644 --- a/resources/function_help/json/y_at +++ b/resources/function_help/json/$y_at @@ -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"} ] } diff --git a/resources/function_help/json/area b/resources/function_help/json/area new file mode 100644 index 00000000000..6659fcd55ef --- /dev/null +++ b/resources/function_help/json/area @@ -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"}] +} + diff --git a/resources/function_help/json/end_point b/resources/function_help/json/end_point new file mode 100644 index 00000000000..f5b4e2b1d81 --- /dev/null +++ b/resources/function_help/json/end_point @@ -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)'"}] +} diff --git a/resources/function_help/json/length b/resources/function_help/json/length index 7e667cf7efe..09e91dfe6fc 100644 --- a/resources/function_help/json/length +++ b/resources/function_help/json/length @@ -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"}] + }] } diff --git a/resources/function_help/json/make_point b/resources/function_help/json/make_point new file mode 100644 index 00000000000..9002728124a --- /dev/null +++ b/resources/function_help/json/make_point @@ -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)'"}] +} diff --git a/resources/function_help/json/perimeter b/resources/function_help/json/perimeter new file mode 100644 index 00000000000..9e2c68d33de --- /dev/null +++ b/resources/function_help/json/perimeter @@ -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"}] +} + diff --git a/resources/function_help/json/point_n b/resources/function_help/json/point_n new file mode 100644 index 00000000000..d4121e6b29b --- /dev/null +++ b/resources/function_help/json/point_n @@ -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)'"}] +} diff --git a/resources/function_help/json/start_point b/resources/function_help/json/start_point new file mode 100644 index 00000000000..c8eaec5516b --- /dev/null +++ b/resources/function_help/json/start_point @@ -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)'"}] +} diff --git a/resources/function_help/json/x b/resources/function_help/json/x new file mode 100644 index 00000000000..cfd38a43881 --- /dev/null +++ b/resources/function_help/json/x @@ -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"} + ] +} diff --git a/resources/function_help/json/y b/resources/function_help/json/y new file mode 100644 index 00000000000..e84fe625210 --- /dev/null +++ b/resources/function_help/json/y @@ -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"} + ] +} diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp index 552d3ec12e7..dbc76ef1ecf 100644 --- a/src/core/qgsexpression.cpp +++ b/src/core/qgsexpression.cpp @@ -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() ) + { + //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::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::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::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" ) diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index 2268e0cea29..915611da79a 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -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( "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