geospatial_types/
elevation.rs1use crate::coordinate::GeographicCoordinate;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
8pub struct ElevationPoint {
9 pub coordinate: GeographicCoordinate,
11 pub elevation_meters: f64,
13}
14
15#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17pub struct ElevationProfile {
18 pub points: Vec<ElevationPoint>,
20}
21
22impl ElevationProfile {
23 pub fn maximum_elevation_gain_meters(&self) -> f64 {
25 let mut minimum_so_far = f64::INFINITY;
26 let mut maximum_gain = 0.0_f64;
27 for point in &self.points {
28 minimum_so_far = minimum_so_far.min(point.elevation_meters);
29 maximum_gain = maximum_gain.max(point.elevation_meters - minimum_so_far);
30 }
31 maximum_gain
32 }
33}
34
35#[cfg(test)]
36mod tests {
37 use super::{ElevationPoint, ElevationProfile};
38 use crate::coordinate::GeographicCoordinate;
39
40 fn point(latitude: f64, elevation_meters: f64) -> ElevationPoint {
41 ElevationPoint {
42 coordinate: GeographicCoordinate::from_degrees(latitude, 0.0).unwrap(),
43 elevation_meters,
44 }
45 }
46
47 #[test]
48 fn maximum_gain_ignores_dips_after_climbs() {
49 let profile = ElevationProfile {
50 points: vec![
51 point(0.0, 100.0),
52 point(1.0, 400.0),
53 point(2.0, 250.0),
54 point(3.0, 380.0),
55 ],
56 };
57 assert_eq!(profile.maximum_elevation_gain_meters(), 300.0);
58 }
59
60 #[test]
61 fn empty_profile_has_zero_gain() {
62 assert_eq!(
63 ElevationProfile { points: vec![] }.maximum_elevation_gain_meters(),
64 0.0
65 );
66 }
67}