mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 03:35:51 +03:00
Add a layer to color unzoomed agents based on their current delay, to help debug gridlock. Something like this used to exist as a first-class way to change unzoomed color schemes, but until we have more ideas about showing agent intent on the minimap, just implementing this as a separate layer. (#353)
This commit is contained in:
parent
4e4e11b404
commit
de4f5c7768
@ -134,6 +134,7 @@ impl PickLayer {
|
||||
} else {
|
||||
Widget::nothing()
|
||||
},
|
||||
btn("delay per agent", Key::F),
|
||||
]),
|
||||
Widget::col(vec![
|
||||
"Data".draw_text(ctx),
|
||||
@ -228,6 +229,9 @@ impl State for PickLayer {
|
||||
"commuter patterns" => {
|
||||
return Transition::Replace(dashboards::CommuterPatterns::new(ctx, app));
|
||||
}
|
||||
"delay per agent" => {
|
||||
app.layer = Some(Box::new(traffic::DelayPerAgent::new(ctx, app)));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => {
|
||||
|
@ -2,9 +2,10 @@ use crate::app::App;
|
||||
use crate::common::{ColorLegend, ColorNetwork, ColorScale, DivergingScale};
|
||||
use crate::layer::{Layer, LayerOutcome};
|
||||
use abstutil::Counter;
|
||||
use geom::{Distance, Duration, Polygon, Time};
|
||||
use map_model::{IntersectionID, Map, Traversable};
|
||||
use geom::{Circle, Distance, Duration, Polygon, Pt2D, Time};
|
||||
use map_model::{IntersectionID, Map, Traversable, NORMAL_LANE_THICKNESS, SIDEWALK_THICKNESS};
|
||||
use maplit::btreeset;
|
||||
use sim::GetDrawAgents;
|
||||
use std::collections::BTreeSet;
|
||||
use widgetry::{
|
||||
Btn, Checkbox, Color, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line,
|
||||
@ -596,3 +597,90 @@ impl Jam {
|
||||
polygons
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DelayPerAgent {
|
||||
time: Time,
|
||||
unzoomed: Drawable,
|
||||
panel: Panel,
|
||||
}
|
||||
|
||||
impl Layer for DelayPerAgent {
|
||||
fn name(&self) -> Option<&'static str> {
|
||||
Some("delay per agent")
|
||||
}
|
||||
fn event(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
app: &mut App,
|
||||
minimap: &Panel,
|
||||
) -> Option<LayerOutcome> {
|
||||
if app.primary.sim.time() != self.time {
|
||||
*self = DelayPerAgent::new(ctx, app);
|
||||
}
|
||||
|
||||
self.panel.align_above(ctx, minimap);
|
||||
match self.panel.event(ctx) {
|
||||
Outcome::Clicked(x) => match x.as_ref() {
|
||||
"close" => {
|
||||
return Some(LayerOutcome::Close);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
self.panel.draw(g);
|
||||
if g.canvas.cam_zoom < app.opts.min_zoom_for_detail {
|
||||
g.redraw(&self.unzoomed);
|
||||
}
|
||||
}
|
||||
fn draw_minimap(&self, g: &mut GfxCtx) {
|
||||
g.redraw(&self.unzoomed);
|
||||
}
|
||||
}
|
||||
|
||||
impl DelayPerAgent {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App) -> DelayPerAgent {
|
||||
let mut delays = app.primary.sim.all_waiting_people();
|
||||
let mut unzoomed = GeomBatch::new();
|
||||
unzoomed.push(
|
||||
app.cs.fade_map_dark,
|
||||
app.primary.map.get_boundary_polygon().clone(),
|
||||
);
|
||||
// A bit of copied code from draw_unzoomed_agents
|
||||
let car_circle = Circle::new(Pt2D::new(0.0, 0.0), 4.0 * NORMAL_LANE_THICKNESS).to_polygon();
|
||||
let ped_circle = Circle::new(Pt2D::new(0.0, 0.0), 4.0 * SIDEWALK_THICKNESS).to_polygon();
|
||||
for agent in app.primary.sim.get_unzoomed_agents(&app.primary.map) {
|
||||
if let Some(delay) = agent.person.and_then(|p| delays.remove(&p)) {
|
||||
let color = app
|
||||
.cs
|
||||
.good_to_bad_red
|
||||
.eval((delay / Duration::minutes(15)).min(1.0));
|
||||
if agent.vehicle_type.is_some() {
|
||||
unzoomed.push(color, car_circle.translate(agent.pos.x(), agent.pos.y()));
|
||||
} else {
|
||||
unzoomed.push(color, ped_circle.translate(agent.pos.x(), agent.pos.y()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DelayPerAgent {
|
||||
time: app.primary.sim.time(),
|
||||
unzoomed: ctx.upload(unzoomed),
|
||||
panel: Panel::new(Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
Widget::draw_svg(ctx, "system/assets/tools/layers.svg"),
|
||||
"Delay per agent (minutes)".draw_text(ctx),
|
||||
Btn::plaintext("X")
|
||||
.build(ctx, "close", Key::Escape)
|
||||
.align_right(),
|
||||
]),
|
||||
ColorLegend::gradient(ctx, &app.cs.good_to_bad_red, vec!["0", "5", "10", "15+"]),
|
||||
]))
|
||||
.aligned(HorizontalAlignment::Right, VerticalAlignment::Center)
|
||||
.build(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -264,4 +264,13 @@ impl CarState {
|
||||
CarState::IdlingAtStop(_, ref time_int) => time_int.end,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn time_spent_waiting(&self, now: Time) -> Duration {
|
||||
match self {
|
||||
CarState::Queued { blocked_since } | CarState::WaitingToAdvance { blocked_since } => {
|
||||
now - *blocked_since
|
||||
}
|
||||
_ => Duration::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -895,13 +895,7 @@ impl DrivingSimState {
|
||||
pub fn agent_properties(&self, id: CarID, now: Time) -> AgentProperties {
|
||||
let car = self.cars.get(&id).unwrap();
|
||||
let path = car.router.get_path();
|
||||
|
||||
let time_spent_waiting = match car.state {
|
||||
CarState::Queued { blocked_since } | CarState::WaitingToAdvance { blocked_since } => {
|
||||
now - blocked_since
|
||||
}
|
||||
_ => Duration::ZERO,
|
||||
};
|
||||
let time_spent_waiting = car.state.time_spent_waiting(now);
|
||||
|
||||
AgentProperties {
|
||||
total_time: now - car.started_at,
|
||||
@ -1036,4 +1030,15 @@ impl DrivingSimState {
|
||||
}
|
||||
affected
|
||||
}
|
||||
|
||||
pub fn all_waiting_people(&self, now: Time, delays: &mut BTreeMap<PersonID, Duration>) {
|
||||
for c in self.cars.values() {
|
||||
if let Some((_, person)) = c.trip_and_person {
|
||||
let delay = c.state.time_spent_waiting(now);
|
||||
if delay > Duration::ZERO {
|
||||
delays.insert(person, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -316,12 +316,8 @@ impl WalkingSimState {
|
||||
|
||||
pub fn agent_properties(&self, id: PedestrianID, now: Time) -> AgentProperties {
|
||||
let p = &self.peds[&id];
|
||||
let time_spent_waiting = match p.state {
|
||||
PedState::WaitingToTurn(_, blocked_since)
|
||||
| PedState::WaitingForBus(_, blocked_since) => now - blocked_since,
|
||||
_ => Duration::ZERO,
|
||||
};
|
||||
|
||||
let time_spent_waiting = p.state.time_spent_waiting(now);
|
||||
// TODO Incorporate this somewhere
|
||||
/*if let PedState::WaitingForBus(r, _) = p.state {
|
||||
extra.push(format!("Waiting for bus {}", map.get_br(r).name));
|
||||
@ -503,6 +499,15 @@ impl WalkingSimState {
|
||||
}
|
||||
affected
|
||||
}
|
||||
|
||||
pub fn all_waiting_people(&self, now: Time, delays: &mut BTreeMap<PersonID, Duration>) {
|
||||
for p in self.peds.values() {
|
||||
let delay = p.state.time_spent_waiting(now);
|
||||
if delay > Duration::ZERO {
|
||||
delays.insert(p.person, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
@ -731,6 +736,14 @@ impl PedState {
|
||||
PedState::WaitingForBus(_, _) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn time_spent_waiting(&self, now: Time) -> Duration {
|
||||
match self {
|
||||
PedState::WaitingToTurn(_, blocked_since)
|
||||
| PedState::WaitingForBus(_, blocked_since) => now - *blocked_since,
|
||||
_ => Duration::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The crowds returned here may have low/high values extending up to radius past the real geometry.
|
||||
|
@ -371,6 +371,13 @@ impl Sim {
|
||||
pub fn infinite_parking(&self) -> bool {
|
||||
self.parking.is_infinite()
|
||||
}
|
||||
|
||||
pub fn all_waiting_people(&self) -> BTreeMap<PersonID, Duration> {
|
||||
let mut delays = BTreeMap::new();
|
||||
self.walking.all_waiting_people(self.time, &mut delays);
|
||||
self.driving.all_waiting_people(self.time, &mut delays);
|
||||
delays
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AgentProperties {
|
||||
|
Loading…
Reference in New Issue
Block a user