Start work on skyvector
This commit is contained in:
parent
7e3b7599c7
commit
083089ea51
@ -6,6 +6,7 @@ pub mod dmm;
|
|||||||
pub mod dms;
|
pub mod dms;
|
||||||
pub mod latlon;
|
pub mod latlon;
|
||||||
pub mod plus;
|
pub mod plus;
|
||||||
|
pub mod skyvector;
|
||||||
pub mod urls;
|
pub mod urls;
|
||||||
pub mod utm;
|
pub mod utm;
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
@ -50,10 +51,16 @@ impl Direction {
|
|||||||
pub fn is_lat(&self) -> bool {
|
pub fn is_lat(&self) -> bool {
|
||||||
self == &Direction::North || self == &Direction::South
|
self == &Direction::North || self == &Direction::South
|
||||||
}
|
}
|
||||||
|
|
||||||
/// True if this is east/west
|
/// True if this is east/west
|
||||||
pub fn is_lon(&self) -> bool {
|
pub fn is_lon(&self) -> bool {
|
||||||
self == &Direction::East || self == &Direction::West
|
self == &Direction::East || self == &Direction::West
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// True if this is "positive" (North of East)
|
||||||
|
pub fn is_positive(&self) -> bool {
|
||||||
|
self == &Self::North || self == &Self::East
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, EnumDiscriminants)]
|
#[derive(Debug, PartialEq, Clone, EnumDiscriminants)]
|
||||||
@ -71,6 +78,7 @@ pub enum Coordinate {
|
|||||||
UTM(utm::Coordinate),
|
UTM(utm::Coordinate),
|
||||||
// Xpin(xpin::Xpin),
|
// Xpin(xpin::Xpin),
|
||||||
Plus(plus::Coordinate),
|
Plus(plus::Coordinate),
|
||||||
|
Skyvector(skyvector::Coordinate),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Coordinate {
|
impl Coordinate {
|
||||||
@ -90,6 +98,7 @@ impl Coordinate {
|
|||||||
map(dd::Coordinate::parse, Coordinate::DD),
|
map(dd::Coordinate::parse, Coordinate::DD),
|
||||||
// map(xpin::Coordinate::parse, Cordinate::Xpin),
|
// map(xpin::Coordinate::parse, Cordinate::Xpin),
|
||||||
map(plus::Coordinate::parse, Coordinate::Plus),
|
map(plus::Coordinate::parse, Coordinate::Plus),
|
||||||
|
map(skyvector::Coordinate::parse, Coordinate::Skyvector),
|
||||||
// Try to parse as a URL last
|
// Try to parse as a URL last
|
||||||
map(urls::CoordinateUrls::parse, |coordinate_urls| {
|
map(urls::CoordinateUrls::parse, |coordinate_urls| {
|
||||||
Coordinate::DD(dd::Coordinate::from(coordinate_urls.latlon))
|
Coordinate::DD(dd::Coordinate::from(coordinate_urls.latlon))
|
||||||
@ -111,6 +120,7 @@ impl Coordinate {
|
|||||||
CoordinateType::DMM => Self::DMM(dmm::Coordinate::from(lat_lon)),
|
CoordinateType::DMM => Self::DMM(dmm::Coordinate::from(lat_lon)),
|
||||||
CoordinateType::UTM => Self::UTM(utm::Coordinate::try_from(lat_lon)?),
|
CoordinateType::UTM => Self::UTM(utm::Coordinate::try_from(lat_lon)?),
|
||||||
CoordinateType::Plus => Self::Plus(plus::Coordinate::try_from(lat_lon)?),
|
CoordinateType::Plus => Self::Plus(plus::Coordinate::try_from(lat_lon)?),
|
||||||
|
CoordinateType::Skyvector => Self::Skyvector(skyvector::Coordinate::from(lat_lon)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +132,7 @@ impl Coordinate {
|
|||||||
Self::UTM(_) => CoordinateType::UTM,
|
Self::UTM(_) => CoordinateType::UTM,
|
||||||
// Self::Xpin(_) => CoordinateType::Xpin,
|
// Self::Xpin(_) => CoordinateType::Xpin,
|
||||||
Self::Plus(_) => CoordinateType::Plus,
|
Self::Plus(_) => CoordinateType::Plus,
|
||||||
|
Self::Skyvector(_) => CoordinateType::Skyvector,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,6 +152,7 @@ impl From<&Coordinate> for LatLon {
|
|||||||
Coordinate::UTM(utm) => utm.into(),
|
Coordinate::UTM(utm) => utm.into(),
|
||||||
// Coordinate::Xpin(xpin) => xpin.into(),
|
// Coordinate::Xpin(xpin) => xpin.into(),
|
||||||
Coordinate::Plus(plus) => plus.into(),
|
Coordinate::Plus(plus) => plus.into(),
|
||||||
|
Coordinate::Skyvector(skyvector) => skyvector.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,6 +196,11 @@ impl From<plus::Coordinate> for Coordinate {
|
|||||||
Self::Plus(c)
|
Self::Plus(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<skyvector::Coordinate> for Coordinate {
|
||||||
|
fn from(c: skyvector::Coordinate) -> Self {
|
||||||
|
Self::Skyvector(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// impl TryFrom<Coordinate> for dms::Coordinate {
|
// impl TryFrom<Coordinate> for dms::Coordinate {
|
||||||
// type Error = Error;
|
// type Error = Error;
|
||||||
@ -218,6 +235,7 @@ impl fmt::Display for Coordinate {
|
|||||||
Coordinate::DMS(dms) => write!(f, "{}", dms),
|
Coordinate::DMS(dms) => write!(f, "{}", dms),
|
||||||
Coordinate::UTM(utm) => write!(f, "{}", utm),
|
Coordinate::UTM(utm) => write!(f, "{}", utm),
|
||||||
Coordinate::Plus(plus) => write!(f, "{}", plus),
|
Coordinate::Plus(plus) => write!(f, "{}", plus),
|
||||||
|
Coordinate::Skyvector(skyvector) => write!(f, "{}", skyvector),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
183
spatial-coordinate-systems/src/skyvector.rs
Normal file
183
spatial-coordinate-systems/src/skyvector.rs
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
use crate::{
|
||||||
|
common::{optional_separator, parse_direction, parse_f64},
|
||||||
|
dms::DMS,
|
||||||
|
Direction, Error, LatLon,
|
||||||
|
};
|
||||||
|
use nom::{
|
||||||
|
branch::alt,
|
||||||
|
bytes::complete::take,
|
||||||
|
bytes::complete::take_while1,
|
||||||
|
character::complete::{self, digit1, space0},
|
||||||
|
combinator::{map, map_parser, map_res, opt},
|
||||||
|
sequence::tuple,
|
||||||
|
IResult,
|
||||||
|
};
|
||||||
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug, Clone)]
|
||||||
|
pub struct Coordinate(String, LatLon);
|
||||||
|
|
||||||
|
impl Coordinate {
|
||||||
|
pub fn parse(i: &str) -> IResult<&str, Self> {
|
||||||
|
map_res(
|
||||||
|
tuple((
|
||||||
|
// Lat
|
||||||
|
Self::parse_n_digits::<2>,
|
||||||
|
opt(Self::parse_n_digits::<2>),
|
||||||
|
opt(Self::parse_n_digits::<2>),
|
||||||
|
parse_direction,
|
||||||
|
// Separator
|
||||||
|
space0,
|
||||||
|
complete::char('/'),
|
||||||
|
space0,
|
||||||
|
// Lon
|
||||||
|
Self::parse_n_digits::<3>,
|
||||||
|
opt(Self::parse_n_digits::<2>),
|
||||||
|
opt(Self::parse_n_digits::<2>),
|
||||||
|
parse_direction,
|
||||||
|
)),
|
||||||
|
|(lat_d, lat_m, lat_s, lat_direction, _, _, _, lon_d, lon_m, lon_s, lon_direction)| {
|
||||||
|
// Ensure this is a north/south then east/west direction
|
||||||
|
if !lat_direction.is_lat() {
|
||||||
|
Err(Error::InvalidLatitudeBearing(lat_direction))
|
||||||
|
} else if !lon_direction.is_lon() {
|
||||||
|
Err(Error::InvalidLongitudeBearing(lon_direction))
|
||||||
|
} else {
|
||||||
|
let lat = Self::dms_to_f64_helper(lat_d, lat_m, lat_s, &lat_direction);
|
||||||
|
let lon = Self::dms_to_f64_helper(lon_d, lon_m, lon_s, &lon_direction);
|
||||||
|
|
||||||
|
let latlon = LatLon::new(lat, lon)?;
|
||||||
|
// Ok(Coordinate(..., latlon))
|
||||||
|
|
||||||
|
// let latlon = LatLon::new(lat.to_decimal_degrees(), lon.to_decimal_degrees())?;
|
||||||
|
// Ok(Coordinate(lat, lon, latlon))
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper to convert u8, Option<u8>, Option<u8> into an f64
|
||||||
|
fn dms_to_f64_helper(d: u8, m: Option<u8>, s: Option<u8>, direction: &Direction) -> f64 {
|
||||||
|
(d as f64
|
||||||
|
+ m.map(|m| m as f64 / 100.0_f64).unwrap_or(0.0_f64)
|
||||||
|
+ s.map(|s| s as f64 / 10_000.0_f64).unwrap_or(0.0_f64))
|
||||||
|
* if direction.is_positive() {
|
||||||
|
1.0_f64
|
||||||
|
} else {
|
||||||
|
-1.0_f64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes n digits from the input and parses it as a u8
|
||||||
|
fn parse_n_digits<const C: u8>(i: &str) -> IResult<&str, u8> {
|
||||||
|
map_parser(take(C), complete::u8)(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn latlon_to_string(latlon: &LatLon) -> String {
|
||||||
|
let dms = crate::dms::Coordinate::from(latlon.clone());
|
||||||
|
let lat = dms.get_lat();
|
||||||
|
let lon = dms.get_lon();
|
||||||
|
format!(
|
||||||
|
"{}{}{}{}{}{}{}{}",
|
||||||
|
lat.degrees,
|
||||||
|
lat.minutes,
|
||||||
|
lat.seconds,
|
||||||
|
lat.direction,
|
||||||
|
lon.degrees,
|
||||||
|
lon.minutes,
|
||||||
|
lon.seconds,
|
||||||
|
lon.direction,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Coordinate {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(i: &str) -> Result<Self, Self::Err> {
|
||||||
|
Self::parse(i).map_err(Into::into).map(|(_, ret)| ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Coordinate {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}, {}", self.0, self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Coordinate> for LatLon {
|
||||||
|
fn from(coordinate: &Coordinate) -> LatLon {
|
||||||
|
coordinate.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LatLon> for Coordinate {
|
||||||
|
fn from(latlon: LatLon) -> Self {
|
||||||
|
// TODO: Encode the source and target
|
||||||
|
todo!()
|
||||||
|
// Self(ret, latlon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_helpers() {
|
||||||
|
assert_eq!(
|
||||||
|
Coordinate::dms_to_f64_helper(90, None, None, &Direction::North),
|
||||||
|
90.0000_f64
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Coordinate::dms_to_f64_helper(90, None, None, &Direction::South),
|
||||||
|
-90.0000_f64
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Coordinate::dms_to_f64_helper(90, Some(12), Some(8), &Direction::North),
|
||||||
|
90.1208_f64
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Coordinate::dms_to_f64_helper(90, None, Some(8), &Direction::North),
|
||||||
|
90.0008_f64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn test_general() {
|
||||||
|
// macro_rules! p {
|
||||||
|
// ($tt:tt) => {{
|
||||||
|
// let cvt = Coordinate::from_str($tt);
|
||||||
|
// eprintln!("Testing: {} => {:?}", $tt, cvt);
|
||||||
|
// assert!(cvt.is_ok());
|
||||||
|
// }};
|
||||||
|
// ($tt:tt, DMS) => {{
|
||||||
|
// let cvt = DMS::parse($tt);
|
||||||
|
// eprintln!("Testing: {} => {:?}", $tt, cvt);
|
||||||
|
// assert!(cvt.is_ok());
|
||||||
|
// }};
|
||||||
|
// }
|
||||||
|
// // p!(r#"0° 0' 0" N 100° 30' 1" W"#);
|
||||||
|
// // p!(r#"0 0 0 N 100 30 1 W"#);
|
||||||
|
|
||||||
|
// p!("0° 0' N", DMS);
|
||||||
|
// p!("0° 0'N", DMS);
|
||||||
|
// p!("N 0° 0'", DMS);
|
||||||
|
|
||||||
|
// p!("0° 0' N", DMS);
|
||||||
|
// p!("0° 0'N", DMS);
|
||||||
|
// p!("N 0° 0' 0", DMS);
|
||||||
|
|
||||||
|
// p!(r#"E 100° 30'"#, DMS);
|
||||||
|
// p!(r#"N 0° 0' E 100° 30'"#);
|
||||||
|
|
||||||
|
// // parse_dmm_numeric(r#"38 53.2148425"#).unwrap();
|
||||||
|
|
||||||
|
// // p!(r#"38 53.2148425"#, DMS);
|
||||||
|
// // p!(r#"38 53.2148425'"#, DMS);
|
||||||
|
// // p!(r#"38° 53.2148425"#, DMS);
|
||||||
|
// // p!(r#"38° 53.2148425'"#, DMS);
|
||||||
|
// // p!(r#"-77° -1.7611312866219464'"#, DMS);
|
||||||
|
// // p!(r#"38° 53.2148425', -77° -1.7611312866219464'"#);
|
||||||
|
// }
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user