use log::warn;
pub fn dms_to_dd(d: i32, m: u16, s: f64) -> f64 {
(d.abs() as f64 + (m as f64 + s / 60.) / 60.).copysign(d as f64)
}
pub fn dm_to_dd(d: i32, m: f64) -> f64 {
(d.abs() as f64 + (m / 60.)).copysign(d as f64)
}
pub fn iso_dm_to_dd(iso_dm: f64) -> f64 {
let magn = iso_dm.abs();
let dm = magn.trunc() as u64;
let d = (dm / 100) as f64;
let m = (dm % 100) as f64 + magn.fract();
(d + (m / 60.)).copysign(iso_dm)
}
pub fn dd_to_iso_dm(dd: f64) -> f64 {
let dm = dd.abs();
let d = dm.trunc();
let m = dm.fract() * 60.;
(d * 100. + m).copysign(dd)
}
pub fn iso_dms_to_dd(iso_dms: f64) -> f64 {
let magn = iso_dms.abs();
let dms = magn as u32;
let d = dms / 10000;
let ms = dms % 10000;
let m = ms / 100;
let s = (ms % 100) as f64 + magn.fract();
(d as f64 + ((s / 60.) + m as f64) / 60.).copysign(iso_dms)
}
pub fn dd_to_iso_dms(dd: f64) -> f64 {
let magn = dd.abs();
let d = magn.trunc();
let mm = magn.fract() * 60.;
let m = mm.trunc();
let s = mm.fract() * 60.;
(d * 10000. + m * 100. + s).copysign(dd)
}
pub fn normalize_symmetric(angle: f64) -> f64 {
use std::f64::consts::PI;
let angle = (angle + PI) % (2.0 * PI);
angle - PI * angle.signum()
}
pub fn normalize_positive(angle: f64) -> f64 {
use std::f64::consts::PI;
let angle = angle % (2.0 * PI);
if angle < 0. {
return angle + 2.0 * PI;
}
angle
}
pub fn parse_sexagesimal(angle: &str) -> f64 {
let mut dms = [0.0, 0.0, 0.0];
let mut angle = angle.trim();
let n = angle.len();
if n == 0 || angle == "NaN" {
return f64::NAN;
}
let mut postfix_sign = 1.0;
if "wWsSeEnN".contains(&angle[n - 1..]) {
if "wWsS".contains(&angle[n - 1..]) {
postfix_sign = -1.0;
}
angle = &angle[..n - 1];
}
for (i, element) in angle.split(':').enumerate() {
if i < 3 {
if let Ok(v) = element.parse::<f64>() {
dms[i] = v;
continue;
}
}
warn!("Cannot parse {angle} as a real number or sexagesimal angle");
return f64::NAN;
}
let sign = dms[0].signum() * postfix_sign;
sign * (dms[0].abs() + (dms[1] + dms[2] / 60.0) / 60.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_angular() {
assert_eq!(dms_to_dd(55, 30, 36.), 55.51);
assert_eq!(dm_to_dd(55, 30.60), 55.51);
assert!((iso_dm_to_dd(5530.60) - 55.51).abs() < 1e-10);
assert!((iso_dm_to_dd(15530.60) - 155.51).abs() < 1e-10);
assert!((iso_dm_to_dd(-15530.60) + 155.51).abs() < 1e-10);
assert!((iso_dms_to_dd(553036.0) - 55.51).abs() < 1e-10);
assert_eq!(dd_to_iso_dm(55.5025), 5530.15);
assert_eq!(dd_to_iso_dm(-55.5025), -5530.15);
assert_eq!(dd_to_iso_dms(55.5025), 553009.);
assert_eq!(dd_to_iso_dms(-55.51), -553036.);
assert_eq!(iso_dm_to_dd(5500.), 55.);
assert_eq!(iso_dm_to_dd(-5500.), -55.);
assert_eq!(iso_dm_to_dd(5530.60), -iso_dm_to_dd(-5530.60));
assert_eq!(iso_dms_to_dd(553036.), -iso_dms_to_dd(-553036.00));
}
#[test]
fn test_parse_sexagesimal() {
assert_eq!(1.51, parse_sexagesimal("1:30:36"));
assert_eq!(-1.51, parse_sexagesimal("-1:30:36"));
assert_eq!(1.51, parse_sexagesimal("1:30:36N"));
assert_eq!(-1.51, parse_sexagesimal("1:30:36S"));
assert_eq!(1.51, parse_sexagesimal("1:30:36e"));
assert_eq!(-1.51, parse_sexagesimal("1:30:36w"));
assert!(parse_sexagesimal("q1:30:36w").is_nan());
}
}