UI fixes discovered during riding buses off-map

- speed limit dropdown crash
- link to bus route from bus stop panel, not some currently running bus
- show number of waiting people per stop in route panel and route dash
This commit is contained in:
Dustin Carlino 2020-07-27 08:19:14 -07:00
parent e79e186784
commit 2239059961
5 changed files with 81 additions and 52 deletions

View File

@ -596,29 +596,29 @@ pub fn can_edit_lane(mode: &GameplayMode, l: LaneID, app: &App) -> bool {
} }
pub fn change_speed_limit(ctx: &mut EventCtx, default: Speed) -> Widget { pub fn change_speed_limit(ctx: &mut EventCtx, default: Speed) -> Widget {
let mut choices = vec![
Choice::new("10 mph", Speed::miles_per_hour(10.0)),
Choice::new("15 mph", Speed::miles_per_hour(15.0)),
Choice::new("20 mph", Speed::miles_per_hour(20.0)),
Choice::new("25 mph", Speed::miles_per_hour(25.0)),
Choice::new("30 mph", Speed::miles_per_hour(30.0)),
Choice::new("35 mph", Speed::miles_per_hour(35.0)),
Choice::new("40 mph", Speed::miles_per_hour(40.0)),
Choice::new("45 mph", Speed::miles_per_hour(45.0)),
Choice::new("50 mph", Speed::miles_per_hour(50.0)),
Choice::new("55 mph", Speed::miles_per_hour(55.0)),
Choice::new("60 mph", Speed::miles_per_hour(60.0)),
Choice::new("65 mph", Speed::miles_per_hour(65.0)),
Choice::new("70 mph", Speed::miles_per_hour(70.0)),
// Don't need anything higher. Though now I kind of miss 3am drives on TX-71...
];
if !choices.iter().any(|c| c.data == default) {
choices.push(Choice::new(default.to_string(), default));
}
Widget::row(vec![ Widget::row(vec![
"Change speed limit:".draw_text(ctx).centered_vert(), "Change speed limit:".draw_text(ctx).centered_vert(),
Widget::dropdown( Widget::dropdown(ctx, "speed limit", default, choices),
ctx,
"speed limit",
default,
vec![
Choice::new("10 mph", Speed::miles_per_hour(10.0)),
Choice::new("15 mph", Speed::miles_per_hour(15.0)),
Choice::new("20 mph", Speed::miles_per_hour(20.0)),
Choice::new("25 mph", Speed::miles_per_hour(25.0)),
Choice::new("30 mph", Speed::miles_per_hour(30.0)),
Choice::new("35 mph", Speed::miles_per_hour(35.0)),
Choice::new("40 mph", Speed::miles_per_hour(40.0)),
Choice::new("45 mph", Speed::miles_per_hour(45.0)),
Choice::new("50 mph", Speed::miles_per_hour(50.0)),
Choice::new("55 mph", Speed::miles_per_hour(55.0)),
Choice::new("60 mph", Speed::miles_per_hour(60.0)),
Choice::new("65 mph", Speed::miles_per_hour(65.0)),
Choice::new("70 mph", Speed::miles_per_hour(70.0)),
// Don't need anything higher. Though now I kind of miss 3am drives on TX-71...
],
),
]) ])
} }

View File

@ -22,19 +22,10 @@ pub fn stop(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusStopID)
let all_arrivals = &sim.get_analytics().bus_arrivals; let all_arrivals = &sim.get_analytics().bus_arrivals;
for r in app.primary.map.get_routes_serving_stop(id) { for r in app.primary.map.get_routes_serving_stop(id) {
let buses = app.primary.sim.status_of_buses(r.id, &app.primary.map); rows.push(Btn::text_fg(format!("Route {}", r.short_name)).build(ctx, &r.full_name, None));
if buses.is_empty() { details
rows.push(format!("Route {}: no buses running", r.short_name).draw_text(ctx)); .hyperlinks
} else { .insert(r.full_name.clone(), Tab::BusRoute(r.id));
rows.push(Btn::text_fg(format!("Route {}", r.short_name)).build(
ctx,
&r.full_name,
None,
));
details
.hyperlinks
.insert(r.full_name.clone(), Tab::BusStatus(buses[0].0));
}
let arrivals: Vec<(Time, CarID)> = all_arrivals let arrivals: Vec<(Time, CarID)> = all_arrivals
.iter() .iter()
@ -201,6 +192,7 @@ pub fn route(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusRouteI
let mut boardings: Counter<BusStopID> = Counter::new(); let mut boardings: Counter<BusStopID> = Counter::new();
let mut alightings: Counter<BusStopID> = Counter::new(); let mut alightings: Counter<BusStopID> = Counter::new();
let mut waiting: Counter<BusStopID> = Counter::new();
for bs in &route.stops { for bs in &route.stops {
if let Some(list) = app.primary.sim.get_analytics().passengers_boarding.get(bs) { if let Some(list) = app.primary.sim.get_analytics().passengers_boarding.get(bs) {
for (_, r, _) in list { for (_, r, _) in list {
@ -216,15 +208,22 @@ pub fn route(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusRouteI
} }
} }
} }
for (_, r, _, _) in app.primary.sim.get_people_waiting_at_stop(*bs) {
if *r == id {
waiting.inc(*bs);
}
}
} }
rows.push( rows.push(
Text::from_all(vec![ Text::from_all(vec![
Line("Total"), Line("Total"),
Line(format!( Line(format!(
": {} boardings, {} alightings", ": {} boardings, {} alightings, {} currently waiting",
prettyprint_usize(boardings.sum()), prettyprint_usize(boardings.sum()),
prettyprint_usize(alightings.sum()) prettyprint_usize(alightings.sum()),
prettyprint_usize(waiting.sum())
)) ))
.secondary(), .secondary(),
]) ])
@ -257,9 +256,10 @@ pub fn route(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusRouteI
Text::from_all(vec![ Text::from_all(vec![
Line(&bs.name), Line(&bs.name),
Line(format!( Line(format!(
": {} boardings, {} alightings", ": {} boardings, {} alightings, {} currently waiting",
prettyprint_usize(boardings.get(bs.id)), prettyprint_usize(boardings.get(bs.id)),
prettyprint_usize(alightings.get(bs.id)) prettyprint_usize(alightings.get(bs.id)),
prettyprint_usize(waiting.get(bs.id))
)) ))
.secondary(), .secondary(),
]) ])

View File

@ -70,7 +70,7 @@ pub struct TransitRoutes {
impl TransitRoutes { impl TransitRoutes {
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> { pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
// Count total boardings/alightings per route // Count totals per route
let mut boardings = Counter::new(); let mut boardings = Counter::new();
for list in app.primary.sim.get_analytics().passengers_boarding.values() { for list in app.primary.sim.get_analytics().passengers_boarding.values() {
for (_, r, _) in list { for (_, r, _) in list {
@ -89,13 +89,20 @@ impl TransitRoutes {
alightings.inc(*r); alightings.inc(*r);
} }
} }
let mut waiting = Counter::new();
for bs in app.primary.map.all_bus_stops().keys() {
for (_, r, _, _) in app.primary.sim.get_people_waiting_at_stop(*bs) {
waiting.inc(*r);
}
}
// Sort descending by count, but ascending by name. Hence the funny negation. // Sort descending by count, but ascending by name. Hence the funny negation.
let mut routes: Vec<(isize, isize, String, BusRouteID)> = Vec::new(); let mut routes: Vec<(isize, isize, isize, String, BusRouteID)> = Vec::new();
for r in app.primary.map.all_bus_routes() { for r in app.primary.map.all_bus_routes() {
routes.push(( routes.push((
-1 * (boardings.get(r.id) as isize), -1 * (boardings.get(r.id) as isize),
-1 * (alightings.get(r.id) as isize), -1 * (alightings.get(r.id) as isize),
-1 * (waiting.get(r.id) as isize),
r.full_name.clone(), r.full_name.clone(),
r.id, r.id,
)); ));
@ -111,7 +118,7 @@ impl TransitRoutes {
ctx, ctx,
routes routes
.iter() .iter()
.map(|(_, _, r, id)| (r.clone(), *id)) .map(|(_, _, _, r, id)| (r.clone(), *id))
.collect(), .collect(),
) )
.named("search"), .named("search"),
@ -121,13 +128,14 @@ impl TransitRoutes {
Widget::col( Widget::col(
routes routes
.into_iter() .into_iter()
.map(|(boardings, alightings, name, id)| { .map(|(boardings, alightings, waiting, name, id)| {
Widget::row(vec![ Widget::row(vec![
Btn::text_fg(name).build(ctx, id.to_string(), None), Btn::text_fg(name).build(ctx, id.to_string(), None),
format!( format!(
"{} boardings, {} alightings", "{} boardings, {} alightings, {} currently waiting",
prettyprint_usize(-boardings as usize), prettyprint_usize(-boardings as usize),
prettyprint_usize(-alightings as usize) prettyprint_usize(-alightings as usize),
prettyprint_usize(-waiting as usize)
) )
.draw_text(ctx), .draw_text(ctx),
]) ])

View File

@ -13,8 +13,8 @@ use derivative::Derivative;
use geom::{Distance, Duration, PolyLine, Pt2D, Speed, Time}; use geom::{Distance, Duration, PolyLine, Pt2D, Speed, Time};
use instant::Instant; use instant::Instant;
use map_model::{ use map_model::{
BuildingID, BusRoute, BusRouteID, IntersectionID, Lane, LaneID, Map, ParkingLotID, Path, BuildingID, BusRoute, BusRouteID, BusStopID, IntersectionID, Lane, LaneID, Map, ParkingLotID,
PathConstraints, PathRequest, Position, RoadID, Traversable, Path, PathConstraints, PathRequest, Position, RoadID, Traversable,
}; };
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -118,7 +118,7 @@ impl Sim {
opts.dont_block_the_box, opts.dont_block_the_box,
opts.break_turn_conflict_cycles, opts.break_turn_conflict_cycles,
), ),
transit: TransitSimState::new(), transit: TransitSimState::new(map),
trips: TripManager::new(opts.pathfinding_upfront), trips: TripManager::new(opts.pathfinding_upfront),
pandemic: if let Some(rng) = opts.enable_pandemic_model { pandemic: if let Some(rng) = opts.enable_pandemic_model {
Some(PandemicModel::new(rng)) Some(PandemicModel::new(rng))
@ -854,6 +854,7 @@ impl Sim {
} }
// Queries of all sorts // Queries of all sorts
// TODO Many of these just delegate to an inner piece. This is unorganized and hard to maintain.
impl Sim { impl Sim {
pub fn time(&self) -> Time { pub fn time(&self) -> Time {
self.time self.time
@ -1178,6 +1179,13 @@ impl Sim {
self.driving.target_lane_penalty(lane.id) self.driving.target_lane_penalty(lane.id)
} }
} }
pub fn get_people_waiting_at_stop(
&self,
at: BusStopID,
) -> &Vec<(PedestrianID, BusRouteID, Option<BusStopID>, Time)> {
self.transit.get_people_waiting_at_stop(at)
}
} }
// Invasive debugging // Invasive debugging

View File

@ -67,11 +67,17 @@ pub struct TransitSimState {
} }
impl TransitSimState { impl TransitSimState {
pub fn new() -> TransitSimState { pub fn new(map: &Map) -> TransitSimState {
// Keep this filled out always so get_passengers can return &Vec without a hassle
let mut peds_waiting = BTreeMap::new();
for bs in map.all_bus_stops().keys() {
peds_waiting.insert(*bs, Vec::new());
}
TransitSimState { TransitSimState {
buses: BTreeMap::new(), buses: BTreeMap::new(),
routes: BTreeMap::new(), routes: BTreeMap::new(),
peds_waiting: BTreeMap::new(), peds_waiting,
events: Vec::new(), events: Vec::new(),
} }
} }
@ -207,7 +213,7 @@ impl TransitSimState {
// Board new passengers. // Board new passengers.
let mut still_waiting = Vec::new(); let mut still_waiting = Vec::new();
for (ped, route, maybe_stop2, started_waiting) in for (ped, route, maybe_stop2, started_waiting) in
self.peds_waiting.remove(&stop1).unwrap_or_else(Vec::new) self.peds_waiting.remove(&stop1).unwrap()
{ {
if bus.route == route { if bus.route == route {
let (trip, person) = trips.ped_boarded_bus( let (trip, person) = trips.ped_boarded_bus(
@ -347,8 +353,8 @@ impl TransitSimState {
} }
self.peds_waiting self.peds_waiting
.entry(stop1) .get_mut(&stop1)
.or_insert_with(Vec::new) .unwrap()
.push((ped, route_id, maybe_stop2, now)); .push((ped, route_id, maybe_stop2, now));
None None
} }
@ -407,4 +413,11 @@ impl TransitSimState {
} }
(buses, trains) (buses, trains)
} }
pub fn get_people_waiting_at_stop(
&self,
at: BusStopID,
) -> &Vec<(PedestrianID, BusRouteID, Option<BusStopID>, Time)> {
&self.peds_waiting[&at]
}
} }