mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-27 00:12:55 +03:00
start a layer to compare delay. needs work.
This commit is contained in:
parent
a96b77ba3f
commit
0cff318d0a
@ -24,6 +24,7 @@ TODO:
|
||||
- proposed
|
||||
[here](https://old.reddit.com/r/SeattleWA/comments/gr4dsi/its_time_for_mayor_durkan_to_bring_stay_healthy/)
|
||||
- partly [happening already](https://www.openstreetmap.org/way/814244753)
|
||||
- another [proposal](https://twitter.com/pushtheneedle/status/1270757771802103809/photo/1)
|
||||
- Traffic signal timing at Montlake/520 and Montlake/Pacific
|
||||
- Walking around here is frustrating, and pre-COVID, vehicle traffic got
|
||||
fairly stuck
|
||||
|
@ -125,7 +125,7 @@ impl State for PickLayer {
|
||||
)));
|
||||
}
|
||||
"delay" => {
|
||||
app.layer = Some(Box::new(traffic::Dynamic::delay(ctx, app)));
|
||||
app.layer = Some(Box::new(traffic::Delay::new(ctx, app, false)));
|
||||
}
|
||||
"worst traffic jams" => {
|
||||
app.layer = Some(Box::new(traffic::Dynamic::traffic_jams(ctx, app)));
|
||||
|
@ -27,7 +27,6 @@ impl Layer for Dynamic {
|
||||
) -> Option<LayerOutcome> {
|
||||
if app.primary.sim.time() != self.time {
|
||||
*self = match self.name {
|
||||
"delay" => Dynamic::delay(ctx, app),
|
||||
"worst traffic jams" => Dynamic::traffic_jams(ctx, app),
|
||||
"backpressure" => Dynamic::backpressure(ctx, app),
|
||||
_ => unreachable!(),
|
||||
@ -49,53 +48,6 @@ impl Layer for Dynamic {
|
||||
}
|
||||
|
||||
impl Dynamic {
|
||||
pub fn delay(ctx: &mut EventCtx, app: &App) -> Dynamic {
|
||||
// TODO explain more
|
||||
let mut colorer = Colorer::scaled(
|
||||
ctx,
|
||||
"Delay (minutes)",
|
||||
Vec::new(),
|
||||
app.cs.good_to_bad_monochrome_red.to_vec(),
|
||||
vec!["0.5", "1", "5", "15", "longer"],
|
||||
);
|
||||
|
||||
let (per_road, per_intersection) = app.primary.sim.worst_delay(&app.primary.map);
|
||||
for (r, d) in per_road {
|
||||
let color = if d < Duration::seconds(30.0) {
|
||||
continue;
|
||||
} else if d < Duration::minutes(1) {
|
||||
app.cs.good_to_bad_monochrome_red[0]
|
||||
} else if d < Duration::minutes(5) {
|
||||
app.cs.good_to_bad_monochrome_red[1]
|
||||
} else if d < Duration::minutes(15) {
|
||||
app.cs.good_to_bad_monochrome_red[2]
|
||||
} else {
|
||||
app.cs.good_to_bad_monochrome_red[3]
|
||||
};
|
||||
colorer.add_r(r, color, &app.primary.map);
|
||||
}
|
||||
for (i, d) in per_intersection {
|
||||
let color = if d < Duration::seconds(30.0) {
|
||||
continue;
|
||||
} else if d < Duration::minutes(1) {
|
||||
app.cs.good_to_bad_monochrome_red[0]
|
||||
} else if d < Duration::minutes(5) {
|
||||
app.cs.good_to_bad_monochrome_red[1]
|
||||
} else if d < Duration::minutes(15) {
|
||||
app.cs.good_to_bad_monochrome_red[2]
|
||||
} else {
|
||||
app.cs.good_to_bad_monochrome_red[3]
|
||||
};
|
||||
colorer.add_i(i, color);
|
||||
}
|
||||
|
||||
Dynamic {
|
||||
time: app.primary.sim.time(),
|
||||
colorer: colorer.build_unzoomed(ctx, app),
|
||||
name: "delay",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn traffic_jams(ctx: &mut EventCtx, app: &App) -> Dynamic {
|
||||
let jams = app.primary.sim.delayed_intersections(Duration::minutes(5));
|
||||
|
||||
@ -454,3 +406,195 @@ impl Throughput {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Delay {
|
||||
time: Time,
|
||||
compare: bool,
|
||||
unzoomed: Drawable,
|
||||
composite: Composite,
|
||||
}
|
||||
|
||||
impl Layer for Delay {
|
||||
fn name(&self) -> Option<&'static str> {
|
||||
Some("delay")
|
||||
}
|
||||
fn event(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
app: &mut App,
|
||||
minimap: &Composite,
|
||||
) -> Option<LayerOutcome> {
|
||||
if app.primary.sim.time() != self.time {
|
||||
*self = Delay::new(ctx, app, self.compare);
|
||||
}
|
||||
|
||||
self.composite.align_above(ctx, minimap);
|
||||
match self.composite.event(ctx) {
|
||||
Some(Outcome::Clicked(x)) => match x.as_ref() {
|
||||
"close" => {
|
||||
return Some(LayerOutcome::Close);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
None => {
|
||||
let new_compare = self.composite.has_widget("Compare before edits")
|
||||
&& self.composite.is_checked("Compare before edits");
|
||||
if new_compare != self.compare {
|
||||
*self = Delay::new(ctx, app, new_compare);
|
||||
self.composite.align_above(ctx, minimap);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
self.composite.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 Delay {
|
||||
pub fn new(ctx: &mut EventCtx, app: &App, compare: bool) -> Delay {
|
||||
if compare {
|
||||
return Delay::compare_delay(ctx, app);
|
||||
}
|
||||
|
||||
// TODO explain more
|
||||
let mut colorer = Colorer::scaled(
|
||||
ctx,
|
||||
"Delay (minutes)",
|
||||
Vec::new(),
|
||||
app.cs.good_to_bad_monochrome_red.to_vec(),
|
||||
vec!["0.5", "1", "5", "15", "longer"],
|
||||
);
|
||||
|
||||
let (per_road, per_intersection) = app.primary.sim.worst_delay(&app.primary.map);
|
||||
for (r, d) in per_road {
|
||||
let color = if d < Duration::seconds(30.0) {
|
||||
continue;
|
||||
} else if d < Duration::minutes(1) {
|
||||
app.cs.good_to_bad_monochrome_red[0]
|
||||
} else if d < Duration::minutes(5) {
|
||||
app.cs.good_to_bad_monochrome_red[1]
|
||||
} else if d < Duration::minutes(15) {
|
||||
app.cs.good_to_bad_monochrome_red[2]
|
||||
} else {
|
||||
app.cs.good_to_bad_monochrome_red[3]
|
||||
};
|
||||
colorer.add_r(r, color, &app.primary.map);
|
||||
}
|
||||
for (i, d) in per_intersection {
|
||||
let color = if d < Duration::seconds(30.0) {
|
||||
continue;
|
||||
} else if d < Duration::minutes(1) {
|
||||
app.cs.good_to_bad_monochrome_red[0]
|
||||
} else if d < Duration::minutes(5) {
|
||||
app.cs.good_to_bad_monochrome_red[1]
|
||||
} else if d < Duration::minutes(15) {
|
||||
app.cs.good_to_bad_monochrome_red[2]
|
||||
} else {
|
||||
app.cs.good_to_bad_monochrome_red[3]
|
||||
};
|
||||
colorer.add_i(i, color);
|
||||
}
|
||||
|
||||
let composite = Composite::new(
|
||||
Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
Widget::draw_svg(ctx, "../data/system/assets/tools/layers.svg")
|
||||
.margin_right(10),
|
||||
"Delay (minutes)".draw_text(ctx),
|
||||
Btn::plaintext("X")
|
||||
.build(ctx, "close", hotkey(Key::Escape))
|
||||
.align_right(),
|
||||
]),
|
||||
if app.has_prebaked().is_some() {
|
||||
Checkbox::text(ctx, "Compare before edits", None, false).margin_below(5)
|
||||
} else {
|
||||
Widget::nothing()
|
||||
},
|
||||
ColorLegend::scale(
|
||||
ctx,
|
||||
app.cs.good_to_bad_monochrome_red.to_vec(),
|
||||
vec!["0.5", "1", "5", "15", "longer"],
|
||||
),
|
||||
])
|
||||
.padding(5)
|
||||
.bg(app.cs.panel_bg),
|
||||
)
|
||||
.aligned(HorizontalAlignment::Right, VerticalAlignment::Center)
|
||||
.build(ctx);
|
||||
|
||||
Delay {
|
||||
time: app.primary.sim.time(),
|
||||
compare: false,
|
||||
unzoomed: colorer.build_both(ctx, app).unzoomed,
|
||||
composite,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Needs work.
|
||||
fn compare_delay(ctx: &mut EventCtx, app: &App) -> Delay {
|
||||
let map = &app.primary.map;
|
||||
let mut batch = GeomBatch::new();
|
||||
batch.push(app.cs.fade_map_dark, map.get_boundary_polygon().clone());
|
||||
let red = Color::hex("#A32015");
|
||||
let green = Color::hex("#5D9630");
|
||||
|
||||
let results = app
|
||||
.primary
|
||||
.sim
|
||||
.get_analytics()
|
||||
.compare_delay(app.primary.sim.time(), app.prebaked());
|
||||
if !results.is_empty() {
|
||||
let fastest = results.iter().min_by_key(|(_, dt)| *dt).unwrap().1;
|
||||
let slowest = results.iter().max_by_key(|(_, dt)| *dt).unwrap().1;
|
||||
|
||||
for (i, dt) in results {
|
||||
let color = if dt < Duration::ZERO {
|
||||
green.lerp(Color::WHITE, (1.0 - (dt / fastest)) as f32)
|
||||
} else {
|
||||
Color::WHITE.lerp(red, (dt / slowest) as f32)
|
||||
};
|
||||
batch.push(color, map.get_i(i).polygon.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let composite = Composite::new(
|
||||
Widget::col(vec![
|
||||
Widget::row(vec![
|
||||
Widget::draw_svg(ctx, "../data/system/assets/tools/layers.svg")
|
||||
.margin_right(10),
|
||||
"Delay".draw_text(ctx),
|
||||
Btn::plaintext("X")
|
||||
.build(ctx, "close", hotkey(Key::Escape))
|
||||
.align_right(),
|
||||
]),
|
||||
Checkbox::text(ctx, "Compare before edits", None, true).margin_below(5),
|
||||
ColorLegend::gradient_3(
|
||||
ctx,
|
||||
green,
|
||||
Color::WHITE,
|
||||
red,
|
||||
vec!["faster", "same", "slower"],
|
||||
),
|
||||
])
|
||||
.padding(5)
|
||||
.bg(app.cs.panel_bg),
|
||||
)
|
||||
.aligned(HorizontalAlignment::Right, VerticalAlignment::Center)
|
||||
.build(ctx);
|
||||
|
||||
Delay {
|
||||
time: app.primary.sim.time(),
|
||||
compare: true,
|
||||
unzoomed: ctx.upload(batch),
|
||||
composite,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,6 +230,35 @@ impl Analytics {
|
||||
results
|
||||
}
|
||||
|
||||
// Find intersections where the cumulative sum of delay has changed. Negative means faster.
|
||||
pub fn compare_delay(&self, now: Time, before: &Analytics) -> Vec<(IntersectionID, Duration)> {
|
||||
let mut results = Vec::new();
|
||||
for (i, list1) in &self.intersection_delays {
|
||||
if let Some(list2) = before.intersection_delays.get(i) {
|
||||
let mut sum1 = Duration::ZERO;
|
||||
for (t, dt) in list1 {
|
||||
if *t > now {
|
||||
break;
|
||||
}
|
||||
sum1 += *dt;
|
||||
}
|
||||
|
||||
let mut sum2 = Duration::ZERO;
|
||||
for (t, dt) in list2 {
|
||||
if *t > now {
|
||||
break;
|
||||
}
|
||||
sum2 += *dt;
|
||||
}
|
||||
|
||||
if sum1 != sum2 {
|
||||
results.push((*i, sum1 - sum2));
|
||||
}
|
||||
}
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
pub fn bus_arrivals(
|
||||
&self,
|
||||
now: Time,
|
||||
|
Loading…
Reference in New Issue
Block a user