From 42fce67ff72a133ec3a11ab2edf5a0f9c9118ee0 Mon Sep 17 00:00:00 2001 From: admrtin Date: Thu, 20 Aug 2026 15:42:09 -0600 Subject: [PATCH 01/12] remove nearest neighbor search in walk eligibility determination --- rust/bambam-modal-metrics/src/wci/ops.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/rust/bambam-modal-metrics/src/wci/ops.rs b/rust/bambam-modal-metrics/src/wci/ops.rs index 9416aa3a..33810c9a 100644 --- a/rust/bambam-modal-metrics/src/wci/ops.rs +++ b/rust/bambam-modal-metrics/src/wci/ops.rs @@ -76,10 +76,4 @@ pub fn is_walk_eligible( entry: &EdgeRTreeEntry, ) -> bool { entry.edge.is_walkable() - || rtree - .locate_within_distance( - [entry.centroid.x(), entry.centroid.y()], - MIN_DISTANCE_RTREE_NEIGHBOR, - ) - .any(|neighbor| neighbor.edge.is_sidewalk()) } From 9025115a8cee4780840462cbfdcbc45ffacbcad2 Mon Sep 17 00:00:00 2001 From: admrtin Date: Thu, 20 Aug 2026 15:42:53 -0600 Subject: [PATCH 02/12] remove neighbors param from is_walk_eligible --- rust/bambam-modal-metrics/src/wci/ops.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rust/bambam-modal-metrics/src/wci/ops.rs b/rust/bambam-modal-metrics/src/wci/ops.rs index 33810c9a..efc1869e 100644 --- a/rust/bambam-modal-metrics/src/wci/ops.rs +++ b/rust/bambam-modal-metrics/src/wci/ops.rs @@ -71,9 +71,6 @@ pub fn traffic_speed_comfort_from_neighbors } /// Determines if the edge is walk-eligible based on its own attributes or nearby sidewalk edges. -pub fn is_walk_eligible( - rtree: &RTree>, - entry: &EdgeRTreeEntry, -) -> bool { +pub fn is_walk_eligible(entry: &EdgeRTreeEntry) -> bool { entry.edge.is_walkable() } From 5fdbecf55ad63285672fad660ec7b8387816a7b4 Mon Sep 17 00:00:00 2001 From: admrtin Date: Thu, 20 Aug 2026 15:43:32 -0600 Subject: [PATCH 03/12] remove unused imports --- rust/bambam-modal-metrics/src/wci/ops.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/bambam-modal-metrics/src/wci/ops.rs b/rust/bambam-modal-metrics/src/wci/ops.rs index efc1869e..b5531ad4 100644 --- a/rust/bambam-modal-metrics/src/wci/ops.rs +++ b/rust/bambam-modal-metrics/src/wci/ops.rs @@ -1,11 +1,10 @@ use geo::{Distance, Euclidean}; use crate::common::cycleway_tag::CyclewayTag; -use crate::common::edge_rtree_entry::{EdgeRTreeEntry, MIN_DISTANCE_RTREE_NEIGHBOR}; +use crate::common::edge_rtree_entry::EdgeRTreeEntry; use crate::common::ops::estimated_speed_from_neighbors; use crate::network_traits::{edge_for_modal_metric::EdgeForModalMetric, spatial_edge::SpatialEdge}; use crate::wci::NO_CYCLEWAY_FOUND_SCORE; -use rstar::RTree; /// Converts a cycleway tag classification to a numerical comfort index. pub fn cycleway_comfort_from_tag(tag: &CyclewayTag) -> i32 { From 902f21c1affbae73c18ab727494a9ec286fb73b3 Mon Sep 17 00:00:00 2001 From: admrtin Date: Thu, 20 Aug 2026 15:46:33 -0600 Subject: [PATCH 04/12] highway tag matching for is_sidewalk and is_footway --- rust/bambam-modal-metrics/src/wci/compute_wci.rs | 2 +- .../bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/rust/bambam-modal-metrics/src/wci/compute_wci.rs b/rust/bambam-modal-metrics/src/wci/compute_wci.rs index b30651a3..75857fbf 100644 --- a/rust/bambam-modal-metrics/src/wci/compute_wci.rs +++ b/rust/bambam-modal-metrics/src/wci/compute_wci.rs @@ -48,7 +48,7 @@ where V: VertexForModalMetric, { // general walk-eligibility based on edge attributes and neighbors. - let is_walk_eligible = is_walk_eligible(rtree, entry); + let is_walk_eligible = is_walk_eligible(entry); // grab the neighboring edges let neighboring_edges = find_neighboring_edges(entry, rtree); diff --git a/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs b/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs index d0cf0fab..608f6d27 100644 --- a/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs +++ b/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs @@ -249,12 +249,14 @@ impl EdgeForModalMetric for OsmWayDataSerializable { .as_ref() .is_some_and(|s| s != "no" && s != "none") || self.footway == Some("sidewalk".to_string()) + || self.highway == Highway::Sidewalk } fn is_footway(&self) -> bool { self.footway .as_ref() .is_some_and(|s| s != "no" && s != "none") + || self.highway == Highway::Footway } fn is_unbikeable(&self) -> bool { From cef1bcfae279477d86559e46d0661bf71c1b561c Mon Sep 17 00:00:00 2001 From: admrtin Date: Thu, 20 Aug 2026 15:53:35 -0600 Subject: [PATCH 05/12] update nearest neighbor constant --- rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs b/rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs index 4abee605..367dbb19 100644 --- a/rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs +++ b/rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs @@ -6,7 +6,7 @@ use crate::network_traits::spatial_edge::SpatialEdge; /// The maximum distance (in degrees) within which another edge is considered a /// spatial "neighbor" for the purpose of neighbor-aware modal penalty scores. /// Roughly 15 meters at mid latitudes. -pub const MIN_DISTANCE_RTREE_NEIGHBOR: f32 = 0.0001378; +pub const MIN_DISTANCE_RTREE_NEIGHBOR: f32 = 1.816e-8; /// `EdgeRTreeEntry` wraps the network edge and caches the bounding box /// and centroid of the edge's `linestring`. It is used solely for efficient spatial queries From 8442fa0ce4d626a0451aa0040dd6752bf45c8110 Mon Sep 17 00:00:00 2001 From: admrtin Date: Thu, 20 Aug 2026 16:03:01 -0600 Subject: [PATCH 06/12] change metric_name: &str to metric: ModalMetric (enumeration) --- .../src/common/bulk_compute_modal_metric.rs | 9 +-------- rust/bambam/src/bin/bambam_util.rs | 5 +++-- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/rust/bambam-modal-metrics/src/common/bulk_compute_modal_metric.rs b/rust/bambam-modal-metrics/src/common/bulk_compute_modal_metric.rs index 59fcf5b9..4ed54f97 100644 --- a/rust/bambam-modal-metrics/src/common/bulk_compute_modal_metric.rs +++ b/rust/bambam-modal-metrics/src/common/bulk_compute_modal_metric.rs @@ -18,12 +18,8 @@ use std::{ /// Bulk compute a specific modal metric for all ways in an OSM network by taking in a vertices-complete.csv /// and edges-complete.csv. -/// -/// `metric_name` can either be: -/// - "WCI" for the Walking Comfort Index metric -/// - "LTS" for the Level of Traffic Stress (cycling comfort) metric pub fn bulk_compute_modal_metric( - metric_name: &str, + metric: ModalMetric, edges_file: &str, vertices_file: &str, output_file: &str, @@ -32,9 +28,6 @@ where E: SpatialEdge + EdgeForModalMetric + DeserializeOwned + Clone + Send + Sync, V: VertexForModalMetric + DeserializeOwned + Send + Sync, { - // determine the modal metric to compute based on the provided metric name. - let metric: ModalMetric = metric_name.parse()?; - log::info!( "\nLoading files for {:?} modal metric computation.\n", metric diff --git a/rust/bambam/src/bin/bambam_util.rs b/rust/bambam/src/bin/bambam_util.rs index d3d7c4ee..b5be901a 100644 --- a/rust/bambam/src/bin/bambam_util.rs +++ b/rust/bambam/src/bin/bambam_util.rs @@ -4,6 +4,7 @@ use bambam::app::overlay::{ self, GeometryColumnType, GeometryFormat, OverlayOperation, OverlaySource, }; use bambam_modal_metrics::common::bulk_compute_modal_metric::bulk_compute_modal_metric; +use bambam_modal_metrics::common::modal_metrics::ModalMetric; use bambam_osm::model::osm::graph::{OsmNodeDataSerializable, OsmWayDataSerializable}; use clap::{Parser, Subcommand}; #[derive(Parser)] @@ -36,7 +37,7 @@ pub enum App { ModalMetricSet { /// modal metric type to compute, either "WCI" or "LTS" #[arg(long)] - metric_name: String, + metric_name: ModalMetric, /// input csv file with edges data #[arg(long)] edges_file: String, @@ -226,7 +227,7 @@ impl App { // to specify the type of graph data to use. { bulk_compute_modal_metric::( - metric_name, + *metric_name, edges_file, vertices_file, output_file, From 868629cb80b2bf76eff543327bdff944f47419e3 Mon Sep 17 00:00:00 2001 From: admrtin Date: Thu, 20 Aug 2026 16:11:06 -0600 Subject: [PATCH 07/12] naming changes --- rust/bambam/src/bin/bambam_util.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/bambam/src/bin/bambam_util.rs b/rust/bambam/src/bin/bambam_util.rs index b5be901a..b4c5dfa1 100644 --- a/rust/bambam/src/bin/bambam_util.rs +++ b/rust/bambam/src/bin/bambam_util.rs @@ -37,7 +37,7 @@ pub enum App { ModalMetricSet { /// modal metric type to compute, either "WCI" or "LTS" #[arg(long)] - metric_name: ModalMetric, + metric_type: ModalMetric, /// input csv file with edges data #[arg(long)] edges_file: String, @@ -214,7 +214,7 @@ impl App { env_logger::init(); match self { Self::ModalMetricSet { - metric_name, + metric_type, output_file, edges_file, vertices_file, @@ -227,7 +227,7 @@ impl App { // to specify the type of graph data to use. { bulk_compute_modal_metric::( - *metric_name, + *metric_type, edges_file, vertices_file, output_file, From 6f3fac44241e78b1f414f1e3154a82c8aaf38aa2 Mon Sep 17 00:00:00 2001 From: admrtin Date: Thu, 20 Aug 2026 16:17:50 -0600 Subject: [PATCH 08/12] add cycleway to walkable highways for osm trait --- rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs b/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs index 608f6d27..92e59f38 100644 --- a/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs +++ b/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs @@ -240,6 +240,7 @@ impl EdgeForModalMetric for OsmWayDataSerializable { | Highway::Steps | Highway::Corridor | Highway::Path + | Highway::Cycleway | Highway::Elevator ) } From b1b6ca97cc3c55a164cc52672e7abfac8b68099b Mon Sep 17 00:00:00 2001 From: admrtin Date: Thu, 20 Aug 2026 16:43:06 -0600 Subject: [PATCH 09/12] add highway test to cycleway tag --- .../src/model/osm/graph/osm_way_data_serializable.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs b/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs index 92e59f38..a58297f6 100644 --- a/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs +++ b/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs @@ -212,7 +212,13 @@ impl EdgeForModalMetric for OsmWayDataSerializable { } fn get_cycleway_tag(&self) -> Option { - self.cycleway.as_ref().map(|tag| CyclewayTag::new(tag)) + if let Some(tag) = self.cycleway.as_ref() { + return Some(CyclewayTag::new(tag)); + } + if self.highway == Highway::Cycleway { + return Some(CyclewayTag::DedicatedWithBuffer); + } + None } fn is_walkable(&self) -> bool { From 73f681721a134c5d84e10aa1a853bfc30acc4816 Mon Sep 17 00:00:00 2001 From: admrtin Date: Thu, 20 Aug 2026 16:46:04 -0600 Subject: [PATCH 10/12] add is_pedestrian_priority method --- .../src/network_traits/edge_for_modal_metric.rs | 2 ++ rust/bambam-modal-metrics/src/wci/compute_wci.rs | 9 +++++++-- .../src/model/osm/graph/osm_way_data_serializable.rs | 4 ++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/rust/bambam-modal-metrics/src/network_traits/edge_for_modal_metric.rs b/rust/bambam-modal-metrics/src/network_traits/edge_for_modal_metric.rs index 92ce45aa..debb718a 100644 --- a/rust/bambam-modal-metrics/src/network_traits/edge_for_modal_metric.rs +++ b/rust/bambam-modal-metrics/src/network_traits/edge_for_modal_metric.rs @@ -24,6 +24,8 @@ pub trait EdgeForModalMetric { fn is_sidewalk(&self) -> bool; /// returns true if the edge is a footway. fn is_footway(&self) -> bool; + /// returns true if the edge is a pedestrian-priority street (e.g. pedestrian mall, living street). + fn is_pedestrian_priority(&self) -> bool; // LTS - only /// returns true if the edge is unbikeable. fn is_unbikeable(&self) -> bool; diff --git a/rust/bambam-modal-metrics/src/wci/compute_wci.rs b/rust/bambam-modal-metrics/src/wci/compute_wci.rs index 75857fbf..06b17fef 100644 --- a/rust/bambam-modal-metrics/src/wci/compute_wci.rs +++ b/rust/bambam-modal-metrics/src/wci/compute_wci.rs @@ -56,9 +56,11 @@ where if !is_walk_eligible { // Total WCI score = Min WCI score (unwalkable edge) WciComponents::min_wci() - } else if entry.edge.is_footway() || (neighboring_edges.is_empty() && entry.edge.is_sidewalk()) + } else if entry.edge.is_footway() + || entry.edge.is_pedestrian_priority() + || (neighboring_edges.is_empty() && entry.edge.is_sidewalk()) { - // Total WCI score = Max WCI score (footway or sidewalk with no adjacent edges) + // Total WCI score = Max WCI score (footway, pedestrian-priority street, or sidewalk with no adjacent edges) WciComponents::max_wci() } else { // Compute all component scores. @@ -135,6 +137,9 @@ mod test { fn is_footway(&self) -> bool { self.footway } + fn is_pedestrian_priority(&self) -> bool { + false + } fn is_unbikeable(&self) -> bool { false } diff --git a/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs b/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs index a58297f6..b80b904f 100644 --- a/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs +++ b/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs @@ -266,6 +266,10 @@ impl EdgeForModalMetric for OsmWayDataSerializable { || self.highway == Highway::Footway } + fn is_pedestrian_priority(&self) -> bool { + matches!(self.highway, Highway::LivingStreet | Highway::Pedestrian) + } + fn is_unbikeable(&self) -> bool { matches!( self.highway, From 607ca4c602ed9526a34746e18d2a9a30ae6fca32 Mon Sep 17 00:00:00 2001 From: admrtin Date: Wed, 2 Sep 2026 09:15:18 -0600 Subject: [PATCH 11/12] new neighbor distance appropriately reflects 15m. --- rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs | 4 ++-- rust/bambam-modal-metrics/src/wci/compute_wci.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs b/rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs index 367dbb19..b0beac2b 100644 --- a/rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs +++ b/rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs @@ -6,7 +6,7 @@ use crate::network_traits::spatial_edge::SpatialEdge; /// The maximum distance (in degrees) within which another edge is considered a /// spatial "neighbor" for the purpose of neighbor-aware modal penalty scores. /// Roughly 15 meters at mid latitudes. -pub const MIN_DISTANCE_RTREE_NEIGHBOR: f32 = 1.816e-8; +pub const DISTANCE_RTREE_NEIGHBOR: f32 = 1.816e-8; /// `EdgeRTreeEntry` wraps the network edge and caches the bounding box /// and centroid of the edge's `linestring`. It is used solely for efficient spatial queries @@ -75,7 +75,7 @@ pub fn find_neighboring_edges<'a, E: SpatialEdge>( rtree .locate_within_distance( [query.centroid.x(), query.centroid.y()], - MIN_DISTANCE_RTREE_NEIGHBOR, + DISTANCE_RTREE_NEIGHBOR, ) .filter(|entry| entry.edge.id() != query_id) .collect() diff --git a/rust/bambam-modal-metrics/src/wci/compute_wci.rs b/rust/bambam-modal-metrics/src/wci/compute_wci.rs index 06b17fef..9c09ace7 100644 --- a/rust/bambam-modal-metrics/src/wci/compute_wci.rs +++ b/rust/bambam-modal-metrics/src/wci/compute_wci.rs @@ -276,17 +276,17 @@ mod test { footway: false, speed_limit: Some(45), cycleway: None, - linestring: LineString::from(vec![(-105.170016, 39.773648), (-105.165381, 39.774176)]), + linestring: LineString::from(vec![(-105.170735, 39.773087), (-105.170445, 39.773137)]), }; - + // the "buffing" edge let neighbor = TestEdge { id: 43, walkable: true, sidewalk: false, footway: false, speed_limit: Some(25), - cycleway: Some(CyclewayTag::DedicatedNoBuffer), - linestring: LineString::from(vec![(-105.168085, 39.773772), (-105.166755, 39.773937)]), + cycleway: Some(CyclewayTag::DedicatedWithBuffer), + linestring: LineString::from(vec![(-105.170612, 39.773116), (-105.170499, 39.773017)]), }; let src_vertex = TestVertex { From 76ef4fd9b53e9d74eafc0d5e31adc489202bdefa Mon Sep 17 00:00:00 2001 From: admrtin Date: Wed, 2 Sep 2026 10:59:10 -0600 Subject: [PATCH 12/12] copilot suggestions --- rust/bambam-modal-metrics/src/wci/compute_wci.rs | 2 +- rust/bambam-modal-metrics/src/wci/ops.rs | 2 +- .../src/model/osm/graph/osm_way_data_serializable.rs | 5 +---- rust/bambam/src/bin/bambam_util.rs | 6 +++--- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/rust/bambam-modal-metrics/src/wci/compute_wci.rs b/rust/bambam-modal-metrics/src/wci/compute_wci.rs index 9c09ace7..79bbe51f 100644 --- a/rust/bambam-modal-metrics/src/wci/compute_wci.rs +++ b/rust/bambam-modal-metrics/src/wci/compute_wci.rs @@ -47,7 +47,7 @@ where E: SpatialEdge + EdgeForModalMetric, V: VertexForModalMetric, { - // general walk-eligibility based on edge attributes and neighbors. + // general walk-eligibility based on edge attributes. let is_walk_eligible = is_walk_eligible(entry); // grab the neighboring edges diff --git a/rust/bambam-modal-metrics/src/wci/ops.rs b/rust/bambam-modal-metrics/src/wci/ops.rs index b5531ad4..6fee8421 100644 --- a/rust/bambam-modal-metrics/src/wci/ops.rs +++ b/rust/bambam-modal-metrics/src/wci/ops.rs @@ -69,7 +69,7 @@ pub fn traffic_speed_comfort_from_neighbors traffic_speed_comfort_from_speed(speed_mph.round() as i32) } -/// Determines if the edge is walk-eligible based on its own attributes or nearby sidewalk edges. +/// Determines if the edge is walk-eligible based on its own attributes. pub fn is_walk_eligible(entry: &EdgeRTreeEntry) -> bool { entry.edge.is_walkable() } diff --git a/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs b/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs index b80b904f..ac0d122f 100644 --- a/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs +++ b/rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs @@ -212,13 +212,10 @@ impl EdgeForModalMetric for OsmWayDataSerializable { } fn get_cycleway_tag(&self) -> Option { - if let Some(tag) = self.cycleway.as_ref() { - return Some(CyclewayTag::new(tag)); - } if self.highway == Highway::Cycleway { return Some(CyclewayTag::DedicatedWithBuffer); } - None + self.cycleway.as_ref().map(|tag| CyclewayTag::new(tag)) } fn is_walkable(&self) -> bool { diff --git a/rust/bambam/src/bin/bambam_util.rs b/rust/bambam/src/bin/bambam_util.rs index b4c5dfa1..974dde3e 100644 --- a/rust/bambam/src/bin/bambam_util.rs +++ b/rust/bambam/src/bin/bambam_util.rs @@ -37,7 +37,7 @@ pub enum App { ModalMetricSet { /// modal metric type to compute, either "WCI" or "LTS" #[arg(long)] - metric_type: ModalMetric, + modal_metric: ModalMetric, /// input csv file with edges data #[arg(long)] edges_file: String, @@ -214,7 +214,7 @@ impl App { env_logger::init(); match self { Self::ModalMetricSet { - metric_type, + modal_metric, output_file, edges_file, vertices_file, @@ -227,7 +227,7 @@ impl App { // to specify the type of graph data to use. { bulk_compute_modal_metric::( - *metric_type, + *modal_metric, edges_file, vertices_file, output_file,