diff --git a/spatial-coordinate-systems/src/all.rs b/spatial-coordinate-systems/src/all.rs index 0964231..c01af50 100644 --- a/spatial-coordinate-systems/src/all.rs +++ b/spatial-coordinate-systems/src/all.rs @@ -14,6 +14,8 @@ use crate::{ LatLon, }; +#[derive(Debug)] +// TODO: Derive serialize #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Coordinates { pub latlon: LatLon, @@ -66,3 +68,15 @@ impl FromStr for Coordinates { Self::try_from(LatLon::from(&Coordinate::from_str(i)?)) } } + +// #[cfg(test)] +// mod tests { +// use super::*; + +// #[test] +// fn test_general() { +// dbg!(Coordinates::from_str( +// "69.79268710495744, -108.23886036872865" +// )); +// } +// } diff --git a/spatial-coordinate-systems/src/dmm.rs b/spatial-coordinate-systems/src/dmm.rs index dfc990f..cd0a187 100644 --- a/spatial-coordinate-systems/src/dmm.rs +++ b/spatial-coordinate-systems/src/dmm.rs @@ -127,7 +127,12 @@ impl DMM { } pub fn to_decimal_degrees(&self) -> f64 { - self.degrees as f64 + self.minutes / 60_f64 + (self.degrees as f64 + self.minutes / 60_f64) + * if self.direction.is_positive() { + 1.0_f64 + } else { + -1.0_f64 + } } } diff --git a/spatial-coordinate-systems/src/dms.rs b/spatial-coordinate-systems/src/dms.rs index 3714b8b..2052a80 100644 --- a/spatial-coordinate-systems/src/dms.rs +++ b/spatial-coordinate-systems/src/dms.rs @@ -18,20 +18,23 @@ impl Coordinate { pub fn parse(i: &str) -> IResult<&str, Self> { map_res( tuple((DMS::parse, optional_separator(','), space0, DMS::parse)), - |(lat, _, _, lon)| { - // 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 latlon = LatLon::new(lat.to_decimal_degrees(), lon.to_decimal_degrees())?; - Ok(Coordinate(lat, lon, latlon)) - } - }, + |(lat, _, _, lon)| Self::from_components(lat, lon), )(i) } + pub fn from_components(lat: DMS, lon: DMS) -> Result { + let (lat, lon) = (lat.normalize(), lon.normalize()); + // 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 latlon = LatLon::new(lat.to_decimal_degrees(), lon.to_decimal_degrees())?; + Ok(Coordinate(lat, lon, latlon)) + } + } + pub fn get_lat(&self) -> DMS { self.0 } @@ -106,6 +109,17 @@ impl DMS { )(i) } + pub fn normalize(&self) -> Self { + let (degrees, direction) = normalize_degrees_direction(self.degrees, self.direction); + + Self { + degrees, + minutes: self.minutes, + seconds: self.seconds, + direction, + } + } + pub fn from_decimal_degrees(d: f64, is_latitude: bool) -> Self { let degrees = d as i16; let minutes = d.abs().fract() * 60_f64; @@ -130,7 +144,12 @@ impl DMS { } pub fn to_decimal_degrees(&self) -> f64 { - self.degrees as f64 + self.minutes as f64 / 60_f64 + self.seconds / 3_600_f64 + (self.degrees as f64 + self.minutes as f64 / 60_f64 + self.seconds / 3_600_f64) + * if self.direction.is_positive() { + 1.0_f64 + } else { + -1.0_f64 + } } } diff --git a/spatial-coordinate-systems/src/skyvector.rs b/spatial-coordinate-systems/src/skyvector.rs index 1c4e63d..cd4b294 100644 --- a/spatial-coordinate-systems/src/skyvector.rs +++ b/spatial-coordinate-systems/src/skyvector.rs @@ -1,4 +1,8 @@ -use crate::{common::parse_direction, Direction, Error, LatLon}; +use crate::{ + common::parse_direction, + dms::{self, DMS}, + Direction, Error, LatLon, +}; use nom::{ bytes::complete::take, character::complete::{self, space0}, @@ -30,34 +34,45 @@ impl Coordinate { 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); + |(lat_d, lat_m, lat_s, lat_direction, _, _, _, lon_d, lon_m, lon_s, lon_direction)| -> Result<_, Error> { + let lat = DMS { + degrees: lat_d as i16, + minutes: lat_m.unwrap_or(0) as i16, + seconds: lat_s.unwrap_or(0) as f64, + direction: lat_direction, + }; + let lon = DMS { + degrees: lon_d as i16, + minutes: lon_m.unwrap_or(0) as i16, + seconds: lon_s.unwrap_or(0) as f64, + direction: lon_direction, + }; - let latlon = LatLon::new(lat, lon)?; - Ok(Coordinate(Self::latlon_to_string(&latlon), latlon)) - } + let dms = dms::Coordinate::from_components(lat, lon)?; + let latlon = LatLon::from(&dms); + + // 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 = DMS {}; + // LatLon::new(lat, lon)?; + Ok(Coordinate(Self::latlon_to_string(&latlon), latlon)) }, )(i) } /// Helper to convert u8, Option, Option into an f64 - fn dms_to_f64_helper(d: u8, m: Option, s: Option, 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 - } - } + // fn dms_to_f64_helper(d: u8, m: Option, s: Option, 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(i: &str) -> IResult<&str, u8> { @@ -96,7 +111,7 @@ impl FromStr for Coordinate { impl fmt::Display for Coordinate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}, {}", self.0, self.1) + write!(f, "{}", self.0) } } @@ -117,25 +132,25 @@ impl From for Coordinate { 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 - ); - } + // #[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 + // ); + // } #[test] fn test_general() { @@ -172,8 +187,9 @@ mod tests { Ok(Coordinate::from(LatLon::new(-90.0_f64, 100.0_f64).unwrap())) ); + // TODO: Are these truncated or rounded? assert_eq!( - Coordinate::from_str("697926N1082388W").unwrap().0, + Coordinate::from_str("694733N1081419W").unwrap().0, Coordinate::from(LatLon::new(69.79268710495744, -108.23886036872865).unwrap()).0 ) }