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 5eb11d3b [SEDONA-342] Implement ST_Polygon (#944)
5eb11d3b is described below
commit 5eb11d3b5e0fb73b34d266ec2d1dc4be96e1802b
Author: Junhao Liu <[email protected]>
AuthorDate: Sun Aug 6 04:32:43 2023 +0800
[SEDONA-342] Implement ST_Polygon (#944)
---
.../java/org/apache/sedona/common/Functions.java | 8 ++++++++
.../org/apache/sedona/common/FunctionsTest.java | 15 ++++++++++++++
docs/api/flink/Function.md | 20 ++++++++++++++++++
docs/api/sql/Function.md | 20 ++++++++++++++++++
.../main/java/org/apache/sedona/flink/Catalog.java | 1 +
.../apache/sedona/flink/expressions/Functions.java | 9 ++++++++
.../java/org/apache/sedona/flink/FunctionTest.java | 9 ++++++++
python/sedona/sql/st_functions.py | 14 +++++++++++++
python/tests/sql/test_dataframe_api.py | 1 +
python/tests/sql/test_function.py | 24 ++++++++++++++++++++++
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 1 +
.../sql/sedona_sql/expressions/Functions.scala | 8 ++++++++
.../sql/sedona_sql/expressions/st_functions.scala | 3 +++
.../apache/sedona/sql/dataFrameAPITestScala.scala | 9 ++++++++
.../org/apache/sedona/sql/functionTestScala.scala | 9 ++++++++
15 files changed, 151 insertions(+)
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 8adc4ec5..032a2cd6 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -733,6 +733,14 @@ public class Functions {
}
}
+ public static Geometry makepolygonWithSRID(Geometry lineString, Integer
srid) {
+ Geometry geom = makePolygon(lineString, null);
+ if(geom != null) {
+ geom.setSRID(srid);
+ }
+ return geom;
+ }
+
public static Geometry createMultiGeometry(Geometry[] geometries) {
if (geometries.length > 1){
return GEOMETRY_FACTORY.buildGeometry(Arrays.asList(geometries));
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 d0e8411f..75b50e03 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -529,6 +529,21 @@ public class FunctionsTest {
assertEquals("Median failed to converge within 1.0E-06 after 5
iterations.", e.getMessage());
}
+ @Test
+ public void makepolygonWithSRID() {
+ Geometry lineString1 = GEOMETRY_FACTORY.createLineString(coordArray(0,
0, 1, 1, 1, 0, 0, 0));
+ Geometry actual1 = Functions.makepolygonWithSRID(lineString1, 4326);
+ Geometry expected1 = GEOMETRY_FACTORY.createPolygon(coordArray(0, 0,
1, 1, 1, 0, 0, 0));
+ assertEquals(expected1.toText(), actual1.toText());
+ assertEquals(4326, actual1.getSRID());
+
+ Geometry lineString2 =
GEOMETRY_FACTORY.createLineString(coordArray3d(75, 29, 1, 77, 29, 2, 77, 29, 3,
75, 29, 1));
+ Geometry actual2 = Functions.makepolygonWithSRID(lineString2, 123);
+ Geometry expected2 = GEOMETRY_FACTORY.createPolygon(coordArray3d(75,
29, 1, 77, 29, 2, 77, 29, 3, 75, 29, 1));
+ assertEquals(expected2.toText(), actual2.toText());
+ assertEquals(123, actual2.getSRID());
+ }
+
@Test
public void haversineDistance() {
// Basic check
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 44a2ea91..f08ddfcc 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -1883,6 +1883,26 @@ FROM df
Output: `POINT Z(0 0 1)`
+## ST_Polygon
+
+Introduction: Function to create a polygon built from the given LineString and
sets the spatial reference system from the srid
+
+Format: `ST_Polygon(geom: geometry, srid: integer)`
+
+Since: `v1.5.0`
+
+Example:
+
+```sql
+SELECT ST_AsText( ST_Polygon(ST_GeomFromEWKT('LINESTRING(75 29 1, 77 29 2, 77
29 3, 75 29 1)'), 4326) );
+```
+
+Output:
+
+```
+POLYGON((75 29 1, 77 29 2, 77 29 3, 75 29 1))
+```
+
## ST_ReducePrecision
Introduction: Reduce the decimals places in the coordinates of the geometry to
the given number of decimal places. The last decimal place will be rounded.
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index b86d9e33..15ef43f6 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -1886,6 +1886,26 @@ SELECT
ST_AsText(ST_PointOnSurface(ST_GeomFromText('LINESTRING(0 5 1, 0 0 1, 0 1
```
+## ST_Polygon
+
+Introduction: Function to create a polygon built from the given LineString and
sets the spatial reference system from the srid
+
+Format: `ST_Polygon(geom: geometry, srid: integer)`
+
+Since: `v1.5.0`
+
+Example:
+
+```sql
+SELECT ST_AsText( ST_Polygon(ST_GeomFromEWKT('LINESTRING(75 29 1, 77 29 2, 77
29 3, 75 29 1)'), 4326) );
+```
+
+Output:
+
+```
+POLYGON((75 29 1, 77 29 2, 77 29 3, 75 29 1))
+```
+
## ST_ReducePrecision
Introduction: Reduce the decimals places in the coordinates of the geometry to
the given number of decimal places. The last decimal place will be rounded.
This function was called ST_PrecisionReduce in versions prior to v1.5.0.
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 abc9bc5f..93a85a60 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -115,6 +115,7 @@ public class Catalog {
new Functions.ST_LineMerge(),
new Functions.ST_LineSubstring(),
new Functions.ST_MakeLine(),
+ new Functions.ST_Polygon(),
new Functions.ST_MakePolygon(),
new Functions.ST_MakeValid(),
new Functions.ST_MinimumBoundingCircle(),
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 1dafc8a7..84c17ca3 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
@@ -719,6 +719,15 @@ public class Functions {
}
}
+ public static class ST_Polygon 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 o1,
+ @DataTypeHint("Integer") Integer srid) {
+ Geometry linestring = (Geometry) o1;
+ return
org.apache.sedona.common.Functions.makepolygonWithSRID(linestring, srid);
+ }
+ }
+
public static class ST_MakeValid 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 9a57ce10..8e3caf32 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -794,6 +794,15 @@ public class FunctionTest extends TestBase{
assertEquals("LINESTRING (0 0, 1 1)", result.toString());
}
+ @Test
+ public void testPolygon() {
+ Table table = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING (0
0, 1 0, 1 1, 0 0)') AS line");
+ table = table.select(call(Functions.ST_Polygon.class.getSimpleName(),
$("line"), 4236));
+ Geometry result = (Geometry) first(table).getField(0);
+ assertEquals("POLYGON ((0 0, 1 0, 1 1, 0 0))", result.toString());
+ assertEquals(4236, result.getSRID());
+ }
+
@Test
public void testMakePolygon() {
Table table = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING (0
0, 1 0, 1 1, 0 0)') AS line");
diff --git a/python/sedona/sql/st_functions.py
b/python/sedona/sql/st_functions.py
index 02712e77..f5922faa 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -76,6 +76,7 @@ __all__ = [
"ST_LineMerge",
"ST_LineSubstring",
"ST_MakeLine",
+ "ST_Polygon"
"ST_MakePolygon",
"ST_MakeValid",
"ST_MinimumBoundingCircle",
@@ -827,6 +828,19 @@ def ST_MakeLine(geom1: ColumnOrName, geom2:
Optional[ColumnOrName] = None) -> Co
args = (geom1,) if geom2 is None else (geom1, geom2)
return _call_st_function("ST_MakeLine", args)
+@validate_argument_types
+def ST_Polygon(line_string: ColumnOrName, srid: ColumnOrNameOrNumber) ->
Column:
+ """Create a polygon built from the given LineString and sets the spatial
reference system from the srid.
+
+ :param line_string: Closed linestring geometry column that describes the
exterior ring of the polygon.
+ :type line_string: ColumnOrName
+ :param srid: Spatial reference system identifier.
+ :type srid: ColumnOrNameOrNumber
+ :return: Polygon geometry column created from the input linestring.
+ :rtype: Column
+ """
+ return _call_st_function("ST_Polygon", (line_string, srid))
+
@validate_argument_types
def ST_MakePolygon(line_string: ColumnOrName, holes: Optional[ColumnOrName] =
None) -> Column:
"""Create a polygon geometry from a linestring describing the exterior
ring as well as an array of linestrings describing holes.
diff --git a/python/tests/sql/test_dataframe_api.py
b/python/tests/sql/test_dataframe_api.py
index e5f49f34..4ed5b463 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -119,6 +119,7 @@ test_configurations = [
(stf.ST_LineSubstring, ("line", 0.5, 1.0), "linestring_geom", "",
"LINESTRING (2.5 0, 3 0, 4 0, 5 0)"),
(stf.ST_MakeValid, ("geom",), "invalid_geom", "", "MULTIPOLYGON (((1 5, 3
3, 1 1, 1 5)), ((5 3, 7 5, 7 1, 5 3)))"),
(stf.ST_MakeLine, ("line1", "line2"), "two_lines", "", "LINESTRING (0 0, 1
1, 0 0, 3 2)"),
+ (stf.ST_Polygon, ("geom", 4236), "closed_linestring_geom", "", "POLYGON
((0 0, 1 0, 1 1, 0 0))"),
(stf.ST_MakePolygon, ("geom",), "closed_linestring_geom", "", "POLYGON ((0
0, 1 0, 1 1, 0 0))"),
(stf.ST_MinimumBoundingCircle, ("line", 8), "linestring_geom",
"ST_ReducePrecision(geom, 2)", "POLYGON ((4.95 -0.49, 4.81 -0.96, 4.58 -1.39,
4.27 -1.77, 3.89 -2.08, 3.46 -2.31, 2.99 -2.45, 2.5 -2.5, 2.01 -2.45, 1.54
-2.31, 1.11 -2.08, 0.73 -1.77, 0.42 -1.39, 0.19 -0.96, 0.05 -0.49, 0 0, 0.05
0.49, 0.19 0.96, 0.42 1.39, 0.73 1.77, 1.11 2.08, 1.54 2.31, 2.01 2.45, 2.5
2.5, 2.99 2.45, 3.46 2.31, 3.89 2.08, 4.27 1.77, 4.58 1.39, 4.81 0.96, 4.95
0.49, 5 0, 4.95 -0.49))"),
(stf.ST_MinimumBoundingCircle, ("line", 2), "linestring_geom",
"ST_ReducePrecision(geom, 2)", "POLYGON ((4.27 -1.77, 2.5 -2.5, 0.73 -1.77, 0
0, 0.73 1.77, 2.5 2.5, 4.27 1.77, 5 0, 4.27 -1.77))"),
diff --git a/python/tests/sql/test_function.py
b/python/tests/sql/test_function.py
index 2cd9b2c9..65954133 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -829,6 +829,30 @@ class TestPredicateJoin(TestBase):
for actual, expected in result:
assert actual == expected
+ def test_st_polygon(self):
+ # Given
+ geometry_df = self.spark.createDataFrame(
+ [
+ ["POINT(21 52)", 4238, None],
+ ["POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35,
30 20, 20 30))", 4237, None],
+ ["LINESTRING (0 0, 0 1, 1 0, 0 0)", 4236, "POLYGON ((0 0, 0 1,
1 0, 0 0))"]
+ ]
+ ).selectExpr("ST_GeomFromText(_1) AS geom", "_2 AS srid", "_3 AS
expected")
+
+ # When calling st_Polygon
+ geom_poly = geometry_df.withColumn("polygon", expr("ST_Polygon(geom,
srid)"))
+
+ # Then only based on closed linestring geom is created
+ geom_poly.filter("polygon IS NOT
NULL").selectExpr("ST_AsText(polygon)", "expected"). \
+ show()
+ result = geom_poly.filter("polygon IS NOT
NULL").selectExpr("ST_AsText(polygon)", "expected"). \
+ collect()
+
+ assert result.__len__() == 1
+
+ for actual, expected in result:
+ assert actual == expected
+
def test_st_make_polygon(self):
# Given
geometry_df = self.spark.createDataFrame(
diff --git a/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
b/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
index c2f3c43f..755ba799 100644
--- a/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
+++ b/sql/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
@@ -124,6 +124,7 @@ object Catalog {
function[ST_SubDivideExplode](),
function[ST_SubDivide](),
function[ST_MakeLine](),
+ function[ST_Polygon](),
function[ST_MakePolygon](null),
function[ST_GeoHash](),
function[ST_GeomFromGeoHash](null),
diff --git
a/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
b/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
index 4c27fc11..de8f808c 100644
---
a/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
+++
b/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
@@ -696,6 +696,14 @@ case class ST_MakeLine(inputExpressions: Seq[Expression])
}
}
+case class ST_Polygon(inputExpressions: Seq[Expression])
+ extends InferredExpression(Functions.makepolygonWithSRID _) {
+
+ protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) =
{
+ copy(inputExpressions = newChildren)
+ }
+}
+
case class ST_MakePolygon(inputExpressions: Seq[Expression])
extends
InferredExpression(InferrableFunction.allowRightNull(Functions.makePolygon)) {
diff --git
a/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
b/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
index 35914ab2..af6a5fe9 100644
---
a/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
+++
b/sql/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
@@ -173,6 +173,9 @@ object st_functions extends DataFrameAPI {
def ST_MakeLine(geom1: Column, geom2: Column): Column =
wrapExpression[ST_MakeLine](geom1, geom2)
def ST_MakeLine(geom1: String, geom2: String): Column =
wrapExpression[ST_MakeLine](geom1, geom2)
+ def ST_Polygon(lineString: Column, srid: Column): Column =
wrapExpression[ST_Polygon](lineString, srid)
+ def ST_Polygon(lineString: String, srid: Integer): Column =
wrapExpression[ST_Polygon](lineString, srid)
+
def ST_MakePolygon(lineString: Column): Column =
wrapExpression[ST_MakePolygon](lineString, null)
def ST_MakePolygon(lineString: String): Column =
wrapExpression[ST_MakePolygon](lineString, null)
def ST_MakePolygon(lineString: Column, holes: Column): Column =
wrapExpression[ST_MakePolygon](lineString, holes)
diff --git
a/sql/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
b/sql/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
index 5d801d41..3de6fe4f 100644
---
a/sql/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
+++
b/sql/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
@@ -317,6 +317,15 @@ class dataFrameAPITestScala extends TestBaseScala {
assert(actualResult == expectedResult)
}
+ it("Passed ST_Polygon") {
+ val invalidDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING (0
0, 1 0, 1 1, 0 0)') AS geom")
+ val df = invalidDf.select(ST_Polygon("geom", 4236))
+ val actualResult = df.take(1)(0).get(0).asInstanceOf[Geometry]
+ val expectedResult = "POLYGON ((0 0, 1 0, 1 1, 0 0))"
+ assert(actualResult.toText() == expectedResult)
+ assert(actualResult.getSRID() == 4236)
+ }
+
it("Passed `ST_MakePolygon`") {
val invalidDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING (0
0, 1 0, 1 1, 0 0)') AS geom")
val df = invalidDf.select(ST_MakePolygon("geom"))
diff --git
a/sql/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
b/sql/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
index 3c14bb88..93216481 100644
--- a/sql/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
+++ b/sql/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
@@ -337,6 +337,15 @@ class functionTestScala extends TestBaseScala with
Matchers with GeometrySample
assert(testtable.take(1)(0).get(0).asInstanceOf[Geometry].toText.equals("LINESTRING
(1 2, 3 4)"))
}
+ it("Passed ST_Polygon") {
+
+ var testtable = sparkSession.sql(
+ "SELECT ST_Polygon(ST_GeomFromText('LINESTRING(75.15 29.53,77 29,77.6
29.5, 75.15 29.53)'), 4326)"
+ )
+ assert(testtable.take(1)(0).get(0).asInstanceOf[Geometry].toText ==
"POLYGON ((75.15 29.53, 77 29, 77.6 29.5, 75.15 29.53))")
+ assert(testtable.take(1)(0).get(0).asInstanceOf[Geometry].getSRID ==
4326)
+ }
+
it("Passed ST_MakeValid On Invalid Polygon") {
val df = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON((1 5, 1 1, 3
3, 5 3, 7 1, 7 5, 5 3, 3 3, 1 5))') AS polygon")