Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SEDONA-564] Add ST_NumInteriorRing #1434

Merged
merged 2 commits into from
May 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/api/flink/Function.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 18 additions & 0 deletions docs/api/snowflake/vector-data/Function.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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 @@ -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.
Expand Down
1 change: 1 addition & 0 deletions flink/src/main/java/org/apache/sedona/flink/Catalog.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public static UserDefinedFunction[] getFuncs() {
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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,14 @@ public Integer eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jt
}
}

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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,13 @@ public void testNumInteriorRings() {
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);
Expand Down
11 changes: 11 additions & 0 deletions python/sedona/sql/st_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions python/tests/sql/test_dataframe_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
(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)"),
Expand Down Expand Up @@ -314,6 +315,7 @@
(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)),
Expand Down
23 changes: 22 additions & 1 deletion python/tests/sql/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ def test_st_is_closed(self):
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))"),
Expand All @@ -802,6 +802,27 @@ def test_num_interior_ring(self):
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)"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,19 @@ public void test_ST_NumInteriorRings() {
);

}

@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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,19 @@ public void test_ST_NumInteriorRings() {
);

}

@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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -875,6 +875,13 @@ public static Integer ST_NumInteriorRings(byte[] geometry) {
);
}

@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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,13 @@ public static Integer ST_NumInteriorRings(String geometry) {
);
}

@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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,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](),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Loading