extern crate pretty_assertions; extern crate rayon; use geo::line_string; use geo::polygon; use geo::CoordFloat; use geo::Geometry; use geo::LineString; use geo::MultiLineString; use geo::MultiPolygon; use geo::Point; use geo::Polygon; use geo_types::Coord; use pretty_assertions::assert_eq; use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; use d3_geo_rs::area::Area; use d3_geo_rs::circle::generator::Generator as CircleGenerator; use d3_geo_rs::data_object::sphere::Sphere; use d3_geo_rs::graticule::generate as generate_graticule; use d3_geo_rs::in_delta::in_delta; use d3_geo_rs::range::range; fn stripes(a: T, b: T) -> Polygon where T: CoordFloat + Send + Sync, { let mut exterior: Vec<(T, T)> = Vec::new(); let mut interior: Vec<(T, T)> = Vec::new(); for (i, d) in [a, b].iter().enumerate() { let mut stripe: Vec<(T, T)> = range( T::from(-180_f64).unwrap(), T::from(180_f64).unwrap(), T::from(0.1_f64).unwrap(), ) .par_iter() .map(|x| (*x, *d)) .collect(); stripe.push(stripe[0]); if i == 0usize { exterior = stripe; } else { stripe.reverse(); interior = stripe; } } Polygon::new(LineString::from(exterior), vec![LineString::from(interior)]) } #[test] fn point() { println!("area: Point"); let g = Geometry::Point(Point::new(0_f64, 0_f64)); let area = Area::calc(&g); assert_eq!(area, 0_f64); } #[test] fn multipoint() { println!("area: Mutlipoint"); let g = Geometry::MultiPoint(vec![(0_f64, 1_f64), (2_f64, 3_f64)].into()); let area = Area::calc(&g); assert_eq!(area, 0_f64); } #[test] fn line_string() { println!("area: LineString"); let g = Geometry::LineString(vec![(0_f64, 1_f64), (2_f64, 3_f64)].into()); let area = Area::calc(&g); assert_eq!(area, 0_f64); } #[test] fn multiline_string() { println!("area: LineString"); let g = MultiLineString(vec![ line_string![(x:0_f64, y:1_f64), (x:2_f64, y:3_f64)], line_string![(x:4_f64, y:5_f64), (x:6_f64,y:7_f64)], ]); let area = Area::calc(&g); assert_eq!(area, 0_f64); } #[test] fn polygon_tiny() { println!("area: Polygon - tiny"); #[allow(clippy::excessive_precision)] let g = polygon![ (x:-64.66070178517852, y:18.33986913231323), (x:-64.66079715091509, y:18.33994007490749), (x:-64.66074946804680, y:18.33994007490749), (x:-64.66070178517852, y:18.33986913231323) ]; let area = Area::calc(&g); assert!(in_delta(area, 4.890516e-13, 1e-13)); } // This is only works with f32s // // Looking at src/area.rs // // if area_ring < T::zero() { // self.area_sum = self.area_sum + T::TAU() + area_ring; // } else { // self.area_sum = self.area_sum + area_ring; // } // // Given the tightly specified floats in the polygon. // The test is designed to make 'self.area_ring' equal exactly zero. // In rust and with f64s the round errors make the sum slightly negative // ... so the test fails because TAU has been added. // #[test] fn polygon_zero_area() { println!("area: Polygon - zero area"); #[allow(clippy::excessive_precision)] let g = polygon![ (x: 96.79142432523281, y:5.262704519048153), (x: 96.81065389253769, y:5.272455576551362), (x: 96.82988345984256, y:5.272455576551362), (x: 96.81065389253769, y:5.272455576551362), (x: 96.79142432523281, y:5.262704519048153) ]; let area = Area::calc(&g); assert_eq!(area, 0_f32); } #[test] fn polygon_semilune() { println!("area: Polygon - semilune"); let g = polygon![ (x:0_f64, y:0_f64), (x:0_f64, y:90_f64), (x:90_f64, y:0_f64), (x:0_f64, y:0_f64) ]; let area = Area::calc(&g); assert!(in_delta(area, std::f64::consts::PI / 2_f64, 1e-6)); } #[test] fn polygon_lune() { println!("area: Polygon - lune"); let g = polygon![ (x:0_f64, y:0_f64), (x:0_f64, y:90_f64), (x:90_f64, y:0_f64), (x:0_f64, y:-90_f64), (x:0_f64, y:0_f64) ]; let area = Area::calc(&g); assert!(in_delta(area, std::f64::consts::PI, 1e-6)); } #[test] fn polygon_hemispheres_north() { println!("area: Polygon - hemispheres north"); let g = polygon![ (x:0_f64, y:0_f64), (x:-90_f64, y:0_f64), (x:180_f64, y:0_f64), (x:90_f64, y:0_f64), (x:0_f64, y:0_f64) ]; let area = Area::calc(&g); assert!(in_delta(area, 2_f64 * std::f64::consts::PI, 1e-6)); } #[test] fn polygon_hemispheres_south() { println!("area: Polygon - hemispheres south"); let g = polygon![ (x:0_f64, y:0_f64), (x:90_f64, y:0_f64), (x:180_f64, y:0_f64), (x:-90_f64, y:0_f64), (x:0_f64, y:0_f64) ]; let area = Area::calc(&g); assert!(in_delta(area, 2_f64 * std::f64::consts::PI, 1e-6)); } #[test] fn polygon_hemispheres_east() { println!("area: Polygon - hemispheres east"); let g = polygon![ (x:0_f64, y:0_f64), (x:0_f64, y:90_f64), (x:180_f64, y:0_f64), (x:0_f64, y:-90_f64), (x:0_f64, y:0_f64) ]; let area = Area::calc(&g); assert!(in_delta(area, 2_f64 * std::f64::consts::PI, 1e-6)); } #[test] fn polygon_hemispheres_west() { println!("area: Polygon - hemispheres west"); let g = polygon![ (x:0_f64, y:0_f64), (x:0_f64, y:-90_f64), (x:180_f64, y:0_f64), (x:0_f64, y:90_f64), (x:0_f64, y:0_f64) ]; let area = Area::calc(&g); assert!(in_delta(area, 2_f64 * std::f64::consts::PI, 1e-6)); } #[test] fn graticule_outline_sphere() { println!("area: Polygon - graticule outline sphere"); let mut graticule = generate_graticule(); graticule.extent_set([[-180_f64, -90_f64], [180_f64, 90_f64]]); let outline = graticule.outline(); let area = Area::calc(&outline); assert!(in_delta(area, 4_f64 * std::f64::consts::PI, 1e-5)); } #[test] fn graticule_outline_hemisphere() { println!("area: Polygon - graticule outline hemisphere"); let mut graticule = generate_graticule(); graticule.extent_set([[-180_f64, 0_f64], [180_f64, 90_f64]]); let outline = graticule.outline(); let area = Area::calc(&outline); assert!(in_delta(area, 2_f64 * std::f64::consts::PI, 1e-5)); } #[test] fn graticule_outline_semilune() { println!("area: Polygon - graticule outline semilune"); let mut graticule = generate_graticule(); graticule.extent_set([[0_f64, 0_f64], [90_f64, 90_f64]]); let outline = graticule.outline(); let area = Area::calc(&outline); assert!(in_delta(area, std::f64::consts::FRAC_PI_2, 1e-5)); } #[test] fn circle_hemisphere() { println!("area: Polygon - circles hemisphere"); let circle = Polygon::new( CircleGenerator::default().radius_set(90_f64).circle(), vec![], ); let area = Area::calc(&circle); assert!(in_delta(area, 2_f64 * std::f64::consts::PI, 1e-5)); } #[test] fn circle_plus_60() { println!("area: Polygon - circles 60°"); let circle = Polygon::new( CircleGenerator::default() .radius_set(60_f64) .precision_set(0.1_f64) .circle(), vec![], ); let area = Area::calc(&circle); assert!(in_delta(area, std::f64::consts::PI, 1e-5)); } #[test] fn circle_plus_60_north() { println!("area: Polygon - circles 60° North"); let circle = Polygon::new( CircleGenerator::default() .radius_set(60_f64) .precision_set(0.1_f64) .center_set(&Coord { x: 0_f64, y: 90_f64, }) .circle(), vec![], ); let area = Area::calc(&circle); assert!(in_delta(area, std::f64::consts::PI, 1e-5)); } #[test] fn circle_plus_45() { println!("area: Polygon - circles 45°"); let circle = Polygon::new( CircleGenerator::default() .radius_set(45_f64) .precision_set(0.1_f64) .circle(), vec![], ); let area = Area::calc(&circle); assert!(in_delta( area, (2_f64 - (2_f64).sqrt()) * std::f64::consts::PI, 1e-5 )); } #[test] fn circle_plus_45_north() { println!("area: Polygon - circles 45° North"); let circle = Polygon::new( CircleGenerator::default() .radius_set(45_f64) .precision_set(0.1_f64) .center_set(&Coord { x: 0_f64, y: 90_f64, }) .circle(), vec![], ); let area = Area::calc(&circle); assert!(in_delta( area, (2_f64 - (2_f64).sqrt()) * std::f64::consts::PI, 1e-5 )); } #[test] fn circle_plus_45_south() { println!("area: Polygon - circles 45° South"); let circle = Polygon::new( CircleGenerator::default() .radius_set(45_f64) .precision_set(0.1_f64) .center_set(&Coord { x: 0_f64, y: -90_f64, }) .circle(), vec![], ); let area = Area::calc(&circle); assert!(in_delta( area, (2_f64 - (2_f64).sqrt()) * std::f64::consts::PI, 1e-5 )); } #[test] fn circle_plus_135() { println!("area: Polygon - circles 45° South"); let circle = Polygon::new( CircleGenerator::default() .radius_set(135_f64) .precision_set(0.1_f64) .circle(), vec![], ); let area = Area::calc(&circle); assert!(in_delta( area, (2_f64 + (2_f64).sqrt()) * std::f64::consts::PI, 1e-5 )); } #[test] fn circle_plus_135_north() { println!("area: Polygon - circles 45° South"); let circle = Polygon::new( CircleGenerator::default() .radius_set(135_f64) .precision_set(0.1_f64) .center_set(&Coord { x: 0_f64, y: 90_f64, }) .circle(), vec![], ); let area = Area::calc(&circle); assert!(in_delta( area, (2_f64 + (2_f64).sqrt()) * std::f64::consts::PI, 1e-5 )); } #[test] fn circle_plus_135_south() { println!("area: Polygon - circles 45° South"); let circle = Polygon::new( CircleGenerator::default() .radius_set(135_f64) .precision_set(0.1_f64) .center_set(&Coord { x: 0_f64, y: -90_f64, }) .circle(), vec![], ); let area = Area::calc(&circle); assert!(in_delta( area, (2_f64 + (2_f64).sqrt()) * std::f64::consts::PI, 1e-5 )); } #[test] fn circles_tiny() { println!("area: Polygon - circles tiny"); let circle = CircleGenerator::default() .radius_set(1e-6_f64) .precision_set(0.1_f64) .circle(); let area = Area::calc(&circle); assert!(in_delta(area, 0_f64, 1e-5)); } #[test] fn circles_huge() { println!("area: Polygon - circles tiny"); let circle = Polygon::new( CircleGenerator::default() .radius_set(180_f64 - 1e-6_f64) .precision_set(0.1_f64) .circle(), vec![], ); let area = Area::calc(&circle); assert!(in_delta(area, 4_f64 * std::f64::consts::PI, 1e-5)); } #[test] fn circles_60_with_45_hole() { println!("area: Polygon - circles 60° with 45° hole"); let ring1 = CircleGenerator::default() .precision_set(0.1) .radius_set(60_f64) .circle() .clone(); let ring2 = CircleGenerator::default() .precision_set(0.1) .radius_set(45_f64) .circle() .clone(); let rev_vec = ring2.into_iter().rev().collect(); let ring2_rev = LineString(rev_vec); let polygon = Polygon::new(ring1, vec![ring2_rev]); assert!(in_delta( Area::calc(&polygon), (2_f64.sqrt() - 1_f64) * std::f64::consts::PI, 1e-5 )); } #[test] fn circles_45_with_hole_0_0_and_0_90() { println!("area: Polygon - circles 45° holes at [0°, 0°] and [0°, 90°]"); let ring1 = CircleGenerator::default() .precision_set(0.1) .radius_set(45_f64) .center_set(&Coord { x: 0_f64, y: 0_f64 }) .circle() .clone(); let rev1_vec: Vec> = ring1.into_iter().rev().collect(); let ring1_rev = LineString(rev1_vec); let ring2 = CircleGenerator::default() .precision_set(0.1) .radius_set(45_f64) .center_set(&Coord { x: 0_f64, y: 90_f64, }) .circle() .clone(); let rev2_vec = ring2.into_iter().rev().collect(); let ring2_rev = LineString(rev2_vec); let polygon = Polygon::new(ring1_rev, vec![ring2_rev]); assert!(in_delta( Area::calc(&polygon), 2_f64 * 2_f64.sqrt() * std::f64::consts::PI, 1e-5 )); } #[test] fn circles_45_with_hole_0_90_and_0_0() { println!("area: Polygon - circles 45° holes at [0°, 90°] and [0°, 0°]"); let ring1 = CircleGenerator::default() .precision_set(0.1) .radius_set(45_f64) .center_set(&Coord { x: 0_f64, y: 90_f64, }) .circle() .clone(); let rev1_vec: Vec> = ring1.into_iter().rev().collect(); let ring1_rev = LineString(rev1_vec); let ring2 = CircleGenerator::default() .precision_set(0.1) .radius_set(45_f64) .center_set(&Coord { x: 0_f64, y: 0_f64 }) .circle() .clone(); let rev2_vec: Vec> = ring2.into_iter().rev().collect(); let ring2_rev = LineString(rev2_vec); let polygon = Polygon::new(ring1_rev, vec![ring2_rev]); assert!(in_delta( Area::calc(&polygon), 2_f64 * 2_f64.sqrt() * std::f64::consts::PI, 1e-5 )); } #[test] fn stripes_45_minus_45() { println!("area: Polygon - stripes 45°, -45°"); let stripes = stripes(45_f64, -45_f64); let area = Area::calc(&stripes); assert!(in_delta( area, std::f64::consts::PI * 2_f64 * 2f64.sqrt(), 1e-5 )); } #[test] fn stripes_minus_45_plus_45() { println!("area: Polygon - stripes 45°, 45°"); let stripes = stripes(-45_f64, 45_f64); let area = Area::calc(&stripes); assert!(in_delta( area, std::f64::consts::PI * 2_f64 * (2_f64 - 2f64.sqrt()), 1e-5 )); } #[test] fn stripes_45_30() { println!("area: Polygon - stripes 45°, 30°"); let stripes = stripes(45_f64, 30_f64); let area = Area::calc(&stripes); assert!(in_delta( area, std::f64::consts::PI * (2f64.sqrt() - 1_f64), 1e-5 )); } #[test] fn two_hemispheres() { println!("area: MultiPolygon two hemispheres"); let mp = MultiPolygon(vec![ Polygon::new( line_string![ (x: 0_f64, y:0_f64), (x:-90_f64, y:0_f64), (x: 180_f64, y: 0_f64), (x:90_f64, y:0_f64), (x:0_f64, y:0_f64) ], vec![], ), Polygon::new( line_string![ (x: 0_f64, y:0_f64), (x:90_f64, y:0_f64), (x: 180_f64, y: 0_f64), (x:-90_f64, y:0_f64), (x:0_f64, y:0_f64) ], vec![], ), ]); let data_o = Geometry::MultiPolygon(mp); let area = Area::calc(&data_o); assert!(in_delta(area, 4_f64 * std::f64::consts::PI, 1e-6)); } #[test] fn sphere() { println!("area: Sphere"); let g = Sphere::default(); let area = Area::calc(&g); assert!(in_delta(area, 4_f64 * std::f64::consts::PI, 1e-6)); } // TODO add GC, FC, F here.