experiment with finding sources of gridlock, exposed as a new heatmap

This commit is contained in:
Dustin Carlino 2020-02-26 12:13:28 -08:00
parent 1ffcd1e5b5
commit e19f88cd40
4 changed files with 70 additions and 0 deletions

View File

@ -20,6 +20,7 @@ pub enum Overlays {
Inactive,
ParkingAvailability(Time, Colorer),
IntersectionDelay(Time, Colorer),
Gridlock(Time, Colorer),
CumulativeThroughput(Time, Colorer),
BikeNetwork(Colorer),
BusNetwork(Colorer),
@ -48,6 +49,11 @@ impl Overlays {
ui.overlay = Overlays::intersection_delay(ctx, ui);
}
}
Overlays::Gridlock(t, _) => {
if now != t {
ui.overlay = Overlays::gridlock(ctx, ui);
}
}
Overlays::CumulativeThroughput(t, _) => {
if now != t {
ui.overlay = Overlays::cumulative_throughput(ctx, ui);
@ -94,6 +100,7 @@ impl Overlays {
| Overlays::BikeNetwork(ref mut heatmap)
| Overlays::BusNetwork(ref mut heatmap)
| Overlays::IntersectionDelay(_, ref mut heatmap)
| Overlays::Gridlock(_, ref mut heatmap)
| Overlays::CumulativeThroughput(_, ref mut heatmap)
| Overlays::Edits(ref mut heatmap) => {
heatmap.legend.align_above(ctx, minimap);
@ -182,6 +189,7 @@ impl Overlays {
| Overlays::BikeNetwork(ref heatmap)
| Overlays::BusNetwork(ref heatmap)
| Overlays::IntersectionDelay(_, ref heatmap)
| Overlays::Gridlock(_, ref heatmap)
| Overlays::CumulativeThroughput(_, ref heatmap)
| Overlays::Edits(ref heatmap) => {
heatmap.draw(g);
@ -209,6 +217,7 @@ impl Overlays {
| Overlays::BikeNetwork(ref heatmap)
| Overlays::BusNetwork(ref heatmap)
| Overlays::IntersectionDelay(_, ref heatmap)
| Overlays::Gridlock(_, ref heatmap)
| Overlays::CumulativeThroughput(_, ref heatmap)
| Overlays::Edits(ref heatmap) => Some(heatmap),
Overlays::BusRoute(_, _, ref s) => Some(&s.colorer),
@ -220,6 +229,7 @@ impl Overlays {
let mut choices = vec![
WrappedComposite::text_button(ctx, "None", hotkey(Key::N)),
WrappedComposite::text_button(ctx, "map edits", hotkey(Key::E)),
WrappedComposite::text_button(ctx, "worst traffic jams", hotkey(Key::G)),
ManagedWidget::btn(Button::rectangle_svg(
"../data/system/assets/layers/parking_avail.svg",
"parking availability",
@ -267,6 +277,10 @@ impl Overlays {
"intersection delay",
ManagedWidget::draw_svg(ctx, "../data/system/assets/layers/intersection_delay.svg"),
)),
Overlays::Gridlock(_, _) => Some((
"worst traffic jams",
Button::inactive_button(ctx, "worst traffic jams"),
)),
Overlays::CumulativeThroughput(_, _) => Some((
"throughput",
ManagedWidget::draw_svg(ctx, "../data/system/assets/layers/throughput.svg"),
@ -328,6 +342,13 @@ impl Overlays {
Some(Transition::Pop)
}),
)
.maybe_cb(
"worst traffic jams",
Box::new(|ctx, ui| {
ui.overlay = Overlays::gridlock(ctx, ui);
Some(Transition::Pop)
}),
)
.maybe_cb(
"throughput",
Box::new(|ctx, ui| {
@ -465,6 +486,35 @@ impl Overlays {
Overlays::IntersectionDelay(ui.primary.sim.time(), colorer.build(ctx, ui))
}
fn gridlock(ctx: &mut EventCtx, ui: &UI) -> Overlays {
let jams = ui.primary.sim.delayed_intersections(Duration::minutes(5));
// TODO Silly colors
let others = Color::hex("#7FFA4D");
let early = Color::hex("#F4DA22");
let earliest = Color::hex("#EB5757");
let mut colorer = Colorer::new(
Text::from(Line(format!("{} traffic jams", jams.len()))),
vec![
("longest lasting", earliest),
("recent problems", early),
("others", others),
],
);
for (idx, (i, _)) in jams.into_iter().enumerate() {
if idx == 0 {
colorer.add_i(i, earliest);
} else if idx <= 5 {
colorer.add_i(i, early);
} else {
colorer.add_i(i, others);
}
}
Overlays::Gridlock(ui.primary.sim.time(), colorer.build(ctx, ui))
}
fn cumulative_throughput(ctx: &mut EventCtx, ui: &UI) -> Overlays {
let light = Color::hex("#7FFA4D");
let medium = Color::hex("#F4DA22");

View File

@ -475,6 +475,7 @@ impl TimeWarpScreen {
WrappedComposite::text_bg_button(ctx, "stop now", hotkey(Key::Escape))
.centered_horiz(),
])
.padding(10)
.bg(colors::PANEL_BG),
)
.build(ctx),

View File

@ -267,6 +267,19 @@ impl IntersectionSimState {
pub fn collect_events(&mut self) -> Vec<Event> {
std::mem::replace(&mut self.events, Vec::new())
}
pub fn find_gridlock(&self, now: Time, threshold: Duration) -> Vec<(IntersectionID, Time)> {
let mut candidates = Vec::new();
for state in self.state.values() {
if let Some(earliest) = state.waiting.values().min() {
if now - *earliest >= threshold {
candidates.push((state.id, *earliest));
}
}
}
candidates.sort_by_key(|(_, t)| *t);
candidates
}
}
impl State {

View File

@ -1060,6 +1060,12 @@ impl Sim {
.find_blockage_front(car, map, &self.intersections)
}
// For intersections with an agent waiting beyond some threshold, return when they started
// waiting. Sorted by earliest waiting (likely the root cause of gridlock).
pub fn delayed_intersections(&self, threshold: Duration) -> Vec<(IntersectionID, Time)> {
self.intersections.find_gridlock(self.time, threshold)
}
pub fn trip_spec_to_path_req(&self, spec: &TripSpec, map: &Map) -> PathRequest {
spec.get_pathfinding_request(map, &self.parking)
}