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,
population::Options {
heatmap: Some(HeatmapOptions::new()),
with_finished_trip_blocked_pct: None,
},
);
Some(Transition::Pop)

View File

@ -4,7 +4,7 @@ use crate::layer::Layers;
use abstutil::prettyprint_usize;
use ezgui::{
hotkey, Btn, Checkbox, Color, Composite, EventCtx, GeomBatch, HorizontalAlignment, Key, Line,
Spinner, TextExt, VerticalAlignment, Widget,
VerticalAlignment, Widget,
};
use geom::{Circle, Distance, Pt2D};
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
// return this kind of data instead!
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();
// 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) {
if let Some(p) = a.person {
if filter(p) {
pts.push(a.pos);
}
if a.person.is_some() {
pts.push(a.pos);
}
}
@ -48,14 +31,12 @@ pub fn new(ctx: &mut EventCtx, app: &App, opts: Options) -> Layers {
// Already covered above
PersonState::Trip(_) => {}
PersonState::Inside(b) => {
if filter(person.id) {
let pt = app.primary.map.get_b(b).polygon.center();
if seen_bldgs.contains(&b) {
repeat_pts.push(pt);
} else {
seen_bldgs.insert(b);
pts.push(pt);
}
let pt = app.primary.map.get_b(b).polygon.center();
if seen_bldgs.contains(&b) {
repeat_pts.push(pt);
} else {
seen_bldgs.insert(b);
pts.push(pt);
}
}
PersonState::OffMap | PersonState::Limbo => {}
@ -87,8 +68,6 @@ pub fn new(ctx: &mut EventCtx, app: &App, opts: Options) -> Layers {
pub struct Options {
// If None, just a dot map
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(
@ -118,21 +97,6 @@ fn make_controls(
])
.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(
ctx,
@ -155,17 +119,5 @@ pub fn options(c: &mut Composite) -> Options {
} else {
None
};
Options {
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
},
}
Options { heatmap }
}

View File

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