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
|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
|Python/CSV
|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;
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(
tuple((parse, optional_separator(','), parse)),
|(ns, _, ew)| {
// Ensure this is a north/south then east/west direction
if ns.direction.is_lat() && ew.direction.is_lon() {
Some((ns, ew))
Some(Coordinate(ns, ew))
} else {
None
}
@ -26,7 +36,7 @@ pub fn parse_coordinate(i: &str) -> IResult<&str, (DegreeMinute, DegreeMinute)>
)(i)
}
pub fn parse(i: &str) -> IResult<&str, DegreeMinute> {
pub fn parse(i: &str) -> IResult<&str, DM> {
map(
tuple((
// Degrees
@ -38,7 +48,7 @@ pub fn parse(i: &str) -> IResult<&str, DegreeMinute> {
// Direction
parse_direction,
)),
|(degrees, (), minutes, (), direction)| DegreeMinute {
|(degrees, (), minutes, (), direction)| DM {
degrees,
minutes,
direction,
@ -46,14 +56,7 @@ pub fn parse(i: &str) -> IResult<&str, DegreeMinute> {
)(i)
}
#[derive(PartialEq, Debug)]
pub struct DegreeMinute {
pub degrees: i16,
pub minutes: f64,
pub direction: Direction,
}
impl FromStr for DegreeMinute {
impl FromStr for DM {
type Err = ();
/// Recognizes DMS in the formats:
@ -62,43 +65,22 @@ impl FromStr for DegreeMinute {
/// * `40 31 21 N, 105 5 39 W`
///
/// ```rust
/// use spatial_coordinate_systems::dm::DegreeMinute;
/// use spatial_coordinate_systems::dm::DM;
/// use spatial_coordinate_systems::Direction;
/// 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,
/// minutes: 31.3_f64,
/// 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,
/// minutes: 31_f64,
/// direction: Direction::North,
/// });
/// ```
fn from_str(i: &str) -> Result<Self, Self::Err> {
map(
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)
parse(i).map_err(|_| ()).map(|(_, ret)| ret)
}
}

View File

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

View File

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