Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow implicit Z geometries #25

Merged
merged 1 commit into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ geojson = "0.24.1"
geozero = "0.14.0"
lazy_static = "1.5"
pest = "2.7"
pest_derive = "2.7"
pest_derive = { version = "2.7", features = ["grammar-extras"] }
serde = "1.0"
serde_derive = "1.0"
serde_json = { version = "1.0", features = ["preserve_order"] }
Expand Down
38 changes: 19 additions & 19 deletions src/cql2.pest
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,32 @@ TIMESTAMP_STR = { DATE_STR ~ ("T" | " ") ~ TIME_STR }
TORD = { QUOTE ~ (TIMESTAMP_STR | DATE_STR) ~ QUOTE }

// wkt
PADDED_DECIMAL = _{ WHITESPACE* ~ DECIMAL* ~ WHITESPACE* }
COORD = _{ PADDED_DECIMAL{1, 4} }
PCOORD = _{ WHITESPACE* ~ LPAREN ~ COORD ~ RPAREN ~ WHITESPACE* }
COORDLIST = _{ WHITESPACE* ~ COORD ~ (COMMADELIM ~ COORD)* ~ WHITESPACE* }
PCOORDLIST = _{ WHITESPACE* ~ LPAREN ~ COORDLIST ~ RPAREN ~ WHITESPACE* }
PCOORDLISTLIST = _{ WHITESPACE* ~ LPAREN ~ PCOORDLIST ~ (COMMADELIM ~ PCOORDLIST)* ~ RPAREN ~ WHITESPACE* }
PCOORDLISTLISTLIST = _{ WHITESPACE* ~ LPAREN ~ PCOORDLISTLIST ~ (COMMADELIM ~ PCOORDLISTLIST)* ~ RPAREN ~ WHITESPACE* }
PADDED_DECIMAL = { WHITESPACE* ~ DECIMAL ~ WHITESPACE* }
COORD = { #four_d = PADDED_DECIMAL{4} | #three_d = PADDED_DECIMAL{3} | #two_d = PADDED_DECIMAL{2} }
gadomski marked this conversation as resolved.
Show resolved Hide resolved
PCOORD = { WHITESPACE* ~ LPAREN ~ COORD ~ RPAREN ~ WHITESPACE* }
COORDLIST = { WHITESPACE* ~ COORD ~ (COMMADELIM ~ COORD)* ~ WHITESPACE* }
PCOORDLIST = { WHITESPACE* ~ LPAREN ~ COORDLIST ~ RPAREN ~ WHITESPACE* }
PCOORDLISTLIST = { WHITESPACE* ~ LPAREN ~ PCOORDLIST ~ (COMMADELIM ~ PCOORDLIST)* ~ RPAREN ~ WHITESPACE* }
PCOORDLISTLISTLIST = { WHITESPACE* ~ LPAREN ~ PCOORDLISTLIST ~ (COMMADELIM ~ PCOORDLISTLIST)* ~ RPAREN ~ WHITESPACE* }

ZM = _{ WHITESPACE* ~ (^"ZM" | ^"Z" | ^"M")? ~ WHITESPACE* }
ZM = { WHITESPACE* ~ (^"ZM" | ^"Z" | ^"M")? ~ WHITESPACE* }

POINT = @{ ^"POINT" ~ ZM ~ PCOORD }
LINESTRING = @{ ^"LINESTRING" ~ ZM ~ PCOORDLIST }
POLYGON = @{ ^"POLYGON" ~ ZM ~ PCOORDLISTLIST }
POINT = ${ ^"POINT" ~ ZM ~ PCOORD }
LINESTRING = ${ ^"LINESTRING" ~ ZM ~ PCOORDLIST }
POLYGON = ${ ^"POLYGON" ~ ZM ~ PCOORDLISTLIST }

MULTIPOINT_1 = _{ ^"MULTIPOINT" ~ ZM ~ PCOORDLIST }
MULTIPOINT_2 = _{ ^"MULTIPOINT" ~ ZM ~ PCOORDLISTLIST }
MULTIPOINT = @{ MULTIPOINT_1 | MULTIPOINT_2 }
MULTIPOINT_1 = ${ ^"MULTIPOINT" ~ ZM ~ PCOORDLIST }
MULTIPOINT_2 = ${ ^"MULTIPOINT" ~ ZM ~ PCOORDLISTLIST }
MULTIPOINT = ${ MULTIPOINT_1 | MULTIPOINT_2 }

MULTILINESTRING = @{ ^"MULTILINESTRING" ~ ZM ~ PCOORDLISTLIST }
MULTIPOLYGON = @{ ^"MULTIPOLYGON" ~ ZM ~ PCOORDLISTLISTLIST }
MULTILINESTRING = ${ ^"MULTILINESTRING" ~ ZM ~ PCOORDLISTLIST }
MULTIPOLYGON = ${ ^"MULTIPOLYGON" ~ ZM ~ PCOORDLISTLISTLIST }

GEOMETRY_SINGLE = _{ WHITESPACE* ~ (POINT | LINESTRING | POLYGON | MULTIPOINT | MULTILINESTRING | MULTIPOLYGON) ~ WHITESPACE* }
GEOMETRY_SINGLE = ${ WHITESPACE* ~ (POINT | LINESTRING | POLYGON | MULTIPOINT | MULTILINESTRING | MULTIPOLYGON) ~ WHITESPACE* }

GEOMETRY_COLLECTION = @{ ^"GEOMETRYCOLLECTION" ~ WHITESPACE* ~ LPAREN ~ GEOMETRY_SINGLE ~ (COMMADELIM ~ GEOMETRY_SINGLE)* ~ RPAREN }
GEOMETRY_COLLECTION = ${ ^"GEOMETRYCOLLECTION" ~ WHITESPACE* ~ LPAREN ~ GEOMETRY_SINGLE ~ (COMMADELIM ~ GEOMETRY_SINGLE)* ~ RPAREN }

GEOMETRY = @{ GEOMETRY_SINGLE | GEOMETRY_COLLECTION }
GEOMETRY = ${ GEOMETRY_SINGLE | GEOMETRY_COLLECTION }

IdentifierInner = _{
ALPHABETIC ~ (ALPHABETIC | NUMBER | UNDERSCORE | PERIOD | COLON)*
Expand Down
7 changes: 6 additions & 1 deletion src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,6 @@ impl FromStr for Expr {
}
}
}

#[cfg(test)]
mod tests {
use super::Expr;
Expand All @@ -292,6 +291,12 @@ mod tests {
assert_eq!("POINT Z(-105.1019 40.1672 4981)", point.to_text().unwrap());
}

#[test]
fn implicit_z() {
let point: Expr = "POINT (-105.1019 40.1672 4981)".parse().unwrap();
assert_eq!("POINT Z(-105.1019 40.1672 4981)", point.to_text().unwrap());
}

#[test]
fn keep_m() {
let point: Expr = "POINT M(-105.1019 40.1672 42)".parse().unwrap();
Expand Down
25 changes: 24 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,30 @@ fn parse_expr(expression_pairs: Pairs<'_, Rule>) -> Result<Expr, Error> {
Rule::Identifier => Ok(Expr::Property {
property: strip_quotes(primary.as_str()).to_string(),
}),
Rule::GEOMETRY => Ok(Expr::Geometry(Geometry::Wkt(primary.as_str().to_string()))),
Rule::GEOMETRY => {
// These are some incredibly annoying backflips to handle
// geometries without `Z` but that have 3D coordinates. It's
// not part of OGC WKT, but CQL2 demands 🤦‍♀️.
let start = primary.as_span().start();
let s = primary.as_str().to_string();
let pairs = primary.into_inner();
if pairs.find_first_tagged("three_d").is_some() {
let zm = pairs
.flatten()
.find(|pair| matches!(pair.as_rule(), Rule::ZM))
.expect("all geometries should have a ZM rule");
if zm.as_str().chars().all(|c| c.is_ascii_whitespace()) {
let span = zm.as_span();
let s = format!(
"{} Z{}",
&s[0..span.start() - start],
&s[span.end() - start..]
);
return Ok(Expr::Geometry(Geometry::Wkt(s)));
}
}
Ok(Expr::Geometry(Geometry::Wkt(s)))
}
Rule::Function => {
let mut pairs = primary.into_inner();
let op = strip_quotes(
Expand Down
8 changes: 6 additions & 2 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# Expected test output
# Tests

The `fixtures` directory is copied directly from <https://github.com/opengeospatial/ogcapi-features/tree/cql2-1.0.0/cql2/standard/schema/examples>.

## Expected test output

To generate:

```shell
tests/expected/generate
tests/generate-expected
```
2 changes: 1 addition & 1 deletion tests/fixtures/text/example49-alt01.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
S_WITHIN(POLYGON Z((-49.88024 0.5 -75993.341684, -1.5 -0.99999 -100000.0, 0.0 0.5 -0.333333, -49.88024 0.5 -75993.341684), (-65.887123 2.00001 -100000.0, 0.333333 -53.017711 -79471.332949, 180.0 0.0 1852.616704, -65.887123 2.00001 -100000.0)), "geometry")
S_WITHIN(POLYGON ((-49.88024 0.5 -75993.341684, -1.5 -0.99999 -100000.0, 0.0 0.5 -0.333333, -49.88024 0.5 -75993.341684), (-65.887123 2.00001 -100000.0, 0.333333 -53.017711 -79471.332949, 180.0 0.0 1852.616704, -65.887123 2.00001 -100000.0)), "geometry")
Loading