mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-11 08:15:34 +03:00
the new table makes the population map's weird filter irrelevant. also
add sorting by delta time
This commit is contained in:
parent
a34b7acb8b
commit
f0c0d88764
@ -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)
|
||||||
|
@ -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
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
])
|
])
|
||||||
|
Loading…
Reference in New Issue
Block a user