From 225f1a1155c9ccb554248c79627091c8938f4b64 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Thu, 18 Jul 2024 15:25:03 -0400 Subject: [PATCH] Add no-whitespace support --- src/lib.rs | 178 ++++++++++++++++++++++++++++++++++++++-- src/types/linestring.rs | 22 +++++ src/types/point.rs | 13 +++ 3 files changed, 204 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2bbc41e..2b97b13 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -159,33 +159,186 @@ where word: &str, tokens: &mut PeekableTokens, ) -> Result { + // Normally Z/M/ZM is separated by a space from the primary WKT word. E.g. `POINT Z` + // instead of `POINTZ`. However we wish to support both types (in reading). When written + // without a space, `POINTZ` is considered a single word, which means we need to include + // matches here. match word { w if w.eq_ignore_ascii_case("POINT") => { - let x = as FromTokens>::from_tokens_with_header(tokens); + let x = as FromTokens>::from_tokens_with_header(tokens, None); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("POINTZ") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZ), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("POINTM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYM), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("POINTZM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZM), + ); x.map(|y| y.into()) } w if w.eq_ignore_ascii_case("LINESTRING") || w.eq_ignore_ascii_case("LINEARRING") => { - let x = as FromTokens>::from_tokens_with_header(tokens); + let x = as FromTokens>::from_tokens_with_header(tokens, None); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("LINESTRINGZ") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZ), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("LINESTRINGM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYM), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("LINESTRINGZM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZM), + ); x.map(|y| y.into()) } w if w.eq_ignore_ascii_case("POLYGON") => { - let x = as FromTokens>::from_tokens_with_header(tokens); + let x = as FromTokens>::from_tokens_with_header(tokens, None); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("POLYGONZ") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZ), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("POLYGONM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYM), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("POLYGONZM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZM), + ); x.map(|y| y.into()) } w if w.eq_ignore_ascii_case("MULTIPOINT") => { - let x = as FromTokens>::from_tokens_with_header(tokens); + let x = as FromTokens>::from_tokens_with_header(tokens, None); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("MULTIPOINTZ") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZ), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("MULTIPOINTM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYM), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("MULTIPOINTZM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZM), + ); x.map(|y| y.into()) } w if w.eq_ignore_ascii_case("MULTILINESTRING") => { - let x = as FromTokens>::from_tokens_with_header(tokens); + let x = + as FromTokens>::from_tokens_with_header(tokens, None); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("MULTILINESTRINGZ") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZ), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("MULTILINESTRINGM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYM), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("MULTILINESTRINGZM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZM), + ); x.map(|y| y.into()) } w if w.eq_ignore_ascii_case("MULTIPOLYGON") => { - let x = as FromTokens>::from_tokens_with_header(tokens); + let x = as FromTokens>::from_tokens_with_header(tokens, None); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("MULTIPOLYGONZ") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZ), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("MULTIPOLYGONM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYM), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("MULTIPOLYGONZM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZM), + ); x.map(|y| y.into()) } w if w.eq_ignore_ascii_case("GEOMETRYCOLLECTION") => { - let x = as FromTokens>::from_tokens_with_header(tokens); + let x = + as FromTokens>::from_tokens_with_header(tokens, None); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("GEOMETRYCOLLECTIONZ") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZ), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("GEOMETRYCOLLECTIONM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYM), + ); + x.map(|y| y.into()) + } + w if w.eq_ignore_ascii_case("GEOMETRYCOLLECTIONZM") => { + let x = as FromTokens>::from_tokens_with_header( + tokens, + Some(Dimension::XYZM), + ); x.map(|y| y.into()) } _ => Err("Invalid type encountered"), @@ -279,8 +432,15 @@ where /// The preferred top-level FromTokens API, which additionally checks for the presence of Z, M, /// and ZM in the token stream. - fn from_tokens_with_header(tokens: &mut PeekableTokens) -> Result { - let dim = infer_geom_dimension(tokens)?; + fn from_tokens_with_header( + tokens: &mut PeekableTokens, + dim: Option, + ) -> Result { + let dim = if let Some(dim) = dim { + dim + } else { + infer_geom_dimension(tokens)? + }; FromTokens::from_tokens_with_parens(tokens, dim) } diff --git a/src/types/linestring.rs b/src/types/linestring.rs index 54ed424..ff84bcf 100644 --- a/src/types/linestring.rs +++ b/src/types/linestring.rs @@ -153,6 +153,28 @@ mod tests { assert_eq!(Some(5.0), coords[1].m); } + #[test] + fn basic_linestring_zm_one_word() { + let wkt = Wkt::from_str("LINESTRINGZM (-117 33 2 3, -116 34 4 5)") + .ok() + .unwrap(); + let coords = match wkt { + Wkt::LineString(LineString(coords)) => coords, + _ => unreachable!(), + }; + assert_eq!(2, coords.len()); + + assert_eq!(-117.0, coords[0].x); + assert_eq!(33.0, coords[0].y); + assert_eq!(Some(2.0), coords[0].z); + assert_eq!(Some(3.0), coords[0].m); + + assert_eq!(-116.0, coords[1].x); + assert_eq!(34.0, coords[1].y); + assert_eq!(Some(4.0), coords[1].z); + assert_eq!(Some(5.0), coords[1].m); + } + #[test] fn write_empty_linestring() { let linestring: LineString = LineString(vec![]); diff --git a/src/types/point.rs b/src/types/point.rs index 92e448b..a4d6123 100644 --- a/src/types/point.rs +++ b/src/types/point.rs @@ -98,6 +98,19 @@ mod tests { assert_eq!(None, coord.m); } + #[test] + fn basic_point_z_one_word() { + let wkt = Wkt::from_str("POINTZ(-117 33 10)").ok().unwrap(); + let coord = match wkt { + Wkt::Point(Point(Some(coord))) => coord, + _ => unreachable!(), + }; + assert_eq!(-117.0, coord.x); + assert_eq!(33.0, coord.y); + assert_eq!(Some(10.0), coord.z); + assert_eq!(None, coord.m); + } + #[test] fn basic_point_whitespace() { let wkt: Wkt = Wkt::from_str(" \n\t\rPOINT \n\t\r( \n\r\t10 \n\t\r-20 \n\t\r) \n\t\r")