map_model/make/
mod.rs

1//! See <https://a-b-street.github.io/docs/tech/map/importing/index.html> for an overview. This module
2//! covers the RawMap->Map stage.
3
4use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
5use std::sync::{Arc, RwLock};
6
7use structopt::StructOpt;
8
9use abstutil::{MultiMap, Tags, Timer};
10use geom::{
11    Distance, FindClosest, HashablePt2D, Line, PolyLine, Polygon, Pt2D, Speed, EPSILON_DIST,
12};
13use osm2streets::Transformation;
14use raw_map::RawMap;
15
16pub use self::parking_lots::snap_driveway;
17use crate::pathfind::{CreateEngine, Pathfinder};
18use crate::{
19    connectivity, osm, AccessRestrictions, Area, AreaID, ControlStopSign, ControlTrafficSignal,
20    Intersection, IntersectionControl, IntersectionID, IntersectionKind, Lane, LaneID, Map,
21    MapEdits, OriginalRoad, PathConstraints, Position, Road, RoadID, RoutingParams, Zone,
22};
23
24mod bridges;
25mod buildings;
26mod parking_lots;
27pub mod traffic_signals;
28pub mod transit;
29pub mod turns;
30mod walking_turns;
31
32/// Options for converting RawMaps to Maps.
33#[derive(Clone, Default, StructOpt)]
34pub struct RawToMapOptions {
35    /// Should contraction hierarchies for pathfinding be built? They're slow to build, but without
36    /// them, pathfinding on the map later will be very slow.
37    #[structopt(long)]
38    pub skip_ch: bool,
39    /// Preserve all OSM tags for buildings, increasing the final file size substantially.
40    #[structopt(long)]
41    pub keep_bldg_tags: bool,
42}
43
44impl Map {
45    pub fn create_from_raw(mut raw: RawMap, opts: RawToMapOptions, timer: &mut Timer) -> Map {
46        raw.streets
47            .apply_transformations(Transformation::abstreet(), timer);
48
49        let mut map = Map {
50            roads: Vec::new(),
51            intersections: Vec::new(),
52            intersection_quad_tree: Arc::new(RwLock::new(None)),
53            buildings: Vec::new(),
54            transit_stops: BTreeMap::new(),
55            transit_routes: Vec::new(),
56            areas: Vec::new(),
57            parking_lots: Vec::new(),
58            zones: Vec::new(),
59            census_zones: raw.census_zones.clone(),
60            extra_pois: raw.extra_pois.clone(),
61            boundary_polygon: raw.streets.boundary_polygon.clone(),
62            stop_signs: BTreeMap::new(),
63            traffic_signals: BTreeMap::new(),
64            bus_routes_on_roads: std::mem::take(&mut raw.bus_routes_on_roads),
65            gps_bounds: raw.streets.gps_bounds.clone(),
66            bounds: raw.streets.gps_bounds.to_bounds(),
67            config: raw.streets.config.clone(),
68            pathfinder: Pathfinder::empty(),
69            pathfinder_dirty: false,
70            routing_params: RoutingParams::default(),
71            name: raw.name.clone(),
72            edits: MapEdits::new(),
73            edits_generation: 0,
74            road_to_buildings: MultiMap::new(),
75        };
76        map.edits = map.new_edits();
77
78        let road_id_mapping: BTreeMap<osm2streets::RoadID, RoadID> = raw
79            .streets
80            .roads
81            .keys()
82            .enumerate()
83            .map(|(idx, id)| (*id, RoadID(idx)))
84            .collect();
85        let mut intersection_id_mapping: BTreeMap<osm2streets::IntersectionID, IntersectionID> =
86            BTreeMap::new();
87        for (idx, i) in raw.streets.intersections.values().enumerate() {
88            let id = IntersectionID(idx);
89            map.intersections.push(Intersection {
90                id,
91                polygon: i.polygon.clone(),
92                turns: Vec::new(),
93                movements: BTreeMap::new(),
94                elevation: raw.elevation_per_intersection[&i.id],
95                // Might change later
96                kind: i.kind,
97                control: match i.control {
98                    // Nothing in A/B Street handles uncontrolled intersections yet
99                    IntersectionControl::Uncontrolled => IntersectionControl::Signed,
100                    x => x,
101                },
102                // TODO Hack. Handle multiple OSM IDs everywhere instead
103                orig_id: i
104                    .osm_ids
105                    .get(0)
106                    .cloned()
107                    .unwrap_or(osm::NodeID(-1 * (map.intersections.len() as i64))),
108                incoming_lanes: Vec::new(),
109                outgoing_lanes: Vec::new(),
110                roads: i.roads.iter().map(|id| road_id_mapping[id]).collect(),
111                modal_filter: None,
112                merged: !raw.streets.intersections[&i.id]
113                    .trim_roads_for_merging
114                    .is_empty(),
115            });
116            intersection_id_mapping.insert(i.id, id);
117        }
118
119        timer.start_iter("expand roads to lanes", raw.streets.roads.len());
120        for r in raw.streets.roads.values_mut() {
121            timer.next();
122
123            let road_id = road_id_mapping[&r.id];
124            let i1 = intersection_id_mapping[&r.src_i];
125            let i2 = intersection_id_mapping[&r.dst_i];
126
127            let extra = &raw.extra_road_data[&r.id];
128            let barrier_nodes = snap_nodes_to_line(&extra.barrier_nodes, &r.center_line);
129            let crossing_nodes =
130                snap_nodes_with_data_to_line(&extra.crossing_nodes, &r.center_line);
131
132            // TODO Hack. Roads and intersections each may have ZERO or more OSM IDs.
133            let orig_id = OriginalRoad {
134                osm_way_id: r
135                    .osm_ids
136                    .get(0)
137                    .cloned()
138                    .unwrap_or(osm::WayID(-1 * (map.roads.len() as i64))),
139                i1: map.intersections[i1.0].orig_id,
140                i2: map.intersections[i2.0].orig_id,
141            };
142
143            let mut road = Road {
144                id: road_id,
145                // Arbitrarily remember OSM tags from one of the ways
146                // TODO If this road was introduced synthetically, we'll have empty tags, which
147                // might break various downstream bits of code
148                osm_tags: if let Some(id) = r.osm_ids.get(0) {
149                    raw.osm_tags[id].clone()
150                } else {
151                    Tags::empty()
152                },
153                orig_id,
154                turn_restrictions: r
155                    .turn_restrictions
156                    .iter()
157                    .filter_map(|(rt, to)| {
158                        // Missing roads are filtered (like some service roads) or clipped out
159                        road_id_mapping.get(to).map(|to| (*rt, *to))
160                    })
161                    .collect(),
162                complicated_turn_restrictions: r
163                    .complicated_turn_restrictions
164                    .iter()
165                    .filter_map(|(via, to)| {
166                        if let (Some(via), Some(to)) =
167                            (road_id_mapping.get(via), road_id_mapping.get(to))
168                        {
169                            Some((*via, *to))
170                        } else {
171                            warn!(
172                                "Complicated turn restriction from {} has invalid via {} or dst {}",
173                                r.id, via, to
174                            );
175                            None
176                        }
177                    })
178                    .collect(),
179                lanes: Vec::new(),
180                center_pts: r.center_line.clone(),
181                untrimmed_center_pts: r.get_untrimmed_center_line(raw.streets.config.driving_side),
182                trim_start: r.trim_start,
183                trim_end: r.trim_end,
184                src_i: i1,
185                dst_i: i2,
186                speed_limit: Speed::ZERO,
187                zorder: r.layer,
188                access_restrictions: AccessRestrictions::new(),
189                percent_incline: extra.percent_incline,
190                crosswalk_forward: extra.crosswalk_forward,
191                crosswalk_backward: extra.crosswalk_backward,
192                transit_stops: BTreeSet::new(),
193                modal_filter: None,
194                barrier_nodes,
195                crossing_nodes,
196                crossings: Vec::new(),
197            };
198            road.speed_limit = road.speed_limit_from_osm();
199            road.access_restrictions = road.access_restrictions_from_osm();
200
201            road.recreate_lanes(r.lane_specs_ltr.clone());
202            for lane in &road.lanes {
203                map.intersections[lane.src_i.0].outgoing_lanes.push(lane.id);
204                map.intersections[lane.dst_i.0].incoming_lanes.push(lane.id);
205            }
206
207            map.roads.push(road);
208        }
209
210        for i in map.intersections.iter_mut() {
211            if i.is_border() && i.roads.len() != 1 {
212                // i.orig_id may be synthetic and useless, so also print OSM links of the roads
213                let border_roads = i
214                    .roads
215                    .iter()
216                    .map(|r| map.roads[r.0].orig_id.osm_way_id.to_string())
217                    .collect::<Vec<_>>();
218                panic!(
219                    "{} ({}) is a border, but is connected to >1 road: {:?}",
220                    i.id, i.orig_id, border_roads
221                );
222            }
223            if i.control == IntersectionControl::Signalled {
224                let mut ok = true;
225                for r in &i.roads {
226                    let road = &map.roads[r.0];
227                    // Skip signals only connected to roads under construction or purely to control
228                    // light rail tracks.
229                    if road.osm_tags.is(osm::HIGHWAY, "construction") || road.is_light_rail() {
230                        ok = false;
231                        break;
232                    }
233                    // Skip signals that likely don't have correct intersection geometry
234                    if road.trim_start == Distance::ZERO || road.trim_end == Distance::ZERO {
235                        ok = false;
236                        break;
237                    }
238                }
239                if !ok {
240                    i.control = IntersectionControl::Signed;
241                }
242            }
243        }
244
245        let mut all_turns = Vec::new();
246        let mut connectivity_problems = 0;
247        for i in &map.intersections {
248            if i.is_border() || i.is_closed() {
249                continue;
250            }
251            if !i.is_footway(&map) && (i.incoming_lanes.is_empty() || i.outgoing_lanes.is_empty()) {
252                warn!("{} is orphaned!", i.orig_id);
253                continue;
254            }
255
256            let results = turns::make_all_turns(&map, i);
257            if turns::verify_vehicle_connectivity(&results, i, &map).is_err() {
258                connectivity_problems += 1;
259            }
260            all_turns.extend(results);
261        }
262        error!(
263            "{} total intersections have some connectivity problem",
264            connectivity_problems
265        );
266        for t in all_turns {
267            assert!(map.maybe_get_t(t.id).is_none());
268            if t.geom.length() < geom::EPSILON_DIST {
269                warn!("{} is a very short turn", t.id);
270            }
271            map.intersections[t.id.parent.0].turns.push(t);
272        }
273
274        timer.start("find blackholes");
275        for l in connectivity::find_scc(&map, PathConstraints::Car).1 {
276            map.mut_lane(l).driving_blackhole = true;
277        }
278        for l in connectivity::find_scc(&map, PathConstraints::Bike).1 {
279            map.mut_lane(l).biking_blackhole = true;
280        }
281        timer.stop("find blackholes");
282
283        map.buildings =
284            buildings::make_all_buildings(&raw.buildings, &map, opts.keep_bldg_tags, timer);
285
286        map.parking_lots = parking_lots::make_all_parking_lots(
287            &raw.parking_lots,
288            &raw.parking_aisles,
289            &map,
290            timer,
291        );
292
293        map.zones = Zone::make_all(&map);
294
295        for a in &raw.areas {
296            map.areas.push(Area {
297                id: AreaID(map.areas.len()),
298                area_type: a.area_type,
299                polygon: a.polygon.clone(),
300                osm_tags: a.osm_tags.clone(),
301                osm_id: Some(a.osm_id),
302            });
303        }
304
305        bridges::find_bridges(&mut map.roads, timer);
306
307        map.recalculate_all_movements(timer);
308
309        let mut stop_signs: BTreeMap<IntersectionID, ControlStopSign> = BTreeMap::new();
310        let mut traffic_signals: BTreeMap<IntersectionID, ControlTrafficSignal> = BTreeMap::new();
311        for i in &map.intersections {
312            if i.kind == IntersectionKind::MapEdge {
313                continue;
314            }
315            match i.control {
316                IntersectionControl::Signed | IntersectionControl::Uncontrolled => {
317                    stop_signs.insert(i.id, ControlStopSign::new(&map, i.id));
318                }
319                IntersectionControl::Signalled => {
320                    if i.movements.is_empty() {
321                        error!("Traffic signal at {} downgraded to stop sign, because it has no movements -- probably roads under construction", i.orig_id);
322                        stop_signs.insert(i.id, ControlStopSign::new(&map, i.id));
323                    } else {
324                        traffic_signals.insert(
325                            i.id,
326                            ControlTrafficSignal::get_possible_policies(&map, i.id)
327                                .remove(0)
328                                .1,
329                        );
330                    }
331                }
332                IntersectionControl::Construction => {}
333            };
334        }
335        map.stop_signs = stop_signs;
336        map.traffic_signals = traffic_signals;
337        // Fix up the type for any problematic traffic signals
338        for i in map.stop_signs.keys() {
339            map.intersections[i.0].control = IntersectionControl::Signed;
340        }
341
342        traffic_signals::synchronize(&mut map);
343
344        timer.start("setup pathfinding");
345        let engine = if opts.skip_ch {
346            CreateEngine::Dijkstra
347        } else {
348            CreateEngine::CH
349        };
350        map.pathfinder = Pathfinder::new(&map, map.routing_params().clone(), &engine, timer);
351        timer.stop("setup pathfinding");
352
353        transit::finalize_transit(&mut map, &raw, timer);
354        timer.start("setup pathfinding for people using transit");
355        let mut pathfinder = std::mem::replace(&mut map.pathfinder, Pathfinder::empty());
356        pathfinder.finalize_transit(&map, &engine);
357        map.pathfinder = pathfinder;
358        timer.stop("setup pathfinding for people using transit");
359
360        map
361    }
362}
363
364/// Snap points to an exact Position along the nearest lane. If the result doesn't contain a
365/// requested point, then there was no matching lane close enough.
366pub fn match_points_to_lanes<F: Fn(&Lane) -> bool>(
367    map: &Map,
368    pts: HashSet<HashablePt2D>,
369    filter: F,
370    buffer: Distance,
371    max_dist_away: Distance,
372    timer: &mut Timer,
373) -> HashMap<HashablePt2D, Position> {
374    if pts.is_empty() {
375        return HashMap::new();
376    }
377
378    let mut closest: FindClosest<LaneID> = FindClosest::new();
379    timer.start_iter("index lanes", map.all_lanes().count());
380    for l in map.all_lanes() {
381        timer.next();
382        if filter(l) && l.length() > (buffer + EPSILON_DIST) * 2.0 {
383            closest.add(
384                l.id,
385                l.lane_center_pts
386                    .exact_slice(buffer, l.length() - buffer)
387                    .points(),
388            );
389        }
390    }
391
392    // For each point, find the closest point to any lane, using the quadtree to prune the
393    // search.
394    timer
395        .parallelize(
396            "find closest lane point",
397            pts.into_iter().collect(),
398            |query_pt| {
399                if let Some((l, pt)) = closest.closest_pt(query_pt.to_pt2d(), max_dist_away) {
400                    if let Some(dist_along) = map.get_l(l).dist_along_of_point(pt) {
401                        Some((query_pt, Position::new(l, dist_along)))
402                    } else {
403                        panic!(
404                            "{} isn't on {} according to dist_along_of_point, even though \
405                             closest_point thinks it is.\n{}",
406                            pt,
407                            l,
408                            map.get_l(l).lane_center_pts
409                        );
410                    }
411                } else {
412                    None
413                }
414            },
415        )
416        .into_iter()
417        .flatten()
418        .collect()
419}
420
421/// Adjust the path to start on the polygon's border, not center.
422pub fn trim_path(poly: &Polygon, path: Line) -> Line {
423    for line in poly.get_outer_ring().points().windows(2) {
424        if let Ok(l1) = Line::new(line[0], line[1]) {
425            if let Some(hit) = l1.intersection(&path) {
426                if let Ok(l2) = Line::new(hit, path.pt2()) {
427                    return l2;
428                }
429            }
430        }
431    }
432    // Just give up
433    path
434}
435
436// TODO Duplicate code with below. One caller needs data, the other doesn't and adding fake data is
437// annoying
438fn snap_nodes_to_line(pts: &[Pt2D], pl: &PolyLine) -> Vec<Distance> {
439    let mut results = Vec::new();
440    for pt in pts {
441        let projected = pl.project_pt(*pt);
442        // TODO Check distance isn't too crazy? Not sure why it would be
443        if let Some((dist, _)) = pl.dist_along_of_point(projected) {
444            results.push(dist);
445        }
446    }
447    results
448}
449
450fn snap_nodes_with_data_to_line<T: Clone>(
451    input: &[(Pt2D, T)],
452    pl: &PolyLine,
453) -> Vec<(Distance, T)> {
454    let mut results = Vec::new();
455    for (pt, data) in input {
456        let projected = pl.project_pt(*pt);
457        // TODO Check distance isn't too crazy? Not sure why it would be
458        if let Some((dist, _)) = pl.dist_along_of_point(projected) {
459            results.push((dist, data.clone()));
460        }
461    }
462    results
463}