Skip to content

Commit

Permalink
Complete ST_FrechetDistance implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
iGN5117 committed Jun 22, 2023
1 parent 53b3e19 commit 27c9673
Show file tree
Hide file tree
Showing 13 changed files with 132 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ public static void translateGeom(Geometry geometry, double deltaX, double deltaY
}

public static double getFrechetDistance(Geometry g1, Geometry g2) {
if (g1.isEmpty() || g2.isEmpty()) return 0.0;
return DiscreteFrechetDistance.distance(g1, g2);
}
}
31 changes: 25 additions & 6 deletions common/src/test/java/org/apache/sedona/common/FunctionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;

import javax.sound.sampled.Line;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
Expand Down Expand Up @@ -858,22 +857,42 @@ public void translateHybridGeomCollectionDeltaZ() {
assertEquals(emptyLineString.toText(), actualGeometry.getGeometryN(0).getGeometryN(2).toText());
}

@Test
public void testFrechetGeom2D() {

LineString lineString1 = GEOMETRY_FACTORY.createLineString(coordArray(0, 0, 100, 0));
LineString lineString2 = GEOMETRY_FACTORY.createLineString(coordArray(0, 0, 50, 50, 100, 0));
double expected = 70.7106781186548;
double actual = Functions.frechetDistance(lineString1, lineString2);
assertEquals(expected, actual, 1e-9);
}

@Test
public void testFrechetGeom3D() {

LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray3d(1, 0, 1, 2, 2, 2, 3, 3, 3));
Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray3d(1, 0, 0, 1, 1, 1, 2, 1, 1, 1, 0, 0));
double expected = 3.605551275463989;
double actual = Functions.frechetDistance(lineString, polygon);
assertEquals(expected, actual, 1e-9);
}

@Test
public void testFrechetGeomColection() {

public void testFrechetGeomCollection() {
Geometry point = GEOMETRY_FACTORY.createPoint(new Coordinate(1, 2));
Geometry lineString1 = GEOMETRY_FACTORY.createLineString(coordArray(2, 2, 3, 3, 4, 4));
Geometry lineString2 = GEOMETRY_FACTORY.createLineString(coordArray(-1, -1, -4, -4, -10, -10));
Geometry geometryCollection = GEOMETRY_FACTORY.createGeometryCollection(new Geometry[] {point, lineString1, lineString2});
Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray(1, 0, 1, 1, 2, 1, 2, 0, 1, 0));
double expected = 14.866068747318506;
double actual = Functions.frechetDistance(polygon, geometryCollection);
assertEquals(expected, actual, 1e-9);
}

@Test
public void testFrechetGeomEmpty() {

Polygon p1 = GEOMETRY_FACTORY.createPolygon(coordArray(1, 0, 1, 1, 2, 1, 2, 0, 1, 0));
LineString emptyPoint = GEOMETRY_FACTORY.createLineString();
double expected = 0.0;
double actual = Functions.frechetDistance(p1, emptyPoint);
assertEquals(expected, actual, 1e-9);
}
}
20 changes: 20 additions & 0 deletions docs/api/flink/Function.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,26 @@ Input: `LINESTRING EMPTY`

Output: `LINESTRING EMPTY`

## ST_FrechetDistance

Introduction: Computes and returns discrete [Frechet Distance](https://en.wikipedia.org/wiki/Fr%C3%A9chet_distance) between the given two geometrie,
based on [Computing Discrete Frechet Distance](http://www.kr.tuwien.ac.at/staff/eiter/et-archive/cdtr9464.pdf)

If any of the geometries is empty, returns 0.0

Format: `ST_FrechetDistance(g1: geomtry, g2: geometry)`

Since: `1.5.0`

Example:
```sql
SELECT ST_FrechetDistance(g1, g2)
```

Input: `g1: POINT (0 1), g2: LINESTRING (0 0, 1 0, 2 0, 3 0, 4 0, 5 0)`

Output: `5.0990195135927845`

## ST_GeoHash

Introduction: Returns GeoHash of the geometry with given precision
Expand Down
20 changes: 20 additions & 0 deletions docs/api/sql/Function.md
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,26 @@ Input: `LINESTRING EMPTY`

Output: `LINESTRING EMPTY`

## ST_FrechetDistance

Introduction: Computes and returns discrete [Frechet Distance](https://en.wikipedia.org/wiki/Fr%C3%A9chet_distance) between the given two geometrie,
based on [Computing Discrete Frechet Distance](http://www.kr.tuwien.ac.at/staff/eiter/et-archive/cdtr9464.pdf)

If any of the geometries is empty, returns 0.0

Format: `ST_FrechetDistance(g1: geomtry, g2: geometry)`

Since: `1.5.0`

Example:
```sql
SELECT ST_FrechetDistance(g1, g2)
```

Input: `g1: POINT (0 1), g2: LINESTRING (0 0, 1 0, 2 0, 3 0, 4 0, 5 0)`

Output: `5.0990195135927845`

## ST_GeoHash

Introduction: Returns GeoHash of the geometry with given precision
Expand Down
9 changes: 9 additions & 0 deletions flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -736,4 +736,13 @@ public void testTranslate() {
assertEquals(expected, actual);
}

@Test
public void testFrechet() {
Table polyTable = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (1 2)') AS g1, ST_GeomFromWKT('POINT (10 10)') as g2");
polyTable = polyTable.select(call(Functions.ST_FrechetDistance.class.getSimpleName(), $("g1"), $("g2")));
Double expected = 12.041594578792296;
Double actual = (Double) first(polyTable).getField(0);
assertEquals(expected, actual);
}

}
14 changes: 13 additions & 1 deletion python/sedona/sql/st_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@
"ST_NumPoints",
"ST_Force3D",
"ST_NRings",
"ST_Translate"
"ST_Translate",
"ST_FrechetDistance"
]


Expand Down Expand Up @@ -1277,3 +1278,14 @@ def ST_Translate(geometry: ColumnOrName, deltaX: Union[ColumnOrName, float], del
args = (geometry, deltaX, deltaY, deltaZ)
return _call_st_function("ST_Translate", args)

def ST_FrechetDistance(g1: ColumnOrName, g2: ColumnOrName) -> Column:
"""
Computes discrete frechet distance between the two geometries.
If any of the geometry is empty, ST_FrechetDistance returns 0
:param g1:
:param g2:
:return: Computed Discrete Frechet Distance between g1 and g2
"""

args = (g1, g2)
return _call_st_function("ST_FrechetDistance", args)
3 changes: 3 additions & 0 deletions python/tests/sql/test_dataframe_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
(stf.ST_FlipCoordinates, ("point",), "point_geom", "", "POINT (1 0)"),
(stf.ST_Force_2D, ("point",), "point_geom", "", "POINT (0 1)"),
(stf.ST_Force3D, ("point", 1.0), "point_geom", "", "POINT Z (0 1 1)"),
(stf.ST_FrechetDistance, ("point", "line",), "point_and_line", "", 5.0990195135927845),
(stf.ST_GeometricMedian, ("multipoint",), "multipoint_geom", "", "POINT (22.500002656424286 21.250001168173426)"),
(stf.ST_GeometryN, ("geom", 0), "multipoint", "", "POINT (0 0)"),
(stf.ST_GeometryType, ("point",), "point_geom", "", "ST_Point"),
Expand Down Expand Up @@ -390,6 +391,8 @@ def base_df(self, request):
return TestDataFrameAPI.spark.sql("SELECT ST_GeomFromWKT('LINESTRING (0 0, 2 1)') AS line, ST_GeomFromWKT('POLYGON ((1 0, 2 0, 2 2, 1 2, 1 0))') AS poly")
elif request.param == "square_geom":
return TestDataFrameAPI.spark.sql("SELECT ST_GeomFromWKT('POLYGON ((1 0, 1 1, 2 1, 2 0, 1 0))') AS geom")
elif request.param == "point_and_line":
return TestDataFrameAPI.spark.sql("SELECT ST_GeomFromWKT('POINT (0.0 1.0)') AS point, ST_GeomFromWKT('LINESTRING (0 0, 1 0, 2 0, 3 0, 4 0, 5 0)') AS line")
raise ValueError(f"Invalid base_df name passed: {request.param}")

def _id_test_configuration(val):
Expand Down
7 changes: 7 additions & 0 deletions python/tests/sql/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -1098,3 +1098,10 @@ def test_translate(self):
actual = actualDf.selectExpr("ST_AsText(geom)").take(1)[0][0]
assert expected == actual

def test_frechetDistance(self):
expected = 5.0990195135927845
actual_df = self.spark.sql("SELECT ST_FrechetDistance(ST_GeomFromText('LINESTRING (0 0, 1 0, 2 0, 3 0, 4 0, "
"5 0)'), ST_GeomFromText('POINT (0 1)'))")
actual = actual_df.take(1)[0][0]
assert expected == actual

Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ object Catalog {
function[ST_Force3D](0.0),
function[ST_NRings](),
function[ST_Translate](0.0),
function[ST_FrechetDistance](),
// Expression for rasters
function[RS_NormalizedDifference](),
function[RS_Mean](),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1010,3 +1010,10 @@ case class ST_Translate(inputExpressions: Seq[Expression])
}
}

case class ST_FrechetDistance(inputExpressions: Seq[Expression])
extends InferredBinaryExpression(Functions.frechetDistance) with FoldableExpression {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -326,5 +326,8 @@ object st_functions extends DataFrameAPI {

def ST_Translate(geometry: String, deltaX: Double, deltaY: Double): Column = wrapExpression[ST_Translate](geometry, deltaX, deltaY, 0.0)

def ST_FrechetDistance(g1: Column, g2: Column): Column = wrapExpression[ST_FrechetDistance](g1, g2)

def ST_FrechetDistance(g1: String, g2: String): Column = wrapExpression[ST_FrechetDistance](g1, g2)

}
Original file line number Diff line number Diff line change
Expand Up @@ -994,5 +994,13 @@ class dataFrameAPITestScala extends TestBaseScala {
val expectedDefaultValue = "POLYGON Z((3 3 1, 3 4 1, 4 4 1, 4 3 1, 3 3 1))"
assert(expectedDefaultValue == actualDefaultValue)
}

it("Passed ST_FrechetDistance") {
val polyDf = sparkSession.sql("SELECT ST_GeomFromWKT('POINT (1 2)') as g1, ST_GeomFromWKT('POINT (100 230)') as g2")
val df = polyDf.select(ST_FrechetDistance("g1", "g2"))
val expected = 248.5658866377283
val actual = df.take(1)(0).get(0).asInstanceOf[Double]
assertEquals(expected, actual, 1e-9)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1973,4 +1973,19 @@ class functionTestScala extends TestBaseScala with Matchers with GeometrySample
assertEquals(expectedDefaultValue, actualDefaultValue)
}
}

it ("should pass ST_FrechetDistance") {
val geomTestCases = Map (
("'POINT (1 2)'", "'POINT (10 10)'") -> 12.041594578792296d,
("'LINESTRING (0 0, 100 0)'", "'LINESTRING (0 0, 50 50, 100 0)'") -> 70.7106781186548d
)
for (((geom), expectedResult) <- geomTestCases) {
val g1 = geom._1
val g2 = geom._2
val df = sparkSession.sql(s"SELECT ST_FrechetDistance(ST_GeomFromWKT($g1), ST_GeomFromWKT($g2))")
val actual = df.take(1)(0).get(0).asInstanceOf[Double]
val expected = expectedResult
assertEquals(expected, actual, 1e-9)
}
}
}

0 comments on commit 27c9673

Please sign in to comment.