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-modal-metrics/src/common/edge_rtree_entry.rs b/rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs index 4abee605..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 = 0.0001378; +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/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 b30651a3..79bbe51f 100644 --- a/rust/bambam-modal-metrics/src/wci/compute_wci.rs +++ b/rust/bambam-modal-metrics/src/wci/compute_wci.rs @@ -47,8 +47,8 @@ where E: SpatialEdge + EdgeForModalMetric, V: VertexForModalMetric, { - // general walk-eligibility based on edge attributes and neighbors. - let is_walk_eligible = is_walk_eligible(rtree, entry); + // general walk-eligibility based on edge attributes. + let is_walk_eligible = is_walk_eligible(entry); // grab the neighboring edges let neighboring_edges = find_neighboring_edges(entry, rtree); @@ -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 } @@ -271,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 { diff --git a/rust/bambam-modal-metrics/src/wci/ops.rs b/rust/bambam-modal-metrics/src/wci/ops.rs index 9416aa3a..6fee8421 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 { @@ -70,16 +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. -pub fn is_walk_eligible( - rtree: &RTree>, - entry: &EdgeRTreeEntry, -) -> bool { +/// Determines if the edge is walk-eligible based on its own attributes. +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()) } 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..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,6 +212,9 @@ impl EdgeForModalMetric for OsmWayDataSerializable { } fn get_cycleway_tag(&self) -> Option { + if self.highway == Highway::Cycleway { + return Some(CyclewayTag::DedicatedWithBuffer); + } self.cycleway.as_ref().map(|tag| CyclewayTag::new(tag)) } @@ -240,6 +243,7 @@ impl EdgeForModalMetric for OsmWayDataSerializable { | Highway::Steps | Highway::Corridor | Highway::Path + | Highway::Cycleway | Highway::Elevator ) } @@ -249,12 +253,18 @@ 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_pedestrian_priority(&self) -> bool { + matches!(self.highway, Highway::LivingStreet | Highway::Pedestrian) } fn is_unbikeable(&self) -> bool { diff --git a/rust/bambam/src/bin/bambam_util.rs b/rust/bambam/src/bin/bambam_util.rs index d3d7c4ee..974dde3e 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, + modal_metric: ModalMetric, /// input csv file with edges data #[arg(long)] edges_file: String, @@ -213,7 +214,7 @@ impl App { env_logger::init(); match self { Self::ModalMetricSet { - metric_name, + modal_metric, output_file, edges_file, vertices_file, @@ -226,7 +227,7 @@ impl App { // to specify the type of graph data to use. { bulk_compute_modal_metric::( - metric_name, + *modal_metric, edges_file, vertices_file, output_file,