making an isolated layer for the map creation phase focused on roads and intersections, geometry and merging. not using it yet.

This commit is contained in:
Dustin Carlino 2019-01-26 12:08:08 -08:00
parent 5ca066a7df
commit ec38717a2c
7 changed files with 358 additions and 12 deletions

View File

@ -7,10 +7,14 @@
- sometimes a lane polyline hits the perpendicular of a trimmed road! where was this happening?
- handle small roads again somehow?
- try manually merging stuff after running the automatic. i think we need to repeat based on short roads. maybe we need kind of a hybrid approach, using the fancy deleter but re-running intersection polygon and halfmap and turn creation from a lower level?
- make halfmap also be indexed by stable IDs
- try merging with roads and intersections, but with new trimmed road lengths
- deal with loop roads still
- restore original road points, then redo the intersection polygon and lane center pt expansion
- organize map creation code better...
- remove halfmap now
- dont need to store shortest road lines in a hash anymore
- final Intersection.polygon can now be a Polygon!
- manually draw a picture of the weird intersection to see what would look reasonable. i think we need original road bands from deleted stuff to make decent polygons.
- what's correct for 14th and e boston? if we had less lanes there, would it help?

View File

@ -1,4 +1,5 @@
use crate::{ControlStopSign, ControlTrafficSignal, IntersectionID, Lane, LaneType, Road, RoadID};
use crate::raw_data::StableRoadID;
use crate::{ControlStopSign, ControlTrafficSignal, IntersectionID, Lane, LaneType, Road};
use abstutil;
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
@ -9,7 +10,7 @@ pub struct MapEdits {
pub map_name: String,
// TODO detect when we wind up editing back to the original thing
pub(crate) roads: BTreeMap<RoadID, RoadEdit>,
pub(crate) roads: BTreeMap<StableRoadID, RoadEdit>,
pub(crate) stop_signs: BTreeMap<IntersectionID, ControlStopSign>,
pub(crate) traffic_signals: BTreeMap<IntersectionID, ControlTrafficSignal>,
}
@ -52,11 +53,12 @@ impl MapEdits {
new_type: LaneType,
) {
let edit = RoadEdit::change_lane_type(reason, r, lane, new_type).unwrap();
self.roads.insert(r.id, edit);
self.roads.insert(r.stable_id, edit);
}
pub fn delete_lane(&mut self, r: &Road, lane: &Lane) {
self.roads.insert(r.id, RoadEdit::delete_lane(r, lane));
self.roads
.insert(r.stable_id, RoadEdit::delete_lane(r, lane));
}
}
@ -68,7 +70,7 @@ pub enum EditReason {
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RoadEdit {
road: RoadID,
road: StableRoadID,
pub(crate) forwards_lanes: Vec<LaneType>,
pub(crate) backwards_lanes: Vec<LaneType>,
reason: EditReason,
@ -106,7 +108,7 @@ impl RoadEdit {
}
Some(RoadEdit {
road: r.id,
road: r.stable_id,
forwards_lanes: forwards,
backwards_lanes: backwards,
reason,
@ -127,7 +129,7 @@ impl RoadEdit {
}
RoadEdit {
road: r.id,
road: r.stable_id,
forwards_lanes: forwards,
backwards_lanes: backwards,
reason: EditReason::BasemapWrong,

View File

@ -85,7 +85,7 @@ pub fn make_half_map(
});
// TODO move this to make/lanes.rs too
for lane in make::lanes::get_lane_specs(r, road_id, edits) {
for lane in make::lanes::get_lane_specs(r, *stable_id, edits) {
let id = LaneID(counter);
counter += 1;

View File

@ -0,0 +1,221 @@
use crate::make::initial::{Intersection, Road};
use crate::raw_data::{StableIntersectionID, StableRoadID};
use abstutil::wraparound_get;
use dimensioned::si;
use geom::{Angle, HashablePt2D, Line, PolyLine, Pt2D};
use std::collections::{BTreeMap, HashMap};
use std::marker;
const DEGENERATE_INTERSECTION_HALF_LENGTH: si::Meter<f64> = si::Meter {
value_unsafe: 5.0,
_marker: marker::PhantomData,
};
// The polygon should exist entirely within the thick bands around all original roads -- it just
// carves up part of that space, doesn't reach past it.
pub fn intersection_polygon(
i: &Intersection,
roads: &mut BTreeMap<StableRoadID, Road>,
) -> Vec<Pt2D> {
// Turn all of the incident roads into two PolyLines (the "forwards" and "backwards" borders of
// the road, if the roads were oriented to both be incoming to the intersection), both ending
// at the intersection (which may be different points for merged intersections!), and the angle
// of the last segment of the center line.
// TODO Maybe express the two incoming PolyLines as the "right" and "left"
let mut lines: Vec<(StableRoadID, Angle, PolyLine, PolyLine)> = i
.roads
.iter()
.map(|id| {
let r = &roads[id];
let (line, width_normal, width_reverse) = if r.src_i == i.id {
(r.original_center_pts.reversed(), r.back_width, r.fwd_width)
} else if r.dst_i == i.id {
(r.original_center_pts.clone(), r.fwd_width, r.back_width)
} else {
panic!("Incident road {} doesn't have an endpoint at {}", id, i.id);
};
let pl_normal = line.shift_right(width_normal);
let pl_reverse = line.shift_left(width_reverse);
(*id, line.last_line().angle(), pl_normal, pl_reverse)
})
.collect();
// Sort the polylines by the angle of their last segment.
// TODO This might break weirdly for polylines with very short last lines!
// TODO This definitely can break for merged intersections. To get the lines "in order", maybe
// we have to look at all the endpoints and sort by angle from the center of the points?
lines.sort_by_key(|(_, angle, _, _)| angle.normalized_degrees() as i64);
let mut endpoints = if lines.len() == 1 {
deadend(roads, i.id, &lines)
} else {
generalized_trim_back(roads, i.id, &lines)
};
// Close off the polygon
endpoints.push(endpoints[0]);
endpoints
}
fn generalized_trim_back(
roads: &mut BTreeMap<StableRoadID, Road>,
i: StableIntersectionID,
lines: &Vec<(StableRoadID, Angle, PolyLine, PolyLine)>,
) -> Vec<Pt2D> {
let mut road_lines: Vec<(StableRoadID, &PolyLine)> = Vec::new();
for (r, _, pl1, pl2) in lines {
road_lines.push((*r, pl1));
road_lines.push((*r, pl2));
}
// TODO We can overwrite trimmed now
let mut new_road_centers: HashMap<StableRoadID, PolyLine> = HashMap::new();
// Intersect every road's boundary lines with all the other lines
for (r1, pl1) in &road_lines {
// road_center ends at the intersection.
let road_center = if roads[r1].dst_i == i {
roads[r1].original_center_pts.clone()
} else {
roads[r1].original_center_pts.reversed()
};
// Always trim back a minimum amount, if possible.
let mut shortest_center = if road_center.length() >= DEGENERATE_INTERSECTION_HALF_LENGTH {
road_center
.slice(
0.0 * si::M,
road_center.length() - DEGENERATE_INTERSECTION_HALF_LENGTH,
)
.0
} else {
road_center.clone()
};
for (r2, pl2) in &road_lines {
if r1 == r2 {
continue;
}
if let Some((hit, angle)) = pl1.intersection(pl2) {
// Find where the perpendicular hits the original road line
let perp = Line::new(hit, hit.project_away(1.0, angle.rotate_degs(90.0)));
// How could something perpendicular to a shifted polyline never hit the original
// polyline?
let trim_to = road_center.intersection_infinite_line(perp).unwrap();
let trimmed = road_center.trim_to_pt(trim_to);
if trimmed.length() < shortest_center.length() {
shortest_center = trimmed;
}
// We could also do the update for r2, but we'll just get to it later.
}
}
let new_center = if roads[r1].dst_i == i {
shortest_center
} else {
shortest_center.reversed()
};
if let Some(existing) = new_road_centers.get(r1) {
if new_center.length() < existing.length() {
new_road_centers.insert(*r1, new_center);
}
} else {
new_road_centers.insert(*r1, new_center);
}
}
// After doing all the intersection checks, copy over the new centers. Also shift those centers
// out again to find the endpoints that'll make up the polygon.
let mut endpoints: Vec<Pt2D> = Vec::new();
for (id, center_pts) in new_road_centers {
let r = roads.get_mut(&id).unwrap();
r.trimmed_center_pts = center_pts;
if r.dst_i == i {
endpoints.push(r.trimmed_center_pts.shift_right(r.fwd_width).last_pt());
endpoints.push(r.trimmed_center_pts.shift_left(r.back_width).last_pt());
} else {
endpoints.push(r.trimmed_center_pts.shift_right(r.fwd_width).first_pt());
endpoints.push(r.trimmed_center_pts.shift_left(r.back_width).first_pt());
}
}
// Include collisions between polylines of adjacent roads, so the polygon doesn't cover area
// not originally covered by the thick road bands.
for idx in 0..lines.len() as isize {
let (_, _, fwd_pl, back_pl) = wraparound_get(&lines, idx);
let (_, _, adj_back_pl, _) = wraparound_get(&lines, idx + 1);
let (_, _, _, adj_fwd_pl) = wraparound_get(&lines, idx - 1);
if let Some((hit, _)) = fwd_pl.intersection(adj_fwd_pl) {
endpoints.push(hit);
}
if let Some((hit, _)) = back_pl.intersection(adj_back_pl) {
endpoints.push(hit);
}
}
endpoints.sort_by_key(|pt| HashablePt2D::from(*pt));
endpoints = approx_dedupe(endpoints);
let center = Pt2D::center(&endpoints);
endpoints.sort_by_key(|pt| Line::new(center, *pt).angle().normalized_degrees() as i64);
endpoints
}
fn deadend(
roads: &mut BTreeMap<StableRoadID, Road>,
i: StableIntersectionID,
lines: &Vec<(StableRoadID, Angle, PolyLine, PolyLine)>,
) -> Vec<Pt2D> {
let (id, _, pl_a, pl_b) = &lines[0];
let pt1 = pl_a
.reversed()
.safe_dist_along(DEGENERATE_INTERSECTION_HALF_LENGTH * 2.0)
.map(|(pt, _)| pt);
let pt2 = pl_b
.reversed()
.safe_dist_along(DEGENERATE_INTERSECTION_HALF_LENGTH * 2.0)
.map(|(pt, _)| pt);
if pt1.is_some() && pt2.is_some() {
let r = roads.get_mut(&id).unwrap();
if r.src_i == i {
r.trimmed_center_pts = r
.original_center_pts
.slice(
DEGENERATE_INTERSECTION_HALF_LENGTH * 2.0,
r.original_center_pts.length(),
)
.0;
} else {
r.trimmed_center_pts = r
.original_center_pts
.slice(
0.0 * si::M,
r.original_center_pts.length() - DEGENERATE_INTERSECTION_HALF_LENGTH * 2.0,
)
.0;
}
vec![pt1.unwrap(), pt2.unwrap(), pl_b.last_pt(), pl_a.last_pt()]
} else {
error!(
"{} is a dead-end for {}, which is too short to make degenerate intersection geometry",
i, id
);
vec![pl_a.last_pt(), pl_b.last_pt()]
}
}
// Temporary until Pt2D has proper resolution.
fn approx_dedupe(pts: Vec<Pt2D>) -> Vec<Pt2D> {
let mut result: Vec<Pt2D> = Vec::new();
for pt in pts {
if result.is_empty() || !result.last().unwrap().approx_eq(pt, 1.0 * si::M) {
result.push(pt);
}
}
result
}

View File

@ -0,0 +1,114 @@
mod geometry;
use crate::raw_data::{StableIntersectionID, StableRoadID};
use crate::{make, raw_data, MapEdits, LANE_THICKNESS};
use abstutil::Timer;
use geom::{GPSBounds, PolyLine, Pt2D};
use std::collections::{BTreeMap, BTreeSet};
pub struct InitialMap {
pub roads: BTreeMap<StableRoadID, Road>,
pub intersections: BTreeMap<StableIntersectionID, Intersection>,
}
pub struct Road {
pub id: StableRoadID,
pub src_i: StableIntersectionID,
pub dst_i: StableIntersectionID,
pub original_center_pts: PolyLine,
pub trimmed_center_pts: PolyLine,
pub fwd_width: f64,
pub back_width: f64,
pub lane_specs: Vec<make::lanes::LaneSpec>,
}
pub struct Intersection {
pub id: StableIntersectionID,
pub polygon: Vec<Pt2D>,
pub roads: BTreeSet<StableRoadID>,
}
pub fn make_initial_map(
data: &raw_data::Map,
gps_bounds: &GPSBounds,
edits: &MapEdits,
timer: &mut Timer,
) -> InitialMap {
let mut m = InitialMap {
roads: BTreeMap::new(),
intersections: BTreeMap::new(),
};
for stable_id in data.intersections.keys() {
m.intersections.insert(
*stable_id,
Intersection {
id: *stable_id,
polygon: Vec::new(),
roads: BTreeSet::new(),
},
);
}
for (stable_id, r) in &data.roads {
if r.i1 == r.i2 {
// TODO Cul-de-sacs should be valid, but it really makes pathfinding screwy
error!(
"OSM way {} is a loop on {}, skipping what would've been {}",
r.osm_way_id, r.i1, stable_id
);
continue;
}
m.intersections
.get_mut(&r.i1)
.unwrap()
.roads
.insert(*stable_id);
m.intersections
.get_mut(&r.i2)
.unwrap()
.roads
.insert(*stable_id);
let original_center_pts = PolyLine::new(
r.points
.iter()
.map(|coord| Pt2D::from_gps(*coord, &gps_bounds).unwrap())
.collect(),
);
let lane_specs = make::lanes::get_lane_specs(r, *stable_id, edits);
let mut fwd_width = 0.0;
let mut back_width = 0.0;
for l in &lane_specs {
if l.reverse_pts {
back_width += LANE_THICKNESS;
} else {
fwd_width += LANE_THICKNESS;
}
}
m.roads.insert(
*stable_id,
Road {
id: *stable_id,
src_i: r.i1,
dst_i: r.i2,
original_center_pts: original_center_pts.clone(),
trimmed_center_pts: original_center_pts,
fwd_width,
back_width,
lane_specs,
},
);
}
timer.start_iter("find each intersection polygon", m.intersections.len());
for i in m.intersections.values_mut() {
timer.next();
i.polygon = geometry::intersection_polygon(i, &mut m.roads);
}
m
}

View File

@ -1,4 +1,4 @@
use crate::{raw_data, LaneType, MapEdits, RoadID};
use crate::{raw_data, LaneType, MapEdits};
use serde_derive::{Deserialize, Serialize};
use std::iter;
@ -111,7 +111,11 @@ pub struct LaneSpec {
pub reverse_pts: bool,
}
pub fn get_lane_specs(r: &raw_data::Road, id: RoadID, edits: &MapEdits) -> Vec<LaneSpec> {
pub fn get_lane_specs(
r: &raw_data::Road,
id: raw_data::StableRoadID,
edits: &MapEdits,
) -> Vec<LaneSpec> {
let (side1_types, side2_types) = if let Some(e) = edits.roads.get(&id) {
info!("Using edits for {}", id);
(e.forwards_lanes.clone(), e.backwards_lanes.clone())

View File

@ -1,6 +1,7 @@
mod buildings;
mod bus_stops;
mod half_map;
mod initial;
mod intersections;
mod lanes;
mod merge_intersections;