Repository: developmentseed/cql2-rs (language: python)

Issue:
Add tests for Z, ZM, and M variants of WKT
Add tests and fix any issues when dealing with WKT with 3/4 dimensions.


Discussion / hints:
example 49 in the tests is an example where the generated cql2-text and cql2-json is silently dropping the Z coordinate

Fix patch:
diff --git a/Cargo.lock b/Cargo.lock
index 9274cfc..b400c9e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -117,6 +117,8 @@ version = "0.1.0"
 dependencies = [
  "assert-json-diff",
  "boon",
+ "geo-types",
+ "geojson",
  "geozero",
  "lazy_static",
  "pest",
@@ -174,9 +176,9 @@ dependencies = [
 
 [[package]]
 name = "futures"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -189,9 +191,9 @@ dependencies = [
 
 [[package]]
 name = "futures-channel"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
 dependencies = [
  "futures-core",
  "futures-sink",
@@ -199,15 +201,15 @@ dependencies = [
 
 [[package]]
 name = "futures-core"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
 
 [[package]]
 name = "futures-executor"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
 dependencies = [
  "futures-core",
  "futures-task",
@@ -216,15 +218,15 @@ dependencies = [
 
 [[package]]
 name = "futures-io"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
 
 [[package]]
 name = "futures-macro"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -233,15 +235,15 @@ dependencies = [
 
 [[package]]
 name = "futures-sink"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
 
 [[package]]
 name = "futures-task"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
 
 [[package]]
 name = "futures-timer"
@@ -251,9 +253,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
 
 [[package]]
 name = "futures-util"
-version = "0.3.30"
+version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
 dependencies = [
  "futures-channel",
  "futures-core",
@@ -294,6 +296,7 @@ version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b"
 dependencies = [
+ "geo-types",
  "log",
  "serde",
  "serde_json",
@@ -724,9 +727,9 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.15"
+version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
 
 [[package]]
 name = "unicode-ident"
diff --git a/Cargo.toml b/Cargo.toml
index 3122710..cc11957 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,15 +4,17 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-geozero = "0.14"
-boon = "0.6"
+boon = "0.6.0"
+geo-types = "0.7.13"
+geojson = "0.24.1"
+geozero = "0.14.0"
 lazy_static = "1.5"
 pest = "2.7"
 pest_derive = "2.7"
 serde = "1.0"
 serde_derive = "1.0"
-thiserror = "1.0"
 serde_json = { version = "1.0", features = ["preserve_order"] }
+thiserror = "1.0"
 
 [dev-dependencies]
 assert-json-diff = "2"
diff --git a/src/bin/cql2json.rs b/src/bin/cql2json.rs
index fb2031e..cd8852b 100644
--- a/src/bin/cql2json.rs
+++ b/src/bin/cql2json.rs
@@ -1,6 +1,9 @@
 use cql2::parse_stdin;
 
 fn main() {
-    let parsed = parse_stdin().unwrap();
-    println!("{}", parsed.to_json().unwrap());
+    if let Ok(parsed) = parse_stdin() {
+        println!("{}", parsed.to_json().unwrap());
+    } else {
+        std::process::exit(1)
+    }
 }
diff --git a/src/bin/cql2text.rs b/src/bin/cql2text.rs
index 41ae10f..4c97a2d 100644
--- a/src/bin/cql2text.rs
+++ b/src/bin/cql2text.rs
@@ -1,6 +1,9 @@
 use cql2::parse_stdin;
 
 fn main() {
-    let parsed = parse_stdin().unwrap();
-    println!("{}", parsed.to_text().unwrap());
+    if let Ok(parsed) = parse_stdin() {
+        println!("{}", parsed.to_text().unwrap());
+    } else {
+        std::process::exit(1)
+    }
 }
diff --git a/src/cql2.pest b/src/cql2.pest
index 666eebb..13a1d98 100644
--- a/src/cql2.pest
+++ b/src/cql2.pest
@@ -35,7 +35,7 @@ PCOORDLIST         = _{ WHITESPACE* ~ LPAREN ~ COORDLIST ~ RPAREN ~ WHITESPACE*
 PCOORDLISTLIST     = _{ WHITESPACE* ~ LPAREN ~ PCOORDLIST ~ (COMMADELIM ~ PCOORDLIST)* ~ RPAREN ~ WHITESPACE* }
 PCOORDLISTLISTLIST = _{ WHITESPACE* ~ LPAREN ~ PCOORDLISTLIST ~ (COMMADELIM ~ PCOORDLISTLIST)* ~ RPAREN ~ WHITESPACE* }
 
-ZM = _{ WHITESPACE* ~ (^"Z" | ^"M" | ^"ZM")? ~ WHITESPACE* }
+ZM = _{ WHITESPACE* ~ (^"ZM" | ^"Z" | ^"M")? ~ WHITESPACE* }
 
 POINT      = @{ ^"POINT" ~ ZM ~ PCOORD }
 LINESTRING = @{ ^"LINESTRING" ~ ZM ~ PCOORDLIST }
diff --git a/src/error.rs b/src/error.rs
index 8888e73..c5ebd2f 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -7,6 +7,10 @@ pub enum Error {
     #[error(transparent)]
     BoonCompile(#[from] boon::CompileError),
 
+    /// [geojson::Error]
+    #[error(transparent)]
+    GeoJSON(#[from] geojson::Error),
+
     /// [geozero::error::GeozeroError]
     #[error(transparent)]
     Geozero(#[from] geozero::error::GeozeroError),
diff --git a/src/expr.rs b/src/expr.rs
index b4931db..b5bb74b 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -1,5 +1,4 @@
-use crate::{Error, SqlQuery, Validator};
-use geozero::{geojson::GeoJsonString, ToWkt};
+use crate::{Error, Geometry, SqlQuery, Validator};
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
 use std::str::FromStr;
@@ -31,7 +30,7 @@ pub enum Expr {
     Literal(String),
     Bool(bool),
     Array(Vec<Box<Expr>>),
-    Geometry(serde_json::Value),
+    Geometry(Geometry),
 }
 
 impl Expr {
@@ -79,10 +78,7 @@ impl Expr {
             }
             Expr::Date { date } => Ok(format!("DATE({})", date.to_text()?)),
             Expr::Timestamp { timestamp } => Ok(format!("TIMESTAMP({})", timestamp.to_text()?)),
-            Expr::Geometry(v) => {
-                let gj = GeoJsonString(v.to_string());
-                gj.to_wkt().map_err(Error::from)
-            }
+            Expr::Geometry(v) => v.to_wkt(),
             Expr::Array(v) => {
                 let array_els: Vec<String> =
                     v.iter().map(|a| a.to_text()).collect::<Result<_, _>>()?;
@@ -132,7 +128,7 @@ impl Expr {
     /// let expr = Expr::Bool(true);
     /// let s = expr.to_sql().unwrap();
     /// ```
-    pub fn to_sql(&self) -> Result<SqlQuery, geozero::error::GeozeroError> {
+    pub fn to_sql(&self) -> Result<SqlQuery, Error> {
         let params: &mut Vec<String> = &mut vec![];
         let query = self.to_sql_inner(params)?;
         Ok(SqlQuery {
@@ -141,10 +137,7 @@ impl Expr {
         })
     }
 
-    fn to_sql_inner(
-        &self,
-        params: &mut Vec<String>,
-    ) -> Result<String, geozero::error::GeozeroError> {
+    fn to_sql_inner(&self, params: &mut Vec<String>) -> Result<String, Error> {
         Ok(match self {
             Expr::Bool(v) => {
                 params.push(v.to_string());
@@ -169,8 +162,7 @@ impl Expr {
                 format!("TSTZRANGE({},{})", a[0], a[1],)
             }
             Expr::Geometry(v) => {
-                let gj = GeoJsonString(v.to_string());
-                params.push(format!("EPSG:4326;{}", gj.to_wkt()?));
+                params.push(format!("EPSG:4326;{}", v.to_wkt()?));
                 format!("${}", params.len())
             }
             Expr::Array(v) => {
@@ -289,3 +281,29 @@ impl FromStr for Expr {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::Expr;
+
+    #[test]
+    fn keep_z() {
+        let point: Expr = "POINT Z(-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();
+        assert_eq!("POINT M(-105.1019 40.1672 42)", point.to_text().unwrap());
+    }
+
+    #[test]
+    fn keep_zm() {
+        let point: Expr = "POINT ZM(-105.1019 40.1672 4981 42)".parse().unwrap();
+        assert_eq!(
+            "POINT ZM(-105.1019 40.1672 4981 42)",
+            point.to_text().unwrap()
+        );
+    }
+}
diff --git a/src/geometry.rs b/src/geometry.rs
new file mode 100644
index 0000000..03ccc3d
--- /dev/null
+++ b/src/geometry.rs
@@ -0,0 +1,82 @@
+use crate::Error;
+use geozero::{wkt::Wkt, CoordDimensions, ToGeo, ToWkt};
+use serde::{Deserialize, Serialize, Serializer};
+
+const DEFAULT_NDIM: usize = 2;
+
+/// Crate-specific geometry type to hold either WKT or GeoJSON.
+#[derive(Debug, Serialize, Deserialize, Clone)]
+#[serde(untagged)]
+pub enum Geometry {
+    /// A GeoJSON geometry.
+    GeoJSON(geojson::Geometry),
+
+    /// A WKT geometry.
+    #[serde(skip_deserializing, serialize_with = "to_geojson")]
+    Wkt(String),
+}
+
+impl Geometry {
+    /// Converts this geometry to Well-Known Text (WKT).
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use cql2::Geometry;
+    ///
+    /// let geometry: Geometry = serde_json::from_str(
+    ///      "{\"type\":\"Point\",\"coordinates\":[-105.1019,40.1672]}"
+    /// ).unwrap();
+    /// assert_eq!("POINT(-105.1019 40.1672)", geometry.to_wkt().unwrap());
+    /// ```
+    pub fn to_wkt(&self) -> Result<String, Error> {
+        match self {
+            Geometry::Wkt(wkt) => Ok(wkt.clone()),
+            Geometry::GeoJSON(geojson) => {
+                let dims = match geojson_ndims(geojson) {
+                    3 => CoordDimensions::xyz(),
+                    4 => CoordDimensions::xyzm(),
+                    _ => CoordDimensions::xy(),
+                };
+                let geometry: geo_types::Geometry<f64> = geojson.clone().try_into()?;
+                geometry.to_wkt_ndim(dims).map_err(Error::from)
+            }
+        }
+    }
+}
+
+fn to_geojson<S>(wkt: &String, serializer: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+{
+    use serde::ser::Error;
+
+    let geometry = Wkt(wkt).to_geo().map_err(Error::custom)?;
+    geojson::ser::serialize_geometry(&geometry, serializer)
+}
+
+fn geojson_ndims(geojson: &geojson::Geometry) -> usize {
+    use geojson::Value::*;
+    match &geojson.value {
+        Point(coords) => coords.len(),
+        MultiPoint(v) => v.first().map(|v| v.len()).unwrap_or(DEFAULT_NDIM),
+        LineString(v) => v.first().map(|v| v.len()).unwrap_or(DEFAULT_NDIM),
+        MultiLineString(v) => v
+            .first()
+            .and_then(|v| v.first())
+            .map(|v| v.len())
+            .unwrap_or(DEFAULT_NDIM),
+        Polygon(v) => v
+            .first()
+            .and_then(|v| v.first())
+            .map(|v| v.len())
+            .unwrap_or(DEFAULT_NDIM),
+        MultiPolygon(v) => v
+            .first()
+            .and_then(|v| v.first())
+            .and_then(|v| v.first())
+            .map(|v| v.len())
+            .unwrap_or(DEFAULT_NDIM),
+        GeometryCollection(v) => v.first().map(geojson_ndims).unwrap_or(DEFAULT_NDIM),
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index b1404e1..fc9cbfb 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,12 +4,13 @@
 
 mod error;
 mod expr;
+mod geometry;
 mod parser;
 mod validator;
 
 use serde_derive::{Deserialize, Serialize};
 use std::{fs, io::Read, path::Path};
-pub use {error::Error, expr::Expr, parser::parse_text, validator::Validator};
+pub use {error::Error, expr::Expr, geometry::Geometry, parser::parse_text, validator::Validator};
 
 /// A SQL query, broken into the query and parameters.
 #[derive(Debug, Serialize, Deserialize, Clone)]
diff --git a/src/parser.rs b/src/parser.rs
index d040c0f..5022730 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -1,5 +1,4 @@
-use crate::{Error, Expr};
-use geozero::{geojson::GeoJsonWriter, wkt::Wkt, CoordDimensions, GeozeroGeometry, ToJson};
+use crate::{Error, Expr, Geometry};
 use pest::{
     iterators::{Pair, Pairs},
     pratt_parser::PrattParser,
@@ -103,13 +102,7 @@ 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 => {
-                let geom_wkt = Wkt(primary.as_str());
-                let mut out: Vec<u8> = Vec::new();
-                let mut p = GeoJsonWriter::with_dims(&mut out, CoordDimensions::xyz());
-                let _ = geom_wkt.process_geom(&mut p);
-                Ok(Expr::Geometry(serde_json::from_str(&geom_wkt.to_json()?)?))
-            }
+            Rule::GEOMETRY => Ok(Expr::Geometry(Geometry::Wkt(primary.as_str().to_string()))),
             Rule::Function => {
                 let mut pairs = primary.into_inner();
                 let op = strip_quotes(
@@ -292,3 +285,14 @@ fn parse_expr(expression_pairs: Pairs<'_, Rule>) -> Result<Expr, Error> {
         })
         .parse(expression_pairs)
 }
+
+#[cfg(test)]
+mod tests {
+    use super::{CQL2Parser, Rule};
+    use pest::Parser;
+
+    #[test]
+    fn point_zm() {
+        CQL2Parser::parse(Rule::GEOMETRY, "POINT ZM(-105.1019 40.1672 4981 42)").unwrap();
+    }
+}


Test patch:
diff --git a/tests/expected/json/example24.json b/tests/expected/json/example24.json
index e0bdf87..647b084 100644
--- a/tests/expected/json/example24.json
+++ b/tests/expected/json/example24.json
@@ -1,1 +1,1 @@
-{"op":"s_intersects","args":[{"property":"geometry"},{"type":"Polygon","coordinates":[[[-10,-10],[10,-10],[10,10],[-10,-10]]]}]}
+{"op":"s_intersects","args":[{"property":"geometry"},{"type":"Polygon","coordinates":[[[-10.0,-10.0],[10.0,-10.0],[10.0,10.0],[-10.0,-10.0]]]}]}
diff --git a/tests/expected/text/clause7_10.txt b/tests/expected/text/clause7_10.txt
index 3286a73..26549b0 100644
--- a/tests/expected/text/clause7_10.txt
+++ b/tests/expected/text/clause7_10.txt
@@ -1,1 +1,6 @@
-s_crosses("road", POLYGON((43.7286 -79.2986,43.7311 -79.2996,43.7323 -79.2972,43.7326 -79.2971,43.735 -79.2981,43.735 -79.2982,43.7352 -79.2982,43.7357 -79.2956,43.7337 -79.2948,43.7343 -79.2933,43.7339 -79.2923,43.7327 -79.2947,43.732 -79.2942,43.7322 -79.2937,43.7306 -79.293,43.7303 -79.293,43.7299 -79.2928,43.7286 -79.2986)))
+s_crosses("road", POLYGON((43.7286 -79.2986, 43.7311 -79.2996, 43.7323 -79.2972,
+                        43.7326 -79.2971, 43.7350 -79.2981, 43.7350 -79.2982,
+                        43.7352 -79.2982, 43.7357 -79.2956, 43.7337 -79.2948,
+                        43.7343 -79.2933, 43.7339 -79.2923, 43.7327 -79.2947,
+                        43.7320 -79.2942, 43.7322 -79.2937, 43.7306 -79.2930,
+                        43.7303 -79.2930, 43.7299 -79.2928, 43.7286 -79.2986)))
diff --git a/tests/expected/text/clause7_16.txt b/tests/expected/text/clause7_16.txt
index 0072784..995d226 100644
--- a/tests/expected/text/clause7_16.txt
+++ b/tests/expected/text/clause7_16.txt
@@ -1,1 +1,7 @@
-s_crosses(LINESTRING(43.72992 -79.2998,43.73005 -79.2991,43.73006 -79.2984,43.7314 -79.2956,43.73259 -79.295,43.73266 -79.2945,43.7332 -79.2936,43.73378 -79.2936,43.73486 -79.2917), POLYGON((43.7286 -79.2986,43.7311 -79.2996,43.7323 -79.2972,43.7326 -79.2971,43.735 -79.2981,43.735 -79.2982,43.7352 -79.2982,43.7357 -79.2956,43.7337 -79.2948,43.7343 -79.2933,43.7339 -79.2923,43.7327 -79.2947,43.732 -79.2942,43.7322 -79.2937,43.7306 -79.293,43.7303 -79.293,43.7299 -79.2928,43.7286 -79.2986)))
+s_crosses(LINESTRING(43.72992 -79.2998, 43.73005 -79.2991, 43.73006 -79.2984,
+                     43.73140 -79.2956, 43.73259 -79.2950, 43.73266 -79.2945,
+                     43.73320 -79.2936, 43.73378 -79.2936, 43.73486 -79.2917), POLYGON((43.7286 -79.2986, 43.7311 -79.2996, 43.7323 -79.2972, 43.7326 -79.2971,
+                 43.7350 -79.2981, 43.7350 -79.2982, 43.7352 -79.2982, 43.7357 -79.2956,
+                 43.7337 -79.2948, 43.7343 -79.2933, 43.7339 -79.2923, 43.7327 -79.2947,
+                 43.7320 -79.2942, 43.7322 -79.2937, 43.7306 -79.2930, 43.7303 -79.2930,
+                 43.7299 -79.2928, 43.7286 -79.2986)))
diff --git a/tests/expected/text/example07.txt b/tests/expected/text/example07.txt
index e7f6d32..f1d5689 100644
--- a/tests/expected/text/example07.txt
+++ b/tests/expected/text/example07.txt
@@ -1,1 +1,11 @@
-(like("eo:instrument", 'OLI%') AND s_intersects("footprint", POLYGON((43.5845 -79.5442,43.6079 -79.4893,43.5677 -79.4632,43.6129 -79.3925,43.6223 -79.3238,43.6576 -79.3163,43.7945 -79.1178,43.8144 -79.1542,43.8555 -79.1714,43.7509 -79.639,43.5845 -79.5442))))
+(like("eo:instrument", 'OLI%') AND s_intersects("footprint", POLYGON((43.5845 -79.5442,
+                                                    43.6079 -79.4893,
+                                                    43.5677 -79.4632,
+                                                    43.6129 -79.3925,
+                                                    43.6223 -79.3238,
+                                                    43.6576 -79.3163,
+                                                    43.7945 -79.1178,
+                                                    43.8144 -79.1542,
+                                                    43.8555 -79.1714,
+                                                    43.7509 -79.6390,
+                                                    43.5845 -79.5442))))
diff --git a/tests/expected/text/example08.txt b/tests/expected/text/example08.txt
index 3b5001b..c7223c2 100644
--- a/tests/expected/text/example08.txt
+++ b/tests/expected/text/example08.txt
@@ -1,1 +1,8 @@
-(("beamMode" = 'ScanSAR Narrow') AND ("swathDirection" = 'ascending') AND ("polarization" = 'HH+VV+HV+VH') AND s_intersects("footprint", POLYGON((-77.117938 38.93686,-77.040604 39.995648,-76.910536 38.892912,-77.039359 38.791753,-77.047906 38.841462,-77.034183 38.840655,-77.033142 38.85749,-77.117938 38.93686))))
+(("beamMode" = 'ScanSAR Narrow') AND ("swathDirection" = 'ascending') AND ("polarization" = 'HH+VV+HV+VH') AND s_intersects("footprint", POLYGON((-77.117938 38.936860,
+                                    -77.040604 39.995648,
+                                    -76.910536 38.892912,
+                                    -77.039359 38.791753,
+                                    -77.047906 38.841462,
+                                    -77.034183 38.840655,
+                                    -77.033142 38.857490,
+                                    -77.117938 38.936860))))
diff --git a/tests/expected/text/example24.json b/tests/expected/text/example24.json
index e0bdf87..647b084 100644
--- a/tests/expected/text/example24.json
+++ b/tests/expected/text/example24.json
@@ -1,1 +1,1 @@
-{"op":"s_intersects","args":[{"property":"geometry"},{"type":"Polygon","coordinates":[[[-10,-10],[10,-10],[10,10],[-10,-10]]]}]}
+{"op":"s_intersects","args":[{"property":"geometry"},{"type":"Polygon","coordinates":[[[-10.0,-10.0],[10.0,-10.0],[10.0,10.0],[-10.0,-10.0]]]}]}
diff --git a/tests/expected/text/example24.txt b/tests/expected/text/example24.txt
index c895573..db51e2c 100644
--- a/tests/expected/text/example24.txt
+++ b/tests/expected/text/example24.txt
@@ -1,1 +1,1 @@
-s_intersects("geometry", POLYGON((-10 -10,10 -10,10 10,-10 -10)))
+s_intersects("geometry", POLYGON((-10.0 -10.0,10.0 -10.0,10.0 10.0,-10.0 -10.0)))
diff --git a/tests/expected/text/example46-alt01.json b/tests/expected/text/example46-alt01.json
index f887385..3e4574b 100644
--- a/tests/expected/text/example46-alt01.json
+++ b/tests/expected/text/example46-alt01.json
@@ -1,1 +1,1 @@
-{"op":"s_equals","args":[{"type":"Polygon","coordinates":[[[-0.333333,89],[-102.723546,-0.5],[-179,-89],[-1.9,89],[-0.0,89],[2.00001,-1.9],[-0.333333,89]]]},{"property":"geometry"}]}
+{"op":"s_equals","args":[{"type":"Polygon","coordinates":[[[-0.333333,89.0],[-102.723546,-0.5],[-179.0,-89.0],[-1.9,89.0],[-0.0,89.0],[2.00001,-1.9],[-0.333333,89.0]]]},{"property":"geometry"}]}
diff --git a/tests/expected/text/example46-alt01.txt b/tests/expected/text/example46-alt01.txt
index bb9567f..3543e32 100644
--- a/tests/expected/text/example46-alt01.txt
+++ b/tests/expected/text/example46-alt01.txt
@@ -1,1 +1,3 @@
-s_equals(POLYGON((-0.333333 89,-102.723546 -0.5,-179 -89,-1.9 89,-0 89,2.00001 -1.9,-0.333333 89)), "geometry")
+s_equals(POLYGON (
+        (-0.333333 89.0, -102.723546 -0.5, -179.0 -89.0, -1.9 89.0, -0.0 89.0, 2.00001 -1.9, -0.333333 89.0)
+    ), "geometry")
diff --git a/tests/expected/text/example46.json b/tests/expected/text/example46.json
index f887385..3e4574b 100644
--- a/tests/expected/text/example46.json
+++ b/tests/expected/text/example46.json
@@ -1,1 +1,1 @@
-{"op":"s_equals","args":[{"type":"Polygon","coordinates":[[[-0.333333,89],[-102.723546,-0.5],[-179,-89],[-1.9,89],[-0.0,89],[2.00001,-1.9],[-0.333333,89]]]},{"property":"geometry"}]}
+{"op":"s_equals","args":[{"type":"Polygon","coordinates":[[[-0.333333,89.0],[-102.723546,-0.5],[-179.0,-89.0],[-1.9,89.0],[-0.0,89.0],[2.00001,-1.9],[-0.333333,89.0]]]},{"property":"geometry"}]}
diff --git a/tests/expected/text/example46.txt b/tests/expected/text/example46.txt
index bb9567f..6d81c78 100644
--- a/tests/expected/text/example46.txt
+++ b/tests/expected/text/example46.txt
@@ -1,1 +1,1 @@
-s_equals(POLYGON((-0.333333 89,-102.723546 -0.5,-179 -89,-1.9 89,-0 89,2.00001 -1.9,-0.333333 89)), "geometry")
+s_equals(POLYGON ((-0.333333 89.0, -102.723546 -0.5, -179.0 -89.0, -1.9 89.0, -0.0 89.0, 2.00001 -1.9, -0.333333 89.0)), "geometry")
diff --git a/tests/expected/text/example47.json b/tests/expected/text/example47.json
index 6937f7a..f8ab5ae 100644
--- a/tests/expected/text/example47.json
+++ b/tests/expected/text/example47.json
@@ -1,1 +1,1 @@
-{"op":"s_disjoint","args":[{"property":"geometry"},{"type":"MultiPolygon","coordinates":[[[[144.022387,45.176126],[-1.1,0],[180,47.808086],[144.022387,45.176126]]]]}]}
+{"op":"s_disjoint","args":[{"property":"geometry"},{"type":"MultiPolygon","coordinates":[[[[144.022387,45.176126],[-1.1,0.0],[180.0,47.808086],[144.022387,45.176126]]]]}]}
diff --git a/tests/expected/text/example47.txt b/tests/expected/text/example47.txt
index 0f33e66..5915229 100644
--- a/tests/expected/text/example47.txt
+++ b/tests/expected/text/example47.txt
@@ -1,1 +1,1 @@
-s_disjoint("geometry", MULTIPOLYGON(((144.022387 45.176126,-1.1 0,180 47.808086,144.022387 45.176126))))
+s_disjoint("geometry", MULTIPOLYGON (((144.022387 45.176126, -1.1 0.0, 180.0 47.808086, 144.022387 45.176126))))
diff --git a/tests/expected/text/example48.json b/tests/expected/text/example48.json
index a0be44d..acaa6d0 100644
--- a/tests/expected/text/example48.json
+++ b/tests/expected/text/example48.json
@@ -1,1 +1,1 @@
-{"op":"s_touches","args":[{"property":"geometry"},{"type":"MultiLineString","coordinates":[[[-1.9,-0.99999],[75.292574,1.5],[-0.5,-4.016458],[-31.708594,-74.743801],[179,-90]],[[-1.9,-1.1],[1.5,8.547371]]]}]}
+{"op":"s_touches","args":[{"property":"geometry"},{"type":"MultiLineString","coordinates":[[[-1.9,-0.99999],[75.292574,1.5],[-0.5,-4.016458],[-31.708594,-74.743801],[179.0,-90.0]],[[-1.9,-1.1],[1.5,8.547371]]]}]}
diff --git a/tests/expected/text/example48.txt b/tests/expected/text/example48.txt
index 70b9299..e190d62 100644
--- a/tests/expected/text/example48.txt
+++ b/tests/expected/text/example48.txt
@@ -1,1 +1,1 @@
-s_touches("geometry", MULTILINESTRING((-1.9 -0.99999,75.292574 1.5,-0.5 -4.016458,-31.708594 -74.743801,179 -90),(-1.9 -1.1,1.5 8.547371)))
+s_touches("geometry", MULTILINESTRING ((-1.9 -0.99999, 75.292574 1.5, -0.5 -4.016458, -31.708594 -74.743801, 179.0 -90.0),(-1.9 -1.1, 1.5 8.547371)))
diff --git a/tests/expected/text/example49-alt01.json b/tests/expected/text/example49-alt01.json
index 347a400..56a412e 100644
--- a/tests/expected/text/example49-alt01.json
+++ b/tests/expected/text/example49-alt01.json
@@ -1,1 +1,1 @@
-{"op":"s_within","args":[{"type":"Polygon","coordinates":[[[-49.88024,0.5],[-1.5,-0.99999],[0,0.5],[-49.88024,0.5]],[[-65.887123,2.00001],[0.333333,-53.017711],[180,0],[-65.887123,2.00001]]]},{"property":"geometry"}]}
+{"op":"s_within","args":[{"type":"Polygon","coordinates":[[[-49.88024,0.5],[-1.5,-0.99999],[0.0,0.5],[-49.88024,0.5]],[[-65.887123,2.00001],[0.333333,-53.017711],[180.0,0.0],[-65.887123,2.00001]]]},{"property":"geometry"}]}
diff --git a/tests/expected/text/example49-alt01.txt b/tests/expected/text/example49-alt01.txt
index 566c287..fc17db5 100644
--- a/tests/expected/text/example49-alt01.txt
+++ b/tests/expected/text/example49-alt01.txt
@@ -1,1 +1,1 @@
-s_within(POLYGON((-49.88024 0.5,-1.5 -0.99999,0 0.5,-49.88024 0.5),(-65.887123 2.00001,0.333333 -53.017711,180 0,-65.887123 2.00001)), "geometry")
+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")
diff --git a/tests/expected/text/example49.json b/tests/expected/text/example49.json
index 347a400..56a412e 100644
--- a/tests/expected/text/example49.json
+++ b/tests/expected/text/example49.json
@@ -1,1 +1,1 @@
-{"op":"s_within","args":[{"type":"Polygon","coordinates":[[[-49.88024,0.5],[-1.5,-0.99999],[0,0.5],[-49.88024,0.5]],[[-65.887123,2.00001],[0.333333,-53.017711],[180,0],[-65.887123,2.00001]]]},{"property":"geometry"}]}
+{"op":"s_within","args":[{"type":"Polygon","coordinates":[[[-49.88024,0.5],[-1.5,-0.99999],[0.0,0.5],[-49.88024,0.5]],[[-65.887123,2.00001],[0.333333,-53.017711],[180.0,0.0],[-65.887123,2.00001]]]},{"property":"geometry"}]}
diff --git a/tests/expected/text/example49.txt b/tests/expected/text/example49.txt
index 566c287..d175592 100644
--- a/tests/expected/text/example49.txt
+++ b/tests/expected/text/example49.txt
@@ -1,1 +1,1 @@
-s_within(POLYGON((-49.88024 0.5,-1.5 -0.99999,0 0.5,-49.88024 0.5),(-65.887123 2.00001,0.333333 -53.017711,180 0,-65.887123 2.00001)), "geometry")
+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")
diff --git a/tests/expected/text/example51.json b/tests/expected/text/example51.json
index 5c66f70..a1b90f1 100644
--- a/tests/expected/text/example51.json
+++ b/tests/expected/text/example51.json
@@ -1,1 +1,1 @@
-{"op":"s_crosses","args":[{"property":"geometry"},{"type":"LineString","coordinates":[[172.03086,1.5],[1.1,-90],[-159.757695,0.99999],[-180,0.5],[-12.111235,81.336403],[-0.5,64.43958],[0,81.991815],[-155.93831,90]]}]}
+{"op":"s_crosses","args":[{"property":"geometry"},{"type":"LineString","coordinates":[[172.03086,1.5],[1.1,-90.0],[-159.757695,0.99999],[-180.0,0.5],[-12.111235,81.336403],[-0.5,64.43958],[0.0,81.991815],[-155.93831,90.0]]}]}
diff --git a/tests/expected/text/example51.txt b/tests/expected/text/example51.txt
index 43f9619..4666f5a 100644
--- a/tests/expected/text/example51.txt
+++ b/tests/expected/text/example51.txt
@@ -1,1 +1,1 @@
-s_crosses("geometry", LINESTRING(172.03086 1.5,1.1 -90,-159.757695 0.99999,-180 0.5,-12.111235 81.336403,-0.5 64.43958,0 81.991815,-155.93831 90))
+s_crosses("geometry", LINESTRING (172.03086 1.5, 1.1 -90.0, -159.757695 0.99999, -180.0 0.5, -12.111235 81.336403, -0.5 64.43958, 0.0 81.991815, -155.93831 90.0))
diff --git a/tests/expected/text/example52.txt b/tests/expected/text/example52.txt
index d970c3f..357ee7a 100644
--- a/tests/expected/text/example52.txt
+++ b/tests/expected/text/example52.txt
@@ -1,1 +1,1 @@
-s_contains("geometry", POINT(-3.508362 -1.754181))
+s_contains("geometry", POINT (-3.508362 -1.754181))
diff --git a/tests/expected/text/example83.json b/tests/expected/text/example83.json
index a6069b1..8cd0d1b 100644
--- a/tests/expected/text/example83.json
+++ b/tests/expected/text/example83.json
@@ -1,1 +1,1 @@
-{"op":"s_equals","args":[{"type":"MultiPoint","coordinates":[[180,-0.5],[179,-47.121701],[180,-0.0],[33.470475,-0.99999],[179,-15.333062]]},{"property":"geometry"}]}
+{"op":"s_equals","args":[{"type":"MultiPoint","coordinates":[[180.0,-0.5],[179.0,-47.121701],[180.0,-0.0],[33.470475,-0.99999],[179.0,-15.333062]]},{"property":"geometry"}]}
diff --git a/tests/expected/text/example83.txt b/tests/expected/text/example83.txt
index 680973f..4c89ff6 100644
--- a/tests/expected/text/example83.txt
+++ b/tests/expected/text/example83.txt
@@ -1,1 +1,1 @@
-s_equals(MULTIPOINT(180 -0.5,179 -47.121701,180 -0,33.470475 -0.99999,179 -15.333062), "geometry")
+s_equals(MULTIPOINT ((180.0 -0.5), (179.0 -47.121701), (180.0 -0.0), (33.470475 -0.99999), (179.0 -15.333062)), "geometry")
diff --git a/tests/expected/text/example84.json b/tests/expected/text/example84.json
index 37c2407..475c6e4 100644
--- a/tests/expected/text/example84.json
+++ b/tests/expected/text/example84.json
@@ -1,1 +1,1 @@
-{"op":"s_equals","args":[{"type":"GeometryCollection","geometries":[{"type":"Point","coordinates":[1.9,2.00001]},{"type":"Point","coordinates":[0,-2.00001]},{"type":"MultiLineString","coordinates":[[[-2.00001,-0.0],[-77.292642,-0.5],[-87.515626,-0.0],[-180,12.502773],[21.204842,-1.5],[-21.878857,-90]]]},{"type":"Point","coordinates":[1.9,0.5]},{"type":"LineString","coordinates":[[179,1.179148],[-148.192487,-65.007816],[0.5,0.333333]]}]},{"property":"geometry"}]}
+{"op":"s_equals","args":[{"type":"GeometryCollection","geometries":[{"type":"Point","coordinates":[1.9,2.00001]},{"type":"Point","coordinates":[0.0,-2.00001]},{"type":"MultiLineString","coordinates":[[[-2.00001,-0.0],[-77.292642,-0.5],[-87.515626,-0.0],[-180.0,12.502773],[21.204842,-1.5],[-21.878857,-90.0]]]},{"type":"Point","coordinates":[1.9,0.5]},{"type":"LineString","coordinates":[[179.0,1.179148],[-148.192487,-65.007816],[0.5,0.333333]]}]},{"property":"geometry"}]}
diff --git a/tests/expected/text/example84.txt b/tests/expected/text/example84.txt
index 894380d..ac58619 100644
--- a/tests/expected/text/example84.txt
+++ b/tests/expected/text/example84.txt
@@ -1,1 +1,1 @@
-s_equals(GEOMETRYCOLLECTION(POINT(1.9 2.00001),POINT(0 -2.00001),MULTILINESTRING((-2.00001 -0,-77.292642 -0.5,-87.515626 -0,-180 12.502773,21.204842 -1.5,-21.878857 -90)),POINT(1.9 0.5),LINESTRING(179 1.179148,-148.192487 -65.007816,0.5 0.333333)), "geometry")
+s_equals(GEOMETRYCOLLECTION (POINT (1.9 2.00001), POINT (0.0 -2.00001), MULTILINESTRING ((-2.00001 -0.0, -77.292642 -0.5, -87.515626 -0.0, -180.0 12.502773, 21.204842 -1.5, -21.878857 -90.0)), POINT (1.9 0.5), LINESTRING (179.0 1.179148, -148.192487 -65.007816, 0.5 0.333333)), "geometry")