diff --git a/convert_osm/src/clip.rs b/convert_osm/src/clip.rs index 6e79dd7325..0bd18fd1ad 100644 --- a/convert_osm/src/clip.rs +++ b/convert_osm/src/clip.rs @@ -1,7 +1,8 @@ use abstutil::{retain_btreemap, Timer}; -use geom::{PolyLine, Ring}; +use geom::{PolyLine, Pt2D, Ring}; use map_model::raw::{OriginalIntersection, OriginalRoad, RawMap}; use map_model::IntersectionType; +use std::collections::BTreeMap; // TODO This needs to update turn restrictions too pub fn clip_map(map: &mut RawMap, timer: &mut Timer) { @@ -27,6 +28,11 @@ pub fn clip_map(map: &mut RawMap, timer: &mut Timer) { first_in || last_in || light_rail_ok }); + // When we split an intersection out of bounds into two, one of them gets a new ID. Remember + // that here. + let mut extra_borders: BTreeMap = + BTreeMap::new(); + // First pass: Clip roads beginning out of bounds let road_ids: Vec = map.roads.keys().cloned().collect(); for id in road_ids { @@ -36,6 +42,7 @@ pub fn clip_map(map: &mut RawMap, timer: &mut Timer) { } let mut move_i = id.i1; + let orig_id = id.i1; // The road crosses the boundary. If the intersection happens to have another connected // road, then we need to copy the intersection before trimming it. This effectively @@ -53,6 +60,7 @@ pub fn clip_map(map: &mut RawMap, timer: &mut Timer) { move_i = OriginalIntersection { osm_node_id: map.new_osm_node_id(-1), }; + extra_borders.insert(orig_id, (move_i, copy.point)); map.intersections.insert(move_i, copy); println!("Disconnecting {} from some other stuff (starting OOB)", id); } @@ -94,6 +102,7 @@ pub fn clip_map(map: &mut RawMap, timer: &mut Timer) { } let mut move_i = id.i2; + let orig_id = id.i2; // The road crosses the boundary. If the intersection happens to have another connected // road, then we need to copy the intersection before trimming it. This effectively @@ -110,6 +119,7 @@ pub fn clip_map(map: &mut RawMap, timer: &mut Timer) { move_i = OriginalIntersection { osm_node_id: map.new_osm_node_id(-1), }; + extra_borders.insert(orig_id, (move_i, copy.point)); map.intersections.insert(move_i, copy); println!("Disconnecting {} from some other stuff (ending OOB)", id); } @@ -163,41 +173,48 @@ pub fn clip_map(map: &mut RawMap, timer: &mut Timer) { let mut keep_routes = Vec::new(); for mut r in map.bus_routes.drain(..) { - let mut borders = Vec::new(); - let num_pts = r.all_pts.len(); - for (idx, pt) in r.all_pts.drain(..).enumerate() { - if map - .intersections - .get(&pt) - .map(|i| i.intersection_type == IntersectionType::Border) - .unwrap_or(false) - { - borders.push((pt, idx)); - } - } - if borders.len() > 2 { - timer.warn(format!( - "Route {} matches too many borders: {:?}", - r.osm_rel_id, borders - )); - } else { - // https://wiki.openstreetmap.org/w/index.php?title=Relation:route&uselang=en#Order_matters - // Of course the ways aren't in order. :( Use distance to the first/last stop. If - // there's just one stop, we're just gambling at this point. - if borders.len() == 2 { - // Presumably the borders are in order. - r.border_start = Some(borders[0].0); - r.border_end = Some(borders[1].0); - } else if borders.len() == 1 { - // Alright, which end is which? Use the original index of the point to guess. - if borders[0].1 < num_pts / 2 { - r.border_start = Some(borders[0].0); - } else { - r.border_end = Some(borders[0].0); + let mut borders: Vec<(OriginalIntersection, Pt2D)> = Vec::new(); + for pt in r.all_pts.drain(..) { + if let Some(i) = map.intersections.get(&pt) { + if i.intersection_type == IntersectionType::Border { + borders.push((pt, i.point)); } } - keep_routes.push(r); + if let Some(pair) = extra_borders.get(&pt) { + borders.push(*pair); + } } + + if !borders.is_empty() { + // Guess which border is for the beginning and end of the route. This can fail if: + // - there's just one stop + // - the border isn't actually connected to the stop by any path (roads causing copied + // intersections trigger this) + let start_pt = r.stops[0].vehicle_pos; + let closest_to_start = borders + .iter() + .min_by_key(|(_, pt)| start_pt.dist_to(*pt)) + .unwrap(); + let end_pt = r.stops.last().unwrap().vehicle_pos; + let closest_to_end = borders + .iter() + .min_by_key(|(_, pt)| end_pt.dist_to(*pt)) + .unwrap(); + + if closest_to_start.0 != closest_to_end.0 { + r.border_start = Some(closest_to_start.0); + r.border_end = Some(closest_to_end.0); + } else { + // Break the tie... + if closest_to_start.1.dist_to(start_pt) < closest_to_end.1.dist_to(end_pt) { + r.border_start = Some(closest_to_start.0); + } else { + r.border_end = Some(closest_to_end.0); + } + } + } + // TODO Warn for some of these weird things and maybe skip + keep_routes.push(r); } map.bus_routes = keep_routes; diff --git a/game/src/devtools/mod.rs b/game/src/devtools/mod.rs index 06a2dedaf4..f0903e803c 100644 --- a/game/src/devtools/mod.rs +++ b/game/src/devtools/mod.rs @@ -126,7 +126,10 @@ fn choose_polygon(wiz: &mut Wizard, ctx: &mut EventCtx, _: &mut App) -> Option Some(Transition::Replace(polygon::PolygonEditor::new( ctx, name, pts, ))), diff --git a/game/src/devtools/polygon.rs b/game/src/devtools/polygon.rs index 40f5bb453d..2612006424 100644 --- a/game/src/devtools/polygon.rs +++ b/game/src/devtools/polygon.rs @@ -146,7 +146,7 @@ impl State for PolygonEditor { // https://wiki.openstreetmap.org/wiki/Osmosis/Polygon_Filter_File_Format fn save_as_osmosis(name: &str, pts: &Vec) -> Result<(), Error> { - let path = "bounding_boy.poly"; + let path = format!("{}.poly", name); let mut f = File::create(&path)?; writeln!(f, "{}", name)?; diff --git a/game/src/layer/transit.rs b/game/src/layer/transit.rs index f53886a21d..c1dbe4cf19 100644 --- a/game/src/layer/transit.rs +++ b/game/src/layer/transit.rs @@ -227,14 +227,20 @@ impl ShowBusRoute { bus_locations.push(pt); } - let mut colorer = ColorDiscrete::new( - app, - vec![ - ("route", app.cs.unzoomed_bus), - ("start", Color::RED), - ("end", Color::GREEN), - ], - ); + let mut categories = vec![("route", app.cs.unzoomed_bus)]; + if route.start_border.is_some() { + categories.push(("start", Color::RED)); + } + if route.end_border.is_some() { + categories.push(("end", Color::GREEN)); + } + let mut colorer = ColorDiscrete::new(app, categories); + if let Some(l) = route.start_border { + colorer.add_i(map.get_l(l).src_i, "start"); + } + if let Some(l) = route.end_border { + colorer.add_i(map.get_l(l).dst_i, "end"); + } for pair in route.stops.windows(2) { for step in map .pathfind(PathRequest { @@ -250,12 +256,6 @@ impl ShowBusRoute { } } } - if let Some(l) = route.start_border { - colorer.add_i(map.get_l(l).src_i, "start"); - } - if let Some(l) = route.end_border { - colorer.add_i(map.get_l(l).dst_i, "end"); - } let mut labels = Vec::new(); for (idx, bs) in route.stops.iter().enumerate() { diff --git a/map_model/src/make/transit.rs b/map_model/src/make/transit.rs index a150ce696a..c1c8ffa6bb 100644 --- a/map_model/src/make/transit.rs +++ b/map_model/src/make/transit.rs @@ -99,8 +99,9 @@ fn make_route( } else { // TODO Should panic println!( - "Route {} starts at {}, but no starting lane for a {:?}?", + "Route {} starts at {} ({}), but no starting lane for a {:?}?", rel_url(r.osm_rel_id), + i.id, i.orig_id, route_type ); @@ -116,8 +117,9 @@ fn make_route( } else { // TODO Should panic println!( - "Route {} ends at {}, but no ending lane for a {:?}?", + "Route {} ends at {} ({}), but no ending lane for a {:?}?", rel_url(r.osm_rel_id), + i.id, i.orig_id, route_type ); diff --git a/map_model/src/raw.rs b/map_model/src/raw.rs index 2624ea7c80..d88a215204 100644 --- a/map_model/src/raw.rs +++ b/map_model/src/raw.rs @@ -155,6 +155,7 @@ impl RawMap { } } + // TODO Almost gone... pub fn new_osm_way_id(&self, start: i64) -> i64 { assert!(start < 0); // Slow, but deterministic.