flatten some of the map building code

This commit is contained in:
Dustin Carlino 2020-07-02 10:48:05 -07:00
parent 3acec91f51
commit ebe1f486f7

View File

@ -16,7 +16,7 @@ use crate::{
PathConstraints, Road, RoadID, NORMAL_LANE_THICKNESS, SIDEWALK_THICKNESS,
};
use abstutil::Timer;
use geom::{Bounds, Distance, GPSBounds, PolyLine, Polygon, Speed};
use geom::{Distance, PolyLine, Polygon, Speed};
use std::collections::{BTreeMap, BTreeSet, HashSet};
impl Map {
@ -30,47 +30,289 @@ impl Map {
let initial_map = initial::InitialMap::new(&raw, &bounds, timer);
timer.stop("raw_map to InitialMap");
timer.start("InitialMap to half of Map");
let mut m = make_half_map(&raw, initial_map, gps_bounds, bounds, timer);
timer.stop("InitialMap to half of Map");
let mut map = Map {
roads: Vec::new(),
lanes: Vec::new(),
intersections: Vec::new(),
turns: BTreeMap::new(),
buildings: Vec::new(),
bus_stops: BTreeMap::new(),
bus_routes: Vec::new(),
areas: Vec::new(),
parking_lots: Vec::new(),
zones: Vec::new(),
boundary_polygon: raw.boundary_polygon.clone(),
stop_signs: BTreeMap::new(),
traffic_signals: BTreeMap::new(),
gps_bounds,
bounds,
driving_side: raw.driving_side,
pathfinder: None,
pathfinder_dirty: false,
city_name: raw.city_name.clone(),
name: raw.name.clone(),
edits: MapEdits::new(),
};
timer.start("finalize Map");
// TODO Can probably move this into make_half_map.
{
let mut stop_signs: BTreeMap<IntersectionID, ControlStopSign> = BTreeMap::new();
let mut traffic_signals: BTreeMap<IntersectionID, ControlTrafficSignal> =
BTreeMap::new();
for i in &m.intersections {
match i.intersection_type {
IntersectionType::StopSign => {
stop_signs.insert(i.id, ControlStopSign::new(&m, i.id));
}
IntersectionType::TrafficSignal => {
traffic_signals.insert(i.id, ControlTrafficSignal::new(&m, i.id, timer));
}
IntersectionType::Border | IntersectionType::Construction => {}
};
}
m.stop_signs = stop_signs;
m.traffic_signals = traffic_signals;
let road_id_mapping: BTreeMap<OriginalRoad, RoadID> = initial_map
.roads
.keys()
.enumerate()
.map(|(idx, id)| (*id, RoadID(idx)))
.collect();
let mut intersection_id_mapping: BTreeMap<OriginalIntersection, IntersectionID> =
BTreeMap::new();
for (idx, i) in initial_map.intersections.values().enumerate() {
let id = IntersectionID(idx);
map.intersections.push(Intersection {
id,
// IMPORTANT! We're relying on the triangulation algorithm not to mess with the
// order of the points. Sidewalk corner rendering depends on it
// later.
polygon: Polygon::new(&i.polygon),
turns: BTreeSet::new(),
elevation: i.elevation,
// Might change later
intersection_type: i.intersection_type,
orig_id: i.id,
incoming_lanes: Vec::new(),
outgoing_lanes: Vec::new(),
roads: i.roads.iter().map(|id| road_id_mapping[id]).collect(),
});
intersection_id_mapping.insert(i.id, id);
}
timer.start_iter("expand roads to lanes", initial_map.roads.len());
for r in initial_map.roads.values() {
timer.next();
let road_id = road_id_mapping[&r.id];
let i1 = intersection_id_mapping[&r.src_i];
let i2 = intersection_id_mapping[&r.dst_i];
let mut road = Road {
id: road_id,
osm_tags: raw.roads[&r.id].osm_tags.clone(),
turn_restrictions: raw.roads[&r.id]
.turn_restrictions
.iter()
.filter_map(|(rt, to)| {
if let Some(to) = road_id_mapping.get(to) {
Some((*rt, *to))
} else {
timer.warn(format!(
"Turn restriction from {} points to invalid dst {}",
r.id, to
));
None
}
})
.collect(),
complicated_turn_restrictions: raw.roads[&r.id]
.complicated_turn_restrictions
.iter()
.filter_map(|(via, to)| {
if let (Some(via), Some(to)) =
(road_id_mapping.get(via), road_id_mapping.get(to))
{
Some((*via, *to))
} else {
timer.warn(format!(
"Complicated turn restriction from {} has invalid via {} or dst {}",
r.id, via, to
));
None
}
})
.collect(),
orig_id: r.id,
children_forwards: Vec::new(),
children_backwards: Vec::new(),
center_pts: r.trimmed_center_pts.clone(),
src_i: i1,
dst_i: i2,
speed_limit: Speed::ZERO,
zorder: if let Some(layer) = raw.roads[&r.id].osm_tags.get("layer") {
layer.parse::<isize>().unwrap()
} else {
0
},
zone: None,
};
road.speed_limit = road.speed_limit_from_osm();
for lane in &r.lane_specs {
let id = LaneID(map.lanes.len());
let (src_i, dst_i) = if lane.reverse_pts { (i2, i1) } else { (i1, i2) };
map.intersections[src_i.0].outgoing_lanes.push(id);
map.intersections[dst_i.0].incoming_lanes.push(id);
let (unshifted_pts, other_lanes_width): (PolyLine, Distance) = {
let dir = lane.reverse_pts;
let w = road.width(&map, !dir);
road.children_mut(!dir).push((id, lane.lane_type));
if dir {
(road.center_pts.reversed(), w)
} else {
(road.center_pts.clone(), w)
}
};
// TODO probably different behavior for oneways
// TODO need to factor in yellow center lines (but what's the right thing to even
// do?
let width = if lane.lane_type == LaneType::Sidewalk {
SIDEWALK_THICKNESS
} else {
NORMAL_LANE_THICKNESS
};
let lane_center_pts = map
.right_shift(unshifted_pts, other_lanes_width + width / 2.0)
.with_context(timer, format!("shift for {}", id));
map.lanes.push(Lane {
id,
lane_center_pts,
width,
src_i,
dst_i,
lane_type: lane.lane_type,
parent: road_id,
building_paths: Vec::new(),
bus_stops: Vec::new(),
parking_blackhole: None,
});
}
if road.get_name() == "???" {
timer.warn(format!(
"{} has no name. Tags: {:?}",
road.id, road.osm_tags
));
}
map.roads.push(road);
}
for i in map.intersections.iter_mut() {
if is_border(i, &map.lanes) {
i.intersection_type = IntersectionType::Border;
}
if i.is_border() {
if i.roads.len() != 1 {
panic!(
"{} ({}) is a border, but is connected to >1 road: {:?}",
i.id, i.orig_id, i.roads
);
}
continue;
}
if i.is_closed() {
continue;
}
if i.intersection_type == IntersectionType::TrafficSignal {
let mut ok = false;
for r in &i.roads {
if map.roads[r.0].osm_tags.get(osm::HIGHWAY)
!= Some(&"construction".to_string())
{
ok = true;
break;
}
}
if !ok {
i.intersection_type = IntersectionType::StopSign;
}
}
if i.incoming_lanes.is_empty() || i.outgoing_lanes.is_empty() {
timer.warn(format!("{:?} is orphaned!", i));
continue;
}
for t in turns::make_all_turns(map.driving_side, i, &map.roads, &map.lanes, timer) {
assert!(!map.turns.contains_key(&t.id));
i.turns.insert(t.id);
if t.geom.length() < geom::EPSILON_DIST {
timer.warn(format!("{} is a very short turn", t.id));
}
map.turns.insert(t.id, t);
}
}
timer.start("find parking blackholes");
for (l, redirect) in connectivity::redirect_parking_blackholes(&map, timer) {
map.lanes[l.0].parking_blackhole = Some(redirect);
}
timer.stop("find parking blackholes");
map.buildings = buildings::make_all_buildings(&raw.buildings, &map, timer);
for b in &map.buildings {
let lane = b.sidewalk();
// TODO Could be more performant and cleanly written
let mut bldgs = map.lanes[lane.0].building_paths.clone();
bldgs.push(b.id);
bldgs.sort_by_key(|b| map.buildings[b.0].front_path.sidewalk.dist_along());
map.lanes[lane.0].building_paths = bldgs;
}
map.parking_lots =
buildings::make_all_parking_lots(&raw.parking_lots, &raw.parking_aisles, &map, timer);
map.zones = zones::make_all_zones(&map);
for z in &map.zones {
for r in &z.members {
map.roads[r.0].zone = Some(z.id);
}
}
for (idx, a) in raw.areas.iter().enumerate() {
map.areas.push(Area {
id: AreaID(idx),
area_type: a.area_type,
polygon: a.polygon.clone(),
osm_tags: a.osm_tags.clone(),
osm_id: a.osm_id,
});
}
bridges::find_bridges(&mut map.roads, &map.bounds, timer);
let mut stop_signs: BTreeMap<IntersectionID, ControlStopSign> = BTreeMap::new();
let mut traffic_signals: BTreeMap<IntersectionID, ControlTrafficSignal> = BTreeMap::new();
for i in &map.intersections {
match i.intersection_type {
IntersectionType::StopSign => {
stop_signs.insert(i.id, ControlStopSign::new(&map, i.id));
}
IntersectionType::TrafficSignal => {
traffic_signals.insert(i.id, ControlTrafficSignal::new(&map, i.id, timer));
}
IntersectionType::Border | IntersectionType::Construction => {}
};
}
map.stop_signs = stop_signs;
map.traffic_signals = traffic_signals;
// Here's a fun one: we can't set up walking_using_transit yet, because we haven't
// finalized bus stops and routes. We need the bus graph in place for that. So setup
// pathfinding in two stages.
if build_ch {
timer.start("setup (most of) Pathfinder");
m.pathfinder = Some(Pathfinder::new_without_transit(&m, timer));
map.pathfinder = Some(Pathfinder::new_without_transit(&map, timer));
timer.stop("setup (most of) Pathfinder");
{
let (stops, routes) =
bus_stops::make_bus_stops(&m, &raw.bus_routes, &m.gps_bounds, &m.bounds, timer);
m.bus_stops = stops;
let (stops, routes) = bus_stops::make_bus_stops(
&map,
&raw.bus_routes,
&map.gps_bounds,
&map.bounds,
timer,
);
map.bus_stops = stops;
// The IDs are sorted in the BTreeMap, so this order winds up correct.
for id in m.bus_stops.keys() {
m.lanes[id.sidewalk.0].bus_stops.push(*id);
for id in map.bus_stops.keys() {
map.lanes[id.sidewalk.0].bus_stops.push(*id);
}
timer.start_iter("verify bus routes are connected", routes.len());
@ -79,9 +321,9 @@ impl Map {
if r.stops.is_empty() {
continue;
}
if bus_stops::fix_bus_route(&m, &mut r) {
r.id = BusRouteID(m.bus_routes.len());
m.bus_routes.push(r);
if bus_stops::fix_bus_route(&map, &mut r) {
r.id = BusRouteID(map.bus_routes.len());
map.bus_routes.push(r);
} else {
timer.warn(format!("Skipping route {}", r.name));
}
@ -89,27 +331,27 @@ impl Map {
// Remove orphaned bus stops
let mut remove_stops = HashSet::new();
for id in m.bus_stops.keys() {
if m.get_routes_serving_stop(*id).is_empty() {
for id in map.bus_stops.keys() {
if map.get_routes_serving_stop(*id).is_empty() {
remove_stops.insert(*id);
}
}
for id in &remove_stops {
m.bus_stops.remove(id);
m.lanes[id.sidewalk.0]
map.bus_stops.remove(id);
map.lanes[id.sidewalk.0]
.bus_stops
.retain(|stop| !remove_stops.contains(stop))
}
}
timer.start("setup rest of Pathfinder (walking with transit)");
let mut pathfinder = m.pathfinder.take().unwrap();
pathfinder.setup_walking_with_transit(&m);
m.pathfinder = Some(pathfinder);
let mut pathfinder = map.pathfinder.take().unwrap();
pathfinder.setup_walking_with_transit(&map);
map.pathfinder = Some(pathfinder);
timer.stop("setup rest of Pathfinder (walking with transit)");
}
let (_, disconnected) = connectivity::find_scc(&m, PathConstraints::Pedestrian);
let (_, disconnected) = connectivity::find_scc(&map, PathConstraints::Pedestrian);
if !disconnected.is_empty() {
timer.warn(format!(
"{} sidewalks are disconnected!",
@ -122,264 +364,10 @@ impl Map {
}
}
timer.stop("finalize Map");
m
map
}
}
fn make_half_map(
raw: &RawMap,
initial_map: initial::InitialMap,
gps_bounds: GPSBounds,
bounds: Bounds,
timer: &mut Timer,
) -> Map {
let mut map = Map {
roads: Vec::new(),
lanes: Vec::new(),
intersections: Vec::new(),
turns: BTreeMap::new(),
buildings: Vec::new(),
bus_stops: BTreeMap::new(),
bus_routes: Vec::new(),
areas: Vec::new(),
parking_lots: Vec::new(),
zones: Vec::new(),
boundary_polygon: raw.boundary_polygon.clone(),
stop_signs: BTreeMap::new(),
traffic_signals: BTreeMap::new(),
gps_bounds,
bounds,
driving_side: raw.driving_side,
pathfinder: None,
pathfinder_dirty: false,
city_name: raw.city_name.clone(),
name: raw.name.clone(),
edits: MapEdits::new(),
};
let road_id_mapping: BTreeMap<OriginalRoad, RoadID> = initial_map
.roads
.keys()
.enumerate()
.map(|(idx, id)| (*id, RoadID(idx)))
.collect();
let mut intersection_id_mapping: BTreeMap<OriginalIntersection, IntersectionID> =
BTreeMap::new();
for (idx, i) in initial_map.intersections.values().enumerate() {
let id = IntersectionID(idx);
map.intersections.push(Intersection {
id,
// IMPORTANT! We're relying on the triangulation algorithm not to mess with the order
// of the points. Sidewalk corner rendering depends on it later.
polygon: Polygon::new(&i.polygon),
turns: BTreeSet::new(),
elevation: i.elevation,
// Might change later
intersection_type: i.intersection_type,
orig_id: i.id,
incoming_lanes: Vec::new(),
outgoing_lanes: Vec::new(),
roads: i.roads.iter().map(|id| road_id_mapping[id]).collect(),
});
intersection_id_mapping.insert(i.id, id);
}
timer.start_iter("expand roads to lanes", initial_map.roads.len());
for r in initial_map.roads.values() {
timer.next();
let road_id = road_id_mapping[&r.id];
let i1 = intersection_id_mapping[&r.src_i];
let i2 = intersection_id_mapping[&r.dst_i];
let mut road = Road {
id: road_id,
osm_tags: raw.roads[&r.id].osm_tags.clone(),
turn_restrictions: raw.roads[&r.id]
.turn_restrictions
.iter()
.filter_map(|(rt, to)| {
if let Some(to) = road_id_mapping.get(to) {
Some((*rt, *to))
} else {
timer.warn(format!(
"Turn restriction from {} points to invalid dst {}",
r.id, to
));
None
}
})
.collect(),
complicated_turn_restrictions: raw.roads[&r.id]
.complicated_turn_restrictions
.iter()
.filter_map(|(via, to)| {
if let (Some(via), Some(to)) =
(road_id_mapping.get(via), road_id_mapping.get(to))
{
Some((*via, *to))
} else {
timer.warn(format!(
"Complicated turn restriction from {} has invalid via {} or dst {}",
r.id, via, to
));
None
}
})
.collect(),
orig_id: r.id,
children_forwards: Vec::new(),
children_backwards: Vec::new(),
center_pts: r.trimmed_center_pts.clone(),
src_i: i1,
dst_i: i2,
speed_limit: Speed::ZERO,
zorder: if let Some(layer) = raw.roads[&r.id].osm_tags.get("layer") {
layer.parse::<isize>().unwrap()
} else {
0
},
zone: None,
};
road.speed_limit = road.speed_limit_from_osm();
for lane in &r.lane_specs {
let id = LaneID(map.lanes.len());
let (src_i, dst_i) = if lane.reverse_pts { (i2, i1) } else { (i1, i2) };
map.intersections[src_i.0].outgoing_lanes.push(id);
map.intersections[dst_i.0].incoming_lanes.push(id);
let (unshifted_pts, other_lanes_width): (PolyLine, Distance) = {
let dir = lane.reverse_pts;
let w = road.width(&map, !dir);
road.children_mut(!dir).push((id, lane.lane_type));
if dir {
(road.center_pts.reversed(), w)
} else {
(road.center_pts.clone(), w)
}
};
// TODO probably different behavior for oneways
// TODO need to factor in yellow center lines (but what's the right thing to even do?
let width = if lane.lane_type == LaneType::Sidewalk {
SIDEWALK_THICKNESS
} else {
NORMAL_LANE_THICKNESS
};
let lane_center_pts = map
.right_shift(unshifted_pts, other_lanes_width + width / 2.0)
.with_context(timer, format!("shift for {}", id));
map.lanes.push(Lane {
id,
lane_center_pts,
width,
src_i,
dst_i,
lane_type: lane.lane_type,
parent: road_id,
building_paths: Vec::new(),
bus_stops: Vec::new(),
parking_blackhole: None,
});
}
if road.get_name() == "???" {
timer.warn(format!(
"{} has no name. Tags: {:?}",
road.id, road.osm_tags
));
}
map.roads.push(road);
}
for i in map.intersections.iter_mut() {
if is_border(i, &map.lanes) {
i.intersection_type = IntersectionType::Border;
}
if i.is_border() {
if i.roads.len() != 1 {
panic!(
"{} ({}) is a border, but is connected to >1 road: {:?}",
i.id, i.orig_id, i.roads
);
}
continue;
}
if i.is_closed() {
continue;
}
if i.intersection_type == IntersectionType::TrafficSignal {
let mut ok = false;
for r in &i.roads {
if map.roads[r.0].osm_tags.get(osm::HIGHWAY) != Some(&"construction".to_string()) {
ok = true;
break;
}
}
if !ok {
i.intersection_type = IntersectionType::StopSign;
}
}
if i.incoming_lanes.is_empty() || i.outgoing_lanes.is_empty() {
timer.warn(format!("{:?} is orphaned!", i));
continue;
}
for t in turns::make_all_turns(map.driving_side, i, &map.roads, &map.lanes, timer) {
assert!(!map.turns.contains_key(&t.id));
i.turns.insert(t.id);
if t.geom.length() < geom::EPSILON_DIST {
timer.warn(format!("{} is a very short turn", t.id));
}
map.turns.insert(t.id, t);
}
}
timer.start("find parking blackholes");
for (l, redirect) in connectivity::redirect_parking_blackholes(&map, timer) {
map.lanes[l.0].parking_blackhole = Some(redirect);
}
timer.stop("find parking blackholes");
map.buildings = buildings::make_all_buildings(&raw.buildings, &map, timer);
for b in &map.buildings {
let lane = b.sidewalk();
// TODO Could be more performant and cleanly written
let mut bldgs = map.lanes[lane.0].building_paths.clone();
bldgs.push(b.id);
bldgs.sort_by_key(|b| map.buildings[b.0].front_path.sidewalk.dist_along());
map.lanes[lane.0].building_paths = bldgs;
}
map.parking_lots =
buildings::make_all_parking_lots(&raw.parking_lots, &raw.parking_aisles, &map, timer);
map.zones = zones::make_all_zones(&map);
for z in &map.zones {
for r in &z.members {
map.roads[r.0].zone = Some(z.id);
}
}
for (idx, a) in raw.areas.iter().enumerate() {
map.areas.push(Area {
id: AreaID(idx),
area_type: a.area_type,
polygon: a.polygon.clone(),
osm_tags: a.osm_tags.clone(),
osm_id: a.osm_id,
});
}
bridges::find_bridges(&mut map.roads, &map.bounds, timer);
map
}
fn is_border(intersection: &Intersection, lanes: &Vec<Lane>) -> bool {
// RawIntersection said it is.
if intersection.is_border() {