Skip to content

Commit 0a9e994

Browse files
committedAug 30, 2017
[FEATURE] Expression function for hausdorff distance
Allows calculation of the Hausdorff distance between two geometries
1 parent d860722 commit 0a9e994

File tree

3 files changed

+40
-0
lines changed

3 files changed

+40
-0
lines changed
 
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "hausdorff_distance",
3+
"type": "function",
4+
"description": "Returns the Hausdorff distance between two geometries. This is basically a measure of how similar or dissimilar 2 geometries are, with a lower distance indicating more similar geometries.<br>The function can be executed with an optional densify fraction argument. If not specified, an appoximation to the standard Hausdorff distance is used. This approximation is exact or close enough for a large subset of useful cases. Examples of these are:<br><br><li>computing distance between Linestrings that are roughly parallel to each other, and roughly equal in length. This occurs in matching linear networks.</li><li>Testing similarity of geometries.</li><br><br>If the default approximate provided by this method is insufficient, specify the optional densify fraction argument. Specifying this argument performs a segment densification before computing the discrete Hausdorff distance. The parameter sets the fraction by which to densify each segment. Each segment will be split into a number of equal-length subsegments, whose fraction of the total length is closest to the given fraction. Decreasing the densify fraction parameter will make the distance returned approach the true Hausdorff distance for the geometries.",
5+
"arguments": [ {"arg":"geometry a","description":"a geometry"},
6+
{"arg":"geometry b","description":"a geometry"},
7+
{"arg":"densify_fraction","description":"densify fraction amount", "optional":true}],
8+
"examples": [ { "expression":"hausdorff_distance( geometry1:= geom_from_wkt('LINESTRING (0 0, 2 1)'),geometry2:=geom_from_wkt('LINESTRING (0 0, 2 0)'))", "returns":"2"},
9+
{ "expression":"hausdorff_distance( geom_from_wkt('LINESTRING (130 0, 0 0, 0 150)'),geom_from_wkt('LINESTRING (10 10, 10 150, 130 10)'))", "returns":"14.142135623"},
10+
{ "expression":"hausdorff_distance( geom_from_wkt('LINESTRING (130 0, 0 0, 0 150)'),geom_from_wkt('LINESTRING (10 10, 10 150, 130 10)'),0.5)", "returns":"70.0"}
11+
]
12+
}

‎src/core/expression/qgsexpressionfunction.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2574,6 +2574,27 @@ static QVariant fcnDistance( const QVariantList &values, const QgsExpressionCont
25742574
QgsGeometry sGeom = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
25752575
return QVariant( fGeom.distance( sGeom ) );
25762576
}
2577+
2578+
static QVariant fcnHausdorffDistance( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent )
2579+
{
2580+
QgsGeometry g1 = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
2581+
QgsGeometry g2 = QgsExpressionUtils::getGeometry( values.at( 1 ), parent );
2582+
2583+
double res = -1;
2584+
if ( values.length() == 3 && values.at( 2 ).isValid() )
2585+
{
2586+
double densify = QgsExpressionUtils::getDoubleValue( values.at( 2 ), parent );
2587+
densify = qBound( 0.0, densify, 1.0 );
2588+
res = g1.hausdorffDistanceDensify( g2, densify );
2589+
}
2590+
else
2591+
{
2592+
res = g1.hausdorffDistance( g2 );
2593+
}
2594+
2595+
return res > -1 ? QVariant( res ) : QVariant();
2596+
}
2597+
25772598
static QVariant fcnIntersection( const QVariantList &values, const QgsExpressionContext *, QgsExpression *parent )
25782599
{
25792600
QgsGeometry fGeom = QgsExpressionUtils::getGeometry( values.at( 0 ), parent );
@@ -4113,6 +4134,8 @@ const QList<QgsExpressionFunction *> &QgsExpression::Functions()
41134134
<< new QgsStaticExpressionFunction( QStringLiteral( "convex_hull" ), 1, fcnConvexHull, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "convexHull" ) )
41144135
<< new QgsStaticExpressionFunction( QStringLiteral( "difference" ), 2, fcnDifference, QStringLiteral( "GeometryGroup" ) )
41154136
<< new QgsStaticExpressionFunction( QStringLiteral( "distance" ), 2, fcnDistance, QStringLiteral( "GeometryGroup" ) )
4137+
<< new QgsStaticExpressionFunction( QStringLiteral( "hausdorff_distance" ), QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "geometry1" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "geometry2" ) )
4138+
<< QgsExpressionFunction::Parameter( QStringLiteral( "densify_fraction" ), true ), fcnHausdorffDistance, QStringLiteral( "GeometryGroup" ) )
41164139
<< new QgsStaticExpressionFunction( QStringLiteral( "intersection" ), 2, fcnIntersection, QStringLiteral( "GeometryGroup" ) )
41174140
<< new QgsStaticExpressionFunction( QStringLiteral( "sym_difference" ), 2, fcnSymDifference, QStringLiteral( "GeometryGroup" ), QString(), false, QSet<QString>(), false, QStringList() << QStringLiteral( "symDifference" ) )
41184141
<< new QgsStaticExpressionFunction( QStringLiteral( "combine" ), 2, fcnCombine, QStringLiteral( "GeometryGroup" ) )

‎tests/src/core/testqgsexpression.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,11 @@ class TestQgsExpression: public QObject
872872
QTest::newRow( "smooth point" ) << "geom_to_wkt(smooth(geom_from_wkt('POINT(1 2)'),10))" << false << QVariant( "Point (1 2)" );
873873
QTest::newRow( "smooth line" ) << "geom_to_wkt(smooth(geometry:=geom_from_wkt('LineString(0 0, 5 0, 5 5)'),iterations:=1,offset:=0.2,min_length:=-1,max_angle:=180))" << false << QVariant( "LineString (0 0, 4 0, 5 1, 5 5)" );
874874
QTest::newRow( "transform invalid" ) << "transform(make_point(500,500),'EPSG:4326','EPSG:28356')" << false << QVariant();
875+
QTest::newRow( "hausdorff line to line" ) << " hausdorff_distance( geometry1:= geom_from_wkt('LINESTRING (0 0, 2 1)'),geometry2:=geom_from_wkt('LINESTRING (0 0, 2 0)'))" << false << QVariant( 1.0 );
876+
QTest::newRow( "hausdorff line to line default" ) << " round(hausdorff_distance( geom_from_wkt('LINESTRING (130 0, 0 0, 0 150)'),geom_from_wkt('LINESTRING (10 10, 10 150, 130 10)')))" << false << QVariant( 14 );
877+
QTest::newRow( "hausdorff line to line densify" ) << " round(hausdorff_distance( geom_from_wkt('LINESTRING (130 0, 0 0, 0 150)'),geom_from_wkt('LINESTRING (10 10, 10 150, 130 10)'),0.5))" << false << QVariant( 70 );
878+
QTest::newRow( "hausdorff not geom 1" ) << " hausdorff_distance( 'a',geom_from_wkt('LINESTRING (0 0, 2 0)'))" << true << QVariant();
879+
QTest::newRow( "hausdorff not geom 2" ) << " hausdorff_distance( geom_from_wkt('LINESTRING (0 0, 2 0)'), 'b')" << true << QVariant();
875880

876881
// string functions
877882
QTest::newRow( "lower" ) << "lower('HeLLo')" << false << QVariant( "hello" );

0 commit comments

Comments
 (0)
Please sign in to comment.