sounding_analysis/sounding/
station_info.rs

1use metfor::Meters;
2use optional::Optioned;
3
4/// Station information including location data and identification number.
5#[derive(Debug, Clone, PartialEq, Default)]
6pub struct StationInfo {
7    /// station number, USAF number, eg 727730
8    num: Optioned<i32>,
9    /// station id, usually a 3 or 4 letter value
10    id: Option<String>,
11    /// Latitude and longitude.
12    location: Option<(f64, f64)>,
13    /// Elevation, this may be in model terrain which is not necessarily the same as the real world.
14    elevation: Optioned<Meters>,
15}
16
17impl StationInfo {
18    /// Create a new `StationInfo` object.
19    ///
20    /// # Arguments
21    /// station_num: The USAF station identifier, or None.
22    ///
23    /// location: The latitude and longitude as a tuple, or None.
24    ///
25    /// elevation: The elevation of the station **in meters**.
26    ///
27    /// # Examples
28    ///
29    /// ```rust
30    /// use metfor::{Meters, Feet};
31    /// use sounding_analysis::StationInfo;
32    /// use optional::{some, none};
33    ///
34    /// let _stn = StationInfo::new_with_values(12345, None, (45.2,-113.5), Meters(2000.0));
35    /// let _stn = StationInfo::new_with_values(12345, None, (45.2,-113.5), Feet(2000.0));
36    /// let _stn = StationInfo::new_with_values(12345, None, (45.2,-113.5), some(Meters(2000.0)));
37    /// let _stn = StationInfo::new_with_values(12345, None, (45.2,-113.5), some(Feet(2000.0)));
38    /// let _stn = StationInfo::new_with_values(12345, None, Some((45.2,-113.5)), Meters(2000.0));
39    /// let _stn = StationInfo::new_with_values(12345, None, Some((45.2,-113.5)), Feet(2000.0));
40    /// let _stn = StationInfo::new_with_values(12345, None, Some((45.2,-113.5)), some(Meters(2000.0)));
41    /// let _stn = StationInfo::new_with_values(12345, None, Some((45.2,-113.5)), some(Feet(2000.0)));
42    /// let _stn = StationInfo::new_with_values(Some(12345), None, None, Meters(2000.0));
43    /// let _stn = StationInfo::new_with_values(Some(12345), None, None, Feet(2000.0));
44    /// let _stn = StationInfo::new_with_values(None, None, (45.2,-113.5), some(Meters(2000.0)));
45    /// let _stn = StationInfo::new_with_values(None, None, (45.2,-113.5), some(Feet(2000.0)));
46    ///
47    /// // Note that lat-lon is an `Option` and not an `Optioned`
48    /// let _stn = StationInfo::new_with_values(some(12345), None, None, none::<Feet>());
49    /// let _stn = StationInfo::new_with_values(some(12345), None, None, none::<Meters>());
50    /// ```
51    #[inline]
52    pub fn new_with_values<S, T, U, V, W>(
53        station_num: T,
54        station_id: S,
55        location: U,
56        elevation: V,
57    ) -> Self
58    where
59        S: Into<Option<String>>,
60        T: Into<Optioned<i32>>,
61        U: Into<Option<(f64, f64)>>,
62        Optioned<W>: From<V>,
63        W: optional::Noned + metfor::Length,
64        Meters: From<W>,
65    {
66        let elev: Optioned<W> = Optioned::from(elevation);
67        let elev: Optioned<Meters> = elev.map_t(Meters::from);
68
69        StationInfo {
70            num: station_num.into(),
71            id: station_id.into(),
72            location: location.into(),
73            elevation: elev,
74        }
75    }
76
77    /// Create a new object with default values.
78    ///
79    /// # Examples
80    ///
81    /// ```rust
82    /// use sounding_analysis::StationInfo;
83    ///
84    /// assert!(StationInfo::new().station_num().is_none());
85    /// assert!(StationInfo::new().location().is_none());
86    /// assert!(StationInfo::new().elevation().is_none());
87    ///
88    /// ```
89    #[inline]
90    pub fn new() -> Self {
91        Self::default()
92    }
93
94    /// Builder method to add a station number.
95    ///
96    /// # Examples
97    ///
98    /// ```rust
99    /// use sounding_analysis::StationInfo;
100    ///
101    /// assert_eq!(StationInfo::new().with_station(12345).station_num().unwrap(), 12345);
102    /// assert_eq!(StationInfo::new().with_station(Some(12345)).station_num().unwrap(), 12345);
103    ///
104    /// ```
105    #[inline]
106    pub fn with_station<T>(mut self, number: T) -> Self
107    where
108        Optioned<i32>: From<T>,
109    {
110        self.num = Optioned::from(number);
111
112        self
113    }
114
115    /// Builder method to add a location.
116    ///
117    /// # Examples
118    ///
119    /// ```rust
120    /// use sounding_analysis::StationInfo;
121    ///
122    /// assert_eq!(
123    ///     StationInfo::new().with_lat_lon((45.0, -116.0)).location().unwrap(), (45.0, -116.0));
124    /// assert_eq!(
125    ///     StationInfo::new().with_lat_lon(Some((45.0, -116.0)))
126    ///         .location()
127    ///         .unwrap(),
128    ///     (45.0, -116.0));
129    ///
130    /// ```
131    #[inline]
132    pub fn with_lat_lon<T>(mut self, coords: T) -> Self
133    where
134        Option<(f64, f64)>: From<T>,
135    {
136        self.location = Option::from(coords);
137        self
138    }
139
140    /// Builder method to add elevation.
141    ///
142    /// # Examples
143    ///```rust
144    /// use metfor::{Meters, Feet, Km};
145    /// use sounding_analysis::StationInfo;
146    /// use optional::{some, none};
147    ///
148    /// let _info = StationInfo::new().with_elevation(Feet(200.0));
149    /// let _info = StationInfo::new().with_elevation(Meters(200.0));
150    /// let _info = StationInfo::new().with_elevation(Km(2.0));
151    /// let _info = StationInfo::new().with_elevation(some(Feet(200.0)));
152    /// let _info = StationInfo::new().with_elevation(some(Meters(200.0)));
153    /// let _info = StationInfo::new().with_elevation(some(Km(2.0)));
154    /// let _info = StationInfo::new().with_elevation(none::<Feet>());
155    /// let _info = StationInfo::new().with_elevation(none::<Meters>());
156    /// let _info = StationInfo::new().with_elevation(none::<Km>());
157    ///```
158    #[inline]
159    pub fn with_elevation<T, U>(mut self, elev: T) -> Self
160    where
161        Optioned<U>: From<T>,
162        U: optional::Noned + metfor::Length,
163        Meters: From<U>,
164    {
165        let elevation: Optioned<U> = Optioned::from(elev);
166        let elevation: Optioned<Meters> = elevation.map_t(Meters::from);
167
168        self.elevation = elevation;
169        self
170    }
171
172    /// Builder method to add a station ID. These are usually 3 or 4 alphanumeric codes that may
173    /// not be unique to the location like the station number is supposed to be.
174    #[inline]
175    pub fn with_station_id<T>(mut self, station_id: T) -> Self
176    where
177        Option<String>: From<T>,
178    {
179        let id: Option<String> = Option::from(station_id);
180        self.id = id;
181        self
182    }
183
184    /// station number, USAF number, eg 727730
185    #[inline]
186    pub fn station_num(&self) -> Optioned<i32> {
187        self.num
188    }
189
190    /// Latitude and longitude.
191    #[inline]
192    pub fn location(&self) -> Option<(f64, f64)> {
193        self.location
194    }
195
196    /// Elevation in meters, this may be in model terrain, not necessarily the same as
197    /// the real world.
198    #[inline]
199    pub fn elevation(&self) -> Optioned<Meters> {
200        self.elevation
201    }
202
203    /// Get the station ID that was used with this station. This is normally a series of 3 or 4
204    /// letters. It is not unique to the location like the station number is supposed to be.
205    ///
206    /// # Examples
207    /// ```
208    /// use sounding_analysis::StationInfo;
209    ///
210    /// let info = StationInfo::new().with_station_id("KXLY".to_owned());
211    /// assert_eq!(Some("KXLY"), info.station_id());
212    /// ```
213    #[inline]
214    pub fn station_id(&self) -> Option<&str> {
215        self.id.as_ref().map(|s| s.as_ref())
216    }
217}