Simplify the intersection geometry interface, which also simplifies the implementation a bit. #893

This commit is contained in:
Dustin Carlino 2022-04-22 14:58:37 +01:00
parent 4d443ba4c8
commit b64874b92c
7 changed files with 85 additions and 97 deletions

View File

@ -564,6 +564,11 @@ impl Polygon {
pub fn simplify(&self, epsilon: f64) -> Polygon { pub fn simplify(&self, epsilon: f64) -> Polygon {
to_geo(self.points()).simplifyvw_preserve(&epsilon).into() to_geo(self.points()).simplifyvw_preserve(&epsilon).into()
} }
/// An arbitrary placeholder value, when Option types aren't worthwhile
pub fn dummy() -> Self {
Polygon::rectangle(0.1, 0.1)
}
} }
impl fmt::Display for Polygon { impl fmt::Display for Polygon {

View File

@ -650,7 +650,7 @@ fn recalculate_intersection_polygon(
map.intersections[i.0].polygon = results.intersection_polygon; map.intersections[i.0].polygon = results.intersection_polygon;
// Copy over the re-trimmed road centers // Copy over the re-trimmed road centers
let mut affected = Vec::new(); let mut affected = Vec::new();
for (orig_id, pl, _) in results.trimmed_center_pts { for (orig_id, (pl, _)) in results.trimmed_center_pts {
let id = id_mapping[&orig_id]; let id = id_mapping[&orig_id];
map.roads[id.0].center_pts = pl; map.roads[id.0].center_pts = pl;
if id != changed_road { if id != changed_road {

View File

@ -108,39 +108,42 @@ pub fn intersection_polygon(
.normalized_degrees() as i64 .normalized_degrees() as i64
}); });
let mut debug = Vec::new(); let mut results = Results {
intersection_id,
intersection_polygon: Polygon::dummy(),
debug: Vec::new(),
trimmed_center_pts: BTreeMap::new(),
};
// Debug the sorted order. // Debug the sorted order.
if true { if true {
debug.push(( results.debug.push((
"center".to_string(), "center".to_string(),
Circle::new(intersection_center, Distance::meters(1.0)).to_polygon(), Circle::new(intersection_center, Distance::meters(1.0)).to_polygon(),
)); ));
for (idx, r) in road_lines.iter().enumerate() { for (idx, r) in road_lines.iter().enumerate() {
debug.push(( results.debug.push((
idx.to_string(), idx.to_string(),
Circle::new(r.sorting_pt, Distance::meters(1.0)).to_polygon(), Circle::new(r.sorting_pt, Distance::meters(1.0)).to_polygon(),
)); ));
if let Ok(l) = Line::new(intersection_center, r.sorting_pt) { if let Ok(l) = Line::new(intersection_center, r.sorting_pt) {
debug.push((idx.to_string(), l.make_polygons(Distance::meters(0.5)))); results
.debug
.push((idx.to_string(), l.make_polygons(Distance::meters(0.5))));
} }
} }
} }
if road_lines.len() == 1 { if road_lines.len() == 1 {
return deadend(roads.clone(), intersection_id, &road_lines, debug); return deadend(results, roads, &road_lines);
} }
if !trim_roads_for_merging.is_empty() { if !trim_roads_for_merging.is_empty() {
pretrimmed_geometry(roads, intersection_id, &road_lines, debug) pretrimmed_geometry(results, roads, &road_lines)
} else if let Some(result) = on_off_ramp( } else if let Some(result) = on_off_ramp(results.clone(), roads.clone(), road_lines.clone()) {
roads.clone(),
intersection_id,
road_lines.clone(),
debug.clone(),
) {
Ok(result) Ok(result)
} else { } else {
generalized_trim_back(roads, intersection_id, &road_lines, debug) generalized_trim_back(results, roads, &road_lines)
} }
} }
@ -157,22 +160,23 @@ struct RoadLine {
} }
fn generalized_trim_back( fn generalized_trim_back(
mut results: Results,
mut roads: BTreeMap<OriginalRoad, InputRoad>, mut roads: BTreeMap<OriginalRoad, InputRoad>,
i: osm::NodeID,
input_road_lines: &[RoadLine], input_road_lines: &[RoadLine],
mut debug: Vec<(String, Polygon)>,
) -> Result<Results> { ) -> Result<Results> {
let i = results.intersection_id;
let mut road_lines: Vec<(OriginalRoad, PolyLine)> = Vec::new(); let mut road_lines: Vec<(OriginalRoad, PolyLine)> = Vec::new();
for r in input_road_lines { for r in input_road_lines {
road_lines.push((r.id, r.fwd_pl.clone())); road_lines.push((r.id, r.fwd_pl.clone()));
road_lines.push((r.id, r.back_pl.clone())); road_lines.push((r.id, r.back_pl.clone()));
if false { if false {
debug.push(( results.debug.push((
format!("{} fwd", r.id.osm_way_id), format!("{} fwd", r.id.osm_way_id),
r.fwd_pl.make_polygons(Distance::meters(1.0)), r.fwd_pl.make_polygons(Distance::meters(1.0)),
)); ));
debug.push(( results.debug.push((
format!("{} back", r.id.osm_way_id), format!("{} back", r.id.osm_way_id),
r.back_pl.make_polygons(Distance::meters(1.0)), r.back_pl.make_polygons(Distance::meters(1.0)),
)); ));
@ -348,7 +352,7 @@ fn generalized_trim_back(
deduped = Pt2D::approx_dedupe(deduped, Distance::meters(0.1)); deduped = Pt2D::approx_dedupe(deduped, Distance::meters(0.1));
deduped = close_off_polygon(deduped); deduped = close_off_polygon(deduped);
let intersection_polygon = if main_result.len() == deduped.len() { results.intersection_polygon = if main_result.len() == deduped.len() {
Ring::must_new(main_result).into_polygon() Ring::must_new(main_result).into_polygon()
} else { } else {
warn!( warn!(
@ -363,30 +367,27 @@ fn generalized_trim_back(
endpoints = Pt2D::approx_dedupe(endpoints, Distance::meters(0.1)); endpoints = Pt2D::approx_dedupe(endpoints, Distance::meters(0.1));
let center = Pt2D::center(&endpoints); let center = Pt2D::center(&endpoints);
endpoints.sort_by_key(|pt| pt.angle_to(center).normalized_degrees() as i64); endpoints.sort_by_key(|pt| pt.angle_to(center).normalized_degrees() as i64);
(close_off_polygon(endpoints), debug)*/ close_off_polygon(endpoints)*/
Ok(Results { // TODO We always do this. Maybe Results has the InputRoad and we just work in-place
intersection_id: i, for (id, r) in roads {
intersection_polygon, results
debug, .trimmed_center_pts
trimmed_center_pts: roads .insert(id, (r.center_pts, r.half_width));
.into_iter() }
.map(|(_, r)| (r.id, r.center_pts, r.half_width)) Ok(results)
.collect(),
})
} }
fn pretrimmed_geometry( fn pretrimmed_geometry(
mut results: Results,
roads: BTreeMap<OriginalRoad, InputRoad>, roads: BTreeMap<OriginalRoad, InputRoad>,
i: osm::NodeID,
road_lines: &[RoadLine], road_lines: &[RoadLine],
debug: Vec<(String, Polygon)>,
) -> Result<Results> { ) -> Result<Results> {
let mut endpoints: Vec<Pt2D> = Vec::new(); let mut endpoints: Vec<Pt2D> = Vec::new();
for r in road_lines { for r in road_lines {
let r = &roads[&r.id]; let r = &roads[&r.id];
// Shift those final centers out again to find the main endpoints for the polygon. // Shift those final centers out again to find the main endpoints for the polygon.
if r.id.i2 == i { if r.id.i2 == results.intersection_id {
endpoints.push(r.center_pts.shift_right(r.half_width)?.last_pt()); 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_pts.shift_left(r.half_width)?.last_pt());
} else { } else {
@ -396,27 +397,23 @@ fn pretrimmed_geometry(
} }
// TODO Do all of the crazy deduping that generalized_trim_back does? // TODO Do all of the crazy deduping that generalized_trim_back does?
let intersection_polygon = Ring::new(close_off_polygon(Pt2D::approx_dedupe( results.intersection_polygon = Ring::new(close_off_polygon(Pt2D::approx_dedupe(
endpoints, endpoints,
Distance::meters(0.1), Distance::meters(0.1),
)))? )))?
.into_polygon(); .into_polygon();
Ok(Results { for (id, r) in roads {
intersection_id: i, results
intersection_polygon, .trimmed_center_pts
debug, .insert(id, (r.center_pts, r.half_width));
trimmed_center_pts: roads }
.into_iter() Ok(results)
.map(|(_, r)| (r.id, r.center_pts, r.half_width))
.collect(),
})
} }
fn deadend( fn deadend(
mut results: Results,
mut roads: BTreeMap<OriginalRoad, InputRoad>, mut roads: BTreeMap<OriginalRoad, InputRoad>,
i: osm::NodeID,
road_lines: &[RoadLine], road_lines: &[RoadLine],
debug: Vec<(String, Polygon)>,
) -> Result<Results> { ) -> Result<Results> {
let len = DEGENERATE_INTERSECTION_HALF_LENGTH * 4.0; let len = DEGENERATE_INTERSECTION_HALF_LENGTH * 4.0;
@ -433,7 +430,7 @@ fn deadend(
let r = roads.get_mut(&id).unwrap(); let r = roads.get_mut(&id).unwrap();
let len_with_buffer = len + 3.0 * EPSILON_DIST; let len_with_buffer = len + 3.0 * EPSILON_DIST;
let trimmed = if r.center_pts.length() >= len_with_buffer { let trimmed = if r.center_pts.length() >= len_with_buffer {
if r.id.i1 == i { if r.id.i1 == results.intersection_id {
r.center_pts = r.center_pts.exact_slice(len, r.center_pts.length()); r.center_pts = r.center_pts.exact_slice(len, r.center_pts.length());
} else { } else {
r.center_pts = r r.center_pts = r
@ -441,7 +438,7 @@ fn deadend(
.exact_slice(Distance::ZERO, r.center_pts.length() - len); .exact_slice(Distance::ZERO, r.center_pts.length() - len);
} }
r.center_pts.clone() r.center_pts.clone()
} else if r.id.i1 == i { } else if r.id.i1 == results.intersection_id {
r.center_pts.extend_to_length(len_with_buffer) r.center_pts.extend_to_length(len_with_buffer)
} else { } else {
r.center_pts r.center_pts
@ -454,7 +451,7 @@ fn deadend(
// points, so shift the center out again to find the endpoints. // points, so shift the center out again to find the endpoints.
// TODO Refactor with generalized_trim_back. // TODO Refactor with generalized_trim_back.
let mut endpts = vec![pl_b.last_pt(), pl_a.last_pt()]; let mut endpts = vec![pl_b.last_pt(), pl_a.last_pt()];
if r.id.i2 == i { if r.id.i2 == results.intersection_id {
endpts.push(trimmed.shift_right(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()); endpts.push(trimmed.shift_left(r.half_width)?.last_pt());
} else { } else {
@ -463,15 +460,13 @@ fn deadend(
} }
endpts.dedup(); endpts.dedup();
Ok(Results { results.intersection_polygon = Ring::must_new(close_off_polygon(endpts)).into_polygon();
intersection_id: i, for (id, r) in roads {
intersection_polygon: Ring::must_new(close_off_polygon(endpts)).into_polygon(), results
debug, .trimmed_center_pts
trimmed_center_pts: roads .insert(id, (r.center_pts, r.half_width));
.into_iter() }
.map(|(_, r)| (r.id, r.center_pts, r.half_width)) Ok(results)
.collect(),
})
} }
fn close_off_polygon(mut pts: Vec<Pt2D>) -> Vec<Pt2D> { fn close_off_polygon(mut pts: Vec<Pt2D>) -> Vec<Pt2D> {
@ -494,10 +489,9 @@ struct Piece {
// certain angles. It usually happens for highway on/off ramps. Try something different here. In // certain angles. It usually happens for highway on/off ramps. Try something different here. In
// lieu of proper docs, see https://twitter.com/CarlinoDustin/status/1290799086036111360. // lieu of proper docs, see https://twitter.com/CarlinoDustin/status/1290799086036111360.
fn on_off_ramp( fn on_off_ramp(
mut results: Results,
mut roads: BTreeMap<OriginalRoad, InputRoad>, mut roads: BTreeMap<OriginalRoad, InputRoad>,
i: osm::NodeID,
road_lines: Vec<RoadLine>, road_lines: Vec<RoadLine>,
mut debug: Vec<(String, Polygon)>,
) -> Option<Results> { ) -> Option<Results> {
if road_lines.len() != 3 { if road_lines.len() != 3 {
return None; return None;
@ -534,7 +528,7 @@ fn on_off_ramp(
let right = r.fwd_pl; let right = r.fwd_pl;
let left = r.back_pl; let left = r.back_pl;
let r = &roads[&id]; let r = &roads[&id];
let center = if r.id.i2 == i { let center = if r.id.i2 == results.intersection_id {
r.center_pts.clone() r.center_pts.clone()
} else { } else {
r.center_pts.reversed() r.center_pts.reversed()
@ -548,7 +542,7 @@ fn on_off_ramp(
} }
// Break ties by preferring the outbound roads for thin // Break ties by preferring the outbound roads for thin
pieces.sort_by_key(|r| (roads[&r.id].half_width, r.id.i2 == i)); pieces.sort_by_key(|r| (roads[&r.id].half_width, r.id.i2 == results.intersection_id));
let thick1 = pieces.pop().unwrap(); let thick1 = pieces.pop().unwrap();
let thick2 = pieces.pop().unwrap(); let thick2 = pieces.pop().unwrap();
let thin = pieces.pop().unwrap(); let thin = pieces.pop().unwrap();
@ -584,15 +578,15 @@ fn on_off_ramp(
.and_then(|trim_to| thick.center.get_slice_ending_at(trim_to))?; .and_then(|trim_to| thick.center.get_slice_ending_at(trim_to))?;
if false { if false {
debug.push(( results.debug.push((
"1".to_string(), "1".to_string(),
Circle::new(hit, Distance::meters(3.0)).to_polygon(), Circle::new(hit, Distance::meters(3.0)).to_polygon(),
)); ));
debug.push(( results.debug.push((
"2".to_string(), "2".to_string(),
Circle::new(trimmed_thin.last_pt(), Distance::meters(3.0)).to_polygon(), Circle::new(trimmed_thin.last_pt(), Distance::meters(3.0)).to_polygon(),
)); ));
debug.push(( results.debug.push((
"3".to_string(), "3".to_string(),
Circle::new(trimmed_thick.last_pt(), Distance::meters(3.0)) Circle::new(trimmed_thick.last_pt(), Distance::meters(3.0))
.to_polygon(), .to_polygon(),
@ -613,13 +607,13 @@ fn on_off_ramp(
{ {
// Trim the thin // Trim the thin
let (mut trimmed_thin, mut trimmed_thick, thick_id) = best_hit?; let (mut trimmed_thin, mut trimmed_thick, thick_id) = best_hit?;
if thin.id.i2 != i { if thin.id.i2 != results.intersection_id {
trimmed_thin = trimmed_thin.reversed(); trimmed_thin = trimmed_thin.reversed();
} }
roads.get_mut(&thin.id).unwrap().center_pts = trimmed_thin; roads.get_mut(&thin.id).unwrap().center_pts = trimmed_thin;
// Trim the thick extra ends at the intersection // Trim the thick extra ends at the intersection
let extra = if thick_id.i2 == i { let extra = if thick_id.i2 == results.intersection_id {
roads[&thick_id] roads[&thick_id]
.center_pts .center_pts
.get_slice_starting_at(trimmed_thick.last_pt())? .get_slice_starting_at(trimmed_thick.last_pt())?
@ -645,7 +639,7 @@ fn on_off_ramp(
&thick1.id &thick1.id
}) })
.unwrap(); .unwrap();
if other.id.i2 == i { if other.id.i2 == results.intersection_id {
other.center_pts = other.center_pts.clone().extend(extra.reversed()).ok()?; other.center_pts = other.center_pts.clone().extend(extra.reversed()).ok()?;
} else { } else {
other.center_pts = extra.extend(other.center_pts.clone()).ok()?; other.center_pts = extra.extend(other.center_pts.clone()).ok()?;
@ -657,7 +651,7 @@ fn on_off_ramp(
for id in [thin.id, thick1.id, thick2.id] { for id in [thin.id, thick1.id, thick2.id] {
let r = &roads[&id]; let r = &roads[&id];
// Shift those final centers out again to find the main endpoints for the polygon. // Shift those final centers out again to find the main endpoints for the polygon.
if r.id.i2 == i { if r.id.i2 == results.intersection_id {
endpoints.push(r.center_pts.shift_right(r.half_width).ok()?.last_pt()); 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_pts.shift_left(r.half_width).ok()?.last_pt());
} else { } else {
@ -674,13 +668,11 @@ fn on_off_ramp(
let center = Pt2D::center(&endpoints); let center = Pt2D::center(&endpoints);
endpoints.sort_by_key(|pt| pt.angle_to(center).normalized_degrees() as i64); endpoints.sort_by_key(|pt| pt.angle_to(center).normalized_degrees() as i64);
endpoints.dedup(); endpoints.dedup();
Some(Results { results.intersection_polygon = Ring::must_new(close_off_polygon(endpoints)).into_polygon();
intersection_id: i, for (id, r) in roads {
intersection_polygon: Ring::must_new(close_off_polygon(endpoints)).into_polygon(), results
debug, .trimmed_center_pts
trimmed_center_pts: roads .insert(id, (r.center_pts, r.half_width));
.into_iter() }
.map(|(_, r)| (r.id, r.center_pts, r.half_width)) Some(results)
.collect(),
})
} }

View File

@ -136,7 +136,7 @@ impl Results {
}); });
} }
for (id, pl, half_width) in &self.trimmed_center_pts { for (id, (pl, half_width)) in &self.trimmed_center_pts {
// Add both a line-string and polygon per road // Add both a line-string and polygon per road
let mut properties = serde_json::Map::new(); let mut properties = serde_json::Map::new();
properties.insert("osm_way_id".to_string(), id.osm_way_id.0.into()); properties.insert("osm_way_id".to_string(), id.osm_way_id.0.into());

View File

@ -33,11 +33,12 @@ pub struct InputRoad {
pub osm_tags: Tags, pub osm_tags: Tags,
} }
#[derive(Clone)]
pub struct Results { pub struct Results {
pub intersection_id: osm::NodeID, pub intersection_id: osm::NodeID,
pub intersection_polygon: Polygon, pub intersection_polygon: Polygon,
/// (Road, trimmed center line, half width) /// Road -> (trimmed center line, half width)
pub trimmed_center_pts: Vec<(OriginalRoad, PolyLine, Distance)>, pub trimmed_center_pts: BTreeMap<OriginalRoad, (PolyLine, Distance)>,
/// Extra polygons with labels to debug the algorithm /// Extra polygons with labels to debug the algorithm
pub debug: Vec<(String, Polygon)>, pub debug: Vec<(String, Polygon)>,
} }

View File

@ -123,7 +123,7 @@ impl InitialMap {
) { ) {
Ok(results) => { Ok(results) => {
i.polygon = results.intersection_polygon; i.polygon = results.intersection_polygon;
for (r, pl, _) in results.trimmed_center_pts { for (r, (pl, _)) in results.trimmed_center_pts {
m.roads.get_mut(&r).unwrap().trimmed_center_pts = pl; m.roads.get_mut(&r).unwrap().trimmed_center_pts = pl;
} }
} }
@ -180,7 +180,7 @@ impl InitialMap {
) )
.unwrap(); .unwrap();
i.polygon = results.intersection_polygon; i.polygon = results.intersection_polygon;
for (r, pl, _) in results.trimmed_center_pts { for (r, (pl, _)) in results.trimmed_center_pts {
m.roads.get_mut(&r).unwrap().trimmed_center_pts = pl; m.roads.get_mut(&r).unwrap().trimmed_center_pts = pl;
} }
info!( info!(

View File

@ -224,8 +224,8 @@ impl RawMap {
results.intersection_polygon, results.intersection_polygon,
results results
.trimmed_center_pts .trimmed_center_pts
.into_iter() .into_values()
.map(|(_, pl, half_width)| pl.make_polygons(2.0 * half_width)) .map(|(pl, half_width)| pl.make_polygons(2.0 * half_width))
.collect(), .collect(),
results.debug, results.debug,
)) ))
@ -239,18 +239,13 @@ impl RawMap {
for r in self.roads_per_intersection(road_id.i1) { for r in self.roads_per_intersection(road_id.i1) {
input_roads.push(initial::Road::new(self, r)?.to_input_road()); input_roads.push(initial::Road::new(self, r)?.to_input_road());
} }
let results = intersection_polygon( let mut results = intersection_polygon(
road_id.i1, road_id.i1,
input_roads, input_roads,
// TODO Not sure if we should use this or not // TODO Not sure if we should use this or not
&BTreeMap::new(), &BTreeMap::new(),
)?; )?;
results results.trimmed_center_pts.remove(&road_id).unwrap().0
.trimmed_center_pts
.into_iter()
.find(|(id, _, _)| *id == road_id)
.unwrap()
.1
}; };
// Now the second // Now the second
@ -263,18 +258,13 @@ impl RawMap {
} }
input_roads.push(road); input_roads.push(road);
} }
let results = intersection_polygon( let mut results = intersection_polygon(
road_id.i2, road_id.i2,
input_roads, input_roads,
// TODO Not sure if we should use this or not // TODO Not sure if we should use this or not
&BTreeMap::new(), &BTreeMap::new(),
)?; )?;
Ok(results Ok(results.trimmed_center_pts.remove(&road_id).unwrap().0)
.trimmed_center_pts
.into_iter()
.find(|(id, _, _)| *id == road_id)
.unwrap()
.1)
} }
} }