Update osm2streets and handle OSM tags moved off of roads

This commit is contained in:
Dustin Carlino 2022-11-25 22:10:53 +00:00
parent 3d3062772c
commit a77c022200
16 changed files with 186 additions and 125 deletions

4
Cargo.lock generated
View File

@ -2952,7 +2952,7 @@ dependencies = [
[[package]]
name = "osm2streets"
version = "0.1.0"
source = "git+https://github.com/a-b-street/osm2streets#663d5a7c1f00b8b627d953dff9fec67d4b55ae5a"
source = "git+https://github.com/a-b-street/osm2streets#7fa918b7dc1120f70a5e47f260330c620fa6a836"
dependencies = [
"aabb-quadtree",
"abstutil",
@ -3976,7 +3976,7 @@ checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
[[package]]
name = "streets_reader"
version = "0.1.0"
source = "git+https://github.com/a-b-street/osm2streets#663d5a7c1f00b8b627d953dff9fec67d4b55ae5a"
source = "git+https://github.com/a-b-street/osm2streets#7fa918b7dc1120f70a5e47f260330c620fa6a836"
dependencies = [
"abstutil",
"anyhow",

View File

@ -9,7 +9,7 @@ use map_gui::options::OptionsPanel;
use map_gui::render::{calculate_corners, DrawMap, DrawOptions};
use map_gui::{AppLike, ID};
use map_model::{
osm, ControlTrafficSignal, IntersectionID, PathConstraints, Perimeter, Position, RoadID,
ControlTrafficSignal, IntersectionID, PathConstraints, Perimeter, Position, RoadID,
NORMAL_LANE_THICKNESS,
};
use sim::Sim;
@ -938,10 +938,6 @@ fn find_degenerate_roads(app: &App) {
fn diff_tags(t1: &Tags, t2: &Tags) {
for (k, v1, v2) in t1.diff(t2) {
// Ignore the most common diff
if k == osm::OSM_WAY_ID {
continue;
}
println!("- {} = \"{}\" vs \"{}\"", k, v1, v2);
}
}

View File

@ -4,7 +4,8 @@ use geom::{Bounds, CornerRadii, Distance, Polygon, Pt2D, UnitFmt};
use map_gui::render::{Renderable, OUTLINE_THICKNESS};
use map_gui::ID;
use map_model::{
BufferType, Direction, EditCmd, EditRoad, LaneID, LaneSpec, LaneType, MapEdits, Road, RoadID,
osm, BufferType, Direction, EditCmd, EditRoad, LaneID, LaneSpec, LaneType, MapEdits, Road,
RoadID,
};
use widgetry::tools::PopupMsg;
use widgetry::{
@ -18,6 +19,9 @@ use crate::common::Warping;
use crate::edit::zones::ZoneEditor;
use crate::edit::{apply_map_edits, can_edit_lane, speed_limit_choices};
// TODO Future bug alert: osm_tags.get(osm::HIGHWAY) is brittle, because it'll break for railways.
// Plumb through road.highway instead.
pub struct RoadEditor {
r: RoadID,
selected_lane: Option<LaneID>,
@ -290,10 +294,16 @@ impl State<App> for RoadEditor {
} else {
LaneType::from_short_name(lt).unwrap()
};
let width =
LaneSpec::typical_lane_widths(lt, &app.primary.map.get_r(self.r).osm_tags)
[0]
.0;
let width = LaneSpec::typical_lane_widths(
lt,
app.primary
.map
.get_r(self.r)
.osm_tags
.get(osm::HIGHWAY)
.unwrap(),
)[0]
.0;
return self.modify_current_lane(ctx, app, Some(0), |new, idx| {
new.lanes_ltr[idx].lt = lt;
new.lanes_ltr[idx].width = width;
@ -324,7 +334,12 @@ impl State<App> for RoadEditor {
let idx = LaneSpec::add_new_lane(
&mut new.lanes_ltr,
lt,
&app.primary.map.get_r(self.r).osm_tags,
app.primary
.map
.get_r(self.r)
.osm_tags
.get(osm::HIGHWAY)
.unwrap(),
app.primary.map.get_config().driving_side,
);
edits.commands.push(EditCmd::ChangeRoad {
@ -400,10 +415,16 @@ impl State<App> for RoadEditor {
"change to buffer" => {
let lt = self.main_panel.persistent_split_value("change to buffer");
app.session.buffer_lane_type = lt;
let width =
LaneSpec::typical_lane_widths(lt, &app.primary.map.get_r(self.r).osm_tags)
[0]
.0;
let width = LaneSpec::typical_lane_widths(
lt,
app.primary
.map
.get_r(self.r)
.osm_tags
.get(osm::HIGHWAY)
.unwrap(),
)[0]
.0;
return self.modify_current_lane(ctx, app, Some(0), |new, idx| {
new.lanes_ltr[idx].lt = lt;
new.lanes_ltr[idx].width = width;
@ -608,7 +629,9 @@ fn make_main_panel(
.into_iter()
.map(|buf| {
let lt = LaneType::Buffer(buf);
let width = LaneSpec::typical_lane_widths(lt, &road.osm_tags)[0].0;
let width =
LaneSpec::typical_lane_widths(lt, road.osm_tags.get(osm::HIGHWAY).unwrap())[0]
.0;
Choice::new(
format!("{} ({})", lt.short_name(), width.to_string(&app.opts.units)),
lt,
@ -982,7 +1005,12 @@ fn width_choices(app: &App, l: LaneID) -> Vec<Choice<Distance>> {
let lane = app.primary.map.get_l(l);
let mut choices = LaneSpec::typical_lane_widths(
lane.lane_type,
&app.primary.map.get_r(lane.id.road).osm_tags,
app.primary
.map
.get_r(lane.id.road)
.osm_tags
.get(osm::HIGHWAY)
.unwrap(),
);
if !choices.iter().any(|(x, _)| *x == lane.width) {
choices.push((lane.width, "custom"));

View File

@ -1,3 +1,4 @@
use abstutil::Tags;
use geom::{ArrowCap, Distance};
use osm2streets::RoadID;
use widgetry::{
@ -31,8 +32,15 @@ impl EditRoad {
);
}
let tags = app
.model
.map
.road_to_osm_tags(r)
.cloned()
.unwrap_or_else(Tags::empty);
let mut txt = Text::new();
for (k, v) in road.osm_tags.inner() {
for (k, v) in tags.inner() {
txt.add_line(Line(format!("{} = {}", k, v)).secondary());
}
txt.add_line(Line(format!(
@ -60,8 +68,7 @@ impl EditRoad {
ctx,
"lanes:forward",
(1, 5),
road.osm_tags
.get("lanes:forward")
tags.get("lanes:forward")
.and_then(|x| x.parse::<usize>().ok())
.unwrap_or(1),
1,
@ -73,16 +80,9 @@ impl EditRoad {
ctx,
"lanes:backward",
(0, 5),
road.osm_tags
.get("lanes:backward")
tags.get("lanes:backward")
.and_then(|x| x.parse::<usize>().ok())
.unwrap_or_else(|| {
if road.osm_tags.is("oneway", "yes") {
0
} else {
1
}
}),
.unwrap_or_else(|| if tags.is("oneway", "yes") { 0 } else { 1 }),
1,
),
]),
@ -91,13 +91,13 @@ impl EditRoad {
Widget::dropdown(
ctx,
"sidewalk",
if road.osm_tags.is("sidewalk", "both") {
if tags.is("sidewalk", "both") {
"both"
} else if road.osm_tags.is("sidewalk", "none") {
} else if tags.is("sidewalk", "none") {
"none"
} else if road.osm_tags.is("sidewalk", "left") {
} else if tags.is("sidewalk", "left") {
"left"
} else if road.osm_tags.is("sidewalk", "right") {
} else if tags.is("sidewalk", "right") {
"right"
} else {
"both"
@ -112,16 +112,13 @@ impl EditRoad {
ctx,
"parking",
// TODO Not all possibilities represented here; very simplified.
if road.osm_tags.is("parking:lane:both", "parallel") {
if tags.is("parking:lane:both", "parallel") {
"both"
} else if road
.osm_tags
.is_any("parking:lane:both", vec!["no_parking", "no_stopping"])
{
} else if tags.is_any("parking:lane:both", vec!["no_parking", "no_stopping"]) {
"none"
} else if road.osm_tags.is("parking:lane:left", "parallel") {
} else if tags.is("parking:lane:left", "parallel") {
"left"
} else if road.osm_tags.is("parking:lane:right", "parallel") {
} else if tags.is("parking:lane:right", "parallel") {
"right"
} else {
"none"
@ -174,47 +171,52 @@ impl SimpleState<App> for EditRoad {
"Apply" => {
app.model.road_deleted(self.r);
let road = app.model.map.streets.roads.get_mut(&self.r).unwrap();
let mut tags = app
.model
.map
.road_to_osm_tags(self.r)
.cloned()
.unwrap_or_else(Tags::empty);
road.osm_tags.remove("lanes");
road.osm_tags.remove("oneway");
tags.remove("lanes");
tags.remove("oneway");
let fwd: usize = panel.spinner("lanes:forward");
let back: usize = panel.spinner("lanes:backward");
if back == 0 {
road.osm_tags.insert("oneway", "yes");
road.osm_tags.insert("lanes", fwd.to_string());
tags.insert("oneway", "yes");
tags.insert("lanes", fwd.to_string());
} else {
road.osm_tags.insert("lanes", (fwd + back).to_string());
road.osm_tags.insert("lanes:forward", fwd.to_string());
road.osm_tags.insert("lanes:backward", back.to_string());
tags.insert("lanes", (fwd + back).to_string());
tags.insert("lanes:forward", fwd.to_string());
tags.insert("lanes:backward", back.to_string());
}
road.osm_tags
.insert("sidewalk", panel.dropdown_value::<String, &str>("sidewalk"));
tags.insert("sidewalk", panel.dropdown_value::<String, &str>("sidewalk"));
road.osm_tags.remove("parking:lane:both");
road.osm_tags.remove("parking:lane:left");
road.osm_tags.remove("parking:lane:right");
tags.remove("parking:lane:both");
tags.remove("parking:lane:left");
tags.remove("parking:lane:right");
match panel.dropdown_value::<String, &str>("parking").as_ref() {
"both" => {
road.osm_tags.insert("parking:lane:both", "parallel");
tags.insert("parking:lane:both", "parallel");
}
"none" => {
road.osm_tags.insert("parking:lane:both", "none");
tags.insert("parking:lane:both", "none");
}
"left" => {
road.osm_tags.insert("parking:lane:left", "parallel");
road.osm_tags.insert("parking:lane:right", "none");
tags.insert("parking:lane:left", "parallel");
tags.insert("parking:lane:right", "none");
}
"right" => {
road.osm_tags.insert("parking:lane:left", "none");
road.osm_tags.insert("parking:lane:right", "parallel");
tags.insert("parking:lane:left", "none");
tags.insert("parking:lane:right", "parallel");
}
_ => unreachable!(),
}
let road = app.model.map.streets.roads.get_mut(&self.r).unwrap();
road.lane_specs_ltr =
osm2streets::get_lane_specs_ltr(&road.osm_tags, &app.model.map.streets.config);
osm2streets::get_lane_specs_ltr(&tags, &app.model.map.streets.config);
let scale = panel.spinner("width_scale");
for lane in &mut road.lane_specs_ltr {
lane.width *= scale;
@ -235,9 +237,14 @@ impl SimpleState<App> for EditRoad {
) -> Option<Transition<App>> {
let scale = panel.spinner("width_scale");
app.model.road_deleted(self.r);
let tags = app
.model
.map
.road_to_osm_tags(self.r)
.cloned()
.unwrap_or_else(Tags::empty);
let road = app.model.map.streets.roads.get_mut(&self.r).unwrap();
road.lane_specs_ltr =
osm2streets::get_lane_specs_ltr(&road.osm_tags, &app.model.map.streets.config);
road.lane_specs_ltr = osm2streets::get_lane_specs_ltr(&tags, &app.model.map.streets.config);
for lane in &mut road.lane_specs_ltr {
lane.width *= scale;
}

View File

@ -301,7 +301,7 @@ impl Model {
let hitbox = center.make_polygons(total_width);
let mut draw = GeomBatch::new();
draw.push(
if road.osm_tags.is("junction", "intersection") {
if road.internal_junction_road {
Color::PINK
} else {
Color::grey(0.8)
@ -350,8 +350,6 @@ impl Model {
osm_tags.insert("parking:both:lane", "parallel");
osm_tags.insert("sidewalk", "both");
osm_tags.insert("lanes", "2");
osm_tags.insert(osm::ENDPT_FWD, "true");
osm_tags.insert(osm::ENDPT_BACK, "true");
// Reasonable defaults.
osm_tags.insert("name", "Streety McStreetFace");
osm_tags.insert("maxspeed", "25 mph");
@ -570,11 +568,7 @@ impl Model {
self.road_deleted(id);
let road = self.map.streets.roads.get_mut(&id).unwrap();
if road.osm_tags.is("junction", "intersection") {
road.osm_tags.remove("junction");
} else {
road.osm_tags.insert("junction", "intersection");
}
road.internal_junction_road = !road.internal_junction_road;
self.road_added(ctx, id);
}
@ -667,8 +661,9 @@ fn dump_to_osm(map: &RawMap) -> Result<(), std::io::Error> {
pt_to_id[&pt.to_hashable()].0
)?;
}
for (k, v) in r.osm_tags.inner() {
if !k.starts_with("abst:") {
// TODO Brittle. Instead we should effectively do lanes2osm
if let Some(tags) = map.road_to_osm_tags(*id) {
for (k, v) in tags.inner() {
writeln!(f, r#" <tag k="{}" v="{}"/>"#, k, v)?;
}
}

View File

@ -6,7 +6,6 @@ use map_gui::options::OptionsPanel;
use map_gui::render::{DrawOptions, BIG_ARROW_THICKNESS};
use map_gui::tools::{CityPicker, Minimap, MinimapControls, Navigator, TurnExplorer};
use map_gui::{SimpleApp, ID};
use map_model::osm;
use widgetry::tools::{open_browser, PopupMsg, URLManager};
use widgetry::{
lctrl, Color, DrawBaselayer, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
@ -110,9 +109,9 @@ impl Viewer {
continue;
}
if tags.contains_key("abst:parking_source")
&& (k == osm::PARKING_RIGHT
|| k == osm::PARKING_LEFT
|| k == osm::PARKING_BOTH)
&& (k == "parking:lane:right"
|| k == "parking:lane:left"
|| k == "parking:lane:both")
{
continue;
}

View File

@ -526,29 +526,29 @@ fn generate_osmc(data: &BTreeMap<WayID, Value>, in_seattle: bool, timer: &mut Ti
}
// Fill out the tags.
osm_tags.remove(osm::PARKING_LEFT);
osm_tags.remove(osm::PARKING_RIGHT);
osm_tags.remove(osm::PARKING_BOTH);
osm_tags.remove("parking:lane:left");
osm_tags.remove("parking:lane:right");
osm_tags.remove("parking_lane_both");
match value {
Value::BothSides => {
osm_tags.insert(osm::PARKING_BOTH, "parallel");
osm_tags.insert("parking_lane_both", "parallel");
if in_seattle {
osm_tags.insert("parking:condition:both:maxstay", "3 days");
}
}
Value::NoStopping => {
osm_tags.insert(osm::PARKING_BOTH, "no_stopping");
osm_tags.insert("parking_lane_both", "no_stopping");
}
Value::RightOnly => {
osm_tags.insert(osm::PARKING_RIGHT, "parallel");
osm_tags.insert(osm::PARKING_LEFT, "no_stopping");
osm_tags.insert("parking:lane:right", "parallel");
osm_tags.insert("parking:lane:left", "no_stopping");
if in_seattle {
osm_tags.insert("parking:condition:right:maxstay", "3 days");
}
}
Value::LeftOnly => {
osm_tags.insert(osm::PARKING_LEFT, "parallel");
osm_tags.insert(osm::PARKING_RIGHT, "no_stopping");
osm_tags.insert("parking:lane:left", "parallel");
osm_tags.insert("parking:lane:right", "no_stopping");
if in_seattle {
osm_tags.insert("parking:condition:left:maxstay", "3 days");
}

View File

@ -15,7 +15,11 @@ pub fn extract_osm(
clip_path: Option<String>,
opts: &Options,
timer: &mut Timer,
) -> (OsmExtract, MultiMap<osm::WayID, String>) {
) -> (
OsmExtract,
streets_reader::osm_reader::Document,
MultiMap<osm::WayID, String>,
) {
let osm_xml = fs_err::read_to_string(osm_input_path).unwrap();
let mut doc =
streets_reader::osm_reader::read(&osm_xml, &map.streets.gps_bounds, timer).unwrap();
@ -73,8 +77,6 @@ pub fn extract_osm(
timer.next();
let id = *id;
way.tags.insert(osm::OSM_WAY_ID, id.0.to_string());
if out.handle_way(id, &way, opts) {
continue;
} else if way.tags.is(osm::HIGHWAY, "service") {
@ -132,12 +134,6 @@ pub fn extract_osm(
let boundary = map.streets.boundary_polygon.get_outer_ring();
// TODO Fill this out in a separate loop to keep a mutable borrow short. Maybe do this in
// reader, or stop doing this entirely.
for (id, rel) in &mut doc.relations {
rel.tags.insert(osm::OSM_REL_ID, id.0.to_string());
}
timer.start_iter("processing OSM relations", doc.relations.len());
for (id, rel) in &doc.relations {
timer.next();
@ -285,7 +281,7 @@ pub fn extract_osm(
find_parking_aisles(map, &mut out.roads);
timer.stop("find service roads crossing parking lots");
(out, bus_routes_on_roads)
(out, doc, bus_routes_on_roads)
}
fn is_bldg(tags: &Tags) -> bool {

View File

@ -3,6 +3,8 @@ extern crate anyhow;
#[macro_use]
extern crate log;
use std::collections::HashSet;
use anyhow::Result;
use abstio::MapName;
@ -42,7 +44,7 @@ pub fn convert(
map.streets.gps_bounds = gps_bounds;
}
let (extract, bus_routes_on_roads) =
let (extract, doc, bus_routes_on_roads) =
extract::extract_osm(&mut map, &osm_input_path, clip_path, &opts, timer);
map.bus_routes_on_roads = bus_routes_on_roads;
let split_output = streets_reader::split_ways::split_up_roads(&mut map.streets, extract, timer);
@ -52,6 +54,19 @@ pub fn convert(
// doing the parking hint matching.
map.streets.retain_roads(|r| r.src_i != r.dst_i);
// Remember OSM tags for all roads. Do this before apply_parking, which looks at tags
let mut way_ids = HashSet::new();
for r in map.streets.roads.values() {
for id in &r.osm_ids {
way_ids.insert(id.osm_way_id);
}
}
for (id, way) in doc.ways {
if way_ids.contains(&id) {
map.osm_tags.insert(id, way.tags);
}
}
parking::apply_parking(&mut map, &opts, timer);
streets_reader::use_barrier_nodes(
@ -92,7 +107,9 @@ pub fn convert(
if map.name == MapName::new("gb", "bristol", "east") {
bristol_hack(&mut map);
}
timer.stop("create RawMap from input data");
map
}

View File

@ -27,9 +27,9 @@ pub fn apply_parking(map: &mut RawMap, opts: &Options, timer: &mut Timer) {
}
fn unknown_parking(tags: &Tags) -> bool {
!tags.contains_key(osm::PARKING_LEFT)
&& !tags.contains_key(osm::PARKING_RIGHT)
&& !tags.contains_key(osm::PARKING_BOTH)
!tags.contains_key("parking:lane:left")
&& !tags.contains_key("parking:lane:right")
&& !tags.contains_key("parking:lane:both")
&& !tags.is_any(osm::HIGHWAY, vec!["motorway", "motorway_link", "service"])
&& !tags.is("junction", "roundabout")
}
@ -42,7 +42,7 @@ fn use_parking_hints(map: &mut RawMap, path: String, timer: &mut Timer) {
let mut closest: FindClosest<(RoadID, bool)> =
FindClosest::new(&map.streets.gps_bounds.to_bounds());
for (id, r) in &map.streets.roads {
if r.is_light_rail() || r.is_footway() || r.is_service() {
if r.is_service() || !r.is_driveable() {
continue;
}
closest.add(
@ -75,7 +75,7 @@ fn use_parking_hints(map: &mut RawMap, path: String, timer: &mut Timer) {
continue;
};
if let Some(((r, fwds), _)) = closest.closest_pt(middle, DIRECTED_ROAD_THICKNESS * 5.0) {
let tags = &mut map.streets.roads.get_mut(&r).unwrap().osm_tags;
let mut tags = map.road_to_osm_tags(r).cloned().unwrap_or_else(Tags::empty);
// Skip if the road already has this mapped.
if !unknown_parking(&tags) {
@ -105,16 +105,16 @@ fn use_parking_hints(map: &mut RawMap, path: String, timer: &mut Timer) {
continue;
}
if let Some(both) = tags.remove(osm::PARKING_BOTH) {
tags.insert(osm::PARKING_LEFT, both.clone());
tags.insert(osm::PARKING_RIGHT, both);
if let Some(both) = tags.remove("parking:lane:both") {
tags.insert("parking:lane:left", both.clone());
tags.insert("parking:lane:right", both);
}
tags.insert(
if fwds {
osm::PARKING_RIGHT
"parking:lane:right"
} else {
osm::PARKING_LEFT
"parking:lane:left"
},
if has_parking {
"parallel"
@ -124,19 +124,22 @@ fn use_parking_hints(map: &mut RawMap, path: String, timer: &mut Timer) {
);
// Maybe fold back into "both"
if tags.contains_key(osm::PARKING_LEFT)
&& tags.get(osm::PARKING_LEFT) == tags.get(osm::PARKING_RIGHT)
if tags.contains_key("parking:lane:left")
&& tags.get("parking:lane:left") == tags.get("parking:lane:right")
{
let value = tags.remove(osm::PARKING_LEFT).unwrap();
tags.remove(osm::PARKING_RIGHT).unwrap();
tags.insert(osm::PARKING_BOTH, value);
let value = tags.remove("parking:lane:left").unwrap();
tags.remove("parking:lane:right").unwrap();
tags.insert("parking:lane:both", value);
}
// Remember that this isn't OSM data
tags.insert("abst:parking_source", "blockface");
let lane_specs_ltr = osm2streets::get_lane_specs_ltr(tags, &map.streets.config);
let lane_specs_ltr = osm2streets::get_lane_specs_ltr(&tags, &map.streets.config);
map.streets.roads.get_mut(&r).unwrap().lane_specs_ltr = lane_specs_ltr;
// Note the change to the tag isn't saved, so regenerating lanes from tags later would
// lose this
}
}
timer.stop("apply parking hints");

View File

@ -392,6 +392,7 @@ fn fix_lane_widths(value: &mut Value, map: &Map) -> Result<()> {
// Before this commit, lane widths weren't modifiable, so this lookup works
// for both "old" and "new".
width: road.lanes[idx].width,
turn_restrictions: Vec::new(),
});
}
cmd[key]["lanes_ltr"] = serde_json::to_value(lanes_ltr).unwrap();

View File

@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
use abstutil::Timer;
use geom::{Distance, HashablePt2D, Line, Speed, Time};
use osm2streets::{get_lane_specs_ltr, InputRoad};
use osm2streets::{get_lane_specs_ltr, osm, InputRoad};
pub use self::perma::PermanentMapEdits;
use crate::make::{match_points_to_lanes, snap_driveway, trim_path};
@ -632,7 +632,7 @@ fn recalculate_intersection_polygon(
dst_i: osm2streets::IntersectionID(r.dst_i.0),
center_pts: trimmed_center_pts,
half_width,
osm_tags: r.osm_tags.clone(),
highway_type: r.osm_tags.get(osm::HIGHWAY).unwrap().to_string(),
});
}

View File

@ -120,7 +120,8 @@ impl Map {
snap_nodes_with_data_to_line(&r.crossing_nodes, &r.trimmed_center_line);
let mut road = Road {
id: road_id,
osm_tags: r.osm_tags.clone(),
// Arbitrarily remember OSM tags from one of the ways
osm_tags: raw.osm_tags[&r.osm_ids[0].osm_way_id].clone(),
turn_restrictions: r
.turn_restrictions
.iter()
@ -154,7 +155,7 @@ impl Map {
src_i: i1,
dst_i: i2,
speed_limit: Speed::ZERO,
zorder: r.get_zorder(),
zorder: r.layer,
access_restrictions: AccessRestrictions::new(),
percent_incline: r.percent_incline,
crosswalk_forward: r.crosswalk_forward,

View File

@ -6,8 +6,8 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
use geom::{Distance, Line, PolyLine, Polygon, Pt2D};
use crate::{
osm, DirectedRoadID, Direction, DrivingSide, IntersectionID, LaneType, Map, MapConfig, Road,
RoadID, RoadSideID, SideOfRoad, TurnType,
DirectedRoadID, Direction, DrivingSide, IntersectionID, LaneType, Map, MapConfig, Road, RoadID,
RoadSideID, SideOfRoad, TurnType,
};
/// From some manually audited cases in Seattle, the length of parallel street parking spots is a
@ -244,11 +244,15 @@ impl Lane {
return None;
}
let all = if self.dir == Direction::Fwd && road.osm_tags.contains_key(osm::ENDPT_FWD) {
// TODO This'll interpret turn restrictions along every segment of an OSM way. They maybe
// only make sense for the first or last segment.
// TODO We could plumb forward LaneSpec.turn_restrictions instead of redoing some work here
let all = if self.dir == Direction::Fwd {
road.osm_tags
.get("turn:lanes:forward")
.or_else(|| road.osm_tags.get("turn:lanes"))?
} else if self.dir == Direction::Back && road.osm_tags.contains_key(osm::ENDPT_BACK) {
} else if self.dir == Direction::Back {
road.osm_tags.get("turn:lanes:backward")?
} else {
return None;

View File

@ -203,6 +203,8 @@ impl Road {
lt: l.lane_type,
dir: l.dir,
width: l.width,
// TODO These get lost from osm2streets
turn_restrictions: Vec::new(),
})
.collect()
}
@ -228,7 +230,7 @@ impl Road {
}
pub(crate) fn speed_limit_from_osm(&self) -> Speed {
if let Some(limit) = self.osm_tags.get(osm::MAXSPEED) {
if let Some(limit) = self.osm_tags.get("maxspeed") {
if let Ok(kmph) = limit.parse::<f64>() {
if kmph == 0.0 {
warn!("{} has a speed limit of 0", self.orig_id.osm_way_id);
@ -366,7 +368,7 @@ impl Road {
}
}
if let Some(name) = self.osm_tags.get(osm::NAME) {
if let Some(name) = self.osm_tags.get("name") {
if name.is_empty() {
return "???".to_string();
} else {

View File

@ -4,7 +4,7 @@
use std::collections::BTreeMap;
use osm2streets::{osm, StreetNetwork};
use osm2streets::{osm, RoadID, StreetNetwork};
use serde::{Deserialize, Serialize};
use abstio::{CityName, MapName};
@ -46,6 +46,11 @@ pub struct RawMap {
deserialize_with = "deserialize_multimap"
)]
pub bus_routes_on_roads: MultiMap<osm::WayID, String>,
#[serde(
serialize_with = "serialize_btreemap",
deserialize_with = "deserialize_btreemap"
)]
pub osm_tags: BTreeMap<osm::WayID, Tags>,
}
impl RawMap {
@ -60,6 +65,7 @@ impl RawMap {
transit_routes: Vec::new(),
transit_stops: BTreeMap::new(),
bus_routes_on_roads: MultiMap::new(),
osm_tags: BTreeMap::new(),
}
}
@ -70,6 +76,12 @@ impl RawMap {
pub fn get_city_name(&self) -> &CityName {
&self.name.city
}
// Only returns tags for one of the way IDs arbitrarily!
pub fn road_to_osm_tags(&self, id: RoadID) -> Option<&Tags> {
let way = self.streets.roads[&id].osm_ids.get(0)?.osm_way_id;
self.osm_tags.get(&way)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]