From eee5981c14455f1cd114a9374bc46a869081197d Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Tue, 17 Sep 2019 13:11:08 -0700 Subject: [PATCH] stable IDs for buildings in raw layer too. stop futzing with ID problems there. --- convert_osm/src/clip.rs | 2 +- convert_osm/src/lib.rs | 20 ++++++------ convert_osm/src/osm.rs | 16 +++++---- map_model/src/make/buildings.rs | 26 +++++++-------- map_model/src/raw_data.rs | 12 +++++-- synthetic/src/main.rs | 8 ++--- synthetic/src/model.rs | 58 +++++++++++++++++++-------------- 7 files changed, 82 insertions(+), 60 deletions(-) diff --git a/convert_osm/src/clip.rs b/convert_osm/src/clip.rs index 5c422d45c0..bf86b7cce9 100644 --- a/convert_osm/src/clip.rs +++ b/convert_osm/src/clip.rs @@ -92,7 +92,7 @@ pub fn clip_map(map: &mut raw_data::Map, timer: &mut Timer) { } } - map.buildings.retain(|b| { + retain_btreemap(&mut map.buildings, |_, b| { b.polygon .points() .iter() diff --git a/convert_osm/src/lib.rs b/convert_osm/src/lib.rs index e33048ce18..a1c3606db3 100644 --- a/convert_osm/src/lib.rs +++ b/convert_osm/src/lib.rs @@ -211,32 +211,32 @@ fn use_offstreet_parking(map: &mut raw_data::Map, path: &str, timer: &mut Timer) timer.start("match offstreet parking points"); let shapes = kml::load(path, &map.gps_bounds, timer).expect("loading offstreet_parking failed"); - // Building indices - let mut closest: FindClosest = FindClosest::new(&map.gps_bounds.to_bounds()); - for (idx, b) in map.buildings.iter().enumerate() { - closest.add(idx, b.polygon.points()); + let mut closest: FindClosest = + FindClosest::new(&map.gps_bounds.to_bounds()); + for (id, b) in &map.buildings { + closest.add(*id, b.polygon.points()); } // TODO Another function just to use ?. Try blocks would rock. let mut handle_shape: Box Option<()>> = Box::new(|s| { assert_eq!(s.points.len(), 1); let pt = Pt2D::from_gps(s.points[0], &map.gps_bounds)?; - let (idx, _) = closest.closest_pt(pt, Distance::meters(50.0))?; + let (id, _) = closest.closest_pt(pt, Distance::meters(50.0))?; // TODO Handle parking lots. - if !map.buildings[idx].polygon.contains_pt(pt) { + if !map.buildings[&id].polygon.contains_pt(pt) { return None; } let name = s.attributes.get("DEA_FACILITY_NAME")?.to_string(); let num_stalls = s.attributes.get("DEA_STALLS")?.parse::().ok()?; // TODO Update the existing one instead - if let Some(ref existing) = map.buildings[idx].parking { + if let Some(ref existing) = map.buildings[&id].parking { // TODO Can't use timer inside this closure println!( - "Two offstreet parking hints apply to building {}: {} @ {}, and {} @ {}", - idx, existing.num_stalls, existing.name, num_stalls, name + "Two offstreet parking hints apply to {}: {} @ {}, and {} @ {}", + id, existing.num_stalls, existing.name, num_stalls, name ); } - map.buildings[idx].parking = Some(OffstreetParking { + map.buildings.get_mut(&id).unwrap().parking = Some(OffstreetParking { name, num_stalls, // Temporary values, populate later diff --git a/convert_osm/src/osm.rs b/convert_osm/src/osm.rs index 380cc2c6c8..9c6398781f 100644 --- a/convert_osm/src/osm.rs +++ b/convert_osm/src/osm.rs @@ -97,12 +97,16 @@ pub fn extract_osm( if deduped.len() < 3 { continue; } - map.buildings.push(raw_data::Building { - osm_way_id: way.id, - polygon: Polygon::new(&deduped), - osm_tags: tags, - parking: None, - }); + let id = raw_data::StableBuildingID(map.buildings.len()); + map.buildings.insert( + id, + raw_data::Building { + osm_way_id: way.id, + polygon: Polygon::new(&deduped), + osm_tags: tags, + parking: None, + }, + ); } else if let Some(at) = get_area_type(&tags) { if pts.len() < 3 { continue; diff --git a/map_model/src/make/buildings.rs b/map_model/src/make/buildings.rs index 02da2c774b..11a7b3b18b 100644 --- a/map_model/src/make/buildings.rs +++ b/map_model/src/make/buildings.rs @@ -2,26 +2,26 @@ use crate::make::sidewalk_finder::find_sidewalk_points; use crate::{raw_data, Building, BuildingID, FrontPath, Lane, LaneID, Position, Road}; use abstutil::Timer; use geom::{Bounds, Distance, FindClosest, HashablePt2D, Line, Polygon}; -use std::collections::HashSet; +use std::collections::{BTreeMap, HashSet}; pub fn make_all_buildings( results: &mut Vec, - input: &Vec, + input: &BTreeMap, bounds: &Bounds, lanes: &Vec, roads: &Vec, timer: &mut Timer, ) { timer.start("convert buildings"); - let mut center_per_bldg: Vec = Vec::new(); + let mut center_per_bldg: BTreeMap = BTreeMap::new(); let mut query: HashSet = HashSet::new(); timer.start_iter("get building center points", input.len()); - for b in input { + for (id, b) in input { timer.next(); // TODO Use the polylabel? Want to have visually distinct lines for front path and // driveway; using two different "centers" is a lazy way for now. let center = b.polygon.center().to_hashable(); - center_per_bldg.push(center); + center_per_bldg.insert(*id, center); query.insert(center); } @@ -45,7 +45,7 @@ pub fn make_all_buildings( let sidewalk_pts = find_sidewalk_points(bounds, query, lanes, Distance::meters(100.0), timer); timer.start_iter("create building front paths", center_per_bldg.len()); - for (idx, bldg_center) in center_per_bldg.into_iter().enumerate() { + for (stable_id, bldg_center) in center_per_bldg { timer.next(); if let Some(sidewalk_pos) = sidewalk_pts.get(&bldg_center) { let sidewalk_pt = lanes[sidewalk_pos.lane().0] @@ -55,21 +55,21 @@ pub fn make_all_buildings( timer.warn("Skipping a building because front path has 0 length".to_string()); continue; } - let polygon = &input[idx].polygon; - let line = trim_path(polygon, Line::new(bldg_center.to_pt2d(), sidewalk_pt)); + let b = &input[&stable_id]; + let line = trim_path(&b.polygon, Line::new(bldg_center.to_pt2d(), sidewalk_pt)); let id = BuildingID(results.len()); let mut bldg = Building { id, - polygon: polygon.clone(), - osm_tags: input[idx].osm_tags.clone(), - osm_way_id: input[idx].osm_way_id, + polygon: b.polygon.clone(), + osm_tags: b.osm_tags.clone(), + osm_way_id: b.osm_way_id, front_path: FrontPath { sidewalk: *sidewalk_pos, line, }, - parking: input[idx].parking.clone(), - label_center: polygon.polylabel(), + parking: b.parking.clone(), + label_center: b.polygon.polylabel(), }; // Make a driveway from the parking icon to the nearest road. diff --git a/map_model/src/raw_data.rs b/map_model/src/raw_data.rs index 9c3072133e..f3d12e4a70 100644 --- a/map_model/src/raw_data.rs +++ b/map_model/src/raw_data.rs @@ -25,12 +25,20 @@ impl fmt::Display for StableIntersectionID { } } +#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, PartialOrd, Ord, Clone, Copy, Hash)] +pub struct StableBuildingID(pub usize); +impl fmt::Display for StableBuildingID { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "StableBuildingID({0})", self.0) + } +} + #[derive(Debug, Serialize, Deserialize)] pub struct Map { pub name: String, pub roads: BTreeMap, pub intersections: BTreeMap, - pub buildings: Vec, + pub buildings: BTreeMap, pub bus_routes: Vec, pub areas: Vec, // from OSM way => [(restriction, to OSM way)] @@ -46,7 +54,7 @@ impl Map { name, roads: BTreeMap::new(), intersections: BTreeMap::new(), - buildings: Vec::new(), + buildings: BTreeMap::new(), bus_routes: Vec::new(), areas: Vec::new(), turn_restrictions: BTreeMap::new(), diff --git a/synthetic/src/main.rs b/synthetic/src/main.rs index e35ca1ec4d..e9d0ccc9e8 100644 --- a/synthetic/src/main.rs +++ b/synthetic/src/main.rs @@ -2,8 +2,8 @@ mod model; use ezgui::{Color, EventCtx, EventLoopMode, GfxCtx, Key, Line, Text, Wizard, GUI}; use geom::{Distance, Line, Polygon, Pt2D}; -use map_model::raw_data::{StableIntersectionID, StableRoadID}; -use model::{BuildingID, Direction, Model, ID}; +use map_model::raw_data::{StableBuildingID, StableIntersectionID, StableRoadID}; +use model::{Direction, Model, ID}; use std::{env, process}; struct UI { @@ -15,8 +15,8 @@ struct UI { enum State { Viewing, MovingIntersection(StableIntersectionID), - MovingBuilding(BuildingID), - LabelingBuilding(BuildingID, Wizard), + MovingBuilding(StableBuildingID), + LabelingBuilding(StableBuildingID, Wizard), LabelingRoad((StableRoadID, Direction), Wizard), LabelingIntersection(StableIntersectionID, Wizard), CreatingRoad(StableIntersectionID), diff --git a/synthetic/src/model.rs b/synthetic/src/model.rs index 951047ccab..30405e148c 100644 --- a/synthetic/src/model.rs +++ b/synthetic/src/model.rs @@ -2,7 +2,7 @@ use abstutil::{read_binary, MultiMap, Timer}; use ezgui::world::{Object, ObjectID, World}; use ezgui::{Color, EventCtx, GfxCtx, Line, Prerender, Text}; use geom::{Bounds, Circle, Distance, PolyLine, Polygon, Pt2D}; -use map_model::raw_data::{MapFix, MapFixes, StableIntersectionID, StableRoadID}; +use map_model::raw_data::{MapFix, MapFixes, StableBuildingID, StableIntersectionID, StableRoadID}; use map_model::{raw_data, IntersectionType, LaneType, RoadSpec, LANE_THICKNESS}; use std::collections::BTreeMap; use std::mem; @@ -13,7 +13,6 @@ const CENTER_LINE_THICKNESS: Distance = Distance::const_meters(0.5); const SYNTHETIC_OSM_WAY_ID: i64 = -1; -pub type BuildingID = usize; pub type Direction = bool; const FORWARDS: Direction = true; const BACKWARDS: Direction = false; @@ -52,6 +51,10 @@ impl Model { model.map = read_binary(path, &mut timer).unwrap(); model.map.apply_fixes(&model.fixes, &mut timer); + for id in model.map.buildings.keys() { + model.id_counter = model.id_counter.max(id.0 + 1); + } + for id in model.map.intersections.keys() { model.id_counter = model.id_counter.max(id.0 + 1); } @@ -65,7 +68,7 @@ impl Model { model.world = World::new(&model.compute_bounds()); if !model.exclude_bldgs { - for id in 0..model.map.buildings.len() { + for id in model.map.buildings.keys().cloned().collect::>() { model.bldg_added(id, prerender); } } @@ -114,7 +117,7 @@ impl Model { fn compute_bounds(&self) -> Bounds { let mut bounds = Bounds::new(); - for b in &self.map.buildings { + for b in self.map.buildings.values() { for pt in b.polygon.points() { bounds.update(*pt); } @@ -132,8 +135,8 @@ impl Model { pub fn delete_everything_inside(&mut self, area: Polygon) { if !self.exclude_bldgs { - for id in 0..self.map.buildings.len() { - if area.contains_pt(self.map.buildings[id].polygon.center()) { + for id in self.map.buildings.keys().cloned().collect::>() { + if area.contains_pt(self.map.buildings[&id].polygon.center()) { self.delete_b(id); } } @@ -496,8 +499,8 @@ impl Model { } impl Model { - fn bldg_added(&mut self, id: BuildingID, prerender: &Prerender) { - let b = &self.map.buildings[id]; + fn bldg_added(&mut self, id: StableBuildingID, prerender: &Prerender) { + let b = &self.map.buildings[&id]; self.world.add( prerender, Object::new(ID::Building(id), Color::BLUE, b.polygon.clone()) @@ -506,21 +509,25 @@ impl Model { } pub fn create_b(&mut self, center: Pt2D, prerender: &Prerender) { - let id = self.map.buildings.len(); - self.map.buildings.push(raw_data::Building { - polygon: Polygon::rectangle(center, BUILDING_LENGTH, BUILDING_LENGTH), - osm_tags: BTreeMap::new(), - osm_way_id: SYNTHETIC_OSM_WAY_ID, - parking: None, - }); + let id = StableBuildingID(self.id_counter); + self.id_counter += 1; + self.map.buildings.insert( + id, + raw_data::Building { + polygon: Polygon::rectangle(center, BUILDING_LENGTH, BUILDING_LENGTH), + osm_tags: BTreeMap::new(), + osm_way_id: SYNTHETIC_OSM_WAY_ID, + parking: None, + }, + ); self.bldg_added(id, prerender); } - pub fn move_b(&mut self, id: BuildingID, new_center: Pt2D, prerender: &Prerender) { + pub fn move_b(&mut self, id: StableBuildingID, new_center: Pt2D, prerender: &Prerender) { self.world.delete(ID::Building(id)); - let b = &mut self.map.buildings[id]; + let b = self.map.buildings.get_mut(&id).unwrap(); let old_center = b.polygon.center(); b.polygon = b.polygon.translate( Distance::meters(new_center.x() - old_center.x()), @@ -530,30 +537,33 @@ impl Model { self.bldg_added(id, prerender); } - pub fn set_b_label(&mut self, id: BuildingID, label: String, prerender: &Prerender) { + pub fn set_b_label(&mut self, id: StableBuildingID, label: String, prerender: &Prerender) { self.world.delete(ID::Building(id)); - self.map.buildings[id] + self.map + .buildings + .get_mut(&id) + .unwrap() .osm_tags .insert("abst:label".to_string(), label); self.bldg_added(id, prerender); } - pub fn get_b_label(&self, id: BuildingID) -> Option { - self.map.buildings[id].osm_tags.get("abst:label").cloned() + pub fn get_b_label(&self, id: StableBuildingID) -> Option { + self.map.buildings[&id].osm_tags.get("abst:label").cloned() } - pub fn delete_b(&mut self, id: BuildingID) { + pub fn delete_b(&mut self, id: StableBuildingID) { self.world.delete(ID::Building(id)); - self.map.buildings.remove(id); + self.map.buildings.remove(&id); } } #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub enum ID { - Building(BuildingID), + Building(StableBuildingID), Intersection(StableIntersectionID), Lane(StableRoadID, Direction, usize), }