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