simplify dataflow a bit in convert_osm

This commit is contained in:
Dustin Carlino 2019-08-26 15:22:03 -07:00
parent ead5593448
commit a5fc3b092f
4 changed files with 62 additions and 73 deletions

View File

@ -1,13 +1,14 @@
use abstutil::{retain_btreemap, Timer}; use abstutil::{retain_btreemap, Timer};
use geom::{LonLat, PolyLine, Polygon}; use geom::PolyLine;
use map_model::{raw_data, IntersectionType}; use map_model::{raw_data, IntersectionType};
pub fn clip_map(map: &mut raw_data::Map, boundary_poly_pts: Vec<LonLat>, timer: &mut Timer) { pub fn clip_map(map: &mut raw_data::Map, timer: &mut Timer) {
timer.start("clipping map to boundary"); timer.start("clipping map to boundary");
let boundary_poly = Polygon::new(&map.gps_bounds.must_convert(&boundary_poly_pts)); // So we can use retain_btreemap without borrowing issues
map.boundary_polygon = boundary_poly.clone(); let boundary_polygon = map.boundary_polygon.clone();
let boundary_lines: Vec<PolyLine> = boundary_poly let boundary_lines: Vec<PolyLine> = map
.boundary_polygon
.points() .points()
.windows(2) .windows(2)
.map(|pair| PolyLine::new(pair.to_vec())) .map(|pair| PolyLine::new(pair.to_vec()))
@ -16,16 +17,18 @@ pub fn clip_map(map: &mut raw_data::Map, boundary_poly_pts: Vec<LonLat>, timer:
// This is kind of indirect and slow, but first pass -- just remove roads that start or end // This is kind of indirect and slow, but first pass -- just remove roads that start or end
// outside the boundary polygon. // outside the boundary polygon.
retain_btreemap(&mut map.roads, |_, r| { retain_btreemap(&mut map.roads, |_, r| {
let first_in = boundary_poly.contains_pt(r.center_points[0]); let first_in = boundary_polygon.contains_pt(r.center_points[0]);
let last_in = boundary_poly.contains_pt(*r.center_points.last().unwrap()); let last_in = boundary_polygon.contains_pt(*r.center_points.last().unwrap());
first_in || last_in first_in || last_in
}); });
let road_ids: Vec<raw_data::StableRoadID> = map.roads.keys().cloned().collect(); let road_ids: Vec<raw_data::StableRoadID> = map.roads.keys().cloned().collect();
for id in road_ids { for id in road_ids {
let r = &map.roads[&id]; let r = &map.roads[&id];
let first_in = boundary_poly.contains_pt(r.center_points[0]); let first_in = map.boundary_polygon.contains_pt(r.center_points[0]);
let last_in = boundary_poly.contains_pt(*r.center_points.last().unwrap()); let last_in = map
.boundary_polygon
.contains_pt(*r.center_points.last().unwrap());
// Some roads start and end in-bounds, but dip out of bounds. Leave those alone for now. // Some roads start and end in-bounds, but dip out of bounds. Leave those alone for now.
if first_in && last_in { if first_in && last_in {
@ -93,7 +96,7 @@ pub fn clip_map(map: &mut raw_data::Map, boundary_poly_pts: Vec<LonLat>, timer:
b.polygon b.polygon
.points() .points()
.iter() .iter()
.all(|pt| boundary_poly.contains_pt(*pt)) .all(|pt| boundary_polygon.contains_pt(*pt))
}); });
let mut result_areas = Vec::new(); let mut result_areas = Vec::new();

View File

@ -5,7 +5,7 @@ mod remove_disconnected;
mod split_ways; mod split_ways;
use abstutil::Timer; use abstutil::Timer;
use geom::{Distance, FindClosest, GPSBounds, LonLat, PolyLine, Pt2D}; use geom::{Distance, FindClosest, GPSBounds, LonLat, PolyLine, Polygon, Pt2D};
use kml::ExtraShapes; use kml::ExtraShapes;
use map_model::{raw_data, OffstreetParking, LANE_THICKNESS}; use map_model::{raw_data, OffstreetParking, LANE_THICKNESS};
use std::fs::File; use std::fs::File;
@ -52,18 +52,11 @@ pub fn convert(flags: &Flags, timer: &mut abstutil::Timer) -> raw_data::Map {
if flags.clip.is_empty() { if flags.clip.is_empty() {
panic!("You must specify an Osmosis boundary polygon with --clip"); panic!("You must specify an Osmosis boundary polygon with --clip");
} }
let boundary_polygon_pts = read_osmosis_polygon(&flags.clip);
let mut gps_bounds = GPSBounds::new();
for pt in &boundary_polygon_pts {
gps_bounds.update(*pt);
}
let mut map = split_ways::split_up_roads( let mut map = split_ways::split_up_roads(
osm::osm_to_raw_roads(&flags.osm, &gps_bounds, timer), osm::extract_osm(&flags.osm, read_osmosis_polygon(&flags.clip), timer),
gps_bounds,
timer, timer,
); );
clip::clip_map(&mut map, boundary_polygon_pts, timer); clip::clip_map(&mut map, timer);
remove_disconnected::remove_disconnected_roads(&mut map, timer); remove_disconnected::remove_disconnected_roads(&mut map, timer);
if flags.fast_dev { if flags.fast_dev {
@ -143,8 +136,9 @@ fn use_parking_hints(map: &mut raw_data::Map, path: &str, timer: &mut Timer) {
timer.stop("apply parking hints"); timer.stop("apply parking hints");
} }
fn read_osmosis_polygon(path: &str) -> Vec<LonLat> { fn read_osmosis_polygon(path: &str) -> raw_data::Map {
let mut pts: Vec<LonLat> = Vec::new(); let mut pts: Vec<LonLat> = Vec::new();
let mut gps_bounds = GPSBounds::new();
for (idx, maybe_line) in BufReader::new(File::open(path).unwrap()) for (idx, maybe_line) in BufReader::new(File::open(path).unwrap())
.lines() .lines()
.enumerate() .enumerate()
@ -158,11 +152,18 @@ fn read_osmosis_polygon(path: &str) -> Vec<LonLat> {
} }
let parts: Vec<&str> = line.trim_start().split(" ").collect(); let parts: Vec<&str> = line.trim_start().split(" ").collect();
assert!(parts.len() == 2); assert!(parts.len() == 2);
let lon = parts[0].parse::<f64>().unwrap(); let pt = LonLat::new(
let lat = parts[1].parse::<f64>().unwrap(); parts[0].parse::<f64>().unwrap(),
pts.push(LonLat::new(lon, lat)); parts[1].parse::<f64>().unwrap(),
);
pts.push(pt);
gps_bounds.update(pt);
} }
pts
let mut map = raw_data::Map::blank();
map.boundary_polygon = Polygon::new(&gps_bounds.must_convert(&pts));
map.gps_bounds = gps_bounds;
map
} }
fn use_offstreet_parking(map: &mut raw_data::Map, path: &str, timer: &mut Timer) { fn use_offstreet_parking(map: &mut raw_data::Map, path: &str, timer: &mut Timer) {

View File

@ -1,21 +1,19 @@
use abstutil::{FileWithProgress, Timer}; use abstutil::{FileWithProgress, Timer};
use geom::{GPSBounds, HashablePt2D, LonLat, Polygon, Pt2D}; use geom::{HashablePt2D, LonLat, Polygon, Pt2D};
use map_model::{raw_data, AreaType}; use map_model::{raw_data, AreaType};
use osm_xml; use osm_xml;
use std::collections::{BTreeMap, HashMap, HashSet}; use std::collections::{BTreeMap, HashMap, HashSet};
pub fn osm_to_raw_roads( pub fn extract_osm(
osm_path: &str, osm_path: &str,
gps_bounds: &GPSBounds, mut map: raw_data::Map,
timer: &mut Timer, timer: &mut Timer,
) -> ( ) -> (
raw_data::Map,
// Un-split roads
Vec<raw_data::Road>, Vec<raw_data::Road>,
Vec<raw_data::Building>, // Traffic signals
Vec<raw_data::Area>,
// traffic signals as Pt2D
HashSet<HashablePt2D>, HashSet<HashablePt2D>,
// turn restrictions
BTreeMap<i64, Vec<(String, i64)>>,
) { ) {
let (reader, done) = FileWithProgress::new(osm_path).unwrap(); let (reader, done) = FileWithProgress::new(osm_path).unwrap();
let doc = osm_xml::OSM::parse(reader).expect("OSM parsing failed"); let doc = osm_xml::OSM::parse(reader).expect("OSM parsing failed");
@ -27,11 +25,8 @@ pub fn osm_to_raw_roads(
); );
done(timer); done(timer);
let mut id_to_way: HashMap<i64, Vec<LonLat>> = HashMap::new(); let mut id_to_way: HashMap<i64, Vec<Pt2D>> = HashMap::new();
let mut roads: Vec<raw_data::Road> = Vec::new(); 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();
let mut turn_restrictions: BTreeMap<i64, Vec<(String, i64)>> = BTreeMap::new();
let mut traffic_signals: HashSet<HashablePt2D> = HashSet::new(); let mut traffic_signals: HashSet<HashablePt2D> = HashSet::new();
timer.start_iter("processing OSM nodes", doc.nodes.len()); timer.start_iter("processing OSM nodes", doc.nodes.len());
@ -40,7 +35,8 @@ pub fn osm_to_raw_roads(
let tags = tags_to_map(&node.tags); let tags = tags_to_map(&node.tags);
if tags.get("highway") == Some(&"traffic_signals".to_string()) { if tags.get("highway") == Some(&"traffic_signals".to_string()) {
traffic_signals.insert( traffic_signals.insert(
Pt2D::forcibly_from_gps(LonLat::new(node.lon, node.lat), gps_bounds).to_hashable(), Pt2D::forcibly_from_gps(LonLat::new(node.lon, node.lat), &map.gps_bounds)
.to_hashable(),
); );
} }
} }
@ -50,11 +46,11 @@ pub fn osm_to_raw_roads(
timer.next(); timer.next();
let mut valid = true; let mut valid = true;
let mut pts = Vec::new(); let mut gps_pts = Vec::new();
for node_ref in &way.nodes { for node_ref in &way.nodes {
match doc.resolve_reference(node_ref) { match doc.resolve_reference(node_ref) {
osm_xml::Reference::Node(node) => { osm_xml::Reference::Node(node) => {
pts.push(LonLat::new(node.lon, node.lat)); gps_pts.push(LonLat::new(node.lon, node.lat));
} }
// Don't handle nested ways/relations yet // Don't handle nested ways/relations yet
_ => { _ => {
@ -65,14 +61,15 @@ pub fn osm_to_raw_roads(
if !valid { if !valid {
continue; continue;
} }
let pts = map.gps_bounds.forcibly_convert(&gps_pts);
let tags = tags_to_map(&way.tags); let tags = tags_to_map(&way.tags);
if is_road(&tags) { if is_road(&tags) {
roads.push(raw_data::Road { roads.push(raw_data::Road {
osm_way_id: way.id, osm_way_id: way.id,
center_points: gps_bounds.forcibly_convert(&pts), center_points: pts,
orig_id: raw_data::OriginalRoad { orig_id: raw_data::OriginalRoad {
pt1: pts[0], pt1: gps_pts[0],
pt2: *pts.last().unwrap(), pt2: *gps_pts.last().unwrap(),
}, },
osm_tags: tags, osm_tags: tags,
// We'll fill this out later // We'll fill this out later
@ -82,20 +79,17 @@ pub fn osm_to_raw_roads(
parking_lane_back: false, parking_lane_back: false,
}); });
} else if is_bldg(&tags) { } else if is_bldg(&tags) {
buildings.push(raw_data::Building { map.buildings.push(raw_data::Building {
osm_way_id: way.id, osm_way_id: way.id,
polygon: Polygon::new(&Pt2D::approx_dedupe( polygon: Polygon::new(&Pt2D::approx_dedupe(pts, geom::EPSILON_DIST)),
gps_bounds.forcibly_convert(&pts),
geom::EPSILON_DIST,
)),
osm_tags: tags, osm_tags: tags,
parking: None, parking: None,
}); });
} else if let Some(at) = get_area_type(&tags) { } else if let Some(at) = get_area_type(&tags) {
areas.push(raw_data::Area { map.areas.push(raw_data::Area {
area_type: at, area_type: at,
osm_id: way.id, osm_id: way.id,
polygon: Polygon::new(&gps_bounds.forcibly_convert(&pts)), polygon: Polygon::new(&pts),
osm_tags: tags, osm_tags: tags,
}); });
} else { } else {
@ -111,7 +105,7 @@ pub fn osm_to_raw_roads(
if let Some(at) = get_area_type(&tags) { if let Some(at) = get_area_type(&tags) {
if tags.get("type") == Some(&"multipolygon".to_string()) { if tags.get("type") == Some(&"multipolygon".to_string()) {
let mut ok = true; let mut ok = true;
let mut pts_per_way: Vec<Vec<LonLat>> = Vec::new(); let mut pts_per_way: Vec<Vec<Pt2D>> = Vec::new();
for member in &rel.members { for member in &rel.members {
match member { match member {
osm_xml::Member::Way(osm_xml::UnresolvedReference::Way(id), ref role) => { osm_xml::Member::Way(osm_xml::UnresolvedReference::Way(id), ref role) => {
@ -138,11 +132,11 @@ pub fn osm_to_raw_roads(
if polygons.is_empty() { if polygons.is_empty() {
println!("Relation {} failed to glue multipolygon", rel.id); println!("Relation {} failed to glue multipolygon", rel.id);
} else { } else {
for points in polygons { for polygon in polygons {
areas.push(raw_data::Area { map.areas.push(raw_data::Area {
area_type: at, area_type: at,
osm_id: rel.id, osm_id: rel.id,
polygon: Polygon::new(&gps_bounds.forcibly_convert(&points)), polygon,
osm_tags: tags.clone(), osm_tags: tags.clone(),
}); });
} }
@ -165,7 +159,7 @@ pub fn osm_to_raw_roads(
} }
if let (Some(from_way_id), Some(to_way_id)) = (from_way_id, to_way_id) { if let (Some(from_way_id), Some(to_way_id)) = (from_way_id, to_way_id) {
if let Some(restriction) = tags.get("restriction") { if let Some(restriction) = tags.get("restriction") {
turn_restrictions map.turn_restrictions
.entry(from_way_id) .entry(from_way_id)
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push((restriction.to_string(), to_way_id)); .push((restriction.to_string(), to_way_id));
@ -174,7 +168,7 @@ pub fn osm_to_raw_roads(
} }
} }
(roads, buildings, areas, traffic_signals, turn_restrictions) (map, roads, traffic_signals)
} }
fn tags_to_map(raw_tags: &[osm_xml::Tag]) -> BTreeMap<String, String> { fn tags_to_map(raw_tags: &[osm_xml::Tag]) -> BTreeMap<String, String> {
@ -247,12 +241,12 @@ fn get_area_type(tags: &BTreeMap<String, String>) -> Option<AreaType> {
} }
// The result could be more than one disjoint polygon. // The result could be more than one disjoint polygon.
fn glue_multipolygon(mut pts_per_way: Vec<Vec<LonLat>>) -> Vec<Vec<LonLat>> { fn glue_multipolygon(mut pts_per_way: Vec<Vec<Pt2D>>) -> Vec<Polygon> {
// First deal with all of the closed loops. // First deal with all of the closed loops.
let mut polygons: Vec<Vec<LonLat>> = Vec::new(); let mut polygons: Vec<Polygon> = Vec::new();
pts_per_way.retain(|pts| { pts_per_way.retain(|pts| {
if pts[0] == *pts.last().unwrap() { if pts[0] == *pts.last().unwrap() {
polygons.push(pts.to_vec()); polygons.push(Polygon::new(pts));
false false
} else { } else {
true true
@ -294,6 +288,6 @@ fn glue_multipolygon(mut pts_per_way: Vec<Vec<LonLat>>) -> Vec<Vec<LonLat>> {
if result[0] != *result.last().unwrap() { if result[0] != *result.last().unwrap() {
result.push(result[0]); result.push(result[0]);
} }
polygons.push(result); polygons.push(Polygon::new(&result));
polygons polygons
} }

View File

@ -1,17 +1,14 @@
use abstutil::Timer; use abstutil::Timer;
use geom::{GPSBounds, HashablePt2D, Pt2D}; use geom::{HashablePt2D, Pt2D};
use map_model::{raw_data, IntersectionType}; use map_model::{raw_data, IntersectionType};
use std::collections::{BTreeMap, HashMap, HashSet}; use std::collections::{HashMap, HashSet};
pub fn split_up_roads( pub fn split_up_roads(
(mut roads, buildings, areas, traffic_signals, turn_restrictions): ( (mut map, mut roads, traffic_signals): (
raw_data::Map,
Vec<raw_data::Road>, Vec<raw_data::Road>,
Vec<raw_data::Building>,
Vec<raw_data::Area>,
HashSet<HashablePt2D>, HashSet<HashablePt2D>,
BTreeMap<i64, Vec<(String, i64)>>,
), ),
gps_bounds: GPSBounds,
timer: &mut Timer, timer: &mut Timer,
) -> raw_data::Map { ) -> raw_data::Map {
timer.start("splitting up roads"); timer.start("splitting up roads");
@ -60,12 +57,6 @@ pub fn split_up_roads(
} }
} }
let mut map = raw_data::Map::blank();
map.gps_bounds = gps_bounds;
map.buildings = buildings;
map.areas = areas;
map.turn_restrictions = turn_restrictions;
// All of the roundabout points will just keep moving the intersection // All of the roundabout points will just keep moving the intersection
for (pt, id) in &pt_to_intersection { for (pt, id) in &pt_to_intersection {
let point = pt.to_pt2d(); let point = pt.to_pt2d();