You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sedona.apache.org by ji...@apache.org on 2022/09/07 23:01:04 UTC

[incubator-sedona] branch master updated: [SEDONA-164] Add geometry count functions to the Flink API (#684)

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/incubator-sedona.git


The following commit(s) were added to refs/heads/master by this push:
     new dad4e5ee [SEDONA-164] Add geometry count functions to the Flink API (#684)
dad4e5ee is described below

commit dad4e5ee0754f3b694eca830e7335560f85f2e03
Author: Kengo Seki <se...@apache.org>
AuthorDate: Thu Sep 8 08:00:59 2022 +0900

    [SEDONA-164] Add geometry count functions to the Flink API (#684)
---
 .../java/org/apache/sedona/common/Functions.java   | 15 ++++++++
 docs/api/flink/Function.md                         | 43 ++++++++++++++++++++++
 .../main/java/org/apache/sedona/flink/Catalog.java |  3 ++
 .../apache/sedona/flink/expressions/Functions.java | 24 ++++++++++++
 .../java/org/apache/sedona/flink/FunctionTest.java | 21 +++++++++++
 .../sql/sedona_sql/expressions/Functions.scala     | 36 ++----------------
 .../expressions/NullSafeExpressions.scala          |  4 ++
 7 files changed, 113 insertions(+), 33 deletions(-)

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 3c729bc9..d3e6bcc3 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -209,6 +209,21 @@ public class Functions {
         return writer.write(geometry).toString();
     }
 
+    public static int nPoints(Geometry geometry) {
+        return geometry.getNumPoints();
+    }
+
+    public static int numGeometries(Geometry geometry) {
+        return geometry.getNumGeometries();
+    }
+
+    public static Integer numInteriorRings(Geometry geometry) {
+        if (geometry instanceof Polygon) {
+            return ((Polygon) geometry).getNumInteriorRing();
+        }
+        return null;
+    }
+
     public static String asGML(Geometry geometry) {
         return new GMLWriter().write(geometry);
     }
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 1396cfda..b21c0bba 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -431,6 +431,49 @@ SELECT ST_Length(polygondf.countyshape)
 FROM polygondf
 ```
 
+## ST_NPoints
+
+Introduction: Returns the number of points of the geometry
+
+Since: `v1.3.0`
+
+Format: `ST_NPoints (A:geometry)`
+
+Example:
+```SQL
+SELECT ST_NPoints(polygondf.countyshape)
+FROM polygondf
+```
+
+## ST_NumGeometries
+
+Introduction: Returns the number of Geometries. If geometry is a GEOMETRYCOLLECTION (or MULTI*) return the number of geometries, for single geometries will return 1.
+
+Format: `ST_NumGeometries (A:geometry)`
+
+Since: `v1.3.0`
+
+Example:
+```SQL
+SELECT ST_NumGeometries(df.geometry)
+FROM df
+```
+
+## ST_NumInteriorRings
+
+Introduction: Returns number of interior rings of polygon geometries.
+
+Format: `ST_NumInteriorRings(geom: geometry)`
+
+Since: `v1.3.0`
+
+Example:
+```SQL
+SELECT ST_NumInteriorRings(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_PointN
 
 Introduction: Return the Nth point in a single linestring or circular linestring in the geometry. Negative values are counted backwards from the end of the LineString, so that -1 is the last point. Returns NULL if there is no linestring in the geometry.
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 48e9a7b5..7b246f44 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -50,6 +50,9 @@ public class Catalog {
                 new Functions.ST_GeometryN(),
                 new Functions.ST_InteriorRingN(),
                 new Functions.ST_PointN(),
+                new Functions.ST_NPoints(),
+                new Functions.ST_NumGeometries(),
+                new Functions.ST_NumInteriorRings(),
                 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 798065f7..a7965446 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
@@ -173,6 +173,30 @@ public class Functions {
         }
     }
 
+    public static class ST_NPoints 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.nPoints(geom);
+        }
+    }
+
+    public static class ST_NumGeometries 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.numGeometries(geom);
+        }
+    }
+
+    public static class ST_NumInteriorRings 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 a78e6ad1..a7f44d14 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -197,6 +197,27 @@ public class FunctionTest extends TestBase{
         assertEquals("POINT (0.5 0.5)", point.toString());
     }
 
+    @Test
+    public void testNPoints() {
+        Table polygonTable = createPolygonTable(1);
+        Table resultTable = polygonTable.select(call(Functions.ST_NPoints.class.getSimpleName(), $(polygonColNames[0])));
+        assertEquals(5, first(resultTable).getField(0));
+    }
+
+    @Test
+    public void testNumGeometries() {
+        Table collectionTable = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('GEOMETRYCOLLECTION(POINT(10 10), POINT(30 30), LINESTRING(15 15, 20 20))') AS collection");
+        Table resultTable = collectionTable.select(call(Functions.ST_NumGeometries.class.getSimpleName(), $("collection")));
+        assertEquals(3, first(resultTable).getField(0));
+    }
+
+    @Test
+    public void testNumInteriorRings() {
+        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_NumInteriorRings.class.getSimpleName(), $("polygon")));
+        assertEquals(2, first(resultTable).getField(0));
+    }
+
     @Test
     public void testExteriorRing() {
         Table polygonTable = createPolygonTable(1);
diff --git a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
index 450ea530..9d7da6d8 100644
--- a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
+++ b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
@@ -107,15 +107,7 @@ case class ST_ConvexHull(inputExpressions: Seq[Expression])
   * @param inputExpressions
   */
 case class ST_NPoints(inputExpressions: Seq[Expression])
-  extends UnaryGeometryExpression with CodegenFallback {
-
-  override def nullSafeEval(geometry: Geometry): Any = {
-    geometry.getCoordinates.length
-  }
-
-  override def dataType: DataType = IntegerType
-
-  override def children: Seq[Expression] = inputExpressions
+  extends InferredUnaryExpression(Functions.nPoints) {
 
   protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
     copy(inputExpressions = newChildren)
@@ -840,19 +832,7 @@ case class ST_IsClosed(inputExpressions: Seq[Expression])
 }
 
 case class ST_NumInteriorRings(inputExpressions: Seq[Expression])
-  extends UnaryGeometryExpression with CodegenFallback {
-  assert(inputExpressions.length == 1)
-
-  override protected def nullSafeEval(geometry: Geometry): Any = {
-    geometry match {
-      case polygon: Polygon => polygon.getNumInteriorRing
-      case _: Geometry => null
-    }
-  }
-
-  override def dataType: DataType = IntegerType
-
-  override def children: Seq[Expression] = inputExpressions
+  extends InferredUnaryExpression(Functions.numInteriorRings) {
 
   protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
     copy(inputExpressions = newChildren)
@@ -972,17 +952,7 @@ case class ST_IsRing(inputExpressions: Seq[Expression])
   * @param inputExpressions Geometry
   */
 case class ST_NumGeometries(inputExpressions: Seq[Expression])
-  extends UnaryGeometryExpression with CodegenFallback {
-  assert(inputExpressions.length == 1)
-
-  override protected def nullSafeEval(geometry: Geometry): Any = {
-    geometry.getNumGeometries()
-  }
-
-  override def dataType: DataType = IntegerType
-
-
-  override def children: Seq[Expression] = inputExpressions
+  extends InferredUnaryExpression(Functions.numGeometries) {
 
   protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
     copy(inputExpressions = newChildren)
diff --git a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/NullSafeExpressions.scala b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/NullSafeExpressions.scala
index 4fe2d5b1..8b2b1496 100644
--- a/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/NullSafeExpressions.scala
+++ b/sql/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/NullSafeExpressions.scala
@@ -74,6 +74,8 @@ object InferrableType {
     new InferrableType[Geometry] {}
   implicit val javaDoubleInstance: InferrableType[java.lang.Double] =
     new InferrableType[java.lang.Double] {}
+  implicit val javaIntegerInstance: InferrableType[java.lang.Integer] =
+    new InferrableType[java.lang.Integer] {}
   implicit val doubleInstance: InferrableType[Double] =
     new InferrableType[Double] {}
   implicit val booleanInstance: InferrableType[Boolean] =
@@ -120,6 +122,8 @@ object InferredTypes {
       GeometryUDT
     } else if (typeOf[T] =:= typeOf[java.lang.Double]) {
       DoubleType
+    } else if (typeOf[T] =:= typeOf[java.lang.Integer]) {
+      IntegerType
     } else if (typeOf[T] =:= typeOf[Double]) {
       DoubleType
     } else if (typeOf[T] =:= typeOf[Int]) {