diff --git a/convert_osm/src/osm_reader.rs b/convert_osm/src/osm_reader.rs index 04858c3ff9..68dbc1b148 100644 --- a/convert_osm/src/osm_reader.rs +++ b/convert_osm/src/osm_reader.rs @@ -1,10 +1,10 @@ use abstutil::{FileWithProgress, Timer}; use geom::{GPSBounds, HashablePt2D, LonLat, PolyLine, Polygon, Pt2D, Ring}; use map_model::raw::{ - OriginalBuilding, RawArea, RawBuilding, RawMap, RawParkingLot, RawRoad, RestrictionType, + OriginalBuilding, RawArea, RawBuilding, RawBusRoute, RawBusStop, RawMap, RawParkingLot, + RawRoad, RestrictionType, }; use map_model::{osm, AreaType}; -use osm_xml; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; pub fn extract_osm( @@ -276,20 +276,9 @@ pub fn extract_osm( }, ); } - /*} else if tags.get("type") == Some(&"route_master".to_string()) { - let name = tags.get("name").unwrap(); - //let mut fwd_stops = Vec::new(); - //let mut back_stops = Vec::new(); - for member in &rel.members { - if let osm_xml::Member::Relation(rel_ref, _) = member { - if let osm_xml::Reference::Relation(inner_rel) = doc.resolve_reference(&rel_ref) { - let inner_tags = tags_to_map(&inner_rel.tags); - assert_eq!(inner_tags.get("type"), Some(&"route".to_string())); - for member in &inner_rel.members { - } - } - } - }*/ + } else if tags.get("type") == Some(&"route_master".to_string()) { + map.new_bus_routes + .extend(extract_route(&tags, rel, &doc, &id_to_way, &map.gps_bounds)); } } @@ -605,3 +594,117 @@ fn glue_to_boundary(result_pl: PolyLine, boundary: &Ring) -> Option { assert_eq!(trimmed_pts[0], *trimmed_pts.last().unwrap()); Some(Polygon::new(&trimmed_pts)) } + +fn extract_route( + master_tags: &BTreeMap, + master_rel: &osm_xml::Relation, + doc: &osm_xml::OSM, + id_to_way: &HashMap>, + gps_bounds: &GPSBounds, +) -> Option { + let route_name = master_tags.get("name")?.clone(); + let is_bus = match master_tags.get("route_master")?.as_ref() { + "bus" => true, + "light_rail" => false, + x => { + println!("Skipping route {} of unknown type {}", route_name, x); + return None; + } + }; + + let mut directions = Vec::new(); + for (_, route_member) in get_members(master_rel, doc) { + if let osm_xml::Reference::Relation(route_rel) = route_member { + let route_tags = tags_to_map(&route_rel.tags); + assert_eq!(route_tags.get("type"), Some(&"route".to_string())); + // Gather stops in order. Platforms may exist or not; match them up by name. + let mut stops = Vec::new(); + let mut platforms = HashMap::new(); + for (role, member) in get_members(&route_rel, doc) { + if role == "stop" { + if let osm_xml::Reference::Node(node) = member { + stops.push(RawBusStop { + name: tags_to_map(&node.tags) + .get("name") + .cloned() + .unwrap_or_else(|| format!("stop #{}", stops.len() + 1)), + vehicle_pos: Pt2D::forcibly_from_gps( + LonLat::new(node.lon, node.lat), + gps_bounds, + ), + ped_pos: None, + }); + } + } else if role == "platform" { + let (platform_name, pt) = match member { + osm_xml::Reference::Node(node) => ( + tags_to_map(&node.tags) + .get("name") + .cloned() + .unwrap_or_else(|| format!("stop #{}", platforms.len() + 1)), + Pt2D::forcibly_from_gps(LonLat::new(node.lon, node.lat), gps_bounds), + ), + osm_xml::Reference::Way(way) => ( + tags_to_map(&way.tags) + .get("name") + .cloned() + .unwrap_or_else(|| format!("stop #{}", platforms.len() + 1)), + if let Some(ref pts) = id_to_way.get(&way.id) { + Pt2D::center(pts) + } else { + continue; + }, + ), + _ => continue, + }; + platforms.insert(platform_name, pt); + } + } + for stop in &mut stops { + if let Some(pt) = platforms.remove(&stop.name) { + stop.ped_pos = Some(pt); + } + } + if stops.len() >= 2 { + directions.push(stops); + } + } + } + + if directions.len() == 2 { + Some(RawBusRoute { + name: route_name, + is_bus, + osm_rel_id: master_rel.id, + // The direction is arbitrary right now + fwd_stops: directions.pop().unwrap(), + back_stops: directions.pop().unwrap(), + }) + } else { + println!( + "Skipping route {} with {} sub-routes", + route_name, + directions.len() + ); + None + } +} + +// Work around osm_xml's API, which shows the node/way/relation distinction twice. This returns +// (role, resolved node/way/relation) +fn get_members<'a>( + rel: &'a osm_xml::Relation, + doc: &'a osm_xml::OSM, +) -> Vec<(&'a String, osm_xml::Reference<'a>)> { + rel.members + .iter() + .map(|member| { + let (id_ref, role) = match member { + osm_xml::Member::Node(id, role) + | osm_xml::Member::Way(id, role) + | osm_xml::Member::Relation(id, role) => (id, role), + }; + (role, doc.resolve_reference(id_ref)) + }) + .collect() +} diff --git a/data/MANIFEST.txt b/data/MANIFEST.txt index da6152c6e5..ac14613fbe 100644 --- a/data/MANIFEST.txt +++ b/data/MANIFEST.txt @@ -1,16 +1,16 @@ data/input/austin/osm/Austin.osm,7c8d72cf97072af34cee665006b1e9e6,https://www.dropbox.com/s/8bedio4fpt6yvhg/Austin.osm.zip?dl=0 data/input/austin/osm/downtown_atx.osm,a30b0f460a481598e494f16a9d07a822,https://www.dropbox.com/s/tbadw3f0ex2zzx7/downtown_atx.osm.zip?dl=0 data/input/austin/osm/huge_austin.osm,fb166029fc8006bd20dc959fbbbde3b6,https://www.dropbox.com/s/4x421o9o8px0m6o/huge_austin.osm.zip?dl=0 -data/input/raw_maps/ballard.bin,fe5f56a77d784e1604a7062805e6eed8,https://www.dropbox.com/s/urt8jms4st0nodt/ballard.bin.zip?dl=0 -data/input/raw_maps/downtown.bin,d1817deb55761f7cc10f8550850762c9,https://www.dropbox.com/s/qj927riunhv9ygv/downtown.bin.zip?dl=0 -data/input/raw_maps/downtown_atx.bin,0cd7ecaf548124710936a173ef617e0a,https://www.dropbox.com/s/0qiji6lmwmkpt6y/downtown_atx.bin.zip?dl=0 -data/input/raw_maps/huge_austin.bin,5d0fdca0eb9bae5cd5e0955442972bd4,https://www.dropbox.com/s/zs6te7tq7vgjicc/huge_austin.bin.zip?dl=0 -data/input/raw_maps/huge_seattle.bin,22f323f1ce1917df9e261f24f557780f,https://www.dropbox.com/s/r6cj78jq3z7s5bs/huge_seattle.bin.zip?dl=0 -data/input/raw_maps/lakeslice.bin,c346ac30ad726456802937729af76af4,https://www.dropbox.com/s/uh4oacev5raty9z/lakeslice.bin.zip?dl=0 -data/input/raw_maps/montlake.bin,b17d1f76d6c5dd4a3af07343ca52590d,https://www.dropbox.com/s/dfcoo90hpwa8osj/montlake.bin.zip?dl=0 -data/input/raw_maps/south_seattle.bin,a8ded05512d20fac14cdb8e605c14995,https://www.dropbox.com/s/1m7fkvuyhyev3cw/south_seattle.bin.zip?dl=0 -data/input/raw_maps/udistrict.bin,eba1715dd8a66ff3fb0a39aa92419d93,https://www.dropbox.com/s/wbudupyrqd1aqg6/udistrict.bin.zip?dl=0 -data/input/raw_maps/west_seattle.bin,04c347cc62a3c2f078190b98af04c10f,https://www.dropbox.com/s/350gq0dtgvw2lrg/west_seattle.bin.zip?dl=0 +data/input/raw_maps/ballard.bin,e86dc091224aebad6e87c374cfdc0ae7,https://www.dropbox.com/s/aqrjfur2d7bqb2o/ballard.bin.zip?dl=0 +data/input/raw_maps/downtown.bin,23a8f0e8d0adf61f1bede08f9e05c755,https://www.dropbox.com/s/wq2wm0gb62diz4x/downtown.bin.zip?dl=0 +data/input/raw_maps/downtown_atx.bin,10952f03672526eab180c724af188809,https://www.dropbox.com/s/37jx8htloc5j5rr/downtown_atx.bin.zip?dl=0 +data/input/raw_maps/huge_austin.bin,201d0aff6c9dde9c6f60a2af727d1b32,https://www.dropbox.com/s/jv8dhiebx35n0kg/huge_austin.bin.zip?dl=0 +data/input/raw_maps/huge_seattle.bin,c497258745e278ad7d30b5801a66aaeb,https://www.dropbox.com/s/oikef0juh0iqmw3/huge_seattle.bin.zip?dl=0 +data/input/raw_maps/lakeslice.bin,7ec24ec2860b91a7616cb8fe55afb3b8,https://www.dropbox.com/s/5tkypxuarkr65cd/lakeslice.bin.zip?dl=0 +data/input/raw_maps/montlake.bin,e18c8a7f5992cb4af39843275f42125a,https://www.dropbox.com/s/2bn4k7axosjig4u/montlake.bin.zip?dl=0 +data/input/raw_maps/south_seattle.bin,96303da5abeea874585e789eaaf4be51,https://www.dropbox.com/s/ql70xxsjgyuypd8/south_seattle.bin.zip?dl=0 +data/input/raw_maps/udistrict.bin,0f876dc016f2f032c4b85395a1d73fa2,https://www.dropbox.com/s/bq38e79k7md98z5/udistrict.bin.zip?dl=0 +data/input/raw_maps/west_seattle.bin,91b3ac23ceb67c81f8fc0ced172f915c,https://www.dropbox.com/s/vtdnt5zpchmdh0s/west_seattle.bin.zip?dl=0 data/input/screenshots/downtown/01x01.gif,873df007edd02e5967f3917cbe8f342f,https://www.dropbox.com/s/4209gvxkypinlqs/01x01.gif.zip?dl=0 data/input/screenshots/downtown/01x02.gif,ec8b579d6cb498a2a85bc2af8b51f390,https://www.dropbox.com/s/9x5qtp86h3uemzm/01x02.gif.zip?dl=0 data/input/screenshots/downtown/01x03.gif,c126afdf55a94993e6c9a909eb685f8c,https://www.dropbox.com/s/4ev4pepyvrl6bs4/01x03.gif.zip?dl=0 diff --git a/map_model/src/raw.rs b/map_model/src/raw.rs index 976a3a0d21..b6b4f2c556 100644 --- a/map_model/src/raw.rs +++ b/map_model/src/raw.rs @@ -27,6 +27,7 @@ pub struct RawMap { )] pub buildings: BTreeMap, pub bus_routes: Vec, + pub new_bus_routes: Vec, pub areas: Vec, pub parking_lots: Vec, pub parking_aisles: Vec>, @@ -94,6 +95,7 @@ impl RawMap { intersections: BTreeMap::new(), buildings: BTreeMap::new(), bus_routes: Vec::new(), + new_bus_routes: Vec::new(), areas: Vec::new(), parking_lots: Vec::new(), parking_aisles: Vec::new(), @@ -421,3 +423,21 @@ impl DrivingSide { } } } + +#[derive(Debug, Serialize, Deserialize)] +pub struct RawBusRoute { + pub name: String, + pub osm_rel_id: i64, + // If not, light rail + pub is_bus: bool, + pub fwd_stops: Vec, + pub back_stops: Vec, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct RawBusStop { + pub name: String, + pub vehicle_pos: Pt2D, + // If it's not explicitly mapped, we'll do equiv_pos. + pub ped_pos: Option, +}