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-585] [SEDONA-586] Add ST_ForceCollection, ST_Force3DZ #1460

Merged
merged 4 commits into from
Jun 7, 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
12 changes: 12 additions & 0 deletions common/src/main/java/org/apache/sedona/common/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -1535,6 +1535,18 @@ public static Geometry force3D(Geometry geometry) {
return GeomUtils.get3DGeom(geometry, 0.0);
}

public static Geometry forceCollection(Geometry geom) {
return new GeometryFactory().createGeometryCollection(convertGeometryToArray(geom));
}

private static Geometry[] convertGeometryToArray(Geometry geom) {
Geometry[] array = new Geometry[geom.getNumGeometries()];
for (int i = 0; i < array.length; i++) {
array[i] = geom.getGeometryN(i);
}
return array;
}

public static Integer nRings(Geometry geometry) throws Exception {
String geometryType = geometry.getGeometryType();
if (!(geometry instanceof Polygon || geometry instanceof MultiPolygon)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,34 @@ public void force3DObject3D() {
assertEquals(expectedDims, Functions.nDims(forcedLine3D));
}

@Test
public void forceCollection() throws ParseException {
Geometry geom = Constructors.geomFromWKT("MULTIPOINT (30 10, 40 40, 20 20, 10 30, 10 10, 20 50)", 0);
int actual = Functions.numGeometries(Functions.forceCollection(geom));
int expected = 6;
assertEquals(expected, actual);

geom = Constructors.geomFromWKT("MULTIPOLYGON(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 0,1 1 1,1 0 1,1 0 0,1 1 0)),((0 1 0,0 1 1,1 1 1,1 1 0,0 1 0)),((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)))", 0);
actual = Functions.numGeometries(Functions.forceCollection(geom));
expected = 5;
assertEquals(expected, actual);

geom = Constructors.geomFromWKT("MULTILINESTRING ((10 10, 20 20, 30 30), (15 15, 25 25, 35 35))", 0);
actual = Functions.numGeometries(Functions.forceCollection(geom));
expected = 2;
assertEquals(expected, actual);

geom = Constructors.geomFromWKT("POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))", 0);
actual = Functions.numGeometries(Functions.forceCollection(geom));
expected = 1;
assertEquals(expected, actual);

geom = Constructors.geomFromWKT("GEOMETRYCOLLECTION(POLYGON((0 0 1,0 5 1,5 0 1,0 0 1),(1 1 1,3 1 1,1 3 1,1 1 1)))",0);
String actualWKT = Functions.asWKT(Functions.forceCollection(geom));
String expectedWKT = "GEOMETRYCOLLECTION Z(POLYGON Z((0 0 1, 0 5 1, 5 0 1, 0 0 1), (1 1 1, 3 1 1, 1 3 1, 1 1 1)))";
assertEquals(expectedWKT, actualWKT);
}

@Test
public void force3DObject3DDefaultValue() {
int expectedDims = 3;
Expand Down
62 changes: 62 additions & 0 deletions docs/api/flink/Function.md
Original file line number Diff line number Diff line change
Expand Up @@ -1266,6 +1266,68 @@ Output:
LINESTRING EMPTY
```

## ST_Force3DZ

Introduction: Forces the geometry into a 3-dimensional model so that all output representations will have X, Y and Z coordinates.
An optionally given zValue is tacked onto the geometry if the geometry is 2-dimensional. Default value of zValue is 0.0
If the given geometry is 3-dimensional, no change is performed on it.
If the given geometry is empty, no change is performed on it. This function is an alias for [ST_Force3D](#st_force3d).

!!!Note
Example output is after calling ST_AsText() on returned geometry, which adds Z for in the WKT for 3D geometries

Format: `ST_Force3DZ(geometry: Geometry, zValue: Double)`

Since: `v1.6.1`

SQL Example

```sql
SELECT ST_AsText(ST_Force3DZ(ST_GeomFromText('POLYGON((0 0 2,0 5 2,5 0 2,0 0 2),(1 1 2,3 1 2,1 3 2,1 1 2))'), 2.3))
```

Output:

```
POLYGON Z((0 0 2, 0 5 2, 5 0 2, 0 0 2), (1 1 2, 3 1 2, 1 3 2, 1 1 2))
```

SQL Example

```sql
SELECT ST_AsText(ST_Force3DZ(ST_GeomFromText('LINESTRING(0 1,1 0,2 0)'), 2.3))
```

Output:

```
LINESTRING Z(0 1 2.3, 1 0 2.3, 2 0 2.3)
```

## ST_ForceCollection

Introduction: This function converts the input geometry into a GeometryCollection, regardless of the original geometry type. If the input is a multipart geometry, such as a MultiPolygon or MultiLineString, it will be decomposed into a GeometryCollection containing each individual Polygon or LineString element from the original multipart geometry.

Format: `ST_ForceCollection(geom: Geometry)`

Since: `v1.6.1`

SQL Example

```sql
SELECT ST_ForceCollection(
ST_GeomFromWKT(
"MULTIPOINT (30 10, 40 40, 20 20, 10 30, 10 10, 20 50)"
)
)
```

Output:

```
GEOMETRYCOLLECTION (POINT (30 10), POINT (40 40), POINT (20 20), POINT (10 30), POINT (10 10), POINT (20 50))
```

## ST_ForcePolygonCCW

Introduction: For (Multi)Polygon geometries, this function sets the exterior ring orientation to counter-clockwise and interior rings to clockwise orientation. Non-polygonal geometries are returned unchanged.
Expand Down
58 changes: 58 additions & 0 deletions docs/api/snowflake/vector-data/Function.md
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,64 @@ Input: `LINESTRING EMPTY`

Output: `LINESTRING EMPTY`

## ST_Force3DZ

Introduction: Forces the geometry into a 3-dimensional model so that all output representations will have X, Y and Z coordinates.
An optionally given zValue is tacked onto the geometry if the geometry is 2-dimensional. Default value of zValue is 0.0
If the given geometry is 3-dimensional, no change is performed on it.
If the given geometry is empty, no change is performed on it. This function is an alias for [ST_Force3D](#st_force3d).

!!!Note
Example output is after calling ST_AsText() on returned geometry, which adds Z for in the WKT for 3D geometries

Format: `ST_Force3DZ(geometry: Geometry, zValue: Double)`

SQL Example

```sql
SELECT ST_AsText(ST_Force3DZ(ST_GeomFromText('POLYGON((0 0 2,0 5 2,5 0 2,0 0 2),(1 1 2,3 1 2,1 3 2,1 1 2))'), 2.3))
```

Output:

```
POLYGON Z((0 0 2, 0 5 2, 5 0 2, 0 0 2), (1 1 2, 3 1 2, 1 3 2, 1 1 2))
```

SQL Example

```sql
SELECT ST_AsText(ST_Force3DZ(ST_GeomFromText('LINESTRING(0 1,1 0,2 0)'), 2.3))
```

Output:

```
LINESTRING Z(0 1 2.3, 1 0 2.3, 2 0 2.3)
```

## ST_ForceCollection

Introduction: This function converts the input geometry into a GeometryCollection, regardless of the original geometry type. If the input is a multipart geometry, such as a MultiPolygon or MultiLineString, it will be decomposed into a GeometryCollection containing each individual Polygon or LineString element from the original multipart geometry.

Format: `ST_ForceCollection(geom: Geometry)`

SQL Example

```sql
SELECT ST_ForceCollection(
ST_GeomFromWKT(
"MULTIPOINT (30 10, 40 40, 20 20, 10 30, 10 10, 20 50)"
)
)
```

Output:

```
GEOMETRYCOLLECTION (POINT (30 10), POINT (40 40), POINT (20 20), POINT (10 30), POINT (10 10), POINT (20 50))
```

## ST_ForcePolygonCCW

Introduction: For (Multi)Polygon geometries, this function sets the exterior ring orientation to counter-clockwise and interior rings to clockwise orientation. Non-polygonal geometries are returned unchanged.
Expand Down
62 changes: 62 additions & 0 deletions docs/api/sql/Function.md
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,68 @@ Output:
LINESTRING EMPTY
```

## ST_Force3DZ

Introduction: Forces the geometry into a 3-dimensional model so that all output representations will have X, Y and Z coordinates.
An optionally given zValue is tacked onto the geometry if the geometry is 2-dimensional. Default value of zValue is 0.0
If the given geometry is 3-dimensional, no change is performed on it.
If the given geometry is empty, no change is performed on it. This function is an alias for [ST_Force3D](#st_force3d).

!!!Note
Example output is after calling ST_AsText() on returned geometry, which adds Z for in the WKT for 3D geometries

Format: `ST_Force3DZ(geometry: Geometry, zValue: Double)`

Since: `v1.6.1`

SQL Example

```sql
SELECT ST_AsText(ST_Force3DZ(ST_GeomFromText('POLYGON((0 0 2,0 5 2,5 0 2,0 0 2),(1 1 2,3 1 2,1 3 2,1 1 2))'), 2.3))
```

Output:

```
POLYGON Z((0 0 2, 0 5 2, 5 0 2, 0 0 2), (1 1 2, 3 1 2, 1 3 2, 1 1 2))
```

SQL Example

```sql
SELECT ST_AsText(ST_Force3DZ(ST_GeomFromText('LINESTRING(0 1,1 0,2 0)'), 2.3))
```

Output:

```
LINESTRING Z(0 1 2.3, 1 0 2.3, 2 0 2.3)
```

## ST_ForceCollection

Introduction: This function converts the input geometry into a GeometryCollection, regardless of the original geometry type. If the input is a multipart geometry, such as a MultiPolygon or MultiLineString, it will be decomposed into a GeometryCollection containing each individual Polygon or LineString element from the original multipart geometry.

Format: `ST_ForceCollection(geom: Geometry)`

Since: `v1.6.1`

SQL Example

```sql
SELECT ST_ForceCollection(
ST_GeomFromWKT(
"MULTIPOINT (30 10, 40 40, 20 20, 10 30, 10 10, 20 50)"
)
)
```

Output:

```
GEOMETRYCOLLECTION (POINT (30 10), POINT (40 40), POINT (20 20), POINT (10 30), POINT (10 10), POINT (20 50))
```

## ST_ForcePolygonCCW

Introduction: For (Multi)Polygon geometries, this function sets the exterior ring orientation to counter-clockwise and interior rings to clockwise orientation. Non-polygonal geometries are returned unchanged.
Expand Down
2 changes: 2 additions & 0 deletions flink/src/main/java/org/apache/sedona/flink/Catalog.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ public static UserDefinedFunction[] getFuncs() {
new Functions.ST_GeometricMedian(),
new Functions.ST_NumPoints(),
new Functions.ST_Force3D(),
new Functions.ST_Force3DZ(),
new Functions.ST_ForceCollection(),
new Functions.ST_ForcePolygonCW(),
new Functions.ST_ForceRHR(),
new Functions.ST_NRings(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,30 @@ public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.j
}
}

public static class ST_Force3DZ 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,
@DataTypeHint("Double") Double zValue) {
Geometry geometry = (Geometry) o;
return org.apache.sedona.common.Functions.force3D(geometry, zValue);
}

@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) {
Geometry geometry = (Geometry) o;
return org.apache.sedona.common.Functions.force3D(geometry);
}
}

public static class ST_ForceCollection 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) {
Geometry geometry = (Geometry) o;
return org.apache.sedona.common.Functions.forceCollection(geometry);
}
}

public static class ST_ForcePolygonCW 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
45 changes: 45 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 @@ -1284,6 +1284,51 @@ public void testForce3DDefaultValue() {
assertEquals(expectedDims, actual);
}

@Test
public void testForce3DZ() {
Integer expectedDims = 3;
Table polyTable = tableEnv.sqlQuery("SELECT ST_Force3DZ(ST_GeomFromWKT('LINESTRING(0 1, 1 0, 2 0)'), 1.2) " +
"AS " + polygonColNames[0]);
Integer actual = (Integer) first(polyTable.select(call(Functions.ST_NDims.class.getSimpleName(), $(polygonColNames[0])))).getField(0);
assertEquals(expectedDims, actual);

polyTable = tableEnv.sqlQuery("SELECT ST_Force3DZ(ST_GeomFromWKT('LINESTRING(0 1, 1 0, 2 0)')) " +
"AS " + polygonColNames[0]);
actual = (Integer) first(polyTable.select(call(Functions.ST_NDims.class.getSimpleName(), $(polygonColNames[0])))).getField(0);
assertEquals(expectedDims, actual);
}

@Test
public void testForceCollection() {
int actual = (int) first(
tableEnv.sqlQuery("SELECT ST_GeomFromWKT('MULTIPOINT (30 10, 40 40, 20 20, 10 30, 10 10, 20 50)') AS geom").select(call(Functions.ST_ForceCollection.class.getSimpleName(), $("geom"))).as("geom")
.select(call(Functions.ST_NumGeometries.class.getSimpleName(), $("geom")))
).getField(0);
int expected = 6;
assertEquals(expected, actual);

actual = (int) first(
tableEnv.sqlQuery("SELECT ST_GeomFromWKT('MULTIPOLYGON(((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),((1 1 0,1 1 1,1 0 1,1 0 0,1 1 0)),((0 1 0,0 1 1,1 1 1,1 1 0,0 1 0)),((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)))') AS geom").select(call(Functions.ST_ForceCollection.class.getSimpleName(), $("geom"))).as("geom")
.select(call(Functions.ST_NumGeometries.class.getSimpleName(), $("geom")))
).getField(0);
expected = 5;
assertEquals(expected, actual);

actual = (int) first(
tableEnv.sqlQuery("SELECT ST_GeomFromWKT('MULTILINESTRING ((10 10, 20 20, 30 30), (15 15, 25 25, 35 35))') AS geom").select(call(Functions.ST_ForceCollection.class.getSimpleName(), $("geom"))).as("geom")
.select(call(Functions.ST_NumGeometries.class.getSimpleName(), $("geom")))
).getField(0);
expected = 2;
assertEquals(expected, actual);

actual = (int) first(
tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))') AS geom").select(call(Functions.ST_ForceCollection.class.getSimpleName(), $("geom"))).as("geom")
.select(call(Functions.ST_NumGeometries.class.getSimpleName(), $("geom")))
).getField(0);
expected = 1;
assertEquals(expected, actual);
}

@Test
public void testTriangulatePolygon() {
Table polyTable = tableEnv.sqlQuery("SELECT ST_TriangulatePolygon(ST_GeomFromWKT('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (5 5, 5 8, 8 8, 8 5, 5 5))')) as poly");
Expand Down
21 changes: 21 additions & 0 deletions python/sedona/sql/st_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,27 @@ def ST_Force3D(geometry: ColumnOrName, zValue: Optional[Union[ColumnOrName, floa
args = (geometry, zValue)
return _call_st_function("ST_Force3D", args)

@validate_argument_types
def ST_Force3DZ(geometry: ColumnOrName, zValue: Optional[Union[ColumnOrName, float]] = 0.0) -> Column:
"""
Return a geometry with a 3D coordinate of value 'zValue' forced upon it. No change happens if the geometry is already 3D
:param zValue: Optional value of z coordinate to be potentially added, default value is 0.0
:param geometry: Geometry column to make 3D
:return: 3D geometry with either already present z coordinate if any, or zcoordinate with given zValue
"""
args = (geometry, zValue)
return _call_st_function("ST_Force3DZ", args)

@validate_argument_types
def ST_ForceCollection(geometry: ColumnOrName) -> Column:
"""
Converts a geometry to a geometry collection

:param geometry: Geometry column to change orientation
:return: a Geometry Collection
"""
return _call_st_function("ST_ForceCollection", geometry)

@validate_argument_types
def ST_ForcePolygonCW(geometry: ColumnOrName) -> Column:
"""
Expand Down
4 changes: 4 additions & 0 deletions python/tests/sql/test_dataframe_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@
(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_Force3DZ, ("point", 1.0), "point_geom", "", "POINT Z (0 1 1)"),
(stf.ST_ForceCollection, ("multipoint",), "multipoint_geom", "ST_NumGeometries(geom)", 4),
(stf.ST_ForcePolygonCW, ("geom",), "geom_with_hole", "", "POLYGON ((0 0, 3 3, 3 0, 0 0), (1 1, 2 1, 2 2, 1 1))"),
(stf.ST_ForcePolygonCCW, ("geom",), "geom_with_hole", "", "POLYGON ((0 0, 3 0, 3 3, 0 0), (1 1, 2 2, 2 1, 1 1))"),
(stf.ST_ForceRHR, ("geom",), "geom_with_hole", "", "POLYGON ((0 0, 3 3, 3 0, 0 0), (1 1, 2 1, 2 2, 1 1))"),
Expand Down Expand Up @@ -296,6 +298,8 @@
(stf.ST_ExteriorRing, (None,)),
(stf.ST_FlipCoordinates, (None,)),
(stf.ST_Force_2D, (None,)),
(stf.ST_Force3DZ, (None,)),
(stf.ST_ForceCollection, (None,)),
(stf.ST_ForcePolygonCW, (None,)),
(stf.ST_ForcePolygonCCW, (None,)),
(stf.ST_ForceRHR, (None,)),
Expand Down
Loading
Loading