From 93dedd48697c69fd1a152753699f15683f1a13ac Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Fri, 2 Sep 2022 11:32:34 +0100 Subject: [PATCH] Fix SQL Geometry type serialization and output Closes #93 --- lib/src/sql/geometry.rs | 51 +++++++-- lib/tests/geometry.rs | 229 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 274 insertions(+), 6 deletions(-) create mode 100644 lib/tests/geometry.rs diff --git a/lib/src/sql/geometry.rs b/lib/src/sql/geometry.rs index 8735c00f..7646d322 100644 --- a/lib/src/sql/geometry.rs +++ b/lib/src/sql/geometry.rs @@ -373,10 +373,14 @@ impl Serialize for Geometry { Geometry::Point(v) => s.serialize_newtype_variant("Geometry", 0, "Point", v), Geometry::Line(v) => s.serialize_newtype_variant("Geometry", 1, "Line", v), Geometry::Polygon(v) => s.serialize_newtype_variant("Geometry", 2, "Polygon", v), - Geometry::MultiPoint(v) => s.serialize_newtype_variant("Geometry", 3, "Points", v), - Geometry::MultiLine(v) => s.serialize_newtype_variant("Geometry", 4, "Lines", v), + Geometry::MultiPoint(v) => { + s.serialize_newtype_variant("Geometry", 3, "MultiPoint", v) + } + Geometry::MultiLine(v) => { + s.serialize_newtype_variant("Geometry", 4, "MultiLine", v) + } Geometry::MultiPolygon(v) => { - s.serialize_newtype_variant("Geometry", 5, "Polygons", v) + s.serialize_newtype_variant("Geometry", 5, "MultiPolygon", v) } Geometry::Collection(v) => { s.serialize_newtype_variant("Geometry", 6, "Collection", v) @@ -437,7 +441,12 @@ impl Serialize for Geometry { map.serialize_key("type")?; map.serialize_value("MultiPoint")?; map.serialize_key("coordinates")?; - map.serialize_value(v.0.as_slice())?; + map.serialize_value( + v.0.iter() + .map(|v| vec![v.x(), v.y()]) + .collect::>>() + .as_slice(), + )?; map.end() } Geometry::MultiLine(v) => { @@ -445,7 +454,14 @@ impl Serialize for Geometry { map.serialize_key("type")?; map.serialize_value("MultiLineString")?; map.serialize_key("coordinates")?; - map.serialize_value(v.0.as_slice())?; + map.serialize_value( + v.0.iter() + .map(|v| { + v.points().map(|v| vec![v.x(), v.y()]).collect::>>() + }) + .collect::>>>() + .as_slice(), + )?; map.end() } Geometry::MultiPolygon(v) => { @@ -453,7 +469,30 @@ impl Serialize for Geometry { map.serialize_key("type")?; map.serialize_value("MultiPolygon")?; map.serialize_key("coordinates")?; - map.serialize_value(v.0.as_slice())?; + map.serialize_value( + v.0.iter() + .map(|v| { + vec![v + .exterior() + .points() + .map(|p| vec![p.x(), p.y()]) + .collect::>>()] + .into_iter() + .chain( + v.interiors() + .iter() + .map(|i| { + i.points() + .map(|p| vec![p.x(), p.y()]) + .collect::>>() + }) + .collect::>>>(), + ) + .collect::>>>() + }) + .collect::>>>>() + .as_slice(), + )?; map.end() } Geometry::Collection(v) => { diff --git a/lib/tests/geometry.rs b/lib/tests/geometry.rs new file mode 100644 index 00000000..e006b683 --- /dev/null +++ b/lib/tests/geometry.rs @@ -0,0 +1,229 @@ +mod parse; +use parse::Parse; +use surrealdb::sql::Value; +use surrealdb::Datastore; +use surrealdb::Error; +use surrealdb::Session; + +#[tokio::test] +async fn geometry_point() -> Result<(), Error> { + let sql = " + UPDATE city:london SET centre = (-0.118092, 51.509865); + SELECT * FROM city:london; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 2); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + r#"[ + { + "centre": { + "type": "Point", + "coordinates": [-0.118092, 51.509865] + }, + "id": "city:london" + } + ]"#, + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + r#"[ + { + "centre": { + "type": "Point", + "coordinates": [-0.118092, 51.509865] + }, + "id": "city:london" + } + ]"#, + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn geometry_polygon() -> Result<(), Error> { + let sql = " + UPDATE city:london SET area = { + type: 'Polygon', + coordinates: [[ + [-0.38314819, 51.37692386], [0.1785278, 51.37692386], + [0.1785278, 51.61460570], [-0.38314819, 51.61460570], + [-0.38314819, 51.37692386] + ]] + }; + SELECT * FROM city:london; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 2); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + r#"[ + { + "area": { + "type": "Polygon", + "coordinates": [ + [ + [-0.38314819, 51.37692386], + [0.1785278, 51.37692386], + [0.1785278, 51.6146057], + [-0.38314819, 51.6146057], + [-0.38314819, 51.37692386] + ] + ] + }, + "id": "city:london" + } + ]"#, + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + r#"[ + { + "area": { + "type": "Polygon", + "coordinates": [ + [ + [-0.38314819, 51.37692386], + [0.1785278, 51.37692386], + [0.1785278, 51.6146057], + [-0.38314819, 51.6146057], + [-0.38314819, 51.37692386] + ] + ] + }, + "id": "city:london" + } + ]"#, + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn geometry_multipoint() -> Result<(), Error> { + let sql = " + UPDATE city:london SET points = { + type: 'MultiPoint', + coordinates: [ + [-0.118092, 51.509865], + [-0.118092, 51.509865] + ] + }; + SELECT * FROM city:london; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 2); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + r#"[ + { + "points": { + "type": "MultiPoint", + "coordinates": [ + [-0.118092, 51.509865], + [-0.118092, 51.509865] + ] + }, + "id": "city:london" + } + ]"#, + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + r#"[ + { + "points": { + "type": "MultiPoint", + "coordinates": [ + [-0.118092, 51.509865], + [-0.118092, 51.509865] + ] + }, + "id": "city:london" + } + ]"#, + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn geometry_multipolygon() -> Result<(), Error> { + let sql = " + UPDATE university:oxford SET area = { + type: 'MultiPolygon', + coordinates: [ + [[ [10.0, 11.2], [10.5, 11.9], [10.8, 12.0], [10.0, 11.2] ]], + [[ [9.0, 11.2], [10.5, 11.9], [10.3, 13.0], [9.0, 11.2] ]] + ] + }; + SELECT * FROM university:oxford; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 2); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + r#"[ + { + "area": { + "type": "MultiPolygon", + "coordinates": [ + [ + [[10.0, 11.2], [10.5, 11.9], [10.8, 12.0], [10.0, 11.2]] + ], + [ + [[9.0, 11.2], [10.5, 11.9], [10.3, 13.0], [9.0, 11.2]] + ] + ] + }, + "id": "university:oxford" + } + ]"#, + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + r#"[ + { + "area": { + "type": "MultiPolygon", + "coordinates": [ + [ + [[10.0, 11.2], [10.5, 11.9], [10.8, 12.0], [10.0, 11.2]] + ], + [ + [[9.0, 11.2], [10.5, 11.9], [10.3, 13.0], [9.0, 11.2]] + ] + ] + }, + "id": "university:oxford" + } + ]"#, + ); + assert_eq!(tmp, val); + // + Ok(()) +}