1use anyhow::Result;
5use serde::{Deserialize, Serialize};
6
7use map_model::{BuildingID, Map, PathConstraints, Position, TransitRouteID, TransitStopID};
8use synthpop::{TripEndpoint, TripMode};
9
10use crate::{CarID, DrivingGoal, SidewalkSpot, TripLeg, VehicleType, SPAWN_DIST};
11
12#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
15pub(crate) struct StartTripArgs {
16 pub retry_if_no_room: bool,
17 pub use_vehicle: Option<CarID>,
18}
19
20#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
22pub(crate) enum TripSpec {
23 VehicleAppearing {
25 start_pos: Position,
26 goal: DrivingGoal,
27 use_vehicle: CarID,
29 retry_if_no_room: bool,
30 },
31 SpawningFailure {
33 use_vehicle: Option<CarID>,
34 error: String,
35 },
36 UsingParkedCar {
37 car: CarID,
39 start_bldg: BuildingID,
40 goal: DrivingGoal,
41 },
42 JustWalking {
43 start: SidewalkSpot,
44 goal: SidewalkSpot,
45 },
46 UsingBike {
47 bike: CarID,
48 start: BuildingID,
49 goal: DrivingGoal,
50 },
51 UsingTransit {
52 start: SidewalkSpot,
53 goal: SidewalkSpot,
54 route: TransitRouteID,
55 stop1: TransitStopID,
56 maybe_stop2: Option<TransitStopID>,
57 },
58}
59
60impl TripSpec {
61 pub fn into_plan(self, map: &Map) -> (TripSpec, Vec<TripLeg>) {
62 let mut legs = Vec::new();
64 match &self {
65 TripSpec::VehicleAppearing {
66 start_pos,
67 goal,
68 use_vehicle,
69 ..
70 } => {
71 if start_pos.dist_along() >= map.get_l(start_pos.lane()).length() {
72 panic!("Can't spawn at {}; it isn't that long", start_pos);
73 }
74 if let DrivingGoal::Border(_, end_lane) = goal {
75 if start_pos.lane() == *end_lane
76 && start_pos.dist_along() == map.get_l(*end_lane).length()
77 {
78 panic!(
79 "Can't start at {}; it's the edge of a border already",
80 start_pos
81 );
82 }
83 }
84
85 let constraints = if use_vehicle.vehicle_type == VehicleType::Bike {
86 PathConstraints::Bike
87 } else {
88 PathConstraints::Car
89 };
90
91 legs.push(TripLeg::Drive(*use_vehicle, goal.clone()));
92 if let DrivingGoal::ParkNear(b) = goal {
93 legs.push(TripLeg::Walk(SidewalkSpot::building(*b, map)));
94 }
95
96 if goal.goal_pos(constraints, map).is_none() {
97 return TripSpec::SpawningFailure {
98 use_vehicle: Some(*use_vehicle),
99 error: format!("goal_pos to {:?} for a {:?} failed", goal, constraints),
100 }
101 .into_plan(map);
102 }
103 }
104 TripSpec::SpawningFailure { .. } => {
105 legs.push(TripLeg::RideBus(TransitRouteID(0), None));
109 }
110 TripSpec::UsingParkedCar { car, goal, .. } => {
111 legs.push(TripLeg::Walk(SidewalkSpot::deferred_parking_spot()));
112 legs.push(TripLeg::Drive(*car, goal.clone()));
113 match goal {
114 DrivingGoal::ParkNear(b) => {
115 legs.push(TripLeg::Walk(SidewalkSpot::building(*b, map)));
116 }
117 DrivingGoal::Border(_, _) => {}
118 }
119 }
120 TripSpec::JustWalking { start, goal, .. } => {
121 if start == goal {
122 panic!(
123 "A trip just walking from {:?} to {:?} doesn't make sense",
124 start, goal
125 );
126 }
127 legs.push(TripLeg::Walk(goal.clone()));
128 }
129 TripSpec::UsingBike { start, goal, bike } => {
130 let backup_plan = match goal {
132 DrivingGoal::ParkNear(b) => Some(TripSpec::JustWalking {
133 start: SidewalkSpot::building(*start, map),
134 goal: SidewalkSpot::building(*b, map),
135 }),
136 DrivingGoal::Border(i, _) => {
137 SidewalkSpot::end_at_border(*i, map).map(|goal| TripSpec::JustWalking {
138 start: SidewalkSpot::building(*start, map),
139 goal,
140 })
141 }
142 };
143
144 if let Some(start_spot) = SidewalkSpot::bike_rack(*start, map) {
145 if let DrivingGoal::ParkNear(b) = goal {
146 if let Some(goal_spot) = SidewalkSpot::bike_rack(*b, map) {
147 if start_spot.sidewalk_pos.lane() == goal_spot.sidewalk_pos.lane() {
148 info!(
149 "Bike trip from {} to {} will just walk; it's the same \
150 sidewalk!",
151 start, b
152 );
153 return backup_plan.unwrap().into_plan(map);
154 }
155 } else {
156 info!(
157 "Can't find biking connection for goal {}, walking instead",
158 b
159 );
160 return backup_plan.unwrap().into_plan(map);
161 }
162 }
163
164 legs.push(TripLeg::Walk(start_spot));
165 legs.push(TripLeg::Drive(*bike, goal.clone()));
166 match goal {
167 DrivingGoal::ParkNear(b) => {
168 legs.push(TripLeg::Walk(SidewalkSpot::building(*b, map)));
169 }
170 DrivingGoal::Border(_, _) => {}
171 }
172 } else if let Some(plan) = backup_plan {
173 info!("Can't start biking from {}. Walking instead", start);
174 return plan.into_plan(map);
175 } else {
176 return TripSpec::SpawningFailure {
177 use_vehicle: Some(*bike),
178 error: format!(
179 "Can't start biking from {} and can't walk either! Goal is {:?}",
180 start, goal
181 ),
182 }
183 .into_plan(map);
184 }
185 }
186 TripSpec::UsingTransit {
187 route,
188 stop1,
189 maybe_stop2,
190 goal,
191 ..
192 } => {
193 let walk_to = SidewalkSpot::bus_stop(*stop1, map);
194 if let Some(stop2) = maybe_stop2 {
195 legs = vec![
196 TripLeg::Walk(walk_to),
197 TripLeg::RideBus(*route, Some(*stop2)),
198 TripLeg::Walk(goal.clone()),
199 ];
200 } else {
201 legs = vec![TripLeg::Walk(walk_to), TripLeg::RideBus(*route, None)];
202 }
203 }
204 };
205
206 (self, legs)
207 }
208
209 pub fn maybe_new(
212 from: TripEndpoint,
213 to: TripEndpoint,
214 mode: TripMode,
215 use_vehicle: Option<CarID>,
216 retry_if_no_room: bool,
217 map: &Map,
218 ) -> Result<TripSpec> {
219 Ok(match mode {
220 TripMode::Drive | TripMode::Bike => {
221 let constraints = if mode == TripMode::Drive {
222 PathConstraints::Car
223 } else {
224 PathConstraints::Bike
225 };
226 let goal = driving_goal(to, constraints, map)?;
227 match from {
228 TripEndpoint::Building(start_bldg) => {
229 if mode == TripMode::Drive {
230 TripSpec::UsingParkedCar {
231 start_bldg,
232 goal,
233 car: use_vehicle.unwrap(),
234 }
235 } else {
236 TripSpec::UsingBike {
237 start: start_bldg,
238 goal,
239 bike: use_vehicle.unwrap(),
240 }
241 }
242 }
243 TripEndpoint::Border(i) => {
244 let start_lane = map
245 .get_i(i)
246 .some_outgoing_road(map)
247 .and_then(|dr| dr.lanes(constraints, map).pop())
250 .ok_or_else(|| {
251 anyhow!("can't start a {} trip from {}", mode.ongoing_verb(), i)
252 })?;
253 TripSpec::VehicleAppearing {
254 start_pos: Position::new(start_lane, SPAWN_DIST),
255 goal,
256 use_vehicle: use_vehicle.unwrap(),
257 retry_if_no_room,
258 }
259 }
260 TripEndpoint::SuddenlyAppear(start_pos) => TripSpec::VehicleAppearing {
261 start_pos,
262 goal,
263 use_vehicle: use_vehicle.unwrap(),
264 retry_if_no_room,
265 },
266 }
267 }
268 TripMode::Walk => TripSpec::JustWalking {
269 start: start_sidewalk_spot(from, map)?,
270 goal: end_sidewalk_spot(to, map)?,
271 },
272 TripMode::Transit => {
273 let start = start_sidewalk_spot(from, map)?;
274 let goal = end_sidewalk_spot(to, map)?;
275 if let Some((stop1, maybe_stop2, route)) =
276 map.should_use_transit(start.sidewalk_pos, goal.sidewalk_pos)
277 {
278 TripSpec::UsingTransit {
279 start,
280 goal,
281 route,
282 stop1,
283 maybe_stop2,
284 }
285 } else {
286 TripSpec::JustWalking { start, goal }
289 }
290 }
291 })
292 }
293}
294
295fn start_sidewalk_spot(endpt: TripEndpoint, map: &Map) -> Result<SidewalkSpot> {
296 match endpt {
297 TripEndpoint::Building(b) => Ok(SidewalkSpot::building(b, map)),
298 TripEndpoint::Border(i) => SidewalkSpot::start_at_border(i, map)
299 .ok_or_else(|| anyhow!("can't start walking from {}", i)),
300 TripEndpoint::SuddenlyAppear(pos) => Ok(SidewalkSpot::suddenly_appear(pos, map)),
301 }
302}
303
304fn end_sidewalk_spot(endpt: TripEndpoint, map: &Map) -> Result<SidewalkSpot> {
305 match endpt {
306 TripEndpoint::Building(b) => Ok(SidewalkSpot::building(b, map)),
307 TripEndpoint::Border(i) => {
308 SidewalkSpot::end_at_border(i, map).ok_or_else(|| anyhow!("can't end walking at {}", i))
309 }
310 TripEndpoint::SuddenlyAppear(_) => unreachable!(),
311 }
312}
313
314fn driving_goal(
315 endpt: TripEndpoint,
316 constraints: PathConstraints,
317 map: &Map,
318) -> Result<DrivingGoal> {
319 match endpt {
320 TripEndpoint::Building(b) => Ok(DrivingGoal::ParkNear(b)),
321 TripEndpoint::Border(i) => map
323 .get_i(i)
324 .some_incoming_road(map)
325 .and_then(|dr| {
326 let lanes = dr.lanes(constraints, map);
327 if lanes.is_empty() {
328 None
329 } else {
330 Some(DrivingGoal::Border(dr.dst_i(map), lanes[0]))
332 }
333 })
334 .ok_or_else(|| anyhow!("can't end at {} for {:?}", i, constraints)),
335 TripEndpoint::SuddenlyAppear(_) => unreachable!(),
336 }
337}