use crate::geo::types::Coordinate;
#[must_use]
pub fn coord_distance(a: &Coordinate, b: &Coordinate) -> f64 {
const R: f64 = 6_371_000.0;
let lat1 = a.lat.to_radians();
let lat2 = b.lat.to_radians();
let dlat = lat2 - lat1;
let dlon = (b.lon - a.lon).to_radians();
let sin_dlat = (dlat / 2.0).sin();
let sin_dlon = (dlon / 2.0).sin();
let a_val = sin_dlat * sin_dlat + lat1.cos() * lat2.cos() * sin_dlon * sin_dlon;
let c = 2.0 * a_val.sqrt().atan2((1.0 - a_val).sqrt());
R * c
}
#[allow(dead_code)]
#[must_use]
pub fn bearing(a: &Coordinate, b: &Coordinate) -> f64 {
let lat1 = a.lat.to_radians();
let lat2 = b.lat.to_radians();
let lon_diff = (b.lon - a.lon).to_radians();
let x = lon_diff.sin() * lat2.cos();
let y = lat1.cos() * lat2.sin() - lat1.sin() * lat2.cos() * lon_diff.cos();
(x.atan2(y).to_degrees() + 360.0) % 360.0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_coord_distance_symmetry() {
let a = Coordinate::new(45.5, -73.6);
let b = Coordinate::new(45.6, -73.7);
let d1 = coord_distance(&a, &b);
let d2 = coord_distance(&b, &a);
assert!((d1 - d2).abs() < 1e-6, "distance should be symmetric");
}
#[test]
fn test_coord_distance_to_self() {
let a = Coordinate::new(45.5, -73.6);
let d = coord_distance(&a, &a);
assert!(d.abs() < 1e-6, "distance to self should be ~0");
}
#[test]
fn test_known_distance() {
let nyc = Coordinate::new(40.7128, -74.0060);
let la = Coordinate::new(34.0522, -118.2437);
let d = coord_distance(&nyc, &la);
assert!((d - 3944000.0).abs() < 50000.0, "NYC-LA should be ~3944km: {d}m");
}
}