Don't create RawRoads too early (before splitting ways into actual segments). Otherwise, we can't enforce invariants on RawRoads properly.

This commit is contained in:
Dustin Carlino 2022-05-07 21:34:09 +01:00
parent 43c946a2b6
commit 6974d26cac
2 changed files with 49 additions and 61 deletions

View File

@ -8,15 +8,15 @@ use geom::{Distance, FindClosest, HashablePt2D, Polygon, Pt2D, Ring};
use kml::{ExtraShape, ExtraShapes};
use raw_map::{
osm, Amenity, AreaType, Direction, DrivingSide, NamePerLanguage, RawArea, RawBuilding, RawMap,
RawParkingLot, RawRoad, RestrictionType,
RawParkingLot, RestrictionType,
};
use crate::osm_geom::{get_multipolygon_members, glue_multipolygon, multipoly_geometry};
use crate::Options;
pub struct OsmExtract {
/// Unsplit roads
pub roads: Vec<(WayID, RawRoad)>,
/// Unsplit roads. These aren't RawRoads yet, because they may not obey those invariants.
pub roads: Vec<(WayID, Vec<Pt2D>, Tags)>,
/// Traffic signals to the direction they apply
pub traffic_signals: HashMap<HashablePt2D, Direction>,
pub osm_node_ids: HashMap<HashablePt2D, NodeID>,
@ -109,10 +109,7 @@ pub fn extract_osm(
way.tags.insert(osm::SIDEWALK, "right");
}
out.roads.push((
id,
RawRoad::new(way.pts.clone(), way.tags.clone(), &opts.map_config),
));
out.roads.push((id, way.pts.clone(), way.tags.clone()));
continue;
} else if way.tags.is(osm::HIGHWAY, "service") {
// If we got here, is_road didn't interpret it as a normal road
@ -568,33 +565,30 @@ fn get_area_type(tags: &Tags) -> Option<AreaType> {
// Look for any service roads that collide with parking lots, and treat them as parking aisles
// instead.
fn find_parking_aisles(map: &mut RawMap, roads: &mut Vec<(WayID, RawRoad)>) {
fn find_parking_aisles(map: &mut RawMap, roads: &mut Vec<(WayID, Vec<Pt2D>, Tags)>) {
let mut closest: FindClosest<usize> = FindClosest::new(&map.gps_bounds.to_bounds());
for (idx, lot) in map.parking_lots.iter().enumerate() {
closest.add(idx, lot.polygon.points());
}
let mut keep_roads = Vec::new();
let mut parking_aisles = Vec::new();
for (id, road) in roads.drain(..) {
if !road.osm_tags.is(osm::HIGHWAY, "service") {
keep_roads.push((id, road));
for (id, pts, osm_tags) in roads.drain(..) {
if !osm_tags.is(osm::HIGHWAY, "service") {
keep_roads.push((id, pts, osm_tags));
continue;
}
// TODO This code is repeated later in make/parking_lots.rs, but oh well.
// Use the center of all the aisle points to match it to lots
let candidates: Vec<usize> = closest
.all_close_pts(
Pt2D::center(&road.osm_center_points),
Distance::meters(500.0),
)
.all_close_pts(Pt2D::center(&pts), Distance::meters(500.0))
.into_iter()
.map(|(idx, _, _)| idx)
.collect();
if service_road_crosses_parking_lot(map, &road, candidates) {
parking_aisles.push((id, road.osm_center_points.clone()));
if service_road_crosses_parking_lot(map, &pts, candidates) {
parking_aisles.push((id, pts));
} else {
keep_roads.push((id, road));
keep_roads.push((id, pts, osm_tags));
}
}
roads.extend(keep_roads);
@ -603,8 +597,8 @@ fn find_parking_aisles(map: &mut RawMap, roads: &mut Vec<(WayID, RawRoad)>) {
}
}
fn service_road_crosses_parking_lot(map: &RawMap, road: &RawRoad, candidates: Vec<usize>) -> bool {
if let Ok((polylines, rings)) = Ring::split_points(&road.osm_center_points) {
fn service_road_crosses_parking_lot(map: &RawMap, pts: &[Pt2D], candidates: Vec<usize>) -> bool {
if let Ok((polylines, rings)) = Ring::split_points(pts) {
for pl in polylines {
for idx in &candidates {
if map.parking_lots[*idx].polygon.clip_polyline(&pl).is_some() {

View File

@ -1,7 +1,7 @@
use std::collections::{hash_map::Entry, HashMap, HashSet};
use abstutil::{Counter, Timer};
use geom::{Distance, HashablePt2D, Pt2D};
use abstutil::{Counter, Tags, Timer};
use geom::{Distance, HashablePt2D, PolyLine, Pt2D};
use raw_map::{
osm, Amenity, Direction, IntersectionType, OriginalRoad, RawIntersection, RawMap, RawRoad,
};
@ -22,34 +22,30 @@ pub fn split_up_roads(map: &mut RawMap, mut input: OsmExtract, timer: &mut Timer
let mut roundabout_centers: HashMap<osm::NodeID, Pt2D> = HashMap::new();
let mut pt_to_intersection: HashMap<HashablePt2D, osm::NodeID> = HashMap::new();
{
let mut roads = std::mem::take(&mut input.roads);
roads.retain(|(id, r)| {
if should_collapse_roundabout(r) {
info!("Collapsing tiny roundabout {}", id);
// Arbitrarily use the first node's ID
let id = input.osm_node_ids[&r.osm_center_points[0].to_hashable()];
roundabout_centers.insert(id, Pt2D::center(&r.osm_center_points));
for pt in &r.osm_center_points {
pt_to_intersection.insert(pt.to_hashable(), id);
}
false
} else {
true
input.roads.retain(|(id, pts, tags)| {
if should_collapse_roundabout(pts, tags) {
info!("Collapsing tiny roundabout {}", id);
// Arbitrarily use the first node's ID
let id = input.osm_node_ids[&pts[0].to_hashable()];
roundabout_centers.insert(id, Pt2D::center(pts));
for pt in pts {
pt_to_intersection.insert(pt.to_hashable(), id);
}
});
input.roads = roads;
}
false
} else {
true
}
});
let mut counts_per_pt = Counter::new();
for (_, r) in &input.roads {
for (idx, raw_pt) in r.osm_center_points.iter().enumerate() {
for (_, pts, _) in &input.roads {
for (idx, raw_pt) in pts.iter().enumerate() {
let pt = raw_pt.to_hashable();
let count = counts_per_pt.inc(pt);
// All start and endpoints of ways are also intersections.
if count == 2 || idx == 0 || idx == r.osm_center_points.len() - 1 {
if count == 2 || idx == 0 || idx == pts.len() - 1 {
if let Entry::Vacant(e) = pt_to_intersection.entry(pt) {
let id = input.osm_node_ids[&pt];
e.insert(id);
@ -82,27 +78,25 @@ pub fn split_up_roads(map: &mut RawMap, mut input: OsmExtract, timer: &mut Timer
// Now actually split up the roads based on the intersections
timer.start_iter("split roads", input.roads.len());
for (osm_way_id, orig_road) in &input.roads {
for (osm_way_id, orig_pts, orig_tags) in &input.roads {
timer.next();
let mut r = orig_road.clone();
let mut tags = orig_tags.clone();
let mut pts = Vec::new();
let endpt1 = pt_to_intersection[&orig_road.osm_center_points[0].to_hashable()];
let endpt2 = pt_to_intersection[&orig_road.osm_center_points.last().unwrap().to_hashable()];
let endpt1 = pt_to_intersection[&orig_pts[0].to_hashable()];
let endpt2 = pt_to_intersection[&orig_pts.last().unwrap().to_hashable()];
let mut i1 = endpt1;
for pt in &orig_road.osm_center_points {
for pt in orig_pts {
pts.push(*pt);
if pts.len() == 1 {
continue;
}
if let Some(i2) = pt_to_intersection.get(&pt.to_hashable()) {
if i1 == endpt1 {
r.osm_tags
.insert(osm::ENDPT_BACK.to_string(), "true".to_string());
tags.insert(osm::ENDPT_BACK.to_string(), "true".to_string());
}
if *i2 == endpt2 {
r.osm_tags
.insert(osm::ENDPT_FWD.to_string(), "true".to_string());
tags.insert(osm::ENDPT_FWD.to_string(), "true".to_string());
}
let id = OriginalRoad {
osm_way_id: *osm_way_id,
@ -117,11 +111,12 @@ pub fn split_up_roads(map: &mut RawMap, mut input: OsmExtract, timer: &mut Timer
}
}
r.osm_center_points = simplify_linestring(std::mem::take(&mut pts));
let osm_center_pts = simplify_linestring(std::mem::take(&mut pts));
map.roads
.insert(id, RawRoad::new(osm_center_pts, tags, &map.config));
// Start a new road
map.roads.insert(id, r.clone());
r.osm_tags.remove(osm::ENDPT_FWD);
r.osm_tags.remove(osm::ENDPT_BACK);
tags = orig_tags.clone();
i1 = *i2;
pts.push(*pt);
}
@ -244,9 +239,8 @@ fn simplify_linestring(pts: Vec<Pt2D>) -> Vec<Pt2D> {
/// Note https://www.openstreetmap.org/way/394991047 is an example of something that shouldn't get
/// modified. The only distinction, currently, is length -- but I'd love a better definition.
/// Possibly the number of connecting roads.
fn should_collapse_roundabout(r: &RawRoad) -> bool {
r.osm_tags
.is_any("junction", vec!["roundabout", "circular"])
&& r.osm_center_points[0] == *r.osm_center_points.last().unwrap()
&& r.length() < Distance::meters(50.0)
fn should_collapse_roundabout(pts: &[Pt2D], tags: &Tags) -> bool {
tags.is_any("junction", vec!["roundabout", "circular"])
&& pts[0] == *pts.last().unwrap()
&& PolyLine::unchecked_new(pts.to_vec()).length() < Distance::meters(50.0)
}