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

prantogg pushed a commit to branch orient-ccw
in repository https://gitbox.apache.org/repos/asf/sedona-spatialbench.git

commit 4cbcee5123deaf719128ea907346b229421dc043
Author: Pranav Toggi <[email protected]>
AuthorDate: Sat Nov 8 10:13:55 2025 -0800

    add logic to handle antimeridian crossing geometries
---
 spatialbench/src/spatial/geometry.rs           |  19 +-
 spatialbench/src/spatial/utils/affine.rs       |  15 --
 spatialbench/src/spatial/utils/antimeridian.rs | 258 +++++++++++++++++++++++++
 spatialbench/src/spatial/utils/mod.rs          |   2 +
 4 files changed, 277 insertions(+), 17 deletions(-)

diff --git a/spatialbench/src/spatial/geometry.rs 
b/spatialbench/src/spatial/geometry.rs
index a0f9aa5..b753d57 100644
--- a/spatialbench/src/spatial/geometry.rs
+++ b/spatialbench/src/spatial/geometry.rs
@@ -1,4 +1,7 @@
-use crate::spatial::utils::{apply_affine, round_coordinates, 
wrap_around_longitude};
+use crate::spatial::utils::{
+    apply_affine, clamp_polygon_to_dateline, crosses_dateline, 
round_coordinates,
+    wrap_around_longitude,
+};
 use crate::spatial::{GeomType, SpatialConfig};
 use geo::orient::Direction;
 use geo::{coord, Geometry, LineString, Orient, Point, Polygon};
@@ -6,7 +9,7 @@ use rand::rngs::StdRng;
 use rand::Rng;
 use std::f64::consts::PI;
 
-const GEOMETRY_PRECISION: f64 = 1_000_000_000.0;
+pub const GEOMETRY_PRECISION: f64 = 1_000_000_000.0;
 
 pub fn emit_geom(
     center01: (f64, f64),
@@ -54,6 +57,12 @@ pub fn generate_box_geom(
         .collect();
 
     let mut polygon = Polygon::new(LineString::from(coords), vec![]);
+
+    // Handle polygons crossing the dateline
+    if crosses_dateline(&polygon) {
+        polygon = clamp_polygon_to_dateline(&polygon);
+    }
+
     polygon = polygon.orient(Direction::Default);
     Geometry::Polygon(polygon)
 }
@@ -94,6 +103,12 @@ pub fn generate_polygon_geom(
     }
 
     let mut polygon = Polygon::new(LineString::from(ring), vec![]);
+
+    // Handle polygons crossing the dateline
+    if crosses_dateline(&polygon) {
+        polygon = clamp_polygon_to_dateline(&polygon);
+    }
+
     polygon = polygon.orient(Direction::Default);
     Geometry::Polygon(polygon)
 }
diff --git a/spatialbench/src/spatial/utils/affine.rs 
b/spatialbench/src/spatial/utils/affine.rs
index 3e04e4a..5100cad 100644
--- a/spatialbench/src/spatial/utils/affine.rs
+++ b/spatialbench/src/spatial/utils/affine.rs
@@ -15,18 +15,3 @@ pub fn round_coordinates(x: f64, y: f64, precision: f64) -> 
(f64, f64) {
         round_coordinate(y, precision),
     )
 }
-
-/// Wraps a longitude value to ensure it stays within the valid range of 
[-180, 180] degrees.
-///
-/// Longitude is a circular coordinate:
-/// - If longitude exceeds 180°, it wraps around from the eastern hemisphere 
back to the western hemisphere.
-/// - If longitude is below -180°, it wraps around from the western hemisphere 
back to the eastern hemisphere.
-pub fn wrap_around_longitude(mut lon: f64) -> f64 {
-    while lon > 180.0 {
-        lon -= 360.0;
-    }
-    while lon < -180.0 {
-        lon += 360.0;
-    }
-    lon
-}
diff --git a/spatialbench/src/spatial/utils/antimeridian.rs 
b/spatialbench/src/spatial/utils/antimeridian.rs
new file mode 100644
index 0000000..b56db3e
--- /dev/null
+++ b/spatialbench/src/spatial/utils/antimeridian.rs
@@ -0,0 +1,258 @@
+use geo::{Centroid, LineString, Polygon};
+
+/// Wraps a longitude value to ensure it stays within the valid range of 
[-180, 180] degrees.
+///
+/// Longitude is a circular coordinate:
+/// - If longitude exceeds 180°, it wraps around from the eastern hemisphere 
back to the western hemisphere.
+/// - If longitude is below -180°, it wraps around from the western hemisphere 
back to the eastern hemisphere.
+pub fn wrap_around_longitude(mut lon: f64) -> f64 {
+    while lon > 180.0 {
+        lon -= 360.0;
+    }
+    while lon < -180.0 {
+        lon += 360.0;
+    }
+    lon
+}
+
+/// Checks if a polygon crosses the dateline (antimeridian at ±180°)
+pub fn crosses_dateline(polygon: &Polygon) -> bool {
+    let coords = polygon.exterior().coords();
+    let mut has_east = false;
+    let mut has_west = false;
+
+    for coord in coords {
+        if (coord.x > 90.0 && coord.x <= 180.0) || coord.x < -180.0 {
+            has_east = true;
+        }
+        if coord.x > 180.0 || (coord.x >= -180.0 && coord.x < -90.0) {
+            has_west = true;
+        }
+        if has_east && has_west {
+            return true;
+        }
+    }
+    false
+}
+
+/// Clamps a polygon's longitude coordinates to one side of the antimeridian 
(±180°).
+///
+/// When a polygon crosses the dateline, this function constrains its 
coordinates to remain
+/// on either the eastern (0° to 180°) or western (-180° to 0°) hemisphere 
based on where
+/// the polygon's centroid is located.
+///
+/// # Behavior
+/// - If the centroid is in the eastern hemisphere (≥ 0°), coordinates are 
clamped to [0°, 180°]
+///   or kept at their original values if already within [-180°, 0°]
+/// - If the centroid is in the western hemisphere (< 0°), coordinates are 
clamped to [-180°, 0°]
+///   or kept at their original values if already within [0°, 180°]
+/// - Latitude values (y-coordinates) remain unchanged
+///
+pub fn clamp_polygon_to_dateline(polygon: &Polygon) -> Polygon {
+    let centroid = polygon.centroid().expect("Polygon should have centroid");
+    let east_bound = centroid.x() >= 0.0;
+    let keep_east = (centroid.x() >= 0.0 && centroid.x() <= 180.0) || 
(centroid.x() < -180.0);
+
+    let exterior_coords: Vec<_> = polygon
+        .exterior()
+        .coords()
+        .map(|coord| {
+            let clamped_x = if keep_east {
+                if east_bound {
+                    coord.x.clamp(0.0, 180.0)
+                } else {
+                    coord.x.max(-180.0)
+                }
+            } else if east_bound {
+                coord.x.min(180.0)
+            } else {
+                coord.x.clamp(-180.0, 0.0)
+            };
+            geo::Coord {
+                x: clamped_x,
+                y: coord.y,
+            }
+        })
+        .collect();
+
+    if exterior_coords.len() >= 4 {
+        Polygon::new(LineString::from(exterior_coords), vec![])
+    } else {
+        polygon.clone()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use geo::polygon;
+
+    #[test]
+    fn test_wrap_around_longitude_positive_overflow() {
+        assert_eq!(wrap_around_longitude(190.0), -170.0);
+        assert_eq!(wrap_around_longitude(240.0), -120.0);
+    }
+
+    #[test]
+    fn test_wrap_around_longitude_negative_overflow() {
+        assert_eq!(wrap_around_longitude(-190.0), 170.0);
+        assert_eq!(wrap_around_longitude(-240.0), 120.0);
+    }
+
+    #[test]
+    fn test_wrap_around_longitude_within_range() {
+        assert_eq!(wrap_around_longitude(0.0), 0.0);
+        assert_eq!(wrap_around_longitude(90.0), 90.0);
+        assert_eq!(wrap_around_longitude(-90.0), -90.0);
+        assert_eq!(wrap_around_longitude(180.0), 180.0);
+        assert_eq!(wrap_around_longitude(-180.0), -180.0);
+    }
+
+    #[test]
+    fn test_crosses_dateline_no_crossing() {
+        let poly = polygon![
+            (x: 10.0, y: 10.0),
+            (x: 20.0, y: 10.0),
+            (x: 20.0, y: 20.0),
+            (x: 10.0, y: 20.0),
+            (x: 10.0, y: 10.0),
+        ];
+        assert!(!crosses_dateline(&poly));
+    }
+
+    #[test]
+    fn test_crosses_dateline_crossing() {
+        let mut poly = polygon![
+            (x: 170.0, y: 10.0),
+            (x: -170.0, y: 10.0),
+            (x: -170.0, y: 20.0),
+            (x: 170.0, y: 20.0),
+            (x: 170.0, y: 10.0),
+        ];
+        assert!(crosses_dateline(&poly));
+
+        poly = polygon![
+            (x: -160.0, y: 10.0),
+            (x: -170.0, y: 10.0),
+            (x: -170.0, y: 20.0),
+            (x: -160.0, y: 20.0),
+            (x: -160.0, y: 10.0),
+        ];
+        assert!(!crosses_dateline(&poly));
+    }
+
+    #[test]
+    fn test_clamp_polygon_to_dateline_positive_side() {
+        let mut poly = polygon![
+            (x: 170.0, y: 10.0),
+            (x: 180.0, y: 10.0),
+            (x: 180.0, y: 20.0),
+            (x: 170.0, y: 20.0),
+            (x: 170.0, y: 10.0),
+        ];
+        let mut clamped = clamp_polygon_to_dateline(&poly);
+
+        // Polygon should be preserved appropriately
+        assert_eq!(clamped, poly);
+
+        poly = polygon![
+            (x: 170.0, y: 10.0),
+            (x: 185.0, y: 10.0),
+            (x: 185.0, y: 20.0),
+            (x: 170.0, y: 20.0),
+            (x: 170.0, y: 10.0),
+        ];
+        clamped = clamp_polygon_to_dateline(&poly);
+
+        let expected = polygon![
+            (x: 170.0, y: 10.0),
+            (x: 180.0, y: 10.0),
+            (x: 180.0, y: 20.0),
+            (x: 170.0, y: 20.0),
+            (x: 170.0, y: 10.0),
+        ];
+
+        // Polygon should be clamped appropriately
+        assert_eq!(clamped, expected);
+    }
+
+    #[test]
+    fn test_clamp_polygon_to_dateline_with_centroid_on_dateline() {
+        // East bound polygon
+        let mut poly = polygon![
+            (x: 170.0, y: 10.0),
+            (x: 190.0, y: 10.0),
+            (x: 190.0, y: 20.0),
+            (x: 170.0, y: 20.0),
+            (x: 170.0, y: 10.0),
+        ];
+        let mut clamped = clamp_polygon_to_dateline(&poly);
+
+        let mut expected = polygon![
+            (x: 170.0, y: 10.0),
+            (x: 180.0, y: 10.0),
+            (x: 180.0, y: 20.0),
+            (x: 170.0, y: 20.0),
+            (x: 170.0, y: 10.0),
+        ];
+
+        // Polygon should be preserved appropriately
+        assert_eq!(clamped, expected);
+
+        // West bound polygon
+        poly = polygon![
+            (x: -170.0, y: 10.0),
+            (x: -190.0, y: 10.0),
+            (x: -190.0, y: 20.0),
+            (x: -170.0, y: 20.0),
+            (x: -170.0, y: 10.0),
+        ];
+        clamped = clamp_polygon_to_dateline(&poly);
+
+        expected = polygon![
+            (x: -170.0, y: 10.0),
+            (x: -180.0, y: 10.0),
+            (x: -180.0, y: 20.0),
+            (x: -170.0, y: 20.0),
+            (x: -170.0, y: 10.0),
+        ];
+
+        // Polygon should be clamped appropriately
+        assert_eq!(clamped, expected);
+    }
+
+    #[test]
+    fn test_clamp_polygon_to_dateline_negative_side() {
+        let mut poly = polygon![
+            (x: -170.0, y: 10.0),
+            (x: -180.0, y: 10.0),
+            (x: -180.0, y: 20.0),
+            (x: -170.0, y: 20.0),
+            (x: -170.0, y: 10.0),
+        ];
+        let mut clamped = clamp_polygon_to_dateline(&poly);
+
+        // Polygon should be preserved appropriately
+        assert_eq!(clamped, poly);
+
+        poly = polygon![
+            (x: -170.0, y: 10.0),
+            (x: -185.0, y: 10.0),
+            (x: -185.0, y: 20.0),
+            (x: -170.0, y: 20.0),
+            (x: -170.0, y: 10.0),
+        ];
+        clamped = clamp_polygon_to_dateline(&poly);
+
+        let expected = polygon![
+            (x: -170.0, y: 10.0),
+            (x: -180.0, y: 10.0),
+            (x: -180.0, y: 20.0),
+            (x: -170.0, y: 20.0),
+            (x: -170.0, y: 10.0),
+        ];
+
+        // Polygon should be clamped appropriately
+        assert_eq!(clamped, expected);
+    }
+}
diff --git a/spatialbench/src/spatial/utils/mod.rs 
b/spatialbench/src/spatial/utils/mod.rs
index 349537a..ab79032 100644
--- a/spatialbench/src/spatial/utils/mod.rs
+++ b/spatialbench/src/spatial/utils/mod.rs
@@ -1,7 +1,9 @@
 pub mod affine;
+mod antimeridian;
 pub mod continent;
 pub mod random;
 
 pub use affine::*;
+pub use antimeridian::*;
 pub use continent::*;
 pub use random::*;

Reply via email to