This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sedona-spatialbench.git
The following commit(s) were added to refs/heads/main by this push:
new efb3454 Ensure geometry validity, CCW orientation and antimeridian
handling (#60)
efb3454 is described below
commit efb345416f30197ac8405885ea65c666528da01e
Author: Pranav Toggi <[email protected]>
AuthorDate: Mon Nov 10 17:23:39 2025 -0800
Ensure geometry validity, CCW orientation and antimeridian handling (#60)
* orient polygons to counter clockwise
* fix zero area polygons
* add wrap around logic to keep points within [-180, 180] degrees
* add logic to handle antimeridian crossing geometries
* add test suite for geometry validation
* fix cli-integration tests
* Update spatialbench/tests/geometry_tests.rs
Co-authored-by: Copilot <[email protected]>
* pin python version
* update comments
---------
Co-authored-by: Copilot <[email protected]>
---
.github/workflows/packaging.yml | 2 +-
Cargo.toml | 2 +-
spatialbench-cli/tests/cli_integration.rs | 2 +-
spatialbench/data/sf-v1/building.tbl.gz | Bin 38908 -> 39409 bytes
spatialbench/data/sf-v1/trip.tbl.gz | Bin 286086 -> 286085 bytes
spatialbench/src/generators.rs | 6 +-
spatialbench/src/spatial/geometry.rs | 36 +++-
spatialbench/src/spatial/utils/antimeridian.rs | 267 +++++++++++++++++++++++++
spatialbench/src/spatial/utils/mod.rs | 2 +
spatialbench/tests/geometry_tests.rs | 136 +++++++++++++
10 files changed, 440 insertions(+), 13 deletions(-)
diff --git a/.github/workflows/packaging.yml b/.github/workflows/packaging.yml
index 507ba95..afce8ef 100644
--- a/.github/workflows/packaging.yml
+++ b/.github/workflows/packaging.yml
@@ -41,7 +41,7 @@ jobs:
- uses: actions/setup-python@v5
with:
- python-version: "3.x"
+ python-version: "3.13"
- name: Install docs requirements
run: pip install -r docs/requirements.txt
diff --git a/Cargo.toml b/Cargo.toml
index 0d419ef..d95e73a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,7 +14,7 @@ repository = "https://github.com/apache/sedona-spatialbench/"
version = "1.1.1"
[workspace.dependencies]
-geo = "0.30.0"
+geo = "0.31.0"
geozero = { version = "0.14.0", features = ["with-wkb", "with-geo"]}
[profile.release]
diff --git a/spatialbench-cli/tests/cli_integration.rs
b/spatialbench-cli/tests/cli_integration.rs
index 3a53f7e..d1f325c 100644
--- a/spatialbench-cli/tests/cli_integration.rs
+++ b/spatialbench-cli/tests/cli_integration.rs
@@ -108,7 +108,7 @@ fn test_spatialbench_cli_tbl_no_overwrite() {
run_command();
let original_metadata =
fs::metadata(&expected_file).expect("Failed to get metadata of
generated file");
- assert_eq!(original_metadata.len(), 826311);
+ assert_eq!(original_metadata.len(), 826317);
// Run the spatialbench-cli command again with the same parameters and
expect the
// file to not be overwritten
diff --git a/spatialbench/data/sf-v1/building.tbl.gz
b/spatialbench/data/sf-v1/building.tbl.gz
index 297656c..9638c86 100644
Binary files a/spatialbench/data/sf-v1/building.tbl.gz and
b/spatialbench/data/sf-v1/building.tbl.gz differ
diff --git a/spatialbench/data/sf-v1/trip.tbl.gz
b/spatialbench/data/sf-v1/trip.tbl.gz
index 7114b91..8990906 100644
Binary files a/spatialbench/data/sf-v1/trip.tbl.gz and
b/spatialbench/data/sf-v1/trip.tbl.gz differ
diff --git a/spatialbench/src/generators.rs b/spatialbench/src/generators.rs
index b7319e2..122718f 100644
--- a/spatialbench/src/generators.rs
+++ b/spatialbench/src/generators.rs
@@ -11,7 +11,7 @@ use crate::random::{RandomAlphaNumeric,
RandomAlphaNumericInstance};
use crate::random::{RandomBoundedInt, RandomString, RandomStringSequence,
RandomText};
use crate::spatial::overrides as spatial_overrides;
use crate::spatial::utils::continent::{build_continent_cdf, WeightedTarget};
-use crate::spatial::utils::{hash_to_unit_u64, spider_seed_for_index};
+use crate::spatial::utils::{hash_to_unit_u64, spider_seed_for_index,
wrap_around_longitude};
use crate::spatial::{ContinentAffines, SpatialDefaults, SpatialGenerator};
use crate::text::TextPool;
use geo::Point;
@@ -1132,6 +1132,8 @@ impl TripGeneratorIterator {
let angle: f64 = angle_rng.gen::<f64>() * std::f64::consts::TAU;
let mut dropoff_x = pickuploc.x() + distance_value * angle.cos();
+ dropoff_x = wrap_around_longitude(dropoff_x);
+
let mut dropoff_y = pickuploc.y() + distance_value * angle.sin();
// Hard code coordinate precision to 8 decimal places - milimeter
level precision for WGS 84
@@ -1570,6 +1572,6 @@ mod tests {
// Check first Building
let first = &buildings[1];
assert_eq!(first.b_buildingkey, 2);
- assert_eq!(first.to_string(), "2|blush|POLYGON((124.218033476
10.538071565,124.215762091 10.536069114,124.214352934
10.536014944,124.212486371 10.539913704,124.217919324
10.539075339,124.218033476 10.538071565))|")
+ assert_eq!(first.to_string(), "2|blush|POLYGON((124.218033476
10.538071565,124.217919324 10.539075339,124.212486371
10.539913704,124.214352934 10.536014944,124.215762091
10.536069114,124.218033476 10.538071565))|")
}
}
diff --git a/spatialbench/src/spatial/geometry.rs
b/spatialbench/src/spatial/geometry.rs
index b856585..b753d57 100644
--- a/spatialbench/src/spatial/geometry.rs
+++ b/spatialbench/src/spatial/geometry.rs
@@ -1,11 +1,15 @@
-use crate::spatial::utils::{apply_affine, round_coordinates};
+use crate::spatial::utils::{
+ apply_affine, clamp_polygon_to_dateline, crosses_dateline,
round_coordinates,
+ wrap_around_longitude,
+};
use crate::spatial::{GeomType, SpatialConfig};
-use geo::{coord, Geometry, LineString, Point, Polygon};
+use geo::orient::Direction;
+use geo::{coord, Geometry, LineString, Orient, Point, Polygon};
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),
@@ -23,6 +27,7 @@ pub fn emit_geom(
pub fn generate_point_geom(center: (f64, f64), m: &[f64; 6]) -> Geometry {
let (x, y) = apply_affine(center.0, center.1, m);
+ let x = wrap_around_longitude(x);
let (x, y) = round_coordinates(x, y, GEOMETRY_PRECISION);
Geometry::Point(Point::new(x, y))
}
@@ -51,7 +56,15 @@ pub fn generate_box_geom(
.map(|(x, y)| coord! { x: x, y: y })
.collect();
- Geometry::Polygon(Polygon::new(LineString::from(coords), vec![]))
+ 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)
}
pub fn generate_polygon_geom(
@@ -79,9 +92,8 @@ pub fn generate_polygon_geom(
center.0 + config.polysize * ang.cos(),
center.1 + config.polysize * ang.sin(),
);
- let (x1, y1) = (x0.clamp(0.0, 1.0), y0.clamp(0.0, 1.0));
- let (x2, y2) = apply_affine(x1, y1, m);
- let (xr, yr) = round_coordinates(x2, y2, GEOMETRY_PRECISION);
+ let (x1, y1) = apply_affine(x0, y0, m);
+ let (xr, yr) = round_coordinates(x1, y1, GEOMETRY_PRECISION);
coord! { x: xr, y: yr }
})
.collect::<Vec<_>>();
@@ -90,5 +102,13 @@ pub fn generate_polygon_geom(
ring.push(first);
}
- Geometry::Polygon(Polygon::new(LineString::from(ring), vec![]))
+ 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/antimeridian.rs
b/spatialbench/src/spatial/utils/antimeridian.rs
new file mode 100644
index 0000000..22e9190
--- /dev/null
+++ b/spatialbench/src/spatial/utils/antimeridian.rs
@@ -0,0 +1,267 @@
+use geo::{Centroid, LineString, Polygon};
+
+/// Normalizes longitude values to the valid range [-180, 180] by wrapping
around the antimeridian.
+///
+/// This function is specifically designed for point geometries (e.g., trip
pickup/dropoff locations)
+/// that may have longitude values slightly outside the standard range. Unlike
polygon clamping,
+/// which handles geometries crossing the dateline, this function simply
normalizes point coordinates.
+///
+/// # Examples
+/// - POINT(181, 20) becomes POINT(-179, 20) after wraparound
+/// - POINT(-181, 20) becomes POINT(179, 20) after wraparound
+///
+/// # Note
+/// This is different from `clamp_polygon_to_dateline()`, which handles
polygons that cross
+/// the dateline by splitting them. This function is used before CCW
orientation enforcement.
+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 prevent it from crossing the
antimeridian (±180°).
+///
+/// This function is used to handle polygons that span across the dateline,
which can cause
+/// rendering and spatial operation issues. It constrains the polygon to stay
on one side of
+/// the dateline by clamping coordinates based on the polygon's centroid
location.
+///
+/// # Behavior
+/// - If the centroid is in the eastern hemisphere (≥ 0°), coordinates are
clamped to [0°, 180°]
+/// - If the centroid is in the western hemisphere (< 0°), coordinates are
clamped to [-180°, 0°]
+/// - Latitude values (y-coordinates) remain unchanged
+///
+/// # Note
+/// This is different from `wrap_around_longitude()`, which normalizes
individual point coordinates
+/// that fall slightly outside [-180, 180]. This function handles entire
polygons that cross the dateline.
+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::*;
diff --git a/spatialbench/tests/geometry_tests.rs
b/spatialbench/tests/geometry_tests.rs
new file mode 100644
index 0000000..3c484e4
--- /dev/null
+++ b/spatialbench/tests/geometry_tests.rs
@@ -0,0 +1,136 @@
+//! Geometry validation tests for generated spatial data.
+//!
+//! This test suite validates that all generated geometries meet quality and
correctness standards:
+//! - **Coordinate validity**: Longitude [-180°, 180°], latitude [-90°, 90°]
+//! - **OGC compliance**: Valid topology, counter-clockwise winding for
exterior rings
+//! - **Precision constraints**: Coordinates limited to 9 decimal places
+//! - **Antimeridian handling**: No dateline crossings in generated polygons
+//!
+//! These tests ensure spatial data quality and compatibility with spatial
databases and GIS tools.
+
+#[cfg(test)]
+mod geometry_tests {
+ use geo::{Validation, Winding};
+ use spatialbench::generators::{BuildingGenerator, TripGenerator};
+ use spatialbench::spatial::geometry::GEOMETRY_PRECISION;
+ use spatialbench::spatial::utils::crosses_dateline;
+
+ #[test]
+ fn test_trip_coordinates_are_valid() {
+ let generator = TripGenerator::new(0.1, 1, 1);
+ let trips: Vec<_> = generator.iter().collect();
+
+ for trip in trips {
+ // Check pickup coordinates are valid
+ assert!(
+ trip.t_pickuploc.x() >= -180.0 && trip.t_pickuploc.x() <=
180.0,
+ "Pickup longitude out of range: {}",
+ trip.t_pickuploc.x()
+ );
+ assert!(
+ trip.t_pickuploc.y() >= -90.0 && trip.t_pickuploc.y() <= 90.0,
+ "Pickup latitude out of range: {}",
+ trip.t_pickuploc.y()
+ );
+
+ // Check dropoff coordinates are valid
+ assert!(
+ trip.t_dropoffloc.x() >= -180.0 && trip.t_dropoffloc.x() <=
180.0,
+ "Dropoff longitude out of range: {}",
+ trip.t_dropoffloc.x()
+ );
+ assert!(
+ trip.t_dropoffloc.y() >= -90.0 && trip.t_dropoffloc.y() <=
90.0,
+ "Dropoff latitude out of range: {}",
+ trip.t_dropoffloc.y()
+ );
+
+ // Check coordinates have proper precision (9 decimal places)
+ let pickup_x_precision =
+ (trip.t_pickuploc.x() * GEOMETRY_PRECISION).round() /
GEOMETRY_PRECISION;
+ assert_eq!(trip.t_pickuploc.x(), pickup_x_precision);
+
+ let dropoff_x_precision =
+ (trip.t_dropoffloc.x() * GEOMETRY_PRECISION).round() /
GEOMETRY_PRECISION;
+ assert_eq!(trip.t_dropoffloc.x(), dropoff_x_precision);
+ }
+ }
+
+ #[test]
+ fn test_building_polygons_are_valid() {
+ let generator = BuildingGenerator::new(10.0, 1, 1);
+ let buildings: Vec<_> = generator.iter().collect();
+
+ for building in buildings {
+ let polygon = &building.b_boundary;
+
+ assert!(
+ polygon.is_valid(),
+ "Building {} has invalid polygon",
+ building.b_buildingkey
+ );
+ }
+ }
+
+ #[test]
+ fn test_building_polygon_winding() {
+ // Create a generator with a small scale factor
+ let generator = BuildingGenerator::new(1.0, 1, 1);
+ let buildings: Vec<_> = generator.iter().collect();
+
+ // Check that all building polygons have counter-clockwise winding
+ for building in buildings.iter() {
+ let exterior = building.b_boundary.exterior();
+ assert!(
+ exterior.is_ccw(),
+ "Building {} polygon should have counter-clockwise winding",
+ building.b_buildingkey
+ );
+ }
+ }
+
+ #[test]
+ fn test_coordinate_precision() {
+ let generator = TripGenerator::new(0.01, 1, 1);
+ let trips: Vec<_> = generator.iter().collect();
+
+ for trip in trips {
+ // Check 9 decimal place precision
+ let pickup_x_str = format!("{:.9}", trip.t_pickuploc.x());
+ let pickup_y_str = format!("{:.9}", trip.t_pickuploc.y());
+ let dropoff_x_str = format!("{:.9}", trip.t_dropoffloc.x());
+ let dropoff_y_str = format!("{:.9}", trip.t_dropoffloc.y());
+
+ // Verify no extra precision beyond 9 decimals
+ let pickup_x_parsed: f64 = pickup_x_str.parse().unwrap();
+ let pickup_y_parsed: f64 = pickup_y_str.parse().unwrap();
+ let dropoff_x_parsed: f64 = dropoff_x_str.parse().unwrap();
+ let dropoff_y_parsed: f64 = dropoff_y_str.parse().unwrap();
+
+ assert_eq!(trip.t_pickuploc.x(), pickup_x_parsed);
+ assert_eq!(trip.t_pickuploc.y(), pickup_y_parsed);
+ assert_eq!(trip.t_dropoffloc.x(), dropoff_x_parsed);
+ assert_eq!(trip.t_dropoffloc.y(), dropoff_y_parsed);
+ }
+ }
+
+ #[test]
+ fn test_building_dateline_crossing() {
+ use spatialbench::generators::BuildingGenerator;
+
+ let generator = BuildingGenerator::new(1000.0, 1, 1);
+ let buildings: Vec<_> = generator.iter().collect();
+
+ for building in buildings {
+ let polygon = &building.b_boundary;
+
+ assert_eq!(
+ crosses_dateline(polygon),
+ false,
+ "Building {} polygon crosses dateline: {:?}",
+ building.b_buildingkey,
+ building.b_boundary
+ );
+ }
+ }
+}