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 69c1ede76 [SEDONA-564] Add ST_NumInteriorRing (#1434)
69c1ede76 is described below
commit 69c1ede76241e78be064fd19b05fa428e910db73
Author: Jia Yu <[email protected]>
AuthorDate: Tue May 28 09:04:29 2024 -0700
[SEDONA-564] Add ST_NumInteriorRing (#1434)
* [189] Add ST_NumInteriorRing (#138)
* feat: add ST_NumInteriorRing
* feat: add python tests
* docs: remove version number
* Update version
---------
Co-authored-by: Furqaan Khan <[email protected]>
---
docs/api/flink/Function.md | 20 +++++++++++++++
docs/api/snowflake/vector-data/Function.md | 18 ++++++++++++++
docs/api/sql/Function.md | 20 +++++++++++++++
.../main/java/org/apache/sedona/flink/Catalog.java | 1 +
.../apache/sedona/flink/expressions/Functions.java | 8 ++++++
.../java/org/apache/sedona/flink/FunctionTest.java | 7 ++++++
python/sedona/sql/st_functions.py | 11 ++++++++
python/tests/sql/test_dataframe_api.py | 2 ++
python/tests/sql/test_function.py | 23 ++++++++++++++++-
.../sedona/snowflake/snowsql/TestFunctions.java | 13 ++++++++++
.../sedona/snowflake/snowsql/TestFunctionsV2.java | 13 ++++++++++
.../org/apache/sedona/snowflake/snowsql/UDFs.java | 7 ++++++
.../apache/sedona/snowflake/snowsql/UDFsV2.java | 7 ++++++
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 1 +
.../sql/sedona_sql/expressions/Functions.scala | 8 ++++++
.../sql/sedona_sql/expressions/st_functions.scala | 3 +++
.../org/apache/sedona/sql/functionTestScala.scala | 29 ++++++++++++++++++++++
17 files changed, 190 insertions(+), 1 deletion(-)
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 5c6b2f6f3..ca1c5f6b8 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -2283,6 +2283,26 @@ Output:
1
```
+## ST_NumInteriorRing
+
+Introduction: Returns number of interior rings of polygon geometries. It is an
alias of [ST_NumInteriorRings](#st_numinteriorrings).
+
+Format: `ST_NumInteriorRing(geom: Geometry)`
+
+Since: `v1.6.1`
+
+SQL Example
+
+```sql
+SELECT ST_NumInteriorRing(ST_GeomFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0),
(1 1, 2 1, 2 2, 1 2, 1 1))'))
+```
+
+Output:
+
+```
+1
+```
+
## ST_NumInteriorRings
Introduction: Returns number of interior rings of polygon geometries.
diff --git a/docs/api/snowflake/vector-data/Function.md
b/docs/api/snowflake/vector-data/Function.md
index 6f02aaed1..eedcc50a2 100644
--- a/docs/api/snowflake/vector-data/Function.md
+++ b/docs/api/snowflake/vector-data/Function.md
@@ -1759,6 +1759,24 @@ SELECT ST_NumGeometries(df.geometry)
FROM df
```
+## ST_NumInteriorRing
+
+Introduction: Returns number of interior rings of polygon geometries. It is an
alias of [ST_NumInteriorRings](#st_numinteriorrings).
+
+Format: `ST_NumInteriorRing(geom: Geometry)`
+
+SQL Example
+
+```sql
+SELECT ST_NumInteriorRing(ST_GeomFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0),
(1 1, 2 1, 2 2, 1 2, 1 1))'))
+```
+
+Output:
+
+```
+1
+```
+
## ST_NumInteriorRings
Introduction: RETURNS number of interior rings of polygon geometries.
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index 814c9c376..0fe320261 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -2297,6 +2297,26 @@ Output:
1
```
+## ST_NumInteriorRing
+
+Introduction: Returns number of interior rings of polygon geometries. It is an
alias of [ST_NumInteriorRings](#st_numinteriorrings).
+
+Format: `ST_NumInteriorRing(geom: Geometry)`
+
+Since: `v1.6.1`
+
+SQL Example
+
+```sql
+SELECT ST_NumInteriorRing(ST_GeomFromText('POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0),
(1 1, 2 1, 2 2, 1 2, 1 1))'))
+```
+
+Output:
+
+```
+1
+```
+
## ST_NumInteriorRings
Introduction: RETURNS number of interior rings of polygon geometries.
diff --git a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
index ad2c9a022..24395238f 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -87,6 +87,7 @@ public class Catalog {
new Functions.ST_NPoints(),
new Functions.ST_NumGeometries(),
new Functions.ST_NumInteriorRings(),
+ new Functions.ST_NumInteriorRing(),
new Functions.ST_ExteriorRing(),
new Functions.ST_AsEWKT(),
new Functions.ST_AsEWKB(),
diff --git
a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
index 150c2048a..fe11db120 100644
--- a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
+++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
@@ -475,6 +475,14 @@ public class Functions {
}
}
+ public static class ST_NumInteriorRing extends ScalarFunction {
+ @DataTypeHint("Integer")
+ public Integer eval(@DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class) Object o) {
+ Geometry geom = (Geometry) o;
+ return org.apache.sedona.common.Functions.numInteriorRings(geom);
+ }
+ }
+
public static class ST_ExteriorRing extends ScalarFunction {
@DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class)
public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class) Object o) {
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 c8d1a2f2e..7649e0040 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -572,6 +572,13 @@ public class FunctionTest extends TestBase{
assertEquals(2, first(resultTable).getField(0));
}
+ @Test
+ public void testNumInteriorRing() {
+ Table polygonTable = tableEnv.sqlQuery("SELECT
ST_GeomFromText('POLYGON((7 9,8 7,11 6,15 8,16 6,17 7,17 10,18 12,17 14,15
15,11 15,10 13,9 12,7 9),(9 9,10 10,11 11,11 10,10 8,9 9),(12 14,15 14,13 11,12
14))') AS polygon");
+ Table resultTable =
polygonTable.select(call(Functions.ST_NumInteriorRing.class.getSimpleName(),
$("polygon")));
+ assertEquals(2, first(resultTable).getField(0));
+ }
+
@Test
public void testExteriorRing() {
Table polygonTable = createPolygonTable(1);
diff --git a/python/sedona/sql/st_functions.py
b/python/sedona/sql/st_functions.py
index dfd9ae13c..c22b85490 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -1024,6 +1024,17 @@ def ST_NumInteriorRings(geometry: ColumnOrName) ->
Column:
"""
return _call_st_function("ST_NumInteriorRings", geometry)
+@validate_argument_types
+def ST_NumInteriorRing(geometry: ColumnOrName) -> Column:
+ """Return the number of interior rings contained in a polygon geometry.
+
+ :param geometry: Polygon geometry column to return for.
+ :type geometry: ColumnOrName
+ :return: Number of interior rings polygons contain as an integer column.
+ :rtype: Column
+ """
+ return _call_st_function("ST_NumInteriorRing", geometry)
+
@validate_argument_types
def ST_PointN(geometry: ColumnOrName, n: Union[ColumnOrName, int]) -> Column:
diff --git a/python/tests/sql/test_dataframe_api.py
b/python/tests/sql/test_dataframe_api.py
index cabe4b005..67ba78c52 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -156,6 +156,7 @@ test_configurations = [
(stf.ST_NRings, ("geom",), "square_geom", "", 1),
(stf.ST_NumGeometries, ("geom",), "multipoint", "", 2),
(stf.ST_NumInteriorRings, ("geom",), "geom_with_hole", "", 1),
+ (stf.ST_NumInteriorRing, ("geom",), "geom_with_hole", "", 1),
(stf.ST_NumPoints, ("line",), "linestring_geom", "", 6),
(stf.ST_PointN, ("line", 2), "linestring_geom", "", "POINT (1 0)"),
(stf.ST_PointOnSurface, ("line",), "linestring_geom", "", "POINT (2 0)"),
@@ -316,6 +317,7 @@ wrong_type_configurations = [
(stf.ST_NPoints, (None,)),
(stf.ST_NumGeometries, (None,)),
(stf.ST_NumInteriorRings, (None,)),
+ (stf.ST_NumInteriorRing, (None,)),
(stf.ST_PointN, (None, 2)),
(stf.ST_PointN, ("", None)),
(stf.ST_PointN, ("", 2.0)),
diff --git a/python/tests/sql/test_function.py
b/python/tests/sql/test_function.py
index ea8497132..791a3073b 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -781,7 +781,7 @@ class TestPredicateJoin(TestBase):
is_closed_collected = [[*row] for row in is_closed]
assert (is_closed_collected == expected_result)
- def test_num_interior_ring(self):
+ def test_num_interior_rings(self):
geometries = [
(1, "Point(21 52)"),
(2, "Polygon((0 0, 0 1, 1 1, 1 0, 0 0))"),
@@ -802,6 +802,27 @@ class TestPredicateJoin(TestBase):
collected_interior_rings = [[*row] for row in
number_of_interior_rings.filter("num is not null").collect()]
assert (collected_interior_rings == [[2, 0], [11, 1]])
+ def test_num_interior_ring(self):
+ geometries = [
+ (1, "Point(21 52)"),
+ (2, "Polygon((0 0, 0 1, 1 1, 1 0, 0 0))"),
+ (3, "Linestring(0 0, 1 1, 1 0)"),
+ (4, "Linestring(0 0, 1 1, 1 0, 0 0)"),
+ (5, "MULTIPOINT ((10 40), (40 30), (20 20), (30 10))"),
+ (6, "MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10,
10 20, 5 10, 15 5)))"),
+ (7, "MULTILINESTRING ((10 10, 20 20, 10 40, 10 10), (40 40, 30 30,
40 20, 30 10, 40 40))"),
+ (8, "MULTILINESTRING ((10 10, 20 20, 10 40, 10 10), (40 40, 30 30,
40 20, 30 10))"),
+ (9, "MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20,
30 10))"),
+ (10,
+ "GEOMETRYCOLLECTION (POINT (40 10), LINESTRING (10 10, 20 20, 10
40), POLYGON ((40 40, 20 45, 45 30, 40 40)))"),
+ (11, "POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 2 1, 2 2, 1 2, 1
1))")]
+
+ geometry_df = self.__wkt_pair_list_with_index_to_data_frame(geometries)
+
+ number_of_interior_rings = geometry_df.selectExpr("index",
"ST_NumInteriorRing(geom) as num")
+ collected_interior_rings = [[*row] for row in
number_of_interior_rings.filter("num is not null").collect()]
+ assert (collected_interior_rings == [[2, 0], [11, 1]])
+
def test_st_add_point(self):
geometry = [
("Point(21 52)", "Point(21 52)"),
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 a0b283156..8c26cc404 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
@@ -687,6 +687,19 @@ public class TestFunctions extends TestBase {
);
}
+
+ @Test
+ public void test_ST_NumInteriorRing() {
+ registerUDF("ST_NumInteriorRing", byte[].class);
+ verifySqlSingleRes(
+ "select
sedona.ST_NumInteriorRing(sedona.ST_GeomFromText('POLYGON((0 0 0,0 5 0,5 0 0,0
0 5),(1 1 0,3 1 0,1 3 0,1 1 0))'))",
+ 1
+ );
+ verifySqlSingleRes(
+ "select
sedona.ST_NumInteriorRing(sedona.ST_GeomFromText('POLYGON((0 0, 0 1, 1 1, 1 0,
0 0))'))",
+ 0
+ );
+ }
@Test
public void test_ST_PointN() {
registerUDF("ST_PointN", byte[].class, int.class);
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 3921b4f09..289d40ee3 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
@@ -634,6 +634,19 @@ public class TestFunctionsV2
);
}
+
+ @Test
+ public void test_ST_NumInteriorRing() {
+ registerUDFV2("ST_NumInteriorRing", String.class);
+ verifySqlSingleRes(
+ "select
sedona.ST_NumInteriorRing(ST_GeometryFromWKT('POLYGON((0 0,0 5,5 0,0 0),(1 1,3
1,1 3,1 1))'))",
+ 1
+ );
+ verifySqlSingleRes(
+ "select
sedona.ST_NumInteriorRing(ST_GeometryFromWKT('POLYGON((0 0, 0 1, 1 1, 1 0, 0
0))'))",
+ 0
+ );
+ }
@Test
public void test_ST_PointN() {
registerUDFV2("ST_PointN", String.class, int.class);
diff --git
a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
index 0342778bf..116766ed3 100644
--- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
+++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
@@ -880,6 +880,13 @@ public class UDFs {
);
}
+ @UDFAnnotations.ParamMeta(argNames = {"geometry"})
+ public static Integer ST_NumInteriorRing(byte[] geometry) {
+ return Functions.numInteriorRings(
+ GeometrySerde.deserialize(geometry)
+ );
+ }
+
@UDFAnnotations.ParamMeta(argNames = {"leftGeometry", "rightGeometry"})
public static boolean ST_OrderingEquals(byte[] leftGeometry, byte[]
rightGeometry) {
return Predicates.orderingEquals(
diff --git
a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java
b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java
index 2c6ed7907..da0e56027 100644
--- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java
+++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java
@@ -766,6 +766,13 @@ public class UDFsV2
);
}
+ @UDFAnnotations.ParamMeta(argNames = {"geometry"}, argTypes = {"Geometry"})
+ public static Integer ST_NumInteriorRing(String geometry) {
+ return Functions.numInteriorRings(
+ GeometrySerde.deserGeoJson(geometry)
+ );
+ }
+
@UDFAnnotations.ParamMeta(argNames = {"leftGeometry", "rightGeometry"},
argTypes = {"Geometry", "Geometry"})
public static boolean ST_OrderingEquals(String leftGeometry, String
rightGeometry) {
return Predicates.orderingEquals(
diff --git
a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
index a0b34d78e..bab9ffe1b 100644
--- a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
+++ b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
@@ -124,6 +124,7 @@ object Catalog {
function[ST_IsClosed](),
function[ST_IsCollection](),
function[ST_NumInteriorRings](),
+ function[ST_NumInteriorRing](),
function[ST_AddPoint](-1),
function[ST_RemovePoint](-1),
function[ST_SetPoint](),
diff --git
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
index 4b9c09c03..f734c99de 100644
---
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
+++
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
@@ -621,6 +621,14 @@ case class ST_NumInteriorRings(inputExpressions:
Seq[Expression])
}
}
+case class ST_NumInteriorRing(inputExpressions: Seq[Expression])
+ extends InferredExpression(Functions.numInteriorRings _) {
+
+ protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) =
{
+ copy(inputExpressions = newChildren)
+ }
+}
+
case class ST_AddPoint(inputExpressions: Seq[Expression])
extends InferredExpression(inferrableFunction3(Functions.addPoint)) {
diff --git
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
index b78917303..4ec1885e8 100644
---
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
+++
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
@@ -265,6 +265,9 @@ object st_functions extends DataFrameAPI {
def ST_NumInteriorRings(geometry: Column): Column =
wrapExpression[ST_NumInteriorRings](geometry)
def ST_NumInteriorRings(geometry: String): Column =
wrapExpression[ST_NumInteriorRings](geometry)
+ def ST_NumInteriorRing(geometry: Column): Column =
wrapExpression[ST_NumInteriorRing](geometry)
+ def ST_NumInteriorRing(geometry: String): Column =
wrapExpression[ST_NumInteriorRing](geometry)
+
def ST_PointN(geometry: Column, n: Column): Column =
wrapExpression[ST_PointN](geometry, n)
def ST_PointN(geometry: String, n: Int): Column =
wrapExpression[ST_PointN](geometry, n)
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 3fee1e53c..807513951 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
@@ -1191,6 +1191,35 @@ class functionTestScala extends TestBaseScala with
Matchers with GeometrySample
.collect().toList should contain theSameElementsAs List((2, 0), (11, 1))
}
+ it("Should pass ST_NumInteriorRing") {
+ Given("Geometry DataFrame")
+ val geometryDf = Seq(
+ (1, "Point(21 52)"),
+ (2, "Polygon((0 0, 0 1, 1 1, 1 0, 0 0))"),
+ (3, "Linestring(0 0, 1 1, 1 0)"),
+ (4, "Linestring(0 0, 1 1, 1 0, 0 0)"),
+ (5, "MULTIPOINT ((10 40), (40 30), (20 20), (30 10))"),
+ (6, "MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20,
5 10, 15 5)))"),
+ (7, "MULTILINESTRING ((10 10, 20 20, 10 40, 10 10), (40 40, 30 30, 40
20, 30 10, 40 40))"),
+ (8, "MULTILINESTRING ((10 10, 20 20, 10 40, 10 10), (40 40, 30 30, 40
20, 30 10))"),
+ (9, "MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30
10))"),
+ (10, "GEOMETRYCOLLECTION (POINT (40 10), LINESTRING (10 10, 20 20, 10
40), POLYGON ((40 40, 20 45, 45 30, 40 40)))"),
+ (11, "POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 2 1, 2 2, 1 2, 1 1))")
+ ).map({ case (index, wkt) => Tuple2(index, wktReader.read(wkt))
}).toDF("id", "geom")
+
+ When("Using ST_NumInteriorRing")
+ val numberOfInteriorRings = geometryDf.selectExpr(
+ "id", "ST_NumInteriorRing(geom) as num"
+ )
+
+ Then("Result should match with expected values")
+
+ numberOfInteriorRings
+ .filter("num is not null")
+ .as[(Int, Int)]
+ .collect().toList should contain theSameElementsAs List((2, 0), (11, 1))
+ }
+
it("Should pass ST_AddPoint") {
Given("Geometry df")
val geometryDf = Seq(