From 481c8434d9b24b84d313be9c611c5dfbe5468719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 24 Apr 2020 00:11:24 +0200 Subject: [PATCH] readme, code structuring --- Cargo.toml | 8 +- README.md | 43 +++ src/errors.rs | 27 ++ src/lib.rs | 729 ++++++++++++-------------------------------------- src/tests.rs | 303 +++++++++++++++++++++ 5 files changed, 546 insertions(+), 564 deletions(-) create mode 100644 README.md create mode 100644 src/errors.rs create mode 100644 src/tests.rs diff --git a/Cargo.toml b/Cargo.toml index ec476bf..698cc59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,12 +2,14 @@ name = "latlon" version = "0.1.0" authors = ["Ondřej Hruška "] +description = "Parse latitude/longitude from many common formats" edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +readme = "README.md" +keywords = ["gps", "geo", "geography", "geospatial", "location", "parse"] +categories = ["parser-implementations", "science"] +license = "MIT" [dependencies] geo-types = "0.5.0" regex = "1.3.7" lazy_static = "1.4.0" - diff --git a/README.md b/README.md new file mode 100644 index 0000000..1da26dd --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# latlon + +Parse geographic coordinates from string. + +A wide range of commonly used formats is supported. +See the unit tests for a complete reference. + +If a format you need is missing, please submit a merge request (including unit tests). + +## Usage + +```rust +// parse a coord +let coord : geo::Point = latlon::parse("N 50°5.30385', E 14°26.94732'").unwrap(); + +// individual lat/lng parsing +let lat : f64 = latlon::parse_lat("N 50°5.30385'").unwrap(); +let lng : f64 = latlon::parse_lng("E 14°26.94732'").unwrap(); +``` + +## Supported formats + +Example of supported formats: + +- `40° 26′ 46″ N 79° 58′ 56″ W` +- `N 40° 26′ 46″ W 79° 58′ 56″` +- `40° 26.767' N 79° 58.933' W` +- `40° 26′ 46″ 79° 58′ 56″`, `40° 26′ 46″, 79° 58′ 56″`, ... +- `N 40° 26.767' W 79° 58.933'` +- `40° 26.767' 79° 58.933'`, `40° 26.767', 79° 58.933'`, ... +- `N 40.446° W 79.982°` +- `40.446° N 79.982° W` +- `40.446° 79.982°`, `40.446,79.982`, etc. + +## Parser rules +- All formats support negative degrees (preceded by a minus sign). Positive latitude is North, positive longitude is East. +- Whitespace is optional and ignored, except for formats that would become unparsable. +- Degree, minute and second symbols can be omitted. +- Comma (`,`) may be used as an alternate decimal separator. +- Unicode quotes (`’`, `”`) may be used in place of apostrophe and double quote (`'`, `"`) + for minutes and seconds. +- The two coordinates can be separated by comma (`,`), semicolon (`;`), whitespace (` `), or nothing + at all, if not ambiguous. diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..241ad92 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,27 @@ +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::num::ParseFloatError; + +#[derive(Debug)] +pub(crate) struct ParseErrorInternal; + +impl From for ParseErrorInternal { + fn from(_: ParseFloatError) -> Self { + ParseErrorInternal + } +} + +impl Display for ParseErrorInternal { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("Parse error") + } +} + +#[derive(Debug)] +pub struct GeoParseError + Display>(pub T); + +impl + Display> Display for GeoParseError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "Error parsing coordinates from {}", self.0) + } +} diff --git a/src/lib.rs b/src/lib.rs index b87b57d..4cfd0f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,51 +4,20 @@ extern crate lazy_static; use geo_types::Point; use regex::Regex; -use std::fmt::{Display, Formatter}; use std::convert::TryFrom; -use std::fmt; +use std::fmt::Display; use std::num::ParseFloatError; -pub mod errors { - use std::fmt::{Display, Formatter}; - use std::fmt; - use std::num::ParseFloatError; - - #[derive(Debug)] - pub(crate) struct ParseErrorInternal; - - impl From for ParseErrorInternal { - fn from(_: ParseFloatError) -> Self { - ParseErrorInternal - } - } - - impl Display for ParseErrorInternal { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_str("Parse error") - } - } - - - #[derive(Debug)] - pub struct GeoParseError + Display>(pub T); - - impl + Display> Display for GeoParseError { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "Error parsing coordinates from {}", self.0) - } - } -} +#[cfg(test)] +mod tests; +mod errors; -use crate::errors::ParseErrorInternal; pub use crate::errors::GeoParseError; +use crate::errors::ParseErrorInternal; // Two-sided patterns lazy_static! { // 40° 26′ 46″ N 79° 58′ 56″ W - // 40 26 46 N 79 58 56 W - // -40 26 46 N -79 58 56 W - // (spaces optional if not ambiguous) static ref RE_DMS_NS_DMS_EW: Regex = Regex::new(r"(?x) ^ (-?\d{1,2})(?:°\s*|\s+) @@ -66,10 +35,6 @@ lazy_static! { ").unwrap(); // N 40° 26′ 46″ W 79° 58′ 56″ - // N 40° 26’ 46″ W 79° 58’ 56″ - // N 40 26 46 W 79 58 56 - // N -40 26 46 W -79 58 56 - // (spaces optional if not ambiguous) static ref RE_NS_DMS_EW_DMS: Regex = Regex::new(r"(?x) ^ (N|S)\s* @@ -87,9 +52,6 @@ lazy_static! { ").unwrap(); // 40° 26′ 46″ 79° 58′ 56″ - // 40 26 46 79 58 56 (strange, but not ambiguous) - // -40 26 46; -79 58 56 - // (spaces optional if not ambiguous) static ref RE_DMS_DMS: Regex = Regex::new(r"(?x) ^ (-?\d{1,2})(?:°\s*|\s+) @@ -104,11 +66,6 @@ lazy_static! { ").unwrap(); // 40° 26.767' N 79° 58.933' W - // 40° 26.767’ N 79° 58.933’ W - // 40 26.767 N 79 58.933 W - // 40 26,767 N 79 58,933 W - // -40 26,767 N -79 58,933 W - // (spaces optional if not ambiguous) static ref RE_DM_NS_DM_EW: Regex = Regex::new(r"(?x) ^ (-?\d{1,2})(?:°\s*|\s+) @@ -124,11 +81,6 @@ lazy_static! { ").unwrap(); // N 40° 26.767' W 79° 58.933' - // N 40° 26.767’ W 79° 58.933’ - // N 40 26.767 W 79 58.933 - // N 40 26,767 W 79 58,933 - // N -40 26,767 W -79 58,933 - // (spaces optional if not ambiguous) static ref RE_NS_DM_EW_DM: Regex = Regex::new(r"(?x) ^ (N|S)\s* @@ -144,11 +96,6 @@ lazy_static! { ").unwrap(); // 40° 26.767' 79° 58.933' - // 40° 26.767’ 79° 58.933’ - // 40 26.767 79 58.933 - // 40 26,767 79 58,933 - // -40 26,767 -79 58,933 - // (spaces optional if not ambiguous) static ref RE_DM_DM: Regex = Regex::new(r"(?x) ^ (-?\d{1,2})(?:°\s*|\s+) @@ -162,10 +109,6 @@ lazy_static! { ").unwrap(); // N 40.446° W 79.982° - // N 40.446 W 79.982 - // N 40,446 W 79,982 - // N -40,446 W -79,982 - // (spaces optional) static ref RE_NS_D_EW_D: Regex = Regex::new(r"(?x) ^ (N|S)\s* @@ -179,10 +122,6 @@ lazy_static! { ").unwrap(); // 40.446° N 79.982° W - // 40.446 N 79.982 W - // 40,446 N 79,982 W - // -40,446 N -79,982 W - // (spaces optional) static ref RE_D_NS_D_EW: Regex = Regex::new(r"(?x) ^ (-?\d{1,2}(?:[.,]\d+)?)°?\s* @@ -195,15 +134,6 @@ lazy_static! { ").unwrap(); // 40.446° 79.982° - // 40.446°, 79.982° - // 40.446°; 79.982° - // 40.446 79.982 - // 40.446; 79.982 - // 40.446, 79.982 - // 40,446 79,982 - // 40,446, 79,982 - // -40,446 -79,982 - // (spaces optional if not ambiguous) static ref RE_D_D: Regex = Regex::new(r"(?x) ^ (-?\d{1,2}(?:[.,]\d+)?)(?:°\s*[,;]?\s*|\s*[,;]\s*|\s+) @@ -294,18 +224,18 @@ lazy_static! { } /// Parsed degrees, minutes, seconds -#[derive(Debug)] +#[derive(Debug, Clone)] struct DMS { d: f64, m: f64, - s: f64 + s: f64, } // North / South -#[derive(Debug,Clone,Copy,Eq,PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] enum NS { North, - South + South, } impl NS { @@ -318,15 +248,7 @@ impl NS { } } -impl Display for NS { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_str(match self { - NS::North => "N", - NS::South => "S", - }) - } -} - +// Parsing from string impl<'a> TryFrom<&str> for NS { type Error = ParseErrorInternal; @@ -334,16 +256,16 @@ impl<'a> TryFrom<&str> for NS { match value { "N" => Ok(NS::North), "S" => Ok(NS::South), - _ => Err(ParseErrorInternal) + _ => Err(ParseErrorInternal), } } } /// East / West -#[derive(Debug,Clone,Copy,Eq,PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] enum EW { East, - West + West, } impl EW { @@ -355,15 +277,7 @@ impl EW { } } -impl Display for EW { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_str(match self { - EW::East => "E", - EW::West => "W", - }) - } -} - +// Parsing from string impl<'a> TryFrom<&str> for EW { type Error = ParseErrorInternal; @@ -371,7 +285,7 @@ impl<'a> TryFrom<&str> for EW { match value { "E" => Ok(EW::East), "W" => Ok(EW::West), - _ => Err(ParseErrorInternal) + _ => Err(ParseErrorInternal), } } } @@ -404,7 +318,7 @@ impl<'a> TryFrom<&str> for EW { /// # Returns /// Returns a `Point` with longitude as X and latitude as Y (natural map orientation), or /// a parse error wrapping the source string (for zero-copy patterns) -pub fn parse + Display>(text : T) -> Result, GeoParseError> { +pub fn parse + Display>(text: T) -> Result, GeoParseError> { let s = text.as_ref().trim(); match do_parse(s) { @@ -416,7 +330,7 @@ pub fn parse + Display>(text : T) -> Result, GeoParseE /// Parse string as latitude (N/S). Positive latitude is North. /// /// See `parse()` for supported formats. -pub fn parse_lat + Display>(text : T) -> Result> { +pub fn parse_lat + Display>(text: T) -> Result> { let s = text.as_ref().trim(); match do_parse_lat(s) { @@ -428,7 +342,7 @@ pub fn parse_lat + Display>(text : T) -> Result + Display>(text : T) -> Result> { +pub fn parse_lng + Display>(text: T) -> Result> { let s = text.as_ref().trim(); match do_parse_lng(s) { @@ -437,6 +351,7 @@ pub fn parse_lng + Display>(text : T) -> Result Result; } @@ -452,21 +367,20 @@ impl<'a> ParseFloatWithComma for &'a str { } } -fn build_point(lat: DMS, ns : NS, lng: DMS, ew : EW) -> Result, ParseErrorInternal> { - Ok(Point::new( - build_lng(lng, ew)?, - build_lat(lat, ns)? - )) +/// Validate and compose a complete coordinate (Lat Lng). +fn build_point(lat: DMS, ns: NS, lng: DMS, ew: EW) -> Result, ParseErrorInternal> { + Ok(Point::new(build_lng(lng, ew)?, build_lat(lat, ns)?)) } -fn build_lng(mut lng: DMS, mut ew : EW) -> Result { +/// Validate and compose Lng. +fn build_lng(mut lng: DMS, mut ew: EW) -> Result { // the minus sign must go in front of the whole coordinate, not just degrees! if lng.d < 0. { ew = ew.invert(); lng.d = -lng.d; } - let mut lng_f: f64 = lng.d + (lng.m/60f64) + (lng.s/3600f64); + let mut lng_f: f64 = lng.d + (lng.m / 60f64) + (lng.s / 3600f64); if ew == EW::West { lng_f = -lng_f; @@ -479,14 +393,15 @@ fn build_lng(mut lng: DMS, mut ew : EW) -> Result { Ok(lng_f) } -fn build_lat(mut lat: DMS, mut ns : NS) -> Result { +/// Validate and compose Lat +fn build_lat(mut lat: DMS, mut ns: NS) -> Result { // the minus sign must go in front of the whole coordinate, not just degrees! if lat.d < 0. { ns = ns.invert(); lat.d = -lat.d; } - let mut lat_f: f64 = lat.d + (lat.m/60f64) + (lat.s/3600f64); + let mut lat_f: f64 = lat.d + (lat.m / 60f64) + (lat.s / 3600f64); if ns == NS::South { lat_f = -lat_f; @@ -499,12 +414,54 @@ fn build_lat(mut lat: DMS, mut ns : NS) -> Result { Ok(lat_f) } -fn do_parse(s : &str) -> Result, ParseErrorInternal> { +/// Parse a complete coordinate (Lat Lng). +/// Patterns are ordered by subjective frequency of use. +fn do_parse(s: &str) -> Result, ParseErrorInternal> { + if let Some(cap) = RE_D_NS_D_EW.captures(s) { + let lat = DMS { + d: cap.get(1).unwrap().as_str().parse_allow_comma()?, + m: 0., + s: 0., + }; + + let ns = NS::try_from(cap.get(2).unwrap().as_str())?; + + let lng = DMS { + d: cap.get(3).unwrap().as_str().parse_allow_comma()?, + m: 0., + s: 0., + }; + + let ew = EW::try_from(cap.get(4).unwrap().as_str())?; + + return build_point(lat, ns, lng, ew); + } + + if let Some(cap) = RE_NS_DM_EW_DM.captures(s) { + let ns = NS::try_from(cap.get(1).unwrap().as_str())?; + + let lat = DMS { + d: cap.get(2).unwrap().as_str().parse()?, + m: cap.get(3).unwrap().as_str().parse_allow_comma()?, + s: 0., + }; + + let ew = EW::try_from(cap.get(4).unwrap().as_str())?; + + let lng = DMS { + d: cap.get(5).unwrap().as_str().parse()?, + m: cap.get(6).unwrap().as_str().parse_allow_comma()?, + s: 0., + }; + + return build_point(lat, ns, lng, ew); + } + if let Some(cap) = RE_DMS_NS_DMS_EW.captures(s) { let lat = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse()?, - s: cap.get(3).unwrap().as_str().parse()? + s: cap.get(3).unwrap().as_str().parse()?, }; let ns = NS::try_from(cap.get(4).unwrap().as_str())?; @@ -512,7 +469,7 @@ fn do_parse(s : &str) -> Result, ParseErrorInternal> { let lng = DMS { d: cap.get(5).unwrap().as_str().parse()?, m: cap.get(6).unwrap().as_str().parse()?, - s: cap.get(7).unwrap().as_str().parse()? + s: cap.get(7).unwrap().as_str().parse()?, }; let ew = EW::try_from(cap.get(8).unwrap().as_str())?; @@ -520,13 +477,33 @@ fn do_parse(s : &str) -> Result, ParseErrorInternal> { return build_point(lat, ns, lng, ew); } + if let Some(cap) = RE_D_D.captures(s) { + let lat = DMS { + d: cap.get(1).unwrap().as_str().parse_allow_comma()?, + m: 0., + s: 0., + }; + + let ns = NS::North; + + let lng = DMS { + d: cap.get(2).unwrap().as_str().parse_allow_comma()?, + m: 0., + s: 0., + }; + + let ew = EW::East; + + return build_point(lat, ns, lng, ew); + } + if let Some(cap) = RE_NS_DMS_EW_DMS.captures(s) { let ns = NS::try_from(cap.get(1).unwrap().as_str())?; let lat = DMS { d: cap.get(2).unwrap().as_str().parse()?, m: cap.get(3).unwrap().as_str().parse()?, - s: cap.get(4).unwrap().as_str().parse()? + s: cap.get(4).unwrap().as_str().parse()?, }; let ew = EW::try_from(cap.get(5).unwrap().as_str())?; @@ -534,7 +511,7 @@ fn do_parse(s : &str) -> Result, ParseErrorInternal> { let lng = DMS { d: cap.get(6).unwrap().as_str().parse()?, m: cap.get(7).unwrap().as_str().parse()?, - s: cap.get(8).unwrap().as_str().parse()? + s: cap.get(8).unwrap().as_str().parse()?, }; return build_point(lat, ns, lng, ew); @@ -547,13 +524,13 @@ fn do_parse(s : &str) -> Result, ParseErrorInternal> { let lat = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse()?, - s: cap.get(3).unwrap().as_str().parse()? + s: cap.get(3).unwrap().as_str().parse()?, }; let lng = DMS { d: cap.get(4).unwrap().as_str().parse()?, m: cap.get(5).unwrap().as_str().parse()?, - s: cap.get(6).unwrap().as_str().parse()? + s: cap.get(6).unwrap().as_str().parse()?, }; return build_point(lat, ns, lng, ew); @@ -563,7 +540,7 @@ fn do_parse(s : &str) -> Result, ParseErrorInternal> { let lat = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse_allow_comma()?, - s: 0. + s: 0., }; let ns = NS::try_from(cap.get(3).unwrap().as_str())?; @@ -571,7 +548,7 @@ fn do_parse(s : &str) -> Result, ParseErrorInternal> { let lng = DMS { d: cap.get(4).unwrap().as_str().parse()?, m: cap.get(5).unwrap().as_str().parse_allow_comma()?, - s: 0. + s: 0., }; let ew = EW::try_from(cap.get(6).unwrap().as_str())?; @@ -579,26 +556,6 @@ fn do_parse(s : &str) -> Result, ParseErrorInternal> { return build_point(lat, ns, lng, ew); } - if let Some(cap) = RE_NS_DM_EW_DM.captures(s) { - let ns = NS::try_from(cap.get(1).unwrap().as_str())?; - - let lat = DMS { - d: cap.get(2).unwrap().as_str().parse()?, - m: cap.get(3).unwrap().as_str().parse_allow_comma()?, - s: 0. - }; - - let ew = EW::try_from(cap.get(4).unwrap().as_str())?; - - let lng = DMS { - d: cap.get(5).unwrap().as_str().parse()?, - m: cap.get(6).unwrap().as_str().parse_allow_comma()?, - s: 0. - }; - - return build_point(lat, ns, lng, ew); - } - if let Some(cap) = RE_DM_DM.captures(s) { let ns = NS::North; let ew = EW::East; @@ -606,13 +563,13 @@ fn do_parse(s : &str) -> Result, ParseErrorInternal> { let lat = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse_allow_comma()?, - s: 0. + s: 0., }; let lng = DMS { d: cap.get(3).unwrap().as_str().parse()?, m: cap.get(4).unwrap().as_str().parse_allow_comma()?, - s: 0. + s: 0., }; return build_point(lat, ns, lng, ew); @@ -624,7 +581,7 @@ fn do_parse(s : &str) -> Result, ParseErrorInternal> { let lat = DMS { d: cap.get(2).unwrap().as_str().parse_allow_comma()?, m: 0., - s: 0. + s: 0., }; let ew = EW::try_from(cap.get(3).unwrap().as_str())?; @@ -632,63 +589,47 @@ fn do_parse(s : &str) -> Result, ParseErrorInternal> { let lng = DMS { d: cap.get(4).unwrap().as_str().parse_allow_comma()?, m: 0., - s: 0. + s: 0., }; return build_point(lat, ns, lng, ew); } + Err(ParseErrorInternal) +} - if let Some(cap) = RE_D_NS_D_EW.captures(s) { +/// Parse Lat. +/// Patterns are ordered by subjective frequency of use. +fn do_parse_lat(s: &str) -> Result { + if let Some(cap) = RE_D_NSEW.captures(s) { let lat = DMS { d: cap.get(1).unwrap().as_str().parse_allow_comma()?, m: 0., - s: 0. + s: 0., }; let ns = NS::try_from(cap.get(2).unwrap().as_str())?; - let lng = DMS { - d: cap.get(3).unwrap().as_str().parse_allow_comma()?, - m: 0., - s: 0. - }; - - let ew = EW::try_from(cap.get(4).unwrap().as_str())?; - - return build_point(lat, ns, lng, ew); + return build_lat(lat, ns); } + if let Some(cap) = RE_NSEW_DM.captures(s) { + let ns = NS::try_from(cap.get(1).unwrap().as_str())?; - if let Some(cap) = RE_D_D.captures(s) { let lat = DMS { - d: cap.get(1).unwrap().as_str().parse_allow_comma()?, - m: 0., - s: 0. - }; - - let ns = NS::North; - - let lng = DMS { - d: cap.get(2).unwrap().as_str().parse_allow_comma()?, - m: 0., - s: 0. + d: cap.get(2).unwrap().as_str().parse()?, + m: cap.get(3).unwrap().as_str().parse_allow_comma()?, + s: 0., }; - let ew = EW::East; - - return build_point(lat, ns, lng, ew); + return build_lat(lat, ns); } - Err(ParseErrorInternal) -} - -fn do_parse_lat(s : &str) -> Result { if let Some(cap) = RE_DMS_NSEW.captures(s) { let lat = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse()?, - s: cap.get(3).unwrap().as_str().parse()? + s: cap.get(3).unwrap().as_str().parse()?, }; let ns = NS::try_from(cap.get(4).unwrap().as_str())?; @@ -696,13 +637,25 @@ fn do_parse_lat(s : &str) -> Result { return build_lat(lat, ns); } + if let Some(cap) = RE_D.captures(s) { + let lat = DMS { + d: cap.get(1).unwrap().as_str().parse_allow_comma()?, + m: 0., + s: 0., + }; + + let ns = NS::North; + + return build_lat(lat, ns); + } + if let Some(cap) = RE_NSEW_DMS.captures(s) { let ns = NS::try_from(cap.get(1).unwrap().as_str())?; let lat = DMS { d: cap.get(2).unwrap().as_str().parse()?, m: cap.get(3).unwrap().as_str().parse()?, - s: cap.get(4).unwrap().as_str().parse()? + s: cap.get(4).unwrap().as_str().parse()?, }; return build_lat(lat, ns); @@ -714,7 +667,7 @@ fn do_parse_lat(s : &str) -> Result { let lat = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse()?, - s: cap.get(3).unwrap().as_str().parse()? + s: cap.get(3).unwrap().as_str().parse()?, }; return build_lat(lat, ns); @@ -724,7 +677,7 @@ fn do_parse_lat(s : &str) -> Result { let lat = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse_allow_comma()?, - s: 0. + s: 0., }; let ns = NS::try_from(cap.get(3).unwrap().as_str())?; @@ -732,25 +685,13 @@ fn do_parse_lat(s : &str) -> Result { return build_lat(lat, ns); } - if let Some(cap) = RE_NSEW_DM.captures(s) { - let ns = NS::try_from(cap.get(1).unwrap().as_str())?; - - let lat = DMS { - d: cap.get(2).unwrap().as_str().parse()?, - m: cap.get(3).unwrap().as_str().parse_allow_comma()?, - s: 0. - }; - - return build_lat(lat, ns); - } - if let Some(cap) = RE_DM.captures(s) { let ns = NS::North; let lat = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse_allow_comma()?, - s: 0. + s: 0., }; return build_lat(lat, ns); @@ -762,47 +703,47 @@ fn do_parse_lat(s : &str) -> Result { let lat = DMS { d: cap.get(2).unwrap().as_str().parse_allow_comma()?, m: 0., - s: 0. + s: 0., }; return build_lat(lat, ns); } + Err(ParseErrorInternal) +} +/// Parse Lng. +/// Patterns are ordered by subjective frequency of use. +fn do_parse_lng(s: &str) -> Result { if let Some(cap) = RE_D_NSEW.captures(s) { - let lat = DMS { + let lng = DMS { d: cap.get(1).unwrap().as_str().parse_allow_comma()?, m: 0., - s: 0. + s: 0., }; - let ns = NS::try_from(cap.get(2).unwrap().as_str())?; + let ew = EW::try_from(cap.get(2).unwrap().as_str())?; - return build_lat(lat, ns); + return build_lng(lng, ew); } + if let Some(cap) = RE_NSEW_D.captures(s) { + let ew = EW::try_from(cap.get(1).unwrap().as_str())?; - if let Some(cap) = RE_D.captures(s) { - let lat = DMS { - d: cap.get(1).unwrap().as_str().parse_allow_comma()?, + let lng = DMS { + d: cap.get(2).unwrap().as_str().parse_allow_comma()?, m: 0., - s: 0. + s: 0., }; - let ns = NS::North; - - return build_lat(lat, ns); + return build_lng(lng, ew); } - Err(ParseErrorInternal) -} - -fn do_parse_lng(s : &str) -> Result { if let Some(cap) = RE_DMS_NSEW.captures(s) { let lng = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse()?, - s: cap.get(3).unwrap().as_str().parse()? + s: cap.get(3).unwrap().as_str().parse()?, }; let ew = EW::try_from(cap.get(4).unwrap().as_str())?; @@ -810,13 +751,25 @@ fn do_parse_lng(s : &str) -> Result { return build_lng(lng, ew); } + if let Some(cap) = RE_D.captures(s) { + let lng = DMS { + d: cap.get(1).unwrap().as_str().parse_allow_comma()?, + m: 0., + s: 0., + }; + + let ew = EW::East; + + return build_lng(lng, ew); + } + if let Some(cap) = RE_NSEW_DMS.captures(s) { let ew = EW::try_from(cap.get(1).unwrap().as_str())?; let lng = DMS { d: cap.get(2).unwrap().as_str().parse()?, m: cap.get(3).unwrap().as_str().parse()?, - s: cap.get(4).unwrap().as_str().parse()? + s: cap.get(4).unwrap().as_str().parse()?, }; return build_lng(lng, ew); @@ -828,7 +781,7 @@ fn do_parse_lng(s : &str) -> Result { let lng = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse()?, - s: cap.get(3).unwrap().as_str().parse()? + s: cap.get(3).unwrap().as_str().parse()?, }; return build_lng(lng, ew); @@ -838,7 +791,7 @@ fn do_parse_lng(s : &str) -> Result { let lng = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse_allow_comma()?, - s: 0. + s: 0., }; let ew = EW::try_from(cap.get(3).unwrap().as_str())?; @@ -852,7 +805,7 @@ fn do_parse_lng(s : &str) -> Result { let lng = DMS { d: cap.get(2).unwrap().as_str().parse()?, m: cap.get(3).unwrap().as_str().parse_allow_comma()?, - s: 0. + s: 0., }; return build_lng(lng, ew); @@ -864,357 +817,11 @@ fn do_parse_lng(s : &str) -> Result { let lng = DMS { d: cap.get(1).unwrap().as_str().parse()?, m: cap.get(2).unwrap().as_str().parse_allow_comma()?, - s: 0. - }; - - return build_lng(lng, ew); - } - - if let Some(cap) = RE_NSEW_D.captures(s) { - let ew = EW::try_from(cap.get(1).unwrap().as_str())?; - - let lng = DMS { - d: cap.get(2).unwrap().as_str().parse_allow_comma()?, - m: 0., - s: 0. + s: 0., }; return build_lng(lng, ew); } - - if let Some(cap) = RE_D_NSEW.captures(s) { - let lng = DMS { - d: cap.get(1).unwrap().as_str().parse_allow_comma()?, - m: 0., - s: 0. - }; - - let ew = EW::try_from(cap.get(2).unwrap().as_str())?; - - return build_lng(lng, ew); - } - - - if let Some(cap) = RE_D.captures(s) { - let lng = DMS { - d: cap.get(1).unwrap().as_str().parse_allow_comma()?, - m: 0., - s: 0. - }; - - let ew = EW::East; - - return build_lng(lng, ew); - } - Err(ParseErrorInternal) } - - -#[cfg(test)] -mod tests { - use crate::{parse, parse_lat, parse_lng}; - use geo_types::Point; - - #[test] - fn dms_ns_dms_ew() { - let reference = Point::new(-79.98222222222222, 40.44611111111111); - - assert_eq!(reference, parse(r#"40° 26′ 46″ N 79° 58′ 56″ W"#).unwrap(), "normal"); - assert_eq!(reference, parse(r#"40° 26’ 46″ N 79° 58’ 56″ W"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse(r#"40° 26′ 46″ N, 79° 58′ 56″ W"#).unwrap(), "comma"); - assert_eq!(reference, parse(r#"40° 26′ 46″ N; 79° 58′ 56″ W"#).unwrap(), "semi"); - assert_eq!(reference, parse(r#"40° 26′ 46″ N,79° 58′ 56″ W"#).unwrap(), "comma2"); - assert_eq!(reference, parse(r#"40° 26′ 46″ N;79° 58′ 56″ W"#).unwrap(), "semi2"); - assert_eq!(reference, parse(r#"40° 26′ 46″ N ,79° 58′ 56″ W"#).unwrap(), "comma2"); - assert_eq!(reference, parse(r#"40° 26′ 46″ N ;79° 58′ 56″ W"#).unwrap(), "semi2"); - assert_eq!(reference, parse(r#"40°26′46″N79°58′56″W"#).unwrap(), "compact"); - assert_eq!(reference, parse(r#"40°26′46N79°58′56W"#).unwrap(), "compact, no sec mark"); - assert_eq!(reference, parse(r#"40 26 46 N 79 58 56 W"#).unwrap(), "no symbols"); - assert_eq!(reference, parse(r#"-40 26 46 S -79 58 56 E"#).unwrap(), "inverted"); - } - - #[test] - fn ns_dms_ew_dms() { - let reference = Point::new(-79.98222222222222, 40.44611111111111); - - assert_eq!(reference, parse(r#"N 40° 26′ 46″ W 79° 58′ 56″"#).unwrap(), "normal"); - assert_eq!(reference, parse(r#"N 40° 26’ 46″ W 79° 58’ 56″"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse(r#"N 40° 26′ 46″, W 79° 58′ 56″"#).unwrap(), "comma"); - assert_eq!(reference, parse(r#"N 40° 26′ 46″; W 79° 58′ 56″"#).unwrap(), "semi"); - assert_eq!(reference, parse(r#"N 40° 26′ 46″,W 79° 58′ 56″"#).unwrap(), "comma2"); - assert_eq!(reference, parse(r#"N 40° 26′ 46″;W 79° 58′ 56″"#).unwrap(), "semi2"); - assert_eq!(reference, parse(r#"N 40° 26′ 46″ , W 79° 58′ 56″"#).unwrap(), "comma3"); - assert_eq!(reference, parse(r#"N 40° 26′ 46″ ; W 79° 58′ 56″"#).unwrap(), "semi3"); - assert_eq!(reference, parse(r#"N40°26′46″W79°58′56″"#).unwrap(), "compact"); - assert_eq!(reference, parse(r#"N40°26′46W79°58′56"#).unwrap(), "compact, no sec mark"); - assert_eq!(reference, parse(r#"N 40 26 46 W 79 58 56"#).unwrap(), "no symbols"); - assert_eq!(reference, parse(r#"S -40 26 46 E -79 58 56"#).unwrap(), "inverted"); - } - - #[test] - fn dms_dms() { - let reference = Point::new(79.98222222222222, 40.44611111111111); - - assert_eq!(reference, parse(r#"40° 26′ 46″ 79° 58′ 56″"#).unwrap(), "normal"); - assert_eq!(reference, parse(r#"40° 26’ 46″ 79° 58’ 56″"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse(r#"40° 26′ 46″, 79° 58′ 56″"#).unwrap(), "comma"); - assert_eq!(reference, parse(r#"40° 26′ 46″; 79° 58′ 56″"#).unwrap(), "semi"); - assert_eq!(reference, parse(r#"40° 26′ 46″,79° 58′ 56″"#).unwrap(), "comma2"); - assert_eq!(reference, parse(r#"40° 26′ 46″;79° 58′ 56″"#).unwrap(), "semi2"); - assert_eq!(reference, parse(r#"40° 26′ 46″ , 79° 58′ 56″"#).unwrap(), "comma3"); - assert_eq!(reference, parse(r#"40° 26′ 46″ ; 79° 58′ 56″"#).unwrap(), "semi3"); - assert_eq!(reference, parse(r#"40°26′46″79°58′56″"#).unwrap(), "compact"); - assert_eq!(reference, parse(r#"40 26 46 79 58 56"#).unwrap(), "no symbols"); - } - - #[test] - fn dm_ns_dm_ew() { - let reference = Point::new(-79.98221666666667, 40.44055); - - assert_eq!(reference, parse(r#"40° 26.433′ N 79° 58.933′ W"#).unwrap(), "normal"); - assert_eq!(reference, parse(r#"40° 26,433′ N 79° 58,933′ W"#).unwrap(), "comma dec"); - assert_eq!(reference, parse(r#"40° 26.433’ N 79° 58.933’ W"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse(r#"40° 26.433′ N, 79° 58.933′ W"#).unwrap(), "comma"); - assert_eq!(reference, parse(r#"40° 26,433′ N, 79° 58,933′ W"#).unwrap(), "comma dec and comma sep"); - assert_eq!(reference, parse(r#"40° 26.433′ N; 79° 58.933′ W"#).unwrap(), "semi"); - assert_eq!(reference, parse(r#"40° 26.433′ N, 79° 58.933′ W"#).unwrap(), "comma2"); - assert_eq!(reference, parse(r#"40° 26.433′ N; 79° 58.933′ W"#).unwrap(), "semi2"); - assert_eq!(reference, parse(r#"40° 26.433 N,79° 58.933′ W"#).unwrap(), "comma3"); - assert_eq!(reference, parse(r#"40° 26.433 N;79° 58.933′ W"#).unwrap(), "semi3"); - assert_eq!(reference, parse(r#"40° 26.433′N , 79° 58.933′ W"#).unwrap(), "comma4"); - assert_eq!(reference, parse(r#"40° 26.433′N ; 79° 58.933′ W"#).unwrap(), "semi4"); - assert_eq!(reference, parse(r#"40°26.433′N79°58.933′W"#).unwrap(), "compact"); - assert_eq!(reference, parse(r#"40°26.433N79°58.933W"#).unwrap(), "compact, no min mark"); - assert_eq!(reference, parse(r#"40 26.433 N 79 58.933 W"#).unwrap(), "no symbols"); - assert_eq!(reference, parse(r#"-40 26.433 S -79 58.933 E"#).unwrap(), "inverted"); - } - - #[test] - fn ns_dm_ew_dm() { - let reference = Point::new(-79.98221666666667, 40.44055); - - assert_eq!(reference, parse(r#"N 40° 26.433′ W 79° 58.933′"#).unwrap(), "normal"); - assert_eq!(reference, parse(r#"N 40° 26.433’ W 79° 58.933’"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse(r#"N 40° 26,433′ W 79° 58,933′"#).unwrap(), "comma dec"); - assert_eq!(reference, parse(r#"N 40° 26.433′, W 79° 58.933′"#).unwrap(), "comma"); - assert_eq!(reference, parse(r#"N 40° 26,433′, W 79° 58,933′"#).unwrap(), "comma dec and comma sep"); - assert_eq!(reference, parse(r#"N 40° 26.433′; W 79° 58.933′"#).unwrap(), "semi"); - assert_eq!(reference, parse(r#"N 40° 26.433′, W 79° 58.933′"#).unwrap(), "comma2"); - assert_eq!(reference, parse(r#"N 40° 26.433′; W 79° 58.933′"#).unwrap(), "semi2"); - assert_eq!(reference, parse(r#"N 40° 26.433 ,W 79° 58.933′"#).unwrap(), "comma3"); - assert_eq!(reference, parse(r#"N 40° 26.433 ;W 79° 58.933′"#).unwrap(), "semi3"); - assert_eq!(reference, parse(r#"N 40° 26.433′ , W 79° 58.933′"#).unwrap(), "comma4"); - assert_eq!(reference, parse(r#"N 40° 26.433′ ; W 79° 58.933′"#).unwrap(), "semi4"); - assert_eq!(reference, parse(r#"N40°26.433′W79°58.933′"#).unwrap(), "compact"); - assert_eq!(reference, parse(r#"N40°26.433W79°58.933"#).unwrap(), "compact, no min mark"); - assert_eq!(reference, parse(r#"N40 26.433W79 58.933"#).unwrap(), "no symbols"); - assert_eq!(reference, parse(r#"S -40 26.433 E -79 58.933"#).unwrap(), "inverted"); - } - - #[test] - fn dm_dm() { - let reference = Point::new(79.98221666666667, 40.44055); - - assert_eq!(reference, parse(r#"40° 26.433′ 79° 58.933′"#).unwrap(), "normal"); - assert_eq!(reference, parse(r#"40° 26.433’ 79° 58.933’"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse(r#"40° 26,433′ 79° 58,933′"#).unwrap(), "comma dec"); - assert_eq!(reference, parse(r#"40° 26.433′, 79° 58.933′"#).unwrap(), "comma"); - assert_eq!(reference, parse(r#"40° 26,433′, 79° 58,933′"#).unwrap(), "comma dec and comma sep"); - assert_eq!(reference, parse(r#"40° 26.433′; 79° 58.933′"#).unwrap(), "semi"); - assert_eq!(reference, parse(r#"40° 26.433′, 79° 58.933′"#).unwrap(), "comma2"); - assert_eq!(reference, parse(r#"40° 26.433′; 79° 58.933′"#).unwrap(), "semi2"); - assert_eq!(reference, parse(r#"40° 26.433 ,79° 58.933′"#).unwrap(), "comma3"); - assert_eq!(reference, parse(r#"40° 26.433 ;79° 58.933′"#).unwrap(), "semi3"); - assert_eq!(reference, parse(r#"40° 26.433′ , 79° 58.933′"#).unwrap(), "comma4"); - assert_eq!(reference, parse(r#"40° 26.433′ ; 79° 58.933′"#).unwrap(), "semi4"); - assert_eq!(reference, parse(r#"40°26.433′79°58.933′"#).unwrap(), "compact"); - assert_eq!(reference, parse(r#"40 26.433 79 58.933"#).unwrap(), "no symbols"); - } - - #[test] - fn d_ns_d_ew() { - let reference = Point::new(-79.9822, 40.44055); - - assert_eq!(reference, parse(r#"40.44055° N 79.9822° W"#).unwrap(), "normal"); - assert_eq!(reference, parse(r#"40,44055° N 79,9822° W"#).unwrap(), "comma dec"); - assert_eq!(reference, parse(r#"40.44055 N 79.9822 W"#).unwrap(), "no deg"); - assert_eq!(reference, parse(r#"40.44055° N, 79.9822° W"#).unwrap(), "comma"); - assert_eq!(reference, parse(r#"40,44055° N, 79,9822° W"#).unwrap(), "comma comma"); - assert_eq!(reference, parse(r#"40.44055° N; 79.9822° W"#).unwrap(), "semi"); - assert_eq!(reference, parse(r#"40.44055° N,79.9822° W"#).unwrap(), "comma2"); - assert_eq!(reference, parse(r#"40.44055° N;79.9822° W"#).unwrap(), "semi2"); - assert_eq!(reference, parse(r#"40.44055° N ,79.9822° W"#).unwrap(), "comma3"); - assert_eq!(reference, parse(r#"40.44055° N ;79.9822° W"#).unwrap(), "semi3"); - assert_eq!(reference, parse(r#"40.44055N79.9822W"#).unwrap(), "compact"); - assert_eq!(reference, parse(r#"-40.44055° S -79.9822° E"#).unwrap(), "inverted"); - } - - #[test] - fn d_d() { - let reference = Point::new(79.9822, 40.44055); - - assert_eq!(reference, parse(r#"40.44055° 79.9822°"#).unwrap(), "normal"); - assert_eq!(reference, parse(r#"40,44055° 79,9822°"#).unwrap(), "comma dec"); - assert_eq!(reference, parse(r#"40.44055 79.9822"#).unwrap(), "no deg"); - assert_eq!(reference, parse(r#"40.44055°, 79.9822°"#).unwrap(), "comma"); - assert_eq!(reference, parse(r#"40,44055°, 79,9822°"#).unwrap(), "comma comma"); - assert_eq!(reference, parse(r#"40.44055°; 79.9822°"#).unwrap(), "semi"); - assert_eq!(reference, parse(r#"40.44055°,79.9822°"#).unwrap(), "comma2"); - assert_eq!(reference, parse(r#"40.44055°;79.9822°"#).unwrap(), "semi2"); - assert_eq!(reference, parse(r#"40.44055° ,79.9822°"#).unwrap(), "comma3"); - assert_eq!(reference, parse(r#"40.44055° ;79.9822°"#).unwrap(), "semi3"); - } - - - // ------ lat / lng separate ------ - - #[test] - fn dms_nsew() { - let reference = 40.44611111111111; - - assert_eq!(reference, parse_lat(r#"40° 26′ 46″ N"#).unwrap(), "normal"); - assert_eq!(reference, parse_lat(r#"40° 26’ 46″ N"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lat(r#"40°26′46″N"#).unwrap(), "compact"); - assert_eq!(reference, parse_lat(r#"40°26′46N"#).unwrap(), "compact, no sec mark"); - assert_eq!(reference, parse_lat(r#"40 26 46 N"#).unwrap(), "no symbols"); - assert_eq!(reference, parse_lat(r#"-40 26 46 S"#).unwrap(), "inverted"); - - assert_eq!(reference, parse_lng(r#"40° 26′ 46″ E"#).unwrap(), "normal"); - assert_eq!(reference, parse_lng(r#"40° 26’ 46″ E"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lng(r#"40°26′46″E"#).unwrap(), "compact"); - assert_eq!(reference, parse_lng(r#"40°26′46E"#).unwrap(), "compact, no sec mark"); - assert_eq!(reference, parse_lng(r#"40 26 46 E"#).unwrap(), "no symbols"); - assert_eq!(reference, parse_lng(r#"-40 26 46 W"#).unwrap(), "inverted"); - } - - #[test] - fn nsew_dms() { - let reference = 40.44611111111111; - - assert_eq!(reference, parse_lat(r#"N 40° 26′ 46″"#).unwrap(), "normal"); - assert_eq!(reference, parse_lat(r#"N 40° 26’ 46″"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lat(r#"N40°26′46"#).unwrap(), "compact, no sec mark"); - assert_eq!(reference, parse_lat(r#"N 40 26 46"#).unwrap(), "no symbols"); - assert_eq!(reference, parse_lat(r#"S -40 26 46"#).unwrap(), "inverted"); - - assert_eq!(reference, parse_lng(r#"E 40° 26′ 46″"#).unwrap(), "normal"); - assert_eq!(reference, parse_lng(r#"E 40° 26’ 46″"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lng(r#"E40°26′46"#).unwrap(), "compact, no sec mark"); - assert_eq!(reference, parse_lng(r#"E 40 26 46"#).unwrap(), "no symbols"); - assert_eq!(reference, parse_lng(r#"W -40 26 46"#).unwrap(), "inverted"); - } - - #[test] - fn dms() { - let reference = 40.44611111111111; - let ref_neg = -reference; - - assert_eq!(reference, parse_lat(r#"40° 26′ 46″"#).unwrap(), "normal"); - assert_eq!(reference, parse_lat(r#"40° 26’ 46″"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lat(r#"40°26′46″"#).unwrap(), "compact"); - assert_eq!(reference, parse_lat(r#"40 26 46"#).unwrap(), "no symbols"); - assert_eq!(ref_neg, parse_lat(r#"-40° 26′ 46″"#).unwrap(), "neg"); - - assert_eq!(reference, parse_lng(r#"40° 26′ 46″"#).unwrap(), "normal"); - assert_eq!(reference, parse_lng(r#"40° 26’ 46″"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lng(r#"40°26′46″"#).unwrap(), "compact"); - assert_eq!(reference, parse_lng(r#"40 26 46"#).unwrap(), "no symbols"); - assert_eq!(ref_neg, parse_lng(r#"-40° 26′ 46″"#).unwrap(), "neg"); - } - - #[test] - fn dm_nsew() { - let reference = 40.44055; - - assert_eq!(reference, parse_lat(r#"40° 26.433′ N"#).unwrap(), "normal"); - assert_eq!(reference, parse_lat(r#"40° 26,433′ N"#).unwrap(), "comma dec"); - assert_eq!(reference, parse_lat(r#"40° 26.433’ N"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lat(r#"40°26.433′N"#).unwrap(), "compact"); - assert_eq!(reference, parse_lat(r#"40°26.433N"#).unwrap(), "compact, no min mark"); - assert_eq!(reference, parse_lat(r#"40 26.433 N"#).unwrap(), "no symbols"); - assert_eq!(reference, parse_lat(r#"-40 26.433 S"#).unwrap(), "inverted"); - - assert_eq!(reference, parse_lng(r#"40° 26.433′ E"#).unwrap(), "normal"); - assert_eq!(reference, parse_lng(r#"40° 26,433′ E"#).unwrap(), "comma dec"); - assert_eq!(reference, parse_lng(r#"40° 26.433’ E"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lng(r#"40°26.433′E"#).unwrap(), "compact"); - assert_eq!(reference, parse_lng(r#"40°26.433E"#).unwrap(), "compact, no min mark"); - assert_eq!(reference, parse_lng(r#"40 26.433 E"#).unwrap(), "no symbols"); - assert_eq!(reference, parse_lng(r#"-40 26.433 W"#).unwrap(), "inverted"); - } - - #[test] - fn nsew_dm() { - let reference = 40.44055; - - assert_eq!(reference, parse_lat(r#"N 40° 26.433′"#).unwrap(), "normal"); - assert_eq!(reference, parse_lat(r#"N 40° 26.433’"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lat(r#"N 40° 26,433′"#).unwrap(), "comma dec"); - assert_eq!(reference, parse_lat(r#"N40°26.433′"#).unwrap(), "compact"); - assert_eq!(reference, parse_lat(r#"N40°26.433"#).unwrap(), "compact, no min mark"); - assert_eq!(reference, parse_lat(r#"N40 26.433"#).unwrap(), "no symbols"); - assert_eq!(reference, parse_lat(r#"S -40 26.433"#).unwrap(), "inverted"); - - assert_eq!(reference, parse_lng(r#"E 40° 26.433′"#).unwrap(), "normal"); - assert_eq!(reference, parse_lng(r#"E 40° 26.433’"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lng(r#"E 40° 26,433′"#).unwrap(), "comma dec"); - assert_eq!(reference, parse_lng(r#"E40°26.433′"#).unwrap(), "compact"); - assert_eq!(reference, parse_lng(r#"E40°26.433"#).unwrap(), "compact, no min mark"); - assert_eq!(reference, parse_lng(r#"E40 26.433"#).unwrap(), "no symbols"); - assert_eq!(reference, parse_lng(r#"W -40 26.433"#).unwrap(), "inverted"); - } - - #[test] - fn dm() { - let reference = 40.44055; - let ref_neg = -reference; - - assert_eq!(reference, parse_lat(r#"40° 26.433′"#).unwrap(), "normal"); - assert_eq!(reference, parse_lat(r#"40° 26.433’"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lat(r#"40° 26,433′"#).unwrap(), "comma dec"); - assert_eq!(reference, parse_lat(r#"40°26.433′"#).unwrap(), "compact"); - assert_eq!(reference, parse_lat(r#"40 26.433"#).unwrap(), "no symbols"); - assert_eq!(ref_neg, parse_lat(r#"-40° 26.433′"#).unwrap(), "neg"); - - assert_eq!(reference, parse_lng(r#"40° 26.433′"#).unwrap(), "normal"); - assert_eq!(reference, parse_lng(r#"40° 26.433’"#).unwrap(), "fancy apos"); - assert_eq!(reference, parse_lng(r#"40° 26,433′"#).unwrap(), "comma dec"); - assert_eq!(reference, parse_lng(r#"40°26.433′"#).unwrap(), "compact"); - assert_eq!(reference, parse_lng(r#"40 26.433"#).unwrap(), "no symbols"); - assert_eq!(ref_neg, parse_lng(r#"-40° 26.433′"#).unwrap(), "neg"); - } - - #[test] - fn d_nsew() { - let reference = 40.44055; - assert_eq!(reference, parse_lat(r#"40.44055° N"#).unwrap(), "normal"); - assert_eq!(reference, parse_lat(r#"40,44055° N"#).unwrap(), "comma dec"); - assert_eq!(reference, parse_lat(r#"40.44055 N"#).unwrap(), "no deg"); - assert_eq!(reference, parse_lat(r#"40.44055N"#).unwrap(), "compact"); - assert_eq!(reference, parse_lat(r#"-40.44055° S"#).unwrap(), "inverted"); - - assert_eq!(reference, parse_lng(r#"40.44055° E"#).unwrap(), "normal"); - assert_eq!(reference, parse_lng(r#"40,44055° E"#).unwrap(), "comma dec"); - assert_eq!(reference, parse_lng(r#"40.44055 E"#).unwrap(), "no deg"); - assert_eq!(reference, parse_lng(r#"40.44055E"#).unwrap(), "compact"); - assert_eq!(reference, parse_lng(r#"-40.44055° W"#).unwrap(), "inverted"); - } - - #[test] - fn d() { - let reference = 40.44055; - let ref_neg = -reference; - - assert_eq!(reference, parse_lat(r#"40.44055°"#).unwrap(), "normal"); - assert_eq!(reference, parse_lat(r#"40,44055°"#).unwrap(), "comma dec"); - assert_eq!(reference, parse_lat(r#"40.44055"#).unwrap(), "no deg"); - assert_eq!(ref_neg, parse_lat(r#"-40.44055"#).unwrap(), "no deg"); - - assert_eq!(reference, parse_lng(r#"40.44055°"#).unwrap(), "normal"); - assert_eq!(reference, parse_lng(r#"40,44055°"#).unwrap(), "comma dec"); - assert_eq!(reference, parse_lng(r#"40.44055"#).unwrap(), "no deg"); - assert_eq!(ref_neg, parse_lng(r#"-40.44055"#).unwrap(), "no deg"); - } -} diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..247a4a6 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,303 @@ +use crate::{parse, parse_lat, parse_lng}; +use geo_types::Point; + +#[test] +fn dms_ns_dms_ew() { + let reference = Point::new(-79.98222222222222, 40.44611111111111); + + assert_eq!(reference, parse(r#"40° 26′ 46″ N 79° 58′ 56″ W"#).unwrap(), "normal"); + assert_eq!(reference, parse(r#"40° 26’ 46″ N 79° 58’ 56″ W"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse(r#"40° 26′ 46″ N, 79° 58′ 56″ W"#).unwrap(), "comma"); + assert_eq!(reference, parse(r#"40° 26′ 46″ N; 79° 58′ 56″ W"#).unwrap(), "semi"); + assert_eq!(reference, parse(r#"40° 26′ 46″ N,79° 58′ 56″ W"#).unwrap(), "comma2"); + assert_eq!(reference, parse(r#"40° 26′ 46″ N;79° 58′ 56″ W"#).unwrap(), "semi2"); + assert_eq!(reference, parse(r#"40° 26′ 46″ N ,79° 58′ 56″ W"#).unwrap(), "comma2"); + assert_eq!(reference, parse(r#"40° 26′ 46″ N ;79° 58′ 56″ W"#).unwrap(), "semi2"); + assert_eq!(reference, parse(r#"40°26′46″N79°58′56″W"#).unwrap(), "compact"); + assert_eq!(reference, parse(r#"40°26′46N79°58′56W"#).unwrap(), "compact, no sec mark"); + assert_eq!(reference, parse(r#"40 26 46 N 79 58 56 W"#).unwrap(), "no symbols"); + assert_eq!(reference, parse(r#"-40 26 46 S -79 58 56 E"#).unwrap(), "inverted"); +} + +#[test] +fn ns_dms_ew_dms() { + let reference = Point::new(-79.98222222222222, 40.44611111111111); + + assert_eq!(reference, parse(r#"N 40° 26′ 46″ W 79° 58′ 56″"#).unwrap(), "normal"); + assert_eq!(reference, parse(r#"N 40° 26’ 46″ W 79° 58’ 56″"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse(r#"N 40° 26′ 46″, W 79° 58′ 56″"#).unwrap(), "comma"); + assert_eq!(reference, parse(r#"N 40° 26′ 46″; W 79° 58′ 56″"#).unwrap(), "semi"); + assert_eq!(reference, parse(r#"N 40° 26′ 46″,W 79° 58′ 56″"#).unwrap(), "comma2"); + assert_eq!(reference, parse(r#"N 40° 26′ 46″;W 79° 58′ 56″"#).unwrap(), "semi2"); + assert_eq!(reference, parse(r#"N 40° 26′ 46″ , W 79° 58′ 56″"#).unwrap(), "comma3"); + assert_eq!(reference, parse(r#"N 40° 26′ 46″ ; W 79° 58′ 56″"#).unwrap(), "semi3"); + assert_eq!(reference, parse(r#"N40°26′46″W79°58′56″"#).unwrap(), "compact"); + assert_eq!(reference, parse(r#"N40°26′46W79°58′56"#).unwrap(), "compact, no sec mark"); + assert_eq!(reference, parse(r#"N 40 26 46 W 79 58 56"#).unwrap(), "no symbols"); + assert_eq!(reference, parse(r#"S -40 26 46 E -79 58 56"#).unwrap(), "inverted"); +} + +#[test] +fn dms_dms() { + let reference = Point::new(79.98222222222222, 40.44611111111111); + + assert_eq!(reference, parse(r#"40° 26′ 46″ 79° 58′ 56″"#).unwrap(), "normal"); + assert_eq!(reference, parse(r#"40° 26’ 46″ 79° 58’ 56″"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse(r#"40° 26′ 46″, 79° 58′ 56″"#).unwrap(), "comma"); + assert_eq!(reference, parse(r#"40° 26′ 46″; 79° 58′ 56″"#).unwrap(), "semi"); + assert_eq!(reference, parse(r#"40° 26′ 46″,79° 58′ 56″"#).unwrap(), "comma2"); + assert_eq!(reference, parse(r#"40° 26′ 46″;79° 58′ 56″"#).unwrap(), "semi2"); + assert_eq!(reference, parse(r#"40° 26′ 46″ , 79° 58′ 56″"#).unwrap(), "comma3"); + assert_eq!(reference, parse(r#"40° 26′ 46″ ; 79° 58′ 56″"#).unwrap(), "semi3"); + assert_eq!(reference, parse(r#"40°26′46″79°58′56″"#).unwrap(), "compact"); + assert_eq!(reference, parse(r#"40 26 46 79 58 56"#).unwrap(), "no symbols"); +} + +#[test] +fn dm_ns_dm_ew() { + let reference = Point::new(-79.98221666666667, 40.44055); + + assert_eq!(reference, parse(r#"40° 26.433′ N 79° 58.933′ W"#).unwrap(), "normal"); + assert_eq!(reference, parse(r#"40° 26,433′ N 79° 58,933′ W"#).unwrap(), "comma dec"); + assert_eq!(reference, parse(r#"40° 26.433’ N 79° 58.933’ W"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse(r#"40° 26.433′ N, 79° 58.933′ W"#).unwrap(), "comma"); + assert_eq!(reference, parse(r#"40° 26,433′ N, 79° 58,933′ W"#).unwrap(), "comma dec and comma sep"); + assert_eq!(reference, parse(r#"40° 26.433′ N; 79° 58.933′ W"#).unwrap(), "semi"); + assert_eq!(reference, parse(r#"40° 26.433′ N, 79° 58.933′ W"#).unwrap(), "comma2"); + assert_eq!(reference, parse(r#"40° 26.433′ N; 79° 58.933′ W"#).unwrap(), "semi2"); + assert_eq!(reference, parse(r#"40° 26.433 N,79° 58.933′ W"#).unwrap(), "comma3"); + assert_eq!(reference, parse(r#"40° 26.433 N;79° 58.933′ W"#).unwrap(), "semi3"); + assert_eq!(reference, parse(r#"40° 26.433′N , 79° 58.933′ W"#).unwrap(), "comma4"); + assert_eq!(reference, parse(r#"40° 26.433′N ; 79° 58.933′ W"#).unwrap(), "semi4"); + assert_eq!(reference, parse(r#"40°26.433′N79°58.933′W"#).unwrap(), "compact"); + assert_eq!(reference, parse(r#"40°26.433N79°58.933W"#).unwrap(), "compact, no min mark"); + assert_eq!(reference, parse(r#"40 26.433 N 79 58.933 W"#).unwrap(), "no symbols"); + assert_eq!(reference, parse(r#"-40 26.433 S -79 58.933 E"#).unwrap(), "inverted"); +} + +#[test] +fn ns_dm_ew_dm() { + let reference = Point::new(-79.98221666666667, 40.44055); + + assert_eq!(reference, parse(r#"N 40° 26.433′ W 79° 58.933′"#).unwrap(), "normal"); + assert_eq!(reference, parse(r#"N 40° 26.433’ W 79° 58.933’"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse(r#"N 40° 26,433′ W 79° 58,933′"#).unwrap(), "comma dec"); + assert_eq!(reference, parse(r#"N 40° 26.433′, W 79° 58.933′"#).unwrap(), "comma"); + assert_eq!(reference, parse(r#"N 40° 26,433′, W 79° 58,933′"#).unwrap(), "comma dec and comma sep"); + assert_eq!(reference, parse(r#"N 40° 26.433′; W 79° 58.933′"#).unwrap(), "semi"); + assert_eq!(reference, parse(r#"N 40° 26.433′, W 79° 58.933′"#).unwrap(), "comma2"); + assert_eq!(reference, parse(r#"N 40° 26.433′; W 79° 58.933′"#).unwrap(), "semi2"); + assert_eq!(reference, parse(r#"N 40° 26.433 ,W 79° 58.933′"#).unwrap(), "comma3"); + assert_eq!(reference, parse(r#"N 40° 26.433 ;W 79° 58.933′"#).unwrap(), "semi3"); + assert_eq!(reference, parse(r#"N 40° 26.433′ , W 79° 58.933′"#).unwrap(), "comma4"); + assert_eq!(reference, parse(r#"N 40° 26.433′ ; W 79° 58.933′"#).unwrap(), "semi4"); + assert_eq!(reference, parse(r#"N40°26.433′W79°58.933′"#).unwrap(), "compact"); + assert_eq!(reference, parse(r#"N40°26.433W79°58.933"#).unwrap(), "compact, no min mark"); + assert_eq!(reference, parse(r#"N40 26.433W79 58.933"#).unwrap(), "no symbols"); + assert_eq!(reference, parse(r#"S -40 26.433 E -79 58.933"#).unwrap(), "inverted"); +} + +#[test] +fn dm_dm() { + let reference = Point::new(79.98221666666667, 40.44055); + + assert_eq!(reference, parse(r#"40° 26.433′ 79° 58.933′"#).unwrap(), "normal"); + assert_eq!(reference, parse(r#"40° 26.433’ 79° 58.933’"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse(r#"40° 26,433′ 79° 58,933′"#).unwrap(), "comma dec"); + assert_eq!(reference, parse(r#"40° 26.433′, 79° 58.933′"#).unwrap(), "comma"); + assert_eq!(reference, parse(r#"40° 26,433′, 79° 58,933′"#).unwrap(), "comma dec and comma sep"); + assert_eq!(reference, parse(r#"40° 26.433′; 79° 58.933′"#).unwrap(), "semi"); + assert_eq!(reference, parse(r#"40° 26.433′, 79° 58.933′"#).unwrap(), "comma2"); + assert_eq!(reference, parse(r#"40° 26.433′; 79° 58.933′"#).unwrap(), "semi2"); + assert_eq!(reference, parse(r#"40° 26.433 ,79° 58.933′"#).unwrap(), "comma3"); + assert_eq!(reference, parse(r#"40° 26.433 ;79° 58.933′"#).unwrap(), "semi3"); + assert_eq!(reference, parse(r#"40° 26.433′ , 79° 58.933′"#).unwrap(), "comma4"); + assert_eq!(reference, parse(r#"40° 26.433′ ; 79° 58.933′"#).unwrap(), "semi4"); + assert_eq!(reference, parse(r#"40°26.433′79°58.933′"#).unwrap(), "compact"); + assert_eq!(reference, parse(r#"40 26.433 79 58.933"#).unwrap(), "no symbols"); +} + +#[test] +fn d_ns_d_ew() { + let reference = Point::new(-79.9822, 40.44055); + + assert_eq!(reference, parse(r#"40.44055° N 79.9822° W"#).unwrap(), "normal"); + assert_eq!(reference, parse(r#"40,44055° N 79,9822° W"#).unwrap(), "comma dec"); + assert_eq!(reference, parse(r#"40.44055 N 79.9822 W"#).unwrap(), "no deg"); + assert_eq!(reference, parse(r#"40.44055° N, 79.9822° W"#).unwrap(), "comma"); + assert_eq!(reference, parse(r#"40,44055° N, 79,9822° W"#).unwrap(), "comma comma"); + assert_eq!(reference, parse(r#"40.44055° N; 79.9822° W"#).unwrap(), "semi"); + assert_eq!(reference, parse(r#"40.44055° N,79.9822° W"#).unwrap(), "comma2"); + assert_eq!(reference, parse(r#"40.44055° N;79.9822° W"#).unwrap(), "semi2"); + assert_eq!(reference, parse(r#"40.44055° N ,79.9822° W"#).unwrap(), "comma3"); + assert_eq!(reference, parse(r#"40.44055° N ;79.9822° W"#).unwrap(), "semi3"); + assert_eq!(reference, parse(r#"40.44055N79.9822W"#).unwrap(), "compact"); + assert_eq!(reference, parse(r#"-40.44055° S -79.9822° E"#).unwrap(), "inverted"); +} + +#[test] +fn d_d() { + let reference = Point::new(79.9822, 40.44055); + + assert_eq!(reference, parse(r#"40.44055° 79.9822°"#).unwrap(), "normal"); + assert_eq!(reference, parse(r#"40,44055° 79,9822°"#).unwrap(), "comma dec"); + assert_eq!(reference, parse(r#"40.44055 79.9822"#).unwrap(), "no deg"); + assert_eq!(reference, parse(r#"40.44055°, 79.9822°"#).unwrap(), "comma"); + assert_eq!(reference, parse(r#"40,44055°, 79,9822°"#).unwrap(), "comma comma"); + assert_eq!(reference, parse(r#"40.44055°; 79.9822°"#).unwrap(), "semi"); + assert_eq!(reference, parse(r#"40.44055°,79.9822°"#).unwrap(), "comma2"); + assert_eq!(reference, parse(r#"40.44055°;79.9822°"#).unwrap(), "semi2"); + assert_eq!(reference, parse(r#"40.44055° ,79.9822°"#).unwrap(), "comma3"); + assert_eq!(reference, parse(r#"40.44055° ;79.9822°"#).unwrap(), "semi3"); +} + + +// ------ lat / lng separate ------ + +#[test] +fn dms_nsew() { + let reference = 40.44611111111111; + + assert_eq!(reference, parse_lat(r#"40° 26′ 46″ N"#).unwrap(), "normal"); + assert_eq!(reference, parse_lat(r#"40° 26’ 46″ N"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lat(r#"40°26′46″N"#).unwrap(), "compact"); + assert_eq!(reference, parse_lat(r#"40°26′46N"#).unwrap(), "compact, no sec mark"); + assert_eq!(reference, parse_lat(r#"40 26 46 N"#).unwrap(), "no symbols"); + assert_eq!(reference, parse_lat(r#"-40 26 46 S"#).unwrap(), "inverted"); + + assert_eq!(reference, parse_lng(r#"40° 26′ 46″ E"#).unwrap(), "normal"); + assert_eq!(reference, parse_lng(r#"40° 26’ 46″ E"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lng(r#"40°26′46″E"#).unwrap(), "compact"); + assert_eq!(reference, parse_lng(r#"40°26′46E"#).unwrap(), "compact, no sec mark"); + assert_eq!(reference, parse_lng(r#"40 26 46 E"#).unwrap(), "no symbols"); + assert_eq!(reference, parse_lng(r#"-40 26 46 W"#).unwrap(), "inverted"); +} + +#[test] +fn nsew_dms() { + let reference = 40.44611111111111; + + assert_eq!(reference, parse_lat(r#"N 40° 26′ 46″"#).unwrap(), "normal"); + assert_eq!(reference, parse_lat(r#"N 40° 26’ 46″"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lat(r#"N40°26′46"#).unwrap(), "compact, no sec mark"); + assert_eq!(reference, parse_lat(r#"N 40 26 46"#).unwrap(), "no symbols"); + assert_eq!(reference, parse_lat(r#"S -40 26 46"#).unwrap(), "inverted"); + + assert_eq!(reference, parse_lng(r#"E 40° 26′ 46″"#).unwrap(), "normal"); + assert_eq!(reference, parse_lng(r#"E 40° 26’ 46″"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lng(r#"E40°26′46"#).unwrap(), "compact, no sec mark"); + assert_eq!(reference, parse_lng(r#"E 40 26 46"#).unwrap(), "no symbols"); + assert_eq!(reference, parse_lng(r#"W -40 26 46"#).unwrap(), "inverted"); +} + +#[test] +fn dms() { + let reference = 40.44611111111111; + let ref_neg = -reference; + + assert_eq!(reference, parse_lat(r#"40° 26′ 46″"#).unwrap(), "normal"); + assert_eq!(reference, parse_lat(r#"40° 26’ 46″"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lat(r#"40°26′46″"#).unwrap(), "compact"); + assert_eq!(reference, parse_lat(r#"40 26 46"#).unwrap(), "no symbols"); + assert_eq!(ref_neg, parse_lat(r#"-40° 26′ 46″"#).unwrap(), "neg"); + + assert_eq!(reference, parse_lng(r#"40° 26′ 46″"#).unwrap(), "normal"); + assert_eq!(reference, parse_lng(r#"40° 26’ 46″"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lng(r#"40°26′46″"#).unwrap(), "compact"); + assert_eq!(reference, parse_lng(r#"40 26 46"#).unwrap(), "no symbols"); + assert_eq!(ref_neg, parse_lng(r#"-40° 26′ 46″"#).unwrap(), "neg"); +} + +#[test] +fn dm_nsew() { + let reference = 40.44055; + + assert_eq!(reference, parse_lat(r#"40° 26.433′ N"#).unwrap(), "normal"); + assert_eq!(reference, parse_lat(r#"40° 26,433′ N"#).unwrap(), "comma dec"); + assert_eq!(reference, parse_lat(r#"40° 26.433’ N"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lat(r#"40°26.433′N"#).unwrap(), "compact"); + assert_eq!(reference, parse_lat(r#"40°26.433N"#).unwrap(), "compact, no min mark"); + assert_eq!(reference, parse_lat(r#"40 26.433 N"#).unwrap(), "no symbols"); + assert_eq!(reference, parse_lat(r#"-40 26.433 S"#).unwrap(), "inverted"); + + assert_eq!(reference, parse_lng(r#"40° 26.433′ E"#).unwrap(), "normal"); + assert_eq!(reference, parse_lng(r#"40° 26,433′ E"#).unwrap(), "comma dec"); + assert_eq!(reference, parse_lng(r#"40° 26.433’ E"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lng(r#"40°26.433′E"#).unwrap(), "compact"); + assert_eq!(reference, parse_lng(r#"40°26.433E"#).unwrap(), "compact, no min mark"); + assert_eq!(reference, parse_lng(r#"40 26.433 E"#).unwrap(), "no symbols"); + assert_eq!(reference, parse_lng(r#"-40 26.433 W"#).unwrap(), "inverted"); +} + +#[test] +fn nsew_dm() { + let reference = 40.44055; + + assert_eq!(reference, parse_lat(r#"N 40° 26.433′"#).unwrap(), "normal"); + assert_eq!(reference, parse_lat(r#"N 40° 26.433’"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lat(r#"N 40° 26,433′"#).unwrap(), "comma dec"); + assert_eq!(reference, parse_lat(r#"N40°26.433′"#).unwrap(), "compact"); + assert_eq!(reference, parse_lat(r#"N40°26.433"#).unwrap(), "compact, no min mark"); + assert_eq!(reference, parse_lat(r#"N40 26.433"#).unwrap(), "no symbols"); + assert_eq!(reference, parse_lat(r#"S -40 26.433"#).unwrap(), "inverted"); + + assert_eq!(reference, parse_lng(r#"E 40° 26.433′"#).unwrap(), "normal"); + assert_eq!(reference, parse_lng(r#"E 40° 26.433’"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lng(r#"E 40° 26,433′"#).unwrap(), "comma dec"); + assert_eq!(reference, parse_lng(r#"E40°26.433′"#).unwrap(), "compact"); + assert_eq!(reference, parse_lng(r#"E40°26.433"#).unwrap(), "compact, no min mark"); + assert_eq!(reference, parse_lng(r#"E40 26.433"#).unwrap(), "no symbols"); + assert_eq!(reference, parse_lng(r#"W -40 26.433"#).unwrap(), "inverted"); +} + +#[test] +fn dm() { + let reference = 40.44055; + let ref_neg = -reference; + + assert_eq!(reference, parse_lat(r#"40° 26.433′"#).unwrap(), "normal"); + assert_eq!(reference, parse_lat(r#"40° 26.433’"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lat(r#"40° 26,433′"#).unwrap(), "comma dec"); + assert_eq!(reference, parse_lat(r#"40°26.433′"#).unwrap(), "compact"); + assert_eq!(reference, parse_lat(r#"40 26.433"#).unwrap(), "no symbols"); + assert_eq!(ref_neg, parse_lat(r#"-40° 26.433′"#).unwrap(), "neg"); + + assert_eq!(reference, parse_lng(r#"40° 26.433′"#).unwrap(), "normal"); + assert_eq!(reference, parse_lng(r#"40° 26.433’"#).unwrap(), "fancy apos"); + assert_eq!(reference, parse_lng(r#"40° 26,433′"#).unwrap(), "comma dec"); + assert_eq!(reference, parse_lng(r#"40°26.433′"#).unwrap(), "compact"); + assert_eq!(reference, parse_lng(r#"40 26.433"#).unwrap(), "no symbols"); + assert_eq!(ref_neg, parse_lng(r#"-40° 26.433′"#).unwrap(), "neg"); +} + +#[test] +fn d_nsew() { + let reference = 40.44055; + assert_eq!(reference, parse_lat(r#"40.44055° N"#).unwrap(), "normal"); + assert_eq!(reference, parse_lat(r#"40,44055° N"#).unwrap(), "comma dec"); + assert_eq!(reference, parse_lat(r#"40.44055 N"#).unwrap(), "no deg"); + assert_eq!(reference, parse_lat(r#"40.44055N"#).unwrap(), "compact"); + assert_eq!(reference, parse_lat(r#"-40.44055° S"#).unwrap(), "inverted"); + + assert_eq!(reference, parse_lng(r#"40.44055° E"#).unwrap(), "normal"); + assert_eq!(reference, parse_lng(r#"40,44055° E"#).unwrap(), "comma dec"); + assert_eq!(reference, parse_lng(r#"40.44055 E"#).unwrap(), "no deg"); + assert_eq!(reference, parse_lng(r#"40.44055E"#).unwrap(), "compact"); + assert_eq!(reference, parse_lng(r#"-40.44055° W"#).unwrap(), "inverted"); +} + +#[test] +fn d() { + let reference = 40.44055; + let ref_neg = -reference; + + assert_eq!(reference, parse_lat(r#"40.44055°"#).unwrap(), "normal"); + assert_eq!(reference, parse_lat(r#"40,44055°"#).unwrap(), "comma dec"); + assert_eq!(reference, parse_lat(r#"40.44055"#).unwrap(), "no deg"); + assert_eq!(ref_neg, parse_lat(r#"-40.44055"#).unwrap(), "no deg"); + + assert_eq!(reference, parse_lng(r#"40.44055°"#).unwrap(), "normal"); + assert_eq!(reference, parse_lng(r#"40,44055°"#).unwrap(), "comma dec"); + assert_eq!(reference, parse_lng(r#"40.44055"#).unwrap(), "no deg"); + assert_eq!(ref_neg, parse_lng(r#"-40.44055"#).unwrap(), "no deg"); +}