pleiades_types/
observer.rs1use core::fmt;
4
5use crate::angles::{Latitude, Longitude};
6
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9#[derive(Clone, Debug, PartialEq)]
10pub struct ObserverLocation {
11 pub latitude: Latitude,
13 pub longitude: Longitude,
15 pub elevation_m: Option<f64>,
17}
18
19impl ObserverLocation {
20 pub const fn new(latitude: Latitude, longitude: Longitude, elevation_m: Option<f64>) -> Self {
22 Self {
23 latitude,
24 longitude,
25 elevation_m,
26 }
27 }
28
29 pub fn validate(&self) -> Result<(), ObserverLocationValidationError> {
32 let latitude = self.latitude.degrees();
33 if !latitude.is_finite() {
34 return Err(ObserverLocationValidationError::NonFiniteLatitude { value: latitude });
35 }
36 if !(-90.0..=90.0).contains(&latitude) {
37 return Err(ObserverLocationValidationError::LatitudeOutOfRange { value: latitude });
38 }
39
40 let longitude = self.longitude.degrees();
41 if !longitude.is_finite() {
42 return Err(ObserverLocationValidationError::NonFiniteLongitude { value: longitude });
43 }
44
45 if let Some(elevation_m) = self.elevation_m {
46 if !elevation_m.is_finite() {
47 return Err(ObserverLocationValidationError::NonFiniteElevation {
48 value: elevation_m,
49 });
50 }
51 }
52
53 Ok(())
54 }
55
56 pub fn summary_line(&self) -> String {
58 let elevation = self
59 .elevation_m
60 .map(|value| format!("{value:.3} m"))
61 .unwrap_or_else(|| "n/a".to_string());
62
63 format!(
64 "latitude={}, longitude={}, elevation={}",
65 self.latitude, self.longitude, elevation
66 )
67 }
68
69 pub fn validated_summary_line(&self) -> Result<String, ObserverLocationValidationError> {
71 self.validate()?;
72 Ok(self.summary_line())
73 }
74}
75
76impl fmt::Display for ObserverLocation {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 f.write_str(&self.summary_line())
79 }
80}
81
82#[derive(Clone, Copy, Debug, PartialEq)]
84#[non_exhaustive]
85pub enum ObserverLocationValidationError {
86 NonFiniteLatitude {
88 value: f64,
90 },
91 LatitudeOutOfRange {
93 value: f64,
95 },
96 NonFiniteLongitude {
98 value: f64,
100 },
101 NonFiniteElevation {
103 value: f64,
105 },
106}
107
108impl ObserverLocationValidationError {
109 pub fn summary_line(&self) -> String {
111 match self {
112 Self::NonFiniteLatitude { value } => {
113 format!("observer latitude must be finite, got {value}")
114 }
115 Self::LatitudeOutOfRange { value } => {
116 format!("observer latitude must stay within [-90, 90], got {value}")
117 }
118 Self::NonFiniteLongitude { value } => {
119 format!("observer longitude must be finite, got {value}")
120 }
121 Self::NonFiniteElevation { value } => {
122 format!("observer elevation must be finite, got {value}")
123 }
124 }
125 }
126}
127
128impl fmt::Display for ObserverLocationValidationError {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 f.write_str(&self.summary_line())
131 }
132}
133
134impl std::error::Error for ObserverLocationValidationError {}