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 63a98e407 [SEDONA-647] Add ST_RemoveRepeatedPoints (#1557)
63a98e407 is described below

commit 63a98e4074511159ad231674b6874151faf43f24
Author: Furqaan Khan <[email protected]>
AuthorDate: Fri Aug 23 17:01:51 2024 -0400

    [SEDONA-647] Add ST_RemoveRepeatedPoints (#1557)
    
    * feat: add ST_RemoveRepeatedPoints
    
    * fix: lint errors
    
    * fix: snowflake typos
    
    * change variable names, divide the function tests, add detailed examples 
docs, add more tests in spark
---
 .../java/org/apache/sedona/common/Functions.java   |   8 +
 .../utils/GeometryDuplicateCoordinateRemover.java  | 183 +++++++++++++++++++++
 .../org/apache/sedona/common/FunctionsTest.java    | 133 +++++++++++++++
 docs/api/flink/Function.md                         |  69 ++++++++
 docs/api/snowflake/vector-data/Function.md         |  67 ++++++++
 docs/api/sql/Function.md                           |  69 ++++++++
 .../main/java/org/apache/sedona/flink/Catalog.java |   1 +
 .../apache/sedona/flink/expressions/Functions.java |  18 ++
 .../java/org/apache/sedona/flink/FunctionTest.java |  19 +++
 python/sedona/sql/st_functions.py                  |  16 ++
 python/tests/sql/test_dataframe_api.py             |   4 +
 python/tests/sql/test_function.py                  |  10 ++
 .../sedona/snowflake/snowsql/TestFunctions.java    |  12 ++
 .../sedona/snowflake/snowsql/TestFunctionsV2.java  |  12 ++
 .../org/apache/sedona/snowflake/snowsql/UDFs.java  |  11 ++
 .../apache/sedona/snowflake/snowsql/UDFsV2.java    |  18 ++
 .../scala/org/apache/sedona/sql/UDF/Catalog.scala  |   1 +
 .../sql/sedona_sql/expressions/Functions.scala     |  10 ++
 .../sql/sedona_sql/expressions/st_functions.scala  |  12 ++
 .../org/apache/sedona/sql/PreserveSRIDSuite.scala  |   1 +
 .../apache/sedona/sql/dataFrameAPITestScala.scala  |  11 ++
 .../org/apache/sedona/sql/functionTestScala.scala  |  41 +++++
 22 files changed, 726 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 ecb0265bb..5c6940539 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -873,6 +873,14 @@ public class Functions {
     return null;
   }
 
+  public static Geometry removeRepeatedPoints(Geometry geom, double tolerance) 
{
+    return GeometryDuplicateCoordinateRemover.process(geom, tolerance);
+  }
+
+  public static Geometry removeRepeatedPoints(Geometry geom) {
+    return removeRepeatedPoints(geom, 0);
+  }
+
   public static Geometry setPoint(Geometry linestring, int position, Geometry 
point) {
     if (linestring instanceof LineString) {
       List<Coordinate> coordinates = new 
ArrayList<>(Arrays.asList(linestring.getCoordinates()));
diff --git 
a/common/src/main/java/org/apache/sedona/common/utils/GeometryDuplicateCoordinateRemover.java
 
b/common/src/main/java/org/apache/sedona/common/utils/GeometryDuplicateCoordinateRemover.java
new file mode 100644
index 000000000..0838b75a3
--- /dev/null
+++ 
b/common/src/main/java/org/apache/sedona/common/utils/GeometryDuplicateCoordinateRemover.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sedona.common.utils;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.Set;
+import org.locationtech.jts.geom.*;
+
+public class GeometryDuplicateCoordinateRemover {
+
+  public static Coordinate[] removeDuplicates(Coordinate[] coords, int 
minPoints) {
+    Coordinate currentPoint;
+    int numPoint = coords.length;
+    int totalPointsOut = 1;
+
+    double distance = Double.MAX_VALUE;
+
+    if (numPoint <= minPoints) return new Coordinate[0];
+
+    Coordinate lastPoint = coords[0];
+    int writeIndex = 1;
+
+    for (int i = 1; i < numPoint; i++) {
+      boolean isLastPoint = (i == numPoint - 1);
+
+      currentPoint = coords[i];
+
+      if (numPoint + totalPointsOut > minPoints + i) {
+        if (TOLERANCE > 0.0) {
+          distance = currentPoint.distance(lastPoint);
+          if (!isLastPoint && distance <= TOLERANCE) {
+            continue;
+          }
+        } else {
+          if (currentPoint.equals2D(lastPoint)) {
+            continue;
+          }
+        }
+
+        if (isLastPoint && totalPointsOut > 1 && TOLERANCE > 0.0 && distance 
<= TOLERANCE) {
+          totalPointsOut--;
+          writeIndex--;
+        }
+      }
+
+      coords[writeIndex] = currentPoint;
+      totalPointsOut++;
+      writeIndex++;
+      lastPoint = currentPoint;
+    }
+    Coordinate[] newCoordinates = new Coordinate[totalPointsOut];
+    System.arraycopy(coords, 0, newCoordinates, 0, totalPointsOut);
+
+    return newCoordinates;
+  }
+
+  public static Coordinate[] removeDuplicatePointsMultiPoint(
+      Coordinate[] coords, boolean recursion) {
+    if (TOLERANCE == 0 || recursion) {
+      Set<Coordinate> uniqueCoords = new 
LinkedHashSet<>(Arrays.asList(coords));
+      return uniqueCoords.toArray(new Coordinate[0]);
+    }
+    Coordinate[] deduplicated =
+        Arrays.stream(removeDuplicatePointsMultiPoint(coords, true))
+            .sorted()
+            .toArray(Coordinate[]::new);
+
+    for (int i = 0; i < deduplicated.length; i++) {
+      for (int j = i + 1; j < deduplicated.length; j++) {
+        if (deduplicated[i] != null
+            && deduplicated[j] != null
+            && deduplicated[i].distance(deduplicated[j]) < TOLERANCE) {
+          deduplicated[j] = null;
+        } else {
+          break;
+        }
+      }
+    }
+
+    return 
Arrays.stream(deduplicated).filter(Objects::nonNull).toArray(Coordinate[]::new);
+  }
+
+  private static GeometryFactory FACTORY = null;
+
+  private static double TOLERANCE = 0;
+
+  public static Geometry process(Geometry geometry, double tolerance) {
+
+    TOLERANCE = tolerance;
+
+    if (geometry.isEmpty()) return geometry;
+
+    FACTORY = geometry.getFactory();
+
+    if (geometry.getGeometryType().equals(Geometry.TYPENAME_POINT)) return 
geometry;
+    if (geometry.getGeometryType().equals(Geometry.TYPENAME_MULTIPOINT))
+      return processMultiPoint((MultiPoint) geometry);
+    if (geometry.getGeometryType().equals(Geometry.TYPENAME_LINEARRING))
+      return processLinearRing((LinearRing) geometry);
+    if (geometry.getGeometryType().equals(Geometry.TYPENAME_LINESTRING))
+      return processLineString((LineString) geometry);
+    if (geometry.getGeometryType().equals(Geometry.TYPENAME_MULTILINESTRING))
+      return processMultiLineString((MultiLineString) geometry);
+    if (geometry.getGeometryType().equals(Geometry.TYPENAME_POLYGON))
+      return processPolygon((Polygon) geometry);
+    if (geometry.getGeometryType().equals(Geometry.TYPENAME_MULTIPOLYGON))
+      return processMultiPolygon((MultiPolygon) geometry);
+    if 
(geometry.getGeometryType().equals(Geometry.TYPENAME_GEOMETRYCOLLECTION))
+      return processGeometryCollection((GeometryCollection) geometry);
+
+    throw new IllegalArgumentException(
+        "Unknown Geometry subtype: " + geometry.getClass().getName());
+  }
+
+  private static MultiPoint processMultiPoint(MultiPoint geometry) {
+    Coordinate[] coords = geometry.getCoordinates();
+    return 
FACTORY.createMultiPointFromCoords(removeDuplicatePointsMultiPoint(coords, 
false));
+  }
+
+  private static LinearRing processLinearRing(LinearRing geometry) {
+    Coordinate[] coords = geometry.getCoordinates();
+    return FACTORY.createLinearRing(removeDuplicates(coords, 4));
+  }
+
+  private static LineString processLineString(LineString geometry) {
+    if (geometry.getNumPoints() <= 2) return geometry;
+
+    Coordinate[] coords = geometry.getCoordinates();
+    return FACTORY.createLineString(removeDuplicates(coords, 2));
+  }
+
+  private static MultiLineString processMultiLineString(MultiLineString 
geometry) {
+    LineString[] lineStrings = new LineString[geometry.getNumGeometries()];
+    for (int i = 0; i < lineStrings.length; i++) {
+      lineStrings[i] = processLineString((LineString) 
geometry.getGeometryN(i));
+    }
+    return FACTORY.createMultiLineString(lineStrings);
+  }
+
+  private static Polygon processPolygon(Polygon geometry) {
+    LinearRing shell = processLinearRing(geometry.getExteriorRing());
+
+    LinearRing[] holes = new LinearRing[geometry.getNumInteriorRing()];
+    for (int i = 0; i < holes.length; i++) {
+      holes[i] = processLinearRing(geometry.getInteriorRingN(i));
+    }
+    return FACTORY.createPolygon(shell, holes);
+  }
+
+  private static MultiPolygon processMultiPolygon(MultiPolygon geometry) {
+    Polygon[] polygons = new Polygon[geometry.getNumGeometries()];
+    for (int i = 0; i < polygons.length; i++) {
+      polygons[i] = processPolygon((Polygon) geometry.getGeometryN(i));
+    }
+    return FACTORY.createMultiPolygon(polygons);
+  }
+
+  private static GeometryCollection 
processGeometryCollection(GeometryCollection geometry) {
+    Geometry[] geometries = new Geometry[geometry.getNumGeometries()];
+    for (int i = 0; i < geometries.length; i++) {
+      geometries[i] = process(geometry.getGeometryN(i), TOLERANCE);
+    }
+    return FACTORY.createGeometryCollection(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 9ba0cbbc2..e6284097b 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -1622,6 +1622,139 @@ public class FunctionsTest extends TestBase {
     assertEquals(expected, e.getMessage());
   }
 
+  @Test
+  public void removeRepeatedPointsMultiPoint() throws ParseException {
+    Geometry geom = Constructors.geomFromWKT("POINT (10 23)", 4321);
+    Geometry actualGeom = Functions.removeRepeatedPoints(geom);
+    String actual = Functions.asWKT(actualGeom);
+    String expected = "POINT (10 23)";
+    assertEquals(expected, actual);
+    int actualSRID = Functions.getSRID(actualGeom);
+    assertEquals(4321, actualSRID);
+
+    geom = Constructors.geomFromWKT("MULTIPOINT ((1 1), (4 4), (2 2), (3 3))", 
1000);
+    actualGeom = Functions.removeRepeatedPoints(geom);
+    actual = Functions.asWKT(actualGeom);
+    expected = "MULTIPOINT ((1 1), (4 4), (2 2), (3 3))";
+    assertEquals(expected, actual);
+    actualSRID = Functions.getSRID(actualGeom);
+    assertEquals(1000, actualSRID);
+
+    geom =
+        Constructors.geomFromWKT("MULTIPOINT (20 20, 10 10, 30 30, 40 40, 20 
20, 30 30, 40 40)", 0);
+    actual = Functions.asWKT(Functions.removeRepeatedPoints(geom, 20));
+    expected = "MULTIPOINT ((10 10), (30 30))";
+    assertEquals(expected, actual);
+
+    actual = Functions.asWKT(Functions.removeRepeatedPoints(geom));
+    expected = "MULTIPOINT ((20 20), (10 10), (30 30), (40 40))";
+    assertEquals(expected, actual);
+
+    geom = Constructors.geomFromWKT("MULTIPOINT ((1 1), (4 4), (2 2), (3 3), 
(3 3))", 0);
+    actual = Functions.asWKT(Functions.removeRepeatedPoints(geom, 2000));
+    expected = "MULTIPOINT ((1 1))";
+    assertEquals(expected, actual);
+  }
+
+  @Test
+  public void removeRepeatedPointsLineString() throws ParseException {
+    Geometry geom = Constructors.geomFromWKT("LINESTRING (0 0, 0 0, 1 1, 0 0, 
1 1, 2 2)", 2000);
+    Geometry actualGeom = Functions.removeRepeatedPoints(geom);
+    String actual = Functions.asWKT(actualGeom);
+    String expected = "LINESTRING (0 0, 1 1, 0 0, 1 1, 2 2)";
+    assertEquals(expected, actual);
+    int actualSRID = Functions.getSRID(actualGeom);
+    assertEquals(2000, actualSRID);
+
+    geom = Constructors.geomFromWKT("LINESTRING (0 0, 0 0, 1 1, 5 5, 1 1, 2 
2)", 0);
+    actual = Functions.asWKT(Functions.removeRepeatedPoints(geom, 2));
+    expected = "LINESTRING (0 0, 5 5, 2 2)";
+    assertEquals(expected, actual);
+
+    actual = Functions.asWKT(Functions.removeRepeatedPoints(geom, 6));
+    expected = "LINESTRING (0 0, 2 2)";
+    assertEquals(expected, actual);
+
+    geom =
+        Constructors.geomFromWKT("LINESTRING (20 20, 10 10, 30 30, 40 40, 20 
20, 30 30, 40 40)", 0);
+    actual = Functions.asWKT(Functions.removeRepeatedPoints(geom, 20));
+    expected = "LINESTRING (20 20, 40 40, 20 20, 40 40)";
+    assertEquals(expected, actual);
+
+    geom =
+        Constructors.geomFromWKT("LINESTRING (10 10, 20 20, 20 20, 30 30, 30 
30, 40 40, 40 40)", 0);
+    actual = Functions.asWKT(Functions.removeRepeatedPoints(geom, 10000));
+    expected = "LINESTRING (10 10, 40 40)";
+    assertEquals(expected, actual);
+
+    geom =
+        Constructors.geomFromWKT(
+            "MULTILINESTRING ((10 10, 20 20, 20 20, 30 30), (40 40, 50 50, 50 
50, 60 60))", 3000);
+    actualGeom = Functions.removeRepeatedPoints(geom);
+    actual = Functions.asWKT(actualGeom);
+    expected = "MULTILINESTRING ((10 10, 20 20, 30 30), (40 40, 50 50, 60 
60))";
+    assertEquals(expected, actual);
+    actualSRID = Functions.getSRID(actualGeom);
+    assertEquals(3000, actualSRID);
+  }
+
+  @Test
+  public void removeRepeatedPointsPolygon() throws ParseException {
+    Geometry geom =
+        Constructors.geomFromWKT(
+            "POLYGON ((10 10, 20 20, 20 20, 30 30, 30 30, 40 40, 40 40, 10 
10))", 4000);
+    Geometry actualGeom = Functions.removeRepeatedPoints(geom);
+    String actual = Functions.asWKT(actualGeom);
+    String expected = "POLYGON ((10 10, 20 20, 30 30, 40 40, 10 10))";
+    assertEquals(expected, actual);
+    int actualSRID = Functions.getSRID(actualGeom);
+    assertEquals(4000, actualSRID);
+
+    geom =
+        Constructors.geomFromWKT(
+            "POLYGON ((10 10, 20 20, 20 20, 30 30, 30 30, 40 40, 40 40, 10 
10),(15 15, 25 25, 25 25, 35 35, 35 35, 15 15),(25 25, 35 35, 35 35, 45 45, 45 
45, 25 25))",
+            0);
+    actual = Functions.asWKT(Functions.removeRepeatedPoints(geom, 1000));
+    expected =
+        "POLYGON ((10 10, 40 40, 40 40, 10 10), (15 15, 35 35, 35 35, 15 15), 
(25 25, 45 45, 45 45, 25 25))";
+    assertEquals(expected, actual);
+
+    geom =
+        Constructors.geomFromWKT(
+            "MULTIPOLYGON (((10 10, 20 20, 20 20, 30 30, 30 30, 40 40, 40 40, 
10 10)),((50 50, 60 60, 60 60, 70 70, 70 70, 80 80, 80 80, 50 50)))",
+            5000);
+    actualGeom = Functions.removeRepeatedPoints(geom, 1000);
+    actual = Functions.asWKT(actualGeom);
+    expected = "MULTIPOLYGON (((10 10, 40 40, 40 40, 10 10)), ((50 50, 80 80, 
80 80, 50 50)))";
+    assertEquals(expected, actual);
+    actualSRID = Functions.getSRID(actualGeom);
+    assertEquals(5000, actualSRID);
+
+    geom =
+        Constructors.geomFromWKT(
+            "MULTIPOLYGON (((10 10, 20 20, 20 20, 30 30, 30 30, 40 40, 40 40, 
10 10),(15 15, 25 25, 25 25, 35 35, 35 35, 15 15),(25 25, 35 35, 35 35, 45 45, 
45 45, 25 25)),((50 50, 60 60, 60 60, 70 70, 70 70, 80 80, 80 80, 50 50),(55 
55, 65 65, 65 65, 75 75, 75 75, 55 55),(65 65, 75 75, 75 75, 85 85, 85 85, 65 
65)))",
+            0);
+    actual = Functions.asWKT(Functions.removeRepeatedPoints(geom, 1000));
+    expected =
+        "MULTIPOLYGON (((10 10, 40 40, 40 40, 10 10), (15 15, 35 35, 35 35, 15 
15), (25 25, 45 45, 45 45, 25 25)), ((50 50, 80 80, 80 80, 50 50), (55 55, 75 
75, 75 75, 55 55), (65 65, 85 85, 85 85, 65 65)))";
+    assertEquals(expected, actual);
+  }
+
+  @Test
+  public void removeRepeatedPointsGeometryCollection() throws ParseException {
+    Geometry geom =
+        Constructors.geomFromWKT(
+            "GEOMETRYCOLLECTION (POINT (10 10),LINESTRING (20 20, 20 20, 30 
30, 30 30),POLYGON ((40 40, 50 50, 50 50, 60 60, 60 60, 70 70, 70 70, 40 40)), 
MULTIPOINT ((80 80), (90 90), (90 90), (100 100)))",
+            6000);
+    Geometry actualGeom = Functions.removeRepeatedPoints(geom);
+    String actual = Functions.asWKT(actualGeom);
+    String expected =
+        "GEOMETRYCOLLECTION (POINT (10 10), LINESTRING (20 20, 30 30), POLYGON 
((40 40, 50 50, 60 60, 70 70, 40 40)), MULTIPOINT ((80 80), (90 90), (100 
100)))";
+    assertEquals(expected, actual);
+    int actualSRID = Functions.getSRID(actualGeom);
+    assertEquals(6000, actualSRID);
+  }
+
   @Test
   public void simplifyVW() throws ParseException {
     Geometry geom = Constructors.geomFromEWKT("LINESTRING(5 2, 3 8, 6 20, 7 
25, 10 10)");
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 27e054027..c371f68ad 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -3206,6 +3206,75 @@ Output:
 LINESTRING(0 0, 1 0)
 ```
 
+## ST_RemoveRepeatedPoints
+
+Introduction: This function eliminates consecutive duplicate points within a 
geometry, preserving endpoints of LineStrings. It operates on 
(Multi)LineStrings, (Multi)Polygons, and MultiPoints, processing 
GeometryCollection elements individually. When an optional 'tolerance' value is 
provided, vertices within that distance are also considered duplicates.
+
+Format:
+
+`ST_RemoveRepeatedPoints(geom: Geometry, tolerance: Double)`
+
+`ST_RemoveRepeatedPoints(geom: Geometry)`
+
+Since: `v1.7.0`
+
+SQL Example:
+
+```sql
+SELECT ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('MULTIPOINT ((20 20), (10 10), (30 30), (40 40), (20 
20), (30 30), (40 40))')
+       )
+```
+
+Output:
+
+```
+MULTIPOINT ((20 20), (10 10), (30 30), (40 40))
+```
+
+SQL Example:
+
+```sql
+SELECT ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('LINESTRING (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 
40 40)')
+       )
+```
+
+Output:
+
+```
+LINESTRING (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 40 40)
+```
+
+SQL Example: Each geometry within a collection is processed independently.
+
+```sql
+ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('GEOMETRYCOLLECTION (POINT (10 10), POINT(10 10), 
LINESTRING (20 20, 20 20, 30 30, 30 30), MULTIPOINT ((80 80), (90 90), (90 90), 
(100 100)))')
+    )
+```
+
+Output:
+
+```
+GEOMETRYCOLLECTION (POINT (10 10), POINT (10 10), LINESTRING (20 20, 30 30), 
MULTIPOINT ((80 80), (90 90), (100 100)))
+```
+
+SQL Example: Elimination of repeated points within a specified distance 
tolerance.
+
+```sql
+SELECT ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('LINESTRING (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 
40 40)'),
+        20
+       )
+```
+
+Output:
+
+```
+LINESTRING (20 20, 40 40, 20 20, 40 40)
+```
+
 ## ST_Rotate
 
 Introduction: Rotates a geometry by a specified angle in radians 
counter-clockwise around a given origin point. The origin for rotation can be 
specified as either a POINT geometry or x and y coordinates. If the origin is 
not specified, the geometry is rotated around POINT(0 0).
diff --git a/docs/api/snowflake/vector-data/Function.md 
b/docs/api/snowflake/vector-data/Function.md
index df04e2f94..7a3e68ae9 100644
--- a/docs/api/snowflake/vector-data/Function.md
+++ b/docs/api/snowflake/vector-data/Function.md
@@ -2425,6 +2425,73 @@ SELECT ST_RemovePoint(ST_GeomFromText('LINESTRING(0 0, 1 
1, 1 0)'), 1)
 
 Output: `LINESTRING(0 0, 1 0)`
 
+## ST_RemoveRepeatedPoints
+
+Introduction: This function eliminates consecutive duplicate points within a 
geometry, preserving endpoints of LineStrings. It operates on 
(Multi)LineStrings, (Multi)Polygons, and MultiPoints, processing 
GeometryCollection elements individually. When an optional 'tolerance' value is 
provided, vertices within that distance are also considered duplicates.
+
+Format:
+
+`ST_RemoveRepeatedPoints(geom: Geometry, tolerance: Double)`
+
+`ST_RemoveRepeatedPoints(geom: Geometry)`
+
+SQL Example:
+
+```sql
+SELECT ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('MULTIPOINT ((20 20), (10 10), (30 30), (40 40), (20 
20), (30 30), (40 40))')
+       )
+```
+
+Output:
+
+```
+MULTIPOINT ((20 20), (10 10), (30 30), (40 40))
+```
+
+SQL Example:
+
+```sql
+SELECT ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('LINESTRING (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 
40 40)')
+       )
+```
+
+Output:
+
+```
+LINESTRING (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 40 40)
+```
+
+SQL Example: Each geometry within a collection is processed independently.
+
+```sql
+ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('GEOMETRYCOLLECTION (POINT (10 10), POINT(10 10), 
LINESTRING (20 20, 20 20, 30 30, 30 30), MULTIPOINT ((80 80), (90 90), (90 90), 
(100 100)))')
+    )
+```
+
+Output:
+
+```
+GEOMETRYCOLLECTION (POINT (10 10), POINT (10 10), LINESTRING (20 20, 30 30), 
MULTIPOINT ((80 80), (90 90), (100 100)))
+```
+
+SQL Example: Elimination of repeated points within a specified distance 
tolerance.
+
+```sql
+SELECT ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('LINESTRING (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 
40 40)'),
+        20
+       )
+```
+
+Output:
+
+```
+LINESTRING (20 20, 40 40, 20 20, 40 40)
+```
+
 ## ST_Reverse
 
 Introduction: Return the geometry with vertex order reversed
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index eab48026c..76587a62e 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -3266,6 +3266,75 @@ Output:
 LINESTRING(0 0, 1 0)
 ```
 
+## ST_RemoveRepeatedPoints
+
+Introduction: This function eliminates consecutive duplicate points within a 
geometry, preserving endpoints of LineStrings. It operates on 
(Multi)LineStrings, (Multi)Polygons, and MultiPoints, processing 
GeometryCollection elements individually. When an optional 'tolerance' value is 
provided, vertices within that distance are also considered duplicates.
+
+Format:
+
+`ST_RemoveRepeatedPoints(geom: Geometry, tolerance: Double)`
+
+`ST_RemoveRepeatedPoints(geom: Geometry)`
+
+Since: `v1.7.0`
+
+SQL Example:
+
+```sql
+SELECT ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('MULTIPOINT ((20 20), (10 10), (30 30), (40 40), (20 
20), (30 30), (40 40))')
+       )
+```
+
+Output:
+
+```
+MULTIPOINT ((20 20), (10 10), (30 30), (40 40))
+```
+
+SQL Example:
+
+```sql
+SELECT ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('LINESTRING (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 
40 40)')
+       )
+```
+
+Output:
+
+```
+LINESTRING (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 40 40)
+```
+
+SQL Example: Each geometry within a collection is processed independently.
+
+```sql
+ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('GEOMETRYCOLLECTION (POINT (10 10), POINT(10 10), 
LINESTRING (20 20, 20 20, 30 30, 30 30), MULTIPOINT ((80 80), (90 90), (90 90), 
(100 100)))')
+    )
+```
+
+Output:
+
+```
+GEOMETRYCOLLECTION (POINT (10 10), POINT (10 10), LINESTRING (20 20, 30 30), 
MULTIPOINT ((80 80), (90 90), (100 100)))
+```
+
+SQL Example: Elimination of repeated points within a specified distance 
tolerance.
+
+```sql
+SELECT ST_RemoveRepeatedPoints(
+        ST_GeomFromWKT('LINESTRING (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 
40 40)'),
+        20
+       )
+```
+
+Output:
+
+```
+LINESTRING (20 20, 40 40, 20 20, 40 40)
+```
+
 ## ST_Reverse
 
 Introduction: Return the geometry with vertex order reversed
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 7ce8a444c..6fd3c7668 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -145,6 +145,7 @@ public class Catalog {
       new Functions.ST_AddMeasure(),
       new Functions.ST_AddPoint(),
       new Functions.ST_RemovePoint(),
+      new Functions.ST_RemoveRepeatedPoints(),
       new Functions.ST_SetPoint(),
       new Functions.ST_LineFromMultiPoint(),
       new Functions.ST_LineMerge(),
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 80d3a6e60..7f8404024 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
@@ -1007,6 +1007,24 @@ public class Functions {
     }
   }
 
+  public static class ST_RemoveRepeatedPoints 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.removeRepeatedPoints(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,
+        double tolerance) {
+      Geometry geom = (Geometry) o;
+      return org.apache.sedona.common.Functions.removeRepeatedPoints(geom, 
tolerance);
+    }
+  }
+
   public static class ST_SetPoint extends ScalarFunction {
     @DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class)
     public Geometry eval(
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 3b203c46e..1b07834a3 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -1451,6 +1451,25 @@ public class FunctionTest extends TestBase {
     assertEquals("LINESTRING (0 0, 1 1)", 
first(pointTable).getField(0).toString());
   }
 
+  @Test
+  public void testRemoveRepeatedPoints() {
+    Table baseTable =
+        tableEnv.sqlQuery(
+            "SELECT ST_GeomFromWKT('LINESTRING (20 20, 10 10, 30 30, 40 40, 20 
20, 30 30, 40 40)', 1000) AS geom");
+    Table actualTable =
+        baseTable
+            
.select(call(Functions.ST_RemoveRepeatedPoints.class.getSimpleName(), 
$("geom"), 100))
+            .as("geom");
+    String actual = first(actualTable).getField(0).toString();
+    String expected = "LINESTRING (20 20, 40 40)";
+    assertEquals(expected, actual);
+    int actualSRID =
+        (Integer)
+            
first(actualTable.select(call(Functions.ST_SRID.class.getSimpleName(), 
$("geom"))))
+                .getField(0);
+    assertEquals(1000, actualSRID);
+  }
+
   @Test
   public void testRemovePointWithIndex() {
     Table pointTable =
diff --git a/python/sedona/sql/st_functions.py 
b/python/sedona/sql/st_functions.py
index 6dbd41a99..96a47684f 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -1339,6 +1339,22 @@ def ST_RemovePoint(line_string: ColumnOrName, index: 
Union[ColumnOrName, int]) -
     return _call_st_function("ST_RemovePoint", (line_string, index))
 
 
+@validate_argument_types
+def ST_RemoveRepeatedPoints(geom: ColumnOrName, tolerance: 
Optional[Union[ColumnOrName, float]] = None) -> Column:
+    """Removes duplicate coordinates from a geometry, optionally removing 
those within a specified distance tolerance.
+
+    @param geom: Geometry with repeated points
+    @type geom: ColumnOrName
+    @param tolerance: Tolerance for removing nearby coordinates
+    @type tolerance: Optional[Union[ColumnOrName, float]]
+    @return:
+    """
+    args = (geom, tolerance)
+    if tolerance is None:
+        args = (geom,)
+    return _call_st_function("ST_RemoveRepeatedPoints", args)
+
+
 @validate_argument_types
 def ST_Reverse(geometry: ColumnOrName) -> Column:
     """Reverse the points for the geometry.
diff --git a/python/tests/sql/test_dataframe_api.py 
b/python/tests/sql/test_dataframe_api.py
index cac26607d..59f79a0f1 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -204,6 +204,7 @@ test_configurations = [
     (stf.ST_PointOnSurface, ("line",), "linestring_geom", "", "POINT (2 0)"),
     (stf.ST_ReducePrecision, ("geom", 1), "precision_reduce_point", "", "POINT 
(0.1 0.2)"),
     (stf.ST_RemovePoint, ("line", 1), "linestring_geom", "", "LINESTRING (0 0, 
2 0, 3 0, 4 0, 5 0)"),
+    (stf.ST_RemoveRepeatedPoints, ("geom",), "repeated_multipoint", "", 
"MULTIPOINT (1 1, 2 2, 3 3, 4 4)"),
     (stf.ST_Reverse, ("line",), "linestring_geom", "", "LINESTRING (5 0, 4 0, 
3 0, 2 0, 1 0, 0 0)"),
     (stf.ST_RotateX, ("line", 10.0), "4D_line", "ST_ReducePrecision(geom, 2)", 
"LINESTRING Z (1 -0.3 -1.383092639965822, 2 -0.59 -2.766185279931644, 3 -0.89 
-4.149277919897466, -1 0.3 1.383092639965822)"),
     (stf.ST_Rotate, ("line", 10.0), "linestring_geom", 
"ST_ReducePrecision(geom, 2)", "LINESTRING (0 0, -0.84 -0.54, -1.68 -1.09, 
-2.52 -1.63, -3.36 -2.18, -4.2 -2.72)"),
@@ -426,6 +427,7 @@ wrong_type_configurations = [
     (stf.ST_RemovePoint, (None, 1)),
     (stf.ST_RemovePoint, ("", None)),
     (stf.ST_RemovePoint, ("", 1.0)),
+    (stf.ST_RemoveRepeatedPoints, (None, None)),
     (stf.ST_Reverse, (None,)),
     (stf.ST_Rotate, (None,None,)),
     (stf.ST_Rotate, (None,None)),
@@ -578,6 +580,8 @@ class TestDataFrameAPI(TestBase):
             return TestDataFrameAPI.spark.sql("SELECT 
ST_GeomFromWKT('LINESTRING (0 0, 1 0, 1 1, 0 0)') AS geom")
         elif request.param == "empty_geom":
             return TestDataFrameAPI.spark.sql("SELECT 
ST_Difference(ST_Point(0.0, 0.0), ST_Point(0.0, 0.0)) AS geom")
+        elif request.param == "repeated_multipoint":
+            return TestDataFrameAPI.spark.sql("SELECT 
ST_GeomFromWKT('MULTIPOINT (1 1, 1 1, 2 2, 3 3, 3 3, 4 4)') AS geom")
         elif request.param == "multiline_geom":
             return TestDataFrameAPI.spark.sql("SELECT 
ST_GeomFromWKT('MULTILINESTRING ((0 0, 1 0), (1 0, 1 1), (1 1, 0 0))') AS geom")
         elif request.param == "geom_collection":
diff --git a/python/tests/sql/test_function.py 
b/python/tests/sql/test_function.py
index be49c5250..b42e8c156 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -977,6 +977,16 @@ class TestPredicateJoin(TestBase):
         for actual, expected in result_and_expected:
             assert (actual == expected)
 
+    def test_st_remove_repeated_points(self):
+        baseDf = self.spark.sql("SELECT ST_GeomFromWKT('GEOMETRYCOLLECTION 
(POINT (10 10),LINESTRING (20 20, 20 20, 30 30, 30 30),POLYGON ((40 40, 50 50, 
50 50, 60 60, 60 60, 70 70, 70 70, 40 40)), MULTIPOINT ((80 80), (90 90), (90 
90), (100 100)))', 1000) AS geom")
+        actualDf = baseDf.selectExpr("ST_RemoveRepeatedPoints(geom, 1000) as 
geom")
+        actual = actualDf.selectExpr("ST_AsText(geom)").first()[0]
+        expected = "GEOMETRYCOLLECTION (POINT (10 10), LINESTRING (20 20, 30 
30), POLYGON ((40 40, 70 70, 70 70, 40 40)), MULTIPOINT ((80 80)))"
+        assert expected == actual
+        actualSRID = actualDf.selectExpr("ST_SRID(geom)").first()[0]
+        assert 1000 == actualSRID
+
+
     def test_isPolygonCW(self):
         actual = self.spark.sql("SELECT ST_IsPolygonCW(ST_GeomFromWKT('POLYGON 
((20 35, 10 30, 10 10, 30 5, 45 20, 20 35),(30 20, 20 15, 20 25, 30 
20))'))").take(1)[0][0]
         assert not actual
diff --git 
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
 
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
index 2e957ad09..3e2eebabf 100644
--- 
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
+++ 
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctions.java
@@ -824,6 +824,18 @@ public class TestFunctions extends TestBase {
         "LINESTRING (1 2, 5 6)");
   }
 
+  @Test
+  public void test_ST_RemoveRepeatedPoints() {
+    registerUDF("ST_RemoveRepeatedPoints", byte[].class);
+    verifySqlSingleRes(
+        "select 
sedona.ST_AsText(sedona.ST_RemoveRepeatedPoints(sedona.ST_GeomFromText('LINESTRING
 (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 40 40)')))",
+        "LINESTRING (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 40 40)");
+    registerUDF("ST_RemoveRepeatedPoints", byte[].class, double.class);
+    verifySqlSingleRes(
+        "select 
sedona.ST_AsText(sedona.ST_RemoveRepeatedPoints(sedona.ST_GeomFromText('LINESTRING
 (20 20, 10 10, 30 30, 40 40, 20 20, 30 30, 40 40)'), 20))",
+        "LINESTRING (20 20, 40 40, 20 20, 40 40)");
+  }
+
   @Test
   public void test_ST_Reverse() {
     registerUDF("ST_Reverse", byte[].class);
diff --git 
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
 
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
index 6f6f81f2e..a8675eba0 100644
--- 
a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
+++ 
b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestFunctionsV2.java
@@ -781,6 +781,18 @@ public class TestFunctionsV2 extends TestBase {
         "LINESTRING(1 2,5 6)");
   }
 
+  @Test
+  public void test_ST_RemoveRepeatedPoints() {
+    registerUDFV2("ST_RemoveRepeatedPoints", String.class);
+    verifySqlSingleRes(
+        "select 
ST_AsText(sedona.ST_RemoveRepeatedPoints(ST_GeomFromText('LINESTRING (20 20, 10 
10, 30 30, 40 40, 20 20, 30 30, 40 40)')))",
+        "LINESTRING(20 20,10 10,30 30,40 40,20 20,30 30,40 40)");
+    registerUDFV2("ST_RemoveRepeatedPoints", String.class, double.class);
+    verifySqlSingleRes(
+        "select 
ST_AsText(sedona.ST_RemoveRepeatedPoints(ST_GeomFromText('LINESTRING (20 20, 10 
10, 30 30, 40 40, 20 20, 30 30, 40 40)'), 20))",
+        "LINESTRING(20 20,40 40,20 20,40 40)");
+  }
+
   @Test
   public void test_ST_Reverse() {
     registerUDFV2("ST_Reverse", String.class);
diff --git 
a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java 
b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
index aca52f847..b9268fc2b 100644
--- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
+++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java
@@ -901,6 +901,17 @@ public class UDFs {
         Functions.removePoint(GeometrySerde.deserialize(linestring), 
position));
   }
 
+  @UDFAnnotations.ParamMeta(argNames = {"geom"})
+  public static byte[] ST_RemoveRepeatedPoints(byte[] geom) {
+    return 
GeometrySerde.serialize(Functions.removeRepeatedPoints(GeometrySerde.deserialize(geom)));
+  }
+
+  @UDFAnnotations.ParamMeta(argNames = {"geom", "tolerance"})
+  public static byte[] ST_RemoveRepeatedPoints(byte[] geom, double tolerance) {
+    return GeometrySerde.serialize(
+        Functions.removeRepeatedPoints(GeometrySerde.deserialize(geom), 
tolerance));
+  }
+
   @UDFAnnotations.ParamMeta(argNames = {"geometry"})
   public static byte[] ST_Reverse(byte[] geometry) {
     return 
GeometrySerde.serialize(Functions.reverse(GeometrySerde.deserialize(geometry)));
diff --git 
a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java 
b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java
index 113ea91bf..089d4002e 100644
--- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java
+++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java
@@ -1007,6 +1007,24 @@ public class UDFsV2 {
         Functions.removePoint(GeometrySerde.deserGeoJson(linestring), 
position));
   }
 
+  @UDFAnnotations.ParamMeta(
+      argNames = {"geom"},
+      argTypes = {"Geometry"},
+      returnTypes = "Geometry")
+  public static String ST_RemoveRepeatedPoints(String geom) {
+    return GeometrySerde.serGeoJson(
+        Functions.removeRepeatedPoints(GeometrySerde.deserGeoJson(geom)));
+  }
+
+  @UDFAnnotations.ParamMeta(
+      argNames = {"geom", "tolerance"},
+      argTypes = {"Geometry", "double"},
+      returnTypes = "Geometry")
+  public static String ST_RemoveRepeatedPoints(String geom, double tolerance) {
+    return GeometrySerde.serGeoJson(
+        Functions.removeRepeatedPoints(GeometrySerde.deserGeoJson(geom), 
tolerance));
+  }
+
   @UDFAnnotations.ParamMeta(
       argNames = {"geometry"},
       argTypes = {"Geometry"},
diff --git 
a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala 
b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
index 9dda973ce..24df446ef 100644
--- a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
+++ b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
@@ -155,6 +155,7 @@ object Catalog {
     function[ST_AddMeasure](),
     function[ST_AddPoint](-1),
     function[ST_RemovePoint](-1),
+    function[ST_RemoveRepeatedPoints](),
     function[ST_SetPoint](),
     function[ST_IsPolygonCW](),
     function[ST_IsRing](),
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
index 0a4f5ecca..ce6500ede 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
@@ -851,6 +851,16 @@ case class ST_RemovePoint(inputExpressions: 
Seq[Expression])
   }
 }
 
+case class ST_RemoveRepeatedPoints(inputExpressions: Seq[Expression])
+    extends InferredExpression(
+      inferrableFunction2(Functions.removeRepeatedPoints),
+      inferrableFunction1(Functions.removeRepeatedPoints)) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
+
 case class ST_SetPoint(inputExpressions: Seq[Expression])
     extends InferredExpression(Functions.setPoint _) {
 
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
index 6dd411b7f..d1c94b126 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
@@ -468,6 +468,18 @@ object st_functions extends DataFrameAPI {
   def ST_RemovePoint(lineString: String, index: Int): Column =
     wrapExpression[ST_RemovePoint](lineString, index)
 
+  def ST_RemoveRepeatedPoints(geom: Column, tolerance: Column): Column =
+    wrapExpression[ST_RemoveRepeatedPoints](geom, tolerance)
+  def ST_RemoveRepeatedPoints(geom: String, tolerance: Double): Column =
+    wrapExpression[ST_RemoveRepeatedPoints](geom, tolerance)
+  def ST_RemoveRepeatedPoints(geom: String, tolerance: String): Column =
+    wrapExpression[ST_RemoveRepeatedPoints](geom, tolerance)
+
+  def ST_RemoveRepeatedPoints(geom: Column): Column =
+    wrapExpression[ST_RemoveRepeatedPoints](geom)
+  def ST_RemoveRepeatedPoints(geom: String): Column =
+    wrapExpression[ST_RemoveRepeatedPoints](geom)
+
   def ST_Reverse(geometry: Column): Column = 
wrapExpression[ST_Reverse](geometry)
   def ST_Reverse(geometry: String): Column = 
wrapExpression[ST_Reverse](geometry)
 
diff --git 
a/spark/common/src/test/scala/org/apache/sedona/sql/PreserveSRIDSuite.scala 
b/spark/common/src/test/scala/org/apache/sedona/sql/PreserveSRIDSuite.scala
index 9d90bbaf8..4714233da 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/PreserveSRIDSuite.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/PreserveSRIDSuite.scala
@@ -68,6 +68,7 @@ class PreserveSRIDSuite extends TestBaseScala with 
TableDrivenPropertyChecks {
       ("ST_AddMeasure(geom3, 0, 1)", 1000),
       ("ST_AddPoint(geom3, ST_Point(0.5, 0.5), 1)", 1000),
       ("ST_RemovePoint(geom3, 1)", 1000),
+      ("ST_RemoveRepeatedPoints(geom3, 1)", 1000),
       ("ST_SetPoint(geom3, 1, ST_Point(0.5, 0.5))", 1000),
       ("ST_ClosestPoint(geom1, geom2)", 1000),
       ("ST_FlipCoordinates(geom1)", 1000),
diff --git 
a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala 
b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
index ee9db762d..b7e0a5686 100644
--- 
a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
+++ 
b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
@@ -1170,6 +1170,17 @@ class dataFrameAPITestScala extends TestBaseScala {
       assert(actualResult == expectedResult)
     }
 
+    it("Passed ST_RemoveRepeatedPoints") {
+      val baseDf = sparkSession.sql(
+        "SELECT ST_GeomFromWKT('MULTIPOINT (20 20, 10 10, 30 30, 40 40, 20 20, 
30 30, 40 40)', 2000) AS geom")
+      val actualDf = baseDf.select(ST_RemoveRepeatedPoints("geom", 
20).as("geom"))
+      val actual = actualDf.select(ST_AsText("geom")).first().get(0)
+      val expected = "MULTIPOINT ((10 10), (30 30))"
+      assertEquals(expected, actual)
+      val actualSRID = actualDf.select(ST_SRID("geom")).first().get(0)
+      assertEquals(2000, actualSRID)
+    }
+
     it("Passed ST_SetPoint") {
       val baseDf = sparkSession.sql(
         "SELECT ST_GeomFromWKT('LINESTRING (0 0, 1 0)') AS line, ST_Point(1.0, 
1.0) AS point")
diff --git 
a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala 
b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
index 72d5b990d..fcbb45d9d 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
@@ -1821,6 +1821,47 @@ class functionTestScala
       3) shouldBe None
   }
 
+  it("Should pass ST_RemoveRepeatedPoints") {
+    val baseDf = sparkSession.sql(
+      "SELECT ST_GeomFromWKT('GEOMETRYCOLLECTION (POINT (10 10),LINESTRING (20 
20, 20 20, 30 30, 30 30),POLYGON ((40 40, 50 50, 50 50, 60 60, 60 60, 70 70, 70 
70, 40 40)), MULTIPOINT ((80 80), (90 90), (90 90), (100 100)))', 1000) AS 
geom")
+    val actualDf = baseDf.selectExpr("ST_RemoveRepeatedPoints(geom, 1000) as 
geom")
+    var actual = actualDf.selectExpr("ST_AsText(geom)").first().get(0)
+    var expected =
+      "GEOMETRYCOLLECTION (POINT (10 10), LINESTRING (20 20, 30 30), POLYGON 
((40 40, 70 70, 70 70, 40 40)), MULTIPOINT ((80 80)))";
+    assertEquals(expected, actual)
+    val actualSRID = actualDf.selectExpr("ST_SRID(geom)").first().get(0)
+    assertEquals(1000, actualSRID)
+
+    actual = sparkSession
+      .sql("SELECT 
ST_AsText(ST_RemoveRepeatedPoints(ST_GeomFromWKT('MULTIPOINT ((1 1), (2 2), (3 
3), (2 2))')))")
+      .first()
+      .get(0);
+    expected = "MULTIPOINT ((1 1), (2 2), (3 3))"
+    assertEquals(expected, actual)
+
+    actual = sparkSession
+      .sql("SELECT 
ST_AsText(ST_RemoveRepeatedPoints(ST_GeomFromWKT('LINESTRING (0 0, 0 0, 1 1, 0 
0, 1 1, 2 2)')))")
+      .first()
+      .get(0);
+    expected = "LINESTRING (0 0, 1 1, 0 0, 1 1, 2 2)"
+    assertEquals(expected, actual)
+
+    actual = sparkSession
+      .sql("SELECT 
ST_AsText(ST_RemoveRepeatedPoints(ST_GeomFromWKT('GEOMETRYCOLLECTION 
(LINESTRING (1 1, 2 2, 2 2, 3 3), POINT (4 4), POINT (4 4), POINT (5 5))')))")
+      .first()
+      .get(0);
+    expected =
+      "GEOMETRYCOLLECTION (LINESTRING (1 1, 2 2, 3 3), POINT (4 4), POINT (4 
4), POINT (5 5))"
+    assertEquals(expected, actual)
+
+    actual = sparkSession
+      .sql("SELECT 
ST_AsText(ST_RemoveRepeatedPoints(ST_GeomFromWKT('LINESTRING (0 0, 0 0, 1 1, 5 
5, 1 1, 2 2)'), 2))")
+      .first()
+      .get(0);
+    expected = "LINESTRING (0 0, 5 5, 2 2)"
+    assertEquals(expected, actual)
+  }
+
   it("Should correctly set using ST_SetPoint") {
     calculateStSetPointOption("Linestring(0 0, 1 1, 1 0, 0 0)", 0, "Point(0 
1)") shouldBe Some(
       "LINESTRING (0 1, 1 1, 1 0, 0 0)")


Reply via email to