This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sedona.git
The following commit(s) were added to refs/heads/master by this push:
new 3a7373395 [SEDONA-659] Upgrade JTS version to 1.20.0 (#1655)
3a7373395 is described below
commit 3a73733953b9b62341a7ef14968f33affac6b6f9
Author: Kristin Cowalcijk <[email protected]>
AuthorDate: Fri Oct 25 23:52:30 2024 +0800
[SEDONA-659] Upgrade JTS version to 1.20.0 (#1655)
* bump jts to 1.20.0
* Fix tests for jts 1.20.0
* Fix python tests for jts 1.20.0, removed unnecessary print statements
from the python test code.
* Fix test failures
* Fix type annotation for ST_Rotate
---------
Co-authored-by: jameswillis <[email protected]>
---
.../java/org/apache/sedona/common/Functions.java | 2 +-
.../sedona/common/utils/InscribedCircle.java | 11 +++
.../org/apache/sedona/common/FunctionsTest.java | 45 ++++++------
.../java/org/apache/sedona/common/TestBase.java | 24 +++++++
.../java/org/apache/sedona/flink/FunctionTest.java | 12 ++--
.../java/org/apache/sedona/flink/TestBase.java | 21 ++++++
pom.xml | 2 +-
python/sedona/sql/st_functions.py | 6 +-
python/tests/core/test_spatial_rdd_from_disc.py | 5 --
python/tests/format_mapper/test_wkt_reader.py | 1 -
.../tests/spatial_operator/test_rectangle_knn.py | 4 +-
python/tests/spatial_rdd/test_linestring_rdd.py | 3 -
python/tests/spatial_rdd/test_polygon_rdd.py | 3 -
python/tests/sql/test_dataframe_api.py | 3 +-
python/tests/sql/test_function.py | 81 ++++++++--------------
.../streaming/spark/test_constructor_functions.py | 2 +-
python/tests/test_base.py | 30 ++++++++
.../apache/sedona/snowflake/snowsql/TestBase.java | 20 ++++++
.../sedona/snowflake/snowsql/TestFunctions.java | 16 ++---
.../sedona/snowflake/snowsql/TestFunctionsV2.java | 16 ++---
.../snowflake/snowsql/TestTableFunctions.java | 12 ++--
.../org/apache/sedona/sql/TestBaseScala.scala | 40 +++++++++++
.../apache/sedona/sql/dataFrameAPITestScala.scala | 32 +++------
.../org/apache/sedona/sql/functionTestScala.scala | 20 ++----
.../sedona/sql/functions/TestStSubDivide.scala | 12 +++-
25 files changed, 259 insertions(+), 164 deletions(-)
diff --git a/common/src/main/java/org/apache/sedona/common/Functions.java
b/common/src/main/java/org/apache/sedona/common/Functions.java
index 26434a228..9c01cbb5f 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -1050,7 +1050,7 @@ public class Functions {
// All non-polygonal geometries use LargestEmptyCircle
if (!geometry.getClass().getSimpleName().equals("Polygon")
&& !geometry.getClass().getSimpleName().equals("MultiPolygon")) {
- LargestEmptyCircle largestEmptyCircle = new LargestEmptyCircle(geometry,
tolerance);
+ LargestEmptyCircle largestEmptyCircle = new LargestEmptyCircle(geometry,
null, tolerance);
center = largestEmptyCircle.getCenter();
nearest = largestEmptyCircle.getRadiusPoint();
radius = largestEmptyCircle.getRadiusLine().getLength();
diff --git
a/common/src/main/java/org/apache/sedona/common/utils/InscribedCircle.java
b/common/src/main/java/org/apache/sedona/common/utils/InscribedCircle.java
index 059e97b54..b669f9f2c 100644
--- a/common/src/main/java/org/apache/sedona/common/utils/InscribedCircle.java
+++ b/common/src/main/java/org/apache/sedona/common/utils/InscribedCircle.java
@@ -45,4 +45,15 @@ public class InscribedCircle {
&& this.nearest.equals(other.nearest)
&& Math.abs(this.radius - other.radius) < epsilon;
}
+
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null || obj.getClass() != this.getClass()) {
+ return false;
+ }
+ InscribedCircle other = (InscribedCircle) obj;
+ return this.equals(other);
+ }
}
diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
index b554339c9..926aa77dc 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -1598,7 +1598,7 @@ public class FunctionsTest extends TestBase {
String actual = Functions.delaunayTriangle(combined).toText();
String expected =
"GEOMETRYCOLLECTION (POLYGON ((20 40, 125 100, 50 60, 20 40)), POLYGON
((20 40, 50 60, 110 170, 20 40)), POLYGON ((110 170, 50 60, 125 100, 110 170)),
POLYGON ((110 170, 125 100, 175 150, 110 170)))";
- assertEquals(expected, actual);
+ assertGeometryEquals(expected, actual);
poly =
Constructors.geomFromEWKT(
@@ -1610,7 +1610,7 @@ public class FunctionsTest extends TestBase {
actual = Functions.delaunayTriangle(poly, 0, 1).toText();
expected =
"MULTILINESTRING ((25 20, 35 20), (20 20, 25 20), (10 20, 20 20), (10
10, 10 20), (10 10, 20 10), (20 10, 25 10), (25 10, 35 10), (35 10, 35 20), (25
20, 35 10), (25 10, 25 20), (20 20, 25 10), (20 10, 20 20), (10 20, 20 10))";
- assertEquals(expected, actual);
+ assertGeometryEquals(expected, actual);
}
@Test
@@ -1802,14 +1802,11 @@ public class FunctionsTest extends TestBase {
expectedPoints = 17;
assertEquals(expectedPoints, actualPoints);
- actualPoints = Functions.nPoints(Functions.simplify(geom, 1));
+ Geometry actual = Functions.simplify(geom, 1);
+ actualPoints = Functions.nPoints(actual);
expectedPoints = 9;
assertEquals(expectedPoints, actualPoints);
- Geometry actual = Functions.simplify(geom, 10);
- actualPoints = Functions.nPoints(actual);
- expectedPoints = 4;
- assertEquals(expectedPoints, actualPoints);
assertEquals(1111, actual.getSRID());
}
@@ -3733,22 +3730,22 @@ public class FunctionsTest extends TestBase {
public void voronoiPolygons() {
MultiPoint multiPoint =
GEOMETRY_FACTORY.createMultiPointFromCoords(coordArray(0, 0, 2, 2));
Geometry actual1 = FunctionsGeoTools.voronoiPolygons(multiPoint, 0, null);
- assertEquals(
+ assertGeometryEquals(
"GEOMETRYCOLLECTION (POLYGON ((-2 -2, -2 4, 4 -2, -2 -2)), POLYGON
((-2 4, 4 4, 4 -2, -2 4)))",
actual1.toText());
Geometry actual2 = FunctionsGeoTools.voronoiPolygons(multiPoint, 30, null);
- assertEquals(
+ assertGeometryEquals(
"GEOMETRYCOLLECTION (POLYGON ((-2 -2, -2 4, 4 4, 4 -2, -2 -2)))",
actual2.toText());
Geometry buf = Functions.buffer(GEOMETRY_FACTORY.createPoint(new
Coordinate(1, 1)), 10);
Geometry actual3 = FunctionsGeoTools.voronoiPolygons(multiPoint, 0, buf);
- assertEquals(
+ assertGeometryEquals(
"GEOMETRYCOLLECTION (POLYGON ((-9 -9, -9 11, 11 -9, -9 -9)), POLYGON
((-9 11, 11 11, 11 -9, -9 11)))",
actual3.toText());
Geometry actual4 = FunctionsGeoTools.voronoiPolygons(multiPoint, 30, buf);
- assertEquals(
+ assertGeometryEquals(
"GEOMETRYCOLLECTION (POLYGON ((-9 -9, -9 11, 11 11, 11 -9, -9 -9)))",
actual4.toText());
Geometry actual5 = FunctionsGeoTools.voronoiPolygons(null, 0, null);
@@ -3827,10 +3824,10 @@ public class FunctionsTest extends TestBase {
InscribedCircle actual = Functions.maximumInscribedCircle(geom);
InscribedCircle expected =
new InscribedCircle(
- Constructors.geomFromEWKT("POINT (96.953125 76.328125)"),
- Constructors.geomFromEWKT("POINT (140 90)"),
- 45.165846);
- assertTrue(expected.equals(actual));
+ Constructors.geomFromEWKT("POINT (96.9287109375 76.3232421875)"),
+ Constructors.geomFromEWKT("POINT (61.64205411585366
104.55256764481707)"),
+ 45.18896951053177);
+ assertEquals(expected, actual);
geom =
Constructors.geomFromEWKT(
@@ -3838,9 +3835,9 @@ public class FunctionsTest extends TestBase {
actual = Functions.maximumInscribedCircle(geom);
expected =
new InscribedCircle(
- Constructors.geomFromEWKT("POINT (65.0419921875 15.1005859375)"),
- Constructors.geomFromEWKT("POINT (65 17)"),
- 1.8998781);
+ Constructors.geomFromEWKT("POINT (65.043212890625
15.098388671875)"),
+ Constructors.geomFromEWKT("POINT (66.52827267530488
13.910340844131097)"),
+ 1.9018044602641058);
assertTrue(expected.equals(actual));
geom =
@@ -3849,9 +3846,9 @@ public class FunctionsTest extends TestBase {
actual = Functions.maximumInscribedCircle(geom);
expected =
new InscribedCircle(
- Constructors.geomFromEWKT("POINT (65.44062499999998
15.953124999999998)"),
- Constructors.geomFromEWKT("POINT (67.5 16.9)"),
- 2.2666269);
+ Constructors.geomFromEWKT("POINT (65.44223632812499
15.945361328125001)"),
+ Constructors.geomFromEWKT("POINT (67.4 14.8)"),
+ 2.2681911662992174);
assertTrue(expected.equals(actual));
geom = Constructors.geomFromEWKT("MULTIPOINT ((60.8 15.5), (63.2 16.3))");
@@ -3869,9 +3866,9 @@ public class FunctionsTest extends TestBase {
actual = Functions.maximumInscribedCircle(geom);
expected =
new InscribedCircle(
- Constructors.geomFromEWKT("POINT (65.44062499999998
15.953124999999998)"),
- Constructors.geomFromEWKT("POINT (67.5 16.9)"),
- 2.2666269);
+ Constructors.geomFromEWKT("POINT (65.44223632812499
15.945361328125001)"),
+ Constructors.geomFromEWKT("POINT (67.4 14.8)"),
+ 2.2681911662992174);
assertTrue(expected.equals(actual));
}
diff --git a/common/src/test/java/org/apache/sedona/common/TestBase.java
b/common/src/test/java/org/apache/sedona/common/TestBase.java
index c3ddaebf6..b44ac3878 100644
--- a/common/src/test/java/org/apache/sedona/common/TestBase.java
+++ b/common/src/test/java/org/apache/sedona/common/TestBase.java
@@ -18,7 +18,11 @@
*/
package org.apache.sedona.common;
+import static org.junit.Assert.fail;
+
import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.io.WKTReader;
public class TestBase {
public Coordinate[] coordArray(double... coordValues) {
@@ -37,4 +41,24 @@ public class TestBase {
}
return coords;
}
+
+ public void assertGeometryEquals(String expectedWkt, String actualWkt,
double tolerance) {
+ WKTReader reader = new WKTReader();
+ try {
+ Geometry expectedGeom = reader.read(expectedWkt);
+ Geometry actualGeom = reader.read(actualWkt);
+ if (!expectedGeom.equals(actualGeom)) {
+ if (!expectedGeom.buffer(tolerance).contains(actualGeom)
+ || !actualGeom.buffer(tolerance).contains(expectedGeom)) {
+ fail(String.format("Geometry %s should equal to %s", actualWkt,
expectedWkt));
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void assertGeometryEquals(String expectedWkt, String actualWkt) {
+ assertGeometryEquals(expectedWkt, actualWkt, 1e-6);
+ }
}
diff --git a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
index cc1a137a3..ca9b8c190 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -277,14 +277,14 @@ public class FunctionTest extends TestBase {
polygonTable.select(
call(Functions.ST_ConcaveHull.class.getSimpleName(), $("geom"),
1.0, true));
Geometry result = (Geometry) first(concaveHullPolygonTable).getField(0);
- assertEquals("POLYGON ((1 2, 2 2, 3 2, 5 0, 4 0, 1 0, 0 0, 1 2))",
result.toString());
+ assertGeometryEquals("POLYGON ((1 2, 2 2, 3 2, 5 0, 4 0, 1 0, 0 0, 1 2))",
result.toString());
Table polygonTable2 =
tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POLYGON ((0 0, 1 0, 1 1, 0
0))') as geom");
Table concaveHullPolygonTable2 =
polygonTable2.select(call(Functions.ST_ConcaveHull.class.getSimpleName(),
$("geom"), 1.0));
Geometry result2 = (Geometry) first(concaveHullPolygonTable2).getField(0);
- assertEquals("POLYGON ((0 0, 1 1, 1 0, 0 0))", result2.toString());
+ assertGeometryEquals("POLYGON ((0 0, 1 1, 1 0, 0 0))", result2.toString());
}
@Test
@@ -2358,7 +2358,7 @@ public class FunctionTest extends TestBase {
Table polyTable1 =
tableEnv.sqlQuery("SELECT
ST_VoronoiPolygons(ST_GeomFromWKT('MULTIPOINT ((0 0), (2 2))'))");
Geometry result = (Geometry) first(polyTable1).getField(0);
- assertEquals(
+ assertGeometryEquals(
"GEOMETRYCOLLECTION (POLYGON ((-2 -2, -2 4, 4 -2, -2 -2)), POLYGON
((-2 4, 4 4, 4 -2, -2 4)))",
result.toString());
@@ -2366,7 +2366,7 @@ public class FunctionTest extends TestBase {
tableEnv.sqlQuery(
"SELECT ST_VoronoiPolygons(ST_GeomFromWKT('MULTIPOINT ((0 0), (2
2))'), 0, ST_Buffer(ST_GeomFromWKT('POINT(1 1)'), 10.0) )");
result = (Geometry) first(polyTable2).getField(0);
- assertEquals(
+ assertGeometryEquals(
"GEOMETRYCOLLECTION (POLYGON ((-9 -9, -9 11, 11 -9, -9 -9)), POLYGON
((-9 11, 11 11, 11 -9, -9 11)))",
result.toString());
@@ -2374,14 +2374,14 @@ public class FunctionTest extends TestBase {
tableEnv.sqlQuery(
"SELECT ST_VoronoiPolygons(ST_GeomFromWKT('MULTIPOINT ((0 0), (2
2))'), 30)");
result = (Geometry) first(polyTable3).getField(0);
- assertEquals(
+ assertGeometryEquals(
"GEOMETRYCOLLECTION (POLYGON ((-2 -2, -2 4, 4 4, 4 -2, -2 -2)))",
result.toString());
Table polyTable4 =
tableEnv.sqlQuery(
"SELECT ST_VoronoiPolygons(ST_GeomFromWKT('MULTIPOINT ((0 0), (2
2))'), 30, ST_Buffer(ST_GeomFromWKT('POINT(1 1)'), 10) )");
result = (Geometry) first(polyTable4).getField(0);
- assertEquals(
+ assertGeometryEquals(
"GEOMETRYCOLLECTION (POLYGON ((-9 -9, -9 11, 11 11, 11 -9, -9 -9)))",
result.toString());
Table polyTable5 =
diff --git a/flink/src/test/java/org/apache/sedona/flink/TestBase.java
b/flink/src/test/java/org/apache/sedona/flink/TestBase.java
index c77b6f646..4506d798e 100644
--- a/flink/src/test/java/org/apache/sedona/flink/TestBase.java
+++ b/flink/src/test/java/org/apache/sedona/flink/TestBase.java
@@ -19,6 +19,7 @@
package org.apache.sedona.flink;
import static org.apache.flink.table.api.Expressions.*;
+import static org.junit.Assert.fail;
import com.google.common.math.DoubleMath;
import java.sql.Timestamp;
@@ -483,4 +484,24 @@ public class TestBase {
}
return count;
}
+
+ public void assertGeometryEquals(String expectedWkt, String actualWkt,
double tolerance) {
+ WKTReader reader = new WKTReader();
+ try {
+ Geometry expectedGeom = reader.read(expectedWkt);
+ Geometry actualGeom = reader.read(actualWkt);
+ if (!expectedGeom.equals(actualGeom)) {
+ if (!expectedGeom.buffer(tolerance).contains(actualGeom)
+ || !actualGeom.buffer(tolerance).contains(expectedGeom)) {
+ fail(String.format("Geometry %s should equal to %s", actualWkt,
expectedWkt));
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void assertGeometryEquals(String expectedWkt, String actualWkt) {
+ assertGeometryEquals(expectedWkt, actualWkt, 1e-6);
+ }
}
diff --git a/pom.xml b/pom.xml
index d512b2ccc..1f9a296c0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,7 +71,7 @@
<geotools.version>28.2</geotools.version>
<hadoop.version>3.2.4</hadoop.version>
<jackson.version>2.13.4</jackson.version>
- <jts.version>1.19.0</jts.version>
+ <jts.version>1.20.0</jts.version>
<jts2geojson.version>0.16.1</jts2geojson.version>
<spatial4j.version>0.8</spatial4j.version>
diff --git a/python/sedona/sql/st_functions.py
b/python/sedona/sql/st_functions.py
index e766c33fd..22fd37358 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -2364,9 +2364,9 @@ def ST_RotateY(geometry: ColumnOrName, angle:
Union[ColumnOrName, float]) -> Col
def ST_Rotate(
geometry: ColumnOrName,
angle: Union[ColumnOrName, float],
- originX: Union[ColumnOrName, float] = None,
- originY: Union[ColumnOrName, float] = None,
- pointOrigin: ColumnOrName = None,
+ originX: Optional[Union[ColumnOrName, float]] = None,
+ originY: Optional[Union[ColumnOrName, float]] = None,
+ pointOrigin: Optional[ColumnOrName] = None,
) -> Column:
"""Return a counter-clockwise rotated geometry along the specified origin.
diff --git a/python/tests/core/test_spatial_rdd_from_disc.py
b/python/tests/core/test_spatial_rdd_from_disc.py
index ff363dc6a..f3f3cbabf 100644
--- a/python/tests/core/test_spatial_rdd_from_disc.py
+++ b/python/tests/core/test_spatial_rdd_from_disc.py
@@ -147,7 +147,6 @@ class TestDiscUtils(TestBase):
assert point_rdd.indexedRawRDD is not None
assert isinstance(point_rdd, PointRDD)
point_rdd.analyze()
- print(point_rdd.boundaryEnvelope)
polygon_rdd = load_spatial_rdd_from_disc(
self.sc, os.path.join(disc_location, "polygon"), GeoType.POLYGON
@@ -158,8 +157,6 @@ class TestDiscUtils(TestBase):
polygon_rdd.indexedRawRDD = polygon_index_rdd
polygon_rdd.analyze()
- print(polygon_rdd.boundaryEnvelope)
-
assert polygon_rdd.indexedRawRDD is not None
assert isinstance(polygon_rdd, PolygonRDD)
@@ -175,7 +172,6 @@ class TestDiscUtils(TestBase):
assert isinstance(linestring_rdd, LineStringRDD)
linestring_rdd.analyze()
- print(linestring_rdd.boundaryEnvelope)
linestring_rdd.spatialPartitioning(GridType.KDBTREE)
polygon_rdd.spatialPartitioning(linestring_rdd.getPartitioner())
@@ -186,5 +182,4 @@ class TestDiscUtils(TestBase):
linestring_rdd, polygon_rdd, True, True
).collect()
- print(result)
remove_directory(disc_location)
diff --git a/python/tests/format_mapper/test_wkt_reader.py
b/python/tests/format_mapper/test_wkt_reader.py
index 56ace0347..ec64158fe 100644
--- a/python/tests/format_mapper/test_wkt_reader.py
+++ b/python/tests/format_mapper/test_wkt_reader.py
@@ -29,4 +29,3 @@ class TestWktReader(TestBase):
wkt_geometries = os.path.join(tests_resource, "county_small.tsv")
wkt_rdd = WktReader.readToGeometryRDD(self.sc, wkt_geometries, 0,
True, False)
assert wkt_rdd.rawSpatialRDD.count() == 103
- print(wkt_rdd.rawSpatialRDD.collect())
diff --git a/python/tests/spatial_operator/test_rectangle_knn.py
b/python/tests/spatial_operator/test_rectangle_knn.py
index 36d54a9d2..793f30f20 100644
--- a/python/tests/spatial_operator/test_rectangle_knn.py
+++ b/python/tests/spatial_operator/test_rectangle_knn.py
@@ -119,7 +119,7 @@ class TestRectangleKNN(TestBase):
rectangle_rdd, self.query_polygon, self.top_k, False
)
- print(result_no_index)
+ assert len(result_no_index) > 0
def test_spatial_knn_using_linestring(self):
rectangle_rdd = RectangleRDD(self.sc, inputLocation, offset, splitter,
True)
@@ -128,4 +128,4 @@ class TestRectangleKNN(TestBase):
rectangle_rdd, self.query_line, self.top_k, False
)
- print(result_no_index)
+ assert len(result_no_index) > 0
diff --git a/python/tests/spatial_rdd/test_linestring_rdd.py
b/python/tests/spatial_rdd/test_linestring_rdd.py
index 4820ad0c6..04ff76a09 100644
--- a/python/tests/spatial_rdd/test_linestring_rdd.py
+++ b/python/tests/spatial_rdd/test_linestring_rdd.py
@@ -129,7 +129,4 @@ class TestLineStringRDD(TestBase):
rectangle_rdd = linestring_rdd.MinimumBoundingRectangle()
result = rectangle_rdd.rawSpatialRDD.collect()
- for el in result:
- print(el)
-
assert result.__len__() > -1
diff --git a/python/tests/spatial_rdd/test_polygon_rdd.py
b/python/tests/spatial_rdd/test_polygon_rdd.py
index f0bc46331..a038f84ab 100644
--- a/python/tests/spatial_rdd/test_polygon_rdd.py
+++ b/python/tests/spatial_rdd/test_polygon_rdd.py
@@ -188,7 +188,4 @@ class TestPolygonRDD(TestBase):
result = rectangle_rdd.rawSpatialRDD.collect()
- for el in result:
- print(el.geom.wkt)
- print(result)
assert result.__len__() > -1
diff --git a/python/tests/sql/test_dataframe_api.py
b/python/tests/sql/test_dataframe_api.py
index 3d40267a8..ca37349cb 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -1653,7 +1653,8 @@ class TestDataFrameAPI(TestBase):
actual_result = df.collect()[0][0]
if isinstance(actual_result, BaseGeometry):
- actual_result = actual_result.wkt
+ self.assert_geometry_almost_equal(expected_result, actual_result)
+ return
elif isinstance(actual_result, bytearray):
actual_result = actual_result.hex()
elif isinstance(actual_result, Row):
diff --git a/python/tests/sql/test_function.py
b/python/tests/sql/test_function.py
index b83343a0a..ec21d853b 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -18,6 +18,7 @@
import math
from typing import List
+import pytest
from pyspark.sql import DataFrame, Row
from pyspark.sql.functions import col, explode, expr
from pyspark.sql.types import IntegerType, StructField, StructType
@@ -98,57 +99,30 @@ class TestPredicateJoin(TestBase):
function_df.show()
def test_st_buffer(self):
- polygon_from_wkt = (
- self.spark.read.format("csv")
- .option("delimiter", "\t")
- .option("header", "false")
- .load(mixed_wkt_geometry_input_location)
- )
-
- polygon_from_wkt.createOrReplaceTempView("polygontable")
- polygon_from_wkt.show()
-
- polygon_df = self.spark.sql(
- "select ST_GeomFromWKT(polygontable._c0) as countyshape from
polygontable"
- )
- polygon_df.createOrReplaceTempView("polygondf")
- polygon_df.show()
-
- function_df = self.spark.sql(
- "select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 1), 2)
from polygondf"
- )
- actual = function_df.take(1)[0][0].wkt
- assert (
- actual
- == "POLYGON ((-98.02 41.77, -98.02 41.78, -98.02 41.8, -98.02
41.81, -98.02 41.82, -98.02 41.83, -98.02 41.84, -98.02 41.85, -98.02 41.86,
-98.02 41.87, -98.02 41.89, -98.02 41.9, -98.02 41.91, -98.02 41.92, -98.02
41.93, -98.02 41.95, -98.02 41.98, -98.02 42, -98.02 42.01, -98.02 42.02,
-98.02 42.04, -98.02 42.05, -98.02 42.07, -98.02 42.09, -98.02 42.11, -98.02
42.12, -97.99 42.31, -97.93 42.5, -97.84 42.66, -97.72 42.81, -97.57 42.93,
-97.4 43.02, -97.21 43.07, -97.02 43.0 [...]
- )
+ geom_expr = "ST_GeomFromWKT('LINESTRING(0 0, 10 10)')"
+ function_df = self.spark.sql(f"select ST_Buffer({geom_expr}, 1)")
+ actual = function_df.take(1)[0][0]
+ expected = "POLYGON ((9.292893218813452 10.707106781186548,
9.444429766980399 10.831469612302545, 9.61731656763491 10.923879532511286,
9.804909677983872 10.98078528040323, 10 11, 10.195090322016128
10.98078528040323, 10.38268343236509 10.923879532511286, 10.555570233019603
10.831469612302545, 10.707106781186548 10.707106781186548, 10.831469612302545
10.555570233019601, 10.923879532511286 10.38268343236509, 10.98078528040323
10.195090322016128, 11 10, 10.98078528040323 9.804909677 [...]
+ self.assert_geometry_almost_equal(expected, actual, 0.1)
- function_df = self.spark.sql(
- "select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 1,
true), 2) from polygondf"
- )
- actual = function_df.take(1)[0][0].wkt
- assert (
- actual
- == "POLYGON ((-97.02 42.01, -97.02 42.02, -97.02 42.03, -97.02
42.04, -97.02 42.05, -97.02 42.06, -97.02 42.07, -97.02 42.08, -97.02 42.09,
-97.01 42.09, -97 42.09, -96.99 42.09, -96.98 42.09, -96.97 42.09, -96.96
42.09, -96.95 42.09, -96.94 42.09, -96.93 42.09, -96.92 42.09, -96.91 42.09,
-96.9 42.09, -96.89 42.09, -96.88 42.09, -96.87 42.09, -96.86 42.09, -96.85
42.09, -96.84 42.09, -96.83 42.09, -96.82 42.09, -96.81 42.09, -96.8 42.09,
-96.79 42.09, -96.78 42.09, -96.77 42 [...]
- )
+ function_df = self.spark.sql(f"select ST_Buffer({geom_expr}, 1, true)")
+ actual = function_df.take(1)[0][0]
+ expected = "POLYGON ((9.999993652864127 10.0000063012992,
9.999995015003332 10.000007407644903, 9.999996568713167 10.00000822931893,
9.999998254285428 10.00000873474483, 10.000000006944521 10.000008904499353,
10.000001759336742 10.000008732058928, 10.000003444118638 10.000008224050353,
10.000004996544984 10.000007399996127, 10.000006356956911 10.000006291564157,
10.000007473074547 10.000004941350873, 10.00000830200612 10.000003401244212,
10.000008811896254 10.000001730429604, 10. [...]
+ self.assert_geometry_almost_equal(expected, actual, 0.1)
function_df = self.spark.sql(
- "select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 10,
false, 'endcap=square'), 2) from polygondf"
- )
- actual = function_df.take(1)[0][0].wkt
- assert (
- actual
- == "POLYGON ((-107.02 42.06, -107.02 42.07, -107.02 42.09, -107.02
42.11, -107.02 42.32, -107.02 42.33, -107.01 42.42, -107.01 42.43, -106.77
44.33, -106.16 46.15, -105.22 47.82, -103.98 49.27, -102.48 50.47, -100.78
51.36, -98.94 51.9, -97.04 52.09, -97.03 52.09, -97.01 52.09, -96.95 52.09,
-96.9 52.09, -96.81 52.09, -96.7 52.09, -96.68 52.09, -96.65 52.09, -96.55
52.09, -96.54 52.09, -96.49 52.09, -96.48 52.09, -94.58 51.89, -92.74 51.33,
-91.04 50.43, -89.55 49.23, -88.32 [...]
+ f"select ST_Buffer({geom_expr}, 10, false, 'endcap=square')"
)
+ actual = function_df.take(1)[0][0]
+ expected = "POLYGON ((2.9289321881345254 17.071067811865476, 10
24.14213562373095, 24.14213562373095 10, 7.071067811865475 -7.071067811865475,
0 -14.142135623730951, -14.14213562373095 -0.0000000000000009,
2.9289321881345254 17.071067811865476))"
+ self.assert_geometry_almost_equal(expected, actual, 0.1)
function_df = self.spark.sql(
- "select ST_ReducePrecision(ST_Buffer(polygondf.countyshape, 10,
true, 'endcap=square'), 2) from polygondf"
- )
- actual = function_df.take(1)[0][0].wkt
- assert (
- actual
- == "POLYGON ((-97.02 42.01, -97.02 42.02, -97.02 42.03, -97.02
42.04, -97.02 42.05, -97.02 42.06, -97.02 42.07, -97.02 42.08, -97.02 42.09,
-97.01 42.09, -97 42.09, -96.99 42.09, -96.98 42.09, -96.97 42.09, -96.96
42.09, -96.95 42.09, -96.94 42.09, -96.93 42.09, -96.92 42.09, -96.91 42.09,
-96.9 42.09, -96.89 42.09, -96.88 42.09, -96.87 42.09, -96.86 42.09, -96.85
42.09, -96.84 42.09, -96.83 42.09, -96.82 42.09, -96.81 42.09, -96.8 42.09,
-96.79 42.09, -96.78 42.09, -96.77 42 [...]
+ f"select ST_Buffer({geom_expr}, 10, true, 'endcap=square')"
)
+ actual = function_df.take(1)[0][0]
+ expected = "POLYGON ((9.999936528641255 10.000063012993312,
10.000000098210357 10.00012592862454, 10.000127040927849 9.999999902648744,
0.0000634713587459 -0.0000639979767969, -0.0000000982103557
-0.0001278970813952, -0.0001270409278476 0.0000000988678222, 9.999936528641255
10.000063012993312))"
+ self.assert_geometry_almost_equal(expected, actual, 0.1)
def test_st_bestsrid(self):
polygon_from_wkt = (
@@ -450,12 +424,13 @@ class TestPredicateJoin(TestBase):
"SELECT ST_GeomFromWKT('POLYGON ((40 180, 110 160, 180 180, 180
120, 140 90, 160 40, 80 10, 70 40, 20 50, 40 180),(60 140, 50 90, 90 140, 60
140))') AS geom"
)
actual =
baseDf.selectExpr("ST_MaximumInscribedCircle(geom)").take(1)[0][0]
- center = actual.center.wkt
- assert center == "POINT (96.953125 76.328125)"
- nearest = actual.nearest.wkt
- assert nearest == "POINT (140 90)"
- radius = actual.radius
- assert radius == 45.165845650018
+ self.assert_geometry_almost_equal(
+ "POINT (96.9287109375 76.3232421875)", actual.center
+ )
+ self.assert_geometry_almost_equal(
+ "POINT (61.64205411585366 104.55256764481707)", actual.nearest
+ )
+ assert actual.radius == pytest.approx(45.18896951053177, 1e-6)
def test_st_is_valid_detail(self):
baseDf = self.spark.sql(
@@ -471,7 +446,7 @@ class TestPredicateJoin(TestBase):
actual = baseDf.selectExpr("ST_IsValidDetail(geom)").first()[0]
expected = Row(
valid=False,
- reason="Ring Self-intersection at or near point (1.0, 1.0, NaN)",
+ reason="Ring Self-intersection at or near point (1.0, 1.0)",
location=self.spark.sql("SELECT ST_GeomFromText('POINT (1
1)')").first()[0],
)
assert expected == actual
@@ -2384,8 +2359,8 @@ class TestPredicateJoin(TestBase):
actual_df = self.spark.sql(
"SELECT ST_VoronoiPolygons(ST_GeomFromText('MULTIPOINT (0 0, 2
2)')) AS geom"
)
- actual = actual_df.selectExpr("ST_AsText(geom)").take(1)[0][0]
- assert expected == actual
+ actual = actual_df.take(1)[0][0]
+ self.assert_geometry_almost_equal(expected, actual)
def test_frechetDistance(self):
expected = 5.0990195135927845
diff --git a/python/tests/streaming/spark/test_constructor_functions.py
b/python/tests/streaming/spark/test_constructor_functions.py
index 30ca829c4..282150be5 100644
--- a/python/tests/streaming/spark/test_constructor_functions.py
+++ b/python/tests/streaming/spark/test_constructor_functions.py
@@ -235,7 +235,7 @@ SEDONA_LISTED_SQL_FUNCTIONS = [
SuiteContainer.empty()
.with_function_name("ST_SimplifyPreserveTopology")
.with_arguments(
- ["ST_GeomFromText('POLYGON ((21 53, 22 53, 22 52, 21 52, 21
53))')", "1.0"]
+ ["ST_GeomFromText('POLYGON ((21 53, 22 53, 22 52, 21 52, 21
53))')", "0.5"]
)
.with_expected_result(1)
.with_transform("ST_AREA")
diff --git a/python/tests/test_base.py b/python/tests/test_base.py
index 4bfbb86b0..d600c45f5 100644
--- a/python/tests/test_base.py
+++ b/python/tests/test_base.py
@@ -16,6 +16,7 @@
# under the License.
import os
from tempfile import mkdtemp
+from typing import Union
import pyspark
@@ -24,6 +25,9 @@ from sedona.utils.decorators import classproperty
SPARK_REMOTE = os.getenv("SPARK_REMOTE")
+from shapely import wkt
+from shapely.geometry.base import BaseGeometry
+
class TestBase:
@@ -60,3 +64,29 @@ class TestBase:
if not hasattr(self, "__spark"):
setattr(self, "__sc", self.spark._sc)
return getattr(self, "__sc")
+
+ @classmethod
+ def assert_geometry_almost_equal(
+ cls,
+ left_geom: Union[str, BaseGeometry],
+ right_geom: Union[str, BaseGeometry],
+ tolerance=1e-6,
+ ):
+ expected_geom = (
+ wkt.loads(left_geom) if isinstance(left_geom, str) else left_geom
+ )
+ actual_geom = (
+ wkt.loads(right_geom) if isinstance(right_geom, str) else
right_geom
+ )
+
+ if not actual_geom.equals_exact(expected_geom, tolerance=tolerance):
+ # If the exact equals check fails, perform a buffer check with
tolerance
+ if actual_geom.buffer(tolerance).contains(
+ expected_geom
+ ) and expected_geom.buffer(tolerance).contains(actual_geom):
+ return
+ else:
+ # fail the test with error message
+ raise ValueError(
+ f"Geometry equality check failed for {left_geom} and
{right_geom}"
+ )
diff --git
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestBase.java
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestBase.java
index a991c526c..2d226beec 100644
---
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestBase.java
+++
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestBase.java
@@ -205,6 +205,26 @@ public class TestBase extends TestCase {
}
}
+ public void verifyGeometry(String sql, String expectWkt, double tolerance) {
+ try {
+ ResultSet res = snowClient.executeQuery(sql);
+ res.next();
+ Geometry e = Constructors.geomFromWKT(expectWkt, 0);
+ Geometry a = Constructors.geomFromWKT(res.getString(1), 0);
+ if (!e.equals(a)) {
+ if (!e.buffer(tolerance).contains(a) ||
!a.buffer(tolerance).contains(e)) {
+ fail(String.format("sql result %s does not equal to %s", a, e));
+ }
+ }
+ } catch (SQLException | ParseException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void verifyGeometry(String sql, String expectWkt) {
+ verifyGeometry(sql, expectWkt, 1e-6);
+ }
+
public ResultSet sqlSingleRes(String sql) {
try {
ResultSet res = snowClient.executeQuery(sql);
diff --git
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
index f4271b8fa..157994b32 100644
---
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
+++
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
@@ -216,7 +216,7 @@ public class TestFunctions extends TestBase {
@Test
public void test_ST_Buffer() {
registerUDF("ST_Buffer", byte[].class, double.class);
- verifySqlSingleRes(
+ verifyGeometry(
"select
sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('POINT (0 1)'), 1))",
"POLYGON ((1 1, 0.9807852804032304 0.8049096779838718,
0.9238795325112867 0.6173165676349102, 0.8314696123025452 0.4444297669803978,
0.7071067811865476 0.2928932188134525, 0.5555702330196023 0.1685303876974548,
0.3826834323650898 0.0761204674887133, 0.1950903220161283 0.0192147195967696,
0.0000000000000001 0, -0.1950903220161282 0.0192147195967696,
-0.3826834323650897 0.0761204674887133, -0.555570233019602 0.1685303876974547,
-0.7071067811865475 0.2928932188134524, -0.83146961230 [...]
registerUDF("ST_Buffer", byte[].class, double.class, boolean.class);
@@ -277,10 +277,10 @@ public class TestFunctions extends TestBase {
public void test_ST_ConcaveHull() {
registerUDF("ST_ConcaveHull", byte[].class, double.class);
registerUDF("ST_ConcaveHull", byte[].class, double.class, boolean.class);
- verifySqlSingleRes(
+ verifyGeometry(
"select
sedona.ST_AsText(sedona.ST_ConcaveHull(sedona.ST_GeomFromText('MULTIPOINT ((10
72), (53 76), (56 66), (63 58), (71 51), (81 48), (91 46), (101 45), (111 46),
(121 47), (131 50), (140 55), (145 64), (144 74), (135 80), (125 83), (115 85),
(105 87), (95 89), (85 91), (75 93), (65 95), (55 98), (45 102), (37 107), (29
114), (22 122), (19 132), (18 142), (21 151), (27 160), (35 167), (44 172), (54
175), (64 178), (74 180), (84 181), (94 181), (104 181), (114 181), (124 181),
[...]
"POLYGON ((18 142, 21 151, 27 160, 35 167, 44 172, 54 175, 64 178, 74
180, 84 181, 94 181, 104 181, 114 181, 124 181, 134 179, 144 177, 153 173, 162
168, 171 162, 177 154, 182 145, 184 135, 173 134, 161 134, 150 133, 139 132,
136 142, 128 149, 119 153, 109 155, 99 155, 89 155, 79 153, 69 150, 61 144, 63
134, 72 128, 82 125, 92 123, 102 121, 112 119, 122 118, 132 116, 142 113, 151
110, 161 106, 170 102, 178 96, 185 88, 189 78, 190 68, 189 58, 185 49, 179 41,
171 34, 162 29, 153 25 [...]
- verifySqlSingleRes(
+ verifyGeometry(
"select
sedona.ST_AsText(sedona.ST_ConcaveHull(sedona.ST_GeomFromText('MULTIPOINT ((132
64), (114 64), (99 64), (81 64), (63 64), (57 49), (52 36), (46 20), (37 20),
(26 20), (32 36), (39 55), (43 69), (50 84), (57 100), (63 118), (68 133), (74
149), (81 164), (88 180), (101 180), (112 180), (119 164), (126 149), (132
131), (139 113), (143 100), (150 84), (157 69), (163 51), (168 36), (174 20),
(163 20), (150 20), (143 36), (139 49), (132 64), (99 151), (92 138), (88 124),
(81 10 [...]
"POLYGON ((43 69, 50 84, 57 100, 63 118, 68 133, 74 149, 81 164, 88
180, 101 180, 112 180, 119 164, 126 149, 132 131, 139 113, 143 100, 150 84, 157
69, 163 51, 168 36, 174 20, 163 20, 150 20, 143 36, 139 49, 132 64, 114 64, 99
64, 81 64, 63 64, 57 49, 52 36, 46 20, 37 20, 26 20, 32 36, 35 45, 39 55, 43
69), (88 124, 81 109, 74 93, 83 82, 99 82, 112 82, 121 96, 114 109, 110 122,
103 138, 92 138, 88 124))");
}
@@ -323,7 +323,7 @@ public class TestFunctions extends TestBase {
@Test
public void test_ST_DelaunayTriangles() {
registerUDF("ST_DelaunayTriangles", byte[].class);
- verifySqlSingleRes(
+ verifyGeometry(
"select
sedona.ST_AsText(sedona.ST_DelaunayTriangles(sedona.ST_GeomFromText('POLYGON
((10 10, 15 30, 20 25, 25 35, 30 20, 40 30, 50 10, 45 5, 35 15, 30 5, 25 15, 20
10, 15 20, 10 10))')))",
"GEOMETRYCOLLECTION (POLYGON ((15 30, 10 10, 15 20, 15 30)), POLYGON
((15 30, 15 20, 20 25, 15 30)), POLYGON ((15 30, 20 25, 25 35, 15 30)), POLYGON
((25 35, 20 25, 30 20, 25 35)), POLYGON ((25 35, 30 20, 40 30, 25 35)), POLYGON
((40 30, 30 20, 35 15, 40 30)), POLYGON ((40 30, 35 15, 50 10, 40 30)), POLYGON
((50 10, 35 15, 45 5, 50 10)), POLYGON ((30 5, 45 5, 35 15, 30 5)), POLYGON
((30 5, 35 15, 25 15, 30 5)), POLYGON ((30 5, 25 15, 20 10, 30 5)), POLYGON
((30 5, 20 10, 10 10, 3 [...]
}
@@ -693,7 +693,7 @@ public class TestFunctions extends TestBase {
@Test
public void test_ST_MinimumBoundingCircle() {
registerUDF("ST_MinimumBoundingCircle", byte[].class, int.class);
- verifySqlSingleRes(
+ verifyGeometry(
"select
sedona.ST_AsText(sedona.ST_MinimumBoundingCircle(sedona.ST_GeomFromText('GeometryCollection
(LINESTRING(55 75,125 150), POINT (20 80))') , 8))",
"POLYGON ((135.5971473206198 115, 134.38475332749803
102.69035721092119, 130.79416296937 90.85376709089948, 124.96336062007234
79.94510316021109, 117.11642074393687 70.38357925606314, 107.55489683978892
62.536639379927664, 96.64623290910052 56.70583703062998, 84.80964278907881
53.11524667250196, 72.5 51.90285267938019, 60.1903572109212 53.11524667250196,
48.35376709089948 56.70583703062998, 37.4451031602111 62.53663937992766,
27.883579256063136 70.38357925606313, 20.0366393799276 [...]
}
@@ -991,13 +991,13 @@ public class TestFunctions extends TestBase {
registerUDF("ST_VoronoiPolygons", byte[].class);
registerUDF("ST_VoronoiPolygons", byte[].class, double.class);
registerUDF("ST_VoronoiPolygons", byte[].class, double.class,
byte[].class);
- verifySqlSingleRes(
+ verifyGeometry(
"select
sedona.ST_AsText(sedona.ST_VoronoiPolygons(sedona.ST_GeomFromText('MULTIPOINT
((0 0), (1 1))')))",
"GEOMETRYCOLLECTION (POLYGON ((-1 -1, -1 2, 2 -1, -1 -1)), POLYGON
((-1 2, 2 2, 2 -1, -1 2)))");
- verifySqlSingleRes(
+ verifyGeometry(
"select
sedona.ST_AsText(sedona.ST_VoronoiPolygons(sedona.ST_GeomFromText('MULTIPOINT
((0 0), (1 1))'), 1))",
"GEOMETRYCOLLECTION (POLYGON ((-1 -1, -1 2, 2 -1, -1 -1)), POLYGON
((-1 2, 2 2, 2 -1, -1 2)))");
- verifySqlSingleRes(
+ verifyGeometry(
"select
sedona.ST_AsText(sedona.ST_VoronoiPolygons(sedona.ST_GeomFromText('MULTIPOINT
((0 0), (1 1))'), 1, sedona.ST_GeomFromText('POLYGON ((-1 -1, -1 2, 2 -1, -1
-1))')))",
"GEOMETRYCOLLECTION (POLYGON ((-1 -1, -1 2, 2 -1, -1 -1)), POLYGON
((-1 2, 2 2, 2 -1, -1 2)))");
}
diff --git
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
index cf80aa245..a387f5d9e 100644
---
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
+++
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
@@ -224,11 +224,11 @@ public class TestFunctionsV2 extends TestBase {
@Test
public void test_ST_Buffer() {
registerUDFV2("ST_Buffer", String.class, double.class);
- verifySqlSingleRes(
+ verifyGeometry(
"select ST_AsText(sedona.ST_Buffer(ST_GeometryFromWKT('POINT (0 1)'),
1))",
"POLYGON((1 1,0.9807852804 0.804909678,0.9238795325
0.6173165676,0.8314696123 0.444429767,0.7071067812 0.2928932188,0.555570233
0.1685303877,0.3826834324 0.07612046749,0.195090322
0.0192147196,6.123233996e-17 0,-0.195090322 0.0192147196,-0.3826834324
0.07612046749,-0.555570233 0.1685303877,-0.7071067812
0.2928932188,-0.8314696123 0.444429767,-0.9238795325 0.6173165676,-0.9807852804
0.804909678,-1 1,-0.9807852804 1.195090322,-0.9238795325
1.382683432,-0.8314696123 1.555570233,-0.7 [...]
registerUDFV2("ST_Buffer", String.class, double.class, boolean.class);
- verifySqlSingleRes(
+ verifyGeometry(
"select
ST_AsText(ST_ReducePrecision(sedona.ST_Buffer(ST_GeometryFromWKT('LINESTRING(0.05
15, -0.05 15)'), 10000, true), 8))",
"POLYGON((-0.06809794 14.91143929,-0.0855181 14.91657277,-0.10157458
14.92491089,-0.11565095 14.93613348,-0.12720661 14.94980963,-0.13579747
14.96541421,-0.14109307 14.98234798,-0.14288927 14.99996056,-0.14111623
15.01757534,-0.13584121 15.03451549,-0.12726608 15.05012991,-0.11571978
15.0638183,-0.10164567 15.07505424,-0.08558461 15.0834055,-0.06815417
15.08855069,-0.05002481 15.09029174,0.0500174 15.09029949,0.06814888
15.08856105,0.08558242 15.08341775,0.10164727 15.07506734,0. [...]
}
@@ -269,10 +269,10 @@ public class TestFunctionsV2 extends TestBase {
public void test_ST_ConcaveHull() {
registerUDFV2("ST_ConcaveHull", String.class, double.class);
registerUDFV2("ST_ConcaveHull", String.class, double.class, boolean.class);
- verifySqlSingleRes(
+ verifyGeometry(
"select ST_AsText(sedona.ST_ConcaveHull(ST_GeometryFromWKT('MULTIPOINT
((10 72), (53 76), (56 66), (63 58), (71 51), (81 48), (91 46), (101 45), (111
46), (121 47), (131 50), (140 55), (145 64), (144 74), (135 80), (125 83), (115
85), (105 87), (95 89), (85 91), (75 93), (65 95), (55 98), (45 102), (37 107),
(29 114), (22 122), (19 132), (18 142), (21 151), (27 160), (35 167), (44 172),
(54 175), (64 178), (74 180), (84 181), (94 181), (104 181), (114 181), (124
181), (134 179), [...]
"POLYGON((18 142,21 151,27 160,35 167,44 172,54 175,64 178,74 180,84
181,94 181,104 181,114 181,124 181,134 179,144 177,153 173,162 168,171 162,177
154,182 145,184 135,173 134,161 134,150 133,139 132,136 142,128 149,119 153,109
155,99 155,89 155,79 153,69 150,61 144,63 134,72 128,82 125,92 123,102 121,112
119,122 118,132 116,142 113,151 110,161 106,170 102,178 96,185 88,189 78,190
68,189 58,185 49,179 41,171 34,162 29,153 25,143 23,133 21,123 19,113 19,102
19,92 19,82 19,72 21,62 [...]
- verifySqlSingleRes(
+ verifyGeometry(
"select ST_AsText(sedona.ST_ConcaveHull(ST_GeometryFromWKT('MULTIPOINT
((132 64), (114 64), (99 64), (81 64), (63 64), (57 49), (52 36), (46 20), (37
20), (26 20), (32 36), (39 55), (43 69), (50 84), (57 100), (63 118), (68 133),
(74 149), (81 164), (88 180), (101 180), (112 180), (119 164), (126 149), (132
131), (139 113), (143 100), (150 84), (157 69), (163 51), (168 36), (174 20),
(163 20), (150 20), (143 36), (139 49), (132 64), (99 151), (92 138), (88 124),
(81 109), (74 93) [...]
"POLYGON((43 69,50 84,57 100,63 118,68 133,74 149,81 164,88 180,101
180,112 180,119 164,126 149,132 131,139 113,143 100,150 84,157 69,163 51,168
36,174 20,163 20,150 20,143 36,139 49,132 64,114 64,99 64,81 64,63 64,57 49,52
36,46 20,37 20,26 20,32 36,35 45,39 55,43 69),(88 124,81 109,74 93,83 82,99
82,112 82,121 96,114 109,110 122,103 138,92 138,88 124))");
}
@@ -315,7 +315,7 @@ public class TestFunctionsV2 extends TestBase {
@Test
public void test_ST_DelaunayTriangles() {
registerUDFV2("ST_DelaunayTriangles", String.class);
- verifySqlSingleRes(
+ verifyGeometry(
"select ST_AsText(sedona.ST_DelaunayTriangles(ST_GeomFromText('POLYGON
((10 10, 15 30, 20 25, 25 35, 30 20, 40 30, 50 10, 45 5, 35 15, 30 5, 25 15, 20
10, 15 20, 10 10))')))",
"GEOMETRYCOLLECTION(POLYGON((15 30,10 10,15 20,15 30)),POLYGON((15
30,15 20,20 25,15 30)),POLYGON((15 30,20 25,25 35,15 30)),POLYGON((25 35,20
25,30 20,25 35)),POLYGON((25 35,30 20,40 30,25 35)),POLYGON((40 30,30 20,35
15,40 30)),POLYGON((40 30,35 15,50 10,40 30)),POLYGON((50 10,35 15,45 5,50
10)),POLYGON((30 5,45 5,35 15,30 5)),POLYGON((30 5,35 15,25 15,30
5)),POLYGON((30 5,25 15,20 10,30 5)),POLYGON((30 5,20 10,10 10,30
5)),POLYGON((10 10,20 10,15 20,10 10)),POLYGON((15 20,20 1 [...]
}
@@ -951,13 +951,13 @@ public class TestFunctionsV2 extends TestBase {
registerUDFV2("ST_VoronoiPolygons", String.class);
registerUDFV2("ST_VoronoiPolygons", String.class, double.class);
registerUDFV2("ST_VoronoiPolygons", String.class, double.class,
String.class);
- verifySqlSingleRes(
+ verifyGeometry(
"select
ST_AsText(sedona.ST_VoronoiPolygons(ST_GeometryFromWKT('MULTIPOINT ((0 0), (1
1))')))",
"GEOMETRYCOLLECTION(POLYGON((-1 -1,-1 2,2 -1,-1 -1)),POLYGON((-1 2,2
2,2 -1,-1 2)))");
- verifySqlSingleRes(
+ verifyGeometry(
"select
ST_AsText(sedona.ST_VoronoiPolygons(ST_GeometryFromWKT('MULTIPOINT ((0 0), (1
1))'), 1))",
"GEOMETRYCOLLECTION(POLYGON((-1 -1,-1 2,2 -1,-1 -1)),POLYGON((-1 2,2
2,2 -1,-1 2)))");
- verifySqlSingleRes(
+ verifyGeometry(
"select
ST_AsText(sedona.ST_VoronoiPolygons(ST_GeometryFromWKT('MULTIPOINT ((0 0), (1
1))'), 1, ST_GeometryFromWKT('POLYGON ((-1 -1, -1 2, 2 -1, -1 -1))')))",
"GEOMETRYCOLLECTION(POLYGON((-1 -1,-1 2,2 -1,-1 -1)),POLYGON((-1 2,2
2,2 -1,-1 2)))");
}
diff --git
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestTableFunctions.java
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestTableFunctions.java
index 27e022f45..0f6db4a4f 100644
---
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestTableFunctions.java
+++
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestTableFunctions.java
@@ -51,15 +51,15 @@ public class TestTableFunctions extends TestBase {
@Test
public void test_ST_MaximumInscribedCircle() {
registerUDTF(ST_MaximumInscribedCircle.class);
- verifySqlSingleRes(
+ verifyGeometry(
"select sedona.ST_AsText(center) from
table(sedona.ST_MaximumInscribedCircle(sedona.ST_GeomFromText('POLYGON ((40
180, 110 160, 180 180, 180 120, 140 90, 160 40, 80 10, 70 40, 20 50, 40
180),(60 140, 50 90, 90 140, 60 140))')))",
- "POINT (96.953125 76.328125)");
- verifySqlSingleRes(
+ "POINT (96.9287109375 76.3232421875)");
+ verifyGeometry(
"select sedona.ST_AsText(nearest) from
table(sedona.ST_MaximumInscribedCircle(sedona.ST_GeomFromText('POLYGON ((40
180, 110 160, 180 180, 180 120, 140 90, 160 40, 80 10, 70 40, 20 50, 40
180),(60 140, 50 90, 90 140, 60 140))')))",
- "POINT (140 90)");
+ "POINT (61.64205411585366 104.55256764481707)");
verifySqlSingleRes(
"select radius from
table(sedona.ST_MaximumInscribedCircle(sedona.ST_GeomFromText('POLYGON ((40
180, 110 160, 180 180, 180 120, 140 90, 160 40, 80 10, 70 40, 20 50, 40
180),(60 140, 50 90, 90 140, 60 140))')))",
- 45.165845650018);
+ 45.18896951053177);
}
@Test
@@ -67,7 +67,7 @@ public class TestTableFunctions extends TestBase {
registerUDTF(ST_IsValidDetail.class);
verifySqlSingleRes(
"select reason from
table(sedona.ST_IsValidDetail(sedona.ST_GeomFromText('POLYGON ((30 10, 40 40,
20 40, 30 10, 10 20, 30 10))'), 0))",
- "Ring Self-intersection at or near point (30.0, 10.0, NaN)");
+ "Ring Self-intersection at or near point (30.0, 10.0)");
verifySqlSingleRes(
"select valid from
table(sedona.ST_IsValidDetail(sedona.ST_GeomFromText('POLYGON ((30 10, 40 40,
20 40, 30 10, 10 20, 30 10))'), 0))",
false);
diff --git
a/spark/common/src/test/scala/org/apache/sedona/sql/TestBaseScala.scala
b/spark/common/src/test/scala/org/apache/sedona/sql/TestBaseScala.scala
index dc9c841eb..a41cdab6e 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/TestBaseScala.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/TestBaseScala.scala
@@ -28,7 +28,9 @@ import org.apache.sedona.common.sphere.{Haversine, Spheroid}
import org.apache.sedona.spark.SedonaContext
import org.apache.spark.SparkContext
import org.apache.spark.sql.{DataFrame, SparkSession}
+import org.junit.Assert.fail
import org.locationtech.jts.geom._
+import org.locationtech.jts.io.WKTReader
import org.scalatest.{BeforeAndAfterAll, FunSpec}
import java.io.File
@@ -318,4 +320,42 @@ trait TestBaseScala extends FunSpec with BeforeAndAfterAll
{
val hdfsCluster = builder.build
(hdfsCluster, "hdfs://127.0.0.1:" + hdfsCluster.getNameNodePort + "/")
}
+
+ def assertGeometryEquals(expectedWkt: String, actualWkt: String, tolerance:
Double): Unit = {
+ val reader = new WKTReader
+ val expectedGeom = reader.read(expectedWkt)
+ val actualGeom = reader.read(actualWkt)
+ assertGeometryEquals(expectedGeom, actualGeom, tolerance)
+ }
+
+ def assertGeometryEquals(expectedWkt: String, actualGeom: Geometry,
tolerance: Double): Unit = {
+ val reader = new WKTReader
+ val expectedGeom = reader.read(expectedWkt)
+ assertGeometryEquals(expectedGeom, actualGeom, tolerance)
+ }
+
+ def assertGeometryEquals(
+ expectedGeom: Geometry,
+ actualGeom: Geometry,
+ tolerance: Double): Unit = {
+ if (!(expectedGeom == actualGeom)) {
+ if (!expectedGeom
+ .buffer(tolerance)
+ .contains(actualGeom) ||
!actualGeom.buffer(tolerance).contains(expectedGeom)) {
+ fail(String.format("Geometry %s should equal to %s", actualGeom,
expectedGeom))
+ }
+ }
+ }
+
+ def assertGeometryEquals(expectedGeom: Geometry, actualGeom: Geometry): Unit
= {
+ assertGeometryEquals(expectedGeom, actualGeom, 1e-6)
+ }
+
+ def assertGeometryEquals(expectedWkt: String, actualWkt: String): Unit = {
+ assertGeometryEquals(expectedWkt, actualWkt, 1e-6)
+ }
+
+ def assertGeometryEquals(expectedWkt: String, actualGeom: Geometry): Unit = {
+ assertGeometryEquals(expectedWkt, actualGeom, 1e-6)
+ }
}
diff --git
a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
index 0f6068221..a2a4ded59 100644
---
a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
+++
b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
@@ -416,7 +416,7 @@ class dataFrameAPITestScala extends TestBaseScala {
"SELECT ST_GeomFromWKT('Polygon ((0 0, 1 2, 2 2, 3 2, 5 0, 4 0, 3 1, 2
1, 1 0, 0 0))') as mline")
val df = baseDF.select(ST_ConcaveHull("mline", 1, true))
val actualResult = df.take(1)(0).get(0).asInstanceOf[Geometry].toText()
- assert(actualResult == "POLYGON ((1 2, 2 2, 3 2, 5 0, 4 0, 1 0, 0 0, 1
2))")
+ assertGeometryEquals("POLYGON ((1 2, 2 2, 3 2, 5 0, 4 0, 1 0, 0 0, 1
2))", actualResult)
}
it("Passed ST_ConvexHull") {
@@ -2004,7 +2004,7 @@ class dataFrameAPITestScala extends TestBaseScala {
val actual = wktWriter3D.write(actualGeom)
val expected =
"GEOMETRYCOLLECTION (POLYGON ((-2 -2, -2 4, 4 -2, -2 -2)), POLYGON
((-2 4, 4 4, 4 -2, -2 4)))"
- assert(expected == actual)
+ assertGeometryEquals(expected, actual)
}
it("Passed ST_FrechetDistance") {
@@ -2188,19 +2188,11 @@ class dataFrameAPITestScala extends TestBaseScala {
val baseDf = sparkSession.sql(
"SELECT ST_GeomFromWKT('POLYGON ((40 180, 110 160, 180 180, 180 120,
140 90, 160 40, 80 10, 70 40, 20 50, 40 180),(60 140, 50 90, 90 140, 60 140))')
AS geom")
val actual: Row =
baseDf.select(ST_MaximumInscribedCircle("geom")).first().getAs[Row](0)
- val expected = Row(
- sparkSession
- .sql("SELECT ST_GeomFromWKT('POINT (96.953125 76.328125)')")
- .first()
- .get(0)
- .asInstanceOf[Geometry],
- sparkSession
- .sql("SELECT ST_GeomFromWKT('POINT (140 90)')")
- .first()
- .get(0)
- .asInstanceOf[Geometry],
- 45.165845650018)
- assertTrue(actual.equals(expected))
+ assertGeometryEquals("POINT (96.9287109375 76.3232421875)",
actual.getAs[Geometry](0))
+ assertGeometryEquals(
+ "POINT (61.64205411585366 104.55256764481707)",
+ actual.getAs[Geometry](1))
+ assertEquals(45.18896951053177, actual.getDouble(2), 1e-6)
}
it("Should pass ST_IsValidTrajectory") {
@@ -2230,7 +2222,7 @@ class dataFrameAPITestScala extends TestBaseScala {
actual = baseDf.select(ST_IsValidDetail("geom", 0)).first().getAs[Row](0)
expected = Row(
false,
- "Ring Self-intersection at or near point (1.0, 1.0, NaN)",
+ "Ring Self-intersection at or near point (1.0, 1.0)",
sparkSession
.sql("SELECT ST_GeomFromText('POINT (1 1)')")
.first()
@@ -2242,7 +2234,7 @@ class dataFrameAPITestScala extends TestBaseScala {
actual = baseDf.select(ST_IsValidDetail($"geom",
lit(1))).first().getAs[Row](0)
expected = Row(
false,
- "Interior is disconnected at or near point (1.0, 1.0, NaN)",
+ "Interior is disconnected at or near point (1.0, 1.0)",
sparkSession
.sql("SELECT ST_GeomFromText('POINT (1 1)')")
.first()
@@ -2267,14 +2259,12 @@ class dataFrameAPITestScala extends TestBaseScala {
// Test with OGC flag (OGC_SFS_VALIDITY = 0)
val ogcValidityTable = specialCaseTable.select(ST_IsValidReason($"geom",
lit(0)))
val ogcValidityReason = ogcValidityTable.take(1)(0).getString(0)
- assertEquals("Ring Self-intersection at or near point (1.0, 1.0, NaN)",
ogcValidityReason)
+ assertTrue(ogcValidityReason.contains("Ring Self-intersection at or near
point"))
// Test with ESRI flag (ESRI_VALIDITY = 1)
val esriValidityTable =
specialCaseTable.select(ST_IsValidReason($"geom", lit(1)))
val esriValidityReason = esriValidityTable.take(1)(0).getString(0)
- assertEquals(
- "Interior is disconnected at or near point (1.0, 1.0, NaN)",
- esriValidityReason)
+ assertTrue(esriValidityReason.contains("Interior is disconnected at or
near point"))
}
it("Passed ST_PointZM") {
diff --git
a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
index e40fcd5b5..c05f745f5 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
@@ -3085,7 +3085,7 @@ class functionTestScala
val df = sparkSession.sql(
s"SELECT ST_AsText(ST_VoronoiPolygons(ST_GeomFromWKT($g1),
($tolerance)))")
val actual = df.take(1)(0).get(0).asInstanceOf[String]
- assertEquals(expectedResult, actual)
+ assertGeometryEquals(expectedResult, actual)
}
}
@@ -3349,19 +3349,11 @@ class functionTestScala
val baseDf = sparkSession.sql(
"SELECT ST_GeomFromWKT('POLYGON ((40 180, 110 160, 180 180, 180 120, 140
90, 160 40, 80 10, 70 40, 20 50, 40 180),(60 140, 50 90, 90 140, 60 140))') AS
geom")
val actual: Row =
baseDf.selectExpr("ST_MaximumInscribedCircle(geom)").first().getAs[Row](0)
- val expected = Row(
- sparkSession
- .sql("SELECT ST_GeomFromWKT('POINT (96.953125 76.328125)')")
- .first()
- .get(0)
- .asInstanceOf[Geometry],
- sparkSession
- .sql("SELECT ST_GeomFromWKT('POINT (140 90)')")
- .first()
- .get(0)
- .asInstanceOf[Geometry],
- 45.165845650018)
- assertTrue(actual.equals(expected))
+ assertGeometryEquals("POINT (96.9287109375 76.3232421875)",
actual.getAs[Geometry](0))
+ assertGeometryEquals(
+ "POINT (61.64205411585366 104.55256764481707)",
+ actual.getAs[Geometry](1))
+ assertEquals(45.18896951053177, actual.getDouble(2), 1e-6)
}
it("Should pass ST_IsValidTrajectory") {
diff --git
a/spark/common/src/test/scala/org/apache/sedona/sql/functions/TestStSubDivide.scala
b/spark/common/src/test/scala/org/apache/sedona/sql/functions/TestStSubDivide.scala
index bfb531cae..28a192ee4 100644
---
a/spark/common/src/test/scala/org/apache/sedona/sql/functions/TestStSubDivide.scala
+++
b/spark/common/src/test/scala/org/apache/sedona/sql/functions/TestStSubDivide.scala
@@ -20,6 +20,7 @@ package org.apache.sedona.sql.functions
import org.apache.sedona.common.subDivide.GeometrySubDivider
import org.locationtech.jts.geom.Geometry
+import org.locationtech.jts.geom.GeometryFactory
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.{TableDrivenPropertyChecks, TableFor3, TableFor4}
@@ -33,15 +34,20 @@ class TestStSubDivide
with TableDrivenPropertyChecks
with FunctionsHelper {
+ val factory = new GeometryFactory()
+
for ((
statement: String,
inputGeometry: String,
maxVertices: Int,
outPutGeometry: Seq[String]) <- Fixtures.testPolygons) {
test("it should return subdivide geometry when input geometry is " +
statement) {
- Fixtures
- .subDivideGeometry(inputGeometry, maxVertices)
- .map(geom => geometryToWkt(geom)) should contain theSameElementsAs
outPutGeometry
+ val subdivided = Fixtures.subDivideGeometry(inputGeometry, maxVertices)
+ subdivided should have size outPutGeometry.size
+ val actual = factory.createGeometryCollection(subdivided.toArray)
+ val expected =
factory.createGeometryCollection(outPutGeometry.map(readGeometry).toArray)
+ assert(actual.buffer(1e-6).contains(expected))
+ assert(expected.buffer(1e-6).contains(actual))
}
}