Store lane_specs_ltr on RawRoad, instead of constantly recalculating. https://github.com/a-b-street/osm2streets/issues/2

This commit is contained in:
Dustin Carlino 2022-05-01 19:39:25 +01:00
parent 2c3157e889
commit f72dd62e56
15 changed files with 1257 additions and 1256 deletions

View File

@ -28,7 +28,7 @@ impl EditRoad {
for (k, v) in road.osm_tags.inner() {
txt.add_line(Line(format!("{} = {}", k, v)).secondary());
}
if let Ok((pl, _)) = app.model.map.untrimmed_road_geometry(r) {
if let Ok((pl, _)) = road.untrimmed_road_geometry() {
txt.add_line(Line(format!("Length before trimming: {}", pl.length())));
}
if let Ok(pl) = app.model.map.trimmed_road_geometry(r) {

View File

@ -262,9 +262,9 @@ impl Model {
// Roads
impl Model {
pub fn road_added(&mut self, ctx: &EventCtx, id: OriginalRoad) {
let (center, total_width) = self.map.untrimmed_road_geometry(id).unwrap();
let hitbox = center.make_polygons(total_width);
let road = &self.map.roads[&id];
let (center, total_width) = road.untrimmed_road_geometry().unwrap();
let hitbox = center.make_polygons(total_width);
let mut draw = GeomBatch::new();
draw.push(
if road.osm_tags.is("junction", "intersection") {
@ -338,6 +338,7 @@ impl Model {
self.map.intersections[&i2].point,
],
osm_tags,
&self.map.config,
),
);
self.road_added(ctx, id);

View File

@ -109,8 +109,10 @@ pub fn extract_osm(
way.tags.insert(osm::SIDEWALK, "right");
}
out.roads
.push((id, RawRoad::new(way.pts.clone(), way.tags.clone())));
out.roads.push((
id,
RawRoad::new(way.pts.clone(), way.tags.clone(), &opts.map_config),
));
continue;
} else if way.tags.is(osm::HIGHWAY, "service") {
// If we got here, is_road didn't interpret it as a normal road

View File

@ -31,6 +31,8 @@ pub fn apply_parking(map: &mut RawMap, opts: &Options, timer: &mut Timer) {
} else {
r.osm_tags.insert(osm::PARKING_BOTH, "parallel");
}
r.lane_specs_ltr = raw_map::get_lane_specs_ltr(&r.osm_tags, &opts.map_config);
}
}
}
@ -138,6 +140,9 @@ fn use_parking_hints(map: &mut RawMap, path: String, timer: &mut Timer) {
tags.remove(osm::PARKING_RIGHT).unwrap();
tags.insert(osm::PARKING_BOTH, value);
}
let lane_specs_ltr = raw_map::get_lane_specs_ltr(tags, &map.config);
map.roads.get_mut(&r).unwrap().lane_specs_ltr = lane_specs_ltr;
}
}
timer.stop("apply parking hints");

File diff suppressed because it is too large Load Diff

View File

@ -142,7 +142,7 @@ impl Map {
orig_id: r.id,
lanes: Vec::new(),
center_pts: r.trimmed_center_pts,
untrimmed_center_pts: raw.untrimmed_road_geometry(r.id).unwrap().0,
untrimmed_center_pts: raw_road.untrimmed_road_geometry().unwrap().0,
src_i: i1,
dst_i: i2,
speed_limit: Speed::ZERO,
@ -156,7 +156,7 @@ impl Map {
road.speed_limit = road.speed_limit_from_osm();
road.access_restrictions = road.access_restrictions_from_osm();
road.recreate_lanes(r.lane_specs_ltr);
road.recreate_lanes(raw_road.lane_specs_ltr.clone());
for lane in &road.lanes {
map.intersections[lane.src_i.0].outgoing_lanes.push(lane.id);
map.intersections[lane.dst_i.0].incoming_lanes.push(lane.id);

View File

@ -11,7 +11,7 @@ impl RawMap {
pub fn save_osm2polygon_input(&self, output_path: String, i: osm::NodeID) -> Result<()> {
let mut features = Vec::new();
for id in self.roads_per_intersection(i) {
let (untrimmed_center_pts, total_width) = self.untrimmed_road_geometry(id)?;
let (untrimmed_center_pts, total_width) = self.roads[&id].untrimmed_road_geometry()?;
let mut properties = serde_json::Map::new();
properties.insert("osm_way_id".to_string(), id.osm_way_id.0.into());

View File

@ -8,7 +8,7 @@ use anyhow::Result;
use abstutil::{Tags, Timer};
use geom::{Bounds, Circle, Distance, PolyLine, Polygon, Pt2D};
use crate::{osm, InputRoad, IntersectionType, LaneSpec, OriginalRoad, RawMap};
use crate::{osm, InputRoad, IntersectionType, OriginalRoad, RawMap};
pub struct InitialMap {
pub roads: BTreeMap<OriginalRoad, Road>,
@ -26,18 +26,13 @@ pub struct Road {
// The true center of the road, including sidewalks
pub trimmed_center_pts: PolyLine,
pub half_width: Distance,
pub lane_specs_ltr: Vec<LaneSpec>,
pub osm_tags: Tags,
}
impl Road {
pub fn new(map: &RawMap, id: OriginalRoad) -> Result<Road> {
let road = &map.roads[&id];
let mut lane_specs_ltr = crate::lane_specs::get_lane_specs_ltr(&road.osm_tags, &map.config);
for l in &mut lane_specs_ltr {
l.width *= road.scale_width;
}
let (trimmed_center_pts, total_width) = map.untrimmed_road_geometry(id)?;
let (trimmed_center_pts, total_width) = road.untrimmed_road_geometry()?;
Ok(Road {
id,
@ -45,7 +40,6 @@ impl Road {
dst_i: id.i2,
trimmed_center_pts,
half_width: total_width / 2.0,
lane_specs_ltr,
osm_tags: road.osm_tags.clone(),
})
}

View File

@ -10,7 +10,7 @@ extern crate log;
use std::collections::BTreeMap;
use std::fmt;
use anyhow::{Context, Result};
use anyhow::Result;
use petgraph::graphmap::DiGraphMap;
use serde::{Deserialize, Serialize};
@ -268,40 +268,6 @@ impl RawMap {
}
}
/// Returns the corrected (but untrimmed) center and total width for a road
pub fn untrimmed_road_geometry(&self, id: OriginalRoad) -> Result<(PolyLine, Distance)> {
let road = &self.roads[&id];
let lane_specs = get_lane_specs_ltr(&road.osm_tags, &self.config);
let mut total_width = Distance::ZERO;
let mut sidewalk_right = None;
let mut sidewalk_left = None;
for l in &lane_specs {
total_width += l.width;
if l.lt.is_walkable() {
if l.dir == Direction::Back {
sidewalk_left = Some(l.width);
} else {
sidewalk_right = Some(l.width);
}
}
}
// If there's a sidewalk on only one side, adjust the true center of the road.
let mut true_center =
PolyLine::new(road.center_points.clone()).with_context(|| id.to_string())?;
match (sidewalk_right, sidewalk_left) {
(Some(w), None) => {
true_center = true_center.must_shift_right(w / 2.0);
}
(None, Some(w)) => {
true_center = true_center.must_shift_right(w / 2.0);
}
_ => {}
}
Ok((true_center, road.scale_width * total_width))
}
pub fn save(&self) {
abstio::write_binary(abstio::path_raw_map(&self.name), self)
}
@ -377,8 +343,6 @@ pub struct RawRoad {
/// cul-de-sac roads for roundabout handling. No transformation of these points whatsoever has
/// happened.
pub center_points: Vec<Pt2D>,
/// Multiply the width of each lane by this ratio, to prevent overlapping roads.
pub scale_width: f64,
pub osm_tags: Tags,
pub turn_restrictions: Vec<(RestrictionType, OriginalRoad)>,
/// (via, to). For turn restrictions where 'via' is an entire road. Only BanTurns.
@ -387,13 +351,17 @@ pub struct RawRoad {
/// Is there a tagged crosswalk near each end of the road?
pub crosswalk_forward: bool,
pub crosswalk_backward: bool,
/// Derived from osm_tags. Not automatically updated.
pub lane_specs_ltr: Vec<LaneSpec>,
}
impl RawRoad {
pub fn new(osm_center_points: Vec<Pt2D>, osm_tags: Tags) -> Self {
pub fn new(osm_center_points: Vec<Pt2D>, osm_tags: Tags, config: &MapConfig) -> Self {
let lane_specs_ltr = get_lane_specs_ltr(&osm_tags, config);
Self {
center_points: osm_center_points,
scale_width: 1.0,
osm_tags,
turn_restrictions: Vec::new(),
complicated_turn_restrictions: Vec::new(),
@ -402,6 +370,8 @@ impl RawRoad {
// later
crosswalk_forward: true,
crosswalk_backward: true,
lane_specs_ltr,
}
}
@ -428,10 +398,10 @@ impl RawRoad {
self.osm_tags.is(osm::HIGHWAY, "service")
}
pub fn is_cycleway(&self, cfg: &MapConfig) -> bool {
pub fn is_cycleway(&self) -> bool {
// Don't repeat the logic looking at the tags, just see what lanes we'll create
let mut bike = false;
for spec in get_lane_specs_ltr(&self.osm_tags, cfg) {
for spec in &self.lane_specs_ltr {
if spec.lt == LaneType::Biking {
bike = true;
} else if spec.lt != LaneType::Shoulder {
@ -441,9 +411,9 @@ impl RawRoad {
bike
}
pub fn is_driveable(&self, cfg: &MapConfig) -> bool {
get_lane_specs_ltr(&self.osm_tags, cfg)
.into_iter()
pub fn is_driveable(&self) -> bool {
self.lane_specs_ltr
.iter()
.any(|spec| spec.lt == LaneType::Driving)
}
@ -478,6 +448,39 @@ impl RawRoad {
0
}
}
/// Returns the corrected (but untrimmed) center and total width for a road
pub fn untrimmed_road_geometry(&self) -> Result<(PolyLine, Distance)> {
let mut total_width = Distance::ZERO;
let mut sidewalk_right = None;
let mut sidewalk_left = None;
for l in &self.lane_specs_ltr {
total_width += l.width;
if l.lt.is_walkable() {
if l.dir == Direction::Back {
sidewalk_left = Some(l.width);
} else {
sidewalk_right = Some(l.width);
}
}
}
// If there's a sidewalk on only one side, adjust the true center of the road.
// TODO I don't remember the rationale for doing this in the first place. What if there's a
// shoulder and a sidewalk of different widths? We don't do anything then
let mut true_center = PolyLine::new(self.center_points.clone())?;
match (sidewalk_right, sidewalk_left) {
(Some(w), None) => {
true_center = true_center.must_shift_right(w / 2.0);
}
(None, Some(w)) => {
true_center = true_center.must_shift_right(w / 2.0);
}
_ => {}
}
Ok((true_center, total_width))
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]

View File

@ -4,9 +4,8 @@ use anyhow::Result;
use geom::{Distance, Pt2D};
use crate::lane_specs::get_lane_specs_ltr;
use crate::osm::NodeID;
use crate::{osm, IntersectionType, LaneSpec, LaneType, OriginalRoad, RawMap};
use crate::{osm, IntersectionType, OriginalRoad, RawMap};
/// Collapse degenerate intersections:
/// - between two cycleways
@ -54,9 +53,7 @@ fn should_collapse(r1: OriginalRoad, r2: OriginalRoad, raw: &RawMap) -> Result<(
bail!("oneway roads point at each other");
}
let lanes1 = get_lane_specs_ltr(&road1.osm_tags, &raw.config);
let lanes2 = get_lane_specs_ltr(&road2.osm_tags, &raw.config);
if lanes1 != lanes2 {
if road1.lane_specs_ltr != road2.lane_specs_ltr {
bail!("lane specs don't match");
}
@ -64,7 +61,7 @@ fn should_collapse(r1: OriginalRoad, r2: OriginalRoad, raw: &RawMap) -> Result<(
bail!("zorders don't match");
}
if is_cycleway(&lanes1) && is_cycleway(&lanes2) {
if road1.is_cycleway() && road2.is_cycleway() {
return Ok(());
}
@ -135,20 +132,6 @@ fn should_collapse(r1: OriginalRoad, r2: OriginalRoad, raw: &RawMap) -> Result<(
Ok(())
}
// Rather bruteforce way of figuring this out... is_cycleway logic lifted from Road, unfortunately.
// Better than repeating the OSM tag log from get_lane_specs_ltr.
fn is_cycleway(lanes: &[LaneSpec]) -> bool {
let mut bike = false;
for spec in lanes {
if spec.lt == LaneType::Biking {
bike = true;
} else if spec.lt != LaneType::Shoulder {
return false;
}
}
bike
}
pub fn collapse_intersection(raw: &mut RawMap, i: NodeID) {
let roads = raw.roads_per_intersection(i);
assert_eq!(roads.len(), 2);
@ -250,8 +233,7 @@ pub fn trim_deadends(raw: &mut RawMap) {
}
let road = &raw.roads[&roads[0]];
if road.length() < SHORT_THRESHOLD
&& (is_cycleway(&get_lane_specs_ltr(&road.osm_tags, &raw.config))
|| road.osm_tags.is(osm::HIGHWAY, "service"))
&& (road.is_cycleway() || road.osm_tags.is(osm::HIGHWAY, "service"))
{
remove_roads.insert(roads[0]);
remove_intersections.insert(*id);

View File

@ -105,7 +105,7 @@ impl RawMap {
{
continue;
}
if let Ok((pl, _)) = self.untrimmed_road_geometry(*id) {
if let Ok((pl, _)) = road.untrimmed_road_geometry() {
if pl.length() <= threshold {
results.push(*id);
}
@ -149,7 +149,7 @@ impl RawMap {
for r in &connections {
// Are both intersections 3-ways of driveable roads? (Don't even attempt
// cycleways yet...)
if !self.roads[r].is_driveable(&self.config) {
if !self.roads[r].is_driveable() {
continue 'ROAD;
}
// Don't do anything near border intersections

View File

@ -1,10 +1,12 @@
use std::collections::HashMap;
use std::collections::{HashSet, HashMap};
use aabb_quadtree::QuadTree;
use abstutil::Timer;
use crate::{osm, RawMap};
/// Look for roads that physically overlap, but aren't connected by an intersection. Shrink their
/// width.
pub fn shrink(raw: &mut RawMap, timer: &mut Timer) {
let mut road_centers = HashMap::new();
let mut road_polygons = HashMap::new();
@ -21,7 +23,7 @@ pub fn shrink(raw: &mut RawMap, timer: &mut Timer) {
continue;
}
let (center, total_width) = match raw.untrimmed_road_geometry(*id) {
let (center, total_width) = match road.untrimmed_road_geometry() {
Ok((center, total_width)) => (center, total_width),
Err(err) => {
// Crashing in Lisbon because of https://www.openstreetmap.org/node/5754625281 and
@ -52,12 +54,24 @@ pub fn shrink(raw: &mut RawMap, timer: &mut Timer) {
}
timer.start_iter("shrink overlapping roads", overlapping.len());
let mut shrunk = HashSet::new();
for (r1, r2) in overlapping {
timer.next();
// TODO It'd be better to gradually shrink each road until they stop touching. I got that
// working in some maps, but it crashes in others (downstream in intersection polygon code)
// for unknown reasons. Just do the simple thing for now.
raw.roads.get_mut(&r1).unwrap().scale_width = 0.5;
raw.roads.get_mut(&r2).unwrap().scale_width = 0.5;
for id in [r1, r2] {
// Don't shrink any road twice!
if shrunk.contains(&id) {
continue;
}
shrunk.insert(id);
// Anything derived from lane_specs_ltr will need to be changed. When we store
// untrimmed and trimmed center points instead of calculating them dynamically, that'll
// have to happen here.
for spec in &mut raw.roads.get_mut(&id).unwrap().lane_specs_ltr {
spec.width *= 0.5;
}
}
}
}

View File

@ -19,11 +19,11 @@ pub fn snap_cycleways(map: &mut RawMap) {
// Because there are so many false positives with snapping, only start with cycleways
// explicitly tagged with
// https://wiki.openstreetmap.org/wiki/Proposed_features/cycleway:separation
if road.is_cycleway(&map.config)
if road.is_cycleway()
&& (road.osm_tags.contains_key("separation:left")
|| road.osm_tags.contains_key("separation:right"))
{
let (center, total_width) = map.untrimmed_road_geometry(*id).unwrap();
let (center, total_width) = road.untrimmed_road_geometry().unwrap();
cycleways.push(Cycleway {
id: *id,
center,
@ -35,10 +35,10 @@ pub fn snap_cycleways(map: &mut RawMap) {
let mut road_edges: HashMap<(OriginalRoad, Direction), PolyLine> = HashMap::new();
for (id, r) in &map.roads {
if r.is_light_rail() || r.is_footway() || r.is_service() || r.is_cycleway(&map.config) {
if r.is_light_rail() || r.is_footway() || r.is_service() || r.is_cycleway() {
continue;
}
let (pl, total_width) = map.untrimmed_road_geometry(*id).unwrap();
let (pl, total_width) = r.untrimmed_road_geometry().unwrap();
road_edges.insert(
(*id, Direction::Fwd),
pl.must_shift_right(total_width / 2.0),

View File

@ -7,16 +7,16 @@ data/system/us/seattle/maps/lakeslice.bin
data/system/us/phoenix/maps/tempe.bin
425 single blocks (1 failures to blockify), 0 partial merges, 0 failures to blockify partitions
data/system/gb/bristol/maps/east.bin
1059 single blocks (3 failures to blockify), 5 partial merges, 0 failures to blockify partitions
1059 single blocks (3 failures to blockify), 6 partial merges, 0 failures to blockify partitions
data/system/gb/leeds/maps/north.bin
2597 single blocks (5 failures to blockify), 17 partial merges, 1 failures to blockify partitions
data/system/gb/london/maps/camden.bin
1561 single blocks (1 failures to blockify), 6 partial merges, 0 failures to blockify partitions
1565 single blocks (1 failures to blockify), 5 partial merges, 0 failures to blockify partitions
data/system/gb/london/maps/southwark.bin
2195 single blocks (3 failures to blockify), 10 partial merges, 0 failures to blockify partitions
2195 single blocks (3 failures to blockify), 11 partial merges, 1 failures to blockify partitions
data/system/gb/manchester/maps/levenshulme.bin
1351 single blocks (1 failures to blockify), 1 partial merges, 0 failures to blockify partitions
1353 single blocks (1 failures to blockify), 0 partial merges, 0 failures to blockify partitions
data/system/fr/lyon/maps/center.bin
5220 single blocks (5 failures to blockify), 26 partial merges, 1 failures to blockify partitions
5216 single blocks (5 failures to blockify), 26 partial merges, 1 failures to blockify partitions
data/system/us/seattle/maps/north_seattle.bin
3376 single blocks (1 failures to blockify), 6 partial merges, 1 failures to blockify partitions

View File

@ -4,14 +4,14 @@
"scenario": "weekday",
"finished_trips": 76640,
"cancelled_trips": 0,
"total_trip_duration_seconds": 43931256.87820016
"total_trip_duration_seconds": 43622111.81940041
},
{
"map": "montlake (in seattle (us))",
"scenario": "weekday",
"finished_trips": 36710,
"cancelled_trips": 86,
"total_trip_duration_seconds": 18533435.094100088
"finished_trips": 36706,
"cancelled_trips": 90,
"total_trip_duration_seconds": 18616755.940000143
},
{
"map": "sao_miguel_paulista (in sao_paulo (br))",