From da78ddeb7cb79eabccb48d9707ea15d9e9b16d50 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 25 Aug 2016 18:36:28 +1000 Subject: [PATCH] [FEATURE] New expression functions for angle/distance interpolation angle_at_vertex: returns average (bisector) angle to a geometry at a specified vertex index distance_to_vertex: returns distance along geometry to a specified vertex index line_interpolate_angle: calculates the angle parallel to a geometry at the specified distance along the geometry Sponsored by Andreas Neumann --- resources/function_help/json/angle_at_vertex | 8 +++++ .../function_help/json/distance_to_vertex | 8 +++++ .../function_help/json/line_interpolate_angle | 8 +++++ src/core/qgsexpression.cpp | 31 +++++++++++++++++++ tests/src/core/testqgsexpression.cpp | 12 +++++++ 5 files changed, 67 insertions(+) create mode 100644 resources/function_help/json/angle_at_vertex create mode 100644 resources/function_help/json/distance_to_vertex create mode 100644 resources/function_help/json/line_interpolate_angle diff --git a/resources/function_help/json/angle_at_vertex b/resources/function_help/json/angle_at_vertex new file mode 100644 index 00000000000..2a585b02ff8 --- /dev/null +++ b/resources/function_help/json/angle_at_vertex @@ -0,0 +1,8 @@ +{ + "name": "angle_at_vertex", + "type": "function", + "description": "Returns the bisector angle (average angle) to the geometry for a specified vertex on a linestring geometry. Angles are in degrees clockwise from north.", + "arguments": [ {"arg":"geometry","description":"a linestring geometry"}, + {"arg":"vertex","description":"vertex index, starting from 0"}], + "examples": [ { "expression":"angle_at_vertex(geometry:=geom_from_wkt('LineString(0 0, 10 0, 10 10)'),vertex:=1)", "returns":"45.0"}] +} diff --git a/resources/function_help/json/distance_to_vertex b/resources/function_help/json/distance_to_vertex new file mode 100644 index 00000000000..7350736edf2 --- /dev/null +++ b/resources/function_help/json/distance_to_vertex @@ -0,0 +1,8 @@ +{ + "name": "distance_to_vertex", + "type": "function", + "description": "Returns the distance along the geometry to a specified vertex.", + "arguments": [ {"arg":"geometry","description":"a linestring geometry"}, + {"arg":"vertex","description":"vertex index, starting from 0"}], + "examples": [ { "expression":"distance_to_vertex(geometry:=geom_from_wkt('LineString(0 0, 10 0, 10 10)'),vertex:=1)", "returns":"10.0"}] +} diff --git a/resources/function_help/json/line_interpolate_angle b/resources/function_help/json/line_interpolate_angle new file mode 100644 index 00000000000..490cdebee10 --- /dev/null +++ b/resources/function_help/json/line_interpolate_angle @@ -0,0 +1,8 @@ +{ + "name": "line_interpolate_angle", + "type": "function", + "description": "Returns the angle parallel to the geometry at a specified distance along a linestring geometry. Angles are in degrees clockwise from north.", + "arguments": [ {"arg":"geometry","description":"a linestring geometry"}, + {"arg":"distance","description":"distance along line to interpolate angle at"}], + "examples": [ { "expression":"line_interpolate_angle(geometry:=geom_from_wkt('LineString(0 0, 10 0)'),distance:=5)", "returns":"90.0"}] +} diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp index 43df882f9ce..ace1be02656 100644 --- a/src/core/qgsexpression.cpp +++ b/src/core/qgsexpression.cpp @@ -2559,6 +2559,30 @@ static QVariant fcnLineInterpolatePoint( const QVariantList& values, const QgsEx return result; } +static QVariant fcnLineInterpolateAngle( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent ) +{ + QgsGeometry lineGeom = getGeometry( values.at( 0 ), parent ); + double distance = getDoubleValue( values.at( 1 ), parent ); + + return lineGeom.interpolateAngle( distance ) * 180.0 / M_PI; +} + +static QVariant fcnAngleAtVertex( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + int vertex = getIntValue( values.at( 1 ), parent ); + + return geom.angleAtVertex( vertex ) * 180.0 / M_PI; +} + +static QVariant fcnDistanceToVertex( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent ) +{ + QgsGeometry geom = getGeometry( values.at( 0 ), parent ); + int vertex = getIntValue( values.at( 1 ), parent ); + + return geom.distanceToVertex( vertex ); +} + static QVariant fcnLineLocatePoint( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent ) { QgsGeometry lineGeom = getGeometry( values.at( 0 ), parent ); @@ -3148,6 +3172,7 @@ const QStringList& QgsExpression::BuiltinFunctions() << "distance" << "intersection" << "sym_difference" << "combine" << "extrude" << "azimuth" << "project" << "closest_point" << "shortest_line" << "line_locate_point" << "line_interpolate_point" + << "line_interpolate_angle" << "angle_at_vertex" << "distance_to_vertex" << "union" << "geom_to_wkt" << "geomToWKT" << "geometry" << "transform" << "get_feature" << "getFeature" << "levenshtein" << "longest_common_substring" << "hamming_distance" @@ -3375,8 +3400,14 @@ const QList& QgsExpression::Functions() << new StaticFunction( "shortest_line", 2, fcnShortestLine, "GeometryGroup" ) << new StaticFunction( "line_interpolate_point", ParameterList() << Parameter( "geometry" ) << Parameter( "distance" ), fcnLineInterpolatePoint, "GeometryGroup" ) + << new StaticFunction( "line_interpolate_angle", ParameterList() << Parameter( "geometry" ) + << Parameter( "distance" ), fcnLineInterpolateAngle, "GeometryGroup" ) << new StaticFunction( "line_locate_point", ParameterList() << Parameter( "geometry" ) << Parameter( "point" ), fcnLineLocatePoint, "GeometryGroup" ) + << new StaticFunction( "angle_at_vertex", ParameterList() << Parameter( "geometry" ) + << Parameter( "vertex" ), fcnAngleAtVertex, "GeometryGroup" ) + << new StaticFunction( "distance_to_vertex", ParameterList() << Parameter( "geometry" ) + << Parameter( "vertex" ), fcnDistanceToVertex, "GeometryGroup" ) << new StaticFunction( "$id", 0, fcnFeatureId, "Record" ) << new StaticFunction( "$currentfeature", 0, fcnFeature, "Record" ) << new StaticFunction( "uuid", 0, fcnUuid, "Record", QString(), false, QStringList(), false, QStringList() << "$uuid" ) diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index 6b767b1b7a3..dc773adc141 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -786,6 +786,18 @@ class TestQgsExpression: public QObject QTest::newRow( "line_locate_point null" ) << "line_locate_point(NULL, geom_from_wkt('Point 5 0'))" << false << QVariant(); QTest::newRow( "line_locate_point point" ) << "line_locate_point(geom_from_wkt('POINT(1 2)'),geom_from_wkt('Point 5 0'))" << false << QVariant(); QTest::newRow( "line_locate_point line" ) << "line_locate_point(geometry:=geom_from_wkt('LineString(0 0, 10 0)'),point:=geom_from_wkt('Point(5 0)'))" << false << QVariant( 5.0 ); + QTest::newRow( "line_interpolate_angle not geom" ) << "line_interpolate_angle('g', 5)" << true << QVariant(); + QTest::newRow( "line_interpolate_angle null" ) << "line_interpolate_angle(NULL, 5)" << false << QVariant(); + QTest::newRow( "line_interpolate_angle point" ) << "line_interpolate_angle(geom_from_wkt('POINT(1 2)'),5)" << false << QVariant( 0.0 ); + QTest::newRow( "line_interpolate_angle line" ) << "line_interpolate_angle(geometry:=geom_from_wkt('LineString(0 0, 10 0)'),distance:=5)" << false << QVariant( 90.0 ); + QTest::newRow( "angle_at_vertex not geom" ) << "angle_at_vertex('g', 5)" << true << QVariant(); + QTest::newRow( "angle_at_vertex null" ) << "angle_at_vertex(NULL, 0)" << false << QVariant(); + QTest::newRow( "angle_at_vertex point" ) << "angle_at_vertex(geom_from_wkt('POINT(1 2)'),0)" << false << QVariant( 0.0 ); + QTest::newRow( "angle_at_vertex line" ) << "angle_at_vertex(geometry:=geom_from_wkt('LineString(0 0, 10 0, 10 10)'),vertex:=1)" << false << QVariant( 45.0 ); + QTest::newRow( "distance_to_vertex not geom" ) << "distance_to_vertex('g', 5)" << true << QVariant(); + QTest::newRow( "distance_to_vertex null" ) << "distance_to_vertex(NULL, 0)" << false << QVariant(); + QTest::newRow( "distance_to_vertex point" ) << "distance_to_vertex(geom_from_wkt('POINT(1 2)'),0)" << false << QVariant( 0.0 ); + QTest::newRow( "distance_to_vertex line" ) << "distance_to_vertex(geometry:=geom_from_wkt('LineString(0 0, 10 0, 10 10)'),vertex:=1)" << false << QVariant( 10.0 ); // string functions QTest::newRow( "lower" ) << "lower('HeLLo')" << false << QVariant( "hello" );