use crate::util::math_helper::HOUR_MINUTES;
use jiff::tz::TimeZone;
#[derive(Debug, Clone, PartialEq)]
pub struct GeoLocation {
pub(crate) latitude: f64,
pub(crate) longitude: f64,
pub(crate) elevation: f64,
pub(crate) timezone: TimeZone,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum GeoLocationError {
InvalidLatitude(f64),
InvalidLongitude(f64),
InvalidElevation(f64),
}
impl core::fmt::Display for GeoLocationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::InvalidLatitude(lat) => {
write!(f, "latitude {lat} is not in the range [-90.0, 90.0]")
}
Self::InvalidLongitude(long) => {
write!(f, "longitude {long} is not in the range [-180.0, 180.0]")
}
Self::InvalidElevation(elev) => {
write!(
f,
"elevation {elev} is not a non-negative finite number of meters"
)
}
}
}
}
impl std::error::Error for GeoLocationError {}
impl GeoLocation {
pub fn new(
latitude: f64,
longitude: f64,
elevation: f64,
timezone: TimeZone,
) -> Result<Self, GeoLocationError> {
if !(-90.0..=90.0).contains(&latitude) {
return Err(GeoLocationError::InvalidLatitude(latitude));
}
if !(-180.0..=180.0).contains(&longitude) {
return Err(GeoLocationError::InvalidLongitude(longitude));
}
if !elevation.is_finite() || elevation < 0.0 {
return Err(GeoLocationError::InvalidElevation(elevation));
}
Ok(Self {
latitude,
longitude,
elevation,
timezone,
})
}
#[must_use]
pub fn latitude(&self) -> f64 {
self.latitude
}
#[must_use]
pub fn longitude(&self) -> f64 {
self.longitude
}
#[must_use]
pub fn elevation(&self) -> f64 {
self.elevation
}
#[must_use]
pub fn timezone(&self) -> &TimeZone {
&self.timezone
}
#[must_use]
pub fn local_mean_time_offset(&self) -> f64 {
(self.longitude * 4.0) / HOUR_MINUTES
}
}