From dcdb4cd0541e31719a7df51a9e8bb9650afc4f17 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 4 Sep 2022 08:00:11 +1000 Subject: [PATCH] [feature][expressions] Add new shared_paths function Returns a collection containing paths shared by the two input geometries. Those going in the same direction are in the first element of the collection, those going in the opposite direction are in the second element. The paths themselves are given in the direction of the first geometry. (Exposes the GEOS shared paths functionality for use in expressions.) --- resources/function_help/json/shared_paths | 23 +++++++++++++++++++ src/core/expression/qgsexpressionfunction.cpp | 23 +++++++++++++++++++ tests/src/core/testqgsexpression.cpp | 8 +++++++ 3 files changed, 54 insertions(+) create mode 100644 resources/function_help/json/shared_paths diff --git a/resources/function_help/json/shared_paths b/resources/function_help/json/shared_paths new file mode 100644 index 00000000000..4707b8bb493 --- /dev/null +++ b/resources/function_help/json/shared_paths @@ -0,0 +1,23 @@ +{ + "name": "shared_paths", + "type": "function", + "groups": ["GeometryGroup"], + "description": "Returns a collection containing paths shared by the two input geometries. Those going in the same direction are in the first element of the collection, those going in the opposite direction are in the second element. The paths themselves are given in the direction of the first geometry.", + "arguments": [{ + "arg": "geometry1", + "description": "a LineString/MultiLineString geometry" + }, + { + "arg": "geometry2", + "description": "a LineString/MultiLineString geometry" + } + ], + "examples": [{ + "expression": "geom_to_wkt(shared_paths(geom_from_wkt('MULTILINESTRING((26 125,26 200,126 200,126 125,26 125),(51 150,101 150,76 175,51 150)))'),geom_from_wkt('LINESTRING(151 100,126 156.25,126 125,90 161, 76 175)')))", + "returns": "'GeometryCollection (MultiLineString ((126 156.25, 126 125),(101 150, 90 161),(90 161, 76 175)),MultiLineString EMPTY)'" + }, { + "expression": "geom_to_wkt(shared_paths(geom_from_wkt('LINESTRING(76 175,90 161,126 125,126 156.25,151 100)'),geom_from_wkt('MULTILINESTRING((26 125,26 200,126 200,126 125,26 125),(51 150,101 150,76 175,51 150))')))", + "returns": "'GeometryCollection (MultiLineString EMPTY,MultiLineString ((76 175, 90 161),(90 161, 101 150),(126 125, 126 156.25)))'" + }], + "tags": ["linestrings", "paths", "overlapping", "shared", "linestring", "multilinestring"] +} diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 0fbd95655e1..9e85c3125b7 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -2926,6 +2926,24 @@ static QVariant fcnLineMerge( const QVariantList &values, const QgsExpressionCon return QVariant::fromValue( merged ); } +static QVariant fcnSharedPaths( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) +{ + const QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent ); + if ( geom.isNull() ) + return QVariant(); + + const QgsGeometry geom2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent ); + if ( geom2.isNull() ) + return QVariant(); + + const QgsGeometry sharedPaths = geom.sharedPaths( geom2 ); + if ( sharedPaths.isNull() ) + return QVariant(); + + return QVariant::fromValue( sharedPaths ); +} + + static QVariant fcnSimplify( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent, const QgsExpressionNodeFunction * ) { QgsGeometry geom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent ); @@ -7984,6 +8002,11 @@ const QList &QgsExpression::Functions() fcnGeometryN, QStringLiteral( "GeometryGroup" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "boundary" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBoundary, QStringLiteral( "GeometryGroup" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "line_merge" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnLineMerge, QStringLiteral( "GeometryGroup" ) ) + << new QgsStaticExpressionFunction( QStringLiteral( "shared_paths" ), QgsExpressionFunction::ParameterList + { + QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ), + QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) ) + }, fcnSharedPaths, QStringLiteral( "GeometryGroup" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "bounds" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ), fcnBounds, QStringLiteral( "GeometryGroup" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "simplify" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplify, QStringLiteral( "GeometryGroup" ) ) << new QgsStaticExpressionFunction( QStringLiteral( "simplify_vw" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "tolerance" ) ), fcnSimplifyVW, QStringLiteral( "GeometryGroup" ) ) diff --git a/tests/src/core/testqgsexpression.cpp b/tests/src/core/testqgsexpression.cpp index edcfa2f31ad..92222e3d698 100644 --- a/tests/src/core/testqgsexpression.cpp +++ b/tests/src/core/testqgsexpression.cpp @@ -1138,6 +1138,14 @@ class TestQgsExpression: public QObject QTest::newRow( "line_merge point" ) << "line_merge(geom_from_wkt('POINT(1 2)'))" << false << QVariant(); QTest::newRow( "line_merge line" ) << "geom_to_wkt(line_merge(geometry:=geom_from_wkt('LineString(0 0, 10 10)')))" << false << QVariant( "LineString (0 0, 10 10)" ); QTest::newRow( "line_merge multiline" ) << "geom_to_wkt(line_merge(geom_from_wkt('MultiLineString((0 0, 10 10),(10 10, 20 20))')))" << false << QVariant( "LineString (0 0, 10 10, 20 20)" ); + QTest::newRow( "shared_paths not geom 1" ) << "shared_paths('g', geom_from_wkt('LineString(0 0, 10 10)'))" << true << QVariant(); + QTest::newRow( "shared_paths not geom 2" ) << "shared_paths(geom_from_wkt('LineString(0 0, 10 10)'), 'g')" << true << QVariant(); + QTest::newRow( "shared_paths null 1" ) << "shared_paths(NULL, geom_from_wkt('LineString(0 0, 10 10)'))" << false << QVariant(); + QTest::newRow( "shared_paths null 2" ) << "shared_paths(geom_from_wkt('LineString(0 0, 10 10)'), NULL)" << false << QVariant(); + QTest::newRow( "shared_paths point 1" ) << "shared_paths(make_point(1,2), geom_from_wkt('LineString(0 0, 10 10)'))" << false << QVariant(); + QTest::newRow( "shared_paths point 2" ) << "shared_paths(geom_from_wkt('LineString(0 0, 10 10)'), make_point(1,2))" << false << QVariant(); + QTest::newRow( "shared_paths lines 1" ) << "geom_to_wkt(shared_paths(geometry1:=geom_from_wkt('MULTILINESTRING((26 125,26 200,126 200,126 125,26 125),(51 150,101 150,76 175,51 150))'), geometry2:=geom_from_wkt('LINESTRING(151 100,126 156.25,126 125,90 161, 76 175)')))" << false << QVariant( "GeometryCollection (MultiLineString ((126 156.25, 126 125),(101 150, 90 161),(90 161, 76 175)),MultiLineString EMPTY)" ); + QTest::newRow( "shared_paths lines 2" ) << "geom_to_wkt(shared_paths(geometry1:=geom_from_wkt('MULTILINESTRING((26 125,26 200,126 200,126 125,26 125),(51 150,101 150,76 175,51 150))'), geometry2:=reverse(geom_from_wkt('LINESTRING(151 100,126 156.25,126 125,90 161, 76 175)'))))" << false << QVariant( "GeometryCollection (MultiLineString EMPTY,MultiLineString ((126 156.25, 126 125),(101 150, 90 161),(90 161, 76 175)))" ); QTest::newRow( "offset_curve not geom" ) << "offset_curve('g', 5)" << true << QVariant(); QTest::newRow( "offset_curve null" ) << "offset_curve(NULL, 5)" << false << QVariant(); QTest::newRow( "offset_curve point" ) << "offset_curve(geom_from_wkt('POINT(1 2)'),5)" << false << QVariant();