diff --git a/convert_osm/src/clip.rs b/convert_osm/src/clip.rs index dd6fcdbac9..655b675d2b 100644 --- a/convert_osm/src/clip.rs +++ b/convert_osm/src/clip.rs @@ -1,13 +1,14 @@ use abstutil::{retain_btreemap, Timer}; -use geom::{LonLat, PolyLine, Polygon}; +use geom::PolyLine; use map_model::{raw_data, IntersectionType}; -pub fn clip_map(map: &mut raw_data::Map, boundary_poly_pts: Vec, timer: &mut Timer) { +pub fn clip_map(map: &mut raw_data::Map, timer: &mut Timer) { timer.start("clipping map to boundary"); - let boundary_poly = Polygon::new(&map.gps_bounds.must_convert(&boundary_poly_pts)); - map.boundary_polygon = boundary_poly.clone(); - let boundary_lines: Vec = boundary_poly + // So we can use retain_btreemap without borrowing issues + let boundary_polygon = map.boundary_polygon.clone(); + let boundary_lines: Vec = map + .boundary_polygon .points() .windows(2) .map(|pair| PolyLine::new(pair.to_vec())) @@ -16,16 +17,18 @@ pub fn clip_map(map: &mut raw_data::Map, boundary_poly_pts: Vec, timer: // This is kind of indirect and slow, but first pass -- just remove roads that start or end // outside the boundary polygon. retain_btreemap(&mut map.roads, |_, r| { - let first_in = boundary_poly.contains_pt(r.center_points[0]); - let last_in = boundary_poly.contains_pt(*r.center_points.last().unwrap()); + let first_in = boundary_polygon.contains_pt(r.center_points[0]); + let last_in = boundary_polygon.contains_pt(*r.center_points.last().unwrap()); first_in || last_in }); let road_ids: Vec = map.roads.keys().cloned().collect(); for id in road_ids { let r = &map.roads[&id]; - let first_in = boundary_poly.contains_pt(r.center_points[0]); - let last_in = boundary_poly.contains_pt(*r.center_points.last().unwrap()); + let first_in = map.boundary_polygon.contains_pt(r.center_points[0]); + 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. if first_in && last_in { @@ -93,7 +96,7 @@ pub fn clip_map(map: &mut raw_data::Map, boundary_poly_pts: Vec, timer: b.polygon .points() .iter() - .all(|pt| boundary_poly.contains_pt(*pt)) + .all(|pt| boundary_polygon.contains_pt(*pt)) }); let mut result_areas = Vec::new(); diff --git a/convert_osm/src/lib.rs b/convert_osm/src/lib.rs index 10f423f896..ab43117110 100644 --- a/convert_osm/src/lib.rs +++ b/convert_osm/src/lib.rs @@ -5,7 +5,7 @@ mod remove_disconnected; mod split_ways; use abstutil::Timer; -use geom::{Distance, FindClosest, GPSBounds, LonLat, PolyLine, Pt2D}; +use geom::{Distance, FindClosest, GPSBounds, LonLat, PolyLine, Polygon, Pt2D}; use kml::ExtraShapes; use map_model::{raw_data, OffstreetParking, LANE_THICKNESS}; 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() { 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( - osm::osm_to_raw_roads(&flags.osm, &gps_bounds, timer), - gps_bounds, + osm::extract_osm(&flags.osm, read_osmosis_polygon(&flags.clip), 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); 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"); } -fn read_osmosis_polygon(path: &str) -> Vec { +fn read_osmosis_polygon(path: &str) -> raw_data::Map { let mut pts: Vec = Vec::new(); + let mut gps_bounds = GPSBounds::new(); for (idx, maybe_line) in BufReader::new(File::open(path).unwrap()) .lines() .enumerate() @@ -158,11 +152,18 @@ fn read_osmosis_polygon(path: &str) -> Vec { } let parts: Vec<&str> = line.trim_start().split(" ").collect(); assert!(parts.len() == 2); - let lon = parts[0].parse::().unwrap(); - let lat = parts[1].parse::().unwrap(); - pts.push(LonLat::new(lon, lat)); + let pt = LonLat::new( + parts[0].parse::().unwrap(), + parts[1].parse::().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) { diff --git a/convert_osm/src/osm.rs b/convert_osm/src/osm.rs index 8ff9b4536a..72f515f645 100644 --- a/convert_osm/src/osm.rs +++ b/convert_osm/src/osm.rs @@ -1,21 +1,19 @@ use abstutil::{FileWithProgress, Timer}; -use geom::{GPSBounds, HashablePt2D, LonLat, Polygon, Pt2D}; +use geom::{HashablePt2D, LonLat, Polygon, Pt2D}; use map_model::{raw_data, AreaType}; use osm_xml; use std::collections::{BTreeMap, HashMap, HashSet}; -pub fn osm_to_raw_roads( +pub fn extract_osm( osm_path: &str, - gps_bounds: &GPSBounds, + mut map: raw_data::Map, timer: &mut Timer, ) -> ( + raw_data::Map, + // Un-split roads Vec, - Vec, - Vec, - // traffic signals as Pt2D + // Traffic signals HashSet, - // turn restrictions - BTreeMap>, ) { let (reader, done) = FileWithProgress::new(osm_path).unwrap(); let doc = osm_xml::OSM::parse(reader).expect("OSM parsing failed"); @@ -27,11 +25,8 @@ pub fn osm_to_raw_roads( ); done(timer); - let mut id_to_way: HashMap> = HashMap::new(); + let mut id_to_way: HashMap> = HashMap::new(); let mut roads: Vec = Vec::new(); - let mut buildings: Vec = Vec::new(); - let mut areas: Vec = Vec::new(); - let mut turn_restrictions: BTreeMap> = BTreeMap::new(); let mut traffic_signals: HashSet = HashSet::new(); 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); if tags.get("highway") == Some(&"traffic_signals".to_string()) { 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(); let mut valid = true; - let mut pts = Vec::new(); + let mut gps_pts = Vec::new(); for node_ref in &way.nodes { match doc.resolve_reference(node_ref) { 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 _ => { @@ -65,14 +61,15 @@ pub fn osm_to_raw_roads( if !valid { continue; } + let pts = map.gps_bounds.forcibly_convert(&gps_pts); let tags = tags_to_map(&way.tags); if is_road(&tags) { roads.push(raw_data::Road { osm_way_id: way.id, - center_points: gps_bounds.forcibly_convert(&pts), + center_points: pts, orig_id: raw_data::OriginalRoad { - pt1: pts[0], - pt2: *pts.last().unwrap(), + pt1: gps_pts[0], + pt2: *gps_pts.last().unwrap(), }, osm_tags: tags, // We'll fill this out later @@ -82,20 +79,17 @@ pub fn osm_to_raw_roads( parking_lane_back: false, }); } else if is_bldg(&tags) { - buildings.push(raw_data::Building { + map.buildings.push(raw_data::Building { osm_way_id: way.id, - polygon: Polygon::new(&Pt2D::approx_dedupe( - gps_bounds.forcibly_convert(&pts), - geom::EPSILON_DIST, - )), + polygon: Polygon::new(&Pt2D::approx_dedupe(pts, geom::EPSILON_DIST)), osm_tags: tags, parking: None, }); } else if let Some(at) = get_area_type(&tags) { - areas.push(raw_data::Area { + map.areas.push(raw_data::Area { area_type: at, osm_id: way.id, - polygon: Polygon::new(&gps_bounds.forcibly_convert(&pts)), + polygon: Polygon::new(&pts), osm_tags: tags, }); } else { @@ -111,7 +105,7 @@ pub fn osm_to_raw_roads( if let Some(at) = get_area_type(&tags) { if tags.get("type") == Some(&"multipolygon".to_string()) { let mut ok = true; - let mut pts_per_way: Vec> = Vec::new(); + let mut pts_per_way: Vec> = Vec::new(); for member in &rel.members { match member { 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() { println!("Relation {} failed to glue multipolygon", rel.id); } else { - for points in polygons { - areas.push(raw_data::Area { + for polygon in polygons { + map.areas.push(raw_data::Area { area_type: at, osm_id: rel.id, - polygon: Polygon::new(&gps_bounds.forcibly_convert(&points)), + polygon, 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(restriction) = tags.get("restriction") { - turn_restrictions + map.turn_restrictions .entry(from_way_id) .or_insert_with(Vec::new) .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 { @@ -247,12 +241,12 @@ fn get_area_type(tags: &BTreeMap) -> Option { } // The result could be more than one disjoint polygon. -fn glue_multipolygon(mut pts_per_way: Vec>) -> Vec> { +fn glue_multipolygon(mut pts_per_way: Vec>) -> Vec { // First deal with all of the closed loops. - let mut polygons: Vec> = Vec::new(); + let mut polygons: Vec = Vec::new(); pts_per_way.retain(|pts| { if pts[0] == *pts.last().unwrap() { - polygons.push(pts.to_vec()); + polygons.push(Polygon::new(pts)); false } else { true @@ -294,6 +288,6 @@ fn glue_multipolygon(mut pts_per_way: Vec>) -> Vec> { if result[0] != *result.last().unwrap() { result.push(result[0]); } - polygons.push(result); + polygons.push(Polygon::new(&result)); polygons } diff --git a/convert_osm/src/split_ways.rs b/convert_osm/src/split_ways.rs index 496e530f50..64c02a3e1b 100644 --- a/convert_osm/src/split_ways.rs +++ b/convert_osm/src/split_ways.rs @@ -1,17 +1,14 @@ use abstutil::Timer; -use geom::{GPSBounds, HashablePt2D, Pt2D}; +use geom::{HashablePt2D, Pt2D}; use map_model::{raw_data, IntersectionType}; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{HashMap, HashSet}; pub fn split_up_roads( - (mut roads, buildings, areas, traffic_signals, turn_restrictions): ( + (mut map, mut roads, traffic_signals): ( + raw_data::Map, Vec, - Vec, - Vec, HashSet, - BTreeMap>, ), - gps_bounds: GPSBounds, timer: &mut Timer, ) -> raw_data::Map { 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 for (pt, id) in &pt_to_intersection { let point = pt.to_pt2d();