From 1c093a9414f94f630093459de2f83dcacd4c7653 Mon Sep 17 00:00:00 2001 From: rfitzger Date: Tue, 27 Aug 2024 14:27:31 -0600 Subject: [PATCH 1/2] wkb read/write integration --- rust/Cargo.toml | 1 + rust/routee-compass-core/Cargo.toml | 1 + .../src/util/geo/geo_io_utils.rs | 39 +++++++++++++++++-- rust/routee-compass/Cargo.toml | 1 + rust/routee-compass/src/app/geom/geom_app.rs | 4 +- .../plugin/output/default/traversal/plugin.rs | 6 +-- .../traversal/traversal_output_format.rs | 33 +++++++++++++++- 7 files changed, 75 insertions(+), 10 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 4e806b8d..c8237ee0 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -26,6 +26,7 @@ itertools = { version = "0.12.0" } chrono = "0.4.26" regex = "1.9.5" wkt = { version = "0.10.3", features = ["serde"] } +wkb = "0.7.1" config = "0.14" ordered-float = { version = "4.1.1", features = ["serde"] } allocative = "0.3.1" diff --git a/rust/routee-compass-core/Cargo.toml b/rust/routee-compass-core/Cargo.toml index e9d8dc4f..df6bc7b7 100644 --- a/rust/routee-compass-core/Cargo.toml +++ b/rust/routee-compass-core/Cargo.toml @@ -28,4 +28,5 @@ itertools = { workspace = true } chrono = { workspace = true } regex = { workspace = true } wkt = { workspace = true } +wkb = { workspace = true } allocative = { workspace = true } diff --git a/rust/routee-compass-core/src/util/geo/geo_io_utils.rs b/rust/routee-compass-core/src/util/geo/geo_io_utils.rs index 3c8f0566..ed46ec1e 100644 --- a/rust/routee-compass-core/src/util/geo/geo_io_utils.rs +++ b/rust/routee-compass-core/src/util/geo/geo_io_utils.rs @@ -1,8 +1,10 @@ use crate::util::fs::{fs_utils, read_utils}; -use geo::{LineString, Point}; +use geo::{Coord, LineString, Point}; +use itertools::Itertools; use kdam::{Bar, BarExt}; use std::path::Path; -use wkt::TryFromWkt; +use wkb; +use wkt::{ToWkt, TryFromWkt}; /// reads a collection of LINESTRINGS pub fn read_linestring_text_file>( @@ -23,7 +25,7 @@ pub fn read_linestring_text_file>( let _ = pb.update(1); }); let geoms: Box<[LineString]> = - read_utils::read_raw_file(filepath, parse_linestring, Some(cb))?; + read_utils::read_raw_file(filepath, parse_wkt_linestring, Some(cb))?; Ok(geoms) } @@ -72,7 +74,7 @@ pub fn concat_linestrings(linestrings: Vec<&LineString>) -> LineString /// # Returns /// /// * a linestring -pub fn parse_linestring(_idx: usize, row: String) -> Result, std::io::Error> { +pub fn parse_wkt_linestring(_idx: usize, row: String) -> Result, std::io::Error> { let geom: LineString = LineString::try_from_wkt_str(row.as_str()).map_err(|e| { let msg = format!( "failure decoding LineString from lookup table. source: {}; error: {}", @@ -83,6 +85,35 @@ pub fn parse_linestring(_idx: usize, row: String) -> Result, std Ok(geom) } +pub fn parse_wkb_linestring(_idx: usize, row: String) -> Result, std::io::Error> { + let mut c = row.as_bytes(); + let geom = wkb::wkb_to_geom(&mut c).map_err(|e| { + let msg = format!("failure decoding WKB string: {:?}", e); + std::io::Error::new(std::io::ErrorKind::InvalidData, msg) + })?; + match geom { + geo::Geometry::LineString(l) => { + // somewhat hackish solution since we cannot choose f32 when parsing wkbs + let coords32 = + l.0.into_iter() + .map(|c| Coord { + x: c.x as f32, + y: c.y as f32, + }) + .collect_vec(); + let l32 = LineString::new(coords32); + Ok(l32) + } + _ => { + let msg = format!( + "decoded WKB expected to be linestring, found: {}", + geom.to_wkt() + ); + Err(std::io::Error::new(std::io::ErrorKind::InvalidData, msg)) + } + } +} + #[cfg(test)] mod test { diff --git a/rust/routee-compass/Cargo.toml b/rust/routee-compass/Cargo.toml index 12ea6ba7..f843607c 100644 --- a/rust/routee-compass/Cargo.toml +++ b/rust/routee-compass/Cargo.toml @@ -30,6 +30,7 @@ geo-types = { workspace = true } geojson = { workspace = true } rstar = { workspace = true } wkt = { workspace = true } +wkb = { workspace = true } env_logger = { workspace = true } kdam = { workspace = true } log = { workspace = true } diff --git a/rust/routee-compass/src/app/geom/geom_app.rs b/rust/routee-compass/src/app/geom/geom_app.rs index 7198ad97..5a51dd1b 100644 --- a/rust/routee-compass/src/app/geom/geom_app.rs +++ b/rust/routee-compass/src/app/geom/geom_app.rs @@ -3,7 +3,7 @@ use geo::LineString; use kdam::Bar; use kdam::BarExt; use routee_compass_core::util::fs::{fs_utils, read_utils}; -use routee_compass_core::util::geo::geo_io_utils::parse_linestring; +use routee_compass_core::util::geo::geo_io_utils::parse_wkt_linestring; use std::io::ErrorKind; pub struct GeomAppConfig { @@ -38,7 +38,7 @@ impl TryFrom<&GeomAppConfig> for GeomApp { }); let op = |idx: usize, row: String| { - let result = parse_linestring(idx, row)?; + let result = parse_wkt_linestring(idx, row)?; Ok(result) }; diff --git a/rust/routee-compass/src/plugin/output/default/traversal/plugin.rs b/rust/routee-compass/src/plugin/output/default/traversal/plugin.rs index 61a1849f..ef44ebb3 100644 --- a/rust/routee-compass/src/plugin/output/default/traversal/plugin.rs +++ b/rust/routee-compass/src/plugin/output/default/traversal/plugin.rs @@ -44,7 +44,7 @@ impl TraversalPlugin { let _ = pb.update(1); }); let geoms = - read_raw_file(filename, geo_io_utils::parse_linestring, Some(cb)).map_err(|e| { + read_raw_file(filename, geo_io_utils::parse_wkt_linestring, Some(cb)).map_err(|e| { PluginError::FileReadError(filename.as_ref().to_path_buf(), e.to_string()) })?; println!(); @@ -154,7 +154,7 @@ fn construct_route_output( mod tests { use routee_compass_core::util::{ - fs::read_utils::read_raw_file, geo::geo_io_utils::parse_linestring, + fs::read_utils::read_raw_file, geo::geo_io_utils::parse_wkt_linestring, }; use std::path::PathBuf; @@ -171,7 +171,7 @@ mod tests { #[test] fn test_geometry_deserialization() { - let result = read_raw_file(mock_geometry_file(), parse_linestring, None).unwrap(); + let result = read_raw_file(mock_geometry_file(), parse_wkt_linestring, None).unwrap(); assert_eq!(result.len(), 3); } diff --git a/rust/routee-compass/src/plugin/output/default/traversal/traversal_output_format.rs b/rust/routee-compass/src/plugin/output/default/traversal/traversal_output_format.rs index a24b5667..7c857db3 100644 --- a/rust/routee-compass/src/plugin/output/default/traversal/traversal_output_format.rs +++ b/rust/routee-compass/src/plugin/output/default/traversal/traversal_output_format.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use super::traversal_ops as ops; use crate::plugin::plugin_error::PluginError; -use geo::LineString; +use geo::{CoordFloat, CoordNum, Geometry, LineString}; use routee_compass_core::{ algorithm::search::{edge_traversal::EdgeTraversal, search_tree_branch::SearchTreeBranch}, model::road_network::vertex_id::VertexId, @@ -15,6 +15,8 @@ use wkt::ToWkt; pub enum TraversalOutputFormat { // concatenates all LINESTRINGS and returns the geometry as a WKT Wkt, + // concatenates all LINESTRINGS and returns the geometry as a WKB + Wkb, // returns the properties of each link traversal as a JSON array of objects Json, // returns the geometries and properties as GeoJSON @@ -35,6 +37,12 @@ impl TraversalOutputFormat { let route_wkt = route_geometry.wkt_string(); Ok(serde_json::Value::String(route_wkt)) } + TraversalOutputFormat::Wkb => { + let linestring = ops::create_route_linestring(route, geoms)?; + let geometry = geo::Geometry::LineString(linestring); + let wkb_str = geometry_to_wkb_string(&geometry)?; + Ok(serde_json::Value::String(wkb_str)) + } TraversalOutputFormat::Json => { let result = serde_json::to_value(route)?; Ok(result) @@ -63,6 +71,12 @@ impl TraversalOutputFormat { let route_wkt = route_geometry.wkt_string(); Ok(serde_json::Value::String(route_wkt)) } + TraversalOutputFormat::Wkb => { + let route_geometry = ops::create_tree_multilinestring(tree, geoms)?; + let geometry = geo::Geometry::MultiLineString(route_geometry); + let wkb_str = geometry_to_wkb_string(&geometry)?; + Ok(serde_json::Value::String(wkb_str)) + } TraversalOutputFormat::Json => { let result = serde_json::to_value(tree.values().collect::>())?; Ok(result) @@ -83,6 +97,23 @@ impl TraversalOutputFormat { } } +fn geometry_to_wkb_string>( + geometry: &Geometry, +) -> Result { + let bytes = wkb::geom_to_wkb(geometry).map_err(|e| { + PluginError::PluginFailed(format!( + "failed to generate wkb for geometry '{:?}' - {:?}", + geometry, e + )) + })?; + let wkb_str = bytes + .iter() + .map(|b| format!("{:02X?}", b)) + .collect::>() + .join(""); + Ok(wkb_str) +} + #[cfg(test)] mod test { use super::*; From 427299f4c108321bf5af02af5a43991cd2d57776 Mon Sep 17 00:00:00 2001 From: rfitzger Date: Tue, 27 Aug 2024 15:15:08 -0600 Subject: [PATCH 2/2] clippy --- .../plugin/output/default/traversal/traversal_output_format.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/routee-compass/src/plugin/output/default/traversal/traversal_output_format.rs b/rust/routee-compass/src/plugin/output/default/traversal/traversal_output_format.rs index 7c857db3..bed69658 100644 --- a/rust/routee-compass/src/plugin/output/default/traversal/traversal_output_format.rs +++ b/rust/routee-compass/src/plugin/output/default/traversal/traversal_output_format.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use super::traversal_ops as ops; use crate::plugin::plugin_error::PluginError; -use geo::{CoordFloat, CoordNum, Geometry, LineString}; +use geo::{CoordFloat, Geometry, LineString}; use routee_compass_core::{ algorithm::search::{edge_traversal::EdgeTraversal, search_tree_branch::SearchTreeBranch}, model::road_network::vertex_id::VertexId,