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 20e0de40 [SEDONA-237] Add ST_Dimension (#867)
20e0de40 is described below
commit 20e0de400f725ba68c1d696590109db23fdb53c4
Author: Junhao Liu <[email protected]>
AuthorDate: Sun Jun 25 09:17:24 2023 +0800
[SEDONA-237] Add ST_Dimension (#867)
Co-authored-by: Jia Yu <[email protected]>
---
.../java/org/apache/sedona/common/Functions.java | 8 +++
.../org/apache/sedona/common/FunctionsTest.java | 76 ++++++++++++++++++++++
docs/api/flink/Function.md | 21 ++++++
docs/api/sql/Function.md | 22 +++++++
.../main/java/org/apache/sedona/flink/Catalog.java | 1 +
.../apache/sedona/flink/expressions/Functions.java | 10 +++
.../java/org/apache/sedona/flink/FunctionTest.java | 10 +++
python/sedona/sql/st_functions.py | 11 ++++
python/tests/sql/test_dataframe_api.py | 3 +
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 1 +
.../sql/sedona_sql/expressions/Functions.scala | 7 ++
.../sql/sedona_sql/expressions/st_functions.scala | 3 +
.../apache/sedona/sql/dataFrameAPITestScala.scala | 8 +++
.../org/apache/sedona/sql/functionTestScala.scala | 31 +++++++++
14 files changed, 212 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 0f006493..8997a508 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -555,6 +555,14 @@ public class Functions {
return new GeometrySplitter(GEOMETRY_FACTORY).split(input, blade);
}
+ public static Integer dimension(Geometry geometry) {
+ Integer dimension = geometry.getDimension();
+ // unknown dimension such as an empty GEOMETRYCOLLECTION
+ if (dimension < 0) {
+ dimension = 0;
+ }
+ return dimension;
+ }
/**
* get the coordinates of a geometry and transform to Google s2 cell id
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 9b6e42c0..6901cb7c 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -267,6 +267,82 @@ public class FunctionsTest {
assertNull(actualResult);
}
+ @Test
+ public void dimensionGeometry2D() {
+ Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(1, 2));
+ Integer actualResult = Functions.dimension(point);
+ Integer expectedResult = 0;
+ assertEquals(actualResult, expectedResult);
+
+ LineString lineString =
GEOMETRY_FACTORY.createLineString(coordArray(0.0, 0.0, 1.5, 1.5));
+ actualResult = Functions.dimension(lineString);
+ expectedResult = 1;
+ assertEquals(actualResult, expectedResult);
+
+ Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray(0.0, 0.0,
1.0, 1.0, 1.0, 0.0, 0.0, 0.0));
+ actualResult = Functions.dimension(polygon);
+ expectedResult = 2;
+ assertEquals(actualResult, expectedResult);
+
+ MultiPoint multiPoint =
GEOMETRY_FACTORY.createMultiPointFromCoords(coordArray(0.0, 0.0, 1.0, 1.0));
+ actualResult = Functions.dimension(multiPoint);
+ expectedResult = 0;
+ assertEquals(actualResult, expectedResult);
+
+ LineString lineString2 =
GEOMETRY_FACTORY.createLineString(coordArray(1.0, 1.0, 2.0, 2.0));
+ MultiLineString multiLineString = GEOMETRY_FACTORY
+ .createMultiLineString(new LineString[] { lineString,
lineString2 });
+ actualResult = Functions.dimension(multiLineString);
+ expectedResult = 1;
+ assertEquals(actualResult, expectedResult);
+
+ Polygon polygon2 = GEOMETRY_FACTORY.createPolygon(coordArray(0.0, 0.0,
2.0, 2.0, 1.0, 0.0, 0.0, 0.0));
+ MultiPolygon multiPolygon = GEOMETRY_FACTORY.createMultiPolygon(new
Polygon[] { polygon, polygon2 });
+ actualResult = Functions.dimension(multiPolygon);
+ expectedResult = 2;
+ assertEquals(actualResult, expectedResult);
+ }
+
+ @Test
+ public void dimensionGeometry3D() {
+ Point point3D = GEOMETRY_FACTORY.createPoint(new Coordinate(1, 1, 1));
+ Integer actualResult = Functions.dimension(point3D);
+ Integer expectedResult = 0;
+ assertEquals(actualResult, expectedResult);
+
+ LineString lineString3D =
GEOMETRY_FACTORY.createLineString(coordArray(1, 0, 1, 1, 1, 2));
+ actualResult = Functions.dimension(lineString3D);
+ expectedResult = 1;
+ assertEquals(actualResult, expectedResult);
+
+ Polygon polygon3D = GEOMETRY_FACTORY.createPolygon(coordArray3d(1, 1,
1, 2, 2, 2, 3, 3, 3, 1, 1, 1));
+ actualResult = Functions.dimension(polygon3D);
+ expectedResult = 2;
+ assertEquals(actualResult, expectedResult);
+ }
+
+ @Test
+ public void dimensionGeometryCollection() {
+ Geometry[] geometry = new Geometry[] {
+ GEOMETRY_FACTORY.createLineString(coordArray(0.0, 0.0, 1.5,
1.5)),
+ GEOMETRY_FACTORY.createPolygon(coordArray(0.0, 0.0, 1.0, 1.0,
1.0, 0.0, 0.0, 0.0))
+ };
+ GeometryCollection geometryCollection =
GEOMETRY_FACTORY.createGeometryCollection(geometry);
+
+ Integer actualResult = Functions.dimension(geometryCollection);
+ Integer expectedResult = 2;
+ assertEquals(actualResult, expectedResult);
+ }
+
+ @Test
+ public void dimensionGeometryEmpty() {
+ GeometryCollection emptyGeometryCollection =
GEOMETRY_FACTORY.createGeometryCollection();
+
+ Integer actualResult = Functions.dimension(emptyGeometryCollection);
+ Integer expectedResult = 0;
+ assertEquals(actualResult, expectedResult);
+ }
+
private static boolean intersects(Set<?> s1, Set<?> s2) {
Set<?> copy = new HashSet<>(s1);
copy.retainAll(s2);
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 34ce65b6..cc8ee0c0 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -289,6 +289,27 @@ Input: `Polygon ((0 0, 1 2, 2 2, 3 2, 5 0, 4 0, 3 1, 2 1,
1 0, 0 0))`
Output: `POLYGON ((1 2, 2 2, 3 2, 5 0, 4 0, 1 0, 0 0, 1 2))`
+## ST_Dimension
+
+Introduction: Return the topological dimension of this Geometry object, which
must be less than or equal to the coordinate dimension. OGC SPEC s2.1.1.1 -
returns 0 for POINT, 1 for LINESTRING, 2 for POLYGON, and the largest dimension
of the components of a GEOMETRYCOLLECTION. If the dimension is unknown (e.g.
for an empty GEOMETRYCOLLECTION) 0 is returned.
+
+Format: `ST_Dimension (A:geometry), ST_Dimension (C:geometrycollection), `
+
+Since: `v1.5.0`
+
+Example:
+```sql
+SELECT ST_Dimension('GEOMETRYCOLLECTION(LINESTRING(1 1,0 0),POINT(0 0))');
+```
+
+Result:
+
+```
+ST_Dimension
+-----------
+1
+```
+
## ST_Distance
Introduction: Return the Euclidean distance between A and B
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index 0b12f6a2..a45f8b9f 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -427,6 +427,28 @@ Result:
POLYGON ((0 -3, -3 -3, -3 3, 0 3, 0 -3))
```
+## ST_Dimension
+
+Introduction: Return the topological dimension of this Geometry object, which
must be less than or equal to the coordinate dimension. OGC SPEC s2.1.1.1 -
returns 0 for POINT, 1 for LINESTRING, 2 for POLYGON, and the largest dimension
of the components of a GEOMETRYCOLLECTION. If the dimension is unknown (e.g.
for an empty GEOMETRYCOLLECTION) 0 is returned.
+
+Format: `ST_Dimension (A:geometry), ST_Dimension (C:geometrycollection), `
+
+Since: `v1.5.0`
+
+Example:
+```sql
+SELECT ST_Dimension('GEOMETRYCOLLECTION(LINESTRING(1 1,0 0),POINT(0 0))');
+```
+
+Result:
+
+```
+ST_Dimension
+-----------
+1
+```
+
+
## ST_Distance
Introduction: Return the Euclidean distance between A and B
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 dc992028..1c654944 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -43,6 +43,7 @@ public class Catalog {
new Functions.ST_Buffer(),
new Functions.ST_ConcaveHull(),
new Functions.ST_Envelope(),
+ new Functions.ST_Dimension(),
new Functions.ST_Distance(),
new Functions.ST_DistanceSphere(),
new Functions.ST_DistanceSpheroid(),
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 c117c3e4..2eb719a5 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
@@ -17,6 +17,7 @@ import org.apache.calcite.runtime.Geometries;
import org.apache.flink.table.annotation.DataTypeHint;
import org.apache.flink.table.functions.ScalarFunction;
import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryCollection;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
@@ -88,6 +89,15 @@ public class Functions {
}
}
+ public static class ST_Dimension 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.dimension(geom);
+ }
+
+ }
+
public static class ST_Distance extends ScalarFunction {
@DataTypeHint("Double")
public Double eval(@DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class) Object o1,
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 ba0c963f..37c9b305 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -154,6 +154,16 @@ public class FunctionTest extends TestBase{
}
+ @Test
+ public void testDimension(){
+ Table pointTable = tableEnv.sqlQuery(
+ "SELECT
ST_Dimension(ST_GeomFromWKT('GEOMETRYCOLLECTION EMPTY'))");
+ assertEquals(0, first(pointTable).getField(0));
+
+ pointTable = tableEnv.sqlQuery(
+ "SELECT
ST_Dimension(ST_GeomFromWKT('GEOMETRYCOLLECTION(MULTIPOLYGON(((0 0, 0 1, 1 1, 1
0, 0 0)), ((2 2, 2 3, 3 3, 3 2, 2 2))), MULTIPOINT(6 6, 7 7, 8 8))'))");
+ assertEquals(2, first(pointTable).getField(0));
+ }
@Test
public void testDistance() {
Table pointTable = createPointTable(testDataSize);
diff --git a/python/sedona/sql/st_functions.py
b/python/sedona/sql/st_functions.py
index fc85ee17..96420d94 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -45,6 +45,7 @@ __all__ = [
"ST_ConcaveHull",
"ST_ConvexHull",
"ST_Difference",
+ "ST_Dimension",
"ST_Distance",
"ST_DistanceSphere",
"ST_DistanceSpheroid",
@@ -385,6 +386,16 @@ def ST_ConvexHull(geometry: ColumnOrName) -> Column:
"""
return _call_st_function("ST_ConvexHull", geometry)
+@validate_argument_types
+def ST_Dimension(geometry: ColumnOrName):
+ """Calculate the inherent dimension of a geometry column.
+
+ :param geometry: Geometry column to calculate the dimension for.
+ :type geometry: ColumnOrName
+ :return: Dimension of geometry as an integer column.
+ :rtype: Column
+ """
+ return _call_st_function("ST_Dimension", geometry)
@validate_argument_types
def ST_Difference(a: ColumnOrName, b: ColumnOrName) -> Column:
diff --git a/python/tests/sql/test_dataframe_api.py
b/python/tests/sql/test_dataframe_api.py
index 7fce30bc..beacf71a 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -75,6 +75,7 @@ test_configurations = [
(stf.ST_ConcaveHull, ("geom", 1.0, True), "triangle_geom", "", "POLYGON
((1 1, 1 0, 0 0, 1 1))"),
(stf.ST_ConvexHull, ("geom",), "triangle_geom", "", "POLYGON ((0 0, 1 1, 1
0, 0 0))"),
(stf.ST_Difference, ("a", "b"), "overlapping_polys", "", "POLYGON ((1 0, 0
0, 0 1, 1 1, 1 0))"),
+ (stf.ST_Dimension, ("geom",), "geometry_geom_collection", "", 1),
(stf.ST_Distance, ("a", "b"), "two_points", "", 3.0),
(stf.ST_DistanceSpheroid, ("point", "point"), "point_geom", "", 0.0),
(stf.ST_DistanceSphere, ("point", "point"), "point_geom", "", 0.0),
@@ -392,6 +393,8 @@ class TestDataFrameAPI(TestBase):
return TestDataFrameAPI.spark.sql("SELECT
ST_GeomFromWKT('LINESTRING (0 0, 2 1)') AS line, ST_GeomFromWKT('POLYGON ((1 0,
2 0, 2 2, 1 2, 1 0))') AS poly")
elif request.param == "square_geom":
return TestDataFrameAPI.spark.sql("SELECT ST_GeomFromWKT('POLYGON
((1 0, 1 1, 2 1, 2 0, 1 0))') AS geom")
+ elif request.param == "geometry_geom_collection":
+ return TestDataFrameAPI.spark.sql("SELECT
ST_GeomFromWKT('GEOMETRYCOLLECTION(POINT(1 1), LINESTRING(0 0, 1 1, 2 2))') AS
geom")
elif request.param == "point_and_line":
return TestDataFrameAPI.spark.sql("SELECT ST_GeomFromWKT('POINT
(0.0 1.0)') AS point, ST_GeomFromWKT('LINESTRING (0 0, 1 0, 2 0, 3 0, 4 0, 5
0)') AS line")
raise ValueError(f"Invalid base_df name passed: {request.param}")
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 50b55567..e14fb554 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
@@ -54,6 +54,7 @@ object Catalog {
function[ST_Within](),
function[ST_Covers](),
function[ST_CoveredBy](),
+ function[ST_Dimension](),
function[ST_Disjoint](),
function[ST_Distance](),
function[ST_3DDistance](),
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 8f39b065..a324da69 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
@@ -1010,6 +1010,13 @@ case class ST_Translate(inputExpressions:
Seq[Expression])
}
}
+case class ST_Dimension(inputExpressions: Seq[Expression])
+ extends InferredUnaryExpression(Functions.dimension) with FoldableExpression
{
+ protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) =
{
+ copy(inputExpressions = newChildren)
+ }
+}
+
case class ST_BoundingDiagonal(inputExpressions: Seq[Expression])
extends InferredUnaryExpression(Functions.boundingDiagonal) with
FoldableExpression {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) =
{
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 d6847cc4..2fc3985b 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
@@ -90,6 +90,9 @@ object st_functions extends DataFrameAPI {
def ST_Difference(a: Column, b: Column): Column =
wrapExpression[ST_Difference](a, b)
def ST_Difference(a: String, b: String): Column =
wrapExpression[ST_Difference](a, b)
+ def ST_Dimension(geometry: Column): Column =
wrapExpression[ST_Dimension](geometry)
+ def ST_Dimension(geometry: String): Column =
wrapExpression[ST_Dimension](geometry)
+
def ST_Distance(a: Column, b: Column): Column =
wrapExpression[ST_Distance](a, b)
def ST_Distance(a: String, b: String): Column =
wrapExpression[ST_Distance](a, b)
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 ba743492..a1c36ade 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
@@ -231,6 +231,14 @@ class dataFrameAPITestScala extends TestBaseScala {
assert(actualResult == expectedResult)
}
+ it("Passed ST_Dimension") {
+ val polygonDf = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON ((0 0,
1 0, 1 1, 0 0))') AS geom")
+ val df = polygonDf.select(ST_Dimension("geom"))
+ val actualResult = df.take(1)(0).get(0).asInstanceOf[Int]
+ val expectedResult = 2
+ assert(actualResult == expectedResult)
+ }
+
it("Passed ST_Distance") {
val pointDf = sparkSession.sql("SELECT ST_Point(0.0, 0.0) AS a,
ST_Point(1.0, 0.0) as b")
val df = pointDf.select(ST_Distance("a", "b"))
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 ab93e676..7b8f23eb 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
@@ -137,6 +137,37 @@ class functionTestScala extends TestBaseScala with
Matchers with GeometrySample
assert(functionDf.count() > 0);
}
+ it("Passed ST_Dimension with Geometry") {
+ val geomTestCases = Map(
+ ("'POINT (51.3168 -0.56)'") -> "0",
+ ("'LineString (0 0, 0 90)'") -> "1",
+ ("'POLYGON((0 0,0 5,5 0,0 0))'") -> "2",
+ ("'MULTILINESTRING((0 0, 0 5, 5 0, 0 0))'") -> "1"
+ )
+ for ((geom, expectedResult) <- geomTestCases) {
+ val df = sparkSession.sql(s"SELECT
ST_Dimension(ST_GeomFromWKT($geom)), " +
+ s"$expectedResult")
+ val actual = df.take(1)(0).get(0).asInstanceOf[Integer]
+ val expected = df.take(1)(0).get(1).asInstanceOf[Integer]
+ assertEquals(expected, actual)
+ }
+ }
+
+ it("Passed DT_Dimension with GeometryCollection") {
+ val geomTestCases = Map(
+ ("'GEOMETRYCOLLECTION EMPTY'") -> "0",
+ ("'GEOMETRYCOLLECTION(LINESTRING(1 1,0 0),POINT(0 0))'") -> "1",
+ ("'GEOMETRYCOLLECTION(MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)), ((2 2,
2 3, 3 3, 3 2, 2 2))), MULTIPOINT(6 6, 7 7, 8 8))'") -> "2"
+ )
+ for ((geom, expectedResult) <- geomTestCases) {
+ val df = sparkSession.sql(s"SELECT
ST_Dimension(ST_GeomFromWKT($geom)), " +
+ s"$expectedResult")
+ val actual = df.take(1)(0).get(0).asInstanceOf[Integer]
+ val expected = df.take(1)(0).get(1).asInstanceOf[Integer]
+ assertEquals(expected, actual)
+ }
+ }
+
it("Passed ST_Distance") {
var polygonWktDf = sparkSession.read.format("csv").option("delimiter",
"\t").option("header", "false").load(mixedWktGeometryInputLocation)
polygonWktDf.createOrReplaceTempView("polygontable")