Create spatial-coordinate-systems crate

This commit is contained in:
Austen Adler 2023-03-19 18:34:04 -04:00
parent e7744656b1
commit f587b15372
7 changed files with 549 additions and 25 deletions

235
Cargo.lock generated
View File

@ -37,6 +37,30 @@ dependencies = [
"subtle",
]
[[package]]
name = "aho-corasick"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4"
[[package]]
name = "approx"
version = "0.4.0"
@ -65,7 +89,7 @@ checksum = "e4655ae1a7b0cdf149156f780c5bf3f1352bc53cbd9e0a361a7ef7b22947e965"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -76,7 +100,7 @@ checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -199,6 +223,21 @@ dependencies = [
"inout",
]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "cookie"
version = "0.16.2"
@ -298,7 +337,7 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -502,6 +541,15 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -716,6 +764,12 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "mio"
version = "0.8.6"
@ -748,6 +802,16 @@ dependencies = [
"version_check",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
@ -859,7 +923,7 @@ dependencies = [
"proc-macro2",
"proc-macro2-diagnostics",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -918,6 +982,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pluscodes"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5d3288e02604f234a7071cba77560e6a6e47395268bf80611f7e2347b082efa"
dependencies = [
"anyhow",
"regex",
"structopt",
]
[[package]]
name = "polyval"
version = "0.6.0"
@ -937,10 +1012,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.50"
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.107",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224"
dependencies = [
"unicode-ident",
]
@ -953,16 +1052,16 @@ checksum = "4bf29726d67464d49fa6224a1d07936a8c08bb3fba727c7493f6cf1616fdaada"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
"version_check",
"yansi",
]
[[package]]
name = "quote"
version = "1.0.23"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
@ -1023,7 +1122,7 @@ checksum = "9f9c0c92af03644e4806106281fe2e068ac5bc0ae74a707266d06ea27bccee5f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1032,6 +1131,8 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
@ -1110,7 +1211,7 @@ dependencies = [
"proc-macro2",
"quote",
"rocket_http",
"syn",
"syn 1.0.107",
"unicode-xid",
]
@ -1162,7 +1263,7 @@ dependencies = [
"proc-macro2",
"quote",
"rust-embed-utils",
"syn",
"syn 1.0.107",
"walkdir",
]
@ -1238,7 +1339,7 @@ checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1312,6 +1413,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "spatial-coordinate-systems"
version = "0.1.0"
dependencies = [
"nom",
"pluscodes",
"thiserror",
"utm",
]
[[package]]
name = "spin"
version = "0.9.5"
@ -1336,6 +1447,36 @@ dependencies = [
"loom",
]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn 1.0.107",
]
[[package]]
name = "subtle"
version = "2.4.1"
@ -1353,6 +1494,17 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59d3276aee1fa0c33612917969b5172b5be2db051232a6e4826f1a1a9191b045"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.3.0"
@ -1368,23 +1520,32 @@ dependencies = [
]
[[package]]
name = "thiserror"
version = "1.0.38"
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.2",
]
[[package]]
@ -1451,7 +1612,7 @@ checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1514,7 +1675,7 @@ checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1595,7 +1756,7 @@ checksum = "d337d3be617449165cb4633c8dece429afd83f84051024079f97ad32a9663716"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
]
[[package]]
@ -1620,6 +1781,18 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "unicode-xid"
version = "0.2.4"
@ -1636,12 +1809,24 @@ dependencies = [
"subtle",
]
[[package]]
name = "utm"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95b09e3b3a0abd1ccb77673a6b7b8875d9d1c80626154add451cf18392dc4c3c"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.4"
@ -1696,7 +1881,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
"wasm-bindgen-shared",
]
@ -1718,7 +1903,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.107",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]

View File

@ -10,6 +10,7 @@ members = [
"./xpin-wasm/",
"./web",
"./web-frontend/",
"./spatial-coordinate-systems/",
]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -0,0 +1,12 @@
[package]
name = "spatial-coordinate-systems"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nom = "7.1.3"
pluscodes = "0.5.0"
thiserror = "1.0.40"
utm = "0.1.6"

View File

@ -0,0 +1,36 @@
use nom::{
branch::alt,
bytes::complete::tag_no_case,
character::{
complete::{space0, space1},
*,
},
combinator::{map, opt},
sequence::tuple,
IResult,
};
use crate::Direction;
pub fn optional_separator<'a>(sep: char) -> impl FnMut(&'a str) -> IResult<&'a str, ()> {
map(
alt((
map(tuple((space0, complete::char(sep), space0)), std::mem::drop),
map(space1, std::mem::drop),
)),
std::mem::drop,
)
}
pub fn parse_direction(i: &str) -> IResult<&str, Direction> {
alt((
map(tag_no_case("north"), |_| Direction::North),
map(tag_no_case("south"), |_| Direction::South),
map(tag_no_case("east"), |_| Direction::East),
map(tag_no_case("west"), |_| Direction::West),
map(tag_no_case("n"), |_| Direction::North),
map(tag_no_case("s"), |_| Direction::South),
map(tag_no_case("e"), |_| Direction::East),
map(tag_no_case("w"), |_| Direction::West),
))(i)
}

View File

@ -0,0 +1,104 @@
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;
pub fn parse_coordinate(i: &str) -> IResult<&str, (DegreeMinute, DegreeMinute)> {
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))
} else {
None
}
},
)(i)
}
pub fn parse(i: &str) -> IResult<&str, DegreeMinute> {
map(
tuple((
// Degrees
complete::i16,
optional_separator('°'),
// Minutes
double,
optional_separator('\''),
// Direction
parse_direction,
)),
|(degrees, (), minutes, (), direction)| DegreeMinute {
degrees,
minutes,
direction,
},
)(i)
}
#[derive(PartialEq, Debug)]
pub struct DegreeMinute {
pub degrees: i16,
pub minutes: f64,
pub direction: Direction,
}
impl FromStr for DegreeMinute {
type Err = ();
/// Recognizes DMS in the formats:
///
/// * `40° 31' 21" N, 105° 5' 39" W`
/// * `40 31 21 N, 105 5 39 W`
///
/// ```rust
/// use spatial_coordinate_systems::dm::DegreeMinute;
/// use spatial_coordinate_systems::Direction;
/// use std::str::FromStr;
///
/// assert_eq!(DegreeMinute::from_str("40 31.3 N").unwrap(), DegreeMinute {
/// degrees: 40,
/// minutes: 31.3_f64,
/// direction: Direction::North,
/// });
/// assert_eq!(DegreeMinute::from_str("40°31' N").unwrap(), DegreeMinute {
/// 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)
}
}

View File

@ -0,0 +1,115 @@
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;
pub fn parse_coordinate(i: &str) -> IResult<&str, (DegreeMinuteSecond, DegreeMinuteSecond)> {
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))
} else {
None
}
},
)(i)
}
pub fn parse(i: &str) -> IResult<&str, DegreeMinuteSecond> {
map(
tuple((
// Degrees
complete::i16,
optional_separator('°'),
// Minutes
complete::i16,
optional_separator('\''),
// Seconds
double,
optional_separator('"'),
// Direction
parse_direction,
)),
|(degrees, (), minutes, (), seconds, (), direction)| DegreeMinuteSecond {
degrees,
minutes,
seconds,
direction,
},
)(i)
}
#[derive(PartialEq, Debug)]
pub struct DegreeMinuteSecond {
pub degrees: i16,
pub minutes: i16,
pub seconds: f64,
pub direction: Direction,
}
impl FromStr for DegreeMinuteSecond {
type Err = ();
/// Recognizes DMS in the formats:
///
/// * `40° 31' 21" N, 105° 5' 39" W`
/// * `40 31 21 N, 105 5 39 W`
///
/// ```rust
/// use spatial_coordinate_systems::dms::DegreeMinuteSecond;
/// use spatial_coordinate_systems::Direction;
/// use std::str::FromStr;
///
/// assert_eq!(DegreeMinuteSecond::from_str("40 31 21 N").unwrap(), DegreeMinuteSecond {
/// degrees: 40,
/// minutes: 31,
/// seconds: 21_f64,
/// direction: Direction::North,
/// });
/// assert_eq!(DegreeMinuteSecond::from_str("40°31' 21 \" N").unwrap(), DegreeMinuteSecond {
/// degrees: 40,
/// minutes: 31,
/// seconds: 21_f64,
/// direction: Direction::North,
/// });
/// ```
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)
}
}

View File

@ -0,0 +1,71 @@
#![allow(unused_imports)]
use std::str::FromStr;
mod common;
pub mod dm;
pub mod dms;
use dm::DegreeMinute;
use dms::DegreeMinuteSecond;
use nom::{
branch::alt,
character::complete::space0,
combinator::{eof, map},
sequence::tuple,
IResult,
};
pub struct LatLon {
pub lat: f64,
pub lon: f64,
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Unknown")]
Unknown,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
/// True if this is north/south
pub fn is_lat(&self) -> bool {
self == &Direction::North || self == &Direction::South
}
/// True if this is east/west
pub fn is_lon(&self) -> bool {
self == &Direction::East || self == &Direction::West
}
}
pub enum Coordinate {
LatLon((f64, f64)),
DegreeMinuteSecond((DegreeMinuteSecond, DegreeMinuteSecond)),
DegreeMinute((DegreeMinute, DegreeMinute)),
}
pub enum CoordinateSystem {
LatLon,
}
impl FromStr for Coordinate {
type Err = ();
fn from_str(i: &str) -> Result<Self, Self::Err> {
tuple((
space0,
alt((map(dms::parse_coordinate, Coordinate::DegreeMinuteSecond),)),
space0,
eof,
))(i)
.map(|(_, (_, coordinate, _, _))| coordinate)
.map_err(|_| ())
}
}