From c573f4676df8a032e8147a7ebda25b098e09038a Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Fri, 10 Mar 2023 11:48:45 +0000 Subject: [PATCH] - Remove connector segments immediately - Remember to regenerate intersection geometry --- osm2streets-js/src/lib.rs | 4 +- osm2streets/src/lib.rs | 12 ++-- .../src/transform/separate_cycletracks.rs | 57 ++++++++++++++++--- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/osm2streets-js/src/lib.rs b/osm2streets-js/src/lib.rs index 4077acc7..e7ad45c5 100644 --- a/osm2streets-js/src/lib.rs +++ b/osm2streets-js/src/lib.rs @@ -68,8 +68,8 @@ impl JsStreetNetwork { } if input.cycletrack_snapping_experiment { transformations.push(Transformation::SnapCycleways); - transformations.push(Transformation::TrimDeadendCycleways); - transformations.push(Transformation::CollapseDegenerateIntersections); + //transformations.push(Transformation::TrimDeadendCycleways); + //transformations.push(Transformation::CollapseDegenerateIntersections); } if input.debug_each_step { street_network.apply_transformations_stepwise_debugging(transformations, &mut timer); diff --git a/osm2streets/src/lib.rs b/osm2streets/src/lib.rs index 389d2383..a922bd33 100644 --- a/osm2streets/src/lib.rs +++ b/osm2streets/src/lib.rs @@ -176,15 +176,19 @@ impl StreetNetwork { pub(crate) fn debug_intersection>(&mut self, i: IntersectionID, label: I) { if let Some(step) = self.debug_steps.last_mut() { - step.points - .push((self.intersections[&i].polygon.center(), label.into())); + if let Some(intersection) = self.intersections.get(&i) { + step.points + .push((intersection.polygon.center(), label.into())); + } } } pub(crate) fn debug_road>(&mut self, r: RoadID, label: I) { if let Some(step) = self.debug_steps.last_mut() { - step.polylines - .push((self.roads[&r].center_line.clone(), label.into())); + if let Some(road) = self.roads.get(&r) { + step.polylines + .push((road.center_line.clone(), label.into())); + } } } diff --git a/osm2streets/src/transform/separate_cycletracks.rs b/osm2streets/src/transform/separate_cycletracks.rs index 21b5882b..a74c41e4 100644 --- a/osm2streets/src/transform/separate_cycletracks.rs +++ b/osm2streets/src/transform/separate_cycletracks.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeSet; + use geom::{Distance, PolyLine}; use crate::{BufferType, Direction, IntersectionID, LaneSpec, LaneType, RoadID, StreetNetwork}; @@ -34,6 +36,8 @@ struct Cycleway { main_road_src_i: IntersectionID, main_road_dst_i: IntersectionID, main_roads: Vec, + connector_src_i: Option, + connector_dst_i: Option, } impl Cycleway { @@ -44,6 +48,12 @@ impl Cycleway { for x in &self.main_roads { streets.debug_road(*x, format!("main road along {}", self.debug_idx)); } + if let Some(r) = self.connector_src_i { + streets.debug_road(r, format!("src_i connector of {}", self.debug_idx)); + } + if let Some(r) = self.connector_dst_i { + streets.debug_road(r, format!("dst_i connector of {}", self.debug_idx)); + } } } @@ -56,27 +66,28 @@ fn find_cycleways(streets: &StreetNetwork) -> Vec { // Look at other roads connected to both endpoints. One of them should be "very short." let mut main_road_endpoints = Vec::new(); for i in cycleway_road.endpoints() { - let mut candidates = Vec::new(); + let mut connector_candidates = Vec::new(); for road in streets.roads_per_intersection(i) { if road.id != cycleway_road.id && road.center_line.length() < SHORT_ROAD_THRESHOLD { - candidates.push(road.id); + connector_candidates.push(road.id); } } - if candidates.len() == 1 { - main_road_endpoints.push(streets.roads[&candidates[0]].other_side(i)); - } else if candidates.is_empty() { + if connector_candidates.len() == 1 { + let connector = connector_candidates[0]; + main_road_endpoints.push((streets.roads[&connector].other_side(i), Some(connector))); + } else if connector_candidates.is_empty() { // Maybe this intersection has been merged already. Use it directly. - main_road_endpoints.push(i); + main_road_endpoints.push((i, None)); } } if main_road_endpoints.len() == 2 { // Often the main road parallel to this cycleway segment is just one road, but it // might be more. - let main_road_src_i = main_road_endpoints[0]; - let main_road_dst_i = main_road_endpoints[1]; + let (main_road_src_i, connector_src_i) = main_road_endpoints[0]; + let (main_road_dst_i, connector_dst_i) = main_road_endpoints[1]; // It may be none at all, when the main road intersection gets merged if main_road_src_i == main_road_dst_i { continue; @@ -97,6 +108,8 @@ fn find_cycleways(streets: &StreetNetwork) -> Vec { main_road_src_i, main_road_dst_i, main_roads: path.into_iter().map(|(r, _)| r).collect(), + connector_src_i, + connector_dst_i, }); } } @@ -107,7 +120,12 @@ fn find_cycleways(streets: &StreetNetwork) -> Vec { fn snap(streets: &mut StreetNetwork, input: Cycleway) { // This analysis shouldn't modify other cycleways when it works on one - assert!(streets.roads.contains_key(&input.cycleway)); + //assert!(streets.roads.contains_key(&input.cycleway)); + // TODO Not true anymore; sometimes the short connector segment is a cycleway and matches stuff + // here incorrectly, but then a previous pass deletes it + if !streets.roads.contains_key(&input.cycleway) { + return; + } // Remove the cycleway, but remember the lanes it contained let mut cycleway_lanes = streets.remove_road(input.cycleway).lane_specs_ltr; @@ -141,6 +159,7 @@ fn snap(streets: &mut StreetNetwork, input: Cycleway) { // - Fixing the direction of the lanes // - Appending them on the left or right side (and "inside" the inferred sidewalk on the road) // - Inserting the buffer + let mut intersections = BTreeSet::new(); for r in input.main_roads { let main_road = &streets.roads[&r]; // Which side is closer to the cycleway? @@ -210,11 +229,31 @@ fn snap(streets: &mut StreetNetwork, input: Cycleway) { let main_road = streets.roads.get_mut(&r).unwrap(); splice_in(&mut main_road.lane_specs_ltr, insert_idx, insert_lanes); + + intersections.extend(main_road.endpoints()); + } + + // Recalculate geometry along all of the main roads we just thickened + for i in intersections { + streets.update_i(i); } // After this transformation, we should run CollapseDegenerateIntersections to handle the // intersection where the side road originally crossed the cycleway, and TrimDeadendCycleways // to clean up any small cycle connection roads. + // + // ALTERNATIVELY, remove the connector segments immediately. + if let Some(r) = input.connector_src_i { + if streets.roads.contains_key(&r) { + // Ignore errors + let _ = streets.collapse_short_road(r); + } + } + if let Some(r) = input.connector_dst_i { + if streets.roads.contains_key(&r) { + let _ = streets.collapse_short_road(r); + } + } } // Insert all of `insert` at `idx` in `target`