mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-25 11:44:25 +03:00
more prep for merging intersections... make an intermediate HalfMap structure
This commit is contained in:
parent
de5c02a6c4
commit
42788363ab
@ -154,5 +154,6 @@ Shelby and 23rd...
|
||||
- populate turns
|
||||
|
||||
- then do the merging magic
|
||||
- need to fix up IDs everywhere
|
||||
|
||||
- ... rest of map construction proceeds
|
||||
|
195
map_model/src/make/half_map.rs
Normal file
195
map_model/src/make/half_map.rs
Normal file
@ -0,0 +1,195 @@
|
||||
use crate::make;
|
||||
use crate::{
|
||||
raw_data, Intersection, IntersectionID, IntersectionType, Lane, LaneID, MapEdits, Road, RoadID,
|
||||
Turn, TurnID, LANE_THICKNESS,
|
||||
};
|
||||
use abstutil::Timer;
|
||||
use geom::{GPSBounds, HashablePt2D, PolyLine, Pt2D};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
|
||||
pub struct HalfMap {
|
||||
pub roads: Vec<Road>,
|
||||
pub lanes: Vec<Lane>,
|
||||
pub intersections: Vec<Intersection>,
|
||||
pub turns: BTreeMap<TurnID, Turn>,
|
||||
}
|
||||
|
||||
pub fn make_half_map(
|
||||
data: &raw_data::Map,
|
||||
gps_bounds: &GPSBounds,
|
||||
edits: &MapEdits,
|
||||
timer: &mut Timer,
|
||||
) -> HalfMap {
|
||||
let mut m = HalfMap {
|
||||
roads: Vec::new(),
|
||||
lanes: Vec::new(),
|
||||
intersections: Vec::new(),
|
||||
turns: BTreeMap::new(),
|
||||
};
|
||||
|
||||
let mut pt_to_intersection: HashMap<HashablePt2D, IntersectionID> = HashMap::new();
|
||||
|
||||
for (idx, i) in data.intersections.iter().enumerate() {
|
||||
let id = IntersectionID(idx);
|
||||
let pt = Pt2D::from_gps(i.point, &gps_bounds).unwrap();
|
||||
m.intersections.push(Intersection {
|
||||
id,
|
||||
point: pt,
|
||||
polygon: Vec::new(),
|
||||
turns: Vec::new(),
|
||||
elevation: i.elevation,
|
||||
// Might change later
|
||||
intersection_type: i.intersection_type,
|
||||
label: i.label.clone(),
|
||||
incoming_lanes: Vec::new(),
|
||||
outgoing_lanes: Vec::new(),
|
||||
roads: BTreeSet::new(),
|
||||
});
|
||||
pt_to_intersection.insert(HashablePt2D::from(pt), id);
|
||||
}
|
||||
|
||||
let mut counter = 0;
|
||||
timer.start_iter("expand roads to lanes", data.roads.len());
|
||||
for (_, r) in data.roads.iter().enumerate() {
|
||||
timer.next();
|
||||
let road_id = RoadID(m.roads.len());
|
||||
let road_center_pts = PolyLine::new(
|
||||
r.points
|
||||
.iter()
|
||||
.map(|coord| Pt2D::from_gps(*coord, &gps_bounds).unwrap())
|
||||
.collect(),
|
||||
);
|
||||
let i1 = pt_to_intersection[&HashablePt2D::from(road_center_pts.first_pt())];
|
||||
let i2 = pt_to_intersection[&HashablePt2D::from(road_center_pts.last_pt())];
|
||||
|
||||
if i1 == 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, i1, road_id
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
m.roads.push(Road {
|
||||
id: road_id,
|
||||
osm_tags: r.osm_tags.clone(),
|
||||
osm_way_id: r.osm_way_id,
|
||||
children_forwards: Vec::new(),
|
||||
children_backwards: Vec::new(),
|
||||
center_pts: road_center_pts.clone(),
|
||||
src_i: i1,
|
||||
dst_i: i2,
|
||||
});
|
||||
|
||||
// TODO move this to make/lanes.rs too
|
||||
for lane in make::lanes::get_lane_specs(r, road_id, edits) {
|
||||
let id = LaneID(counter);
|
||||
counter += 1;
|
||||
|
||||
let mut unshifted_pts = road_center_pts.clone();
|
||||
if lane.reverse_pts {
|
||||
unshifted_pts = unshifted_pts.reversed();
|
||||
}
|
||||
let (src_i, dst_i) = if lane.reverse_pts { (i2, i1) } else { (i1, i2) };
|
||||
m.intersections[src_i.0].outgoing_lanes.push(id);
|
||||
m.intersections[src_i.0].roads.insert(road_id);
|
||||
m.intersections[dst_i.0].incoming_lanes.push(id);
|
||||
m.intersections[dst_i.0].roads.insert(road_id);
|
||||
|
||||
// TODO probably different behavior for oneways
|
||||
// TODO need to factor in yellow center lines (but what's the right thing to even do?
|
||||
// Reverse points for British-style driving on the left
|
||||
let width = LANE_THICKNESS * (0.5 + f64::from(lane.offset));
|
||||
let (lane_center_pts, probably_broken) = match unshifted_pts.shift(width) {
|
||||
Some(pts) => (pts, false),
|
||||
// TODO wasteful to calculate again, but eh
|
||||
None => (unshifted_pts.shift_blindly(width), true),
|
||||
};
|
||||
|
||||
// lane_center_pts will get updated in the next pass
|
||||
m.lanes.push(Lane {
|
||||
id,
|
||||
lane_center_pts,
|
||||
probably_broken,
|
||||
src_i,
|
||||
dst_i,
|
||||
lane_type: lane.lane_type,
|
||||
parent: road_id,
|
||||
building_paths: Vec::new(),
|
||||
bus_stops: Vec::new(),
|
||||
});
|
||||
if lane.reverse_pts {
|
||||
m.roads[road_id.0]
|
||||
.children_backwards
|
||||
.push((id, lane.lane_type));
|
||||
} else {
|
||||
m.roads[road_id.0]
|
||||
.children_forwards
|
||||
.push((id, lane.lane_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i in m.intersections.iter_mut() {
|
||||
// Is the intersection a border?
|
||||
if is_border(i, &m.lanes) {
|
||||
i.intersection_type = IntersectionType::Border;
|
||||
}
|
||||
}
|
||||
|
||||
timer.start_iter("find each intersection polygon", m.intersections.len());
|
||||
for i in m.intersections.iter_mut() {
|
||||
timer.next();
|
||||
|
||||
if i.incoming_lanes.is_empty() && i.outgoing_lanes.is_empty() {
|
||||
panic!("{:?} is orphaned!", i);
|
||||
}
|
||||
|
||||
i.polygon = make::intersections::intersection_polygon(i, &m.roads);
|
||||
}
|
||||
|
||||
timer.start_iter("trim lanes at each intersection", m.intersections.len());
|
||||
for i in &m.intersections {
|
||||
timer.next();
|
||||
make::trim_lines::trim_lines(&mut m.lanes, i);
|
||||
}
|
||||
|
||||
for i in m.intersections.iter_mut() {
|
||||
for t in
|
||||
make::turns::make_all_turns(i, &m.roads.iter().collect(), &m.lanes.iter().collect())
|
||||
{
|
||||
assert!(!m.turns.contains_key(&t.id));
|
||||
i.turns.push(t.id);
|
||||
m.turns.insert(t.id, t);
|
||||
}
|
||||
}
|
||||
|
||||
m
|
||||
}
|
||||
|
||||
fn is_border(intersection: &Intersection, lanes: &Vec<Lane>) -> bool {
|
||||
// Raw data said it is.
|
||||
if intersection.intersection_type == IntersectionType::Border {
|
||||
if !intersection.is_dead_end() {
|
||||
panic!(
|
||||
"{:?} isn't a dead-end, but raw data said it's a border node",
|
||||
intersection
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Bias for driving
|
||||
if !intersection.is_dead_end() {
|
||||
return false;
|
||||
}
|
||||
let has_driving_in = intersection
|
||||
.incoming_lanes
|
||||
.iter()
|
||||
.any(|l| lanes[l.0].is_driving());
|
||||
let has_driving_out = intersection
|
||||
.outgoing_lanes
|
||||
.iter()
|
||||
.any(|l| lanes[l.0].is_driving());
|
||||
has_driving_in != has_driving_out
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
mod buildings;
|
||||
mod bus_stops;
|
||||
mod half_map;
|
||||
mod intersections;
|
||||
mod lanes;
|
||||
mod merge_intersections;
|
||||
@ -10,9 +11,7 @@ mod turns;
|
||||
|
||||
pub use self::buildings::make_all_buildings;
|
||||
pub use self::bus_stops::{make_bus_stops, verify_bus_routes};
|
||||
pub use self::intersections::intersection_polygon;
|
||||
pub use self::lanes::{get_lane_specs, RoadSpec};
|
||||
pub use self::half_map::make_half_map;
|
||||
pub use self::lanes::RoadSpec;
|
||||
pub use self::merge_intersections::merge_intersections;
|
||||
pub use self::parcels::make_all_parcels;
|
||||
pub use self::trim_lines::trim_lines;
|
||||
pub use self::turns::make_all_turns;
|
||||
|
@ -4,13 +4,12 @@ use crate::{
|
||||
make, raw_data, Area, AreaID, Building, BuildingID, BusRoute, BusRouteID, BusStop, BusStopID,
|
||||
ControlStopSign, ControlTrafficSignal, Intersection, IntersectionID, IntersectionType, Lane,
|
||||
LaneID, LaneType, MapEdits, Parcel, ParcelID, Road, RoadID, Turn, TurnID, TurnPriority,
|
||||
LANE_THICKNESS,
|
||||
};
|
||||
use abstutil;
|
||||
use abstutil::{deserialize_btreemap, serialize_btreemap, Error, Timer};
|
||||
use geom::{Bounds, GPSBounds, HashablePt2D, PolyLine, Pt2D};
|
||||
use geom::{Bounds, GPSBounds, Pt2D};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::io;
|
||||
use std::path;
|
||||
|
||||
@ -77,15 +76,16 @@ impl Map {
|
||||
let gps_bounds = data.get_gps_bounds();
|
||||
let bounds = gps_bounds.to_bounds();
|
||||
|
||||
let half_map = make::make_half_map(&data, &gps_bounds, &edits, timer);
|
||||
let mut m = Map {
|
||||
name,
|
||||
edits,
|
||||
gps_bounds: gps_bounds.clone(),
|
||||
bounds: bounds.clone(),
|
||||
roads: Vec::new(),
|
||||
lanes: Vec::new(),
|
||||
intersections: Vec::new(),
|
||||
turns: BTreeMap::new(),
|
||||
roads: half_map.roads,
|
||||
lanes: half_map.lanes,
|
||||
intersections: half_map.intersections,
|
||||
turns: half_map.turns,
|
||||
buildings: Vec::new(),
|
||||
parcels: Vec::new(),
|
||||
bus_stops: BTreeMap::new(),
|
||||
@ -95,145 +95,9 @@ impl Map {
|
||||
traffic_signals: BTreeMap::new(),
|
||||
turn_lookup: Vec::new(),
|
||||
};
|
||||
|
||||
let mut pt_to_intersection: HashMap<HashablePt2D, IntersectionID> = HashMap::new();
|
||||
|
||||
for (idx, i) in data.intersections.iter().enumerate() {
|
||||
let id = IntersectionID(idx);
|
||||
let pt = Pt2D::from_gps(i.point, &gps_bounds).unwrap();
|
||||
m.intersections.push(Intersection {
|
||||
id,
|
||||
point: pt,
|
||||
polygon: Vec::new(),
|
||||
turns: Vec::new(),
|
||||
elevation: i.elevation,
|
||||
// Might change later
|
||||
intersection_type: i.intersection_type,
|
||||
label: i.label.clone(),
|
||||
incoming_lanes: Vec::new(),
|
||||
outgoing_lanes: Vec::new(),
|
||||
roads: BTreeSet::new(),
|
||||
});
|
||||
pt_to_intersection.insert(HashablePt2D::from(pt), id);
|
||||
}
|
||||
|
||||
let mut counter = 0;
|
||||
timer.start_iter("expand roads to lanes", data.roads.len());
|
||||
for (_, r) in data.roads.iter().enumerate() {
|
||||
timer.next();
|
||||
let road_id = RoadID(m.roads.len());
|
||||
let road_center_pts = PolyLine::new(
|
||||
r.points
|
||||
.iter()
|
||||
.map(|coord| Pt2D::from_gps(*coord, &gps_bounds).unwrap())
|
||||
.collect(),
|
||||
);
|
||||
let i1 = pt_to_intersection[&HashablePt2D::from(road_center_pts.first_pt())];
|
||||
let i2 = pt_to_intersection[&HashablePt2D::from(road_center_pts.last_pt())];
|
||||
|
||||
if i1 == 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, i1, road_id
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
m.roads.push(Road {
|
||||
id: road_id,
|
||||
osm_tags: r.osm_tags.clone(),
|
||||
osm_way_id: r.osm_way_id,
|
||||
children_forwards: Vec::new(),
|
||||
children_backwards: Vec::new(),
|
||||
center_pts: road_center_pts.clone(),
|
||||
src_i: i1,
|
||||
dst_i: i2,
|
||||
});
|
||||
|
||||
// TODO move this to make/lanes.rs too
|
||||
for lane in make::get_lane_specs(r, road_id, &m.edits) {
|
||||
let id = LaneID(counter);
|
||||
counter += 1;
|
||||
|
||||
let mut unshifted_pts = road_center_pts.clone();
|
||||
if lane.reverse_pts {
|
||||
unshifted_pts = unshifted_pts.reversed();
|
||||
}
|
||||
let (src_i, dst_i) = if lane.reverse_pts { (i2, i1) } else { (i1, i2) };
|
||||
m.intersections[src_i.0].outgoing_lanes.push(id);
|
||||
m.intersections[src_i.0].roads.insert(road_id);
|
||||
m.intersections[dst_i.0].incoming_lanes.push(id);
|
||||
m.intersections[dst_i.0].roads.insert(road_id);
|
||||
|
||||
// TODO probably different behavior for oneways
|
||||
// TODO need to factor in yellow center lines (but what's the right thing to even do?
|
||||
// Reverse points for British-style driving on the left
|
||||
let width = LANE_THICKNESS * (0.5 + f64::from(lane.offset));
|
||||
let (lane_center_pts, probably_broken) = match unshifted_pts.shift(width) {
|
||||
Some(pts) => (pts, false),
|
||||
// TODO wasteful to calculate again, but eh
|
||||
None => (unshifted_pts.shift_blindly(width), true),
|
||||
};
|
||||
|
||||
// lane_center_pts will get updated in the next pass
|
||||
m.lanes.push(Lane {
|
||||
id,
|
||||
lane_center_pts,
|
||||
probably_broken,
|
||||
src_i,
|
||||
dst_i,
|
||||
lane_type: lane.lane_type,
|
||||
parent: road_id,
|
||||
building_paths: Vec::new(),
|
||||
bus_stops: Vec::new(),
|
||||
});
|
||||
if lane.reverse_pts {
|
||||
m.roads[road_id.0]
|
||||
.children_backwards
|
||||
.push((id, lane.lane_type));
|
||||
} else {
|
||||
m.roads[road_id.0]
|
||||
.children_forwards
|
||||
.push((id, lane.lane_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i in m.intersections.iter_mut() {
|
||||
// Is the intersection a border?
|
||||
if is_border(i, &m.lanes) {
|
||||
i.intersection_type = IntersectionType::Border;
|
||||
}
|
||||
}
|
||||
|
||||
timer.start_iter("find each intersection polygon", m.intersections.len());
|
||||
for i in m.intersections.iter_mut() {
|
||||
timer.next();
|
||||
|
||||
if i.incoming_lanes.is_empty() && i.outgoing_lanes.is_empty() {
|
||||
panic!("{:?} is orphaned!", i);
|
||||
}
|
||||
|
||||
i.polygon = make::intersection_polygon(i, &m.roads);
|
||||
}
|
||||
|
||||
timer.start_iter("trim lanes at each intersection", m.intersections.len());
|
||||
for i in &m.intersections {
|
||||
timer.next();
|
||||
make::trim_lines(&mut m.lanes, i);
|
||||
}
|
||||
|
||||
for i in m.intersections.iter_mut() {
|
||||
for t in make::make_all_turns(i, &m.roads.iter().collect(), &m.lanes.iter().collect()) {
|
||||
assert!(!m.turns.contains_key(&t.id));
|
||||
i.turns.push(t.id);
|
||||
m.turn_lookup.push(t.id);
|
||||
m.turns.insert(t.id, t);
|
||||
}
|
||||
}
|
||||
for (idx, t) in m.turns.values_mut().enumerate() {
|
||||
t.lookup_idx = idx;
|
||||
for t in m.turns.values_mut() {
|
||||
t.lookup_idx = m.turn_lookup.len();
|
||||
m.turn_lookup.push(t.id);
|
||||
}
|
||||
|
||||
// TODO Merge intersections
|
||||
@ -332,15 +196,7 @@ impl Map {
|
||||
}
|
||||
self.intersections[i.0].turns.clear();
|
||||
|
||||
for t in make::make_all_turns(
|
||||
self.get_i(i),
|
||||
&self.roads.iter().collect(),
|
||||
&self.lanes.iter().collect(),
|
||||
) {
|
||||
// TODO ahh need to dedupe
|
||||
self.intersections[i.0].turns.push(t.id);
|
||||
self.turns.insert(t.id, t);
|
||||
}
|
||||
// TODO Actually recalculate. This got complicated after merging intersections.
|
||||
}
|
||||
}
|
||||
|
||||
@ -680,29 +536,3 @@ impl Map {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_border(intersection: &Intersection, lanes: &Vec<Lane>) -> bool {
|
||||
// Raw data said it is.
|
||||
if intersection.intersection_type == IntersectionType::Border {
|
||||
if !intersection.is_dead_end() {
|
||||
panic!(
|
||||
"{:?} isn't a dead-end, but raw data said it's a border node",
|
||||
intersection
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// Bias for driving
|
||||
if !intersection.is_dead_end() {
|
||||
return false;
|
||||
}
|
||||
let has_driving_in = intersection
|
||||
.incoming_lanes
|
||||
.iter()
|
||||
.any(|l| lanes[l.0].is_driving());
|
||||
let has_driving_out = intersection
|
||||
.outgoing_lanes
|
||||
.iter()
|
||||
.any(|l| lanes[l.0].is_driving());
|
||||
has_driving_in != has_driving_out
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user