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 {
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 {

View File

@ -650,7 +650,7 @@ fn recalculate_intersection_polygon(
map.intersections[i.0].polygon = results.intersection_polygon;
// Copy over the re-trimmed road centers
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];
map.roads[id.0].center_pts = pl;
if id != changed_road {

View File

@ -108,39 +108,42 @@ pub fn intersection_polygon(
.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.
if true {
debug.push((
results.debug.push((
"center".to_string(),
Circle::new(intersection_center, Distance::meters(1.0)).to_polygon(),
));
for (idx, r) in road_lines.iter().enumerate() {
debug.push((
results.debug.push((
idx.to_string(),
Circle::new(r.sorting_pt, Distance::meters(1.0)).to_polygon(),
));
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 {
return deadend(roads.clone(), intersection_id, &road_lines, debug);
return deadend(results, roads, &road_lines);
}
if !trim_roads_for_merging.is_empty() {
pretrimmed_geometry(roads, intersection_id, &road_lines, debug)
} else if let Some(result) = on_off_ramp(
roads.clone(),
intersection_id,
road_lines.clone(),
debug.clone(),
) {
pretrimmed_geometry(results, roads, &road_lines)
} else if let Some(result) = on_off_ramp(results.clone(), roads.clone(), road_lines.clone()) {
Ok(result)
} 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(
mut results: Results,
mut roads: BTreeMap<OriginalRoad, InputRoad>,
i: osm::NodeID,
input_road_lines: &[RoadLine],
mut debug: Vec<(String, Polygon)>,
) -> Result<Results> {
let i = results.intersection_id;
let mut road_lines: Vec<(OriginalRoad, PolyLine)> = Vec::new();
for r in input_road_lines {
road_lines.push((r.id, r.fwd_pl.clone()));
road_lines.push((r.id, r.back_pl.clone()));
if false {
debug.push((
results.debug.push((
format!("{} fwd", r.id.osm_way_id),
r.fwd_pl.make_polygons(Distance::meters(1.0)),
));
debug.push((
results.debug.push((
format!("{} back", r.id.osm_way_id),
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 = 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()
} else {
warn!(
@ -363,30 +367,27 @@ fn generalized_trim_back(
endpoints = Pt2D::approx_dedupe(endpoints, Distance::meters(0.1));
let center = Pt2D::center(&endpoints);
endpoints.sort_by_key(|pt| pt.angle_to(center).normalized_degrees() as i64);
(close_off_polygon(endpoints), debug)*/
close_off_polygon(endpoints)*/
Ok(Results {
intersection_id: i,
intersection_polygon,
debug,
trimmed_center_pts: roads
.into_iter()
.map(|(_, r)| (r.id, r.center_pts, r.half_width))
.collect(),
})
// 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));
}
Ok(results)
}
fn pretrimmed_geometry(
mut results: Results,
roads: BTreeMap<OriginalRoad, InputRoad>,
i: osm::NodeID,
road_lines: &[RoadLine],
debug: Vec<(String, Polygon)>,
) -> Result<Results> {
let mut endpoints: Vec<Pt2D> = Vec::new();
for r in road_lines {
let r = &roads[&r.id];
// 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_left(r.half_width)?.last_pt());
} else {
@ -396,27 +397,23 @@ fn pretrimmed_geometry(
}
// 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,
Distance::meters(0.1),
)))?
.into_polygon();
Ok(Results {
intersection_id: i,
intersection_polygon,
debug,
trimmed_center_pts: roads
.into_iter()
.map(|(_, r)| (r.id, r.center_pts, r.half_width))
.collect(),
})
for (id, r) in roads {
results
.trimmed_center_pts
.insert(id, (r.center_pts, r.half_width));
}
Ok(results)
}
fn deadend(
mut results: Results,
mut roads: BTreeMap<OriginalRoad, InputRoad>,
i: osm::NodeID,
road_lines: &[RoadLine],
debug: Vec<(String, Polygon)>,
) -> Result<Results> {
let len = DEGENERATE_INTERSECTION_HALF_LENGTH * 4.0;
@ -433,7 +430,7 @@ 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 {
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());
} else {
r.center_pts = r
@ -441,7 +438,7 @@ fn deadend(
.exact_slice(Distance::ZERO, r.center_pts.length() - len);
}
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)
} else {
r.center_pts
@ -454,7 +451,7 @@ fn deadend(
// points, so shift the center out again to find the endpoints.
// TODO Refactor with generalized_trim_back.
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_left(r.half_width)?.last_pt());
} else {
@ -463,15 +460,13 @@ fn deadend(
}
endpts.dedup();
Ok(Results {
intersection_id: i,
intersection_polygon: Ring::must_new(close_off_polygon(endpts)).into_polygon(),
debug,
trimmed_center_pts: roads
.into_iter()
.map(|(_, r)| (r.id, r.center_pts, r.half_width))
.collect(),
})
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));
}
Ok(results)
}
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
// lieu of proper docs, see https://twitter.com/CarlinoDustin/status/1290799086036111360.
fn on_off_ramp(
mut results: Results,
mut roads: BTreeMap<OriginalRoad, InputRoad>,
i: osm::NodeID,
road_lines: Vec<RoadLine>,
mut debug: Vec<(String, Polygon)>,
) -> Option<Results> {
if road_lines.len() != 3 {
return None;
@ -534,7 +528,7 @@ fn on_off_ramp(
let right = r.fwd_pl;
let left = r.back_pl;
let r = &roads[&id];
let center = if r.id.i2 == i {
let center = if r.id.i2 == results.intersection_id {
r.center_pts.clone()
} else {
r.center_pts.reversed()
@ -548,7 +542,7 @@ fn on_off_ramp(
}
// 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 thick2 = 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))?;
if false {
debug.push((
results.debug.push((
"1".to_string(),
Circle::new(hit, Distance::meters(3.0)).to_polygon(),
));
debug.push((
results.debug.push((
"2".to_string(),
Circle::new(trimmed_thin.last_pt(), Distance::meters(3.0)).to_polygon(),
));
debug.push((
results.debug.push((
"3".to_string(),
Circle::new(trimmed_thick.last_pt(), Distance::meters(3.0))
.to_polygon(),
@ -613,13 +607,13 @@ fn on_off_ramp(
{
// Trim the thin
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();
}
roads.get_mut(&thin.id).unwrap().center_pts = trimmed_thin;
// 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]
.center_pts
.get_slice_starting_at(trimmed_thick.last_pt())?
@ -645,7 +639,7 @@ fn on_off_ramp(
&thick1.id
})
.unwrap();
if other.id.i2 == i {
if other.id.i2 == results.intersection_id {
other.center_pts = other.center_pts.clone().extend(extra.reversed()).ok()?;
} else {
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] {
let r = &roads[&id];
// 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_left(r.half_width).ok()?.last_pt());
} else {
@ -674,13 +668,11 @@ fn on_off_ramp(
let center = Pt2D::center(&endpoints);
endpoints.sort_by_key(|pt| pt.angle_to(center).normalized_degrees() as i64);
endpoints.dedup();
Some(Results {
intersection_id: i,
intersection_polygon: Ring::must_new(close_off_polygon(endpoints)).into_polygon(),
debug,
trimmed_center_pts: roads
.into_iter()
.map(|(_, r)| (r.id, r.center_pts, r.half_width))
.collect(),
})
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));
}
Some(results)
}

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
let mut properties = serde_json::Map::new();
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,
}
#[derive(Clone)]
pub struct Results {
pub intersection_id: osm::NodeID,
pub intersection_polygon: Polygon,
/// (Road, trimmed center line, half width)
pub trimmed_center_pts: Vec<(OriginalRoad, PolyLine, Distance)>,
/// Road -> (trimmed center line, half width)
pub trimmed_center_pts: BTreeMap<OriginalRoad, (PolyLine, Distance)>,
/// Extra polygons with labels to debug the algorithm
pub debug: Vec<(String, Polygon)>,
}

View File

@ -123,7 +123,7 @@ impl InitialMap {
) {
Ok(results) => {
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;
}
}
@ -180,7 +180,7 @@ impl InitialMap {
)
.unwrap();
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;
}
info!(

View File

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