Start a very basic binary classifier for high-stress cycling road, with a layer to check it. Use this to filter the mode shift dashboard -- effectively revealing PCT-esque gaps in the infrastructure. #448, #494

This commit is contained in:
Dustin Carlino 2021-07-26 10:48:00 -07:00
parent 06429d0d5f
commit c5e769d276
4 changed files with 72 additions and 3 deletions

View File

@ -339,4 +339,27 @@ impl Static {
Widget::nothing(),
)
}
pub fn high_stress(ctx: &mut EventCtx, app: &App) -> Static {
let mut colorer = ColorDiscrete::new(app, vec![("high stress", app.cs.edits_layer)]);
for r in app.primary.map.all_roads() {
if r.high_stress_for_bikes(&app.primary.map) {
colorer.add_r(r.id, "high stress");
}
}
Static::new(
ctx,
colorer,
"high stress",
"High stress roads for biking".to_string(),
Text::from_multiline(vec![
Line("High stress defined as:"),
Line("- arterial classification"),
Line("- no dedicated cycle lane"),
])
.into_widget(ctx),
)
}
}

View File

@ -129,6 +129,7 @@ impl PickLayer {
btn("parking efficiency", Key::O),
btn("blackholes", Key::L),
btn("problem map", Key::K),
btn("high stress", Key::H),
if app.primary.sim.get_pandemic_model().is_some() {
btn("pandemic model", Key::Y)
} else {
@ -184,6 +185,9 @@ impl State<App> for PickLayer {
"no sidewalks" => {
app.primary.layer = Some(Box::new(map::Static::no_sidewalks(ctx, app)));
}
"high stress" => {
app.primary.layer = Some(Box::new(map::Static::high_stress(ctx, app)));
}
"favorite buildings" => {
app.primary.layer = Some(Box::new(favorites::ShowFavorites::new(ctx, app)));
}

View File

@ -1,7 +1,9 @@
use std::collections::HashSet;
use abstutil::Counter;
use geom::{Distance, Duration};
use map_gui::tools::ColorNetwork;
use map_model::PathStepV2;
use map_model::{PathStepV2, RoadID};
use sim::{TripEndpoint, TripID, TripMode};
use widgetry::table::{Col, Filter, Table};
use widgetry::{
@ -289,6 +291,20 @@ fn show_route_gaps(ctx: &mut EventCtx, app: &App, table: &Table<App, Entry, Filt
ctx.loading_screen("calculate all routes", |ctx, timer| {
let map = &app.primary.map;
let sim = &app.primary.sim;
// Find all high-stress roads, since we'll filter by them next
let high_stress: HashSet<RoadID> = map
.all_roads()
.into_iter()
.filter_map(|r| {
if r.high_stress_for_bikes(map) {
Some(r.id)
} else {
None
}
})
.collect();
let mut road_counter = Counter::new();
for path in timer
.parallelize("calculate routes", table.get_filtered_data(app), |entry| {
@ -302,8 +318,9 @@ fn show_route_gaps(ctx: &mut EventCtx, app: &App, table: &Table<App, Entry, Filt
for step in path.get_steps() {
// No Contraflow steps for bike paths
if let PathStepV2::Along(dr) = step {
// TODO Filter by high-stress roads only!
road_counter.inc(dr.id);
if high_stress.contains(&dr.id) {
road_counter.inc(dr.id);
}
}
}
}

View File

@ -592,6 +592,31 @@ impl Road {
}
panic!("{} doesn't contain both {} and {}", self.id, l1, l2);
}
/// A simple classification of if the road is stressful or not for cycling. Arterial roads
/// without a bike lane match this. Why arterial, instead of looking at speed limits? Even on
/// arterial roads with official speed limits lowered, in practice vehicles still travel at the
/// speed suggested by the design of the road.
// TODO No treatment of direction
// TODO Should elevation matter or not? Flat high-speed roads are still terrifying, but there's
// something about slogging up (or flying down!) a pothole-filled road inches from cars.
pub fn high_stress_for_bikes(&self, map: &Map) -> bool {
let mut bike_lanes = false;
let mut can_use = false;
// Can a bike even use it, or is it a highway?
for (l, _, lt) in self.lanes_ltr() {
if lt == LaneType::Biking {
bike_lanes = true;
}
if PathConstraints::Bike.can_use(map.get_l(l), map) {
can_use = true;
}
}
if !can_use || bike_lanes {
return false;
}
self.get_rank() == osm::RoadRank::Arterial
}
}
// TODO All of this is kind of deprecated? During the transiton towards lanes_ltr, some pieces