match transit stops to side of the road based on the orientation of the points in the route. don't use this just yet...

not regerating yet either
This commit is contained in:
Dustin Carlino 2020-08-08 17:44:28 -07:00
parent 94f83dcc6a
commit 6c525d625d
7 changed files with 134 additions and 34 deletions

View File

@ -177,20 +177,20 @@ pub fn clip_map(map: &mut RawMap, timer: &mut Timer) {
}
let mut borders: Vec<OriginalIntersection> = Vec::new();
for pt in r.all_pts.drain(..) {
if let Some(i) = map.intersections.get(&pt) {
for pt in &r.all_pts {
if let Some(i) = map.intersections.get(pt) {
if i.intersection_type == IntersectionType::Border {
borders.push(pt);
borders.push(*pt);
}
}
if let Some(i) = extra_borders.get(&pt) {
if let Some(i) = extra_borders.get(pt) {
borders.push(*i);
}
}
// Guess which border is for the beginning and end of the route.
let start_i = map.closest_intersection(r.stops[0].vehicle_pos);
let end_i = map.closest_intersection(r.stops.last().unwrap().vehicle_pos);
let start_i = map.closest_intersection(r.stops[0].vehicle_pos.1);
let end_i = map.closest_intersection(r.stops.last().unwrap().vehicle_pos.1);
let mut best_start: Option<(OriginalIntersection, Distance)> = None;
let mut best_end: Option<(OriginalIntersection, Distance)> = None;
for i in borders {

View File

@ -5,7 +5,8 @@ use abstutil::{retain_btreemap, Tags, Timer};
use geom::{HashablePt2D, PolyLine, Polygon, Pt2D, Ring};
use kml::{ExtraShape, ExtraShapes};
use map_model::raw::{
OriginalBuilding, RawArea, RawBuilding, RawMap, RawParkingLot, RawRoad, RestrictionType,
OriginalBuilding, OriginalIntersection, RawArea, RawBuilding, RawMap, RawParkingLot, RawRoad,
RestrictionType,
};
use map_model::{osm, AreaType};
use osm::{NodeID, OsmID, RelationID, WayID};
@ -185,7 +186,7 @@ pub fn extract_osm(map: &mut RawMap, opts: &Options, timer: &mut Timer) -> OsmEx
let mut amenity_areas: Vec<(String, String, Polygon)> = Vec::new();
// Vehicle position (stop) -> pedestrian position (platform)
let mut stop_areas: Vec<(Pt2D, Pt2D)> = Vec::new();
let mut stop_areas: Vec<((OriginalIntersection, Pt2D), Pt2D)> = Vec::new();
// TODO Fill this out in a separate loop to keep a mutable borrow short. Maybe do this in
// reader, or stop doing this entirely.
@ -305,7 +306,7 @@ pub fn extract_osm(map: &mut RawMap, opts: &Options, timer: &mut Timer) -> OsmEx
if let OsmID::Node(n) = member {
let pt = doc.nodes[n].pt;
if role == "stop" {
stops.push(pt);
stops.push((OriginalIntersection { osm_node_id: *n }, pt));
} else if role == "platform" {
platform = Some(pt);
}

View File

@ -72,13 +72,28 @@ pub fn convert(opts: Options, timer: &mut abstutil::Timer) -> RawMap {
}
let extract = extract::extract_osm(&mut map, &opts, timer);
let amenities = split_ways::split_up_roads(&mut map, extract, timer);
let (amenities, pt_to_road) = split_ways::split_up_roads(&mut map, extract, timer);
clip::clip_map(&mut map, timer);
// Need to do a first pass of removing cul-de-sacs here, or we wind up with loop PolyLines when
// doing the parking hint matching.
abstutil::retain_btreemap(&mut map.roads, |r, _| r.i1 != r.i2);
let all_routes = map.bus_routes.drain(..).collect::<Vec<_>>();
let mut routes = Vec::new();
for route in all_routes {
let name = format!("{} ({})", route.osm_rel_id, route.full_name);
match transit::snap_bus_stops(route, &map, &pt_to_road) {
Ok(r) => {
routes.push(r);
}
Err(err) => {
timer.error(format!("Skipping {}: {}", name, err));
}
}
}
map.bus_routes = routes;
use_amenities(&mut map, amenities, timer);
parking::apply_parking(&mut map, &opts, timer);

View File

@ -5,12 +5,16 @@ use map_model::raw::{OriginalIntersection, OriginalRoad, RawIntersection, RawMap
use map_model::{osm, IntersectionType};
use std::collections::HashMap;
// Returns amenities
// Returns amenities and a mapping of all points to split road. (Some internal points on roads are
// removed, so this mapping isn't redundant.)
pub fn split_up_roads(
map: &mut RawMap,
mut input: OsmExtract,
timer: &mut Timer,
) -> Vec<(Pt2D, String, String)> {
) -> (
Vec<(Pt2D, String, String)>,
HashMap<HashablePt2D, OriginalRoad>,
) {
timer.start("splitting up roads");
let mut pt_to_intersection: HashMap<HashablePt2D, OriginalIntersection> = HashMap::new();
@ -48,6 +52,8 @@ pub fn split_up_roads(
);
}
let mut pt_to_road: HashMap<HashablePt2D, OriginalRoad> = HashMap::new();
// Now actually split up the roads based on the intersections
timer.start_iter("split roads", input.roads.len());
for (osm_way_id, orig_road) in &input.roads {
@ -72,16 +78,18 @@ pub fn split_up_roads(
r.osm_tags
.insert(osm::ENDPT_FWD.to_string(), "true".to_string());
}
let id = OriginalRoad {
osm_way_id: *osm_way_id,
i1,
i2: *i2,
};
for pt in &pts {
pt_to_road.insert(pt.to_hashable(), id);
}
r.center_points = dedupe_angles(std::mem::replace(&mut pts, Vec::new()));
// Start a new road
map.roads.insert(
OriginalRoad {
osm_way_id: *osm_way_id,
i1,
i2: *i2,
},
r.clone(),
);
map.roads.insert(id, r.clone());
r.osm_tags.remove(osm::ENDPT_FWD);
r.osm_tags.remove(osm::ENDPT_BACK);
i1 = *i2;
@ -188,7 +196,7 @@ pub fn split_up_roads(
}
timer.stop("splitting up roads");
input.amenities
(input.amenities, pt_to_road)
}
// TODO Consider doing this in PolyLine::new always. extend() there does this too.

View File

@ -1,8 +1,8 @@
use crate::reader::{Document, Relation};
use abstutil::Timer;
use geom::{Polygon, Pt2D};
use geom::{HashablePt2D, Polygon, Pt2D};
use map_model::osm::{NodeID, OsmID, RelationID, WayID};
use map_model::raw::{OriginalIntersection, RawBusRoute, RawBusStop};
use map_model::raw::{OriginalIntersection, OriginalRoad, RawBusRoute, RawBusStop, RawMap};
use std::collections::HashMap;
pub fn extract_route(
@ -47,7 +47,8 @@ pub fn extract_route(
.get("name")
.cloned()
.unwrap_or_else(|| format!("stop #{}", stops.len() + 1)),
vehicle_pos: node.pt,
vehicle_pos: (OriginalIntersection { osm_node_id: *n }, node.pt),
matched_road: None,
ped_pos: None,
});
}
@ -89,7 +90,7 @@ pub fn extract_route(
let all_pts: Vec<OriginalIntersection> = match glue_route(all_ways, doc) {
Ok(nodes) => nodes
.into_iter()
.map(|n| OriginalIntersection { osm_node_id: n })
.map(|osm_node_id| OriginalIntersection { osm_node_id })
.collect(),
Err(err) => {
timer.error(format!(
@ -106,7 +107,7 @@ pub fn extract_route(
let mut keep_stops = Vec::new();
let orig_num = stops.len();
for stop in stops {
if boundary.contains_pt(stop.vehicle_pos) {
if boundary.contains_pt(stop.vehicle_pos.1) {
keep_stops.push(stop);
} else {
if !keep_stops.is_empty() {
@ -184,7 +185,78 @@ fn glue_route(all_ways: Vec<WayID>, doc: &Document) -> Result<Vec<NodeID>, Strin
extra = nodes2;
}
// And the last lil bit
if nodes.is_empty() {
return Err(format!("empty? ways: {:?}", all_ways));
}
assert_eq!(nodes.pop().unwrap(), extra[0]);
nodes.extend(extra);
Ok(nodes)
}
pub fn snap_bus_stops(
mut route: RawBusRoute,
raw: &RawMap,
pt_to_road: &HashMap<HashablePt2D, OriginalRoad>,
) -> Result<RawBusRoute, String> {
// For every stop, figure out what road segment and direction it matches up to.
for stop in &mut route.stops {
// TODO Handle this, example https://www.openstreetmap.org/node/4560936658
if raw.intersections.contains_key(&stop.vehicle_pos.0) {
return Err(format!(
"{} has a stop {} right at an intersection, skipping",
route.osm_rel_id, stop.vehicle_pos.0.osm_node_id
));
}
let idx_in_route = route
.all_pts
.iter()
.position(|pt| stop.vehicle_pos.0 == *pt)
.unwrap();
// Scan backwards and forwards in the route for the nearest intersections.
// TODO Express better with iterators
let mut i1 = None;
for idx in (0..=idx_in_route).rev() {
let i = route.all_pts[idx];
if raw.intersections.contains_key(&i) {
i1 = Some(i);
break;
}
}
let mut i2 = None;
for idx in idx_in_route..route.all_pts.len() {
let i = route.all_pts[idx];
if raw.intersections.contains_key(&i) {
i2 = Some(i);
break;
}
}
let road = pt_to_road[&stop.vehicle_pos.1.to_hashable()];
let i1 = i1.unwrap();
let i2 = i2.unwrap();
let fwds = if road.i1 == i1 && road.i2 == i2 {
true
} else if road.i1 == i2 && road.i2 == i1 {
false
} else {
return Err(format!(
"Can't figure out where {} is along route. {:?}, {:?}. {} of {}",
stop.vehicle_pos.0.osm_node_id,
i1,
i2,
idx_in_route,
route.all_pts.len()
));
};
stop.matched_road = Some((road, fwds));
if false {
println!(
"{} matched to {}, fwds={}",
stop.vehicle_pos.0.osm_node_id, road, fwds
);
}
}
Ok(route)
}

View File

@ -197,9 +197,9 @@ impl Matcher {
for r in routes {
for stop in &r.stops {
if r.is_bus {
lookup_bus_pts.insert(stop.vehicle_pos.to_hashable());
lookup_bus_pts.insert(stop.vehicle_pos.1.to_hashable());
} else {
lookup_light_rail_pts.insert(stop.vehicle_pos.to_hashable());
lookup_light_rail_pts.insert(stop.vehicle_pos.1.to_hashable());
}
if let Some(pt) = stop.ped_pos {
lookup_sidewalk_pts.insert(pt.to_hashable());
@ -258,9 +258,9 @@ impl Matcher {
.ok_or_else(|| format!("sidewalk for light rail didnt match: {}", sidewalk_pt))?;
let driving_pos = *self
.light_rail_pts
.get(&stop.vehicle_pos.to_hashable())
.get(&stop.vehicle_pos.1.to_hashable())
.ok_or_else(|| {
format!("vehicle for light rail didnt match: {}", stop.vehicle_pos)
format!("vehicle for light rail didnt match: {}", stop.vehicle_pos.0)
})?;
return Ok((sidewalk_pos, driving_pos));
}
@ -278,8 +278,8 @@ impl Matcher {
// rightmost driving/bus lane.
let orig_driving_pos = *self
.bus_pts
.get(&stop.vehicle_pos.to_hashable())
.ok_or("vehicle for bus didnt match")?;
.get(&stop.vehicle_pos.1.to_hashable())
.ok_or_else(|| format!("vehicle for bus didnt match: {}", stop.vehicle_pos.0))?;
let sidewalk = map
.get_parent(orig_driving_pos.lane())
.find_closest_lane(

View File

@ -463,14 +463,18 @@ pub struct RawBusRoute {
pub stops: Vec<RawBusStop>,
pub border_start: Option<OriginalIntersection>,
pub border_end: Option<OriginalIntersection>,
// Temporarily plumbed along
// This is guaranteed to be in order and contiguous. These're ALL nodes, not just
// intersections.
pub all_pts: Vec<OriginalIntersection>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct RawBusStop {
pub name: String,
pub vehicle_pos: Pt2D,
// Probably not an intersection, but this type is more convenient.
pub vehicle_pos: (OriginalIntersection, Pt2D),
// Guaranteed to be filled out when RawMap is fully written. True for forwards.
pub matched_road: Option<(OriginalRoad, bool)>,
// If it's not explicitly mapped, we'll do equiv_pos.
pub ped_pos: Option<Pt2D>,
}