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 52b6ae8e71 [SEDONA-693] Add ST_Perimeter2D (#1745)
52b6ae8e71 is described below

commit 52b6ae8e71601cdf36a6176198839bc3daf5547c
Author: Furqaan Khan <[email protected]>
AuthorDate: Thu Jan 9 13:37:54 2025 -0500

    [SEDONA-693] Add ST_Perimeter2D (#1745)
    
    * feat: add ST_Perimeter2D, alias of ST_Perimeter
    
    * fix: port function to flink
    
    * chore: change note to info in annotation
---
 docs/api/flink/Function.md                         | 48 +++++++++++++++++
 docs/api/snowflake/vector-data/Function.md         | 46 +++++++++++++++++
 docs/api/sql/Function.md                           | 60 ++++++++++++++++++++--
 .../main/java/org/apache/sedona/flink/Catalog.java |  1 +
 .../apache/sedona/flink/expressions/Functions.java | 27 ++++++++++
 .../java/org/apache/sedona/flink/FunctionTest.java | 26 ++++++++++
 python/sedona/sql/st_functions.py                  | 24 +++++++++
 python/tests/sql/test_dataframe_api.py             |  9 ++++
 python/tests/sql/test_function.py                  | 19 +++++++
 .../sedona/snowflake/snowsql/TestFunctions.java    | 19 +++++++
 .../sedona/snowflake/snowsql/TestFunctionsV2.java  | 13 +++++
 .../org/apache/sedona/snowflake/snowsql/UDFs.java  | 15 ++++++
 .../apache/sedona/snowflake/snowsql/UDFsV2.java    | 24 +++++++++
 .../scala/org/apache/sedona/sql/UDF/Catalog.scala  |  1 +
 .../sql/sedona_sql/expressions/Functions.scala     | 11 ++++
 .../sql/sedona_sql/expressions/st_functions.scala  | 11 ++++
 .../apache/sedona/sql/dataFrameAPITestScala.scala  | 19 +++++++
 .../org/apache/sedona/sql/functionTestScala.scala  | 18 +++++++
 18 files changed, 387 insertions(+), 4 deletions(-)

diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index d735fe97b1..e2273ae885 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -3191,6 +3191,54 @@ Output:
 2216860.5497177234
 ```
 
+## ST_Perimeter2D
+
+Introduction: This function calculates the 2D perimeter of a given geometry. 
It supports Polygon, MultiPolygon, and GeometryCollection geometries (as long 
as the GeometryCollection contains polygonal geometries). For other types, it 
returns 0. To measure lines, use [ST_Length](#st_length).
+
+To get the perimeter in meters, set `use_spheroid` to `true`. This calculates 
the geodesic perimeter using the WGS84 spheroid. When using `use_spheroid`, the 
`lenient` parameter defaults to true, assuming the geometry uses EPSG:4326. To 
throw an exception instead, set `lenient` to `false`.
+
+!!!Info
+    This function is an alias for [ST_Perimeter](#st_perimeter).
+
+Format:
+
+`ST_Perimeter2D(geom: Geometry)`
+
+`ST_Perimeter2D(geom: Geometry, use_spheroid: Boolean)`
+
+`ST_Perimeter2D(geom: Geometry, use_spheroid: Boolean, lenient: Boolean = 
True)`
+
+Since: `v1.7.1`
+
+SQL Example:
+
+```sql
+SELECT ST_Perimeter2D(
+        ST_GeomFromText('POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))')
+)
+```
+
+Output:
+
+```
+20.0
+```
+
+SQL Example:
+
+```sql
+SELECT ST_Perimeter2D(
+        ST_GeomFromText('POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))', 4326),
+        true, false
+)
+```
+
+Output:
+
+```
+2216860.5497177234
+```
+
 ## 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/docs/api/snowflake/vector-data/Function.md 
b/docs/api/snowflake/vector-data/Function.md
index 3fdd484283..f6b8c447f2 100644
--- a/docs/api/snowflake/vector-data/Function.md
+++ b/docs/api/snowflake/vector-data/Function.md
@@ -2425,6 +2425,52 @@ Output:
 2216860.5497177234
 ```
 
+## ST_Perimeter2D
+
+Introduction: This function calculates the 2D perimeter of a given geometry. 
It supports Polygon, MultiPolygon, and GeometryCollection geometries (as long 
as the GeometryCollection contains polygonal geometries). For other types, it 
returns 0. To measure lines, use [ST_Length](#st_length).
+
+To get the perimeter in meters, set `use_spheroid` to `true`. This calculates 
the geodesic perimeter using the WGS84 spheroid. When using `use_spheroid`, the 
`lenient` parameter defaults to true, assuming the geometry uses EPSG:4326. To 
throw an exception instead, set `lenient` to `false`.
+
+!!!Info
+    This function is an alias for [ST_Perimeter](#st_perimeter).
+
+Format:
+
+`ST_Perimeter2D(geom: Geometry)`
+
+`ST_Perimeter2D(geom: Geometry, use_spheroid: Boolean)`
+
+`ST_Perimeter2D(geom: Geometry, use_spheroid: Boolean, lenient: Boolean = 
True)`
+
+SQL Example:
+
+```sql
+SELECT ST_Perimeter2D(
+        ST_GeomFromText('POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))')
+)
+```
+
+Output:
+
+```
+20.0
+```
+
+SQL Example:
+
+```sql
+SELECT ST_Perimeter2D(
+        ST_GeomFromText('POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))', 4326),
+        true, false
+)
+```
+
+Output:
+
+```
+2216860.5497177234
+```
+
 ## 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/docs/api/sql/Function.md b/docs/api/sql/Function.md
index 8aa8aa4404..5346fb25bf 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -723,12 +723,14 @@ SQL Example
 
 ```sql
 ST_BinaryDistanceBandColumn(geometry, 1.0, true, true, false, struct(id, 
geometry))
-````
+```
 
 Output:
 
-```
+```sql
+{% raw %}
 [{{15, POINT (3 1.9)}, 1.0}, {{16, POINT (3 2)}, 1.0}, {{17, POINT (3 2.1)}, 
1.0}, {{18, POINT (3 2.2)}, 1.0}]
+{% endraw %}
 ```
 
 ## ST_Boundary
@@ -3377,6 +3379,54 @@ Output:
 2216860.5497177234
 ```
 
+## ST_Perimeter2D
+
+Introduction: This function calculates the 2D perimeter of a given geometry. 
It supports Polygon, MultiPolygon, and GeometryCollection geometries (as long 
as the GeometryCollection contains polygonal geometries). For other types, it 
returns 0. To measure lines, use [ST_Length](#st_length).
+
+To get the perimeter in meters, set `use_spheroid` to `true`. This calculates 
the geodesic perimeter using the WGS84 spheroid. When using `use_spheroid`, the 
`lenient` parameter defaults to true, assuming the geometry uses EPSG:4326. To 
throw an exception instead, set `lenient` to `false`.
+
+!!!Info
+    This function is an alias for [ST_Perimeter](#st_perimeter).
+
+Format:
+
+`ST_Perimeter2D(geom: Geometry)`
+
+`ST_Perimeter2D(geom: Geometry, use_spheroid: Boolean)`
+
+`ST_Perimeter2D(geom: Geometry, use_spheroid: Boolean, lenient: Boolean = 
True)`
+
+Since: `v1.7.1`
+
+SQL Example:
+
+```sql
+SELECT ST_Perimeter2D(
+        ST_GeomFromText('POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))')
+)
+```
+
+Output:
+
+```
+20.0
+```
+
+SQL Example:
+
+```sql
+SELECT ST_Perimeter2D(
+        ST_GeomFromText('POLYGON((0 0, 0 5, 5 5, 5 0, 0 0))', 4326),
+        true, false
+)
+```
+
+Output:
+
+```
+2216860.5497177234
+```
+
 ## 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.
@@ -4531,12 +4581,14 @@ SQL Example
 
 ```sql
 ST_WeightedDistanceBandColumn(geometry, 1.0, -1.0, true, true, 1.0, false, 
struct(id, geometry))
-````
+```
 
 Output:
 
-```
+```sql
+{% raw %}
 [{{15, POINT (3 1.9)}, 1.0}, {{16, POINT (3 2)}, 9.999999999999991}, {{17, 
POINT (3 2.1)}, 4.999999999999996}, {{18, POINT (3 2.2)}, 3.3333333333333304}]
+{% endraw %}
 ```
 
 ## ST_X
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 ba42f9febe..1654bf0d28 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -102,6 +102,7 @@ public class Catalog {
       new Functions.ST_FlipCoordinates(),
       new Functions.ST_GeoHash(),
       new Functions.ST_Perimeter(),
+      new Functions.ST_Perimeter2D(),
       new Functions.ST_PointOnSurface(),
       new Functions.ST_Scale(),
       new Functions.ST_ScaleGeom(),
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 96a00f7b4d..d23adf03f5 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
@@ -622,6 +622,33 @@ public class Functions {
     }
   }
 
+  public static class ST_Perimeter2D extends ScalarFunction {
+    @DataTypeHint(value = "Double")
+    public Double eval(
+        @DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class)
+            Object o) {
+      Geometry geom = (Geometry) o;
+      return org.apache.sedona.common.Functions.perimeter(geom);
+    }
+
+    @DataTypeHint(value = "Double")
+    public Double eval(
+        @DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class) Object o,
+        Boolean use_spheroid) {
+      Geometry geom = (Geometry) o;
+      return org.apache.sedona.common.Functions.perimeter(geom, use_spheroid);
+    }
+
+    @DataTypeHint(value = "Double")
+    public Double eval(
+        @DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class) Object o,
+        Boolean use_spheroid,
+        boolean lenient) {
+      Geometry geom = (Geometry) o;
+      return org.apache.sedona.common.Functions.perimeter(geom, use_spheroid, 
lenient);
+    }
+  }
+
   public static class ST_PointOnSurface 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 dc510c1935..949464b96a 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -821,6 +821,32 @@ public class FunctionTest extends TestBase {
     assertEquals(443770.91724830196, perimeter, FP_TOLERANCE);
   }
 
+  @Test
+  public void testPerimeter2D() {
+    Table polygonTable = createPolygonTable(testDataSize);
+    Table perimeterTable =
+        polygonTable.select(
+            call(Functions.ST_Perimeter2D.class.getSimpleName(), 
$(polygonColNames[0])));
+    Double perimeter = (Double) first(perimeterTable).getField(0);
+    assertEquals(4.0, perimeter, FP_TOLERANCE);
+
+    polygonTable =
+        tableEnv.sqlQuery(
+            "SELECT ST_GeomFromWKT('POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))', 
4326) AS geom");
+    perimeterTable =
+        polygonTable.select(
+            call(Functions.ST_Perimeter2D.class.getSimpleName(), $("geom"), 
true, false));
+    perimeter = (Double) first(perimeterTable).getField(0);
+    assertEquals(443770.91724830196, perimeter, FP_TOLERANCE);
+
+    polygonTable =
+        tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POLYGON ((0 0, 0 1, 1 1, 1 
0, 0 0))') AS geom");
+    perimeterTable =
+        
polygonTable.select(call(Functions.ST_Perimeter2D.class.getSimpleName(), 
$("geom"), true));
+    perimeter = (Double) first(perimeterTable).getField(0);
+    assertEquals(443770.91724830196, perimeter, FP_TOLERANCE);
+  }
+
   @Test
   public void testPointOnSurface() {
     Table pointTable = createPointTable_real(testDataSize);
diff --git a/python/sedona/sql/st_functions.py 
b/python/sedona/sql/st_functions.py
index b3f67fd522..b5d4eb6dcf 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -1225,6 +1225,30 @@ def ST_Perimeter(
     return _call_st_function("ST_Perimeter", args)
 
 
+@validate_argument_types
+def ST_Perimeter2D(
+    geom: ColumnOrName,
+    use_spheroid: Optional[Union[ColumnOrName, bool]] = None,
+    lenient: Optional[Union[ColumnOrName, bool]] = None,
+) -> Column:
+    """Returns the perimeter of a Polygon/MultiPolygon geometries. Otherwise, 
returns 0
+
+    @param geom: Polygonal geometry
+    @param use_spheroid: Use Spheroid
+    @param lenient: suppresses the exception
+    @return: Perimeter of a Polygon/MultiPolygon geometries
+    """
+
+    args = (geom, use_spheroid, lenient)
+
+    if lenient is None:
+        if use_spheroid is None:
+            args = (geom,)
+        else:
+            args = (geom, use_spheroid)
+    return _call_st_function("ST_Perimeter2D", args)
+
+
 @validate_argument_types
 def ST_Points(geometry: ColumnOrName) -> Column:
     """Creates a MultiPoint geometry consisting of all the coordinates of the 
input geometry
diff --git a/python/tests/sql/test_dataframe_api.py 
b/python/tests/sql/test_dataframe_api.py
index 8299f5b45f..0c1d0bd200 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -761,6 +761,15 @@ test_configurations = [
         "ceil(geom)",
         378794,
     ),
+    (stf.ST_Perimeter2D, ("geom",), "triangle_geom", "", 3.414213562373095),
+    (stf.ST_Perimeter2D, ("geom", True), "triangle_geom", "ceil(geom)", 
378794),
+    (
+        stf.ST_Perimeter2D,
+        (lambda: stf.ST_SetSRID("geom", 4326), True),
+        "triangle_geom",
+        "ceil(geom)",
+        378794,
+    ),
     (
         stf.ST_Points,
         ("line",),
diff --git a/python/tests/sql/test_function.py 
b/python/tests/sql/test_function.py
index 6c1e968c0d..0050708bd0 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -1685,6 +1685,25 @@ class TestPredicateJoin(TestBase):
         expected = 443770.91724830196
         assert expected == actual
 
+    def test_st_perimeter2D(self):
+        baseDf = self.spark.sql(
+            "SELECT ST_GeomFromWKT('POLYGON((743238 2967416,743238 
2967450,743265 2967450,743265.625 2967416,743238 2967416))') AS geom"
+        )
+        actual = baseDf.selectExpr("ST_Perimeter2D(geom)").take(1)[0][0]
+        expected = 122.63074400009504
+        assert actual == expected
+
+        baseDf = self.spark.sql(
+            "SELECT ST_GeomFromWKT('POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))', 
4326) AS geom"
+        )
+        actual = baseDf.selectExpr("ST_Perimeter2D(geom, true)").first()[0]
+        expected = 443770.91724830196
+        assert expected == actual
+
+        actual = baseDf.selectExpr("ST_Perimeter2D(geom, true, 
false)").first()[0]
+        expected = 443770.91724830196
+        assert expected == actual
+
     def test_st_points(self):
         # Given
         geometry_df = self.spark.createDataFrame(
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 916c4e3371..e4ffafab3f 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
@@ -807,6 +807,25 @@ public class TestFunctions extends TestBase {
         2216861.0);
   }
 
+  @Test
+  public void test_ST_Perimeter2D() {
+    registerUDF("ST_Perimeter2D", byte[].class);
+    verifySqlSingleRes(
+        "SELECT sedona.ST_Perimeter2D(sedona.ST_GeomFromText('POLYGON((0 0, 0 
5, 5 5, 5 0, 0 0))'))",
+        20.0);
+
+    registerUDF("ST_Perimeter2D", byte[].class, boolean.class);
+    verifySqlSingleRes(
+        "SELECT CEIL(sedona.ST_Perimeter2D(sedona.ST_GeomFromText('POLYGON((0 
0, 0 5, 5 5, 5 0, 0 0))'), true))",
+        2216861.0);
+
+    registerUDF("ST_Perimeter2D", byte[].class, boolean.class, boolean.class);
+    registerUDF("ST_GeomFromText", String.class, int.class);
+    verifySqlSingleRes(
+        "SELECT CEIL(sedona.ST_Perimeter2D(sedona.ST_GeomFromText('POLYGON((0 
0, 0 5, 5 5, 5 0, 0 0))', 4326), true, false))",
+        2216861.0);
+  }
+
   @Test
   public void test_ST_PointOnSurface() {
     registerUDF("ST_PointOnSurface", 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 25a4006690..30488f9fae 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
@@ -748,6 +748,19 @@ public class TestFunctionsV2 extends TestBase {
         2216861.0);
   }
 
+  @Test
+  public void test_ST_Perimeter2D() {
+    registerUDFV2("ST_Perimeter2D", String.class);
+    verifySqlSingleRes(
+        "SELECT sedona.ST_Perimeter2D(ST_GeomFromText('POLYGON((0 0, 0 5, 5 5, 
5 0, 0 0))'))",
+        20.0);
+
+    registerUDFV2("ST_Perimeter2D", String.class, boolean.class);
+    verifySqlSingleRes(
+        "SELECT CEIL(sedona.ST_Perimeter2D(ST_GeomFromText('POLYGON((0 0, 0 5, 
5 5, 5 0, 0 0))'), true))",
+        2216861.0);
+  }
+
   @Test
   public void test_ST_PointOnSurface() {
     registerUDFV2("ST_PointOnSurface", 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 485e54730b..98fdbb0608 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
@@ -839,6 +839,21 @@ public class UDFs {
     return Functions.perimeter(GeometrySerde.deserialize(geometry), 
use_spheroid, lenient);
   }
 
+  @UDFAnnotations.ParamMeta(argNames = {"geometry"})
+  public static double ST_Perimeter2D(byte[] geometry) {
+    return Functions.perimeter(GeometrySerde.deserialize(geometry));
+  }
+
+  @UDFAnnotations.ParamMeta(argNames = {"geometry", "use_spheroid"})
+  public static double ST_Perimeter2D(byte[] geometry, boolean use_spheroid) {
+    return Functions.perimeter(GeometrySerde.deserialize(geometry), 
use_spheroid);
+  }
+
+  @UDFAnnotations.ParamMeta(argNames = {"geometry", "use_spheroid", "lenient"})
+  public static double ST_Perimeter2D(byte[] geometry, boolean use_spheroid, 
boolean lenient) {
+    return Functions.perimeter(GeometrySerde.deserialize(geometry), 
use_spheroid, lenient);
+  }
+
   @UDFAnnotations.ParamMeta(argNames = {"geometry"})
   public static byte[] ST_PointOnSurface(byte[] geometry) {
     return 
GeometrySerde.serialize(Functions.pointOnSurface(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 f135a51881..2d9b349b18 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
@@ -989,6 +989,30 @@ public class UDFsV2 {
     return Functions.perimeter(GeometrySerde.deserGeoJson(geometry), 
use_spheroid, lenient);
   }
 
+  @UDFAnnotations.ParamMeta(
+      argNames = {"geometry"},
+      argTypes = {"Geometry"},
+      returnTypes = "double")
+  public static double ST_Perimeter2D(String geometry) {
+    return Functions.perimeter(GeometrySerde.deserGeoJson(geometry));
+  }
+
+  @UDFAnnotations.ParamMeta(
+      argNames = {"geometry", "use_spheroid"},
+      argTypes = {"Geometry", "boolean"},
+      returnTypes = "double")
+  public static double ST_Perimeter2D(String geometry, boolean use_spheroid) {
+    return Functions.perimeter(GeometrySerde.deserGeoJson(geometry), 
use_spheroid);
+  }
+
+  @UDFAnnotations.ParamMeta(
+      argNames = {"geometry", "use_spheroid", "lenient"},
+      argTypes = {"Geometry", "boolean", "boolean"},
+      returnTypes = "double")
+  public static double ST_Perimeter2D(String geometry, boolean use_spheroid, 
boolean lenient) {
+    return Functions.perimeter(GeometrySerde.deserGeoJson(geometry), 
use_spheroid, lenient);
+  }
+
   @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 371fe41913..3c9fc62450 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
@@ -57,6 +57,7 @@ object Catalog {
     function[ST_GeomFromKML](),
     function[ST_CoordDim](),
     function[ST_Perimeter](),
+    function[ST_Perimeter2D](),
     function[ST_Point](),
     function[ST_Points](),
     function[ST_MakeEnvelope](),
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 0f2a20713f..d90ea0cfac 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
@@ -1015,6 +1015,17 @@ case class ST_Perimeter(inputExpressions: 
Seq[Expression])
   }
 }
 
+case class ST_Perimeter2D(inputExpressions: Seq[Expression])
+    extends InferredExpression(
+      inferrableFunction3(Functions.perimeter),
+      inferrableFunction2(Functions.perimeter),
+      inferrableFunction1(Functions.perimeter)) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
+
 case class ST_Points(inputExpressions: Seq[Expression])
     extends InferredExpression(Functions.points _) {
 
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 0c332b10b9..f239bacbac 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
@@ -385,6 +385,17 @@ object st_functions extends DataFrameAPI {
   def ST_Perimeter(geom: String, use_spheroid: Boolean, lenient: Boolean): 
Column =
     wrapExpression[ST_Perimeter](geom, use_spheroid, lenient)
 
+  def ST_Perimeter2D(geom: Column): Column = 
wrapExpression[ST_Perimeter2D](geom)
+  def ST_Perimeter2D(geom: String): Column = 
wrapExpression[ST_Perimeter2D](geom)
+  def ST_Perimeter2D(geom: Column, use_spheroid: Column): Column =
+    wrapExpression[ST_Perimeter2D](geom, use_spheroid)
+  def ST_Perimeter2D(geom: String, use_spheroid: Boolean): Column =
+    wrapExpression[ST_Perimeter2D](geom, use_spheroid)
+  def ST_Perimeter2D(geom: Column, use_spheroid: Column, lenient: Column): 
Column =
+    wrapExpression[ST_Perimeter2D](geom, use_spheroid, lenient)
+  def ST_Perimeter2D(geom: String, use_spheroid: Boolean, lenient: Boolean): 
Column =
+    wrapExpression[ST_Perimeter2D](geom, use_spheroid, lenient)
+
   def ST_Points(geom: Column): Column = wrapExpression[ST_Points](geom)
   def ST_Points(geom: String): Column = wrapExpression[ST_Points](geom)
 
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 f1ece09481..11de675d53 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
@@ -811,6 +811,25 @@ class dataFrameAPITestScala extends TestBaseScala {
       assertEquals(expected, actual)
     }
 
+    it("Passed ST_Perimeter2D") {
+      var baseDf = sparkSession.sql(
+        "SELECT ST_GeomFromWKT('POLYGON((743238 2967416,743238 2967450,743265 
2967450,743265.625 2967416,743238 2967416))') AS geom")
+      var actual = baseDf.select(ST_Perimeter2D("geom")).first().get(0)
+      var expected = 122.63074400009504
+      assertEquals(expected, actual)
+
+      baseDf = sparkSession.sql(
+        "SELECT ST_GeomFromWKT('POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))', 4326) AS 
geom")
+      actual = baseDf.select(ST_Perimeter2D("geom", use_spheroid = 
true)).first().get(0)
+      expected = 443770.91724830196
+      assertEquals(expected, actual)
+
+      actual =
+        baseDf.select(ST_Perimeter2D("geom", use_spheroid = true, lenient = 
false)).first().get(0)
+      expected = 443770.91724830196
+      assertEquals(expected, actual)
+    }
+
     it("Passed ST_Project") {
       val baseDf = sparkSession.sql(
         "SELECT ST_GeomFromWKT('POINT(0 0)') as point, ST_MakeEnvelope(0, 1, 
2, 0) as poly")
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 90848ebf83..0e352b1e9e 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
@@ -643,6 +643,24 @@ class functionTestScala
       assertEquals(expected, actual)
     }
 
+    it("Passed ST_Perimeter2D") {
+      var baseDf = sparkSession.sql(
+        "SELECT ST_GeomFromWKT('POLYGON((743238 2967416,743238 2967450,743265 
2967450,743265.625 2967416,743238 2967416))') AS geom")
+      var actual = baseDf.selectExpr("ST_Perimeter2D(geom)").first().get(0)
+      var expected = 122.63074400009504
+      assertEquals(expected, actual)
+
+      baseDf = sparkSession.sql(
+        "SELECT ST_GeomFromWKT('POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))', 4326) AS 
geom")
+      actual = baseDf.selectExpr("ST_Perimeter2D(geom, true)").first().get(0)
+      expected = 443770.91724830196
+      assertEquals(expected, actual)
+
+      actual = baseDf.selectExpr("ST_Perimeter2D(geom, true, 
false)").first().get(0)
+      expected = 443770.91724830196
+      assertEquals(expected, actual)
+    }
+
     it("Passed ST_Points") {
 
       val testtable = sparkSession.sql(

Reply via email to