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