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

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

commit da67eb36186552f57a551fbab6bc67fe92c845f7
Author: Furqaan Khan <[email protected]>
AuthorDate: Mon Apr 8 19:27:44 2024 -0400

    [TASK-61] Add ST_AsHEXEWKB (#158)
    
    * feat: add ST_AsHEXEWKB
    
    * fix: snowflake tests
---
 .../java/org/apache/sedona/common/Functions.java   | 14 +++++++++
 .../org/apache/sedona/common/utils/GeomUtils.java  |  5 ++++
 .../org/apache/sedona/common/FunctionsTest.java    | 33 ++++++++++++++++++++++
 docs/api/flink/Function.md                         | 32 +++++++++++++++++++++
 docs/api/snowflake/vector-data/Function.md         | 30 ++++++++++++++++++++
 docs/api/sql/Function.md                           | 32 +++++++++++++++++++++
 .../main/java/org/apache/sedona/flink/Catalog.java |  1 +
 .../apache/sedona/flink/expressions/Functions.java | 15 ++++++++++
 .../java/org/apache/sedona/flink/FunctionTest.java | 10 +++++++
 python/sedona/sql/st_functions.py                  | 13 +++++++++
 python/tests/sql/test_dataframe_api.py             |  2 ++
 python/tests/sql/test_function.py                  | 11 ++++++++
 .../sedona/snowflake/snowsql/TestFunctions.java    | 16 +++++++++++
 .../sedona/snowflake/snowsql/TestFunctionsV2.java  | 17 +++++++++++
 .../org/apache/sedona/snowflake/snowsql/UDFs.java  | 14 +++++++++
 .../apache/sedona/snowflake/snowsql/UDFsV2.java    | 14 +++++++++
 .../scala/org/apache/sedona/sql/UDF/Catalog.scala  |  1 +
 .../sql/sedona_sql/expressions/Functions.scala     |  8 ++++++
 .../sql/sedona_sql/expressions/st_functions.scala  |  6 ++++
 .../apache/sedona/sql/dataFrameAPITestScala.scala  | 12 ++++++++
 .../org/apache/sedona/sql/functionTestScala.scala  | 11 ++++++++
 21 files changed, 297 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 774a2bfe3..5c0d164b0 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -32,6 +32,7 @@ import org.locationtech.jts.algorithm.hull.ConcaveHull;
 import org.locationtech.jts.geom.*;
 import org.locationtech.jts.geom.impl.CoordinateArraySequence;
 import org.locationtech.jts.geom.util.GeometryFixer;
+import org.locationtech.jts.io.ByteOrderValues;
 import org.locationtech.jts.io.gml2.GMLWriter;
 import org.locationtech.jts.io.kml.KMLWriter;
 import org.locationtech.jts.linearref.LengthIndexedLine;
@@ -548,6 +549,19 @@ public class Functions {
         return GeomUtils.getEWKB(geometry);
     }
 
+    public static String asHexEWKB(Geometry geom, String endian) {
+        if (endian.equalsIgnoreCase("NDR")) {
+            return GeomUtils.getHexEWKB(geom, ByteOrderValues.LITTLE_ENDIAN);
+        } else if (endian.equalsIgnoreCase("XDR")) {
+            return GeomUtils.getHexEWKB(geom, ByteOrderValues.BIG_ENDIAN);
+        }
+        throw new IllegalArgumentException("You must select either NDR 
(little-endian) or XDR (big-endian) as the endian format.");
+    }
+
+    public static String asHexEWKB(Geometry geom) {
+        return asHexEWKB(geom, "NDR");
+    }
+
     public static byte[] asWKB(Geometry geometry) {
         return GeomUtils.getWKB(geometry);
     }
diff --git a/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java 
b/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
index 9a4d57e91..fc36e79e5 100644
--- a/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
+++ b/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
@@ -177,6 +177,11 @@ public class GeomUtils {
         return new WKTWriter(4).write(geometry);
     }
 
+    public static String getHexEWKB(Geometry geometry, int endian) {
+        WKBWriter writer = new WKBWriter(GeomUtils.getDimension(geometry), 
endian, geometry.getSRID() != 0);
+        return WKBWriter.toHex(writer.write(geometry));
+    }
+
     public static byte[] getEWKB(Geometry geometry) {
         if (geometry == null) {
             return null;
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 4fe7d1578..5acf28a32 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -133,6 +133,39 @@ public class FunctionsTest extends TestBase {
         assertEquals(expected, geometry);
     }
 
+    @Test
+    public void asHexEWKB() throws ParseException {
+        String[] geoms = {
+                "POINT(1 2)",
+                "LINESTRING (30 20, 20 25, 20 15, 30 20)",
+                "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (5 5, 5 8, 8 8, 8 5, 
5 5))"
+
+        };
+        String[] expected = {
+                //NDR - little endian
+                "0101000000000000000000F03F0000000000000040",
+                
"0102000000040000000000000000003E4000000000000034400000000000003440000000000000394000000000000034400000000000002E400000000000003E400000000000003440",
+                
"010300000002000000050000000000000000000000000000000000000000000000000024400000000000000000000000000000244000000000000024400000000000000000000000000000244000000000000000000000000000000000050000000000000000001440000000000000144000000000000014400000000000002040000000000000204000000000000020400000000000002040000000000000144000000000000014400000000000001440",
+
+                // XDR - big endian
+                "00000000013FF00000000000004000000000000000",
+                
"000000000200000004403E0000000000004034000000000000403400000000000040390000000000004034000000000000402E000000000000403E0000000000004034000000000000",
+                
"000000000300000002000000050000000000000000000000000000000040240000000000000000000000000000402400000000000040240000000000000000000000000000402400000000000000000000000000000000000000000000000000054014000000000000401400000000000040140000000000004020000000000000402000000000000040200000000000004020000000000000401400000000000040140000000000004014000000000000"
+        };
+        testAsHexEWKB(geoms, expected);
+    }
+
+    public void testAsHexEWKB(String[] geoms, String[] expected) throws 
ParseException {
+        String[] endians = {"NDR", "XDR"};
+        int offset = 0;
+        for (String e: endians) {
+            for (int i = 0; i < geoms.length; i++) {
+                assertEquals(expected[i + offset], 
Functions.asHexEWKB(Constructors.geomFromWKT(geoms[i], 0), e));
+            }
+            offset = 3;
+        }
+    }
+
     @Test
     public void pointFromWKB() throws Exception{
         Geometry geometry = GEOMETRY_FACTORY.createPoint(new Coordinate(1.0, 
2.0));
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 28726f8b6..1eaa86ec1 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -397,6 +397,38 @@ Output:
 1.0,1.0 8.0,1.0 8.0,8.0 1.0,8.0 1.0,1.0
 ```
 
+## ST_AsHEXEWKB
+
+Introduction: This function returns the input geometry encoded to a text 
representation in HEXEWKB format. The HEXEWKB encoding can use either 
little-endian (NDR) or big-endian (XDR) byte ordering. If no encoding is 
explicitly specified, the function defaults to using the little-endian (NDR) 
format.
+
+Format: `ST_AsHEXEWKB(geom: Geometry, endian: String = NDR)`
+
+Since: `vTBD`
+
+SQL Example
+
+```sql
+SELECT ST_AsHEXEWKB(ST_GeomFromWKT('POINT(1 2)'), 'XDR')
+```
+
+Output:
+
+```
+00000000013FF00000000000004000000000000000
+```
+
+SQL Example
+
+```sql
+SELECT ST_AsHEXEWKB(ST_GeomFromWKT('LINESTRING (30 20, 20 25, 20 15, 30 20)'))
+```
+
+Output:
+
+```
+0102000000040000000000000000003E4000000000000034400000000000003440000000000000394000000000000034400000000000002E400000000000003E400000000000003440
+```
+
 ## ST_AsKML
 
 Introduction: Return the [KML](https://www.ogc.org/standards/kml) string 
representation of a geometry
diff --git a/docs/api/snowflake/vector-data/Function.md 
b/docs/api/snowflake/vector-data/Function.md
index 54afc40d6..3a5b0de54 100644
--- a/docs/api/snowflake/vector-data/Function.md
+++ b/docs/api/snowflake/vector-data/Function.md
@@ -292,6 +292,36 @@ SELECT ST_AsGML(polygondf.countyshape)
 FROM polygondf
 ```
 
+## ST_AsHEXEWKB
+
+Introduction: This function returns the input geometry encoded to a text 
representation in HEXEWKB format. The HEXEWKB encoding can use either 
little-endian (NDR) or big-endian (XDR) byte ordering. If no encoding is 
explicitly specified, the function defaults to using the little-endian (NDR) 
format.
+
+Format: `ST_AsHEXEWKB(geom: Geometry, endian: String = NDR)`
+
+SQL Example
+
+```sql
+SELECT ST_AsHEXEWKB(ST_GeomFromWKT('POINT(1 2)'), 'XDR')
+```
+
+Output:
+
+```
+00000000013FF00000000000004000000000000000
+```
+
+SQL Example
+
+```sql
+SELECT ST_AsHEXEWKB(ST_GeomFromWKT('LINESTRING (30 20, 20 25, 20 15, 30 20)'))
+```
+
+Output:
+
+```
+0102000000040000000000000000003E4000000000000034400000000000003440000000000000394000000000000034400000000000002E400000000000003E400000000000003440
+```
+
 ## ST_AsKML
 
 Introduction: Return the [KML](https://www.ogc.org/standards/kml) string 
representation of a geometry
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index a09573fa3..8c9ea26ac 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -394,6 +394,38 @@ Output:
 1.0,1.0 8.0,1.0 8.0,8.0 1.0,8.0 1.0,1.0
 ```
 
+## ST_AsHEXEWKB
+
+Introduction: This function returns the input geometry encoded to a text 
representation in HEXEWKB format. The HEXEWKB encoding can use either 
little-endian (NDR) or big-endian (XDR) byte ordering. If no encoding is 
explicitly specified, the function defaults to using the little-endian (NDR) 
format.
+
+Format: `ST_AsHEXEWKB(geom: Geometry, endian: String = NDR)`
+
+Since: `vTBD`
+
+SQL Example
+
+```sql
+SELECT ST_AsHEXEWKB(ST_GeomFromWKT('POINT(1 2)'), 'XDR')
+```
+
+Output:
+
+```
+00000000013FF00000000000004000000000000000
+```
+
+SQL Example
+
+```sql
+SELECT ST_AsHEXEWKB(ST_GeomFromWKT('LINESTRING (30 20, 20 25, 20 15, 30 20)'))
+```
+
+Output:
+
+```
+0102000000040000000000000000003E4000000000000034400000000000003440000000000000394000000000000034400000000000002E400000000000003E400000000000003440
+```
+
 ## ST_AsKML
 
 Introduction: Return the [KML](https://www.ogc.org/standards/kml) string 
representation of a geometry
diff --git a/flink/src/main/java/org/apache/sedona/flink/Catalog.java 
b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
index 234e58b59..104693d11 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -96,6 +96,7 @@ public class Catalog {
                 new Functions.ST_ExteriorRing(),
                 new Functions.ST_AsEWKT(),
                 new Functions.ST_AsEWKB(),
+                new Functions.ST_AsHEXEWKB(),
                 new Functions.ST_AsText(),
                 new Functions.ST_AsBinary(),
                 new Functions.ST_AsGeoJSON(),
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..02756d086 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
@@ -515,6 +515,21 @@ public class Functions {
         }
     }
 
+    public static class ST_AsHEXEWKB extends ScalarFunction {
+        @DataTypeHint("String")
+        public String eval(@DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class) Object o,
+                           @DataTypeHint("String") String endian) {
+            Geometry geom = (Geometry) o;
+            return org.apache.sedona.common.Functions.asHexEWKB(geom, endian);
+        }
+
+        @DataTypeHint("String")
+        public String eval(@DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class) Object o) {
+            Geometry geom = (Geometry) o;
+            return org.apache.sedona.common.Functions.asHexEWKB(geom);
+        }
+    }
+
     public static class ST_AsBinary extends ScalarFunction {
         @DataTypeHint("Bytes")
         public byte[] 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..cd0909b3c 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -612,6 +612,16 @@ public class FunctionTest extends TestBase{
         
assertEquals("01030000000100000005000000000000000000e0bf000000000000e0bf000000000000e0bf000000000000e03f000000000000e03f000000000000e03f000000000000e03f000000000000e0bf000000000000e0bf000000000000e0bf",
 result);
     }
 
+    @Test
+    public void testAsHEXEWKB() {
+        Table pointTable = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (1 
2)') AS point");
+        String result = (String) 
first(pointTable.select(call(Functions.ST_AsHEXEWKB.class.getSimpleName(), 
$("point"), "XDR"))).getField(0);
+        assertEquals("00000000013FF00000000000004000000000000000", result);
+
+        result = (String) 
first(pointTable.select(call(Functions.ST_AsHEXEWKB.class.getSimpleName(), 
$("point")))).getField(0);
+        assertEquals("0101000000000000000000F03F0000000000000040", result);
+    }
+
     @Test
     public void testAsBinary() {
         Table polygonTable = createPolygonTable(testDataSize);
diff --git a/python/sedona/sql/st_functions.py 
b/python/sedona/sql/st_functions.py
index 07814947a..e3b887997 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -123,6 +123,19 @@ def ST_AsEWKB(geometry: ColumnOrName) -> Column:
     """
     return _call_st_function("ST_AsEWKB", geometry)
 
+@validate_argument_types
+def ST_AsHEXEWKB(geometry: ColumnOrName, endian: Optional[ColumnOrName] = 
None) -> Column:
+    """Generate the Extended Well-Known Binary representation of a geometry as 
Hex string.
+
+    :param geometry: Geometry to generate EWKB for.
+    :type geometry: ColumnOrName
+    :return: Extended Well-Known Binary representation of geometry as Hex 
string.
+    :rtype: Column
+    """
+    args = (geometry) if endian is None else (geometry, endian)
+
+    return _call_st_function("ST_AsHEXEWKB", args)
+
 
 @validate_argument_types
 def ST_AsEWKT(geometry: ColumnOrName) -> Column:
diff --git a/python/tests/sql/test_dataframe_api.py 
b/python/tests/sql/test_dataframe_api.py
index 420a26ed1..dfe7e36cb 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -84,6 +84,7 @@ test_configurations = [
     (stf.ST_AreaSpheroid, ("point",), "point_geom", "", 0.0),
     (stf.ST_AsBinary, ("point",), "point_geom", "", 
"01010000000000000000000000000000000000f03f"),
     (stf.ST_AsEWKB, (lambda: f.expr("ST_SetSRID(point, 3021)"),), 
"point_geom", "", "0101000020cd0b00000000000000000000000000000000f03f"),
+    (stf.ST_AsHEXEWKB, ("point",), "point_geom", "", 
"01010000000000000000000000000000000000F03F"),
     (stf.ST_AsEWKT, (lambda: f.expr("ST_SetSRID(point, 4326)"),), 
"point_geom", "", "SRID=4326;POINT (0 1)"),
     (stf.ST_AsGeoJSON, ("point",), "point_geom", "", 
"{\"type\":\"Point\",\"coordinates\":[0.0,1.0]}"),
     (stf.ST_AsGML, ("point",), "point_geom", "", "<gml:Point>\n  
<gml:coordinates>\n    0.0,1.0 \n  </gml:coordinates>\n</gml:Point>\n"),
@@ -263,6 +264,7 @@ wrong_type_configurations = [
     (stf.ST_Area, (None,)),
     (stf.ST_AsBinary, (None,)),
     (stf.ST_AsEWKB, (None,)),
+    (stf.ST_AsHEXEWKB, (None,)),
     (stf.ST_AsEWKT, (None,)),
     (stf.ST_AsGeoJSON, (None,)),
     (stf.ST_AsGML, (None,)),
diff --git a/python/tests/sql/test_function.py 
b/python/tests/sql/test_function.py
index 1df5e8508..fc78afcd0 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -1467,6 +1467,17 @@ class TestPredicateJoin(TestBase):
                                    "1, 2, 1, 2) AS geom")
         actual = actual_df.selectExpr("ST_AsText(geom)").take(1)[0][0]
         assert expected == actual
+
+    def test_st_ashexewkb(self):
+        expected = "0101000000000000000000F03F0000000000000040"
+        actual_df = self.spark.sql("SELECT ST_GeomFromText('POINT(1 2)') as 
point")
+        actual = actual_df.selectExpr("ST_AsHEXEWKB(point)").take(1)[0][0]
+        assert expected == actual
+
+        expected = "00000000013FF00000000000004000000000000000"
+        actual = actual_df.selectExpr("ST_AsHEXEWKB(point, 
'XDR')").take(1)[0][0]
+        assert expected == actual
+
     def test_boundingDiagonal(self):
         expected = "LINESTRING (1 0, 2 1)"
         actual_df = self.spark.sql("SELECT 
ST_BoundingDiagonal(ST_GeomFromText('POLYGON ((1 0, 1 1, 2 1, 2 0, "
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..39e412926 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
@@ -111,6 +111,22 @@ public class TestFunctions extends TestBase {
                 new byte[] {1, 1, 0, 0, 32, -51, 11, 0, 0, 0, 0, 0, 0, 0, 0, 
-16, 63, 0, 0, 0, 0, 0, 0, -16, 63}
         );
     }
+
+    @Test
+    public void test_ST_AsHEXEWKB() throws SQLException {
+        registerUDF("ST_AsHEXEWKB", byte[].class);
+        verifySqlSingleRes(
+                "select sedona.ST_AsHEXEWKB(sedona.ST_GeomFromText('POINT(1 
2)'))",
+                "0101000000000000000000F03F0000000000000040"
+        );
+
+        registerUDF("ST_AsHEXEWKB", byte[].class, String.class);
+        verifySqlSingleRes(
+                "select sedona.ST_AsHEXEWKB(sedona.ST_GeomFromText('POINT(1 
2)'), 'XDR')",
+                "00000000013FF00000000000004000000000000000"
+        );
+    }
+
     @Test
     public void test_ST_AsEWKT() {
         registerUDF("ST_AsEWKT", 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 299cbcdc9..db35c7184 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
@@ -114,6 +114,23 @@ public class TestFunctionsV2
                 new byte[] {1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -16, 63, 0, 0, 0, 
0, 0, 0, -16, 63}
         );
     }
+
+    @Test
+    public void test_ST_AsHEXEWKB() throws SQLException
+    {
+        registerUDFV2("ST_AsHEXEWKB", String.class);
+        verifySqlSingleRes(
+                "select sedona.ST_AsHEXEWKB(ST_GeometryFromWKT('POINT(1 2)'))",
+                "0101000000000000000000F03F0000000000000040"
+        );
+
+        registerUDFV2("ST_AsHEXEWKB", String.class, String.class);
+        verifySqlSingleRes(
+                "select sedona.ST_AsHEXEWKB(ST_GeometryFromWKT('POINT(1 2)'), 
'XDR')",
+                "00000000013FF00000000000004000000000000000"
+        );
+    }
+
     @Test
     public void test_ST_AsEWKT() {
         registerUDFV2("ST_AsEWKT", 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 a4ed232b6..9ab87089c 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
@@ -104,6 +104,20 @@ public class UDFs {
         );
     }
 
+    @UDFAnnotations.ParamMeta(argNames = {"geometry"})
+    public static String ST_AsHEXEWKB(byte[] geometry) {
+        return Functions.asHexEWKB(
+                GeometrySerde.deserialize(geometry)
+        );
+    }
+
+    @UDFAnnotations.ParamMeta(argNames = {"geometry", "endian"})
+    public static String ST_AsHEXEWKB(byte[] geometry, String endian) {
+        return Functions.asHexEWKB(
+                GeometrySerde.deserialize(geometry), endian
+        );
+    }
+
     @UDFAnnotations.ParamMeta(argNames = {"geometry"})
     public static String ST_AsEWKT(byte[] geometry) {
         return Functions.asEWKT(
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..767b04ace 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
@@ -110,6 +110,20 @@ public class UDFsV2
         );
     }
 
+    @UDFAnnotations.ParamMeta(argNames = {"geometry"}, argTypes = {"Geometry"})
+    public static String ST_AsHEXEWKB(String geometry) {
+        return Functions.asHexEWKB(
+                GeometrySerde.deserGeoJson(geometry)
+        );
+    }
+
+    @UDFAnnotations.ParamMeta(argNames = {"geometry", "endian"}, argTypes = 
{"Geometry", "String"})
+    public static String ST_AsHEXEWKB(String geometry, String endian) {
+        return Functions.asHexEWKB(
+                GeometrySerde.deserGeoJson(geometry), endian
+        );
+    }
+
     @UDFAnnotations.ParamMeta(argNames = {"geometry"},
             argTypes = {"Geometry"})
     public static String ST_AsEWKT(String 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 a411f67b7..0870f1f66 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
@@ -99,6 +99,7 @@ object Catalog {
     function[ST_AsGeoJSON](),
     function[ST_AsBinary](),
     function[ST_AsEWKB](),
+    function[ST_AsHEXEWKB](),
     function[ST_AsGML](),
     function[ST_AsKML](),
     function[ST_SRID](),
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..235121d00 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
@@ -365,6 +365,14 @@ case class ST_AsEWKB(inputExpressions: Seq[Expression])
   }
 }
 
+case class ST_AsHEXEWKB(inputExpressions: Seq[Expression])
+  extends InferredExpression(inferrableFunction2(Functions.asHexEWKB), 
inferrableFunction1(Functions.asHexEWKB)) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
+
 case class ST_SRID(inputExpressions: Seq[Expression])
   extends InferredExpression(Functions.getSRID _) {
 
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..c95b64546 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
@@ -43,6 +43,12 @@ object st_functions extends DataFrameAPI {
   def ST_AsEWKB(geometry: Column): Column = wrapExpression[ST_AsEWKB](geometry)
   def ST_AsEWKB(geometry: String): Column = wrapExpression[ST_AsEWKB](geometry)
 
+
+  def ST_AsHEXEWKB(geometry: Column, endian: Column): Column = 
wrapExpression[ST_AsHEXEWKB](geometry, endian)
+  def ST_AsHEXEWKB(geometry: String, endian: String): Column = 
wrapExpression[ST_AsHEXEWKB](geometry, endian)
+  def ST_AsHEXEWKB(geometry: Column): Column = 
wrapExpression[ST_AsHEXEWKB](geometry)
+  def ST_AsHEXEWKB(geometry: String): Column = 
wrapExpression[ST_AsHEXEWKB](geometry)
+
   def ST_AsEWKT(geometry: Column): Column = wrapExpression[ST_AsEWKT](geometry)
   def ST_AsEWKT(geometry: String): Column = wrapExpression[ST_AsEWKT](geometry)
 
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 70e7201dd..cac25fab3 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
@@ -620,6 +620,18 @@ class dataFrameAPITestScala extends TestBaseScala {
       assert(actualResult == expectedResult)
     }
 
+
+    it("Passed ST_AsHEXEWKB") {
+      val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('POINT(1 2)') as 
point")
+      var actual = baseDf.select(ST_AsHEXEWKB("point")).first().get(0)
+      var expected = "0101000000000000000000F03F0000000000000040"
+      assert(expected.equals(actual))
+
+      actual = baseDf.select(ST_AsHEXEWKB(col("point"), 
lit("xdr"))).first().get(0)
+      expected = "00000000013FF00000000000004000000000000000"
+      assert(expected.equals(actual))
+    }
+
     it("Passed ST_NPoints") {
       val lineDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING (0 0, 1 
1)') AS geom")
       val df = lineDf.select(ST_NPoints("geom"))
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..3441dd647 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
@@ -611,6 +611,17 @@ class functionTestScala extends TestBaseScala with 
Matchers with GeometrySample
       assert(Hex.encodeHexString(df.first().get(0).asInstanceOf[Array[Byte]]) 
== s)
     }
 
+    it("Passed ST_AsHEXEWKB") {
+      val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('POINT(1 2)') as 
point")
+      var actual = baseDf.selectExpr("ST_AsHEXEWKB(point)").first().get(0)
+      var expected = "0101000000000000000000F03F0000000000000040"
+      assert(expected.equals(actual))
+
+      actual = baseDf.selectExpr("ST_AsHEXEWKB(point, 'XDR')").first().get(0)
+      expected = "00000000013FF00000000000004000000000000000"
+      assert(expected.equals(actual))
+    }
+
     it("Passed ST_AsEWKB empty geometry") {
       val df = sparkSession.sql("SELECT 
ST_AsEWKB(ST_SetSrid(ST_GeomFromWKT('POINT EMPTY'), 3021))")
       val s = "0101000020cd0b0000000000000000f87f000000000000f87f"

Reply via email to