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 kml::{ExtraShape, ExtraShapes};
use raw_map::{ use raw_map::{
osm, Amenity, AreaType, Direction, DrivingSide, NamePerLanguage, RawArea, RawBuilding, RawMap, 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::osm_geom::{get_multipolygon_members, glue_multipolygon, multipoly_geometry};
use crate::Options; use crate::Options;
pub struct OsmExtract { pub struct OsmExtract {
/// Unsplit roads /// Unsplit roads. These aren't RawRoads yet, because they may not obey those invariants.
pub roads: Vec<(WayID, RawRoad)>, pub roads: Vec<(WayID, Vec<Pt2D>, Tags)>,
/// Traffic signals to the direction they apply /// Traffic signals to the direction they apply
pub traffic_signals: HashMap<HashablePt2D, Direction>, pub traffic_signals: HashMap<HashablePt2D, Direction>,
pub osm_node_ids: HashMap<HashablePt2D, NodeID>, pub osm_node_ids: HashMap<HashablePt2D, NodeID>,
@ -109,10 +109,7 @@ pub fn extract_osm(
way.tags.insert(osm::SIDEWALK, "right"); way.tags.insert(osm::SIDEWALK, "right");
} }
out.roads.push(( out.roads.push((id, way.pts.clone(), way.tags.clone()));
id,
RawRoad::new(way.pts.clone(), way.tags.clone(), &opts.map_config),
));
continue; continue;
} else if way.tags.is(osm::HIGHWAY, "service") { } else if way.tags.is(osm::HIGHWAY, "service") {
// If we got here, is_road didn't interpret it as a normal road // 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 // Look for any service roads that collide with parking lots, and treat them as parking aisles
// instead. // 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()); let mut closest: FindClosest<usize> = FindClosest::new(&map.gps_bounds.to_bounds());
for (idx, lot) in map.parking_lots.iter().enumerate() { for (idx, lot) in map.parking_lots.iter().enumerate() {
closest.add(idx, lot.polygon.points()); closest.add(idx, lot.polygon.points());
} }
let mut keep_roads = Vec::new(); let mut keep_roads = Vec::new();
let mut parking_aisles = Vec::new(); let mut parking_aisles = Vec::new();
for (id, road) in roads.drain(..) { for (id, pts, osm_tags) in roads.drain(..) {
if !road.osm_tags.is(osm::HIGHWAY, "service") { if !osm_tags.is(osm::HIGHWAY, "service") {
keep_roads.push((id, road)); keep_roads.push((id, pts, osm_tags));
continue; continue;
} }
// TODO This code is repeated later in make/parking_lots.rs, but oh well. // 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 // Use the center of all the aisle points to match it to lots
let candidates: Vec<usize> = closest let candidates: Vec<usize> = closest
.all_close_pts( .all_close_pts(Pt2D::center(&pts), Distance::meters(500.0))
Pt2D::center(&road.osm_center_points),
Distance::meters(500.0),
)
.into_iter() .into_iter()
.map(|(idx, _, _)| idx) .map(|(idx, _, _)| idx)
.collect(); .collect();
if service_road_crosses_parking_lot(map, &road, candidates) { if service_road_crosses_parking_lot(map, &pts, candidates) {
parking_aisles.push((id, road.osm_center_points.clone())); parking_aisles.push((id, pts));
} else { } else {
keep_roads.push((id, road)); keep_roads.push((id, pts, osm_tags));
} }
} }
roads.extend(keep_roads); 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 { fn service_road_crosses_parking_lot(map: &RawMap, pts: &[Pt2D], candidates: Vec<usize>) -> bool {
if let Ok((polylines, rings)) = Ring::split_points(&road.osm_center_points) { if let Ok((polylines, rings)) = Ring::split_points(pts) {
for pl in polylines { for pl in polylines {
for idx in &candidates { for idx in &candidates {
if map.parking_lots[*idx].polygon.clip_polyline(&pl).is_some() { 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 std::collections::{hash_map::Entry, HashMap, HashSet};
use abstutil::{Counter, Timer}; use abstutil::{Counter, Tags, Timer};
use geom::{Distance, HashablePt2D, Pt2D}; use geom::{Distance, HashablePt2D, PolyLine, Pt2D};
use raw_map::{ use raw_map::{
osm, Amenity, Direction, IntersectionType, OriginalRoad, RawIntersection, RawMap, RawRoad, osm, Amenity, Direction, IntersectionType, OriginalRoad, RawIntersection, RawMap, RawRoad,
}; };
@ -22,15 +22,13 @@ 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 roundabout_centers: HashMap<osm::NodeID, Pt2D> = HashMap::new();
let mut pt_to_intersection: HashMap<HashablePt2D, osm::NodeID> = HashMap::new(); let mut pt_to_intersection: HashMap<HashablePt2D, osm::NodeID> = HashMap::new();
{ input.roads.retain(|(id, pts, tags)| {
let mut roads = std::mem::take(&mut input.roads); if should_collapse_roundabout(pts, tags) {
roads.retain(|(id, r)| {
if should_collapse_roundabout(r) {
info!("Collapsing tiny roundabout {}", id); info!("Collapsing tiny roundabout {}", id);
// Arbitrarily use the first node's ID // Arbitrarily use the first node's ID
let id = input.osm_node_ids[&r.osm_center_points[0].to_hashable()]; let id = input.osm_node_ids[&pts[0].to_hashable()];
roundabout_centers.insert(id, Pt2D::center(&r.osm_center_points)); roundabout_centers.insert(id, Pt2D::center(pts));
for pt in &r.osm_center_points { for pt in pts {
pt_to_intersection.insert(pt.to_hashable(), id); pt_to_intersection.insert(pt.to_hashable(), id);
} }
@ -39,17 +37,15 @@ pub fn split_up_roads(map: &mut RawMap, mut input: OsmExtract, timer: &mut Timer
true true
} }
}); });
input.roads = roads;
}
let mut counts_per_pt = Counter::new(); let mut counts_per_pt = Counter::new();
for (_, r) in &input.roads { for (_, pts, _) in &input.roads {
for (idx, raw_pt) in r.osm_center_points.iter().enumerate() { for (idx, raw_pt) in pts.iter().enumerate() {
let pt = raw_pt.to_hashable(); let pt = raw_pt.to_hashable();
let count = counts_per_pt.inc(pt); let count = counts_per_pt.inc(pt);
// All start and endpoints of ways are also intersections. // 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) { if let Entry::Vacant(e) = pt_to_intersection.entry(pt) {
let id = input.osm_node_ids[&pt]; let id = input.osm_node_ids[&pt];
e.insert(id); 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 // Now actually split up the roads based on the intersections
timer.start_iter("split roads", input.roads.len()); 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(); timer.next();
let mut r = orig_road.clone(); let mut tags = orig_tags.clone();
let mut pts = Vec::new(); let mut pts = Vec::new();
let endpt1 = pt_to_intersection[&orig_road.osm_center_points[0].to_hashable()]; let endpt1 = pt_to_intersection[&orig_pts[0].to_hashable()];
let endpt2 = pt_to_intersection[&orig_road.osm_center_points.last().unwrap().to_hashable()]; let endpt2 = pt_to_intersection[&orig_pts.last().unwrap().to_hashable()];
let mut i1 = endpt1; let mut i1 = endpt1;
for pt in &orig_road.osm_center_points { for pt in orig_pts {
pts.push(*pt); pts.push(*pt);
if pts.len() == 1 { if pts.len() == 1 {
continue; continue;
} }
if let Some(i2) = pt_to_intersection.get(&pt.to_hashable()) { if let Some(i2) = pt_to_intersection.get(&pt.to_hashable()) {
if i1 == endpt1 { if i1 == endpt1 {
r.osm_tags tags.insert(osm::ENDPT_BACK.to_string(), "true".to_string());
.insert(osm::ENDPT_BACK.to_string(), "true".to_string());
} }
if *i2 == endpt2 { if *i2 == endpt2 {
r.osm_tags tags.insert(osm::ENDPT_FWD.to_string(), "true".to_string());
.insert(osm::ENDPT_FWD.to_string(), "true".to_string());
} }
let id = OriginalRoad { let id = OriginalRoad {
osm_way_id: *osm_way_id, 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 // Start a new road
map.roads.insert(id, r.clone()); tags = orig_tags.clone();
r.osm_tags.remove(osm::ENDPT_FWD);
r.osm_tags.remove(osm::ENDPT_BACK);
i1 = *i2; i1 = *i2;
pts.push(*pt); 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 /// 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. /// modified. The only distinction, currently, is length -- but I'd love a better definition.
/// Possibly the number of connecting roads. /// Possibly the number of connecting roads.
fn should_collapse_roundabout(r: &RawRoad) -> bool { fn should_collapse_roundabout(pts: &[Pt2D], tags: &Tags) -> bool {
r.osm_tags tags.is_any("junction", vec!["roundabout", "circular"])
.is_any("junction", vec!["roundabout", "circular"]) && pts[0] == *pts.last().unwrap()
&& r.osm_center_points[0] == *r.osm_center_points.last().unwrap() && PolyLine::unchecked_new(pts.to_vec()).length() < Distance::meters(50.0)
&& r.length() < Distance::meters(50.0)
} }