Skip to main content

routee_compass/plugin/output/default/traversal/
traversal_output_format.rs

1use std::sync::Arc;
2
3use super::traversal_ops as ops;
4use crate::plugin::output::OutputPluginError;
5use geo::{CoordFloat, Geometry, TryConvert};
6use routee_compass_core::{
7    algorithm::search::{EdgeTraversal, SearchTree},
8    model::{map::MapModel, state::StateModel},
9};
10use serde::{Deserialize, Serialize};
11use wkb::writer::WriteOptions;
12use wkt::ToWkt;
13
14#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
15#[serde(rename_all = "snake_case")]
16pub enum TraversalOutputFormat {
17    // concatenates all LINESTRINGS and returns the geometry as a WKT
18    Wkt,
19    // concatenates all LINESTRINGS and returns the geometry as a WKB
20    Wkb,
21    // returns the properties of each link traversal as a JSON array of objects
22    Json,
23    // returns the geometries and properties as GeoJSON
24    GeoJson,
25    EdgeId,
26}
27
28impl TraversalOutputFormat {
29    /// generates output for a route based on the configured TraversalOutputFormat
30    pub fn generate_route_output(
31        &self,
32        route: &Vec<EdgeTraversal>,
33        map_model: Arc<MapModel>,
34        state_model: Arc<StateModel>,
35    ) -> Result<serde_json::Value, OutputPluginError> {
36        match self {
37            TraversalOutputFormat::Wkt => {
38                let route_geometry = ops::create_route_linestring(route, map_model.clone())?;
39                let route_wkt = route_geometry.wkt_string();
40                Ok(serde_json::Value::String(route_wkt))
41            }
42            TraversalOutputFormat::Wkb => {
43                let linestring = ops::create_route_linestring(route, map_model.clone())?;
44                let geometry = geo::Geometry::LineString(linestring);
45                let wkb_str = geometry_to_wkb_string(&geometry)?;
46                Ok(serde_json::Value::String(wkb_str))
47            }
48            TraversalOutputFormat::Json => {
49                let result = serde_json::to_value(route)?;
50                Ok(result)
51            }
52            TraversalOutputFormat::GeoJson => {
53                let result = ops::create_route_geojson(route, map_model, state_model)?;
54                Ok(result)
55            }
56            TraversalOutputFormat::EdgeId => {
57                let route_ids = route.iter().map(|e| e.edge_id).collect::<Vec<_>>();
58                let json = serde_json::json![route_ids];
59                Ok(json)
60            }
61        }
62    }
63
64    /// generates output for a tree based on the configured TraversalOutputFormat
65    pub fn generate_tree_output(
66        &self,
67        tree: &SearchTree,
68        map_model: Arc<MapModel>,
69        state_model: Arc<StateModel>,
70    ) -> Result<serde_json::Value, OutputPluginError> {
71        match self {
72            TraversalOutputFormat::Wkt => {
73                let route_geometry = ops::create_tree_multilinestring(tree, map_model)?;
74                let route_wkt = route_geometry.wkt_string();
75                Ok(serde_json::Value::String(route_wkt))
76            }
77            TraversalOutputFormat::Wkb => {
78                let route_geometry = ops::create_tree_multilinestring(tree, map_model)?;
79                let geometry = geo::Geometry::MultiLineString(route_geometry);
80                let wkb_str = geometry_to_wkb_string(&geometry)?;
81                Ok(serde_json::Value::String(wkb_str))
82            }
83            TraversalOutputFormat::Json => {
84                let result = serde_json::to_value(tree.values().collect::<Vec<_>>())?;
85                Ok(result)
86            }
87            TraversalOutputFormat::GeoJson => {
88                let result = ops::create_tree_geojson(tree, map_model, state_model)?;
89                Ok(result)
90            }
91            TraversalOutputFormat::EdgeId => {
92                let tree_ids = tree
93                    .values()
94                    .filter_map(|b| b.incoming_edge().map(|e| (e.edge_list_id, e.edge_id)))
95                    .collect::<Vec<_>>();
96                let json = serde_json::json![tree_ids];
97                Ok(json)
98            }
99        }
100    }
101}
102
103fn geometry_to_wkb_string<T: CoordFloat + Into<f64>>(
104    geometry: &Geometry<T>,
105) -> Result<String, OutputPluginError> {
106    let mut out_bytes = vec![];
107    let geom: Geometry<f64> = geometry.try_convert().map_err(|e| {
108        OutputPluginError::OutputPluginFailed(format!("unable to convert geometry to f64: {e}"))
109    })?;
110    let write_options = WriteOptions {
111        endianness: wkb::Endianness::BigEndian,
112    };
113    wkb::writer::write_geometry(&mut out_bytes, &geom, &write_options).map_err(|e| {
114        OutputPluginError::OutputPluginFailed(format!("failed to write geometry as WKB: {e}"))
115    })?;
116    let out_string = String::from_utf8(out_bytes).map_err(|e| {
117        OutputPluginError::OutputPluginFailed(format!("failed to read WKB as utf8: {e}"))
118    })?;
119    Ok(out_string)
120}
121
122// #[cfg(test)]
123// mod test {
124
125//     use crate::app::search::SearchAppResult;
126//     use chrono::Local;
127//     use geo::{coord, LineString};
128//     use routee_compass_core::{
129//         algorithm::search::EdgeTraversal,
130//         model::{network::EdgeId, state::StateVariable, unit::Cost},
131//     };
132//     use std::time::Duration;
133
134//     // #[ignore = "needs mocked graph for map model integration in test"]
135//     // fn test_e2e() {
136//     //     let route = vec![
137//     //         EdgeTraversal {
138//     //             edge_id: EdgeId(0),
139//     //             access_cost: Cost::from(0.0),
140//     //             traversal_cost: Cost::from(10.0),
141//     //             result_state: vec![StateVariable(10.0)],
142//     //         },
143//     //         EdgeTraversal {
144//     //             edge_id: EdgeId(1),
145//     //             access_cost: Cost::from(5.0),
146//     //             traversal_cost: Cost::from(9.0),
147//     //             result_state: vec![StateVariable(24.0)],
148//     //         },
149//     //         EdgeTraversal {
150//     //             edge_id: EdgeId(2),
151//     //             access_cost: Cost::from(0.0),
152//     //             traversal_cost: Cost::from(11.0),
153//     //             result_state: vec![StateVariable(35.0)],
154//     //         },
155//     //     ];
156//     //     let _ = SearchAppResult {
157//     //         routes: vec![route],
158//     //         trees: vec![],
159//     //         search_executed_time: Local::now().to_rfc3339(),
160//     //         search_runtime: Duration::ZERO,
161//     //         iterations: 0,
162//     //     };
163
164//     //     let geoms = vec![
165//     //         LineString(vec![
166//     //             coord! { x: 1.0, y: 0.0 },
167//     //             coord! { x: 1.0, y: 0.0 },
168//     //             coord! { x: 1.0, y: 1.0 },
169//     //         ]),
170//     //         LineString(vec![coord! { x: 2.0, y: 2.0 }, coord! { x: 2.0, y: 3.0 }]),
171//     //         LineString(vec![coord! { x: 3.0, y: 3.0 }, coord! { x: 3.0, y: 4.0 }]),
172//     //     ]
173//     //     .into_boxed_slice();
174
175//     //     // let map_model = MapModel::new(graph, config)
176
177//     //     // println!(
178//     //     //     "{:?}",
179//     //     //     TraversalOutputFormat::Wkt
180//     //     //         .generate_route_output(&result.routes[0], &geoms)
181//     //     //         .map(|r| serde_json::to_string_pretty(&r))
182//     //     // );
183//     //     // println!(
184//     //     //     "{:?}",
185//     //     //     TraversalOutputFormat::Json
186//     //     //         .generate_route_output(&result.routes[0], &geoms)
187//     //     //         .map(|r| serde_json::to_string_pretty(&r))
188//     //     // );
189//     //     // println!(
190//     //     //     "{:?}",
191//     //     //     TraversalOutputFormat::GeoJson
192//     //     //         .generate_route_output(&result.routes[0], &geoms)
193//     //     //         .map(|r| serde_json::to_string_pretty(&r))
194//     //     // );
195//     //     // println!(
196//     //     //     "{:?}",
197//     //     //     TraversalOutputFormat::EdgeId
198//     //     //         .generate_route_output(&result.routes[0], &geoms)
199//     //     //         .map(|r| serde_json::to_string_pretty(&r))
200//     //     // );
201//     // }
202// }