the new table makes the population map's weird filter irrelevant. also

add sorting by delta time
This commit is contained in:
Dustin Carlino 2020-04-09 12:28:11 -07:00
parent a34b7acb8b
commit f0c0d88764
3 changed files with 72 additions and 92 deletions

View File

@ -414,7 +414,6 @@ impl Layers {
app, app,
population::Options { population::Options {
heatmap: Some(HeatmapOptions::new()), heatmap: Some(HeatmapOptions::new()),
with_finished_trip_blocked_pct: None,
}, },
); );
Some(Transition::Pop) Some(Transition::Pop)

View File

@ -4,7 +4,7 @@ use crate::layer::Layers;
use abstutil::prettyprint_usize; use abstutil::prettyprint_usize;
use ezgui::{ use ezgui::{
hotkey, Btn, Checkbox, Color, Composite, EventCtx, GeomBatch, HorizontalAlignment, Key, Line, hotkey, Btn, Checkbox, Color, Composite, EventCtx, GeomBatch, HorizontalAlignment, Key, Line,
Spinner, TextExt, VerticalAlignment, Widget, VerticalAlignment, Widget,
}; };
use geom::{Circle, Distance, Pt2D}; use geom::{Circle, Distance, Pt2D};
use sim::{GetDrawAgents, PersonState}; use sim::{GetDrawAgents, PersonState};
@ -13,28 +13,11 @@ use std::collections::HashSet;
// TODO Disable drawing unzoomed agents... or alternatively, implement this by asking Sim to // TODO Disable drawing unzoomed agents... or alternatively, implement this by asking Sim to
// return this kind of data instead! // return this kind of data instead!
pub fn new(ctx: &mut EventCtx, app: &App, opts: Options) -> Layers { pub fn new(ctx: &mut EventCtx, app: &App, opts: Options) -> Layers {
let filter = |p| {
if let Some(pct) = opts.with_finished_trip_blocked_pct {
// TODO This is probably inefficient...
app.primary.sim.get_person(p).trips.iter().any(|t| {
if let Some((total, blocked)) = app.primary.sim.finished_trip_time(*t) {
(100.0 * blocked / total) as usize >= pct
} else {
false
}
})
} else {
true
}
};
let mut pts = Vec::new(); let mut pts = Vec::new();
// Faster to grab all agent positions than individually map trips to agent positions. // Faster to grab all agent positions than individually map trips to agent positions.
for a in app.primary.sim.get_unzoomed_agents(&app.primary.map) { for a in app.primary.sim.get_unzoomed_agents(&app.primary.map) {
if let Some(p) = a.person { if a.person.is_some() {
if filter(p) { pts.push(a.pos);
pts.push(a.pos);
}
} }
} }
@ -48,14 +31,12 @@ pub fn new(ctx: &mut EventCtx, app: &App, opts: Options) -> Layers {
// Already covered above // Already covered above
PersonState::Trip(_) => {} PersonState::Trip(_) => {}
PersonState::Inside(b) => { PersonState::Inside(b) => {
if filter(person.id) { let pt = app.primary.map.get_b(b).polygon.center();
let pt = app.primary.map.get_b(b).polygon.center(); if seen_bldgs.contains(&b) {
if seen_bldgs.contains(&b) { repeat_pts.push(pt);
repeat_pts.push(pt); } else {
} else { seen_bldgs.insert(b);
seen_bldgs.insert(b); pts.push(pt);
pts.push(pt);
}
} }
} }
PersonState::OffMap | PersonState::Limbo => {} PersonState::OffMap | PersonState::Limbo => {}
@ -87,8 +68,6 @@ pub fn new(ctx: &mut EventCtx, app: &App, opts: Options) -> Layers {
pub struct Options { pub struct Options {
// If None, just a dot map // If None, just a dot map
pub heatmap: Option<HeatmapOptions>, pub heatmap: Option<HeatmapOptions>,
// TODO More filters... Find people with finished/future trips of any/some mode
pub with_finished_trip_blocked_pct: Option<usize>,
} }
fn make_controls( fn make_controls(
@ -118,21 +97,6 @@ fn make_controls(
]) ])
.centered(), .centered(),
]; ];
col.push(Checkbox::text(
ctx,
"Filter by people with a finished trip",
None,
opts.with_finished_trip_blocked_pct.is_some(),
));
if let Some(pct) = opts.with_finished_trip_blocked_pct {
col.push(Widget::row(vec![
"% of time spent waiting".draw_text(ctx).margin(5),
Spinner::new(ctx, (0, 100), pct)
.named("blocked ratio")
.align_right()
.centered_vert(),
]));
}
col.push(Checkbox::text( col.push(Checkbox::text(
ctx, ctx,
@ -155,17 +119,5 @@ pub fn options(c: &mut Composite) -> Options {
} else { } else {
None None
}; };
Options { Options { heatmap }
heatmap,
with_finished_trip_blocked_pct: if c.is_checked("Filter by people with a finished trip") {
if c.has_widget("blocked ratio") {
Some(c.spinner("blocked ratio"))
} else {
// Just changed, use default
Some(0)
}
} else {
None
},
}
} }

View File

@ -4,8 +4,9 @@ use crate::helpers::cmp_duration_shorter;
use crate::info::Tab; use crate::info::Tab;
use crate::sandbox::SandboxMode; use crate::sandbox::SandboxMode;
use ezgui::{hotkey, Btn, Composite, EventCtx, GfxCtx, Key, Line, Outcome, Text, Widget}; use ezgui::{hotkey, Btn, Composite, EventCtx, GfxCtx, Key, Line, Outcome, Text, Widget};
use geom::{Duration, Time};
use maplit::btreeset; use maplit::btreeset;
use sim::TripID; use sim::{TripID, TripMode};
// TODO Hover over a trip to preview its route on the map // TODO Hover over a trip to preview its route on the map
@ -18,6 +19,7 @@ pub struct TripResults {
enum SortBy { enum SortBy {
Departure, Departure,
Duration, Duration,
RelativeDuration,
PercentWaiting, PercentWaiting,
} }
@ -42,6 +44,9 @@ impl State for TripResults {
"Duration" => { "Duration" => {
self.composite = make(ctx, app, SortBy::Duration); self.composite = make(ctx, app, SortBy::Duration);
} }
"Comparison with baseline" => {
self.composite = make(ctx, app, SortBy::RelativeDuration);
}
"Percent of trip spent waiting" => { "Percent of trip spent waiting" => {
self.composite = make(ctx, app, SortBy::PercentWaiting); self.composite = make(ctx, app, SortBy::PercentWaiting);
} }
@ -71,6 +76,16 @@ impl State for TripResults {
} }
} }
struct Entry {
trip: TripID,
mode: TripMode,
departure: Time,
duration: Duration,
baseline_duration: Duration,
waiting: Duration,
percent_waiting: usize,
}
fn make(ctx: &mut EventCtx, app: &App, sort: SortBy) -> Composite { fn make(ctx: &mut EventCtx, app: &App, sort: SortBy) -> Composite {
let mut data = Vec::new(); let mut data = Vec::new();
let sim = &app.primary.sim; let sim = &app.primary.sim;
@ -80,29 +95,30 @@ fn make(ctx: &mut EventCtx, app: &App, sort: SortBy) -> Composite {
} else { } else {
continue; continue;
}; };
let (_, blocked) = sim.finished_trip_time(*id).unwrap(); let (_, waiting) = sim.finished_trip_time(*id).unwrap();
let (start_time, _, _, _) = sim.trip_info(*id); let (departure, _, _, _) = sim.trip_info(*id);
let comparison = if app.has_prebaked().is_some() { let baseline_duration = if app.has_prebaked().is_some() {
cmp_duration_shorter(*duration, app.prebaked().finished_trip_time(*id).unwrap()) app.prebaked().finished_trip_time(*id).unwrap()
} else { } else {
vec![Line("n/a")] Duration::ZERO
}; };
data.push(( data.push(Entry {
*id, trip: *id,
mode, mode,
start_time, departure,
*duration, duration: *duration,
comparison, baseline_duration,
blocked, waiting,
(100.0 * blocked / *duration) as usize, percent_waiting: (100.0 * waiting / *duration) as usize,
)); });
} }
match sort { match sort {
SortBy::Departure => data.sort_by_key(|(_, _, t, _, _, _, _)| *t), SortBy::Departure => data.sort_by_key(|x| x.departure),
SortBy::Duration => data.sort_by_key(|(_, _, _, dt, _, _, _)| *dt), SortBy::Duration => data.sort_by_key(|x| x.duration),
SortBy::PercentWaiting => data.sort_by_key(|(_, _, _, _, _, _, pct)| *pct), SortBy::RelativeDuration => data.sort_by_key(|x| x.duration - x.baseline_duration),
SortBy::PercentWaiting => data.sort_by_key(|x| x.percent_waiting),
} }
// Descending... // Descending...
data.reverse(); data.reverse();
@ -114,20 +130,20 @@ fn make(ctx: &mut EventCtx, app: &App, sort: SortBy) -> Composite {
let mut col2 = Text::new(); let mut col2 = Text::new();
let mut col3 = Text::new(); let mut col3 = Text::new();
let mut col4 = Text::new(); let mut col4 = Text::new();
let mut col5 = Text::new(); let mut maybe_col5 = Text::new();
let mut col6 = Text::new(); let mut col6 = Text::new();
let mut col7 = Text::new(); let mut col7 = Text::new();
for (id, mode, departure, duration, comparison, blocked, pct_blocked) in for x in data.into_iter().take(30) {
data.into_iter().take(30) col1.push(Btn::plaintext(x.trip.0.to_string()).build_def(ctx, None));
{ col2.add(Line(x.mode.ongoing_verb()));
col1.push(Btn::plaintext(id.0.to_string()).build_def(ctx, None)); col3.add(Line(x.departure.ampm_tostring()));
col2.add(Line(mode.ongoing_verb())); col4.add(Line(x.duration.to_string()));
col3.add(Line(departure.ampm_tostring())); if app.has_prebaked().is_some() {
col4.add(Line(duration.to_string())); maybe_col5.add_appended(cmp_duration_shorter(x.duration, x.baseline_duration));
col5.add_appended(comparison); }
col6.add(Line(blocked.to_string())); col6.add(Line(x.waiting.to_string()));
col7.add(Line(format!("{}%", pct_blocked))); col7.add(Line(format!("{}%", x.percent_waiting)));
} }
Composite::new( Composite::new(
@ -144,21 +160,30 @@ fn make(ctx: &mut EventCtx, app: &App, sort: SortBy) -> Composite {
Line("Trip ID").draw(ctx).margin_right(10), Line("Trip ID").draw(ctx).margin_right(10),
Line("Type").draw(ctx).margin_right(10), Line("Type").draw(ctx).margin_right(10),
if sort == SortBy::Departure { if sort == SortBy::Departure {
Btn::text_fg("Departure").inactive(ctx) Btn::text_bg2("Departure").inactive(ctx)
} else { } else {
Btn::text_fg("Departure").build_def(ctx, None) Btn::text_fg("Departure").build_def(ctx, None)
} }
.margin_right(10), .margin_right(10),
if sort == SortBy::Duration { if sort == SortBy::Duration {
Btn::text_fg("Duration").inactive(ctx) Btn::text_bg2("Duration").inactive(ctx)
} else { } else {
Btn::text_fg("Duration").build_def(ctx, None) Btn::text_fg("Duration").build_def(ctx, None)
} }
.margin_right(10), .margin_right(10),
Line("Comparison with baseline").draw(ctx).margin_right(10), if app.has_prebaked().is_some() {
if sort == SortBy::RelativeDuration {
Btn::text_bg2("Comparison with baseline").inactive(ctx)
} else {
Btn::text_fg("Comparison with baseline").build_def(ctx, None)
}
.margin_right(10)
} else {
Widget::nothing()
},
Line("Time spent waiting").draw(ctx).margin_right(10), Line("Time spent waiting").draw(ctx).margin_right(10),
if sort == SortBy::PercentWaiting { if sort == SortBy::PercentWaiting {
Btn::text_fg("Percent of trip spent waiting").inactive(ctx) Btn::text_bg2("Percent of trip spent waiting").inactive(ctx)
} else { } else {
Btn::text_fg("Percent of trip spent waiting").build_def(ctx, None) Btn::text_fg("Percent of trip spent waiting").build_def(ctx, None)
} }
@ -169,7 +194,11 @@ fn make(ctx: &mut EventCtx, app: &App, sort: SortBy) -> Composite {
col2.draw(ctx).margin_right(10), col2.draw(ctx).margin_right(10),
col3.draw(ctx).margin_right(10), col3.draw(ctx).margin_right(10),
col4.draw(ctx).margin_right(10), col4.draw(ctx).margin_right(10),
col5.draw(ctx).margin_right(10), if app.has_prebaked().is_some() {
maybe_col5.draw(ctx).margin_right(10)
} else {
Widget::nothing()
},
col6.draw(ctx).margin_right(10), col6.draw(ctx).margin_right(10),
col7.draw(ctx).margin_right(10), col7.draw(ctx).margin_right(10),
]) ])