adding a stable ID for raw roads and intersections. way easier to refer to things in the midst of deletions.

This commit is contained in:
Dustin Carlino 2019-01-17 08:46:31 -08:00
parent dfe50dcee4
commit 3422877d3d
13 changed files with 138 additions and 95 deletions

View File

@ -58,8 +58,7 @@ pub struct Flags {
pub fn convert(flags: &Flags, timer: &mut abstutil::Timer) -> raw_data::Map {
let elevation = Elevation::new(&flags.elevation).expect("loading .hgt failed");
let raw_map = osm::osm_to_raw_roads(&flags.osm, timer);
let mut map = split_ways::split_up_roads(raw_map, &elevation);
let mut map = split_ways::split_up_roads(osm::osm_to_raw_roads(&flags.osm, timer), &elevation);
remove_disconnected::remove_disconnected_roads(&mut map, timer);
let gps_bounds = map.get_gps_bounds();
@ -111,7 +110,7 @@ pub fn convert(flags: &Flags, timer: &mut abstutil::Timer) -> raw_data::Map {
// intersection
let closest_intersection = map
.intersections
.iter_mut()
.values_mut()
.min_by_key(|i| NotNan::new(distance(i)).unwrap())
.unwrap();
let dist = distance(closest_intersection);
@ -139,8 +138,9 @@ pub fn convert(flags: &Flags, timer: &mut abstutil::Timer) -> raw_data::Map {
fn use_parking_hints(map: &mut raw_data::Map, shapes: ExtraShapes, gps_bounds: &GPSBounds) {
// Match shapes with the nearest road + direction (true for forwards)
let mut closest: FindClosest<(usize, bool)> = FindClosest::new(&gps_bounds.to_bounds());
for (idx, r) in map.roads.iter().enumerate() {
let mut closest: FindClosest<(raw_data::StableRoadID, bool)> =
FindClosest::new(&gps_bounds.to_bounds());
for (id, r) in &map.roads {
let pts = PolyLine::new(
r.points
.iter()
@ -148,8 +148,8 @@ fn use_parking_hints(map: &mut raw_data::Map, shapes: ExtraShapes, gps_bounds: &
.collect(),
);
closest.add((idx, true), &pts.shift_right(LANE_THICKNESS));
closest.add((idx, false), &pts.shift_left(LANE_THICKNESS));
closest.add((*id, true), &pts.shift_right(LANE_THICKNESS));
closest.add((*id, false), &pts.shift_left(LANE_THICKNESS));
}
'SHAPE: for s in shapes.shapes.into_iter() {
@ -173,9 +173,9 @@ fn use_parking_hints(map: &mut raw_data::Map, shapes: ExtraShapes, gps_bounds: &
&& category != Some(&"No Parking Allowed".to_string());
// Blindly override prior values.
if fwds {
map.roads[r].parking_lane_fwd = has_parking;
map.roads.get_mut(&r).unwrap().parking_lane_fwd = has_parking;
} else {
map.roads[r].parking_lane_back = has_parking;
map.roads.get_mut(&r).unwrap().parking_lane_back = has_parking;
}
}
}

View File

@ -6,8 +6,14 @@ use map_model::{raw_data, AreaType};
use osm_xml;
use std::collections::{BTreeMap, HashMap};
// TODO Result, but is there an easy way to say io error or osm xml error?
pub fn osm_to_raw_roads(osm_path: &str, timer: &mut Timer) -> raw_data::Map {
pub fn osm_to_raw_roads(
osm_path: &str,
timer: &mut Timer,
) -> (
Vec<raw_data::Road>,
Vec<raw_data::Building>,
Vec<raw_data::Area>,
) {
let (reader, done) = FileWithProgress::new(osm_path).unwrap();
let doc = osm_xml::OSM::parse(reader).expect("OSM parsing failed");
println!(
@ -19,7 +25,9 @@ pub fn osm_to_raw_roads(osm_path: &str, timer: &mut Timer) -> raw_data::Map {
done(timer);
let mut id_to_way: HashMap<i64, Vec<LonLat>> = HashMap::new();
let mut map = raw_data::Map::blank();
let mut roads: Vec<raw_data::Road> = Vec::new();
let mut buildings: Vec<raw_data::Building> = Vec::new();
let mut areas: Vec<raw_data::Area> = Vec::new();
timer.start_iter("processing OSM ways", doc.ways.len());
for way in doc.ways.values() {
timer.next();
@ -42,7 +50,7 @@ pub fn osm_to_raw_roads(osm_path: &str, timer: &mut Timer) -> raw_data::Map {
}
let tags = tags_to_map(&way.tags);
if is_road(&tags) {
map.roads.push(raw_data::Road {
roads.push(raw_data::Road {
osm_way_id: way.id,
points: pts,
osm_tags: tags,
@ -51,13 +59,13 @@ pub fn osm_to_raw_roads(osm_path: &str, timer: &mut Timer) -> raw_data::Map {
parking_lane_back: false,
});
} else if is_bldg(&tags) {
map.buildings.push(raw_data::Building {
buildings.push(raw_data::Building {
osm_way_id: way.id,
points: pts,
osm_tags: tags,
});
} else if let Some(at) = get_area_type(&tags) {
map.areas.push(raw_data::Area {
areas.push(raw_data::Area {
area_type: at,
osm_way_id: way.id,
points: pts,
@ -81,7 +89,7 @@ pub fn osm_to_raw_roads(osm_path: &str, timer: &mut Timer) -> raw_data::Map {
match id_to_way.get(&id) {
Some(pts) => {
if role == "outer" {
map.areas.push(raw_data::Area {
areas.push(raw_data::Area {
area_type: at,
osm_way_id: id,
points: pts.to_vec(),
@ -108,7 +116,7 @@ pub fn osm_to_raw_roads(osm_path: &str, timer: &mut Timer) -> raw_data::Map {
}
}
map
(roads, buildings, areas)
}
fn tags_to_map(raw_tags: &[osm_xml::Tag]) -> BTreeMap<String, String> {

View File

@ -8,21 +8,19 @@ pub fn remove_disconnected_roads(map: &mut raw_data::Map, timer: &mut Timer) {
// This is a simple floodfill, not Tarjan's. Assumes all roads bidirectional.
// All the usizes are indices into the original list of roads
let mut next_roads: MultiMap<HashablePt2D, usize> = MultiMap::new();
for (idx, r) in map.roads.iter().enumerate() {
next_roads.insert(r.first_pt().to_hashable(), idx);
next_roads.insert(r.last_pt().to_hashable(), idx);
let mut next_roads: MultiMap<HashablePt2D, raw_data::StableRoadID> = MultiMap::new();
for (id, r) in &map.roads {
next_roads.insert(r.first_pt().to_hashable(), *id);
next_roads.insert(r.last_pt().to_hashable(), *id);
}
let mut partitions: Vec<Vec<usize>> = Vec::new();
let mut unvisited_roads: HashSet<usize> = HashSet::new();
for i in 0..map.roads.len() {
unvisited_roads.insert(i);
}
let mut partitions: Vec<Vec<raw_data::StableRoadID>> = Vec::new();
let mut unvisited_roads: HashSet<raw_data::StableRoadID> = map.roads.keys().cloned().collect();
while !unvisited_roads.is_empty() {
let mut queue_roads: Vec<usize> = vec![*unvisited_roads.iter().next().unwrap()];
let mut current_partition: Vec<usize> = Vec::new();
let mut queue_roads: Vec<raw_data::StableRoadID> =
vec![*unvisited_roads.iter().next().unwrap()];
let mut current_partition: Vec<raw_data::StableRoadID> = Vec::new();
while !queue_roads.is_empty() {
let current = queue_roads.pop().unwrap();
if !unvisited_roads.contains(&current) {
@ -31,7 +29,7 @@ pub fn remove_disconnected_roads(map: &mut raw_data::Map, timer: &mut Timer) {
unvisited_roads.remove(&current);
current_partition.push(current);
let current_r = &map.roads[current];
let current_r = &map.roads[&current];
for other_r in next_roads.get(current_r.first_pt().to_hashable()).iter() {
queue_roads.push(*other_r);
}
@ -45,26 +43,30 @@ pub fn remove_disconnected_roads(map: &mut raw_data::Map, timer: &mut Timer) {
partitions.sort_by_key(|roads| roads.len());
partitions.reverse();
println!("Main partition has {} roads", partitions[0].len());
let mut remove_roads = HashSet::new();
for p in partitions.iter().skip(1) {
println!("Removing disconnected partition with {} roads", p.len());
for idx in p {
remove_roads.insert(idx);
for id in p {
let r = map.roads.remove(id).unwrap();
next_roads.remove(r.first_pt().to_hashable(), *id);
next_roads.remove(r.last_pt().to_hashable(), *id);
}
}
let mut roads: Vec<raw_data::Road> = Vec::new();
for (idx, r) in map.roads.iter().enumerate() {
if remove_roads.contains(&idx) {
next_roads.remove(r.first_pt().to_hashable(), idx);
next_roads.remove(r.last_pt().to_hashable(), idx);
} else {
roads.push(r.clone());
}
}
map.roads = roads;
// Remove intersections without any roads
map.intersections
.retain(|i| !next_roads.get(i.point.to_hashable()).is_empty());
// TODO retain for BTreeMap, please!
let remove_intersections: Vec<raw_data::StableIntersectionID> = map
.intersections
.iter()
.filter_map(|(id, i)| {
if next_roads.get(i.point.to_hashable()).is_empty() {
Some(*id)
} else {
None
}
})
.collect();
for id in remove_intersections {
map.intersections.remove(&id);
}
timer.stop("removing disconnected roads");
}

View File

@ -6,14 +6,21 @@ use geom::{HashablePt2D, LonLat};
use map_model::{raw_data, IntersectionType};
use std::collections::{BTreeSet, HashMap};
pub fn split_up_roads(mut input: raw_data::Map, elevation: &srtm::Elevation) -> raw_data::Map {
println!("splitting up {} roads", input.roads.len());
pub fn split_up_roads(
(mut roads, buildings, areas): (
Vec<raw_data::Road>,
Vec<raw_data::Building>,
Vec<raw_data::Area>,
),
elevation: &srtm::Elevation,
) -> raw_data::Map {
println!("splitting up {} roads", roads.len());
// Look for roundabout ways. Map all points on the roundabout to a new point in the center.
// When we process ways that touch any point on the roundabout, make them instead point to the
// roundabout's center, so that the roundabout winds up looking like a single intersection.
let mut remap_roundabouts: HashMap<HashablePt2D, LonLat> = HashMap::new();
input.roads.retain(|r| {
roads.retain(|r| {
if r.osm_tags.get("junction") == Some(&"roundabout".to_string()) {
let center = LonLat::center(&r.points);
for pt in &r.points {
@ -27,7 +34,7 @@ pub fn split_up_roads(mut input: raw_data::Map, elevation: &srtm::Elevation) ->
let mut counts_per_pt: HashMap<HashablePt2D, usize> = HashMap::new();
let mut intersections: BTreeSet<HashablePt2D> = BTreeSet::new();
for r in input.roads.iter_mut() {
for r in roads.iter_mut() {
let added_to_start = if let Some(center) = remap_roundabouts.get(&r.points[0].to_hashable())
{
r.points.insert(0, *center);
@ -74,20 +81,23 @@ pub fn split_up_roads(mut input: raw_data::Map, elevation: &srtm::Elevation) ->
}
let mut map = raw_data::Map::blank();
map.buildings.extend(input.buildings.clone());
map.areas.extend(input.areas.clone());
map.buildings = buildings;
map.areas = areas;
for pt in &intersections {
map.intersections.push(raw_data::Intersection {
point: LonLat::new(pt.x(), pt.y()),
elevation: elevation.get(pt.x(), pt.y()) * si::M,
intersection_type: IntersectionType::StopSign,
label: None,
});
for (idx, pt) in intersections.iter().enumerate() {
map.intersections.insert(
raw_data::StableIntersectionID(idx),
raw_data::Intersection {
point: LonLat::new(pt.x(), pt.y()),
elevation: elevation.get(pt.x(), pt.y()) * si::M,
intersection_type: IntersectionType::StopSign,
label: None,
},
);
}
// Now actually split up the roads based on the intersections
for orig_road in &input.roads {
for orig_road in &roads {
let mut r = orig_road.clone();
r.points.clear();
@ -95,7 +105,8 @@ pub fn split_up_roads(mut input: raw_data::Map, elevation: &srtm::Elevation) ->
r.points.push(pt.clone());
if r.points.len() > 1 && intersections.contains(&pt.to_hashable()) {
// Start a new road
map.roads.push(r.clone());
map.roads
.insert(raw_data::StableRoadID(map.roads.len()), r.clone());
r.points.clear();
r.points.push(pt.clone());
}
@ -103,7 +114,5 @@ pub fn split_up_roads(mut input: raw_data::Map, elevation: &srtm::Elevation) ->
assert!(r.points.len() == 1);
}
// TODO we're somehow returning an intersection here with no roads. figure that out.
map
}

View File

@ -70,7 +70,10 @@ fn tooltip_lines(obj: ID, ctx: &Ctx) -> Text {
None,
);
txt.add_line(format!("From OSM way {}", r.osm_way_id));
txt.add_line(format!("Parent {} points to {}", r.id, r.dst_i));
txt.add_line(format!(
"Parent {} (stable ID {}) points to {}",
r.id, r.stable_id.0, r.dst_i
));
txt.add_line(format!(
"Lane goes from {} to {}",
i1.elevation, i2.elevation
@ -88,7 +91,9 @@ fn tooltip_lines(obj: ID, ctx: &Ctx) -> Text {
}
ID::Intersection(id) => {
txt.add_line(id.to_string());
txt.add_line(format!("Roads: {:?}", map.get_i(id).roads));
let i = map.get_i(id);
txt.add_line(format!("Roads: {:?}", i.roads));
txt.add_line(format!("Stable ID {}", i.stable_id.0));
}
ID::Turn(id) => {
let t = map.get_t(id);

View File

@ -304,8 +304,8 @@ impl ScreenCaptureState {
args.push("full.png".to_string());
let mut file = fs::File::create("screencap/combine.sh").unwrap();
write!(file, "#!/bin/bash\n\n").unwrap();
write!(file, "montage {}\n", args.join(" ")).unwrap();
write!(file, "rm -f combine.sh\n").unwrap();
writeln!(file, "#!/bin/bash\n").unwrap();
writeln!(file, "montage {}", args.join(" ")).unwrap();
writeln!(file, "rm -f combine.sh").unwrap();
}
}

View File

@ -1,6 +1,6 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
use crate::{LaneID, LaneType, Map, RoadID, TurnID};
use crate::{raw_data, LaneID, LaneType, Map, RoadID, TurnID};
use abstutil;
use dimensioned::si;
use geom::Pt2D;
@ -38,6 +38,7 @@ pub struct Intersection {
pub intersection_type: IntersectionType,
pub label: Option<String>,
pub stable_id: raw_data::StableIntersectionID,
// Note that a lane may belong to both incoming_lanes and outgoing_lanes.
// TODO narrow down when and why. is it just sidewalks in weird cases?

View File

@ -29,7 +29,7 @@ pub fn make_half_map(
let mut pt_to_intersection: HashMap<HashablePt2D, IntersectionID> = HashMap::new();
for (idx, i) in data.intersections.iter().enumerate() {
for (idx, (stable_id, i)) in data.intersections.iter().enumerate() {
let id = IntersectionID(idx);
let pt = Pt2D::from_gps(i.point, &gps_bounds).unwrap();
m.intersections.push(Intersection {
@ -41,6 +41,7 @@ pub fn make_half_map(
// Might change later
intersection_type: i.intersection_type,
label: i.label.clone(),
stable_id: *stable_id,
incoming_lanes: Vec::new(),
outgoing_lanes: Vec::new(),
roads: BTreeSet::new(),
@ -50,7 +51,7 @@ pub fn make_half_map(
let mut counter = 0;
timer.start_iter("expand roads to lanes", data.roads.len());
for (_, r) in data.roads.iter().enumerate() {
for (stable_id, r) in &data.roads {
timer.next();
let road_id = RoadID(m.roads.len());
let road_center_pts = PolyLine::new(
@ -75,6 +76,7 @@ pub fn make_half_map(
id: road_id,
osm_tags: r.osm_tags.clone(),
osm_way_id: r.osm_way_id,
stable_id: *stable_id,
children_forwards: Vec::new(),
children_backwards: Vec::new(),
center_pts: road_center_pts.clone(),

View File

@ -72,6 +72,7 @@ fn merge(delete_r: RoadID, mut m: HalfMap) -> HalfMap {
elevation: m.intersections[old_i1.0].elevation,
intersection_type: m.intersections[old_i1.0].intersection_type,
label: m.intersections[old_i1.0].label.clone(),
stable_id: m.intersections[old_i1.0].stable_id,
incoming_lanes: Vec::new(),
outgoing_lanes: Vec::new(),
roads: BTreeSet::new(),

View File

@ -6,10 +6,17 @@ use gtfs::Route;
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeMap;
// Stable IDs don't get compacted as we merge and delete things.
//#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct StableRoadID(pub usize);
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, PartialOrd, Ord, Clone, Copy, Hash)]
pub struct StableIntersectionID(pub usize);
#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub struct Map {
pub roads: Vec<Road>,
pub intersections: Vec<Intersection>,
pub roads: BTreeMap<StableRoadID, Road>,
pub intersections: BTreeMap<StableIntersectionID, Intersection>,
pub buildings: Vec<Building>,
pub parcels: Vec<Parcel>,
pub bus_routes: Vec<Route>,
@ -21,8 +28,8 @@ pub struct Map {
impl Map {
pub fn blank() -> Map {
Map {
roads: Vec::new(),
intersections: Vec::new(),
roads: BTreeMap::new(),
intersections: BTreeMap::new(),
buildings: Vec::new(),
parcels: Vec::new(),
bus_routes: Vec::new(),
@ -34,12 +41,12 @@ impl Map {
pub fn get_gps_bounds(&self) -> GPSBounds {
let mut bounds = GPSBounds::new();
for r in &self.roads {
for r in self.roads.values() {
for pt in &r.points {
bounds.update(*pt);
}
}
for i in &self.intersections {
for i in self.intersections.values() {
bounds.update(i.point);
}
for b in &self.buildings {

View File

@ -1,4 +1,4 @@
use crate::{IntersectionID, LaneID, LaneType};
use crate::{raw_data, IntersectionID, LaneID, LaneType};
use abstutil::Error;
use dimensioned::si;
use geom::PolyLine;
@ -22,6 +22,7 @@ pub struct Road {
pub id: RoadID,
pub osm_tags: BTreeMap<String, String>,
pub osm_way_id: i64,
pub stable_id: raw_data::StableRoadID,
// Invariant: A road must contain at least one child
pub children_forwards: Vec<(LaneID, LaneType)>,

View File

@ -336,25 +336,31 @@ impl Model {
if let Some(ref label) = r.back_label {
osm_tags.insert("back_label".to_string(), label.to_string());
}
map.roads.push(raw_data::Road {
points: vec![
pt(self.intersections[&r.i1].center),
pt(self.intersections[&r.i2].center),
],
osm_tags,
osm_way_id: idx as i64,
parking_lane_fwd: r.lanes.fwd.contains(&LaneType::Parking),
parking_lane_back: r.lanes.back.contains(&LaneType::Parking),
});
map.roads.insert(
raw_data::StableRoadID(idx),
raw_data::Road {
points: vec![
pt(self.intersections[&r.i1].center),
pt(self.intersections[&r.i2].center),
],
osm_tags,
osm_way_id: idx as i64,
parking_lane_fwd: r.lanes.fwd.contains(&LaneType::Parking),
parking_lane_back: r.lanes.back.contains(&LaneType::Parking),
},
);
}
for i in self.intersections.values() {
map.intersections.push(raw_data::Intersection {
point: pt(i.center),
elevation: 0.0 * si::M,
intersection_type: i.intersection_type,
label: i.label.clone(),
});
for (idx, i) in self.intersections.values().enumerate() {
map.intersections.insert(
raw_data::StableIntersectionID(idx),
raw_data::Intersection {
point: pt(i.center),
elevation: 0.0 * si::M,
intersection_type: i.intersection_type,
label: i.label.clone(),
},
);
}
for (idx, b) in self.buildings.values().enumerate() {
@ -387,7 +393,7 @@ impl Model {
let mut pt_to_intersection: HashMap<HashablePt2D, IntersectionID> = HashMap::new();
let mut quadtree = QuadTree::default(gps_bounds.to_bounds().as_bbox());
for (idx, i) in data.intersections.iter().enumerate() {
for (idx, i) in data.intersections.values().enumerate() {
let center = Pt2D::from_gps(i.point, &gps_bounds).unwrap();
let i = Intersection {
center,
@ -399,7 +405,7 @@ impl Model {
pt_to_intersection.insert(center.into(), idx);
}
for r in &data.roads {
for r in data.roads.values() {
let i1 = pt_to_intersection[&Pt2D::from_gps(r.points[0], &gps_bounds).unwrap().into()];
let i2 = pt_to_intersection[&Pt2D::from_gps(*r.points.last().unwrap(), &gps_bounds)
.unwrap()

View File

@ -16,6 +16,7 @@ pub fn run(t: &mut TestRunner) {
gtfs: "../data/input/google_transit_2018_18_08".to_string(),
neighborhoods: "../data/input/neighborhoods.geojson".to_string(),
output: "convert_osm_twice".to_string(),
fast_dev: false,
};
let map1 = convert_osm::convert(&flags, &mut abstutil::Timer::new("convert map"));