1use std::collections::{HashMap, HashSet};
2
3use anyhow::Result;
4
5use abstutil::Timer;
6use geom::{Distance, Duration, FindClosest, HashablePt2D, Time};
7use raw_map::{RawMap, RawTransitRoute, RawTransitStop, RawTransitType};
8
9use crate::make::match_points_to_lanes;
10use crate::{
11 LaneID, Map, PathConstraints, Position, TransitRoute, TransitRouteID, TransitStop,
12 TransitStopID,
13};
14
15pub fn finalize_transit(map: &mut Map, raw: &RawMap, timer: &mut Timer) {
16 let mut query: HashSet<HashablePt2D> = HashSet::new();
18 for stop in raw.transit_stops.values() {
19 query.insert(stop.position.to_hashable());
20 }
21 let sidewalk_pts = match_points_to_lanes(
22 map,
23 query,
24 |l| l.is_walkable(),
25 Distance::ZERO,
27 Distance::meters(3.0),
29 timer,
30 );
31
32 let mut gtfs_to_stop_id: HashMap<String, TransitStopID> = HashMap::new();
34 for stop in raw.transit_stops.values() {
35 if let Err(err) = create_stop(stop, &sidewalk_pts, &mut gtfs_to_stop_id, map) {
36 warn!("Couldn't create stop {}: {}", stop.gtfs_id, err);
37 }
38 }
39
40 let snapper = BorderSnapper::new(map);
41 for route in &raw.transit_routes {
42 if let Err(err) = create_route(route, map, >fs_to_stop_id, &snapper) {
43 warn!(
44 "Couldn't snap route {} ({}): {}",
45 route.gtfs_id, route.short_name, err
46 );
47 }
48 }
49
50 }
52
53fn create_stop(
54 stop: &RawTransitStop,
55 sidewalk_pts: &HashMap<HashablePt2D, Position>,
56 gtfs_to_stop_id: &mut HashMap<String, TransitStopID>,
57 map: &mut Map,
58) -> Result<()> {
59 let vehicle = PathConstraints::Bus;
61 if let Some(sidewalk_pos) = sidewalk_pts.get(&stop.position.to_hashable()) {
62 let sidewalk_lane = sidewalk_pos.lane();
63 if let Some(driving_pos) = map
64 .get_parent(sidewalk_lane)
65 .find_closest_lane(sidewalk_lane, |l| vehicle.can_use(l, map))
66 .map(|l| sidewalk_pos.equiv_pos(l, map))
67 {
68 let road = sidewalk_lane.road;
69 let id = TransitStopID {
70 road,
71 idx: map.get_r(road).transit_stops.len(),
72 };
73 map.mut_road(road).transit_stops.insert(id);
74 map.transit_stops.insert(
75 id,
76 TransitStop {
77 id,
78 name: stop.name.clone(),
79 gtfs_id: stop.gtfs_id.clone(),
80 driving_pos,
81 sidewalk_pos: *sidewalk_pos,
82 is_train_stop: vehicle == PathConstraints::Train,
83 },
84 );
85 gtfs_to_stop_id.insert(stop.gtfs_id.clone(), id);
86 Ok(())
87 } else {
88 bail!(
89 "Couldn't find a lane for {:?} next to sidewalk {}",
90 vehicle,
91 sidewalk_lane
92 );
93 }
94 } else {
95 bail!("Stop position {} wasn't close to a sidewalk", stop.position);
96 }
97}
98
99struct BorderSnapper {
100 bus_incoming_borders: FindClosest<LaneID>,
101 bus_outgoing_borders: FindClosest<LaneID>,
102 train_incoming_borders: FindClosest<LaneID>,
103 train_outgoing_borders: FindClosest<LaneID>,
104}
105
106impl BorderSnapper {
107 fn new(map: &Map) -> BorderSnapper {
108 let mut snapper = BorderSnapper {
109 bus_incoming_borders: FindClosest::new(),
110 bus_outgoing_borders: FindClosest::new(),
111 train_incoming_borders: FindClosest::new(),
112 train_outgoing_borders: FindClosest::new(),
113 };
114 for i in map.all_incoming_borders() {
115 for l in i.get_outgoing_lanes(map, PathConstraints::Bus) {
116 snapper
119 .bus_incoming_borders
120 .add_polygon(l, &map.get_l(l).get_thick_polygon());
121 }
122 for l in i.get_outgoing_lanes(map, PathConstraints::Train) {
123 snapper
124 .train_incoming_borders
125 .add_polygon(l, &map.get_l(l).get_thick_polygon());
126 }
127 }
128 for i in map.all_outgoing_borders() {
129 for l in i.get_incoming_lanes(map, PathConstraints::Bus) {
130 snapper
131 .bus_outgoing_borders
132 .add_polygon(l, &map.get_l(l).get_thick_polygon());
133 }
134 for l in i.get_incoming_lanes(map, PathConstraints::Train) {
135 snapper
136 .train_outgoing_borders
137 .add_polygon(l, &map.get_l(l).get_thick_polygon());
138 }
139 }
140 snapper
141 }
142}
143
144fn create_route(
145 route: &RawTransitRoute,
146 map: &mut Map,
147 gtfs_to_stop_id: &HashMap<String, TransitStopID>,
148 snapper: &BorderSnapper,
149) -> Result<()> {
150 let stops: Vec<TransitStopID> = route
152 .stops
153 .iter()
154 .filter_map(|gtfs_id| gtfs_to_stop_id.get(gtfs_id).cloned())
155 .collect();
156 if stops.is_empty() {
157 bail!("No valid stops");
158 }
159 let border_snap_threshold = Distance::meters(30.0);
160
161 let start = if map.boundary_polygon.contains_pt(route.shape.first_pt()) {
162 map.get_ts(stops[0]).driving_pos.lane()
163 } else {
164 let entry_pt = *map
166 .boundary_polygon
167 .get_outer_ring()
168 .all_intersections(&route.shape)
169 .get(0)
170 .ok_or_else(|| anyhow!("couldn't find where shape enters map"))?;
171 let borders = if route.route_type == RawTransitType::Bus {
173 &snapper.bus_incoming_borders
174 } else {
175 &snapper.train_incoming_borders
176 };
177 match borders.closest_pt(entry_pt, border_snap_threshold) {
178 Some((l, _)) => l,
179 None => bail!(
180 "Couldn't find a {:?} border near start {}",
181 route.route_type,
182 entry_pt
183 ),
184 }
185 };
186
187 let end_border = if map.boundary_polygon.contains_pt(route.shape.last_pt()) {
188 None
189 } else {
190 let exit_pt = *map
192 .boundary_polygon
193 .get_outer_ring()
194 .all_intersections(&route.shape)
195 .last()
196 .ok_or_else(|| anyhow!("couldn't find where shape leaves map"))?;
197 let borders = if route.route_type == RawTransitType::Bus {
199 &snapper.bus_outgoing_borders
200 } else {
201 &snapper.train_outgoing_borders
202 };
203 match borders.closest_pt(exit_pt, border_snap_threshold) {
204 Some((lane, _)) => {
205 let last_stop_lane = map.get_ts(*stops.last().unwrap()).driving_pos.lane();
208 Some(if lane.road == last_stop_lane.road {
209 last_stop_lane
210 } else {
211 lane
212 })
213 }
214 None => bail!(
215 "Couldn't find a {:?} border near end {}",
216 route.route_type,
217 exit_pt
218 ),
219 }
220 };
221
222 let spawn_times: Vec<Time> = (0..48)
224 .map(|i| Time::START_OF_DAY + (i as f64) * Duration::minutes(30))
225 .collect();
226
227 let result = TransitRoute {
228 id: TransitRouteID(map.transit_routes.len()),
229 long_name: route.long_name.clone(),
230 short_name: route.short_name.clone(),
231 gtfs_id: route.gtfs_id.clone(),
232 stops,
233 start,
234 end_border,
235 route_type: match route.route_type {
236 RawTransitType::Bus => PathConstraints::Bus,
237 RawTransitType::Train => PathConstraints::Train,
238 },
239 spawn_times: spawn_times.clone(),
240 orig_spawn_times: spawn_times,
241 };
242
243 result.all_paths(map)?;
245
246 map.transit_routes.push(result);
247 Ok(())
248}