1 use crate::make::match_points_to_lanes;
2 use crate::raw::{RawBusRoute, RawBusStop};
3 use crate::{
4 BusRoute, BusRouteID, BusStop, BusStopID, LaneID, LaneType, Map, PathConstraints, Position,
5 };
6 use abstutil::Timer;
7 use geom::{Distance, Duration, FindClosest, HashablePt2D, Time};
8 use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
9
make_stops_and_routes(map: &mut Map, raw_routes: &Vec<RawBusRoute>, timer: &mut Timer)10 pub fn make_stops_and_routes(map: &mut Map, raw_routes: &Vec<RawBusRoute>, timer: &mut Timer) {
11 timer.start("make transit stops and routes");
12 let matcher = Matcher::new(raw_routes, map, timer);
13
14 // TODO I'm assuming the vehicle_pos <-> driving_pos relation is one-to-one...
15 let mut pt_to_stop: BTreeMap<(Position, Position), BusStopID> = BTreeMap::new();
16 for r in raw_routes {
17 if let Err(err) = make_route(map, r, &mut pt_to_stop, &matcher) {
18 timer.warn(format!(
19 "Skipping route {} ({}): {}",
20 r.full_name, r.osm_rel_id, err
21 ));
22 }
23 }
24
25 // Remove orphaned bus stops. This messes up the BusStopID indexing.
26 for id in map
27 .bus_stops
28 .keys()
29 .filter(|id| map.get_routes_serving_stop(**id).is_empty())
30 .cloned()
31 .collect::<Vec<_>>()
32 {
33 map.bus_stops.remove(&id);
34 map.lanes[id.sidewalk.0].bus_stops.remove(&id);
35 }
36
37 timer.stop("make transit stops and routes");
38 }
39
make_route( map: &mut Map, r: &RawBusRoute, pt_to_stop: &mut BTreeMap<(Position, Position), BusStopID>, matcher: &Matcher, ) -> Result<(), String>40 fn make_route(
41 map: &mut Map,
42 r: &RawBusRoute,
43 pt_to_stop: &mut BTreeMap<(Position, Position), BusStopID>,
44 matcher: &Matcher,
45 ) -> Result<(), String> {
46 let route_type = if r.is_bus {
47 PathConstraints::Bus
48 } else {
49 PathConstraints::Train
50 };
51
52 let mut stops = Vec::new();
53 for stop in &r.stops {
54 match matcher.lookup(route_type, stop, map) {
55 Ok((sidewalk_pos, driving_pos)) => {
56 // Create a new bus stop if needed.
57 let stop_id = if let Some(id) = pt_to_stop.get(&(sidewalk_pos, driving_pos)) {
58 *id
59 } else {
60 let id = BusStopID {
61 sidewalk: sidewalk_pos.lane(),
62 idx: map.get_l(sidewalk_pos.lane()).bus_stops.len(),
63 };
64 pt_to_stop.insert((sidewalk_pos, driving_pos), id);
65 map.lanes[sidewalk_pos.lane().0].bus_stops.insert(id);
66 map.bus_stops.insert(
67 id,
68 BusStop {
69 id,
70 name: stop.name.clone(),
71 driving_pos,
72 sidewalk_pos,
73 is_train_stop: !r.is_bus,
74 },
75 );
76 id
77 };
78 stops.push(stop_id);
79 }
80 Err(err) => {
81 return Err(format!("couldn't match stop {}: {}", stop.name, err));
82 }
83 }
84 }
85
86 // Start or end at a border?
87 let mut end_border = None;
88 let start = if let Some(i) = r.border_start {
89 let i = map.get_i(map.find_i_by_osm_id(i).unwrap());
90 if !i.is_border() {
91 panic!("Route starts at {}, but isn't a border?", i.orig_id);
92 }
93 if let Some(l) = i.get_outgoing_lanes(map, route_type).get(0) {
94 *l
95 } else {
96 return Err(format!(
97 "Route {} starts at {} ({}), but no starting lane for a {:?}?",
98 r.osm_rel_id, i.id, i.orig_id, route_type
99 ));
100 }
101 } else {
102 // Not starting at a border. Find a lane at or before the first stop that's at least 13m.
103 pick_start_lane(map.get_bs(stops[0]).driving_pos, route_type, map)?
104 };
105 if let Some(i) = r.border_end {
106 let i = map.get_i(map.find_i_by_osm_id(i).unwrap());
107 if !i.is_border() {
108 panic!("Route ends at {}, but isn't a border?", i.orig_id);
109 }
110 // If the last stop is on a lane leading to the border, don't try to lane-change last
111 // minute
112 let last_stop_l = map.get_bs(*stops.last().unwrap()).driving_pos.lane();
113 if map.get_l(last_stop_l).dst_i == i.id {
114 end_border = Some(last_stop_l);
115 } else if let Some(l) = i.get_incoming_lanes(map, route_type).next() {
116 end_border = Some(l);
117 } else {
118 // TODO Should panic
119 println!(
120 "Route {} ends at {} ({}), but no ending lane for a {:?}?",
121 r.osm_rel_id, i.id, i.orig_id, route_type
122 );
123 }
124 }
125
126 let route = BusRoute {
127 id: BusRouteID(map.bus_routes.len()),
128 full_name: r.full_name.clone(),
129 short_name: r.short_name.clone(),
130 osm_rel_id: r.osm_rel_id,
131 gtfs_trip_marker: r.gtfs_trip_marker.clone(),
132 stops,
133 route_type,
134 start,
135 end_border,
136 spawn_times: default_spawn_times(),
137 orig_spawn_times: default_spawn_times(),
138 };
139
140 let mut debug_route = format!("All parts of the route:");
141 debug_route = format!("{}\nStart at {}", debug_route, route.start);
142 for (idx, bs) in route.stops.iter().enumerate() {
143 let stop = map.get_bs(*bs);
144 debug_route = format!(
145 "{}\nStop {} ({}): {}",
146 debug_route,
147 idx + 1,
148 stop.name,
149 stop.driving_pos
150 );
151 }
152 if let Some(l) = route.end_border {
153 debug_route = format!("{}\nEnd at {}", debug_route, l);
154 }
155
156 // Make sure the route is connected
157 for req in route.all_steps(map) {
158 if req.start.lane() == req.end.lane() && req.start.dist_along() > req.end.dist_along() {
159 return Err(format!(
160 "Two stops seemingly out of order somewhere on {}",
161 map.get_parent(req.start.lane()).orig_id
162 ));
163 }
164
165 if map.pathfind(req.clone()).is_none() {
166 return Err(format!(
167 "No path between stop on {} and {}: {}. {}",
168 map.get_parent(req.start.lane()).orig_id,
169 map.get_parent(req.end.lane()).orig_id,
170 req,
171 debug_route
172 ));
173 }
174 }
175
176 map.bus_routes.push(route);
177 Ok(())
178 }
179
180 struct Matcher {
181 // TODO Eventually, maybe also map to a station building too
182 sidewalk_pts: HashMap<HashablePt2D, Position>,
183 light_rail_pts: HashMap<HashablePt2D, Position>,
184 }
185
186 impl Matcher {
new(routes: &Vec<RawBusRoute>, map: &Map, timer: &mut Timer) -> Matcher187 fn new(routes: &Vec<RawBusRoute>, map: &Map, timer: &mut Timer) -> Matcher {
188 // Match all of the points to an exact position along a lane.
189 let mut lookup_sidewalk_pts = HashSet::new();
190 let mut lookup_light_rail_pts = HashSet::new();
191 for r in routes {
192 for stop in &r.stops {
193 if !r.is_bus {
194 lookup_light_rail_pts.insert(stop.vehicle_pos.1.to_hashable());
195 }
196 if let Some(pt) = stop.ped_pos {
197 lookup_sidewalk_pts.insert(pt.to_hashable());
198 }
199 }
200 }
201 let sidewalk_pts = match_points_to_lanes(
202 map.get_bounds(),
203 lookup_sidewalk_pts,
204 map.all_lanes(),
205 |l| l.is_walkable(),
206 Distance::ZERO,
207 // TODO Generous for cap hill light rail platform
208 Distance::meters(50.0),
209 timer,
210 );
211 let light_rail_pts = match_points_to_lanes(
212 map.get_bounds(),
213 lookup_light_rail_pts,
214 map.all_lanes(),
215 |l| l.lane_type == LaneType::LightRail,
216 Distance::ZERO,
217 Distance::meters(10.0),
218 timer,
219 );
220
221 Matcher {
222 sidewalk_pts,
223 light_rail_pts,
224 }
225 }
226
227 // returns (sidewalk, driving)
lookup( &self, route_type: PathConstraints, stop: &RawBusStop, map: &Map, ) -> Result<(Position, Position), String>228 fn lookup(
229 &self,
230 route_type: PathConstraints,
231 stop: &RawBusStop,
232 map: &Map,
233 ) -> Result<(Position, Position), String> {
234 if route_type == PathConstraints::Train {
235 // Light rail needs explicit platforms.
236 let sidewalk_pt = stop.ped_pos.ok_or("light rail missing platform")?;
237 let sidewalk_pos = *self
238 .sidewalk_pts
239 .get(&sidewalk_pt.to_hashable())
240 .ok_or_else(|| format!("sidewalk for light rail didnt match: {}", sidewalk_pt))?;
241 let driving_pos = *self
242 .light_rail_pts
243 .get(&stop.vehicle_pos.1.to_hashable())
244 .ok_or_else(|| {
245 format!("vehicle for light rail didnt match: {}", stop.vehicle_pos.0)
246 })?;
247 return Ok((sidewalk_pos, driving_pos));
248 }
249
250 // We already figured out what side of the road we're on
251 let (r, dir) = stop.matched_road.unwrap();
252 let r = map.get_r(map.find_r_by_osm_id(r)?);
253 // Prefer the rightmost match. DON'T use find_closest_lane here; we only want one side of
254 // the road.
255 let l = map.get_l(
256 r.children(dir)
257 .iter()
258 .rev()
259 .find(|(l, _)| route_type.can_use(map.get_l(*l), map))
260 .ok_or_else(|| format!("{} {}, doesn't have a bus or driving lane", r.id, dir))?
261 .0,
262 );
263
264 // Where exactly along this lane?
265 // TODO This should just be a method in PolyLine
266 let mut closest: FindClosest<()> = FindClosest::new(map.get_bounds());
267 closest.add((), l.lane_center_pts.points());
268 let (_, pt) = closest
269 .closest_pt(stop.vehicle_pos.1, Distance::meters(10.0))
270 .ok_or_else(|| format!("{} isn't near {}", stop.vehicle_pos.0, l.id))?;
271 let mut driving_pos = Position::new(l.id, l.dist_along_of_point(pt).unwrap());
272
273 let sidewalk_pos = if let Some(pt) = stop.ped_pos {
274 *self
275 .sidewalk_pts
276 .get(&pt.to_hashable())
277 .ok_or("sidewalk didnt match")?
278 } else {
279 let sidewalk = map
280 .get_parent(driving_pos.lane())
281 .find_closest_lane(
282 driving_pos.lane(),
283 |l| PathConstraints::Pedestrian.can_use(l, map),
284 map,
285 )
286 .ok_or_else(|| format!("driving {} to sidewalk failed", driving_pos.lane()))?;
287 driving_pos.equiv_pos(sidewalk, map)
288 };
289
290 // If we're a stop right at an incoming border, make sure to be at least past where the bus
291 // will spawn from the border. pick_start_lane() can't do anything for borders.
292 if map
293 .get_i(map.get_l(driving_pos.lane()).src_i)
294 .is_incoming_border()
295 {
296 if let Some(pos) = driving_pos.min_dist(Distance::meters(1.0), map) {
297 driving_pos = pos;
298 } else {
299 return Err(format!(
300 "too close to start of a border {}",
301 driving_pos.lane()
302 ));
303 }
304 }
305 Ok((sidewalk_pos, driving_pos))
306 }
307 }
308
pick_start_lane( first_stop: Position, constraints: PathConstraints, map: &Map, ) -> Result<LaneID, String>309 fn pick_start_lane(
310 first_stop: Position,
311 constraints: PathConstraints,
312 map: &Map,
313 ) -> Result<LaneID, String> {
314 let min_len = Distance::meters(13.0);
315 if first_stop.dist_along() >= min_len {
316 return Ok(first_stop.lane());
317 }
318
319 // Flood backwards until we find a long enough lane
320 let mut queue = VecDeque::new();
321 queue.push_back(first_stop.lane());
322 while !queue.is_empty() {
323 let current = queue.pop_front().unwrap();
324 if current != first_stop.lane() && map.get_l(current).length() >= min_len {
325 return Ok(current);
326 }
327 for t in map.get_turns_to_lane(current) {
328 if constraints.can_use(map.get_l(t.id.src), map) {
329 queue.push_back(t.id.src);
330 }
331 }
332 }
333 Err(format!(
334 "couldn't find any lanes leading to {} that're long enough for a bus to spawn",
335 first_stop.lane()
336 ))
337 }
338
default_spawn_times() -> Vec<Time>339 fn default_spawn_times() -> Vec<Time> {
340 // Hourly spawning from midnight to 7, then every 30 minutes till 7, then hourly again
341 let mut times = Vec::new();
342 for i in 0..24 {
343 let hour = Time::START_OF_DAY + Duration::hours(i);
344 times.push(hour);
345 if i >= 7 && i <= 19 {
346 times.push(hour + Duration::minutes(30));
347 }
348 }
349 times
350 }
351