diff --git a/osm2streets/src/geometry/algorithm.rs b/osm2streets/src/geometry/algorithm.rs index 62117c26..86122396 100644 --- a/osm2streets/src/geometry/algorithm.rs +++ b/osm2streets/src/geometry/algorithm.rs @@ -32,9 +32,9 @@ pub fn intersection_polygon( for road in roads.values_mut() { if let Some(endpt) = trim_roads_for_merging.get(&(road.id, road.src_i == intersection_id)) { if road.src_i == intersection_id { - match road.center_pts.safe_get_slice_starting_at(*endpt) { + match road.center_line.safe_get_slice_starting_at(*endpt) { Some(pl) => { - road.center_pts = pl; + road.center_line = pl; } None => { error!("{}'s trimmed points start past the endpt {endpt}", road.id); @@ -44,9 +44,9 @@ pub fn intersection_polygon( } } else { assert_eq!(road.dst_i, intersection_id); - match road.center_pts.safe_get_slice_ending_at(*endpt) { + match road.center_line.safe_get_slice_ending_at(*endpt) { Some(pl) => { - road.center_pts = pl; + road.center_line = pl; } None => { error!("{}'s trimmed points end before the endpt {endpt}", road.id); @@ -61,16 +61,16 @@ pub fn intersection_polygon( for id in sorted_roads { let road = &roads[&id]; let center_pl = if road.src_i == intersection_id { - road.center_pts.reversed() + road.center_line.reversed() } else if road.dst_i == intersection_id { - road.center_pts.clone() + road.center_line.clone() } else { panic!("Incident road {id} doesn't have an endpoint at {intersection_id}"); }; road_lines.push(RoadLine { id, - fwd_pl: center_pl.shift_right(road.half_width)?, - back_pl: center_pl.shift_left(road.half_width)?, + fwd_pl: center_pl.shift_right(road.half_width())?, + back_pl: center_pl.shift_left(road.half_width())?, }); } @@ -135,9 +135,9 @@ fn generalized_trim_back( for (r1, pl1) in &road_lines { // road_center ends at the intersection. let road_center = if roads[r1].dst_i == i { - roads[r1].center_pts.clone() + roads[r1].center_line.clone() } else { - roads[r1].center_pts.reversed() + roads[r1].center_line.reversed() }; // Always trim back a minimum amount, if possible. @@ -239,7 +239,7 @@ fn generalized_trim_back( let adj_back_pl = &wraparound_get(input_road_lines, idx + 1).fwd_pl; let adj_fwd_pl = &wraparound_get(input_road_lines, idx - 1).back_pl; - roads.get_mut(&id).unwrap().center_pts = new_road_centers[&id].clone(); + roads.get_mut(&id).unwrap().center_line = new_road_centers[&id].clone(); let r = &roads[&id]; // Include collisions between polylines of adjacent roads, so the polygon doesn't cover area @@ -264,11 +264,11 @@ fn generalized_trim_back( // Shift those final centers out again to find the main endpoints for the polygon. if r.dst_i == i { - endpoints.push(r.center_pts.shift_right(r.half_width)?.last_pt()); - endpoints.push(r.center_pts.shift_left(r.half_width)?.last_pt()); + endpoints.push(r.center_line.shift_right(r.half_width())?.last_pt()); + endpoints.push(r.center_line.shift_left(r.half_width())?.last_pt()); } else { - endpoints.push(r.center_pts.shift_left(r.half_width)?.first_pt()); - endpoints.push(r.center_pts.shift_right(r.half_width)?.first_pt()); + endpoints.push(r.center_line.shift_left(r.half_width())?.first_pt()); + endpoints.push(r.center_line.shift_right(r.half_width())?.first_pt()); } if back_pl.length() >= EPSILON_DIST * 3.0 && adj_back_pl.length() >= EPSILON_DIST * 3.0 { @@ -319,9 +319,7 @@ fn generalized_trim_back( // TODO We always do this. Maybe Results has the InputRoad and we just work in-place for (id, r) in roads { - results - .trimmed_center_pts - .insert(id, (r.center_pts, r.half_width)); + results.trimmed_center_pts.insert(id, r.center_line); } Ok(results) } @@ -336,11 +334,11 @@ fn pretrimmed_geometry( let r = &roads[&r.id]; // Shift those final centers out again to find the main endpoints for the polygon. if r.dst_i == results.intersection_id { - endpoints.push(r.center_pts.shift_right(r.half_width)?.last_pt()); - endpoints.push(r.center_pts.shift_left(r.half_width)?.last_pt()); + endpoints.push(r.center_line.shift_right(r.half_width())?.last_pt()); + endpoints.push(r.center_line.shift_left(r.half_width())?.last_pt()); } else { - endpoints.push(r.center_pts.shift_left(r.half_width)?.first_pt()); - endpoints.push(r.center_pts.shift_right(r.half_width)?.first_pt()); + endpoints.push(r.center_line.shift_left(r.half_width())?.first_pt()); + endpoints.push(r.center_line.shift_right(r.half_width())?.first_pt()); } } @@ -351,9 +349,7 @@ fn pretrimmed_geometry( )))? .into_polygon(); for (id, r) in roads { - results - .trimmed_center_pts - .insert(id, (r.center_pts, r.half_width)); + results.trimmed_center_pts.insert(id, r.center_line); } Ok(results) } @@ -377,19 +373,19 @@ fn deadend( let r = roads.get_mut(&id).unwrap(); let len_with_buffer = len + 3.0 * EPSILON_DIST; - let trimmed = if r.center_pts.length() >= len_with_buffer { + let trimmed = if r.center_line.length() >= len_with_buffer { if r.src_i == results.intersection_id { - r.center_pts = r.center_pts.exact_slice(len, r.center_pts.length()); + r.center_line = r.center_line.exact_slice(len, r.center_line.length()); } else { - r.center_pts = r - .center_pts - .exact_slice(Distance::ZERO, r.center_pts.length() - len); + r.center_line = r + .center_line + .exact_slice(Distance::ZERO, r.center_line.length() - len); } - r.center_pts.clone() + r.center_line.clone() } else if r.src_i == results.intersection_id { - r.center_pts.extend_to_length(len_with_buffer) + r.center_line.extend_to_length(len_with_buffer) } else { - r.center_pts + r.center_line .reversed() .extend_to_length(len_with_buffer) .reversed() @@ -400,19 +396,17 @@ fn deadend( // TODO Refactor with generalized_trim_back. let mut endpts = vec![pl_b.last_pt(), pl_a.last_pt()]; if r.dst_i == results.intersection_id { - endpts.push(trimmed.shift_right(r.half_width)?.last_pt()); - endpts.push(trimmed.shift_left(r.half_width)?.last_pt()); + endpts.push(trimmed.shift_right(r.half_width())?.last_pt()); + endpts.push(trimmed.shift_left(r.half_width())?.last_pt()); } else { - endpts.push(trimmed.shift_left(r.half_width)?.first_pt()); - endpts.push(trimmed.shift_right(r.half_width)?.first_pt()); + endpts.push(trimmed.shift_left(r.half_width())?.first_pt()); + endpts.push(trimmed.shift_right(r.half_width())?.first_pt()); } endpts.dedup(); results.intersection_polygon = Ring::must_new(close_off_polygon(endpts)).into_polygon(); for (id, r) in roads { - results - .trimmed_center_pts - .insert(id, (r.center_pts, r.half_width)); + results.trimmed_center_pts.insert(id, r.center_line); } Ok(results) } @@ -468,9 +462,9 @@ fn on_off_ramp( for r in road_lines { let road = &roads[&r.id]; let center = if road.dst_i == results.intersection_id { - road.center_pts.clone() + road.center_line.clone() } else { - road.center_pts.reversed() + road.center_line.reversed() }; pieces.push(Piece { id: road.id, @@ -482,7 +476,12 @@ fn on_off_ramp( } // Break ties by preferring the outbound roads for thin - pieces.sort_by_key(|r| (roads[&r.id].half_width, r.dst_i == results.intersection_id)); + pieces.sort_by_key(|r| { + ( + roads[&r.id].half_width(), + r.dst_i == results.intersection_id, + ) + }); let thick1 = pieces.pop().unwrap(); let thick2 = pieces.pop().unwrap(); let thin = pieces.pop().unwrap(); @@ -550,21 +549,21 @@ fn on_off_ramp( if thin.dst_i != results.intersection_id { trimmed_thin = trimmed_thin.reversed(); } - roads.get_mut(&thin.id).unwrap().center_pts = trimmed_thin; + roads.get_mut(&thin.id).unwrap().center_line = trimmed_thin; // Trim the thick extra ends at the intersection let extra = if roads[&thick_id].dst_i == results.intersection_id { roads[&thick_id] - .center_pts + .center_line .get_slice_starting_at(trimmed_thick.last_pt())? } else { trimmed_thick = trimmed_thick.reversed(); roads[&thick_id] - .center_pts + .center_line .get_slice_ending_at(trimmed_thick.first_pt())? .reversed() }; - roads.get_mut(&thick_id).unwrap().center_pts = trimmed_thick; + roads.get_mut(&thick_id).unwrap().center_line = trimmed_thick; // Give the merge point some length if extra.length() <= 2.0 * DEGENERATE_INTERSECTION_HALF_LENGTH + 3.0 * EPSILON_DIST { return None; @@ -580,9 +579,9 @@ fn on_off_ramp( }) .unwrap(); if other.dst_i == results.intersection_id { - other.center_pts = other.center_pts.clone().extend(extra.reversed()).ok()?; + other.center_line = other.center_line.clone().extend(extra.reversed()).ok()?; } else { - other.center_pts = extra.extend(other.center_pts.clone()).ok()?; + other.center_line = extra.extend(other.center_line.clone()).ok()?; } } @@ -592,11 +591,11 @@ fn on_off_ramp( let r = &roads[&id]; // Shift those final centers out again to find the main endpoints for the polygon. if r.dst_i == results.intersection_id { - endpoints.push(r.center_pts.shift_right(r.half_width).ok()?.last_pt()); - endpoints.push(r.center_pts.shift_left(r.half_width).ok()?.last_pt()); + endpoints.push(r.center_line.shift_right(r.half_width()).ok()?.last_pt()); + endpoints.push(r.center_line.shift_left(r.half_width()).ok()?.last_pt()); } else { - endpoints.push(r.center_pts.shift_left(r.half_width).ok()?.first_pt()); - endpoints.push(r.center_pts.shift_right(r.half_width).ok()?.first_pt()); + endpoints.push(r.center_line.shift_left(r.half_width()).ok()?.first_pt()); + endpoints.push(r.center_line.shift_right(r.half_width()).ok()?.first_pt()); } } /*for (idx, pt) in endpoints.iter().enumerate() { @@ -610,9 +609,7 @@ fn on_off_ramp( endpoints.dedup(); results.intersection_polygon = Ring::must_new(close_off_polygon(endpoints)).into_polygon(); for (id, r) in roads { - results - .trimmed_center_pts - .insert(id, (r.center_pts, r.half_width)); + results.trimmed_center_pts.insert(id, r.center_line); } Some(results) } diff --git a/osm2streets/src/geometry/mod.rs b/osm2streets/src/geometry/mod.rs index 0978b45b..c9341ffd 100644 --- a/osm2streets/src/geometry/mod.rs +++ b/osm2streets/src/geometry/mod.rs @@ -26,18 +26,23 @@ pub struct InputRoad { pub src_i: IntersectionID, pub dst_i: IntersectionID, /// The true center of the road, including sidewalks. The input is untrimmed when called on the - /// first endpoint, then trimmed on that one side when called on th second endpoint. - pub center_pts: PolyLine, - pub half_width: Distance, + /// first endpoint, then trimmed on that first side when called on the second endpoint. + pub center_line: PolyLine, + pub total_width: Distance, pub highway_type: String, } +impl InputRoad { + pub fn half_width(&self) -> Distance { + self.total_width / 2.0 + } +} + #[derive(Clone)] pub struct Results { pub intersection_id: IntersectionID, pub intersection_polygon: Polygon, - /// Road -> (trimmed center line, half width) - pub trimmed_center_pts: BTreeMap, + pub trimmed_center_pts: BTreeMap, /// Extra polygons with labels to debug the algorithm pub debug: Vec<(String, Polygon)>, } diff --git a/osm2streets/src/lib.rs b/osm2streets/src/lib.rs index 36b0be61..9bc19ef5 100644 --- a/osm2streets/src/lib.rs +++ b/osm2streets/src/lib.rs @@ -156,7 +156,7 @@ impl StreetNetwork { for road in self.roads_per_intersection(endpts[0]) { // trimmed_center_line hasn't been initialized yet, so override this let mut input = road.to_input_road(); - input.center_pts = road.untrimmed_road_geometry(self.config.driving_side); + input.center_line = road.untrimmed_road_geometry(self.config.driving_side); input_roads.push(input); } let mut results = intersection_polygon( @@ -165,7 +165,7 @@ impl StreetNetwork { // TODO Not sure if we should use this or not &BTreeMap::new(), )?; - results.trimmed_center_pts.remove(&road_id).unwrap().0 + results.trimmed_center_pts.remove(&road_id).unwrap() }; // Now the second @@ -174,9 +174,9 @@ impl StreetNetwork { for road in self.roads_per_intersection(endpts[1]) { let mut input = road.to_input_road(); if road.id == road_id { - input.center_pts = trimmed_center_pts.clone(); + input.center_line = trimmed_center_pts.clone(); } else { - input.center_pts = road.untrimmed_road_geometry(self.config.driving_side); + input.center_line = road.untrimmed_road_geometry(self.config.driving_side); } input_roads.push(input); } @@ -186,7 +186,7 @@ impl StreetNetwork { // TODO Not sure if we should use this or not &BTreeMap::new(), )?; - Ok(results.trimmed_center_pts.remove(&road_id).unwrap().0) + Ok(results.trimmed_center_pts.remove(&road_id).unwrap()) } } diff --git a/osm2streets/src/road.rs b/osm2streets/src/road.rs index 6739eb26..a837a8eb 100644 --- a/osm2streets/src/road.rs +++ b/osm2streets/src/road.rs @@ -242,6 +242,9 @@ impl Road { pub fn total_width(&self) -> Distance { self.lane_specs_ltr.iter().map(|l| l.width).sum() } + pub fn half_width(&self) -> Distance { + self.total_width() / 2.0 + } /// Calculates the number of (forward, both_ways, backward) lanes. The order of the lanes /// doesn't matter. @@ -271,7 +274,7 @@ impl Road { use RoadPosition::*; match position { - FullWidthCenter => self.total_width() / 2.0, + FullWidthCenter => self.half_width(), Center => { // Need to find the midpoint between the first and last occurrence of any roadway. let mut left_buffer = Distance::ZERO; @@ -373,7 +376,7 @@ impl Road { } warn!("named lane doesn't exist"); - self.total_width() / 2.0 + self.half_width() } } } @@ -427,8 +430,8 @@ impl Road { id: self.id, src_i: self.src_i, dst_i: self.dst_i, - center_pts: self.center_line.clone(), - half_width: self.total_width() / 2.0, + center_line: self.center_line.clone(), + total_width: self.total_width(), highway_type: self.highway_type.clone(), } } @@ -469,12 +472,12 @@ impl RoadEdge { for road in sorted_roads { let mut left = RoadEdge { road: road.id, - pl: road.center_line.must_shift_left(road.total_width() / 2.0), + pl: road.center_line.must_shift_left(road.half_width()), lane: road.lane_specs_ltr[0].clone(), }; let mut right = RoadEdge { road: road.id, - pl: road.center_line.must_shift_right(road.total_width() / 2.0), + pl: road.center_line.must_shift_right(road.half_width()), lane: road.lane_specs_ltr.last().unwrap().clone(), }; if road.dst_i == i { diff --git a/osm2streets/src/transform/intersection_geometry.rs b/osm2streets/src/transform/intersection_geometry.rs index 93671cc9..aee276b5 100644 --- a/osm2streets/src/transform/intersection_geometry.rs +++ b/osm2streets/src/transform/intersection_geometry.rs @@ -22,7 +22,7 @@ pub fn generate(streets: &mut StreetNetwork, timer: &mut Timer) { match crate::intersection_polygon(i.id, input_roads, &i.trim_roads_for_merging) { Ok(results) => { set_polygons.push((i.id, results.intersection_polygon)); - for (r, (pl, _)) in results.trimmed_center_pts { + for (r, pl) in results.trimmed_center_pts { streets.roads.get_mut(&r).unwrap().center_line = pl; } } @@ -99,7 +99,7 @@ fn fix_map_edges(streets: &mut StreetNetwork) { ) .unwrap(); set_polygons.push((i.id, results.intersection_polygon)); - for (r, (pl, _)) in results.trimmed_center_pts { + for (r, pl) in results.trimmed_center_pts { streets.roads.get_mut(&r).unwrap().center_line = pl; } info!(