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/20 17:42:44 UTC

[incubator-sedona] branch master updated: [SEDONA-170] Add ST_AddPoint and ST_RemovePoint to the Flink API (#692)

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 82c923d6 [SEDONA-170] Add ST_AddPoint and ST_RemovePoint to the Flink API (#692)
82c923d6 is described below

commit 82c923d6d579818fa4257100e59b370458567889
Author: Kengo Seki <se...@apache.org>
AuthorDate: Wed Sep 21 02:42:38 2022 +0900

    [SEDONA-170] Add ST_AddPoint and ST_RemovePoint to the Flink API (#692)
---
 .../java/org/apache/sedona/common/Functions.java   | 46 +++++++++++++++++++
 docs/api/flink/Function.md                         | 40 +++++++++++++++++
 .../main/java/org/apache/sedona/flink/Catalog.java |  4 +-
 .../apache/sedona/flink/expressions/Functions.java | 33 ++++++++++++++
 .../java/org/apache/sedona/flink/FunctionTest.java | 28 ++++++++++++
 .../sql/sedona_sql/expressions/Functions.scala     | 52 +++++-----------------
 6 files changed, 160 insertions(+), 43 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 3a1b49cc..0466e0d3 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -39,8 +39,14 @@ import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 import org.wololo.jts2geojson.GeoJSONWriter;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
 
 public class Functions {
+    private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();
+
     public static double area(Geometry geometry) {
         return geometry.getArea();
     }
@@ -288,4 +294,44 @@ public class Functions {
     public static boolean isValid(Geometry geometry) {
         return new IsValidOp(geometry).isValid();
     }
+
+    public static Geometry addPoint(Geometry linestring, Geometry point) {
+        return addPoint(linestring, point, -1);
+    }
+
+    public static Geometry addPoint(Geometry linestring, Geometry point, int position) {
+        if (linestring instanceof LineString && point instanceof Point) {
+            List<Coordinate> coordinates = new ArrayList<>(Arrays.asList(linestring.getCoordinates()));
+            if (-1 <= position && position <= coordinates.size()) {
+                if (position < 0) {
+                    coordinates.add(point.getCoordinate());
+                } else {
+                    coordinates.add(position, point.getCoordinate());
+                }
+                return GEOMETRY_FACTORY.createLineString(coordinates.toArray(new Coordinate[0]));
+            }
+        }
+        return null;
+    }
+
+    public static Geometry removePoint(Geometry linestring) {
+        if (linestring != null) {
+            int length = linestring.getCoordinates().length;
+            if (1 < length) {
+                return removePoint(linestring, length - 1);
+            }
+        }
+        return null;
+    }
+
+    public static Geometry removePoint(Geometry linestring, int position) {
+        if (linestring instanceof LineString) {
+            List<Coordinate> coordinates = new ArrayList<>(Arrays.asList(linestring.getCoordinates()));
+            if (2 < coordinates.size() && position < coordinates.size()) {
+                coordinates.remove(position);
+                return GEOMETRY_FACTORY.createLineString(coordinates.toArray(new Coordinate[0]));
+            }
+        }
+        return null;
+    }
 }
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index defff6ea..21f8bb9d 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -13,6 +13,29 @@ SELECT ST_3DDistance(polygondf.countyshape, polygondf.countyshape)
 FROM polygondf
 ```
 
+## ST_AddPoint
+
+Introduction: Return Linestring with additional point at the given index, if position is not available the point will be added at the end of line.
+
+Format: `ST_AddPoint(geom: geometry, point: geometry, position: integer)`
+
+Format: `ST_AddPoint(geom: geometry, point: geometry)`
+
+Since: `v1.3.0`
+
+Example:
+```SQL
+SELECT ST_AddPoint(ST_GeomFromText("LINESTRING(0 0, 1 1, 1 0)"), ST_GeomFromText("Point(21 52)"), 1)
+
+SELECT ST_AddPoint(ST_GeomFromText("Linestring(0 0, 1 1, 1 0)"), ST_GeomFromText("Point(21 52)"))
+```
+
+Output:
+```
+LINESTRING(0 0, 21 52, 1 1, 1 0)
+LINESTRING(0 0, 1 1, 1 0, 21 52)
+```
+
 ## ST_Area
 
 Introduction: Return the area of A
@@ -577,6 +600,23 @@ Input: `POLYGON ((-0.5 -0.5, -0.5 0.5, 0.5 0.5, 0.5 -0.5, -0.5 -0.5))`
 
 Output: `POLYGON ((-0.5 -0.5, 0.5 -0.5, 0.5 0.5, -0.5 0.5, -0.5 -0.5))`
 
+## ST_RemovePoint
+
+Introduction: Return Linestring with removed point at given index, position can be omitted and then last one will be removed.
+
+Format: `ST_RemovePoint(geom: geometry, position: integer)`
+
+Format: `ST_RemovePoint(geom: geometry)`
+
+Since: `v1.3.0`
+
+Example:
+```SQL
+SELECT ST_RemovePoint(ST_GeomFromText("LINESTRING(0 0, 1 1, 1 0)"), 1)
+```
+
+Output: `LINESTRING(0 0, 1 0)`
+
 ## ST_SetSRID
 
 Introduction: Sets the spatial refence system identifier (SRID) of 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 163dc0e1..ca9cd94c 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -77,7 +77,9 @@ public class Catalog {
                 new Functions.ST_IsRing(),
                 new Functions.ST_IsSimple(),
                 new Functions.ST_IsValid(),
-                new Functions.ST_Normalize()
+                new Functions.ST_Normalize(),
+                new Functions.ST_AddPoint(),
+                new Functions.ST_RemovePoint()
         };
     }
 
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 7a9c228d..3640aad1 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
@@ -380,4 +380,37 @@ public class Functions {
             return org.apache.sedona.common.Functions.normalize(geom);
         }
     }
+
+    public static class ST_AddPoint 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(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o2) {
+            Geometry linestring = (Geometry) o1;
+            Geometry point = (Geometry) o2;
+            return org.apache.sedona.common.Functions.addPoint(linestring, point);
+        }
+
+        @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(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o2,
+                             int position) {
+            Geometry linestring = (Geometry) o1;
+            Geometry point = (Geometry) o2;
+            return org.apache.sedona.common.Functions.addPoint(linestring, point, position);
+        }
+    }
+
+    public static class ST_RemovePoint 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 geom = (Geometry) o;
+            return org.apache.sedona.common.Functions.removePoint(geom);
+        }
+
+        @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, int offset) {
+            Geometry geom = (Geometry) o;
+            return org.apache.sedona.common.Functions.removePoint(geom, offset);
+        }
+    }
 }
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 24d11dcc..fcb8e164 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -430,4 +430,32 @@ public class FunctionTest extends TestBase{
         Geometry result = (Geometry) first(polygonTable).getField(0);
         assertEquals("POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))", result.toString());
     }
+
+    @Test
+    public void testAddPoint() {
+        Table pointTable = tableEnv.sqlQuery("SELECT ST_AddPoint(ST_GeomFromWKT('LINESTRING (0 0, 1 1)'), ST_GeomFromWKT('POINT (2 2)'))");
+        assertEquals("LINESTRING (0 0, 1 1, 2 2)", first(pointTable).getField(0).toString());
+
+    }
+
+    @Test
+    public void testAddPointWithIndex() {
+        Table pointTable = tableEnv.sqlQuery("SELECT ST_AddPoint(ST_GeomFromWKT('LINESTRING (0 0, 1 1)'), ST_GeomFromWKT('POINT (2 2)'), 1)");
+        assertEquals("LINESTRING (0 0, 2 2, 1 1)", first(pointTable).getField(0).toString());
+
+    }
+
+    @Test
+    public void testRemovePoint() {
+        Table pointTable = tableEnv.sqlQuery("SELECT ST_RemovePoint(ST_GeomFromWKT('LINESTRING (0 0, 1 1, 2 2)'))");
+        assertEquals("LINESTRING (0 0, 1 1)", first(pointTable).getField(0).toString());
+
+    }
+
+    @Test
+    public void testRemovePointWithIndex() {
+        Table pointTable = tableEnv.sqlQuery("SELECT ST_RemovePoint(ST_GeomFromWKT('LINESTRING (0 0, 1 1, 2 2)'), 1)");
+        assertEquals("LINESTRING (0 0, 2 2)", first(pointTable).getField(0).toString());
+
+    }
 }
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 1da93a84..a563166e 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
@@ -39,8 +39,6 @@ import org.locationtech.jts.operation.linemerge.LineMerger
 import org.locationtech.jts.precision.GeometryPrecisionReducer
 import org.locationtech.jts.simplify.TopologyPreservingSimplifier
 
-import scala.util.{Failure, Success, Try}
-
 /**
   * Return the distance between two geometries.
   *
@@ -843,44 +841,21 @@ case class ST_AddPoint(inputExpressions: Seq[Expression])
   extends Expression with CodegenFallback {
   inputExpressions.betweenLength(2, 3)
 
-  private val geometryFactory = new GeometryFactory()
-
   override def nullable: Boolean = true
 
   override def eval(input: InternalRow): Any = {
     val geometry = inputExpressions.head.toGeometry(input)
     val point = inputExpressions(1).toGeometry(input)
-    if (inputExpressions.length == 2) addPointToGeometry(geometry, point, -1) else {
+    val geom = if (inputExpressions.length == 2) Functions.addPoint(geometry, point) else {
       val index = inputExpressions(2).toInt(input)
-      addPointToGeometry(geometry, point, index)
+      Functions.addPoint(geometry, point, index)
     }
-  }
-
-  private def addPointToGeometry(geometry: Geometry, pointGeom: Geometry, index: Int): GenericArrayData = {
-    geometry match {
-      case string: LineString => pointGeom match {
-        case point: Point => addPointToLineString(string, point, index) match {
-          case None => null
-          case Some(geom) => geom.toGenericArrayData
-        }
-        case _ => null
-      }
+    geom match {
+      case linestring: LineString => linestring.toGenericArrayData
       case _ => null
     }
   }
 
-  private def addPointToLineString(lineString: LineString, point: Point, index: Int): Option[LineString] = {
-    val coordinates = lineString.getCoordinates
-    val length = coordinates.length
-    if (index == -1) Some(lineStringFromCoordinates(coordinates ++ Array(point.getCoordinate)))
-    else if (index >= 0 && index <= length) Some(lineStringFromCoordinates(
-      coordinates.slice(0, index) ++ Array(point.getCoordinate) ++ coordinates.slice(index, length)))
-    else None
-  }
-
-  private def lineStringFromCoordinates(coordinates: Array[Coordinate]): LineString =
-    geometryFactory.createLineString(coordinates)
-
   override def dataType: DataType = GeometryUDT
 
   override def children: Seq[Expression] = inputExpressions
@@ -894,23 +869,16 @@ case class ST_RemovePoint(inputExpressions: Seq[Expression])
   extends Expression with CodegenFallback {
   inputExpressions.betweenLength(1, 2)
 
-  private val geometryFactory = new GeometryFactory()
-
   override def nullable: Boolean = true
 
   override def eval(input: InternalRow): Any = {
     val linesString = inputExpressions(0).toGeometry(input)
-    linesString match {
-      case string: LineString =>
-        val coordinates = string.getCoordinates
-        val length = coordinates.length
-        val pointToRemove = if (inputExpressions.length < 2) coordinates.length - 1
-          else inputExpressions(1).eval(input).asInstanceOf[Int]
-        if (coordinates.length <= pointToRemove | coordinates.length <= 2) null
-        else {
-          val coordinatesWithPointRemoved = coordinates.slice(0, pointToRemove) ++ coordinates.slice(pointToRemove + 1, length)
-          geometryFactory.createLineString(coordinatesWithPointRemoved).toGenericArrayData
-        }
+    val geom = if (inputExpressions.length < 2) Functions.removePoint(linesString) else {
+      val index = inputExpressions(1).toInt(input)
+      Functions.removePoint(linesString, index)
+    }
+    geom match {
+      case linestring: LineString => linestring.toGenericArrayData
       case _ => null
     }
   }