use crate::geo::{haversine, LonLat};
pub fn measure_distance(points: &[LonLat]) -> f64 {
points.windows(2).map(|w| haversine(w[0], w[1])).sum()
}
pub fn measure_area(vertices: &[LonLat]) -> f64 {
if vertices.len() < 3 {
return 0.0;
}
let n = vertices.len() as f64;
let clon = vertices.iter().map(|p| p.lon).sum::<f64>() / n;
let clat = vertices.iter().map(|p| p.lat).sum::<f64>() / n;
let kx = 111_320.0 * (clat.to_radians().cos());
let ky = 110_540.0;
let pts: Vec<(f64, f64)> = vertices
.iter()
.map(|p| ((p.lon - clon) * kx, (p.lat - clat) * ky))
.collect();
let mut area = 0.0;
for i in 0..pts.len() {
let (x1, y1) = pts[i];
let (x2, y2) = pts[(i + 1) % pts.len()];
area += x1 * y2 - x2 * y1;
}
area.abs() / 2.0
}
pub fn format_distance(meters: f64) -> String {
if meters < 1000.0 {
format!("{} m", meters.round() as i64)
} else {
format!("{:.1} km", meters / 1000.0)
}
}
pub fn format_area(sq_meters: f64) -> String {
if sq_meters < 1_000_000.0 {
format!("{} m²", sq_meters.round() as i64)
} else {
format!("{:.2} km²", sq_meters / 1_000_000.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn distance_hanoi_haiphong() {
let d = measure_distance(&[LonLat::new(105.85, 21.02), LonLat::new(106.68, 20.86)]);
assert!((d - 88_000.0).abs() < 5_000.0);
}
#[test]
fn area_unit_square_degrees() {
let sq = vec![
LonLat::new(0.0, 0.0),
LonLat::new(1.0, 0.0),
LonLat::new(1.0, 1.0),
LonLat::new(0.0, 1.0),
];
let a = measure_area(&sq);
let expected = 111_320.0 * 110_540.0;
assert!((a - expected).abs() / expected < 0.02, "got {a}");
}
#[test]
fn degenerate_area_is_zero() {
assert_eq!(measure_area(&[]), 0.0);
assert_eq!(measure_area(&[LonLat::new(0.0, 0.0)]), 0.0);
}
#[test]
fn formatting() {
assert_eq!(format_distance(850.4), "850 m");
assert_eq!(format_distance(12_400.0), "12.4 km");
assert_eq!(format_area(450.0), "450 m²");
assert_eq!(format_area(2_300_000.0), "2.30 km²");
}
}