mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 15:33:44 +03:00
start revamping bus route data model for #190. routes are one-way. make
buses appear at the first stop and vanish at the last. not regenerating anything yet
This commit is contained in:
parent
0ad14d17ac
commit
91a9a9a1bc
@ -290,7 +290,7 @@ pub fn extract_osm(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if tags.is("type", "route_master") {
|
} else if tags.is("type", "route") {
|
||||||
map.bus_routes
|
map.bus_routes
|
||||||
.extend(extract_route(&tags, rel, &doc, &id_to_way, &map.gps_bounds));
|
.extend(extract_route(&tags, rel, &doc, &id_to_way, &map.gps_bounds));
|
||||||
} else if tags.is("type", "multipolygon") && tags.contains_key("amenity") {
|
} else if tags.is("type", "multipolygon") && tags.contains_key("amenity") {
|
||||||
@ -648,104 +648,87 @@ fn glue_to_boundary(result_pl: PolyLine, boundary: &Ring) -> Option<Polygon> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extract_route(
|
fn extract_route(
|
||||||
master_tags: &Tags,
|
tags: &Tags,
|
||||||
master_rel: &osm_xml::Relation,
|
rel: &osm_xml::Relation,
|
||||||
doc: &osm_xml::OSM,
|
doc: &osm_xml::OSM,
|
||||||
id_to_way: &HashMap<i64, Vec<Pt2D>>,
|
id_to_way: &HashMap<i64, Vec<Pt2D>>,
|
||||||
gps_bounds: &GPSBounds,
|
gps_bounds: &GPSBounds,
|
||||||
) -> Option<RawBusRoute> {
|
) -> Option<RawBusRoute> {
|
||||||
let route_name = master_tags.get("name")?.clone();
|
let full_name = tags.get("name")?.clone();
|
||||||
let is_bus = match master_tags.get("route_master")?.as_ref() {
|
let short_name = tags
|
||||||
|
.get("ref")
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| full_name.clone());
|
||||||
|
let is_bus = match tags.get("route")?.as_ref() {
|
||||||
"bus" => true,
|
"bus" => true,
|
||||||
"light_rail" => false,
|
"light_rail" => false,
|
||||||
x => {
|
x => {
|
||||||
println!(
|
println!(
|
||||||
"Skipping route {} of unknown type {}: {}",
|
"Skipping route {} of unknown type {}: {}",
|
||||||
route_name,
|
full_name,
|
||||||
x,
|
x,
|
||||||
rel_url(master_rel.id)
|
rel_url(rel.id)
|
||||||
);
|
);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut directions = Vec::new();
|
// Gather stops in order. Platforms may exist or not; match them up by name.
|
||||||
for (_, route_member) in get_members(master_rel, doc) {
|
let mut stops = Vec::new();
|
||||||
if let osm_xml::Reference::Relation(route_rel) = route_member {
|
let mut platforms = HashMap::new();
|
||||||
let route_tags = tags_to_map(&route_rel.tags);
|
for (role, member) in get_members(rel, doc) {
|
||||||
assert_eq!(route_tags.get("type"), Some(&"route".to_string()));
|
if role == "stop" {
|
||||||
// Gather stops in order. Platforms may exist or not; match them up by name.
|
if let osm_xml::Reference::Node(node) = member {
|
||||||
let mut stops = Vec::new();
|
stops.push(RawBusStop {
|
||||||
let mut platforms = HashMap::new();
|
name: tags_to_map(&node.tags)
|
||||||
for (role, member) in get_members(&route_rel, doc) {
|
.get("name")
|
||||||
if role == "stop" {
|
.cloned()
|
||||||
if let osm_xml::Reference::Node(node) = member {
|
.unwrap_or_else(|| format!("stop #{}", stops.len() + 1)),
|
||||||
stops.push(RawBusStop {
|
vehicle_pos: Pt2D::from_gps(LonLat::new(node.lon, node.lat), gps_bounds),
|
||||||
name: tags_to_map(&node.tags)
|
ped_pos: None,
|
||||||
.get("name")
|
});
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| format!("stop #{}", stops.len() + 1)),
|
|
||||||
vehicle_pos: Pt2D::from_gps(
|
|
||||||
LonLat::new(node.lon, node.lat),
|
|
||||||
gps_bounds,
|
|
||||||
),
|
|
||||||
ped_pos: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if role == "platform" {
|
|
||||||
let (platform_name, pt) = match member {
|
|
||||||
osm_xml::Reference::Node(node) => (
|
|
||||||
tags_to_map(&node.tags)
|
|
||||||
.get("name")
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| format!("stop #{}", platforms.len() + 1)),
|
|
||||||
Pt2D::from_gps(LonLat::new(node.lon, node.lat), gps_bounds),
|
|
||||||
),
|
|
||||||
osm_xml::Reference::Way(way) => (
|
|
||||||
tags_to_map(&way.tags)
|
|
||||||
.get("name")
|
|
||||||
.cloned()
|
|
||||||
.unwrap_or_else(|| format!("stop #{}", platforms.len() + 1)),
|
|
||||||
if let Some(ref pts) = id_to_way.get(&way.id) {
|
|
||||||
Pt2D::center(pts)
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
platforms.insert(platform_name, pt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for stop in &mut stops {
|
|
||||||
if let Some(pt) = platforms.remove(&stop.name) {
|
|
||||||
stop.ped_pos = Some(pt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if stops.len() >= 2 {
|
|
||||||
directions.push(stops);
|
|
||||||
}
|
}
|
||||||
|
} else if role == "platform" {
|
||||||
|
let (platform_name, pt) = match member {
|
||||||
|
osm_xml::Reference::Node(node) => (
|
||||||
|
tags_to_map(&node.tags)
|
||||||
|
.get("name")
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| format!("stop #{}", platforms.len() + 1)),
|
||||||
|
Pt2D::from_gps(LonLat::new(node.lon, node.lat), gps_bounds),
|
||||||
|
),
|
||||||
|
osm_xml::Reference::Way(way) => (
|
||||||
|
tags_to_map(&way.tags)
|
||||||
|
.get("name")
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| format!("stop #{}", platforms.len() + 1)),
|
||||||
|
if let Some(ref pts) = id_to_way.get(&way.id) {
|
||||||
|
Pt2D::center(pts)
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
platforms.insert(platform_name, pt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for stop in &mut stops {
|
||||||
if directions.len() == 2 {
|
if let Some(pt) = platforms.remove(&stop.name) {
|
||||||
Some(RawBusRoute {
|
stop.ped_pos = Some(pt);
|
||||||
name: route_name,
|
}
|
||||||
is_bus,
|
|
||||||
osm_rel_id: master_rel.id,
|
|
||||||
// The direction is arbitrary right now
|
|
||||||
fwd_stops: directions.pop().unwrap(),
|
|
||||||
back_stops: directions.pop().unwrap(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
println!(
|
|
||||||
"Skipping route {} with {} sub-routes (only handling 2): {}",
|
|
||||||
route_name,
|
|
||||||
directions.len(),
|
|
||||||
rel_url(master_rel.id),
|
|
||||||
);
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
if stops.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(RawBusRoute {
|
||||||
|
full_name,
|
||||||
|
short_name,
|
||||||
|
is_bus,
|
||||||
|
osm_rel_id: rel.id,
|
||||||
|
stops,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Work around osm_xml's API, which shows the node/way/relation distinction twice. This returns
|
// Work around osm_xml's API, which shows the node/way/relation distinction twice. This returns
|
||||||
|
@ -175,7 +175,7 @@ impl CommonState {
|
|||||||
if let Some(r) = app.primary.sim.bus_route_id(c) {
|
if let Some(r) = app.primary.sim.bus_route_id(c) {
|
||||||
osd.append_all(vec![
|
osd.append_all(vec![
|
||||||
Line(" serving "),
|
Line(" serving "),
|
||||||
Line(&map.get_br(r).name).fg(name_color),
|
Line(&map.get_br(r).full_name).fg(name_color),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -200,7 +200,7 @@ impl CommonState {
|
|||||||
let routes: BTreeSet<String> = map
|
let routes: BTreeSet<String> = map
|
||||||
.get_routes_serving_stop(bs)
|
.get_routes_serving_stop(bs)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|r| r.name.clone())
|
.map(|r| r.full_name.clone())
|
||||||
.collect();
|
.collect();
|
||||||
list_names(&mut osd, |l| l.fg(name_color), routes);
|
list_names(&mut osd, |l| l.fg(name_color), routes);
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,12 @@ pub fn stop(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BusStopID)
|
|||||||
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);
|
let buses = app.primary.sim.status_of_buses(r.id);
|
||||||
if buses.is_empty() {
|
if buses.is_empty() {
|
||||||
rows.push(format!("Route {}: no buses running", r.name).draw_text(ctx));
|
rows.push(format!("Route {}: no buses running", r.full_name).draw_text(ctx));
|
||||||
} else {
|
} else {
|
||||||
rows.push(Btn::text_fg(format!("Route {}", r.name)).build_def(ctx, None));
|
rows.push(Btn::text_fg(format!("Route {}", r.full_name)).build_def(ctx, None));
|
||||||
details
|
details
|
||||||
.hyperlinks
|
.hyperlinks
|
||||||
.insert(format!("Route {}", r.name), Tab::BusStatus(buses[0].0));
|
.insert(format!("Route {}", r.full_name), Tab::BusStatus(buses[0].0));
|
||||||
}
|
}
|
||||||
|
|
||||||
let arrivals: Vec<(Time, CarID)> = all_arrivals
|
let arrivals: Vec<(Time, CarID)> = all_arrivals
|
||||||
@ -110,7 +110,7 @@ fn bus_header(
|
|||||||
Line(format!(
|
Line(format!(
|
||||||
"{} (route {})",
|
"{} (route {})",
|
||||||
id,
|
id,
|
||||||
app.primary.map.get_br(route).name
|
app.primary.map.get_br(route).full_name
|
||||||
))
|
))
|
||||||
.small_heading()
|
.small_heading()
|
||||||
.draw(ctx),
|
.draw(ctx),
|
||||||
@ -138,12 +138,8 @@ fn delays_over_time(ctx: &mut EventCtx, app: &App, id: BusRouteID) -> Widget {
|
|||||||
.bus_arrivals_over_time(app.primary.sim.time(), id);
|
.bus_arrivals_over_time(app.primary.sim.time(), id);
|
||||||
|
|
||||||
let mut series = Vec::new();
|
let mut series = Vec::new();
|
||||||
for idx1 in 0..route.stops.len() {
|
for idx1 in 0..route.stops.len() - 1 {
|
||||||
let idx2 = if idx1 == route.stops.len() - 1 {
|
let idx2 = idx1 + 1;
|
||||||
0
|
|
||||||
} else {
|
|
||||||
idx1 + 1
|
|
||||||
};
|
|
||||||
series.push(Series {
|
series.push(Series {
|
||||||
label: format!("Stop {}->{}", idx1 + 1, idx2 + 1),
|
label: format!("Stop {}->{}", idx1 + 1, idx2 + 1),
|
||||||
color: app.cs.rotating_color_plot(idx1),
|
color: app.cs.rotating_color_plot(idx1),
|
||||||
@ -169,6 +165,7 @@ fn passenger_delay(ctx: &mut EventCtx, app: &App, details: &mut Details, id: Bus
|
|||||||
.get_analytics()
|
.get_analytics()
|
||||||
.bus_passenger_delays(app.primary.sim.time(), id)
|
.bus_passenger_delays(app.primary.sim.time(), id)
|
||||||
.collect::<BTreeMap<_, _>>();
|
.collect::<BTreeMap<_, _>>();
|
||||||
|
// TODO I smell an off by one
|
||||||
for idx in 0..route.stops.len() {
|
for idx in 0..route.stops.len() {
|
||||||
col.push(Widget::row(vec![
|
col.push(Widget::row(vec![
|
||||||
format!("Stop {}", idx + 1).draw_text(ctx),
|
format!("Stop {}", idx + 1).draw_text(ctx),
|
||||||
@ -202,11 +199,7 @@ fn passenger_delay(ctx: &mut EventCtx, app: &App, details: &mut Details, id: Bus
|
|||||||
for (_, stop_idx, percent_next_stop) in app.primary.sim.status_of_buses(route.id) {
|
for (_, stop_idx, percent_next_stop) in app.primary.sim.status_of_buses(route.id) {
|
||||||
// TODO Line it up right in the middle of the line of text. This is probably a bit
|
// TODO Line it up right in the middle of the line of text. This is probably a bit
|
||||||
// wrong.
|
// wrong.
|
||||||
let base_percent_y = if stop_idx == route.stops.len() - 1 {
|
let base_percent_y = (stop_idx as f64) / ((route.stops.len() - 1) as f64);
|
||||||
0.0
|
|
||||||
} else {
|
|
||||||
(stop_idx as f64) / ((route.stops.len() - 1) as f64)
|
|
||||||
};
|
|
||||||
batch.push(
|
batch.push(
|
||||||
Color::BLUE,
|
Color::BLUE,
|
||||||
Circle::new(
|
Circle::new(
|
||||||
|
@ -192,22 +192,11 @@ impl ShowBusRoute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut colorer = ColorDiscrete::new(app, vec![("route", app.cs.unzoomed_bus)]);
|
let mut colorer = ColorDiscrete::new(app, vec![("route", app.cs.unzoomed_bus)]);
|
||||||
for (stop1, stop2) in
|
for pair in route.stops.windows(2) {
|
||||||
route
|
|
||||||
.stops
|
|
||||||
.iter()
|
|
||||||
.zip(route.stops.iter().skip(1))
|
|
||||||
.chain(std::iter::once((
|
|
||||||
route.stops.last().unwrap(),
|
|
||||||
&route.stops[0],
|
|
||||||
)))
|
|
||||||
{
|
|
||||||
let bs1 = map.get_bs(*stop1);
|
|
||||||
let bs2 = map.get_bs(*stop2);
|
|
||||||
for step in map
|
for step in map
|
||||||
.pathfind(PathRequest {
|
.pathfind(PathRequest {
|
||||||
start: bs1.driving_pos,
|
start: map.get_bs(pair[0]).driving_pos,
|
||||||
end: bs2.driving_pos,
|
end: map.get_bs(pair[1]).driving_pos,
|
||||||
constraints: route.route_type,
|
constraints: route.route_type,
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -237,7 +226,7 @@ impl ShowBusRoute {
|
|||||||
composite: Composite::new(Widget::col(vec![
|
composite: Composite::new(Widget::col(vec![
|
||||||
Widget::row(vec![
|
Widget::row(vec![
|
||||||
Widget::draw_svg(ctx, "system/assets/tools/layers.svg"),
|
Widget::draw_svg(ctx, "system/assets/tools/layers.svg"),
|
||||||
Line(&route.name).draw(ctx),
|
Line(&route.full_name).draw(ctx),
|
||||||
Btn::plaintext("X")
|
Btn::plaintext("X")
|
||||||
.build(ctx, "close", hotkey(Key::Escape))
|
.build(ctx, "close", hotkey(Key::Escape))
|
||||||
.align_right(),
|
.align_right(),
|
||||||
|
@ -72,7 +72,7 @@ impl BusRoutes {
|
|||||||
.map
|
.map
|
||||||
.all_bus_routes()
|
.all_bus_routes()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| r.name.clone())
|
.map(|r| r.full_name.clone())
|
||||||
.collect();
|
.collect();
|
||||||
// TODO Sort first by length, then lexicographically
|
// TODO Sort first by length, then lexicographically
|
||||||
routes.sort();
|
routes.sort();
|
||||||
@ -106,7 +106,7 @@ impl State for BusRoutes {
|
|||||||
if buses.is_empty() {
|
if buses.is_empty() {
|
||||||
Transition::Push(msg(
|
Transition::Push(msg(
|
||||||
"No buses running",
|
"No buses running",
|
||||||
vec![format!("Sorry, no buses for route {} running", r.name)],
|
vec![format!("Sorry, no buses for route {} running", r.full_name)],
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Transition::PopWithData(Box::new(move |state, ctx, app| {
|
Transition::PopWithData(Box::new(move |state, ctx, app| {
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
mod bridges;
|
mod bridges;
|
||||||
mod buildings;
|
mod buildings;
|
||||||
mod bus_stops;
|
|
||||||
pub mod initial;
|
pub mod initial;
|
||||||
mod remove_disconnected;
|
mod remove_disconnected;
|
||||||
pub mod traffic_signals;
|
pub mod traffic_signals;
|
||||||
|
mod transit;
|
||||||
pub mod turns;
|
pub mod turns;
|
||||||
|
|
||||||
use crate::pathfind::Pathfinder;
|
use crate::pathfind::Pathfinder;
|
||||||
use crate::raw::{OriginalIntersection, OriginalRoad, RawMap};
|
use crate::raw::{OriginalIntersection, OriginalRoad, RawMap};
|
||||||
use crate::{
|
use crate::{
|
||||||
connectivity, osm, Area, AreaID, BusRouteID, ControlStopSign, ControlTrafficSignal,
|
connectivity, osm, Area, AreaID, ControlStopSign, ControlTrafficSignal, Intersection,
|
||||||
Intersection, IntersectionID, IntersectionType, Lane, LaneID, Map, MapEdits, PathConstraints,
|
IntersectionID, IntersectionType, Lane, LaneID, Map, MapEdits, PathConstraints, Position, Road,
|
||||||
Position, Road, RoadID, Zone,
|
RoadID, Zone,
|
||||||
};
|
};
|
||||||
use abstutil::Timer;
|
use abstutil::Timer;
|
||||||
use enumset::EnumSet;
|
use enumset::EnumSet;
|
||||||
@ -320,44 +320,9 @@ impl Map {
|
|||||||
map.pathfinder = Some(Pathfinder::new_without_transit(&map, timer));
|
map.pathfinder = Some(Pathfinder::new_without_transit(&map, timer));
|
||||||
timer.stop("setup (most of) Pathfinder");
|
timer.stop("setup (most of) Pathfinder");
|
||||||
|
|
||||||
{
|
transit::make_stops_and_routes(&mut map, &raw.bus_routes, timer);
|
||||||
// Turn the two directions of each route into one loop. Need to do something better
|
for id in map.bus_stops.keys() {
|
||||||
// with borders later.
|
assert!(!map.get_routes_serving_stop(*id).is_empty());
|
||||||
for r in &mut raw.bus_routes {
|
|
||||||
r.fwd_stops.extend(r.back_stops.drain(..));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (stops, routes) = bus_stops::make_bus_stops(&mut map, &raw.bus_routes, timer);
|
|
||||||
map.bus_stops = stops;
|
|
||||||
|
|
||||||
timer.start_iter("verify bus routes are connected", routes.len());
|
|
||||||
for mut r in routes {
|
|
||||||
timer.next();
|
|
||||||
if r.stops.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if bus_stops::fix_bus_route(&map, &mut r) {
|
|
||||||
r.id = BusRouteID(map.bus_routes.len());
|
|
||||||
map.bus_routes.push(r);
|
|
||||||
} else {
|
|
||||||
timer.warn(format!(
|
|
||||||
"Skipping route {} due to connectivity problems",
|
|
||||||
r.name
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove orphaned bus stops. This messes up the BusStopID indexing.
|
|
||||||
for id in map
|
|
||||||
.bus_stops
|
|
||||||
.keys()
|
|
||||||
.filter(|id| map.get_routes_serving_stop(**id).is_empty())
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
{
|
|
||||||
map.bus_stops.remove(&id);
|
|
||||||
map.lanes[id.sidewalk.0].bus_stops.remove(&id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
timer.start("setup rest of Pathfinder (walking with transit)");
|
timer.start("setup rest of Pathfinder (walking with transit)");
|
||||||
|
@ -6,6 +6,101 @@ use crate::{
|
|||||||
use abstutil::Timer;
|
use abstutil::Timer;
|
||||||
use geom::{Distance, HashablePt2D};
|
use geom::{Distance, HashablePt2D};
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
pub fn make_stops_and_routes(map: &mut Map, raw_routes: &Vec<RawBusRoute>, timer: &mut Timer) {
|
||||||
|
timer.start("make transit stops and routes");
|
||||||
|
let matcher = Matcher::new(raw_routes, map, timer);
|
||||||
|
|
||||||
|
// TODO I'm assuming the vehicle_pos <-> driving_pos relation is one-to-one...
|
||||||
|
let mut pt_to_stop: BTreeMap<(Position, Position), BusStopID> = BTreeMap::new();
|
||||||
|
for r in raw_routes {
|
||||||
|
let mut stops = Vec::new();
|
||||||
|
let mut ok = true;
|
||||||
|
for stop in &r.stops {
|
||||||
|
if let Some((sidewalk_pos, driving_pos)) = matcher.lookup(r.is_bus, stop, map) {
|
||||||
|
// Create a new bus stop if needed.
|
||||||
|
let stop_id = if let Some(id) = pt_to_stop.get(&(sidewalk_pos, driving_pos)) {
|
||||||
|
*id
|
||||||
|
} else {
|
||||||
|
let id = BusStopID {
|
||||||
|
sidewalk: sidewalk_pos.lane(),
|
||||||
|
idx: map.get_l(sidewalk_pos.lane()).bus_stops.len(),
|
||||||
|
};
|
||||||
|
pt_to_stop.insert((sidewalk_pos, driving_pos), id);
|
||||||
|
map.lanes[sidewalk_pos.lane().0].bus_stops.insert(id);
|
||||||
|
map.bus_stops.insert(
|
||||||
|
id,
|
||||||
|
BusStop {
|
||||||
|
id,
|
||||||
|
name: stop.name.clone(),
|
||||||
|
driving_pos,
|
||||||
|
sidewalk_pos,
|
||||||
|
is_train_stop: !r.is_bus,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
id
|
||||||
|
};
|
||||||
|
stops.push(stop_id);
|
||||||
|
} else {
|
||||||
|
timer.warn(format!(
|
||||||
|
"Couldn't match stop {} for route {} ({})",
|
||||||
|
stop.name,
|
||||||
|
r.full_name,
|
||||||
|
rel_url(r.osm_rel_id)
|
||||||
|
));
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the stops are connected
|
||||||
|
let route_type = if r.is_bus {
|
||||||
|
PathConstraints::Bus
|
||||||
|
} else {
|
||||||
|
PathConstraints::Train
|
||||||
|
};
|
||||||
|
let mut ok = true;
|
||||||
|
for pair in stops.windows(2) {
|
||||||
|
if let Err(err) = check_stops(route_type, pair[0], pair[1], map) {
|
||||||
|
timer.warn(format!(
|
||||||
|
"Route {} ({}) disconnected: {}",
|
||||||
|
r.full_name,
|
||||||
|
rel_url(r.osm_rel_id),
|
||||||
|
err
|
||||||
|
));
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
map.bus_routes.push(BusRoute {
|
||||||
|
id: BusRouteID(map.bus_routes.len()),
|
||||||
|
full_name: r.full_name.clone(),
|
||||||
|
short_name: r.short_name.clone(),
|
||||||
|
stops,
|
||||||
|
route_type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove orphaned bus stops. This messes up the BusStopID indexing.
|
||||||
|
for id in map
|
||||||
|
.bus_stops
|
||||||
|
.keys()
|
||||||
|
.filter(|id| map.get_routes_serving_stop(**id).is_empty())
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
map.bus_stops.remove(&id);
|
||||||
|
map.lanes[id.sidewalk.0].bus_stops.remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer.stop("make transit stops and routes");
|
||||||
|
}
|
||||||
|
|
||||||
struct Matcher {
|
struct Matcher {
|
||||||
sidewalk_pts: HashMap<HashablePt2D, Position>,
|
sidewalk_pts: HashMap<HashablePt2D, Position>,
|
||||||
@ -14,13 +109,13 @@ struct Matcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Matcher {
|
impl Matcher {
|
||||||
fn new(bus_routes: &Vec<RawBusRoute>, map: &Map, timer: &mut Timer) -> Matcher {
|
fn new(routes: &Vec<RawBusRoute>, map: &Map, timer: &mut Timer) -> Matcher {
|
||||||
// Match all of the points to an exact position along a lane.
|
// Match all of the points to an exact position along a lane.
|
||||||
let mut lookup_sidewalk_pts = HashSet::new();
|
let mut lookup_sidewalk_pts = HashSet::new();
|
||||||
let mut lookup_bus_pts = HashSet::new();
|
let mut lookup_bus_pts = HashSet::new();
|
||||||
let mut lookup_light_rail_pts = HashSet::new();
|
let mut lookup_light_rail_pts = HashSet::new();
|
||||||
for r in bus_routes {
|
for r in routes {
|
||||||
for stop in &r.fwd_stops {
|
for stop in &r.stops {
|
||||||
if r.is_bus {
|
if r.is_bus {
|
||||||
lookup_bus_pts.insert(stop.vehicle_pos.to_hashable());
|
lookup_bus_pts.insert(stop.vehicle_pos.to_hashable());
|
||||||
} else {
|
} else {
|
||||||
@ -107,114 +202,40 @@ impl Matcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_bus_stops(
|
|
||||||
map: &mut Map,
|
|
||||||
bus_routes: &Vec<RawBusRoute>,
|
|
||||||
timer: &mut Timer,
|
|
||||||
) -> (BTreeMap<BusStopID, BusStop>, Vec<BusRoute>) {
|
|
||||||
timer.start("make bus stops");
|
|
||||||
let matcher = Matcher::new(bus_routes, map, timer);
|
|
||||||
|
|
||||||
// TODO I'm assuming the vehicle_pos <-> driving_pos relation is one-to-one...
|
|
||||||
let mut pt_to_stop: BTreeMap<(Position, Position), BusStopID> = BTreeMap::new();
|
|
||||||
let mut bus_stops: BTreeMap<BusStopID, BusStop> = BTreeMap::new();
|
|
||||||
let mut routes: Vec<BusRoute> = Vec::new();
|
|
||||||
|
|
||||||
for r in bus_routes {
|
|
||||||
let mut stops = Vec::new();
|
|
||||||
for stop in &r.fwd_stops {
|
|
||||||
if let Some((sidewalk_pos, driving_pos)) = matcher.lookup(r.is_bus, stop, map) {
|
|
||||||
// Create a new bus stop if needed.
|
|
||||||
let stop_id = if let Some(id) = pt_to_stop.get(&(sidewalk_pos, driving_pos)) {
|
|
||||||
*id
|
|
||||||
} else {
|
|
||||||
let id = BusStopID {
|
|
||||||
sidewalk: sidewalk_pos.lane(),
|
|
||||||
idx: map.get_l(sidewalk_pos.lane()).bus_stops.len(),
|
|
||||||
};
|
|
||||||
pt_to_stop.insert((sidewalk_pos, driving_pos), id);
|
|
||||||
map.lanes[sidewalk_pos.lane().0].bus_stops.insert(id);
|
|
||||||
bus_stops.insert(
|
|
||||||
id,
|
|
||||||
BusStop {
|
|
||||||
id,
|
|
||||||
name: stop.name.clone(),
|
|
||||||
driving_pos,
|
|
||||||
sidewalk_pos,
|
|
||||||
is_train_stop: !r.is_bus,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
id
|
|
||||||
};
|
|
||||||
stops.push(stop_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
routes.push(BusRoute {
|
|
||||||
id: BusRouteID(routes.len()),
|
|
||||||
name: r.name.clone(),
|
|
||||||
stops,
|
|
||||||
route_type: if r.is_bus {
|
|
||||||
PathConstraints::Bus
|
|
||||||
} else {
|
|
||||||
PathConstraints::Train
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
timer.stop("make bus stops");
|
|
||||||
(bus_stops, routes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim out stops if needed; map borders sometimes mean some paths don't work.
|
|
||||||
pub fn fix_bus_route(map: &Map, r: &mut BusRoute) -> bool {
|
|
||||||
let mut stops = Vec::new();
|
|
||||||
for stop in r.stops.drain(..) {
|
|
||||||
if stops.is_empty() {
|
|
||||||
stops.push(stop);
|
|
||||||
} else {
|
|
||||||
if check_stops(&r.name, r.route_type, *stops.last().unwrap(), stop, map) {
|
|
||||||
stops.push(stop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't forget the last and first -- except temporarily for light rail!
|
|
||||||
if r.route_type == PathConstraints::Bus {
|
|
||||||
while stops.len() >= 2 {
|
|
||||||
if check_stops(&r.name, r.route_type, *stops.last().unwrap(), stops[0], map) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// TODO Or the front one
|
|
||||||
stops.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r.stops = stops;
|
|
||||||
r.stops.len() >= 2
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_stops(
|
fn check_stops(
|
||||||
route: &str,
|
|
||||||
constraints: PathConstraints,
|
constraints: PathConstraints,
|
||||||
stop1: BusStopID,
|
stop1: BusStopID,
|
||||||
stop2: BusStopID,
|
stop2: BusStopID,
|
||||||
map: &Map,
|
map: &Map,
|
||||||
) -> bool {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
let start = map.get_bs(stop1).driving_pos;
|
let start = map.get_bs(stop1).driving_pos;
|
||||||
let end = map.get_bs(stop2).driving_pos;
|
let end = map.get_bs(stop2).driving_pos;
|
||||||
if start.lane() == end.lane() && start.dist_along() > end.dist_along() {
|
if start.lane() == end.lane() && start.dist_along() > end.dist_along() {
|
||||||
println!(
|
return Err(format!(
|
||||||
"Route {} has two bus stops seemingly out of order somewhere on {}",
|
"Two stops seemingly out of order somewhere on {}",
|
||||||
route,
|
|
||||||
map.get_parent(start.lane()).orig_id
|
map.get_parent(start.lane()).orig_id
|
||||||
);
|
)
|
||||||
return false;
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
map.pathfind(PathRequest {
|
if map
|
||||||
start,
|
.pathfind(PathRequest {
|
||||||
end,
|
start,
|
||||||
constraints,
|
end,
|
||||||
})
|
constraints,
|
||||||
.is_some()
|
})
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(format!(
|
||||||
|
"No path between stop on {} and {}",
|
||||||
|
map.get_parent(start.lane()).orig_id,
|
||||||
|
map.get_parent(end.lane()).orig_id
|
||||||
|
)
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rel_url(id: i64) -> String {
|
||||||
|
format!("https://www.openstreetmap.org/relation/{}", id)
|
||||||
}
|
}
|
@ -400,7 +400,7 @@ impl Map {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_bus_route(&self, name: &str) -> Option<&BusRoute> {
|
pub fn get_bus_route(&self, name: &str) -> Option<&BusRoute> {
|
||||||
self.bus_routes.iter().find(|r| r.name == name)
|
self.bus_routes.iter().find(|r| r.full_name == name)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_routes_serving_stop(&self, stop: BusStopID) -> Vec<&BusRoute> {
|
pub fn get_routes_serving_stop(&self, stop: BusStopID) -> Vec<&BusRoute> {
|
||||||
|
@ -46,7 +46,8 @@ pub struct BusStop {
|
|||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct BusRoute {
|
pub struct BusRoute {
|
||||||
pub id: BusRouteID,
|
pub id: BusRouteID,
|
||||||
pub name: String,
|
pub full_name: String,
|
||||||
|
pub short_name: String,
|
||||||
pub stops: Vec<BusStopID>,
|
pub stops: Vec<BusStopID>,
|
||||||
pub route_type: PathConstraints,
|
pub route_type: PathConstraints,
|
||||||
}
|
}
|
||||||
|
@ -146,6 +146,15 @@ impl Path {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn one_step(l: LaneID, map: &Map) -> Path {
|
||||||
|
Path::new(
|
||||||
|
map,
|
||||||
|
vec![PathStep::Lane(l)],
|
||||||
|
map.get_l(l).length(),
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Only used for weird serialization magic.
|
// Only used for weird serialization magic.
|
||||||
pub fn dummy() -> Path {
|
pub fn dummy() -> Path {
|
||||||
Path {
|
Path {
|
||||||
@ -596,7 +605,7 @@ impl Pathfinder {
|
|||||||
timer.stop("prepare pathfinding for trains");
|
timer.stop("prepare pathfinding for trains");
|
||||||
|
|
||||||
timer.start("prepare pathfinding for pedestrians");
|
timer.start("prepare pathfinding for pedestrians");
|
||||||
let walking_graph = SidewalkPathfinder::new(map, false, &bus_graph);
|
let walking_graph = SidewalkPathfinder::new(map, false, &bus_graph, &train_graph);
|
||||||
timer.stop("prepare pathfinding for pedestrians");
|
timer.stop("prepare pathfinding for pedestrians");
|
||||||
|
|
||||||
Pathfinder {
|
Pathfinder {
|
||||||
@ -610,7 +619,12 @@ impl Pathfinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup_walking_with_transit(&mut self, map: &Map) {
|
pub fn setup_walking_with_transit(&mut self, map: &Map) {
|
||||||
self.walking_with_transit_graph = Some(SidewalkPathfinder::new(map, true, &self.bus_graph));
|
self.walking_with_transit_graph = Some(SidewalkPathfinder::new(
|
||||||
|
map,
|
||||||
|
true,
|
||||||
|
&self.bus_graph,
|
||||||
|
&self.train_graph,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pathfind(&self, req: PathRequest, map: &Map) -> Option<Path> {
|
pub fn pathfind(&self, req: PathRequest, map: &Map) -> Option<Path> {
|
||||||
@ -883,14 +897,15 @@ impl Pathfinder {
|
|||||||
// Can't edit anything related to trains
|
// Can't edit anything related to trains
|
||||||
|
|
||||||
timer.start("apply edits to pedestrian pathfinding");
|
timer.start("apply edits to pedestrian pathfinding");
|
||||||
self.walking_graph.apply_edits(map, &self.bus_graph);
|
self.walking_graph
|
||||||
|
.apply_edits(map, &self.bus_graph, &self.train_graph);
|
||||||
timer.stop("apply edits to pedestrian pathfinding");
|
timer.stop("apply edits to pedestrian pathfinding");
|
||||||
|
|
||||||
timer.start("apply edits to pedestrian using transit pathfinding");
|
timer.start("apply edits to pedestrian using transit pathfinding");
|
||||||
self.walking_with_transit_graph
|
self.walking_with_transit_graph
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.apply_edits(map, &self.bus_graph);
|
.apply_edits(map, &self.bus_graph, &self.train_graph);
|
||||||
timer.stop("apply edits to pedestrian using transit pathfinding");
|
timer.stop("apply edits to pedestrian using transit pathfinding");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,12 @@ impl WalkingNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SidewalkPathfinder {
|
impl SidewalkPathfinder {
|
||||||
pub fn new(map: &Map, use_transit: bool, bus_graph: &VehiclePathfinder) -> SidewalkPathfinder {
|
pub fn new(
|
||||||
|
map: &Map,
|
||||||
|
use_transit: bool,
|
||||||
|
bus_graph: &VehiclePathfinder,
|
||||||
|
train_graph: &VehiclePathfinder,
|
||||||
|
) -> SidewalkPathfinder {
|
||||||
let mut nodes = NodeMap::new();
|
let mut nodes = NodeMap::new();
|
||||||
// We're assuming that to start with, no sidewalks are closed for construction!
|
// We're assuming that to start with, no sidewalks are closed for construction!
|
||||||
for l in map.all_lanes() {
|
for l in map.all_lanes() {
|
||||||
@ -52,7 +57,13 @@ impl SidewalkPathfinder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let graph = fast_paths::prepare(&make_input_graph(map, &nodes, use_transit, bus_graph));
|
let graph = fast_paths::prepare(&make_input_graph(
|
||||||
|
map,
|
||||||
|
&nodes,
|
||||||
|
use_transit,
|
||||||
|
bus_graph,
|
||||||
|
train_graph,
|
||||||
|
));
|
||||||
SidewalkPathfinder {
|
SidewalkPathfinder {
|
||||||
graph,
|
graph,
|
||||||
nodes,
|
nodes,
|
||||||
@ -61,10 +72,16 @@ impl SidewalkPathfinder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_edits(&mut self, map: &Map, bus_graph: &VehiclePathfinder) {
|
pub fn apply_edits(
|
||||||
|
&mut self,
|
||||||
|
map: &Map,
|
||||||
|
bus_graph: &VehiclePathfinder,
|
||||||
|
train_graph: &VehiclePathfinder,
|
||||||
|
) {
|
||||||
// The NodeMap is all sidewalks and bus stops -- it won't change. So we can also reuse the
|
// The NodeMap is all sidewalks and bus stops -- it won't change. So we can also reuse the
|
||||||
// node ordering.
|
// node ordering.
|
||||||
let input_graph = make_input_graph(map, &self.nodes, self.use_transit, bus_graph);
|
let input_graph =
|
||||||
|
make_input_graph(map, &self.nodes, self.use_transit, bus_graph, train_graph);
|
||||||
let node_ordering = self.graph.get_node_ordering();
|
let node_ordering = self.graph.get_node_ordering();
|
||||||
self.graph = fast_paths::prepare_with_order(&input_graph, &node_ordering).unwrap();
|
self.graph = fast_paths::prepare_with_order(&input_graph, &node_ordering).unwrap();
|
||||||
}
|
}
|
||||||
@ -126,6 +143,7 @@ fn make_input_graph(
|
|||||||
nodes: &NodeMap<WalkingNode>,
|
nodes: &NodeMap<WalkingNode>,
|
||||||
use_transit: bool,
|
use_transit: bool,
|
||||||
bus_graph: &VehiclePathfinder,
|
bus_graph: &VehiclePathfinder,
|
||||||
|
train_graph: &VehiclePathfinder,
|
||||||
) -> InputGraph {
|
) -> InputGraph {
|
||||||
let mut input_graph = InputGraph::new();
|
let mut input_graph = InputGraph::new();
|
||||||
|
|
||||||
@ -181,38 +199,30 @@ fn make_input_graph(
|
|||||||
// Connect each adjacent stop along a route, with the cost based on how long it'll take a
|
// Connect each adjacent stop along a route, with the cost based on how long it'll take a
|
||||||
// bus to drive between the stops. Optimistically assume no waiting time at a stop.
|
// bus to drive between the stops. Optimistically assume no waiting time at a stop.
|
||||||
for route in map.all_bus_routes() {
|
for route in map.all_bus_routes() {
|
||||||
// TODO Gotta connect the stops properly first
|
for pair in route.stops.windows(2) {
|
||||||
if route.route_type == PathConstraints::Train {
|
let (stop1, stop2) = (map.get_bs(pair[0]), map.get_bs(pair[1]));
|
||||||
continue;
|
let graph = match route.route_type {
|
||||||
}
|
PathConstraints::Bus => bus_graph,
|
||||||
for (stop1, stop2) in
|
PathConstraints::Train => train_graph,
|
||||||
route
|
_ => unreachable!(),
|
||||||
.stops
|
};
|
||||||
.iter()
|
if let Some((_, driving_cost)) = graph.pathfind(
|
||||||
.zip(route.stops.iter().skip(1))
|
|
||||||
.chain(std::iter::once((
|
|
||||||
route.stops.last().unwrap(),
|
|
||||||
&route.stops[0],
|
|
||||||
)))
|
|
||||||
{
|
|
||||||
if let Some((_, driving_cost)) = bus_graph.pathfind(
|
|
||||||
&PathRequest {
|
&PathRequest {
|
||||||
start: map.get_bs(*stop1).driving_pos,
|
start: stop1.driving_pos,
|
||||||
end: map.get_bs(*stop2).driving_pos,
|
end: stop2.driving_pos,
|
||||||
constraints: PathConstraints::Bus,
|
constraints: route.route_type,
|
||||||
},
|
},
|
||||||
map,
|
map,
|
||||||
) {
|
) {
|
||||||
input_graph.add_edge(
|
input_graph.add_edge(
|
||||||
nodes.get(WalkingNode::RideBus(*stop1)),
|
nodes.get(WalkingNode::RideBus(stop1.id)),
|
||||||
nodes.get(WalkingNode::RideBus(*stop2)),
|
nodes.get(WalkingNode::RideBus(stop2.id)),
|
||||||
driving_cost,
|
driving_cost,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!(
|
panic!(
|
||||||
"No bus route from {} to {} now! Prevent this edit",
|
"No bus route from {} to {} now for {}! Prevent this edit",
|
||||||
map.get_bs(*stop1).driving_pos,
|
stop1.driving_pos, stop2.driving_pos, route.full_name,
|
||||||
map.get_bs(*stop2).driving_pos
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -429,12 +429,12 @@ impl DrivingSide {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct RawBusRoute {
|
pub struct RawBusRoute {
|
||||||
pub name: String,
|
pub full_name: String,
|
||||||
|
pub short_name: String,
|
||||||
pub osm_rel_id: i64,
|
pub osm_rel_id: i64,
|
||||||
// If not, light rail
|
// If not, light rail
|
||||||
pub is_bus: bool,
|
pub is_bus: bool,
|
||||||
pub fwd_stops: Vec<RawBusStop>,
|
pub stops: Vec<RawBusStop>,
|
||||||
pub back_stops: Vec<RawBusStop>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
@ -78,8 +78,10 @@ impl TripPhaseType {
|
|||||||
TripPhaseType::Walking => "walking".to_string(),
|
TripPhaseType::Walking => "walking".to_string(),
|
||||||
TripPhaseType::Biking => "biking".to_string(),
|
TripPhaseType::Biking => "biking".to_string(),
|
||||||
TripPhaseType::Parking => "parking".to_string(),
|
TripPhaseType::Parking => "parking".to_string(),
|
||||||
TripPhaseType::WaitingForBus(r, _) => format!("waiting for bus {}", map.get_br(r).name),
|
TripPhaseType::WaitingForBus(r, _) => {
|
||||||
TripPhaseType::RidingBus(r, _, _) => format!("riding bus {}", map.get_br(r).name),
|
format!("waiting for bus {}", map.get_br(r).full_name)
|
||||||
|
}
|
||||||
|
TripPhaseType::RidingBus(r, _, _) => format!("riding bus {}", map.get_br(r).full_name),
|
||||||
TripPhaseType::Aborted => "trip aborted due to some bug".to_string(),
|
TripPhaseType::Aborted => "trip aborted due to some bug".to_string(),
|
||||||
TripPhaseType::Finished => "trip finished".to_string(),
|
TripPhaseType::Finished => "trip finished".to_string(),
|
||||||
TripPhaseType::DelayedStart => "delayed by previous trip taking too long".to_string(),
|
TripPhaseType::DelayedStart => "delayed by previous trip taking too long".to_string(),
|
||||||
|
@ -96,7 +96,7 @@ impl Scenario {
|
|||||||
|
|
||||||
if let Some(ref routes) = self.only_seed_buses {
|
if let Some(ref routes) = self.only_seed_buses {
|
||||||
for route in map.all_bus_routes() {
|
for route in map.all_bus_routes() {
|
||||||
if routes.contains(&route.name) {
|
if routes.contains(&route.full_name) {
|
||||||
sim.seed_bus_route(route, map, timer);
|
sim.seed_bus_route(route, map, timer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ impl Car {
|
|||||||
CarState::Unparking(_, _, _) => CarStatus::Parked,
|
CarState::Unparking(_, _, _) => CarStatus::Parked,
|
||||||
CarState::Parking(_, _, _) => CarStatus::Parked,
|
CarState::Parking(_, _, _) => CarStatus::Parked,
|
||||||
// Changing color for idling buses is helpful
|
// Changing color for idling buses is helpful
|
||||||
CarState::Idling(_, _) => CarStatus::Parked,
|
CarState::IdlingAtStop(_, _) => CarStatus::Parked,
|
||||||
},
|
},
|
||||||
on: self.router.head(),
|
on: self.router.head(),
|
||||||
partly_on,
|
partly_on,
|
||||||
@ -223,8 +223,8 @@ impl Car {
|
|||||||
{
|
{
|
||||||
Some(
|
Some(
|
||||||
map.get_br(transit.bus_route(self.vehicle.id))
|
map.get_br(transit.bus_route(self.vehicle.id))
|
||||||
.name
|
.short_name
|
||||||
.to_string(),
|
.clone(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -249,7 +249,7 @@ pub enum CarState {
|
|||||||
// Where's the front of the car while this is happening?
|
// Where's the front of the car while this is happening?
|
||||||
Unparking(Distance, ParkingSpot, TimeInterval),
|
Unparking(Distance, ParkingSpot, TimeInterval),
|
||||||
Parking(Distance, ParkingSpot, TimeInterval),
|
Parking(Distance, ParkingSpot, TimeInterval),
|
||||||
Idling(Distance, TimeInterval),
|
IdlingAtStop(Distance, TimeInterval),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CarState {
|
impl CarState {
|
||||||
@ -260,7 +260,7 @@ impl CarState {
|
|||||||
CarState::WaitingToAdvance { .. } => unreachable!(),
|
CarState::WaitingToAdvance { .. } => unreachable!(),
|
||||||
CarState::Unparking(_, _, ref time_int) => time_int.end,
|
CarState::Unparking(_, _, ref time_int) => time_int.end,
|
||||||
CarState::Parking(_, _, ref time_int) => time_int.end,
|
CarState::Parking(_, _, ref time_int) => time_int.end,
|
||||||
CarState::Idling(_, ref time_int) => time_int.end,
|
CarState::IdlingAtStop(_, ref time_int) => time_int.end,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ use crate::mechanics::Queue;
|
|||||||
use crate::{
|
use crate::{
|
||||||
ActionAtEnd, AgentID, AgentProperties, CarID, Command, CreateCar, DistanceInterval,
|
ActionAtEnd, AgentID, AgentProperties, CarID, Command, CreateCar, DistanceInterval,
|
||||||
DrawCarInput, Event, IntersectionSimState, ParkedCar, ParkingSimState, ParkingSpot, PersonID,
|
DrawCarInput, Event, IntersectionSimState, ParkedCar, ParkingSimState, ParkingSpot, PersonID,
|
||||||
Scheduler, TimeInterval, TransitSimState, TripManager, UnzoomedAgent, Vehicle, VehicleType,
|
Scheduler, TimeInterval, TransitSimState, TripManager, UnzoomedAgent, Vehicle, WalkingSimState,
|
||||||
WalkingSimState, FOLLOWING_DISTANCE,
|
FOLLOWING_DISTANCE,
|
||||||
};
|
};
|
||||||
use abstutil::{deserialize_btreemap, serialize_btreemap};
|
use abstutil::{deserialize_btreemap, serialize_btreemap};
|
||||||
use geom::{Distance, Duration, PolyLine, Time};
|
use geom::{Distance, Duration, PolyLine, Time};
|
||||||
@ -171,7 +171,7 @@ impl DrivingSimState {
|
|||||||
//
|
//
|
||||||
// Crossing -> Queued or WaitingToAdvance
|
// Crossing -> Queued or WaitingToAdvance
|
||||||
// Unparking -> Crossing
|
// Unparking -> Crossing
|
||||||
// Idling -> Crossing
|
// IdlingAtStop -> Crossing
|
||||||
// Queued -> last step handling (Parking or done)
|
// Queued -> last step handling (Parking or done)
|
||||||
// WaitingToAdvance -> try to advance to the next step of the path
|
// WaitingToAdvance -> try to advance to the next step of the path
|
||||||
// Parking -> done
|
// Parking -> done
|
||||||
@ -284,8 +284,8 @@ impl DrivingSimState {
|
|||||||
car.state = car.crossing_state(front, now, map);
|
car.state = car.crossing_state(front, now, map);
|
||||||
scheduler.push(car.state.get_end_time(), Command::UpdateCar(car.vehicle.id));
|
scheduler.push(car.state.get_end_time(), Command::UpdateCar(car.vehicle.id));
|
||||||
}
|
}
|
||||||
CarState::Idling(dist, _) => {
|
CarState::IdlingAtStop(dist, _) => {
|
||||||
car.router = transit.bus_departed_from_stop(car.vehicle.id);
|
car.router = transit.bus_departed_from_stop(car.vehicle.id, map);
|
||||||
self.events
|
self.events
|
||||||
.push(Event::PathAmended(car.router.get_path().clone()));
|
.push(Event::PathAmended(car.router.get_path().clone()));
|
||||||
car.state = car.crossing_state(dist, now, map);
|
car.state = car.crossing_state(dist, now, map);
|
||||||
@ -325,7 +325,7 @@ impl DrivingSimState {
|
|||||||
CarState::Crossing(_, _)
|
CarState::Crossing(_, _)
|
||||||
| CarState::Unparking(_, _, _)
|
| CarState::Unparking(_, _, _)
|
||||||
| CarState::Parking(_, _, _)
|
| CarState::Parking(_, _, _)
|
||||||
| CarState::Idling(_, _) => {}
|
| CarState::IdlingAtStop(_, _) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -429,7 +429,7 @@ impl DrivingSimState {
|
|||||||
match car.state {
|
match car.state {
|
||||||
CarState::Crossing(_, _)
|
CarState::Crossing(_, _)
|
||||||
| CarState::Unparking(_, _, _)
|
| CarState::Unparking(_, _, _)
|
||||||
| CarState::Idling(_, _)
|
| CarState::IdlingAtStop(_, _)
|
||||||
| CarState::WaitingToAdvance { .. } => unreachable!(),
|
| CarState::WaitingToAdvance { .. } => unreachable!(),
|
||||||
CarState::Queued { blocked_since } => {
|
CarState::Queued { blocked_since } => {
|
||||||
match car.router.maybe_handle_end(
|
match car.router.maybe_handle_end(
|
||||||
@ -514,15 +514,10 @@ impl DrivingSimState {
|
|||||||
map,
|
map,
|
||||||
);
|
);
|
||||||
car.total_blocked_time += now - blocked_since;
|
car.total_blocked_time += now - blocked_since;
|
||||||
// TODO Light rail routes are all disconnected. For now, just sit forever
|
car.state = CarState::IdlingAtStop(
|
||||||
// after making one stop.
|
our_dist,
|
||||||
let wait_at_stop = if car.vehicle.vehicle_type == VehicleType::Train {
|
TimeInterval::new(now, now + TIME_TO_WAIT_AT_STOP),
|
||||||
Duration::hours(24 * 30)
|
);
|
||||||
} else {
|
|
||||||
TIME_TO_WAIT_AT_STOP
|
|
||||||
};
|
|
||||||
car.state =
|
|
||||||
CarState::Idling(our_dist, TimeInterval::new(now, now + wait_at_stop));
|
|
||||||
scheduler
|
scheduler
|
||||||
.push(car.state.get_end_time(), Command::UpdateCar(car.vehicle.id));
|
.push(car.state.get_end_time(), Command::UpdateCar(car.vehicle.id));
|
||||||
true
|
true
|
||||||
@ -673,7 +668,7 @@ impl DrivingSimState {
|
|||||||
// They weren't blocked
|
// They weren't blocked
|
||||||
CarState::Unparking(_, _, _)
|
CarState::Unparking(_, _, _)
|
||||||
| CarState::Parking(_, _, _)
|
| CarState::Parking(_, _, _)
|
||||||
| CarState::Idling(_, _) => {}
|
| CarState::IdlingAtStop(_, _) => {}
|
||||||
CarState::WaitingToAdvance { .. } => unreachable!(),
|
CarState::WaitingToAdvance { .. } => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -813,7 +808,7 @@ impl DrivingSimState {
|
|||||||
CarState::Crossing(_, _)
|
CarState::Crossing(_, _)
|
||||||
| CarState::Unparking(_, _, _)
|
| CarState::Unparking(_, _, _)
|
||||||
| CarState::Parking(_, _, _)
|
| CarState::Parking(_, _, _)
|
||||||
| CarState::Idling(_, _) => {}
|
| CarState::IdlingAtStop(_, _) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -150,7 +150,7 @@ impl Queue {
|
|||||||
}
|
}
|
||||||
CarState::Unparking(front, _, _) => front,
|
CarState::Unparking(front, _, _) => front,
|
||||||
CarState::Parking(front, _, _) => front,
|
CarState::Parking(front, _, _) => front,
|
||||||
CarState::Idling(front, _) => front,
|
CarState::IdlingAtStop(front, _) => front,
|
||||||
};
|
};
|
||||||
|
|
||||||
result.push((*id, front));
|
result.push((*id, front));
|
||||||
@ -274,7 +274,7 @@ fn dump_cars(
|
|||||||
CarState::Parking(_, _, ref time_int) => {
|
CarState::Parking(_, _, ref time_int) => {
|
||||||
println!(" Parking during {} .. {}", time_int.start, time_int.end);
|
println!(" Parking during {} .. {}", time_int.start, time_int.end);
|
||||||
}
|
}
|
||||||
CarState::Idling(_, ref time_int) => {
|
CarState::IdlingAtStop(_, ref time_int) => {
|
||||||
println!(" Idling during {} .. {}", time_int.start, time_int.end);
|
println!(" Idling during {} .. {}", time_int.start, time_int.end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use geom::Distance;
|
use geom::Distance;
|
||||||
use map_model::{
|
use map_model::{
|
||||||
BuildingID, IntersectionID, Map, Path, PathConstraints, PathRequest, PathStep, Position,
|
BuildingID, IntersectionID, LaneID, Map, Path, PathConstraints, PathRequest, PathStep,
|
||||||
Traversable, TurnID, TurnType,
|
Position, Traversable, TurnID, TurnType,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@ -57,6 +57,16 @@ impl Router {
|
|||||||
goal: Goal::EndAtBorder { end_dist, i },
|
goal: Goal::EndAtBorder { end_dist, i },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn vanish_bus(l: LaneID, map: &Map) -> Router {
|
||||||
|
let lane = map.get_l(l);
|
||||||
|
Router {
|
||||||
|
path: Path::one_step(l, map),
|
||||||
|
goal: Goal::EndAtBorder {
|
||||||
|
end_dist: lane.length(),
|
||||||
|
i: lane.dst_i,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn park_near(path: Path, bldg: BuildingID) -> Router {
|
pub fn park_near(path: Path, bldg: BuildingID) -> Router {
|
||||||
Router {
|
Router {
|
||||||
|
133
sim/src/sim.rs
133
sim/src/sim.rs
@ -13,7 +13,7 @@ use geom::{Distance, Duration, PolyLine, Pt2D, Speed, Time};
|
|||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
use map_model::{
|
use map_model::{
|
||||||
BuildingID, BusRoute, BusRouteID, IntersectionID, LaneID, Map, ParkingLotID, Path,
|
BuildingID, BusRoute, BusRouteID, IntersectionID, LaneID, Map, ParkingLotID, Path,
|
||||||
PathConstraints, PathRequest, PathStep, Position, RoadID, Traversable,
|
PathConstraints, PathRequest, Position, RoadID, Traversable,
|
||||||
};
|
};
|
||||||
use rand_xorshift::XorShiftRng;
|
use rand_xorshift::XorShiftRng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -227,80 +227,65 @@ impl Sim {
|
|||||||
self.parking.add_parked_car(ParkedCar { vehicle, spot });
|
self.parking.add_parked_car(ParkedCar { vehicle, spot });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn seed_bus_route(&mut self, route: &BusRoute, map: &Map, timer: &mut Timer) -> Vec<CarID> {
|
// TODO Change this to be a periodic "start a bus at stop1 based on a schedule"
|
||||||
let mut results: Vec<CarID> = Vec::new();
|
pub(crate) fn seed_bus_route(&mut self, route: &BusRoute, map: &Map, timer: &mut Timer) {
|
||||||
|
if route.stops.is_empty() {
|
||||||
// Try to spawn just ONE bus anywhere.
|
panic!("{} has no stops", route.full_name);
|
||||||
// TODO Be more realistic. One bus per stop is too much, one is too little.
|
|
||||||
for (next_stop_idx, req, mut path, end_dist) in
|
|
||||||
self.transit.create_empty_route(route, map).into_iter()
|
|
||||||
{
|
|
||||||
// For now, no desire for randomness. Caller can pass in list of specs if that ever
|
|
||||||
// changes.
|
|
||||||
let (vehicle_type, length) = match route.route_type {
|
|
||||||
PathConstraints::Bus => (VehicleType::Bus, BUS_LENGTH),
|
|
||||||
PathConstraints::Train => (VehicleType::Train, LIGHT_RAIL_LENGTH),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let vehicle = VehicleSpec {
|
|
||||||
vehicle_type,
|
|
||||||
length,
|
|
||||||
max_speed: None,
|
|
||||||
}
|
|
||||||
.make(CarID(self.trips.new_car_id(), vehicle_type), None);
|
|
||||||
let id = vehicle.id;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
if path.is_empty() {
|
|
||||||
timer.warn(format!(
|
|
||||||
"Giving up on seeding a bus headed towards stop {} of {} ({})",
|
|
||||||
next_stop_idx, route.name, route.id
|
|
||||||
));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let start_lane = if let PathStep::Lane(l) = path.current_step() {
|
|
||||||
l
|
|
||||||
} else {
|
|
||||||
path.shift(map);
|
|
||||||
// TODO Technically should update request, but it shouldn't matter
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if map.get_l(start_lane).length() < vehicle.length {
|
|
||||||
path.shift(map);
|
|
||||||
// TODO Technically should update request, but it shouldn't matter
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bypass some layers of abstraction that don't make sense for buses.
|
|
||||||
if self.driving.start_car_on_lane(
|
|
||||||
self.time,
|
|
||||||
CreateCar {
|
|
||||||
start_dist: vehicle.length,
|
|
||||||
vehicle: vehicle.clone(),
|
|
||||||
req: req.clone(),
|
|
||||||
router: Router::follow_bus_route(path.clone(), end_dist),
|
|
||||||
maybe_parked_car: None,
|
|
||||||
trip_and_person: None,
|
|
||||||
},
|
|
||||||
map,
|
|
||||||
&self.intersections,
|
|
||||||
&self.parking,
|
|
||||||
&mut self.scheduler,
|
|
||||||
) {
|
|
||||||
self.transit.bus_created(id, route.id, next_stop_idx);
|
|
||||||
self.analytics.record_demand(&path, map);
|
|
||||||
results.push(id);
|
|
||||||
return results;
|
|
||||||
} else {
|
|
||||||
path.shift(map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if results.is_empty() {
|
// TODO This'll be valid when we have borders too
|
||||||
// TODO Bigger failure
|
if route.stops.len() == 1 {
|
||||||
timer.warn(format!("Failed to make ANY buses for {}!", route.name));
|
timer.error(format!("{} only has one stop", route.full_name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn one bus from stop1->stop2.
|
||||||
|
let (req, path) = self.transit.create_empty_route(route, map);
|
||||||
|
|
||||||
|
// For now, no desire for randomness. Caller can pass in list of specs if that ever
|
||||||
|
// changes.
|
||||||
|
let (vehicle_type, length) = match route.route_type {
|
||||||
|
PathConstraints::Bus => (VehicleType::Bus, BUS_LENGTH),
|
||||||
|
PathConstraints::Train => (VehicleType::Train, LIGHT_RAIL_LENGTH),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let vehicle = VehicleSpec {
|
||||||
|
vehicle_type,
|
||||||
|
length,
|
||||||
|
max_speed: None,
|
||||||
|
}
|
||||||
|
.make(CarID(self.trips.new_car_id(), vehicle_type), None);
|
||||||
|
let id = vehicle.id;
|
||||||
|
|
||||||
|
let start = req.start.lane();
|
||||||
|
if map.get_l(start).length() < vehicle.length {
|
||||||
|
timer.error(format!("Can't start a bus on {}, too short", start));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bypass some layers of abstraction that don't make sense for buses.
|
||||||
|
if self.driving.start_car_on_lane(
|
||||||
|
self.time,
|
||||||
|
CreateCar {
|
||||||
|
start_dist: vehicle.length,
|
||||||
|
vehicle,
|
||||||
|
router: Router::follow_bus_route(path.clone(), req.end.dist_along()),
|
||||||
|
req,
|
||||||
|
maybe_parked_car: None,
|
||||||
|
trip_and_person: None,
|
||||||
|
},
|
||||||
|
map,
|
||||||
|
&self.intersections,
|
||||||
|
&self.parking,
|
||||||
|
&mut self.scheduler,
|
||||||
|
) {
|
||||||
|
self.transit.bus_created(id, route.id);
|
||||||
|
self.analytics.record_demand(&path, map);
|
||||||
|
} else {
|
||||||
|
timer.error(format!(
|
||||||
|
"Can't start a bus on {}, something's in the way",
|
||||||
|
start
|
||||||
|
));
|
||||||
}
|
}
|
||||||
results
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_name(&mut self, name: String) {
|
pub fn set_name(&mut self, name: String) {
|
||||||
@ -938,7 +923,7 @@ impl Sim {
|
|||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
"Route".to_string(),
|
"Route".to_string(),
|
||||||
map.get_br(self.transit.bus_route(car)).name.clone(),
|
map.get_br(self.transit.bus_route(car)).full_name.clone(),
|
||||||
),
|
),
|
||||||
("Passengers".to_string(), passengers.len().to_string()),
|
("Passengers".to_string(), passengers.len().to_string()),
|
||||||
]
|
]
|
||||||
|
@ -3,29 +3,25 @@ use crate::{
|
|||||||
WalkingSimState,
|
WalkingSimState,
|
||||||
};
|
};
|
||||||
use abstutil::{deserialize_btreemap, serialize_btreemap};
|
use abstutil::{deserialize_btreemap, serialize_btreemap};
|
||||||
use geom::{Distance, Time};
|
use geom::Time;
|
||||||
use map_model::{
|
use map_model::{BusRoute, BusRouteID, BusStopID, Map, Path, PathRequest, Position};
|
||||||
BusRoute, BusRouteID, BusStopID, Map, Path, PathConstraints, PathRequest, Position,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
// These index stops along a route, not stops along a single sidewalk.
|
// These index stops along a route, not stops along a single sidewalk.
|
||||||
type StopIdx = usize;
|
type StopIdx = usize;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, PartialEq, Clone)]
|
||||||
struct StopForRoute {
|
struct Stop {
|
||||||
id: BusStopID,
|
id: BusStopID,
|
||||||
driving_pos: Position,
|
driving_pos: Position,
|
||||||
req: PathRequest,
|
next_stop: Option<(PathRequest, Path)>,
|
||||||
path_to_next_stop: Path,
|
|
||||||
next_stop_idx: StopIdx,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, PartialEq, Clone)]
|
||||||
struct Route {
|
struct Route {
|
||||||
stops: Vec<StopForRoute>,
|
stops: Vec<Stop>,
|
||||||
buses: Vec<CarID>,
|
buses: BTreeSet<CarID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Clone)]
|
#[derive(Serialize, Deserialize, PartialEq, Clone)]
|
||||||
@ -76,83 +72,61 @@ impl TransitSimState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns (next stop, first path, end distance for next stop) for all of the stops in the
|
// Returns the path and end distance for stop1->stop2.
|
||||||
// route.
|
pub fn create_empty_route(&mut self, bus_route: &BusRoute, map: &Map) -> (PathRequest, Path) {
|
||||||
pub fn create_empty_route(
|
|
||||||
&mut self,
|
|
||||||
bus_route: &BusRoute,
|
|
||||||
map: &Map,
|
|
||||||
) -> Vec<(StopIdx, PathRequest, Path, Distance)> {
|
|
||||||
assert!(bus_route.stops.len() > 1);
|
assert!(bus_route.stops.len() > 1);
|
||||||
|
|
||||||
let mut stops = Vec::new();
|
let mut stops = Vec::new();
|
||||||
for (idx, stop1_id) in bus_route.stops.iter().enumerate() {
|
for (idx, stop1_id) in bus_route.stops.iter().enumerate() {
|
||||||
let stop1 = map.get_bs(*stop1_id);
|
let stop1 = map.get_bs(*stop1_id);
|
||||||
let stop2_idx = if idx + 1 == bus_route.stops.len() {
|
if idx == bus_route.stops.len() - 1 {
|
||||||
0
|
stops.push(Stop {
|
||||||
} else {
|
id: stop1.id,
|
||||||
idx + 1
|
driving_pos: stop1.driving_pos,
|
||||||
};
|
next_stop: None,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let req = PathRequest {
|
let req = PathRequest {
|
||||||
start: stop1.driving_pos,
|
start: stop1.driving_pos,
|
||||||
end: map.get_bs(bus_route.stops[stop2_idx]).driving_pos,
|
end: map.get_bs(bus_route.stops[idx + 1]).driving_pos,
|
||||||
constraints: bus_route.route_type,
|
constraints: bus_route.route_type,
|
||||||
};
|
};
|
||||||
if let Some(path) = map.pathfind(req.clone()) {
|
if let Some(path) = map.pathfind(req.clone()) {
|
||||||
stops.push(StopForRoute {
|
if path.is_empty() {
|
||||||
id: *stop1_id,
|
panic!("Empty path between stops?! {}", req);
|
||||||
|
}
|
||||||
|
stops.push(Stop {
|
||||||
|
id: stop1.id,
|
||||||
driving_pos: stop1.driving_pos,
|
driving_pos: stop1.driving_pos,
|
||||||
req,
|
next_stop: Some((req, path)),
|
||||||
path_to_next_stop: path,
|
|
||||||
next_stop_idx: stop2_idx,
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// TODO Temporarily tolerate this
|
panic!("No route between stops: {}", req);
|
||||||
if bus_route.route_type == PathConstraints::Train {
|
|
||||||
println!(
|
|
||||||
"No route between bus stops {:?} and {:?}",
|
|
||||||
stop1_id, bus_route.stops[stop2_idx]
|
|
||||||
);
|
|
||||||
return Vec::new();
|
|
||||||
} else {
|
|
||||||
panic!(
|
|
||||||
"No route between bus stops {:?} and {:?}",
|
|
||||||
stop1_id, bus_route.stops[stop2_idx]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let results = stops
|
|
||||||
.iter()
|
|
||||||
.map(|s| {
|
|
||||||
(
|
|
||||||
s.next_stop_idx,
|
|
||||||
s.req.clone(),
|
|
||||||
s.path_to_next_stop.clone(),
|
|
||||||
stops[s.next_stop_idx].driving_pos.dist_along(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
|
let first_step = stops[0].next_stop.clone().unwrap();
|
||||||
self.routes.insert(
|
self.routes.insert(
|
||||||
bus_route.id,
|
bus_route.id,
|
||||||
Route {
|
Route {
|
||||||
buses: Vec::new(),
|
buses: BTreeSet::new(),
|
||||||
stops,
|
stops,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
results
|
first_step
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bus_created(&mut self, bus: CarID, route: BusRouteID, next_stop_idx: StopIdx) {
|
pub fn bus_created(&mut self, bus: CarID, route: BusRouteID) {
|
||||||
self.routes.get_mut(&route).unwrap().buses.push(bus);
|
self.routes.get_mut(&route).unwrap().buses.insert(bus);
|
||||||
self.buses.insert(
|
self.buses.insert(
|
||||||
bus,
|
bus,
|
||||||
Bus {
|
Bus {
|
||||||
car: bus,
|
car: bus,
|
||||||
route,
|
route,
|
||||||
passengers: Vec::new(),
|
passengers: Vec::new(),
|
||||||
state: BusState::DrivingToStop(next_stop_idx),
|
state: BusState::DrivingToStop(1),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -216,24 +190,26 @@ impl TransitSimState {
|
|||||||
self.peds_waiting.insert(stop1, still_waiting);
|
self.peds_waiting.insert(stop1, still_waiting);
|
||||||
}
|
}
|
||||||
BusState::AtStop(_) => unreachable!(),
|
BusState::AtStop(_) => unreachable!(),
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bus_departed_from_stop(&mut self, id: CarID) -> Router {
|
pub fn bus_departed_from_stop(&mut self, id: CarID, map: &Map) -> Router {
|
||||||
let mut bus = self.buses.get_mut(&id).unwrap();
|
let mut bus = self.buses.get_mut(&id).unwrap();
|
||||||
match bus.state {
|
match bus.state {
|
||||||
BusState::DrivingToStop(_) => unreachable!(),
|
BusState::DrivingToStop(_) => unreachable!(),
|
||||||
BusState::AtStop(stop_idx) => {
|
BusState::AtStop(stop_idx) => {
|
||||||
let route = &self.routes[&bus.route];
|
let stop = &self.routes[&bus.route].stops[stop_idx];
|
||||||
let stop = &route.stops[stop_idx];
|
|
||||||
|
|
||||||
bus.state = BusState::DrivingToStop(stop.next_stop_idx);
|
|
||||||
self.events
|
self.events
|
||||||
.push(Event::BusDepartedFromStop(id, bus.route, stop.id));
|
.push(Event::BusDepartedFromStop(id, bus.route, stop.id));
|
||||||
Router::follow_bus_route(
|
if let Some((req, path)) = stop.next_stop.clone() {
|
||||||
stop.path_to_next_stop.clone(),
|
bus.state = BusState::DrivingToStop(stop_idx + 1);
|
||||||
route.stops[stop.next_stop_idx].driving_pos.dist_along(),
|
Router::follow_bus_route(path.clone(), req.end.dist_along())
|
||||||
)
|
} else {
|
||||||
|
let on = stop.driving_pos.lane();
|
||||||
|
self.routes.get_mut(&bus.route).unwrap().buses.remove(&id);
|
||||||
|
self.buses.remove(&id);
|
||||||
|
Router::vanish_bus(on, map)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -300,20 +276,14 @@ impl TransitSimState {
|
|||||||
self.buses[&bus].route
|
self.buses[&bus].route
|
||||||
}
|
}
|
||||||
|
|
||||||
// also stop idx
|
// also stop idx that the bus is coming from
|
||||||
pub fn buses_for_route(&self, route: BusRouteID) -> Vec<(CarID, usize)> {
|
pub fn buses_for_route(&self, route: BusRouteID) -> Vec<(CarID, usize)> {
|
||||||
if let Some(ref r) = self.routes.get(&route) {
|
if let Some(ref r) = self.routes.get(&route) {
|
||||||
r.buses
|
r.buses
|
||||||
.iter()
|
.iter()
|
||||||
.map(|bus| {
|
.map(|bus| {
|
||||||
let stop = match self.buses[bus].state {
|
let stop = match self.buses[bus].state {
|
||||||
BusState::DrivingToStop(idx) => {
|
BusState::DrivingToStop(idx) => idx - 1,
|
||||||
if idx == 0 {
|
|
||||||
r.stops.len() - 1
|
|
||||||
} else {
|
|
||||||
idx - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BusState::AtStop(idx) => idx,
|
BusState::AtStop(idx) => idx,
|
||||||
};
|
};
|
||||||
(*bus, stop)
|
(*bus, stop)
|
||||||
|
Loading…
Reference in New Issue
Block a user