dms_coordinates/
cardinal.rs

1//! Cardinal points, only integer angles (N, NE, E, ..) are supported
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6#[derive(PartialEq, Eq, Debug, Copy, Clone)]
7#[repr(u16)]
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
9pub enum Cardinal {
10    /// Northern Cardinal
11    North = 0,
12    /// North Eastern Cardinal
13    NorthEast = 45,
14    /// Eastern Cardinal
15    East = 90,
16    /// South Eastern Cardinal
17    SouthEast = 135,
18    /// Southern Cardinal
19    South = 180,
20    /// South Western Cardinal
21    SouthWest = 225,
22    /// Western Cardinal
23    West = 270,
24    /// North Western Cardinal
25    NorthWest = 315,
26}
27
28impl core::ops::Add<u16> for Cardinal {
29    type Output = Cardinal;
30    /// Adds given angle (°) to Self
31    fn add(self, rhs: u16) -> Self {
32        Cardinal::from_angle((self.to_angle() + rhs) % 360)
33    }
34}
35
36impl Default for Cardinal {
37    /// Builds default Northern Cardinal
38    fn default() -> Self {
39        Self::North
40    }
41}
42
43impl core::fmt::Display for Cardinal {
44    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
45        match self {
46            Cardinal::North => write!(f, "N"),
47            Cardinal::NorthEast => write!(f, "NE"),
48            Cardinal::East => write!(f, "E"),
49            Cardinal::SouthEast => write!(f, "SE"),
50            Cardinal::South => write!(f, "S"),
51            Cardinal::SouthWest => write!(f, "SW"),
52            Cardinal::West => write!(f, "W"),
53            Cardinal::NorthWest => write!(f, "NW"),
54        }
55    }
56}
57
58impl Cardinal {
59    /// Returns True if Self matches a latitude cardinal
60    pub fn is_latitude(&self) -> bool {
61        match self {
62            Cardinal::North | Cardinal::South => true,
63            _ => false,
64        }
65    }
66    /// Returns True if Self matches a longitude cardinal
67    pub fn is_longitude(&self) -> bool {
68        match self {
69            Cardinal::East | Cardinal::West => true,
70            _ => false,
71        }
72    }
73    /// Returns True if Cardinal and `rhs` represents
74    /// same kind of coordinates
75    pub fn same_kind(&self, rhs: Self) -> bool {
76        (self.is_latitude() && rhs.is_latitude()) || (self.is_longitude() && rhs.is_longitude())
77    }
78    /// Returns True if Self is a Northern cardinal
79    pub fn is_northern(&self) -> bool {
80        match self {
81            Cardinal::North | Cardinal::NorthEast | Cardinal::NorthWest => true,
82            _ => false,
83        }
84    }
85    /// Returns True if Self is a Southern cardinal
86    pub fn is_southern(&self) -> bool {
87        match self {
88            Cardinal::South | Cardinal::SouthEast | Cardinal::SouthWest => true,
89            _ => false,
90        }
91    }
92    /// Returns True if Self is an Eastern cardinal
93    pub fn is_eastern(&self) -> bool {
94        match self {
95            Cardinal::East | Cardinal::NorthEast | Cardinal::SouthEast => true,
96            _ => false,
97        }
98    }
99    /// Returns True if Self is a Western cardinal
100    pub fn is_western(&self) -> bool {
101        match self {
102            Cardinal::West | Cardinal::NorthWest | Cardinal::SouthWest => true,
103            _ => false,
104        }
105    }
106    /// Returns True if Self matches a subquadrant cardinal, like NE or SW
107    pub fn is_sub_quadrant(&self) -> bool {
108        (self.to_angle() / 45) % 2 > 0
109    }
110    /// Returns compass angle (in D°) associated to Self,
111    /// 0° being North Cardinal
112    pub fn to_angle(&self) -> u16 {
113        match self {
114            Cardinal::North => 0,
115            Cardinal::NorthEast => 45,
116            Cardinal::East => 90,
117            Cardinal::SouthEast => 135,
118            Cardinal::South => 180,
119            Cardinal::SouthWest => 225,
120            Cardinal::West => 270,
121            Cardinal::NorthWest => 315,
122        }
123    }
124    /// Builds a Cardinal from given compass angle (in D°),
125    /// 0° being North Cardinal
126    pub fn from_angle(angle: u16) -> Cardinal {
127        if angle < 45 {
128            Cardinal::North
129        } else if angle < 90 {
130            Cardinal::NorthEast
131        } else if angle < 135 {
132            Cardinal::East
133        } else if angle < 180 {
134            Cardinal::SouthEast
135        } else if angle < 225 {
136            Cardinal::South
137        } else if angle < 270 {
138            Cardinal::SouthWest
139        } else if angle < 315 {
140            Cardinal::West
141        } else {
142            Cardinal::NorthWest
143        }
144    }
145}