use core::fmt;
use derive_more::{Display, IsVariant, TryUnwrap, Unwrap};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display, IsVariant)]
pub enum Sign {
#[display("+")]
Pos,
#[display("-")]
Neg,
}
impl Sign {
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) const fn from_byte(b: u8) -> Self {
match b {
b'-' => Self::Neg,
_ => Self::Pos,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
const fn multiplier(self) -> f64 {
match self {
Self::Pos => 1.0,
Self::Neg => -1.0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LatDeg {
sign: Sign,
degrees: f64,
}
impl LatDeg {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(sign: Sign, degrees: f64) -> Self {
Self { sign, degrees }
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn sign(&self) -> Sign {
self.sign
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn degrees(&self) -> f64 {
self.degrees
}
}
impl fmt::Display for LatDeg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{:07.4}", self.sign, self.degrees)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LatDegMin {
sign: Sign,
degrees: u8,
minutes: f64,
}
impl LatDegMin {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(sign: Sign, degrees: u8, minutes: f64) -> Self {
Self {
sign,
degrees,
minutes,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn sign(&self) -> Sign {
self.sign
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn degrees(&self) -> u8 {
self.degrees
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn minutes(&self) -> f64 {
self.minutes
}
}
impl fmt::Display for LatDegMin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let min_int = self.minutes as u8;
let min_frac = self.minutes - min_int as f64;
if min_frac == 0.0 {
write!(f, "{}{:02}{min_int:02}", self.sign, self.degrees)
} else {
write!(f, "{}{:02}{:07.4}", self.sign, self.degrees, self.minutes)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LatDMS {
sign: Sign,
degrees: u8,
minutes: u8,
seconds: f64,
}
impl LatDMS {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(sign: Sign, degrees: u8, minutes: u8, seconds: f64) -> Self {
Self {
sign,
degrees,
minutes,
seconds,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn sign(&self) -> Sign {
self.sign
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn degrees(&self) -> u8 {
self.degrees
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn minutes(&self) -> u8 {
self.minutes
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn seconds(&self) -> f64 {
self.seconds
}
}
impl fmt::Display for LatDMS {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sec_int = self.seconds as u8;
let sec_frac = self.seconds - sec_int as f64;
if sec_frac == 0.0 {
write!(
f,
"{}{:02}{:02}{sec_int:02}",
self.sign, self.degrees, self.minutes
)
} else {
write!(
f,
"{}{:02}{:02}{:09.6}",
self.sign, self.degrees, self.minutes, self.seconds
)
}
}
}
#[derive(Debug, Display, Clone, Copy, PartialEq, IsVariant, Unwrap, TryUnwrap)]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
pub enum Latitude {
#[display("{_0}")]
Deg(LatDeg),
#[display("{_0}")]
DegMin(LatDegMin),
#[display("{_0}")]
DMS(LatDMS),
}
impl Latitude {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn sign(&self) -> Sign {
match self {
Self::Deg(v) => v.sign,
Self::DegMin(v) => v.sign,
Self::DMS(v) => v.sign,
}
}
pub fn to_decimal_degrees(&self) -> f64 {
let (sign, val) = match self {
Self::Deg(v) => (v.sign, v.degrees),
Self::DegMin(v) => (v.sign, v.degrees as f64 + v.minutes / 60.0),
Self::DMS(v) => (
v.sign,
v.degrees as f64 + v.minutes as f64 / 60.0 + v.seconds / 3600.0,
),
};
sign.multiplier() * val
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LonDeg {
sign: Sign,
degrees: f64,
}
impl LonDeg {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(sign: Sign, degrees: f64) -> Self {
Self { sign, degrees }
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn sign(&self) -> Sign {
self.sign
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn degrees(&self) -> f64 {
self.degrees
}
}
impl fmt::Display for LonDeg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{:08.4}", self.sign, self.degrees)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LonDegMin {
sign: Sign,
degrees: u16,
minutes: f64,
}
impl LonDegMin {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(sign: Sign, degrees: u16, minutes: f64) -> Self {
Self {
sign,
degrees,
minutes,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn sign(&self) -> Sign {
self.sign
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn degrees(&self) -> u16 {
self.degrees
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn minutes(&self) -> f64 {
self.minutes
}
}
impl fmt::Display for LonDegMin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let min_int = self.minutes as u8;
let min_frac = self.minutes - min_int as f64;
if min_frac == 0.0 {
write!(f, "{}{:03}{min_int:02}", self.sign, self.degrees)
} else {
write!(f, "{}{:03}{:07.4}", self.sign, self.degrees, self.minutes)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LonDMS {
sign: Sign,
degrees: u16,
minutes: u8,
seconds: f64,
}
impl LonDMS {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(sign: Sign, degrees: u16, minutes: u8, seconds: f64) -> Self {
Self {
sign,
degrees,
minutes,
seconds,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn sign(&self) -> Sign {
self.sign
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn degrees(&self) -> u16 {
self.degrees
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn minutes(&self) -> u8 {
self.minutes
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn seconds(&self) -> f64 {
self.seconds
}
}
impl fmt::Display for LonDMS {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sec_int = self.seconds as u8;
let sec_frac = self.seconds - sec_int as f64;
if sec_frac == 0.0 {
write!(
f,
"{}{:03}{:02}{sec_int:02}",
self.sign, self.degrees, self.minutes
)
} else {
write!(
f,
"{}{:03}{:02}{:09.6}",
self.sign, self.degrees, self.minutes, self.seconds
)
}
}
}
#[derive(Debug, Display, Clone, Copy, PartialEq, IsVariant, Unwrap, TryUnwrap)]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
pub enum Longitude {
#[display("{_0}")]
Deg(LonDeg),
#[display("{_0}")]
DegMin(LonDegMin),
#[display("{_0}")]
DMS(LonDMS),
}
impl Longitude {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn sign(&self) -> Sign {
match self {
Self::Deg(v) => v.sign,
Self::DegMin(v) => v.sign,
Self::DMS(v) => v.sign,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn to_decimal_degrees(&self) -> f64 {
let (sign, val) = match self {
Self::Deg(v) => (v.sign, v.degrees),
Self::DegMin(v) => (v.sign, v.degrees as f64 + v.minutes / 60.0),
Self::DMS(v) => (
v.sign,
v.degrees as f64 + v.minutes as f64 / 60.0 + v.seconds / 3600.0,
),
};
sign.multiplier() * val
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Altitude {
sign: Sign,
value: f64,
}
impl Altitude {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(sign: Sign, value: f64) -> Self {
Self { sign, value }
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn sign(&self) -> Sign {
self.sign
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn value(&self) -> f64 {
self.value
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn to_meters(&self) -> f64 {
self.sign.multiplier() * self.value
}
}
impl fmt::Display for Altitude {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let int = self.value as u64;
let frac = self.value - int as f64;
if frac == 0.0 {
write!(f, "{}{int}", self.sign)
} else {
write!(f, "{}{}", self.sign, self.value)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CrsId<'a>(&'a str);
impl<'a> CrsId<'a> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(id: &'a str) -> Self {
Self(id)
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn as_str(&self) -> &'a str {
self.0
}
}
impl fmt::Display for CrsId<'_> {
#[cfg_attr(not(tarpaulin), inline(always))]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "CRS{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Coordinate<'a> {
latitude: Latitude,
longitude: Longitude,
altitude: Option<Altitude>,
crs: Option<CrsId<'a>>,
}
impl<'a> Coordinate<'a> {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new(
latitude: Latitude,
longitude: Longitude,
altitude: Option<Altitude>,
crs: Option<CrsId<'a>>,
) -> Self {
Self {
latitude,
longitude,
altitude,
crs,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn latitude(&self) -> &Latitude {
&self.latitude
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn longitude(&self) -> &Longitude {
&self.longitude
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn altitude(&self) -> Option<&Altitude> {
self.altitude.as_ref()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn crs(&self) -> Option<&CrsId<'a>> {
self.crs.as_ref()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub fn to_decimal_degrees(&self) -> (f64, f64) {
(
self.latitude.to_decimal_degrees(),
self.longitude.to_decimal_degrees(),
)
}
}
impl fmt::Display for Coordinate<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.latitude, self.longitude)?;
if let Some(alt) = &self.altitude {
write!(f, "{alt}")?;
}
if let Some(crs) = &self.crs {
write!(f, "{crs}")?;
}
f.write_str("/")
}
}