mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-27 16:36:02 +03:00
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:
parent
e79e186784
commit
2239059961
@ -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...
|
|
||||||
],
|
|
||||||
),
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(),
|
||||||
])
|
])
|
||||||
|
@ -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),
|
||||||
])
|
])
|
||||||
|
@ -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
|
||||||
|
@ -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]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user