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"
