use std::fmt::{Display, Formatter, Result};
use std::hash::{Hash, Hasher};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use world_magnetic_model::uom::si::{
angle::degree, angle::radian, f32::Angle, f32::Length, length::meter,
};
use world_magnetic_model::GeomagneticField;
use geo::Point;
#[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
pub enum MagneticVariation {
East(f32),
West(f32),
OrientedToTrueNorth,
}
impl Hash for MagneticVariation {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
MagneticVariation::East(v) => {
0u8.hash(state);
v.to_bits().hash(state);
}
MagneticVariation::West(v) => {
1u8.hash(state);
v.to_bits().hash(state);
}
MagneticVariation::OrientedToTrueNorth => {
2u8.hash(state);
}
}
}
}
impl From<Point<f64>> for MagneticVariation {
fn from(value: Point<f64>) -> Self {
let latitude = value.y();
let longitude = value.x();
let mag_var = match GeomagneticField::new(
Length::new::<meter>(0.0),
Angle::new::<radian>(latitude.to_radians() as f32),
Angle::new::<radian>(longitude.to_radians() as f32),
OffsetDateTime::now_utc().date(),
) {
Ok(field) => field.declination().get::<degree>(),
Err(_) => todo!("implement TryFrom to handle unavailable variation!"),
};
if mag_var.is_sign_negative() {
Self::West(mag_var.abs())
} else {
Self::East(mag_var)
}
}
}
impl Display for MagneticVariation {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
Self::East(value) => write!(f, "{value:.1}° E"),
Self::West(value) => write!(f, "{value:.1}° W"),
Self::OrientedToTrueNorth => write!(f, "T"),
}
}
}