diff --git a/data/MANIFEST.txt b/data/MANIFEST.txt index be21258828..1e781c5f4e 100644 --- a/data/MANIFEST.txt +++ b/data/MANIFEST.txt @@ -182,19 +182,19 @@ data/input/seattle/parcels_urbansim.txt,db63d7d606e8702d12f9399e87e6a00f,https:/ data/input/seattle/popdat.bin,793dd4075de7a4cf5ea8005cd68a06c8,https://www.dropbox.com/s/wrpji7e14rdwszs/popdat.bin.zip?dl=0 data/input/seattle/sidewalks.bin,129b460f56f5eb41cab3bfd70fb5fde9,https://www.dropbox.com/s/ma9bmisijc7v7xa/sidewalks.bin.zip?dl=0 data/input/seattle/trips_2014.csv,d4a8e733045b28c0385fb81359d6df03,https://www.dropbox.com/s/5ppravwmk6bf20d/trips_2014.csv.zip?dl=0 -data/system/maps/23rd.bin,9233bb46ba3496b4ab53197f5c029934,https://www.dropbox.com/s/wjl45codk6rqfg4/23rd.bin.zip?dl=0 -data/system/maps/ballard.bin,49ff23b42753cd8d3041d088ae115639,https://www.dropbox.com/s/u4rvz50she3yrk0/ballard.bin.zip?dl=0 -data/system/maps/barranquilla.bin,f486f373eb04e0408847527b84ab66fd,https://www.dropbox.com/s/iuvggdfr16u6luw/barranquilla.bin.zip?dl=0 -data/system/maps/caphill.bin,417bfe016e8085af60d2d2b9f17bceab,https://www.dropbox.com/s/bh20pn3wxygetw8/caphill.bin.zip?dl=0 -data/system/maps/downtown.bin,cf0998f708acb6e86d63f36ab131b19a,https://www.dropbox.com/s/4do5cg4vc17lafo/downtown.bin.zip?dl=0 -data/system/maps/downtown_atx.bin,f0c2d19284b3afdd9194d8cece002a4b,https://www.dropbox.com/s/5avnbkd4oxby2hs/downtown_atx.bin.zip?dl=0 -data/system/maps/downtown_la.bin,ad73e60a7e078881bd12e4558619b368,https://www.dropbox.com/s/gkcl982cj6kxvrz/downtown_la.bin.zip?dl=0 -data/system/maps/huge_austin.bin,cca4408c14146b47715b017449937a3c,https://www.dropbox.com/s/khy0m6v9yt0gjnt/huge_austin.bin.zip?dl=0 -data/system/maps/huge_seattle.bin,e383ab41e6626e08d58e09c01de4c0b9,https://www.dropbox.com/s/btvr3qajshnivhb/huge_seattle.bin.zip?dl=0 -data/system/maps/intl_district.bin,825f6480fcb54983de9eb6dc055898a9,https://www.dropbox.com/s/fohppni52ekc5l3/intl_district.bin.zip?dl=0 -data/system/maps/lakeslice.bin,11d05f5ae2bea0751ca5dc2388fbde1c,https://www.dropbox.com/s/99zi0gcbyvqrkud/lakeslice.bin.zip?dl=0 -data/system/maps/montlake.bin,b71aa517fc4774b7fdd7c7643f360547,https://www.dropbox.com/s/zvhm2j5lavixxcr/montlake.bin.zip?dl=0 -data/system/maps/west_seattle.bin,ee10fd358d16b005e50a473fd00946ed,https://www.dropbox.com/s/5pp1ik9l40yj3wh/west_seattle.bin.zip?dl=0 +data/system/maps/23rd.bin,1b01be89048b08440e9da8a121eaf6c1,https://www.dropbox.com/s/wjl45codk6rqfg4/23rd.bin.zip?dl=0 +data/system/maps/ballard.bin,07e65eacf9d158520a86e5ef5345b9e9,https://www.dropbox.com/s/u4rvz50she3yrk0/ballard.bin.zip?dl=0 +data/system/maps/barranquilla.bin,51fb8787696fd41f2785233b8899992c,https://www.dropbox.com/s/iuvggdfr16u6luw/barranquilla.bin.zip?dl=0 +data/system/maps/caphill.bin,75d42602513554c40190e6dd308d0651,https://www.dropbox.com/s/bh20pn3wxygetw8/caphill.bin.zip?dl=0 +data/system/maps/downtown.bin,09ba0aa6ec9307f1f56e75124fee0060,https://www.dropbox.com/s/4do5cg4vc17lafo/downtown.bin.zip?dl=0 +data/system/maps/downtown_atx.bin,adaaeb4616d8aba97c67e8e168b69d23,https://www.dropbox.com/s/5avnbkd4oxby2hs/downtown_atx.bin.zip?dl=0 +data/system/maps/downtown_la.bin,126479cf28da991b47e75a3300ea1680,https://www.dropbox.com/s/gkcl982cj6kxvrz/downtown_la.bin.zip?dl=0 +data/system/maps/huge_austin.bin,8d0dedf397eccd8cfc57bf9a0bc9af65,https://www.dropbox.com/s/khy0m6v9yt0gjnt/huge_austin.bin.zip?dl=0 +data/system/maps/huge_seattle.bin,63847b7fbcd3bcb428c2661262671fdd,https://www.dropbox.com/s/btvr3qajshnivhb/huge_seattle.bin.zip?dl=0 +data/system/maps/intl_district.bin,5fb9c68f355d5aa571584c153d95ea5d,https://www.dropbox.com/s/fohppni52ekc5l3/intl_district.bin.zip?dl=0 +data/system/maps/lakeslice.bin,ede83326617e1c13a0b73f68a8bb94d1,https://www.dropbox.com/s/99zi0gcbyvqrkud/lakeslice.bin.zip?dl=0 +data/system/maps/montlake.bin,6f32bf30a3de12bbca8d7ceadd3fd86b,https://www.dropbox.com/s/zvhm2j5lavixxcr/montlake.bin.zip?dl=0 +data/system/maps/west_seattle.bin,9ca3dba9a94bfe3857bdf92d2a4574de,https://www.dropbox.com/s/5pp1ik9l40yj3wh/west_seattle.bin.zip?dl=0 data/system/prebaked_results/lakeslice/weekday.bin,54c504f93db1aa7401e38ef7350adfb0,https://www.dropbox.com/s/1c1sohvy50263wg/weekday.bin.zip?dl=0 data/system/prebaked_results/montlake/car vs bike contention.bin,1031311cb5f27dcda4a92083cc0c214f,https://www.dropbox.com/s/jefg0ikjy9dsrdd/car%20vs%20bike%20contention.bin.zip?dl=0 data/system/prebaked_results/montlake/weekday.bin,30033ba544b2bd9810aa7278ee380ec2,https://www.dropbox.com/s/1aq7n9ow8tfqb5d/weekday.bin.zip?dl=0 diff --git a/game/src/edit/mod.rs b/game/src/edit/mod.rs index 7b24f9f969..066d080904 100644 --- a/game/src/edit/mod.rs +++ b/game/src/edit/mod.rs @@ -147,6 +147,7 @@ impl State for EditMode { let id = match edits.commands.pop().unwrap() { EditCmd::ChangeLaneType { id, .. } => ID::Lane(id), EditCmd::ReverseLane { l, .. } => ID::Lane(l), + EditCmd::ChangeSpeedLimit { id, .. } => ID::Road(id), EditCmd::ChangeIntersection { i, .. } => ID::Intersection(i), }; apply_map_edits(ctx, app, edits); diff --git a/game/src/info/lane.rs b/game/src/info/lane.rs index 3470e6ddce..16ac43dd18 100644 --- a/game/src/info/lane.rs +++ b/game/src/info/lane.rs @@ -26,7 +26,7 @@ pub fn info(ctx: &EventCtx, app: &App, details: &mut Details, id: LaneID) -> Vec ), )); } else { - kv.push(("Speed limit", r.get_speed_limit().to_string())); + kv.push(("Speed limit", r.speed_limit.to_string())); } kv.push(("Length", l.length().describe_rounded())); diff --git a/game/src/layer/map.rs b/game/src/layer/map.rs index 234a29c266..a3f630436d 100644 --- a/game/src/layer/map.rs +++ b/game/src/layer/map.rs @@ -143,6 +143,7 @@ pub fn edits(ctx: &mut EventCtx, app: &App) -> Layers { vec![ format!("{} lane types changed", edits.original_lts.len()), format!("{} lanes reversed", edits.reversed_lanes.len()), + format!("{} speed limits changed", edits.changed_speed_limits.len()), format!( "{} intersections changed", edits.original_intersections.len() diff --git a/game/src/sandbox/gameplay/mod.rs b/game/src/sandbox/gameplay/mod.rs index 9c2c8b2495..e913ceb638 100644 --- a/game/src/sandbox/gameplay/mod.rs +++ b/game/src/sandbox/gameplay/mod.rs @@ -170,7 +170,9 @@ impl GameplayMode { pub fn allows(&self, edits: &MapEdits) -> bool { for cmd in &edits.commands { match cmd { - EditCmd::ChangeLaneType { .. } | EditCmd::ReverseLane { .. } => { + EditCmd::ChangeLaneType { .. } + | EditCmd::ReverseLane { .. } + | EditCmd::ChangeSpeedLimit { .. } => { if !self.can_edit_lanes() { return false; } diff --git a/map_model/src/edits.rs b/map_model/src/edits.rs index b792f081e3..2dfbad104d 100644 --- a/map_model/src/edits.rs +++ b/map_model/src/edits.rs @@ -2,7 +2,8 @@ use crate::raw::{OriginalIntersection, OriginalRoad}; use crate::{ ControlStopSign, ControlTrafficSignal, IntersectionID, LaneID, LaneType, Map, RoadID, TurnID, }; -use abstutil::{deserialize_btreemap, retain_btreemap, serialize_btreemap, Timer}; +use abstutil::{deserialize_btreemap, retain_btreemap, retain_btreeset, serialize_btreemap, Timer}; +use geom::Speed; use serde_derive::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet}; @@ -15,6 +16,7 @@ pub struct MapEdits { pub original_lts: BTreeMap, pub reversed_lanes: BTreeSet, pub original_intersections: BTreeMap, + pub changed_speed_limits: BTreeSet, // Edits without these are player generated. pub proposal_description: Vec, @@ -39,6 +41,11 @@ pub enum EditCmd { // New intended dst_i dst_i: IntersectionID, }, + ChangeSpeedLimit { + id: RoadID, + new: Speed, + old: Speed, + }, ChangeIntersection { i: IntersectionID, new: EditIntersection, @@ -64,6 +71,7 @@ impl MapEdits { original_lts: BTreeMap::new(), reversed_lanes: BTreeSet::new(), original_intersections: BTreeMap::new(), + changed_speed_limits: BTreeSet::new(), } } @@ -87,11 +95,11 @@ impl MapEdits { ); } - // Original lane types, reversed lanes, and all changed intersections pub(crate) fn update_derived(&mut self, map: &Map) { let mut orig_lts = BTreeMap::new(); let mut reversed_lanes = BTreeSet::new(); let mut orig_intersections: BTreeMap = BTreeMap::new(); + let mut changed_speed_limits = BTreeSet::new(); for cmd in &self.commands { match cmd { @@ -107,6 +115,9 @@ impl MapEdits { reversed_lanes.insert(*l); } } + EditCmd::ChangeSpeedLimit { id, .. } => { + changed_speed_limits.insert(*id); + } EditCmd::ChangeIntersection { i, ref old, .. } => { if !orig_intersections.contains_key(i) { orig_intersections.insert(*i, old.clone()); @@ -119,10 +130,14 @@ impl MapEdits { retain_btreemap(&mut orig_intersections, |i, orig| { map.get_i_edit(*i) != orig.clone() }); + retain_btreeset(&mut changed_speed_limits, |r| { + map.get_r(*r).speed_limit != map.get_r(*r).speed_limit_from_osm() + }); self.original_lts = orig_lts; self.reversed_lanes = reversed_lanes; self.original_intersections = orig_intersections; + self.changed_speed_limits = changed_speed_limits; } // Assumes update_derived has been called. @@ -147,6 +162,13 @@ impl MapEdits { new: map.get_i_edit(*i), }); } + for r in &self.changed_speed_limits { + self.commands.push(EditCmd::ChangeSpeedLimit { + id: *r, + new: map.get_r(*r).speed_limit, + old: map.get_r(*r).speed_limit_from_osm(), + }); + } } } @@ -214,6 +236,11 @@ enum PermanentEditCmd { // New intended dst_i dst_i: OriginalIntersection, }, + ChangeSpeedLimit { + id: OriginalRoad, + new: Speed, + old: Speed, + }, ChangeIntersection { i: OriginalIntersection, new: PermanentEditIntersection, @@ -241,6 +268,13 @@ impl PermanentMapEdits { l: OriginalLane::to_permanent(*l, map), dst_i: map.get_i(*dst_i).orig_id, }, + EditCmd::ChangeSpeedLimit { id, new, old } => { + PermanentEditCmd::ChangeSpeedLimit { + id: map.get_r(*id).orig_id, + new: *new, + old: *old, + } + } EditCmd::ChangeIntersection { i, new, old } => { PermanentEditCmd::ChangeIntersection { i: map.get_i(*i).orig_id, @@ -273,6 +307,13 @@ impl PermanentMapEdits { let dst_i = map.find_i_by_osm_id(dst_i.osm_node_id)?; Ok(EditCmd::ReverseLane { l, dst_i }) } + PermanentEditCmd::ChangeSpeedLimit { id, new, old } => { + let id = map.find_r_by_osm_id( + id.osm_way_id, + (id.i1.osm_node_id, id.i2.osm_node_id), + )?; + Ok(EditCmd::ChangeSpeedLimit { id, new, old }) + } PermanentEditCmd::ChangeIntersection { i, new, old } => { let id = map.find_i_by_osm_id(i.osm_node_id)?; Ok(EditCmd::ChangeIntersection { @@ -291,6 +332,7 @@ impl PermanentMapEdits { original_lts: BTreeMap::new(), reversed_lanes: BTreeSet::new(), original_intersections: BTreeMap::new(), + changed_speed_limits: BTreeSet::new(), }; edits.update_derived(map); Ok(edits) @@ -322,7 +364,8 @@ impl PermanentEditIntersection { let mut translated_must_stop = BTreeMap::new(); for (r, stop) in must_stop { translated_must_stop.insert( - map.find_r_by_osm_id(r.osm_way_id, (r.i1.osm_node_id, r.i2.osm_node_id))?, + map.find_r_by_osm_id(r.osm_way_id, (r.i1.osm_node_id, r.i2.osm_node_id)) + .ok()?, stop, ); } @@ -360,13 +403,10 @@ impl OriginalLane { } fn from_permanent(self, map: &Map) -> Result { - let r = map.get_r( - map.find_r_by_osm_id( - self.parent.osm_way_id, - (self.parent.i1.osm_node_id, self.parent.i2.osm_node_id), - ) - .ok_or_else(|| format!("can't find {:?}", self))?, - ); + let r = map.get_r(map.find_r_by_osm_id( + self.parent.osm_way_id, + (self.parent.i1.osm_node_id, self.parent.i2.osm_node_id), + )?); if r.children_forwards.len() != self.num_fwd || r.children_backwards.len() != self.num_back { return Err(format!("number of lanes has changed in {:?}", self)); diff --git a/map_model/src/map.rs b/map_model/src/map.rs index cae11ea9a2..b0dd2f119f 100644 --- a/map_model/src/map.rs +++ b/map_model/src/map.rs @@ -8,7 +8,7 @@ use crate::{ NORMAL_LANE_THICKNESS, SIDEWALK_THICKNESS, }; use abstutil::{deserialize_btreemap, serialize_btreemap, Error, Timer, Warn}; -use geom::{Angle, Bounds, Distance, GPSBounds, Line, PolyLine, Polygon, Pt2D}; +use geom::{Angle, Bounds, Distance, GPSBounds, Line, PolyLine, Polygon, Pt2D, Speed}; use serde_derive::{Deserialize, Serialize}; use std::collections::{BTreeMap, BTreeSet, HashSet, VecDeque}; @@ -643,16 +643,23 @@ impl Map { None } - pub fn find_r_by_osm_id(&self, osm_way_id: i64, osm_node_ids: (i64, i64)) -> Option { + pub fn find_r_by_osm_id( + &self, + osm_way_id: i64, + osm_node_ids: (i64, i64), + ) -> Result { for r in self.all_roads() { if r.orig_id.osm_way_id == osm_way_id && r.orig_id.i1.osm_node_id == osm_node_ids.0 && r.orig_id.i2.osm_node_id == osm_node_ids.1 { - return Some(r.id); + return Ok(r.id); } } - None + Err(format!( + "Can't find osm_way_id {} between nodes {} and {}", + osm_way_id, osm_node_ids.0, osm_node_ids.1 + )) } pub fn find_i_by_osm_id(&self, osm_node_id: i64) -> Result { @@ -933,7 +940,9 @@ fn make_half_map( center_pts: r.trimmed_center_pts.clone(), src_i: i1, dst_i: i2, + speed_limit: Speed::ZERO, }; + road.speed_limit = road.speed_limit_from_osm(); for lane in &r.lane_specs { let id = LaneID(map.lanes.len()); @@ -1150,6 +1159,15 @@ impl EditCmd { recalculate_turns(dst_i, map, effects, timer); true } + EditCmd::ChangeSpeedLimit { id, new, .. } => { + if map.roads[id.0].speed_limit != *new { + map.roads[id.0].speed_limit = *new; + effects.changed_roads.insert(*id); + true + } else { + false + } + } EditCmd::ChangeIntersection { i, ref new, @@ -1206,6 +1224,15 @@ impl EditCmd { } .apply(effects, map, timer) } + EditCmd::ChangeSpeedLimit { id, old, .. } => { + if map.roads[id.0].speed_limit != *old { + map.roads[id.0].speed_limit = *old; + effects.changed_roads.insert(*id); + true + } else { + false + } + } EditCmd::ChangeIntersection { i, ref old, diff --git a/map_model/src/pathfind/driving.rs b/map_model/src/pathfind/driving.rs index b7aee583ca..ad0976a5bd 100644 --- a/map_model/src/pathfind/driving.rs +++ b/map_model/src/pathfind/driving.rs @@ -128,8 +128,8 @@ pub fn cost(lane: &Lane, turn: &Turn, constraints: PathConstraints, map: &Map) - match constraints { PathConstraints::Car => { // Prefer slightly longer route on faster roads - let t1 = lane.length() / map.get_r(lane.parent).get_speed_limit(); - let t2 = turn.geom.length() / map.get_parent(turn.id.dst).get_speed_limit(); + let t1 = lane.length() / map.get_r(lane.parent).speed_limit; + let t2 = turn.geom.length() / map.get_parent(turn.id.dst).speed_limit; (t1 + t2).inner_seconds().round() as usize } PathConstraints::Bike => { @@ -155,8 +155,8 @@ pub fn cost(lane: &Lane, turn: &Turn, constraints: PathConstraints, map: &Map) - } PathConstraints::Bus => { // Like Car, but prefer bus lanes. - let t1 = lane.length() / map.get_r(lane.parent).get_speed_limit(); - let t2 = turn.geom.length() / map.get_parent(turn.id.dst).get_speed_limit(); + let t1 = lane.length() / map.get_r(lane.parent).speed_limit; + let t2 = turn.geom.length() / map.get_parent(turn.id.dst).speed_limit; let lt_penalty = if lane.is_bus() { 1.0 } else { diff --git a/map_model/src/road.rs b/map_model/src/road.rs index 81229e65c0..907cf390e4 100644 --- a/map_model/src/road.rs +++ b/map_model/src/road.rs @@ -97,6 +97,7 @@ pub struct Road { // self is 'from'. (via, to). Only BanTurns. pub complicated_turn_restrictions: Vec<(RoadID, RoadID)>, pub orig_id: OriginalRoad, + pub speed_limit: Speed, // Invariant: A road must contain at least one child // These are ordered from closest to center lane (left-most when driving on the right) to @@ -205,8 +206,7 @@ impl Road { } } - pub fn get_speed_limit(&self) -> Speed { - // TODO Should probably cache this + pub(crate) fn speed_limit_from_osm(&self) -> Speed { if let Some(limit) = self.osm_tags.get(osm::MAXSPEED) { // TODO handle other units if limit.ends_with(" mph") { diff --git a/map_model/src/traffic_signals.rs b/map_model/src/traffic_signals.rs index c9bec1ff29..61327bad62 100644 --- a/map_model/src/traffic_signals.rs +++ b/map_model/src/traffic_signals.rs @@ -734,7 +734,9 @@ fn import_turn_group(id: seattle_traffic_signals::Turn, map: &Map) -> Option Option { Some(DirectedRoadID { - id: map.find_r_by_osm_id(id.osm_way_id, (id.osm_node1, id.osm_node2))?, + id: map + .find_r_by_osm_id(id.osm_way_id, (id.osm_node1, id.osm_node2)) + .ok()?, forwards: id.is_forwards, }) } diff --git a/map_model/src/traversable.rs b/map_model/src/traversable.rs index 3f0bfa97b3..7a5179c804 100644 --- a/map_model/src/traversable.rs +++ b/map_model/src/traversable.rs @@ -167,8 +167,8 @@ impl Traversable { pub fn speed_limit(&self, map: &Map) -> Speed { match *self { - Traversable::Lane(id) => map.get_parent(id).get_speed_limit(), - Traversable::Turn(id) => map.get_parent(id.dst).get_speed_limit(), + Traversable::Lane(id) => map.get_parent(id).speed_limit, + Traversable::Turn(id) => map.get_parent(id.dst).speed_limit, } }