use super::{FloatAdder, atan2, mul_add, sin_cos};
use core::f64::consts::PI;
pub trait Coord2d {
fn xy(self) -> (f64, f64);
}
pub fn linear_ring_area<CoordType>(ring: &[CoordType]) -> f64
where
CoordType: Coord2d + Copy,
{
if ring.is_empty() {
return 0.;
}
let (fst_x, fst_y) = ring[0].xy();
let fst_lat = mul_add(fst_y, 0.5, PI * 0.25);
let sincos_fst = sin_cos(fst_lat);
let mut adder = FloatAdder::default();
let mut sincos_a = sincos_fst;
let mut a_x = fst_x;
for b in &ring[1..] {
let (b_x, b_y) = b.xy();
let b_lat = mul_add(b_y, 0.5, PI * 0.25);
let sincos_b = sin_cos(b_lat);
adder += cagnoli(sincos_a, sincos_b, b_x - a_x);
sincos_a = sincos_b;
a_x = b_x;
}
adder += cagnoli(sincos_a, sincos_fst, fst_x - a_x);
if f64::from(adder) < 0. {
adder += 4. * PI;
}
adder.into()
}
#[inline]
fn cagnoli(
(sin_lat_a, cos_lat_a): (f64, f64),
(sin_lat_b, cos_lat_b): (f64, f64),
delta: f64,
) -> f64 {
let sin_a = sin_lat_a * sin_lat_b;
let cos_a = cos_lat_a * cos_lat_b;
let (sin_d, cos_d) = sin_cos(delta);
-2. * atan2(sin_a * sin_d, mul_add(sin_a, cos_d, cos_a))
}
#[cfg(test)]
#[path = "./area_tests.rs"]
mod tests;