use core::fmt;
use supernovas_ffi::novas_on_surface;
use super::Weather;
use crate::{Angle, Coordinate, error::Result};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Site {
latitude: Angle,
longitude: Angle,
height: Coordinate,
weather: Weather,
}
impl Site {
pub fn new(latitude: Angle, longitude: Angle, height: Coordinate) -> Self {
Site {
latitude,
longitude,
height,
weather: Weather::default(),
}
}
pub fn from_degrees(latitude_deg: f64, longitude_deg: f64, height_m: f64) -> Result<Self> {
Ok(Site::new(
Angle::from_degrees(latitude_deg)?,
Angle::from_degrees(longitude_deg)?,
Coordinate::from_meters(height_m)?,
))
}
pub fn with_weather(mut self, weather: Weather) -> Self {
self.weather = weather;
self
}
pub fn latitude(self) -> Angle {
self.latitude
}
pub fn longitude(self) -> Angle {
self.longitude
}
pub fn height(self) -> Coordinate {
self.height
}
pub fn weather(self) -> Weather {
self.weather
}
pub(crate) fn as_on_surface(self) -> novas_on_surface {
novas_on_surface {
latitude: self.latitude.deg(),
longitude: self.longitude.deg(),
height: self.height.m(),
temperature: self.weather.temperature().map_or(f64::NAN, |t| t.celsius()),
pressure: self.weather.pressure().map_or(f64::NAN, |p| p.mbar()),
humidity: self.weather.humidity_percent().unwrap_or(f64::NAN),
}
}
}
impl fmt::Display for Site {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"lat={} lon={} h={}",
self.latitude, self.longitude, self.height
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_degrees_round_trip() {
let s = Site::from_degrees(34.0, -118.0, 100.0).unwrap();
assert!((s.latitude().deg() - 34.0).abs() < 1e-12);
assert!((s.longitude().deg() - -118.0).abs() < 1e-12);
assert!((s.height().m() - 100.0).abs() < 1e-12);
}
#[test]
fn weather_defaults_to_empty() {
let s = Site::from_degrees(0.0, 0.0, 0.0).unwrap();
assert!(s.weather().temperature().is_none());
}
#[test]
fn with_weather_attaches_it() {
let s = Site::from_degrees(34.0, -118.0, 100.0)
.unwrap()
.with_weather(Weather::standard());
assert!((s.weather().temperature().unwrap().celsius() - 15.0).abs() < 1e-12);
}
#[test]
fn as_on_surface_uses_nan_for_missing_weather() {
let s = Site::from_degrees(34.0, -118.0, 100.0).unwrap();
let raw = s.as_on_surface();
assert!((raw.latitude - 34.0).abs() < 1e-12);
assert!((raw.longitude - -118.0).abs() < 1e-12);
assert!((raw.height - 100.0).abs() < 1e-12);
assert!(raw.temperature.is_nan());
assert!(raw.pressure.is_nan());
assert!(raw.humidity.is_nan());
}
#[test]
fn as_on_surface_includes_weather_when_set() {
let s = Site::from_degrees(0.0, 0.0, 0.0)
.unwrap()
.with_weather(Weather::standard());
let raw = s.as_on_surface();
assert!((raw.temperature - 15.0).abs() < 1e-12);
assert!((raw.pressure - 1013.25).abs() < 1e-12);
assert!((raw.humidity - 50.0).abs() < 1e-12);
}
}