some analytics on how long people spend parking. need a histogram still

This commit is contained in:
Dustin Carlino 2019-12-02 12:15:38 -08:00
parent 8722620fb0
commit f483d7ea85
4 changed files with 90 additions and 33 deletions

View File

@ -24,17 +24,8 @@ impl TripExplorer {
// TODO Hack because ColorLegend only takes &str
let mut rows = Vec::new();
for (idx, p) in phases.iter().enumerate() {
let label = if let Some(t2) = p.end_time {
format!("{} .. {} ({})", p.start_time, t2, t2 - p.start_time)
} else {
format!(
"{} .. ongoing ({} so far)",
p.start_time,
ui.primary.sim.time() - p.start_time
)
};
rows.push((
format!("{}: {}", label, p.description),
p.describe(ui.primary.sim.time()),
rotating_color_map(idx + 1),
));
}

View File

@ -93,8 +93,8 @@ fn dump_debug(id: ID, map: &Map, sim: &Sim, draw_map: &DrawMap) {
sim.debug_car(id);
if let Some(t) = sim.agent_to_trip(AgentID::Car(id)) {
println!("Trip log for {}", t);
for ev in sim.get_analytics().get_trip_log(t) {
println!("- {}", ev);
for p in sim.get_analytics().get_trip_phases(t, map) {
println!("- {}", p.describe(sim.time()));
}
}
}
@ -102,8 +102,8 @@ fn dump_debug(id: ID, map: &Map, sim: &Sim, draw_map: &DrawMap) {
sim.debug_ped(id);
if let Some(t) = sim.agent_to_trip(AgentID::Pedestrian(id)) {
println!("Trip log for {}", t);
for ev in sim.get_analytics().get_trip_log(t) {
println!("- {}", ev);
for p in sim.get_analytics().get_trip_phases(t, map) {
println!("- {}", p.describe(sim.time()));
}
}
}

View File

@ -1,5 +1,5 @@
use crate::common::TripExplorer;
use crate::game::{State, Transition, WizardState};
use crate::game::{msg, State, Transition, WizardState};
use crate::sandbox::gameplay::{cmp_count_fewer, cmp_count_more, cmp_duration_shorter};
use crate::ui::UI;
use abstutil::prettyprint_usize;
@ -23,6 +23,7 @@ impl Scoreboard {
vec![
(hotkey(Key::Escape), "quit"),
(hotkey(Key::B), "browse trips"),
(hotkey(Key::P), "examine parking overhead"),
],
ctx,
);
@ -89,7 +90,7 @@ impl Scoreboard {
}
impl State for Scoreboard {
fn event(&mut self, ctx: &mut EventCtx, _: &mut UI) -> Transition {
fn event(&mut self, ctx: &mut EventCtx, ui: &mut UI) -> Transition {
self.menu.event(ctx);
if self.menu.action("quit") {
return Transition::Pop;
@ -97,6 +98,12 @@ impl State for Scoreboard {
if self.menu.action("browse trips") {
return Transition::Push(WizardState::new(Box::new(browse_trips)));
}
if self.menu.action("examine parking overhead") {
return Transition::Push(msg(
"Parking overhead",
ui.primary.sim.get_analytics().analyze_parking_phases(),
));
}
Transition::Keep
}

View File

@ -283,23 +283,6 @@ impl Analytics {
per_mode
}
pub fn get_trip_log(&self, trip: TripID) -> Vec<String> {
self.trip_log
.iter()
.filter_map(|(t, id, maybe_req, md)| {
if *id == trip {
if let Some(req) = maybe_req {
Some(format!("At {}: {} via {}", t, md, req))
} else {
Some(format!("At {}: {}", t, md))
}
} else {
None
}
})
.collect()
}
pub fn get_trip_phases(&self, trip: TripID, map: &Map) -> Vec<TripPhase> {
let mut phases: Vec<TripPhase> = Vec::new();
for (t, id, maybe_req, md) in &self.trip_log {
@ -324,6 +307,61 @@ impl Analytics {
}
phases
}
fn get_all_trip_phases(&self) -> BTreeMap<TripID, Vec<TripPhase>> {
let mut trips = BTreeMap::new();
for (t, id, _, md) in &self.trip_log {
let phases: &mut Vec<TripPhase> = trips.entry(*id).or_insert_with(Vec::new);
if let Some(ref mut last) = phases.last_mut() {
last.end_time = Some(*t);
}
if md == "trip finished" || md == "trip aborted for some reason" {
// TODO Remove aborted trips?
continue;
}
phases.push(TripPhase {
start_time: *t,
end_time: None,
// Don't compute any paths
path: None,
description: md.clone(),
})
}
trips
}
pub fn analyze_parking_phases(&self) -> Vec<String> {
// Of all completed trips involving parking, what percentage of total time was spent as
// "overhead" -- not the main driving part of the trip?
// TODO This is misleading for border trips -- the driving lasts longer.
for (_, phases) in self.get_all_trip_phases() {
if phases.last().as_ref().unwrap().end_time.is_none() {
continue;
}
let mut driving_time = Duration::ZERO;
let mut overhead = Duration::ZERO;
for p in phases {
let dt = p.end_time.unwrap() - p.start_time;
// TODO New enum instead of strings, if there'll be more analyses like this
if p.description.starts_with("CarID(") {
driving_time += dt;
} else if p.description == "parking somewhere else"
|| p.description == "parking on the current lane"
{
overhead += dt;
} else if p.description.starts_with("PedestrianID(") {
overhead += dt;
} else {
// Waiting for a bus. Irrelevant.
}
}
// Only interested in trips with both
if driving_time == Duration::ZERO || overhead == Duration::ZERO {
continue;
}
}
vec![format!("TODO: need a generic histogram")]
}
}
pub struct TripPhase {
@ -333,3 +371,24 @@ pub struct TripPhase {
pub path: Option<(Distance, Path)>,
pub description: String,
}
impl TripPhase {
pub fn describe(&self, now: Time) -> String {
if let Some(t2) = self.end_time {
format!(
"{} .. {} ({}): {}",
self.start_time,
t2,
t2 - self.start_time,
self.description
)
} else {
format!(
"{} .. ongoing ({} so far): {}",
self.start_time,
now - self.start_time,
self.description
)
}
}
}