More work on spatial coordinate converter

This commit is contained in:
Austen Adler 2023-03-19 18:53:22 -04:00
parent f587b15372
commit 23d48b2a7b
5 changed files with 81 additions and 78 deletions

View File

@ -20,6 +20,10 @@ In order of dependency, the role of each of these directories in this repository
|Asciidoctor documentation |Asciidoctor documentation
|Design decisions and algorithm definition documentation |Design decisions and algorithm definition documentation
|spatial-coordinate-systems
|Rust Crate
|Convert among latitude/longitude (DD), degree-minute (DM), degree-minute-second (DMS), UTM, and Pluscodes
|wordlist |wordlist
|Python/CSV |Python/CSV
|Wordlist sources and generator code |Wordlist sources and generator code

View File

@ -0,0 +1,30 @@
use crate::{
common::{optional_separator, parse_direction},
Direction,
};
use nom::{
branch::alt,
character::complete::{self, space0, space1},
combinator::{eof, map, map_opt, map_res, opt},
number::complete::double,
sequence::{pair, tuple},
IResult,
};
use std::str::FromStr;
#[derive(PartialEq, Debug)]
pub struct Coordinate(pub f64, pub f64);
pub fn parse_coordinate(i: &str) -> IResult<&str, Coordinate> {
map_opt(
tuple((double, optional_separator(','), double)),
|(ns, _, ew)| {
// Ensure this is a north/south then east/west direction
if (-90_f64..=90_f64).contains(&ns) && (-180_f64..=180_f64).contains(&ew) {
Some(Coordinate(ns, ew))
} else {
None
}
},
)(i)
}

View File

@ -12,13 +12,23 @@ use nom::{
}; };
use std::str::FromStr; use std::str::FromStr;
pub fn parse_coordinate(i: &str) -> IResult<&str, (DegreeMinute, DegreeMinute)> { #[derive(PartialEq, Debug)]
pub struct Coordinate(pub DM, pub DM);
#[derive(PartialEq, Debug)]
pub struct DM {
pub degrees: i16,
pub minutes: f64,
pub direction: Direction,
}
pub fn parse_coordinate(i: &str) -> IResult<&str, Coordinate> {
map_opt( map_opt(
tuple((parse, optional_separator(','), parse)), tuple((parse, optional_separator(','), parse)),
|(ns, _, ew)| { |(ns, _, ew)| {
// Ensure this is a north/south then east/west direction // Ensure this is a north/south then east/west direction
if ns.direction.is_lat() && ew.direction.is_lon() { if ns.direction.is_lat() && ew.direction.is_lon() {
Some((ns, ew)) Some(Coordinate(ns, ew))
} else { } else {
None None
} }
@ -26,7 +36,7 @@ pub fn parse_coordinate(i: &str) -> IResult<&str, (DegreeMinute, DegreeMinute)>
)(i) )(i)
} }
pub fn parse(i: &str) -> IResult<&str, DegreeMinute> { pub fn parse(i: &str) -> IResult<&str, DM> {
map( map(
tuple(( tuple((
// Degrees // Degrees
@ -38,7 +48,7 @@ pub fn parse(i: &str) -> IResult<&str, DegreeMinute> {
// Direction // Direction
parse_direction, parse_direction,
)), )),
|(degrees, (), minutes, (), direction)| DegreeMinute { |(degrees, (), minutes, (), direction)| DM {
degrees, degrees,
minutes, minutes,
direction, direction,
@ -46,14 +56,7 @@ pub fn parse(i: &str) -> IResult<&str, DegreeMinute> {
)(i) )(i)
} }
#[derive(PartialEq, Debug)] impl FromStr for DM {
pub struct DegreeMinute {
pub degrees: i16,
pub minutes: f64,
pub direction: Direction,
}
impl FromStr for DegreeMinute {
type Err = (); type Err = ();
/// Recognizes DMS in the formats: /// Recognizes DMS in the formats:
@ -62,43 +65,22 @@ impl FromStr for DegreeMinute {
/// * `40 31 21 N, 105 5 39 W` /// * `40 31 21 N, 105 5 39 W`
/// ///
/// ```rust /// ```rust
/// use spatial_coordinate_systems::dm::DegreeMinute; /// use spatial_coordinate_systems::dm::DM;
/// use spatial_coordinate_systems::Direction; /// use spatial_coordinate_systems::Direction;
/// use std::str::FromStr; /// use std::str::FromStr;
/// ///
/// assert_eq!(DegreeMinute::from_str("40 31.3 N").unwrap(), DegreeMinute { /// assert_eq!(DM::from_str("40 31.3 N").unwrap(), DM {
/// degrees: 40, /// degrees: 40,
/// minutes: 31.3_f64, /// minutes: 31.3_f64,
/// direction: Direction::North, /// direction: Direction::North,
/// }); /// });
/// assert_eq!(DegreeMinute::from_str("40°31' N").unwrap(), DegreeMinute { /// assert_eq!(DM::from_str("40°31' N").unwrap(), DM {
/// degrees: 40, /// degrees: 40,
/// minutes: 31_f64, /// minutes: 31_f64,
/// direction: Direction::North, /// direction: Direction::North,
/// }); /// });
/// ``` /// ```
fn from_str(i: &str) -> Result<Self, Self::Err> { fn from_str(i: &str) -> Result<Self, Self::Err> {
map( parse(i).map_err(|_| ()).map(|(_, ret)| ret)
tuple((
space0,
// Degrees
complete::i16,
optional_separator('°'),
// Minutes
double,
optional_separator('\''),
// Direction
parse_direction,
// Ensure no other characters can be read
eof,
)),
|(_, degrees, (), minutes, (), direction, _)| DegreeMinute {
degrees,
minutes,
direction,
},
)(i)
.map_err(|_| ())
.map(|(_, ret)| ret)
} }
} }

View File

@ -12,13 +12,16 @@ use nom::{
}; };
use std::str::FromStr; use std::str::FromStr;
pub fn parse_coordinate(i: &str) -> IResult<&str, (DegreeMinuteSecond, DegreeMinuteSecond)> { #[derive(PartialEq, Debug)]
pub struct Coordinate(pub DMS, pub DMS);
pub fn parse_coordinate(i: &str) -> IResult<&str, Coordinate> {
map_opt( map_opt(
tuple((parse, optional_separator(','), parse)), tuple((parse, optional_separator(','), parse)),
|(ns, _, ew)| { |(ns, _, ew)| {
// Ensure this is a north/south then east/west direction // Ensure this is a north/south then east/west direction
if ns.direction.is_lat() && ew.direction.is_lon() { if ns.direction.is_lat() && ew.direction.is_lon() {
Some((ns, ew)) Some(Coordinate(ns, ew))
} else { } else {
None None
} }
@ -26,7 +29,7 @@ pub fn parse_coordinate(i: &str) -> IResult<&str, (DegreeMinuteSecond, DegreeMin
)(i) )(i)
} }
pub fn parse(i: &str) -> IResult<&str, DegreeMinuteSecond> { pub fn parse(i: &str) -> IResult<&str, DMS> {
map( map(
tuple(( tuple((
// Degrees // Degrees
@ -41,7 +44,7 @@ pub fn parse(i: &str) -> IResult<&str, DegreeMinuteSecond> {
// Direction // Direction
parse_direction, parse_direction,
)), )),
|(degrees, (), minutes, (), seconds, (), direction)| DegreeMinuteSecond { |(degrees, (), minutes, (), seconds, (), direction)| DMS {
degrees, degrees,
minutes, minutes,
seconds, seconds,
@ -51,14 +54,14 @@ pub fn parse(i: &str) -> IResult<&str, DegreeMinuteSecond> {
} }
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
pub struct DegreeMinuteSecond { pub struct DMS {
pub degrees: i16, pub degrees: i16,
pub minutes: i16, pub minutes: i16,
pub seconds: f64, pub seconds: f64,
pub direction: Direction, pub direction: Direction,
} }
impl FromStr for DegreeMinuteSecond { impl FromStr for DMS {
type Err = (); type Err = ();
/// Recognizes DMS in the formats: /// Recognizes DMS in the formats:
@ -67,17 +70,17 @@ impl FromStr for DegreeMinuteSecond {
/// * `40 31 21 N, 105 5 39 W` /// * `40 31 21 N, 105 5 39 W`
/// ///
/// ```rust /// ```rust
/// use spatial_coordinate_systems::dms::DegreeMinuteSecond; /// use spatial_coordinate_systems::dms::DMS;
/// use spatial_coordinate_systems::Direction; /// use spatial_coordinate_systems::Direction;
/// use std::str::FromStr; /// use std::str::FromStr;
/// ///
/// assert_eq!(DegreeMinuteSecond::from_str("40 31 21 N").unwrap(), DegreeMinuteSecond { /// assert_eq!(DMS::from_str("40 31 21 N").unwrap(), DMS {
/// degrees: 40, /// degrees: 40,
/// minutes: 31, /// minutes: 31,
/// seconds: 21_f64, /// seconds: 21_f64,
/// direction: Direction::North, /// direction: Direction::North,
/// }); /// });
/// assert_eq!(DegreeMinuteSecond::from_str("40°31' 21 \" N").unwrap(), DegreeMinuteSecond { /// assert_eq!(DMS::from_str("40°31' 21 \" N").unwrap(), DMS {
/// degrees: 40, /// degrees: 40,
/// minutes: 31, /// minutes: 31,
/// seconds: 21_f64, /// seconds: 21_f64,
@ -85,31 +88,6 @@ impl FromStr for DegreeMinuteSecond {
/// }); /// });
/// ``` /// ```
fn from_str(i: &str) -> Result<Self, Self::Err> { fn from_str(i: &str) -> Result<Self, Self::Err> {
map( parse(i).map_err(|_| ()).map(|(_, ret)| ret)
tuple((
space0,
// Degrees
complete::i16,
optional_separator('°'),
// Minutes
complete::i16,
optional_separator('\''),
// Seconds
double,
optional_separator('"'),
// Direction
parse_direction,
// Ensure no other characters can be read
eof,
)),
|(_, degrees, (), minutes, (), seconds, (), direction, _)| DegreeMinuteSecond {
degrees,
minutes,
seconds,
direction,
},
)(i)
.map_err(|_| ())
.map(|(_, ret)| ret)
} }
} }

View File

@ -2,11 +2,12 @@
use std::str::FromStr; use std::str::FromStr;
mod common; mod common;
pub mod dd;
pub mod dm; pub mod dm;
pub mod dms; pub mod dms;
use dm::DegreeMinute; use dm::DM;
use dms::DegreeMinuteSecond; use dms::DMS;
use nom::{ use nom::{
branch::alt, branch::alt,
character::complete::space0, character::complete::space0,
@ -46,9 +47,11 @@ impl Direction {
} }
pub enum Coordinate { pub enum Coordinate {
LatLon((f64, f64)), DD(dd::Coordinate),
DegreeMinuteSecond((DegreeMinuteSecond, DegreeMinuteSecond)), DMS(dms::Coordinate),
DegreeMinute((DegreeMinute, DegreeMinute)), DM(dm::Coordinate),
// UTM(utm::UTMCoordinate),
// Plus(plus::PlusCoordinate),
} }
pub enum CoordinateSystem { pub enum CoordinateSystem {
@ -61,7 +64,13 @@ impl FromStr for Coordinate {
fn from_str(i: &str) -> Result<Self, Self::Err> { fn from_str(i: &str) -> Result<Self, Self::Err> {
tuple(( tuple((
space0, space0,
alt((map(dms::parse_coordinate, Coordinate::DegreeMinuteSecond),)), alt((
map(dd::parse_coordinate, Coordinate::DD),
map(dms::parse_coordinate, Coordinate::DMS),
map(dm::parse_coordinate, Coordinate::DM),
// map(utm::parse_coordinate, Coordinate::UTM),
// map(plus::parse_coordinate, Coordinate::PLUS),
)),
space0, space0,
eof, eof,
))(i) ))(i)