1 use crate::extract::OsmExtract;
2 use abstutil::{Counter, Timer};
3 use geom::{Distance, HashablePt2D, Pt2D};
4 use map_model::raw::{OriginalRoad, RawIntersection, RawMap};
5 use map_model::{osm, IntersectionType, NamePerLanguage};
6 use std::collections::HashMap;
7 
8 // Returns amenities and a mapping of all points to split road. (Some internal points on roads are
9 // removed, so this mapping isn't redundant.)
split_up_roads( map: &mut RawMap, mut input: OsmExtract, timer: &mut Timer, ) -> ( Vec<(Pt2D, NamePerLanguage, String)>, HashMap<HashablePt2D, OriginalRoad>, )10 pub fn split_up_roads(
11     map: &mut RawMap,
12     mut input: OsmExtract,
13     timer: &mut Timer,
14 ) -> (
15     Vec<(Pt2D, NamePerLanguage, String)>,
16     HashMap<HashablePt2D, OriginalRoad>,
17 ) {
18     timer.start("splitting up roads");
19 
20     let mut pt_to_intersection: HashMap<HashablePt2D, osm::NodeID> = HashMap::new();
21     let mut counts_per_pt = Counter::new();
22     for (_, r) in &input.roads {
23         for (idx, raw_pt) in r.center_points.iter().enumerate() {
24             let pt = raw_pt.to_hashable();
25             let count = counts_per_pt.inc(pt);
26 
27             // All start and endpoints of ways are also intersections.
28             if count == 2 || idx == 0 || idx == r.center_points.len() - 1 {
29                 if !pt_to_intersection.contains_key(&pt) {
30                     let id = input.osm_node_ids[&pt];
31                     pt_to_intersection.insert(pt, id);
32                 }
33             }
34         }
35     }
36 
37     for (pt, id) in &pt_to_intersection {
38         map.intersections.insert(
39             *id,
40             RawIntersection {
41                 point: pt.to_pt2d(),
42                 intersection_type: if input.traffic_signals.remove(pt).is_some() {
43                     IntersectionType::TrafficSignal
44                 } else {
45                     IntersectionType::StopSign
46                 },
47                 // Filled out later
48                 elevation: Distance::ZERO,
49             },
50         );
51     }
52 
53     let mut pt_to_road: HashMap<HashablePt2D, OriginalRoad> = HashMap::new();
54 
55     // Now actually split up the roads based on the intersections
56     timer.start_iter("split roads", input.roads.len());
57     for (osm_way_id, orig_road) in &input.roads {
58         timer.next();
59         let mut r = orig_road.clone();
60         let mut pts = Vec::new();
61         let endpt1 = pt_to_intersection[&orig_road.center_points[0].to_hashable()];
62         let endpt2 = pt_to_intersection[&orig_road.center_points.last().unwrap().to_hashable()];
63         let mut i1 = endpt1;
64 
65         for pt in &orig_road.center_points {
66             pts.push(*pt);
67             if pts.len() == 1 {
68                 continue;
69             }
70             if let Some(i2) = pt_to_intersection.get(&pt.to_hashable()) {
71                 if i1 == endpt1 {
72                     r.osm_tags
73                         .insert(osm::ENDPT_BACK.to_string(), "true".to_string());
74                 }
75                 if *i2 == endpt2 {
76                     r.osm_tags
77                         .insert(osm::ENDPT_FWD.to_string(), "true".to_string());
78                 }
79                 let id = OriginalRoad {
80                     osm_way_id: *osm_way_id,
81                     i1,
82                     i2: *i2,
83                 };
84                 for pt in &pts {
85                     pt_to_road.insert(pt.to_hashable(), id);
86                 }
87 
88                 r.center_points = dedupe_angles(std::mem::replace(&mut pts, Vec::new()));
89                 // Start a new road
90                 map.roads.insert(id, r.clone());
91                 r.osm_tags.remove(osm::ENDPT_FWD);
92                 r.osm_tags.remove(osm::ENDPT_BACK);
93                 i1 = *i2;
94                 pts.push(*pt);
95             }
96         }
97         assert!(pts.len() == 1);
98     }
99 
100     // Resolve simple turn restrictions (via a node)
101     let mut restrictions = Vec::new();
102     for (restriction, from_osm, via_osm, to_osm) in input.simple_turn_restrictions {
103         let roads = map.roads_per_intersection(via_osm);
104         // If some of the roads are missing, they were likely filtered out -- usually service
105         // roads.
106         if let (Some(from), Some(to)) = (
107             roads.iter().find(|r| r.osm_way_id == from_osm),
108             roads.iter().find(|r| r.osm_way_id == to_osm),
109         ) {
110             restrictions.push((*from, restriction, *to));
111         }
112     }
113     for (from, rt, to) in restrictions {
114         map.roads
115             .get_mut(&from)
116             .unwrap()
117             .turn_restrictions
118             .push((rt, to));
119     }
120 
121     // Resolve complicated turn restrictions (via a way). TODO Only handle via ways immediately
122     // connected to both roads, for now
123     let mut complicated_restrictions = Vec::new();
124     for (rel_osm, from_osm, via_osm, to_osm) in input.complicated_turn_restrictions {
125         let via_candidates: Vec<OriginalRoad> = map
126             .roads
127             .keys()
128             .filter(|r| r.osm_way_id == via_osm)
129             .cloned()
130             .collect();
131         if via_candidates.len() != 1 {
132             timer.warn(format!(
133                 "Couldn't resolve turn restriction from way {} to way {} via way {}. Candidate \
134                  roads for via: {:?}. See {}",
135                 from_osm, to_osm, via_osm, via_candidates, rel_osm
136             ));
137             continue;
138         }
139         let via = via_candidates[0];
140 
141         let maybe_from = map
142             .roads_per_intersection(via.i1)
143             .into_iter()
144             .chain(map.roads_per_intersection(via.i2).into_iter())
145             .find(|r| r.osm_way_id == from_osm);
146         let maybe_to = map
147             .roads_per_intersection(via.i1)
148             .into_iter()
149             .chain(map.roads_per_intersection(via.i2).into_iter())
150             .find(|r| r.osm_way_id == to_osm);
151         match (maybe_from, maybe_to) {
152             (Some(from), Some(to)) => {
153                 complicated_restrictions.push((from, via, to));
154             }
155             _ => {
156                 timer.warn(format!(
157                     "Couldn't resolve turn restriction from {} to {} via {:?}",
158                     from_osm, to_osm, via
159                 ));
160             }
161         }
162     }
163     for (from, via, to) in complicated_restrictions {
164         map.roads
165             .get_mut(&from)
166             .unwrap()
167             .complicated_turn_restrictions
168             .push((via, to));
169     }
170 
171     timer.start("match traffic signals to intersections");
172     // Handle traffic signals tagged on incoming ways and not at intersections
173     // (https://wiki.openstreetmap.org/wiki/Tag:highway=traffic%20signals?uselang=en#Tag_all_incoming_ways).
174     let mut pt_to_road: HashMap<HashablePt2D, OriginalRoad> = HashMap::new();
175     for (id, r) in &map.roads {
176         for (idx, pt) in r.center_points.iter().enumerate() {
177             if idx != 0 && idx != r.center_points.len() - 1 {
178                 pt_to_road.insert(pt.to_hashable(), *id);
179             }
180         }
181     }
182     for (pt, forwards) in input.traffic_signals {
183         if let Some(r) = pt_to_road.get(&pt) {
184             // Example: https://www.openstreetmap.org/node/26734224
185             if !map.roads[r].osm_tags.is(osm::HIGHWAY, "construction") {
186                 let i = if forwards { r.i2 } else { r.i1 };
187                 map.intersections.get_mut(&i).unwrap().intersection_type =
188                     IntersectionType::TrafficSignal;
189             }
190         }
191     }
192     timer.stop("match traffic signals to intersections");
193 
194     timer.stop("splitting up roads");
195     (input.amenities, pt_to_road)
196 }
197 
198 // TODO Consider doing this in PolyLine::new always. extend() there does this too.
dedupe_angles(pts: Vec<Pt2D>) -> Vec<Pt2D>199 fn dedupe_angles(pts: Vec<Pt2D>) -> Vec<Pt2D> {
200     let mut result = Vec::new();
201     for (idx, pt) in pts.into_iter().enumerate() {
202         let l = result.len();
203         if idx == 0 || idx == 1 {
204             result.push(pt);
205         } else if result[l - 2]
206             .angle_to(result[l - 1])
207             .approx_eq(result[l - 1].angle_to(pt), 0.1)
208         {
209             result.pop();
210             result.push(pt);
211         } else {
212             result.push(pt);
213         }
214     }
215     result
216 }
217