maps_engine_rust/
measure.rs1use crate::geo::{haversine, LonLat};
4
5pub fn measure_distance(points: &[LonLat]) -> f64 {
7 points.windows(2).map(|w| haversine(w[0], w[1])).sum()
8}
9
10pub fn measure_area(vertices: &[LonLat]) -> f64 {
13 if vertices.len() < 3 {
14 return 0.0;
15 }
16 let n = vertices.len() as f64;
17 let clon = vertices.iter().map(|p| p.lon).sum::<f64>() / n;
18 let clat = vertices.iter().map(|p| p.lat).sum::<f64>() / n;
19 let kx = 111_320.0 * (clat.to_radians().cos());
20 let ky = 110_540.0;
21 let pts: Vec<(f64, f64)> = vertices
22 .iter()
23 .map(|p| ((p.lon - clon) * kx, (p.lat - clat) * ky))
24 .collect();
25 let mut area = 0.0;
27 for i in 0..pts.len() {
28 let (x1, y1) = pts[i];
29 let (x2, y2) = pts[(i + 1) % pts.len()];
30 area += x1 * y2 - x2 * y1;
31 }
32 area.abs() / 2.0
33}
34
35pub fn format_distance(meters: f64) -> String {
37 if meters < 1000.0 {
38 format!("{} m", meters.round() as i64)
39 } else {
40 format!("{:.1} km", meters / 1000.0)
41 }
42}
43
44pub fn format_area(sq_meters: f64) -> String {
46 if sq_meters < 1_000_000.0 {
47 format!("{} m²", sq_meters.round() as i64)
48 } else {
49 format!("{:.2} km²", sq_meters / 1_000_000.0)
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn distance_hanoi_haiphong() {
59 let d = measure_distance(&[LonLat::new(105.85, 21.02), LonLat::new(106.68, 20.86)]);
60 assert!((d - 88_000.0).abs() < 5_000.0);
61 }
62
63 #[test]
64 fn area_unit_square_degrees() {
65 let sq = vec![
67 LonLat::new(0.0, 0.0),
68 LonLat::new(1.0, 0.0),
69 LonLat::new(1.0, 1.0),
70 LonLat::new(0.0, 1.0),
71 ];
72 let a = measure_area(&sq);
73 let expected = 111_320.0 * 110_540.0;
74 assert!((a - expected).abs() / expected < 0.02, "got {a}");
75 }
76
77 #[test]
78 fn degenerate_area_is_zero() {
79 assert_eq!(measure_area(&[]), 0.0);
80 assert_eq!(measure_area(&[LonLat::new(0.0, 0.0)]), 0.0);
81 }
82
83 #[test]
84 fn formatting() {
85 assert_eq!(format_distance(850.4), "850 m");
86 assert_eq!(format_distance(12_400.0), "12.4 km");
87 assert_eq!(format_area(450.0), "450 m²");
88 assert_eq!(format_area(2_300_000.0), "2.30 km²");
89 }
90}