Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<E, V>(
metric_name: &str,
metric: ModalMetric,
edges_file: &str,
vertices_file: &str,
output_file: &str,
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions rust/bambam-modal-metrics/src/common/edge_rtree_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment thread
admrtin marked this conversation as resolved.

/// `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
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
21 changes: 13 additions & 8 deletions rust/bambam-modal-metrics/src/wci/compute_wci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,20 @@ 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);
Comment thread
admrtin marked this conversation as resolved.

// grab the neighboring edges
let neighboring_edges = find_neighboring_edges(entry, rtree);

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.
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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)]),

@admrtin admrtin Sep 2, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit updated the test coordinates so that the neighboring edge's centroid falls within the 15m R-tree search radius. In the previous commit, the centroids were slightly over 15m apart at ~25m.

This caused the test to fail because no neighbors were assigned to the query edge. But this is a good failure because it means that the new R-tree squared neighbor distance (~1.8e-8°) is correctly enforcing the 15m threshold that we want.

Moving the geometries slightly closer allowed the test to pass.

};

let src_vertex = TestVertex {
Expand Down
16 changes: 3 additions & 13 deletions rust/bambam-modal-metrics/src/wci/ops.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -70,16 +69,7 @@ pub fn traffic_speed_comfort_from_neighbors<E: SpatialEdge + EdgeForModalMetric>
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<E: SpatialEdge + EdgeForModalMetric>(
rtree: &RTree<EdgeRTreeEntry<E>>,
entry: &EdgeRTreeEntry<E>,
) -> bool {
/// Determines if the edge is walk-eligible based on its own attributes.
pub fn is_walk_eligible<E: SpatialEdge + EdgeForModalMetric>(entry: &EdgeRTreeEntry<E>) -> 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())
}
10 changes: 10 additions & 0 deletions rust/bambam-osm/src/model/osm/graph/osm_way_data_serializable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ impl EdgeForModalMetric for OsmWayDataSerializable {
}

fn get_cycleway_tag(&self) -> Option<CyclewayTag> {
if self.highway == Highway::Cycleway {
return Some(CyclewayTag::DedicatedWithBuffer);
}
self.cycleway.as_ref().map(|tag| CyclewayTag::new(tag))
}

Expand Down Expand Up @@ -240,6 +243,7 @@ impl EdgeForModalMetric for OsmWayDataSerializable {
| Highway::Steps
| Highway::Corridor
| Highway::Path
| Highway::Cycleway
| Highway::Elevator
)
}
Expand All @@ -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 {
Expand Down
7 changes: 4 additions & 3 deletions rust/bambam/src/bin/bambam_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -213,7 +214,7 @@ impl App {
env_logger::init();
match self {
Self::ModalMetricSet {
metric_name,
modal_metric,
output_file,
edges_file,
vertices_file,
Expand All @@ -226,7 +227,7 @@ impl App {
// to specify the type of graph data to use.
{
bulk_compute_modal_metric::<OsmWayDataSerializable, OsmNodeDataSerializable>(
metric_name,
*modal_metric,
edges_file,
vertices_file,
output_file,
Expand Down
Loading