solverforge_maps/routing/
coord.rs

1//! Coordinate type for geographic locations.
2
3use serde::{Deserialize, Serialize};
4
5use super::error::CoordError;
6
7#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
8pub struct Coord {
9    pub lat: f64,
10    pub lng: f64,
11}
12
13impl Coord {
14    /// Creates a new coordinate, panicking on invalid input.
15    ///
16    /// # Panics
17    ///
18    /// Panics if:
19    /// - `lat` or `lng` is NaN
20    /// - `lat` or `lng` is infinite
21    /// - `lat` is outside [-90, 90]
22    /// - `lng` is outside [-180, 180]
23    #[inline]
24    pub fn new(lat: f64, lng: f64) -> Self {
25        match Self::try_new(lat, lng) {
26            Ok(coord) => coord,
27            Err(e) => panic!("invalid coordinate: {}", e),
28        }
29    }
30
31    /// Attempts to create a new coordinate with validation.
32    ///
33    /// Returns an error if:
34    /// - `lat` or `lng` is NaN
35    /// - `lat` or `lng` is infinite
36    /// - `lat` is outside [-90, 90]
37    /// - `lng` is outside [-180, 180]
38    #[inline]
39    pub fn try_new(lat: f64, lng: f64) -> Result<Self, CoordError> {
40        if lat.is_nan() {
41            return Err(CoordError::LatNaN);
42        }
43
44        if lng.is_nan() {
45            return Err(CoordError::LngNaN);
46        }
47
48        if lat.is_infinite() {
49            return Err(CoordError::LatInfinite { value: lat });
50        }
51
52        if lng.is_infinite() {
53            return Err(CoordError::LngInfinite { value: lng });
54        }
55
56        if !(-90.0..=90.0).contains(&lat) {
57            return Err(CoordError::LatOutOfRange { value: lat });
58        }
59
60        if !(-180.0..=180.0).contains(&lng) {
61            return Err(CoordError::LngOutOfRange { value: lng });
62        }
63
64        Ok(Self { lat, lng })
65    }
66
67    #[inline]
68    pub fn distance_to(self, other: Coord) -> f64 {
69        super::geo::haversine_distance(self, other)
70    }
71}
72
73impl TryFrom<(f64, f64)> for Coord {
74    type Error = CoordError;
75
76    #[inline]
77    fn try_from((lat, lng): (f64, f64)) -> Result<Self, Self::Error> {
78        Self::try_new(lat, lng)
79    }
80}
81
82impl From<Coord> for (f64, f64) {
83    #[inline]
84    fn from(coord: Coord) -> Self {
85        (coord.lat, coord.lng)
86    }
87}