From ab697e31ab245f4c59fad2791d5ae38c3d22f802 Mon Sep 17 00:00:00 2001 From: Yamil Essus Date: Tue, 30 Dec 2025 16:06:20 -0500 Subject: [PATCH 1/4] first implementation of serializing geometries --- rust/bambam-omf/Cargo.toml | 1 + rust/bambam-omf/src/graph/omf_graph.rs | 42 +++++++++++++++- rust/bambam-omf/src/graph/segment_split.rs | 58 +++++++++++++++++++++- rust/bambam-omf/src/graph/serialize_ops.rs | 15 +++++- 4 files changed, 112 insertions(+), 4 deletions(-) diff --git a/rust/bambam-omf/Cargo.toml b/rust/bambam-omf/Cargo.toml index 227b24e2..550ba79e 100644 --- a/rust/bambam-omf/Cargo.toml +++ b/rust/bambam-omf/Cargo.toml @@ -52,3 +52,4 @@ serde_arrow = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } uom = { workspace = true } +wkt = { workspace = true } diff --git a/rust/bambam-omf/src/graph/omf_graph.rs b/rust/bambam-omf/src/graph/omf_graph.rs index 5fd62513..5c582684 100644 --- a/rust/bambam-omf/src/graph/omf_graph.rs +++ b/rust/bambam-omf/src/graph/omf_graph.rs @@ -10,9 +10,11 @@ use crate::{ }; use csv::QuoteStyle; use flate2::{write::GzEncoder, Compression}; +use geo::LineString; use kdam::tqdm; use rayon::prelude::*; use routee_compass_core::model::network::{EdgeConfig, EdgeList, EdgeListId, Vertex}; +use wkt::ToWkt; pub struct OmfGraphVectorized { pub vertices: Vec, @@ -24,7 +26,7 @@ pub struct OmfGraphVectorized { pub struct OmfEdgeList { pub edges: EdgeList, - // pub geometries: Vec> + pub geometries: Vec>, } impl OmfGraphVectorized { @@ -75,8 +77,10 @@ impl OmfGraphVectorized { &vertex_lookup, edge_list_id, )?; + let geometries = ops::create_geometries(&segments, &segment_lookup, &splits)?; let edge_list = OmfEdgeList { edges: EdgeList(edges.into_boxed_slice()), + geometries: geometries, }; edge_lists.push(edge_list); } @@ -161,6 +165,13 @@ impl OmfGraphVectorized { QuoteStyle::Necessary, overwrite, ); + let mut geometries_writer = create_writer( + &mode_dir, + "edges-geometries-enumerated.txt.gz", + false, + QuoteStyle::Never, + overwrite, + ); // Write Edges let e_iter = tqdm!( @@ -193,6 +204,35 @@ impl OmfGraphVectorized { )) })?; } + + // Write geometries + let g_iter = tqdm!( + edge_list.geometries.iter(), + total = edge_list.geometries.len(), + desc = "edges", + position = 1 + ); + for row in g_iter { + if let Some(ref mut writer) = geometries_writer { + writer + .serialize(row.to_wkt().to_string()) + .map_err(|e| { + OvertureMapsCollectionError::CsvWriteError(format!( + "Failed to write to geometry file edges-geometries-enumerated.txt.gz: {}", + e + )) + })?; + } + } + eprintln!(); + + if let Some(ref mut writer) = geometries_writer { + writer.flush().map_err(|e| { + OvertureMapsCollectionError::CsvWriteError(format!( + "Failed to flush edges-geometries-enumerated.txt.gz: {e}" + )) + })?; + } } eprintln!(); diff --git a/rust/bambam-omf/src/graph/segment_split.rs b/rust/bambam-omf/src/graph/segment_split.rs index b8ed7584..603f0d93 100644 --- a/rust/bambam-omf/src/graph/segment_split.rs +++ b/rust/bambam-omf/src/graph/segment_split.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; +use geo::{Haversine, Length, LineString}; use routee_compass_core::model::network::{Edge, EdgeId, EdgeListId, Vertex, VertexId}; -use uom::si::f64::Length; use crate::{ collection::{OvertureMapsCollectionError, TransportationSegmentRecord}, @@ -102,11 +102,65 @@ impl SegmentSplit { edge_id, src_vertex_id: VertexId(*src_id), dst_vertex_id: VertexId(*dst_id), - distance: Length::new::(distance as f64), + distance: uom::si::f64::Length::new::(distance as f64), }; Ok(edge) } } } + + pub fn create_geometry_from_split( + &self, + segments: &[&TransportationSegmentRecord], + segment_lookup: &HashMap, + ) -> Result, OvertureMapsCollectionError> { + use OvertureMapsCollectionError as E; + + match self { + SegmentSplit::SimpleConnectorSplit { src, dst } => { + let segment_id = &src.segment_id; + let segment_idx = segment_lookup.get(segment_id).ok_or_else(|| { + let msg = format!("missing lookup entry for segment {segment_id}"); + E::InvalidSegmentConnectors(msg) + })?; + let segment = segments.get(*segment_idx).ok_or_else(|| { + let msg = format!( + "missing lookup entry for segment {segment_id} with index {segment_idx}" + ); + E::InvalidSegmentConnectors(msg) + })?; + + let distance_to_src = segment.get_distance_at(src.linear_reference.0)?; + let distance_to_dst = segment.get_distance_at(dst.linear_reference.0)?; + let segment_geometry = segment.get_linestring()?; + + let mut out_coords = vec![]; + + // Add the initial point + out_coords.push(segment.get_coord_at(src.linear_reference.0)?); + + // Check all points to see if we need to add them + let mut total_distance = 0.; + for line in segment_geometry.lines() { + let line_distance = Haversine.length(&line); + total_distance += line_distance; + + if total_distance <= distance_to_src { + continue; + } + if total_distance >= distance_to_dst { + break; + } + + out_coords.push(line.end); + } + + // Add final point + out_coords.push(segment.get_coord_at(dst.linear_reference.0)?); + + Ok(LineString::new(out_coords)) + } + } + } } diff --git a/rust/bambam-omf/src/graph/serialize_ops.rs b/rust/bambam-omf/src/graph/serialize_ops.rs index 9d392e48..794f4dd4 100644 --- a/rust/bambam-omf/src/graph/serialize_ops.rs +++ b/rust/bambam-omf/src/graph/serialize_ops.rs @@ -1,4 +1,4 @@ -use geo::Coord; +use geo::{Coord, LineString}; use itertools::Itertools; use kdam::{tqdm, Bar, BarExt}; use rayon::prelude::*; @@ -178,3 +178,16 @@ pub fn create_edges( }) .collect::, OvertureMapsCollectionError>>() } + +pub fn create_geometries( + segments: &[&TransportationSegmentRecord], + segment_lookup: &HashMap, + splits: &[SegmentSplit], +) -> Result>, OvertureMapsCollectionError> { + splits + .iter() + .collect_vec() + .par_iter() + .map(|split| split.create_geometry_from_split(segments, segment_lookup)) + .collect::>, OvertureMapsCollectionError>>() +} From 3fbe3f14024c0589992d24432488d34e49a5f9ce Mon Sep 17 00:00:00 2001 From: Yamil Essus Date: Wed, 31 Dec 2025 14:16:32 -0500 Subject: [PATCH 2/4] clippy fmt --- rust/bambam-gbfs/src/app/gbfs_cli.rs | 4 ++-- rust/bambam-omf/src/app/cli_bbox.rs | 2 +- rust/bambam-omf/src/graph/connector_in_segment.rs | 2 +- rust/bambam-omf/src/graph/omf_graph.rs | 5 ++--- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/rust/bambam-gbfs/src/app/gbfs_cli.rs b/rust/bambam-gbfs/src/app/gbfs_cli.rs index ee4472b3..1cf0a2e2 100644 --- a/rust/bambam-gbfs/src/app/gbfs_cli.rs +++ b/rust/bambam-gbfs/src/app/gbfs_cli.rs @@ -50,6 +50,6 @@ impl GbfsOperation { fn parse_duration(s: &str) -> Result { let std_duration = - humantime::parse_duration(s).map_err(|e| format!("Invalid duration: {}", e))?; - chrono::TimeDelta::from_std(std_duration).map_err(|e| format!("TimeDelta out of range: {}", e)) + humantime::parse_duration(s).map_err(|e| format!("Invalid duration: {e}"))?; + chrono::TimeDelta::from_std(std_duration).map_err(|e| format!("TimeDelta out of range: {e}")) } diff --git a/rust/bambam-omf/src/app/cli_bbox.rs b/rust/bambam-omf/src/app/cli_bbox.rs index 7a29f6df..3f8e2097 100644 --- a/rust/bambam-omf/src/app/cli_bbox.rs +++ b/rust/bambam-omf/src/app/cli_bbox.rs @@ -11,7 +11,7 @@ pub struct CliBoundingBox { pub fn parse_bbox(s: &str) -> Result { let parts: Vec<&str> = s.split(',').collect(); if parts.len() != 4 { - return Err(format!("expected format: xmin,xmax,ymin,ymax, got: {}", s)); + return Err(format!("expected format: xmin,xmax,ymin,ymax, got: {s}")); } let xmin = parse_lon(parts[0])?; diff --git a/rust/bambam-omf/src/graph/connector_in_segment.rs b/rust/bambam-omf/src/graph/connector_in_segment.rs index db88acdc..aba8c364 100644 --- a/rust/bambam-omf/src/graph/connector_in_segment.rs +++ b/rust/bambam-omf/src/graph/connector_in_segment.rs @@ -25,7 +25,7 @@ impl ConnectorInSegment { /// identifiers to sub-segments by their segment id along with linear reference ranges. /// see pub fn new_without_connector_id(segment_id: String, linear_reference: f64) -> Self { - let connector_id = format!("{}@{}", segment_id, linear_reference); + let connector_id = format!("{segment_id}@{linear_reference}"); Self { segment_id, connector_id, diff --git a/rust/bambam-omf/src/graph/omf_graph.rs b/rust/bambam-omf/src/graph/omf_graph.rs index 831bfaf5..b37e3329 100644 --- a/rust/bambam-omf/src/graph/omf_graph.rs +++ b/rust/bambam-omf/src/graph/omf_graph.rs @@ -80,7 +80,7 @@ impl OmfGraphVectorized { let geometries = ops::create_geometries(&segments, &segment_lookup, &splits)?; let edge_list = OmfEdgeList { edges: EdgeList(edges.into_boxed_slice()), - geometries: geometries, + geometries, }; edge_lists.push(edge_list); } @@ -211,8 +211,7 @@ impl OmfGraphVectorized { .serialize(row.to_wkt().to_string()) .map_err(|e| { OvertureMapsCollectionError::CsvWriteError(format!( - "Failed to write to geometry file edges-geometries-enumerated.txt.gz: {}", - e + "Failed to write to geometry file edges-geometries-enumerated.txt.gz: {e}" )) })?; } From f74325acfdd37e5c28055cfc8ea8058f3e6207c3 Mon Sep 17 00:00:00 2001 From: Yamil Essus Date: Wed, 31 Dec 2025 14:31:33 -0500 Subject: [PATCH 3/4] Update rust/bambam-omf/src/graph/omf_graph.rs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- rust/bambam-omf/src/graph/omf_graph.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/bambam-omf/src/graph/omf_graph.rs b/rust/bambam-omf/src/graph/omf_graph.rs index b37e3329..99ebddac 100644 --- a/rust/bambam-omf/src/graph/omf_graph.rs +++ b/rust/bambam-omf/src/graph/omf_graph.rs @@ -202,7 +202,7 @@ impl OmfGraphVectorized { let g_iter = tqdm!( edge_list.geometries.iter(), total = edge_list.geometries.len(), - desc = "edges", + desc = "geometries", position = 1 ); for row in g_iter { From 26d0a4d54ccb6397ebbb1206fba95ac95aab34c6 Mon Sep 17 00:00:00 2001 From: Yamil Essus Date: Wed, 31 Dec 2025 14:32:36 -0500 Subject: [PATCH 4/4] copilot suggestion --- rust/bambam-omf/src/graph/serialize_ops.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/rust/bambam-omf/src/graph/serialize_ops.rs b/rust/bambam-omf/src/graph/serialize_ops.rs index 794f4dd4..24769eaf 100644 --- a/rust/bambam-omf/src/graph/serialize_ops.rs +++ b/rust/bambam-omf/src/graph/serialize_ops.rs @@ -185,8 +185,6 @@ pub fn create_geometries( splits: &[SegmentSplit], ) -> Result>, OvertureMapsCollectionError> { splits - .iter() - .collect_vec() .par_iter() .map(|split| split.create_geometry_from_split(segments, segment_lookup)) .collect::>, OvertureMapsCollectionError>>()