1use std::collections::HashMap;
2
3use abstutil::{prettyprint_usize, MultiMap, Timer};
4use geom::PolyLine;
5use map_model::{osm, BuildingID, Map, Path, PathConstraints, PathRequest, PathStep};
6use synthpop::{
7 IndividTrip, MapBorder, MapBorders, OrigPersonID, PersonSpec, Scenario, TripEndpoint, TripMode,
8};
9
10use crate::soundcast::popdat::{Endpoint, OrigTrip, PopDat};
11
12#[derive(Clone, Debug)]
13struct Trip {
14 from: TripEndpoint,
15 to: TripEndpoint,
16 orig: OrigTrip,
17}
18
19#[allow(clippy::too_many_arguments)]
26fn endpoints(
27 from: &Endpoint,
28 to: &Endpoint,
29 map: &Map,
30 osm_id_to_bldg: &HashMap<osm::OsmID, BuildingID>,
31 (in_borders, out_borders): (&Vec<MapBorder>, &Vec<MapBorder>),
32 constraints: PathConstraints,
33 maybe_huge_map: Option<&(&Map, HashMap<osm::OsmID, BuildingID>)>,
34 only_passthrough_trips: bool,
35) -> Option<(TripEndpoint, TripEndpoint)> {
36 let from_bldg = from
37 .osm_building
38 .and_then(|id| osm_id_to_bldg.get(&id))
39 .map(|b| TripEndpoint::Building(*b));
40 let to_bldg = to
41 .osm_building
42 .and_then(|id| osm_id_to_bldg.get(&id))
43 .map(|b| TripEndpoint::Building(*b));
44
45 if only_passthrough_trips {
46 if from_bldg.is_some() || to_bldg.is_some() {
47 return None;
48 }
49 } else {
50 if let (Some(b1), Some(b2)) = (from_bldg, to_bldg) {
52 return Some((b1, b2));
53 }
54 }
55
56 if from_bldg.is_none() && to_bldg.is_none() {
61 if !only_passthrough_trips {
65 return None;
66 }
67
68 if let Ok(pl) = PolyLine::new(vec![
69 from.pos.to_pt(map.get_gps_bounds()),
70 to.pos.to_pt(map.get_gps_bounds()),
71 ]) {
72 if !map.get_boundary_polygon().intersects_polyline(&pl) {
73 return None;
74 }
75 }
76 }
77
78 let snapper = BorderSnapper::new(from, to, constraints, maybe_huge_map)
83 .unwrap_or(BorderSnapper { path: None });
84
85 let from_endpt = from_bldg
86 .or_else(|| snapper.snap_border(in_borders, true, map, maybe_huge_map))
87 .or_else(|| {
88 in_borders
90 .iter()
91 .min_by_key(|border| border.gps_pos.fast_dist(from.pos))
92 .map(|border| TripEndpoint::Border(border.i))
93 })?;
94 let to_endpt = to_bldg
95 .or_else(|| snapper.snap_border(out_borders, false, map, maybe_huge_map))
96 .or_else(|| {
97 out_borders
99 .iter()
100 .min_by_key(|border| border.gps_pos.fast_dist(from.pos))
101 .map(|border| TripEndpoint::Border(border.i))
102 })?;
103
104 if from_endpt == to_endpt {
105 }
107
108 Some((from_endpt, to_endpt))
109}
110
111struct BorderSnapper {
114 path: Option<Path>,
115}
116
117impl BorderSnapper {
118 fn new(
119 from: &Endpoint,
120 to: &Endpoint,
121 constraints: PathConstraints,
122 maybe_huge_map: Option<&(&Map, HashMap<osm::OsmID, BuildingID>)>,
123 ) -> Option<BorderSnapper> {
124 let (huge_map, huge_osm_id_to_bldg) = maybe_huge_map?;
125 let b1 = *from
126 .osm_building
127 .and_then(|id| huge_osm_id_to_bldg.get(&id))?;
128 let b2 = *to
129 .osm_building
130 .and_then(|id| huge_osm_id_to_bldg.get(&id))?;
131 let req = PathRequest::between_buildings(huge_map, b1, b2, constraints)?;
132 Some(BorderSnapper {
133 path: huge_map.pathfind(req).ok(),
134 })
135 }
136
137 fn snap_border(
138 &self,
139 usable_borders: &[MapBorder],
140 incoming: bool,
141 map: &Map,
142 maybe_huge_map: Option<&(&Map, HashMap<osm::OsmID, BuildingID>)>,
143 ) -> Option<TripEndpoint> {
144 let huge_map = maybe_huge_map?.0;
145 let mut node_id_to_border = HashMap::new();
148 for border in usable_borders {
149 node_id_to_border.insert(map.get_i(border.i).orig_id, border.i);
150 }
151 let mut iter1;
152 let mut iter2;
153 let steps: &mut dyn Iterator<Item = &PathStep> = if incoming {
154 iter1 = self.path.as_ref()?.get_steps().iter();
155 &mut iter1
156 } else {
157 iter2 = self.path.as_ref()?.get_steps().iter().rev();
158 &mut iter2
159 };
160 for step in steps {
161 if let PathStep::Turn(t) | PathStep::ContraflowTurn(t) = step {
162 if let Some(i) = node_id_to_border.get(&huge_map.get_i(t.parent).orig_id) {
163 return Some(TripEndpoint::Border(*i));
164 }
165 }
166 }
167 None
168 }
169}
170
171fn clip_trips(
172 map: &Map,
173 popdat: &PopDat,
174 huge_map: &Map,
175 only_passthrough_trips: bool,
176 timer: &mut Timer,
177) -> Vec<Trip> {
178 let maybe_huge_map = if map.get_name().map == "huge_seattle" {
179 None
180 } else {
181 let mut huge_osm_id_to_bldg = HashMap::new();
182 for b in huge_map.all_buildings() {
183 huge_osm_id_to_bldg.insert(b.orig_id, b.id);
184 }
185 Some((huge_map, huge_osm_id_to_bldg))
186 };
187
188 let mut osm_id_to_bldg = HashMap::new();
189 for b in map.all_buildings() {
190 osm_id_to_bldg.insert(b.orig_id, b.id);
191 }
192 let borders = MapBorders::new(map);
193
194 let total_trips = popdat.trips.len();
195 let maybe_results: Vec<Option<Trip>> =
196 timer.parallelize("clip trips", popdat.trips.iter().collect(), |orig| {
197 let (from, to) = endpoints(
198 &orig.from,
199 &orig.to,
200 map,
201 &osm_id_to_bldg,
202 borders.for_mode(orig.mode),
203 match orig.mode {
204 TripMode::Walk | TripMode::Transit => PathConstraints::Pedestrian,
205 TripMode::Drive => PathConstraints::Car,
206 TripMode::Bike => PathConstraints::Bike,
207 },
208 maybe_huge_map.as_ref(),
209 only_passthrough_trips,
210 )?;
211 Some(Trip {
212 from,
213 to,
214 orig: orig.clone(),
215 })
216 });
217 let trips: Vec<Trip> = maybe_results.into_iter().flatten().collect();
218
219 info!(
220 "{} trips clipped down to just {}",
221 prettyprint_usize(total_trips),
222 prettyprint_usize(trips.len())
223 );
224
225 trips
226}
227
228pub fn make_scenario(
229 scenario_name: &str,
230 map: &Map,
231 popdat: &PopDat,
232 huge_map: &Map,
233 timer: &mut Timer,
234) -> Scenario {
235 let only_passthrough_trips = scenario_name == "passthrough";
236
237 let mut individ_trips: Vec<Option<IndividTrip>> = Vec::new();
238 let mut trips_per_person: MultiMap<OrigPersonID, ((usize, bool, usize), usize)> =
240 MultiMap::new();
241 for trip in clip_trips(map, popdat, huge_map, only_passthrough_trips, timer) {
242 let idx = individ_trips.len();
243 individ_trips.push(Some(IndividTrip::new(
244 trip.orig.depart_at,
245 trip.orig.purpose,
246 trip.from,
247 trip.to,
248 trip.orig.mode,
249 )));
250 trips_per_person.insert(trip.orig.person, (trip.orig.seq, idx));
251 }
252 info!(
253 "{} clipped trips, over {} people",
254 prettyprint_usize(individ_trips.len()),
255 prettyprint_usize(trips_per_person.len())
256 );
257
258 let mut people = Vec::new();
259 for (orig_id, seq_trips) in trips_per_person.consume() {
260 let mut trips = Vec::new();
261 for (_, idx) in seq_trips {
262 trips.push(individ_trips[idx].take().unwrap());
263 }
264 trips.sort_by_key(|t| t.depart);
267 for pair in trips.windows(2) {
269 let destination = &pair[0].destination;
270 let origin = &pair[1].origin;
271 if destination != origin {
272 warn!(
273 "Skipping {:?}, with adjacent trips that warp from {:?} to {:?}",
274 orig_id, destination, origin
275 );
276 continue;
277 }
278 }
279
280 people.push(PersonSpec {
281 orig_id: Some(orig_id),
282 trips,
283 });
284 }
285 for maybe_t in individ_trips {
286 if maybe_t.is_some() {
287 panic!("Some IndividTrip wasn't associated with a Person?!");
288 }
289 }
290
291 Scenario {
292 scenario_name: scenario_name.to_string(),
293 map_name: map.get_name().clone(),
294 people,
295 only_seed_buses: None,
296 }
297 .remove_weird_schedules(true)
298}