This is an automated email from the ASF dual-hosted git repository. jiayu pushed a commit to branch fix/2373-azimuth-identical-points in repository https://gitbox.apache.org/repos/asf/sedona.git
commit 32a26cdaff53fffa7dad3970e2316156121999a7 Author: Jia Yu <[email protected]> AuthorDate: Sat Feb 7 12:42:16 2026 -0800 Fix ST_Azimuth returning 0.0 instead of null for identical points When both input points are identical, ST_Azimuth now returns null instead of 0.0, matching PostGIS behavior. Changed return type from primitive double to Double in common Functions and Snowflake UDF wrappers. Added unit tests for azimuth: north (0.0), south (pi), and identical points (null). Updated docs for Spark SQL, Flink, and Snowflake to clarify null return behavior. Fixes #2373 --- .../java/org/apache/sedona/common/Functions.java | 5 ++++- .../org/apache/sedona/common/FunctionsTest.java | 26 ++++++++++++++++++++++ docs/api/flink/Function.md | 2 +- docs/api/snowflake/vector-data/Function.md | 2 +- docs/api/sql/Function.md | 2 +- .../org/apache/sedona/snowflake/snowsql/UDFs.java | 2 +- .../apache/sedona/snowflake/snowsql/UDFsV2.java | 2 +- 7 files changed, 35 insertions(+), 6 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 0be88a9b1b..6bb88563d7 100644 --- a/common/src/main/java/org/apache/sedona/common/Functions.java +++ b/common/src/main/java/org/apache/sedona/common/Functions.java @@ -201,11 +201,14 @@ public class Functions { return Math.sqrt(closest); } - public static double azimuth(Geometry left, Geometry right) { + public static Double azimuth(Geometry left, Geometry right) { Coordinate leftCoordinate = left.getCoordinate(); Coordinate rightCoordinate = right.getCoordinate(); double deltaX = rightCoordinate.x - leftCoordinate.x; double deltaY = rightCoordinate.y - leftCoordinate.y; + if (deltaX == 0 && deltaY == 0) { + return null; + } double azimuth = Math.atan2(deltaX, deltaY); return azimuth < 0 ? azimuth + (2 * Math.PI) : azimuth; } 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 e379e67d6d..5137f40d6a 100644 --- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java +++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java @@ -3580,6 +3580,32 @@ public class FunctionsTest extends TestBase { assertEquals(expected, actual); } + @Test + public void azimuthNorth() { + Point a = GEOMETRY_FACTORY.createPoint(new Coordinate(0, 0)); + Point b = GEOMETRY_FACTORY.createPoint(new Coordinate(0, 1)); + Double result = Functions.azimuth(a, b); + assertNotNull(result); + assertEquals(0.0, result, 1e-9); + } + + @Test + public void azimuthSouth() { + Point a = GEOMETRY_FACTORY.createPoint(new Coordinate(0, 25)); + Point b = GEOMETRY_FACTORY.createPoint(new Coordinate(0, 0)); + Double result = Functions.azimuth(a, b); + assertNotNull(result); + assertEquals(Math.PI, result, 1e-9); + } + + @Test + public void azimuthIdenticalPoints() { + Point a = GEOMETRY_FACTORY.createPoint(new Coordinate(0, 25)); + Point b = GEOMETRY_FACTORY.createPoint(new Coordinate(0, 25)); + Double result = Functions.azimuth(a, b); + assertNull(result); + } + @Test public void angleFourPoints() { Point start1 = GEOMETRY_FACTORY.createPoint(new Coordinate(0, 0)); diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md index d74552617f..1c13ecf1e2 100644 --- a/docs/api/flink/Function.md +++ b/docs/api/flink/Function.md @@ -617,7 +617,7 @@ POINT ZM(1 1 1 1) ## ST_Azimuth -Introduction: Returns Azimuth for two given points in radians null otherwise. +Introduction: Returns Azimuth for two given points in radians. Returns null if the two points are identical. Format: `ST_Azimuth(pointA: Point, pointB: Point)` diff --git a/docs/api/snowflake/vector-data/Function.md b/docs/api/snowflake/vector-data/Function.md index 28eb433c3b..2ca1f88341 100644 --- a/docs/api/snowflake/vector-data/Function.md +++ b/docs/api/snowflake/vector-data/Function.md @@ -457,7 +457,7 @@ FROM polygondf ## ST_Azimuth -Introduction: Returns Azimuth for two given points in radians null otherwise. +Introduction: Returns Azimuth for two given points in radians. Returns null if the two points are identical. Format: `ST_Azimuth(pointA: Point, pointB: Point)` diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md index 9c61520f6d..9f6d18f93f 100644 --- a/docs/api/sql/Function.md +++ b/docs/api/sql/Function.md @@ -682,7 +682,7 @@ POINT ZM(1 1 1 1) ## ST_Azimuth -Introduction: Returns Azimuth for two given points in radians null otherwise. +Introduction: Returns Azimuth for two given points in radians. Returns null if the two points are identical. Format: `ST_Azimuth(pointA: Point, pointB: Point)` 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 3e2d0095a6..99682eff30 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 @@ -171,7 +171,7 @@ public class UDFs { } @UDFAnnotations.ParamMeta(argNames = {"left", "right"}) - public static double ST_Azimuth(byte[] left, byte[] right) { + public static Double ST_Azimuth(byte[] left, byte[] right) { return Functions.azimuth(GeometrySerde.deserialize(left), GeometrySerde.deserialize(right)); } 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 5d545f4b6c..d68079e818 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 @@ -228,7 +228,7 @@ public class UDFsV2 { @UDFAnnotations.ParamMeta( argNames = {"left", "right"}, argTypes = {"Geometry", "Geometry"}) - public static double ST_Azimuth(String left, String right) { + public static Double ST_Azimuth(String left, String right) { return Functions.azimuth(GeometrySerde.deserGeoJson(left), GeometrySerde.deserGeoJson(right)); }
