This is an automated email from the ASF dual-hosted git repository.

jiayu pushed a commit to branch SEDONA-576
in repository https://gitbox.apache.org/repos/asf/sedona.git

commit 22cf352867ac0c9cdbefd2ddd988a4b35c50e717
Author: Furqaan Khan <[email protected]>
AuthorDate: Fri Apr 5 16:55:11 2024 -0400

    [TASK-105] Add ST_HasZ (#154)
    
    * feat: add ST_HasZ
    
    * fix: type
    
    * fix: typo
    
    * fix: snowflake tests
    
    * fix: old broken snowflake tests
    
    * fix: old broken snowflake tests
    
    * fix: old broken snowflake tests
---
 .../java/org/apache/sedona/common/Functions.java   |  5 +++++
 .../org/apache/sedona/common/FunctionsTest.java    | 15 +++++++++++++++
 docs/api/flink/Function.md                         | 22 ++++++++++++++++++++++
 docs/api/snowflake/vector-data/Function.md         | 20 ++++++++++++++++++++
 docs/api/sql/Function.md                           | 22 ++++++++++++++++++++++
 .../main/java/org/apache/sedona/flink/Catalog.java |  1 +
 .../apache/sedona/flink/expressions/Functions.java |  8 ++++++++
 .../java/org/apache/sedona/flink/FunctionTest.java |  7 +++++++
 python/sedona/sql/st_functions.py                  | 11 +++++++++++
 python/tests/sql/test_dataframe_api.py             |  2 ++
 python/tests/sql/test_function.py                  |  5 +++++
 .../sedona/snowflake/snowsql/TestFunctions.java    | 16 ++++++++++++----
 .../sedona/snowflake/snowsql/TestFunctionsV2.java  |  9 +++++++++
 .../org/apache/sedona/snowflake/snowsql/UDFs.java  |  7 +++++++
 .../apache/sedona/snowflake/snowsql/UDFsV2.java    |  7 +++++++
 .../scala/org/apache/sedona/sql/UDF/Catalog.scala  |  1 +
 .../sql/sedona_sql/expressions/Functions.scala     |  8 ++++++++
 .../sql/sedona_sql/expressions/st_functions.scala  |  3 +++
 .../apache/sedona/sql/dataFrameAPITestScala.scala  |  6 ++++++
 .../org/apache/sedona/sql/functionTestScala.scala  |  6 ++++++
 20 files changed, 177 insertions(+), 4 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 774a2bfe3..79be14b83 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -483,6 +483,11 @@ public class Functions {
         return !Double.isNaN(coord.getM());
     }
 
+    public static boolean hasZ(Geometry geom) {
+        Coordinate coord = geom.getCoordinate();
+        return !Double.isNaN(coord.getZ());
+    }
+
     public static Geometry flipCoordinates(Geometry geometry) {
         GeomUtils.flipCoordinates(geometry);
         return geometry;
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 6d15e20ca..3e1f1cb68 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -388,6 +388,21 @@ public class FunctionsTest extends TestBase {
         assertEquals(actualResult, expectedResult);
     }
 
+    @Test
+    public void hasZ() throws ParseException {
+        Geometry geom = Constructors.geomFromWKT("POINT ZM(1 2 3 4)", 0);
+        assertTrue(Functions.hasZ(geom));
+
+        geom = Constructors.geomFromWKT("POINT(1 2)", 0);
+        assertFalse(Functions.hasZ(geom));
+
+        geom = Constructors.geomFromWKT("POINT(34 25)", 0);
+        assertFalse(Functions.hasZ(geom));
+
+        geom = Constructors.geomFromWKT("POLYGON ZM ((30 10 5 1, 40 40 10 2, 
20 40 15 3, 10 20 20 4, 30 10 5 1))", 0);
+        assertTrue(Functions.hasZ(geom));
+    }
+
     @Test
     public void hasM() throws ParseException {
         Geometry geom = Constructors.geomFromWKT("POINT ZM(1 2 3 4)", 0);
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 318c97354..83efa8d89 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -1574,6 +1574,28 @@ Output:
 True
 ```
 
+## ST_HasZ
+
+Introduction: Checks for the presence of Z coordinate values representing 
measures or linear references. Returns true if the input geometry includes an Z 
coordinate, false otherwise.
+
+Format: `ST_HasZ(geom: Geometry)`
+
+Since: `vTBD`
+
+SQL Example
+
+```sql
+SELECT ST_HasZ(
+        ST_GeomFromWKT('LINESTRING Z (30 10 5, 40 40 10, 20 40 15, 10 20 20)')
+)
+```
+
+Output:
+
+```
+True
+```
+
 ## ST_HausdorffDistance
 
 Introduction: Returns a discretized (and hence approximate) [Hausdorff 
distance](https://en.wikipedia.org/wiki/Hausdorff_distance) between the given 2 
geometries.
diff --git a/docs/api/snowflake/vector-data/Function.md 
b/docs/api/snowflake/vector-data/Function.md
index 54afc40d6..bda335535 100644
--- a/docs/api/snowflake/vector-data/Function.md
+++ b/docs/api/snowflake/vector-data/Function.md
@@ -1125,6 +1125,26 @@ SELECT ST_GeometryType(polygondf.countyshape)
 FROM polygondf
 ```
 
+## ST_HasZ
+
+Introduction: Checks for the presence of Z coordinate values representing 
measures or linear references. Returns true if the input geometry includes an Z 
coordinate, false otherwise.
+
+Format: `ST_HasZ(geom: Geometry)`
+
+SQL Example
+
+```sql
+SELECT ST_HasZ(
+        ST_GeomFromWKT('LINESTRING Z (30 10 5, 40 40 10, 20 40 15, 10 20 20)')
+)
+```
+
+Output:
+
+```
+True
+```
+
 ## ST_HausdorffDistance
 
 Introduction: Returns a discretized (and hence approximate) [Hausdorff 
distance](https://en.wikipedia.org/wiki/Hausdorff_distance) between the given 2 
geometries.
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index 928770887..0643d5662 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -1580,6 +1580,28 @@ Output:
 True
 ```
 
+## ST_HasZ
+
+Introduction: Checks for the presence of Z coordinate values representing 
measures or linear references. Returns true if the input geometry includes an Z 
coordinate, false otherwise.
+
+Format: `ST_HasZ(geom: Geometry)`
+
+Since: `vTBD`
+
+SQL Example
+
+```sql
+SELECT ST_HasZ(
+        ST_GeomFromWKT('LINESTRING Z (30 10 5, 40 40 10, 20 40 15, 10 20 20)')
+)
+```
+
+Output:
+
+```
+True
+```
+
 ## ST_HausdorffDistance
 
 Introduction: Returns a discretized (and hence approximate) [Hausdorff 
distance](https://en.wikipedia.org/wiki/Hausdorff_distance) between the given 2 
geometries.
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 6b5639f97..d635e1bf8 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -126,6 +126,7 @@ public class Catalog {
                 new Functions.ST_LineFromMultiPoint(),
                 new Functions.ST_LineMerge(),
                 new Functions.ST_LineSubstring(),
+                new Functions.ST_HasZ(),
                 new Functions.ST_HasM(),
                 new Functions.ST_M(),
                 new Functions.ST_MMin(),
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 7397087c5..001cbb627 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
@@ -757,6 +757,14 @@ public class Functions {
         }
     }
 
+    public static class ST_HasZ extends ScalarFunction {
+        @DataTypeHint("Boolean")
+        public Boolean eval(@DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class) Object o) {
+            Geometry geom = (Geometry) o;
+            return org.apache.sedona.common.Functions.hasZ(geom);
+        }
+    }
+
     public static class ST_M extends  ScalarFunction {
         @DataTypeHint("Double")
         public Double 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 9d4d6bb79..99781c4d1 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -699,6 +699,13 @@ public class FunctionTest extends TestBase{
         assertEquals(7.89, first(pointTable).getField(0));
     }
 
+    @Test
+    public void testHasZ() {
+        Table polyTable = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POLYGON ZM 
((30 10 5 1, 40 40 10 2, 20 40 15 3, 10 20 20 4, 30 10 5 1))') as poly");
+        boolean actual = (boolean) 
first(polyTable.select(call(Functions.ST_HasZ.class.getSimpleName(), 
$("poly")))).getField(0);
+        assertTrue(actual);
+    }
+
     @Test
     public void testHasM() {
         Table polyTable = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POLYGON ZM 
((30 10 5 1, 40 40 10 2, 20 40 15 3, 10 20 20 4, 30 10 5 1))') as poly");
diff --git a/python/sedona/sql/st_functions.py 
b/python/sedona/sql/st_functions.py
index 07814947a..f65141f95 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -857,6 +857,17 @@ def ST_LineSubstring(line_string: ColumnOrName, 
start_fraction: ColumnOrNameOrNu
     """
     return _call_st_function("ST_LineSubstring", (line_string, start_fraction, 
end_fraction))
 
+@validate_argument_types
+def ST_HasZ(geom: ColumnOrName) -> Column:
+    """Check whether geometry has Z coordinate
+
+    :param geom: Geometry
+    :type geom: ColumnOrName
+    :return: True if geometry has Z coordinate, else False
+    :rtype: Column
+    """
+    return _call_st_function("ST_HasZ", geom)
+
 @validate_argument_types
 def ST_HasM(geom: ColumnOrName) -> Column:
     """Check whether geometry has M coordinate
diff --git a/python/tests/sql/test_dataframe_api.py 
b/python/tests/sql/test_dataframe_api.py
index 628aceefa..60cf13984 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -146,6 +146,7 @@ test_configurations = [
     (stf.ST_LineLocatePoint, ("line", "point"), "line_and_point", "", 0.5),
     (stf.ST_LineMerge, ("geom",), "multiline_geom", "", "LINESTRING (0 0, 1 0, 
1 1, 0 0)"),
     (stf.ST_LineSubstring, ("line", 0.5, 1.0), "linestring_geom", "", 
"LINESTRING (2.5 0, 3 0, 4 0, 5 0)"),
+    (stf.ST_HasZ, ("a",), "two_points", "", True),
     (stf.ST_HasM, ("point",), "4D_point", "", True),
     (stf.ST_M, ("point",), "4D_point", "", 4.0),
     (stf.ST_MMin, ("line",), "4D_line", "", -1.0),
@@ -319,6 +320,7 @@ wrong_type_configurations = [
     (stf.ST_LineSubstring, (None, 0.5, 1.0)),
     (stf.ST_LineSubstring, ("", None, 1.0)),
     (stf.ST_LineSubstring, ("", 0.5, None)),
+    (stf.ST_HasZ, (None,)),
     (stf.ST_HasM, (None,)),
     (stf.ST_M, (None,)),
     (stf.ST_MMin, (None,)),
diff --git a/python/tests/sql/test_function.py 
b/python/tests/sql/test_function.py
index 1df5e8508..0d6bde534 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -931,6 +931,11 @@ class TestPredicateJoin(TestBase):
         # Then
         assert subdivided.count() == 16
 
+    def test_st_has_z(self):
+        baseDf = self.spark.sql("SELECT ST_GeomFromWKT('POLYGON Z ((30 10 5, 
40 40 10, 20 40 15, 10 20 20, 30 10 5))') as poly")
+        actual = baseDf.selectExpr("ST_HasZ(poly)")
+        assert actual
+
     def test_st_has_m(self):
         baseDf = self.spark.sql("SELECT ST_GeomFromWKT('POLYGON ZM ((30 10 5 
1, 40 40 10 2, 20 40 15 3, 10 20 20 4, 30 10 5 1))') as poly")
         actual = baseDf.selectExpr("ST_HasM(poly)")
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 a83645497..c682c869f 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
@@ -192,10 +192,9 @@ public class TestFunctions extends TestBase {
         );
         registerUDF("ST_Buffer", byte[].class, double.class, boolean.class);
         registerUDF("ST_ReducePrecision", byte[].class, int.class);
-        registerUDF("ST_Area", byte[].class);
         verifySqlSingleRes(
-                "select 
sedona.ST_Area(sedona.ST_ReducePrecision(sedona.ST_GeomFromText(sedona.ST_AsText(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(0.05
 15, -0.05 15)'), 10000, true))), 8))",
-                0.04424254827358593
+                "select 
sedona.ST_AsText(sedona.ST_ReducePrecision(sedona.ST_Buffer(sedona.ST_GeomFromText('LINESTRING(5
 15, -5 15)'), 100, true), 4))",
+                "POLYGON ((-5.0002 14.9991, -5.0003 14.9992, -5.0005 14.9993, 
-5.0006 14.9994, -5.0007 14.9995, -5.0008 14.9997, -5.0009 14.9998, -5.0009 15, 
-5.0009 15.0002, -5.0008 15.0003, -5.0007 15.0005, -5.0006 15.0006, -5.0005 
15.0007, -5.0003 15.0008, -5.0002 15.0009, -5 15.0009, 5 15.0009, 5.0002 
15.0009, 5.0003 15.0008, 5.0005 15.0007, 5.0006 15.0006, 5.0007 15.0005, 5.0008 
15.0003, 5.0009 15.0002, 5.0009 15, 5.0009 14.9998, 5.0008 14.9997, 5.0007 
14.9995, 5.0006 14.9994, 5.000 [...]
         );
     }
 
@@ -409,6 +408,15 @@ public class TestFunctions extends TestBase {
         );
     }
 
+    @Test
+    public void test_ST_HasZ() {
+        registerUDF("ST_HasZ", byte[].class);
+        verifySqlSingleRes(
+                "SELECT sedona.ST_HasZ(sedona.ST_GeomFromText('POINT Z(1 2 
3)'))",
+                true
+        );
+    }
+
     @Test
     public void test_ST_HausdorffDistance() {
         registerUDF("ST_HausdorffDistance", byte[].class, byte[].class);
@@ -831,7 +839,7 @@ public class TestFunctions extends TestBase {
     public void test_ST_Snap() {
         registerUDF("ST_Snap", byte[].class, byte[].class, double.class);
         verifySqlSingleRes(
-                "SELECT 
sedona.ST_AsText(sedona.ST_Snap(sedona.ST_GeomFromText('POLYGON((2.6 12.5, 2.6 
20.0, 12.6 20.0, 12.6 12.5, 2.6 12.5 ))'), sedona.ST_GeomFromText('LINESTRING 
(0.5 10.7, 5.4 8.4, 10.1 10.0)'), 2.525))",
+                "SELECT 
sedona.ST_AsText(sedona.ST_Snap(sedona.ST_GeomFromWKT('POLYGON((2.6 12.5, 2.6 
20.0, 12.6 20.0, 12.6 12.5, 2.6 12.5 ))'), sedona.ST_GeomFromWKT('LINESTRING 
(0.5 10.7, 5.4 8.4, 10.1 10.0)'), 2.525))",
                 "POLYGON ((2.6 12.5, 2.6 20, 12.6 20, 12.6 12.5, 10.1 10, 2.6 
12.5))"
         );
     }
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 299cbcdc9..0be3705c1 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
@@ -400,6 +400,15 @@ public class TestFunctionsV2
         );
     }
 
+    @Test
+    public void test_ST_HasZ() {
+        registerUDFV2("ST_HasZ", String.class);
+        verifySqlSingleRes(
+                "SELECT sedona.ST_HasZ(ST_GeomFromText('POINT Z(1 2 3)'))",
+                true
+        );
+    }
+
     @Test
     public void test_ST_HausdorffDistance() {
         registerUDFV2("ST_HausdorffDistance", String.class, 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 14c00a64f..43789f4fd 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
@@ -533,6 +533,13 @@ public class UDFs {
         );
     }
 
+    @UDFAnnotations.ParamMeta(argNames = {"geometry"})
+    public static boolean ST_HasZ(byte[] geometry) {
+        return Functions.hasZ(
+                GeometrySerde.deserialize(geometry)
+        );
+    }
+
     @UDFAnnotations.ParamMeta(argNames = {"geom1", "geom2"})
     public static double ST_HausdorffDistance(byte[] geom1, byte[] geom2)
     {
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 08bad21ec..b5b485757 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
@@ -478,6 +478,13 @@ public class UDFsV2
         );
     }
 
+    @UDFAnnotations.ParamMeta(argNames = {"geometry"}, argTypes = {"Geometry"})
+    public static boolean ST_HasZ(String geometry) {
+        return Functions.hasZ(
+                GeometrySerde.deserGeoJson(geometry)
+        );
+    }
+
     @UDFAnnotations.ParamMeta(argNames = {"geom1", "geom2"}, argTypes = 
{"Geometry", "Geometry"})
     public static double ST_HausdorffDistance(String geom1, String geom2)
     {
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 3e7eacf0a..957504e13 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
@@ -112,6 +112,7 @@ object Catalog {
     function[ST_Snap](),
     function[ST_ClosestPoint](),
     function[ST_Boundary](),
+    function[ST_HasZ](),
     function[ST_HasM](),
     function[ST_M](),
     function[ST_MMin](),
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 21613b37c..b4cb02502 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
@@ -511,6 +511,14 @@ case class ST_MinimumBoundingCircle(inputExpressions: 
Seq[Expression])
   }
 }
 
+case class ST_HasZ(inputExpressions: Seq[Expression])
+  extends InferredExpression(Functions.hasZ _) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
+
 case class ST_HasM(inputExpressions: Seq[Expression])
   extends InferredExpression(Functions.hasM _) {
 
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 65cec5a29..b93aeb972 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
@@ -212,6 +212,9 @@ object st_functions extends DataFrameAPI {
   def ST_LineSubstring(lineString: Column, startFraction: Column, endFraction: 
Column): Column = wrapExpression[ST_LineSubstring](lineString, startFraction, 
endFraction)
   def ST_LineSubstring(lineString: String, startFraction: Double, endFraction: 
Double): Column = wrapExpression[ST_LineSubstring](lineString, startFraction, 
endFraction)
 
+  def ST_HasZ(geoms: Column): Column = wrapExpression[ST_HasZ](geoms)
+  def ST_HasZ(geoms: String): Column = wrapExpression[ST_HasZ](geoms)
+
   def ST_HasM(geoms: Column): Column = wrapExpression[ST_HasM](geoms)
   def ST_HasM(geoms: String): Column = wrapExpression[ST_HasM](geoms)
 
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 3bf263be4..acb1dd4d9 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
@@ -52,6 +52,12 @@ class dataFrameAPITestScala extends TestBaseScala {
       assert(actualResult == expectedResult)
     }
 
+    it("Passed ST_HasZ") {
+      val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON Z ((30 10 
5, 40 40 10, 20 40 15, 10 20 20, 30 10 5))') as poly")
+      val actual = baseDf.select(ST_HasZ("poly")).first().getBoolean(0)
+      assert(actual)
+    }
+
     it("Passed ST_HasM") {
       val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON ZM ((30 10 
5 1, 40 40 10 2, 20 40 15 3, 10 20 20 4, 30 10 5 1))') as poly")
       val actual = baseDf.select(ST_HasM("poly")).first().getBoolean(0)
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 98f4975ea..7da6fbc41 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
@@ -385,6 +385,12 @@ class functionTestScala extends TestBaseScala with 
Matchers with GeometrySample
       assert(!testtable.take(1)(0).get(1).asInstanceOf[Boolean])
     }
 
+    it("Passed ST_HasZ") {
+      val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON Z ((30 10 
5, 40 40 10, 20 40 15, 10 20 20, 30 10 5))') as poly")
+      val actual = baseDf.selectExpr("ST_HasZ(poly)").first().getBoolean(0)
+      assert(actual)
+    }
+
     it("Passed ST_HasM") {
       val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON ZM ((30 10 
5 1, 40 40 10 2, 20 40 15 3, 10 20 20 4, 30 10 5 1))') as poly")
       val actual = baseDf.selectExpr("ST_HasM(poly)").first().getBoolean(0)

Reply via email to