mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 07:25:47 +03:00
separate files for groups of fixes
This commit is contained in:
parent
6f1ac3969e
commit
d86f4a6e89
@ -82,6 +82,10 @@ pub fn path_raw_map(map_name: &str) -> String {
|
||||
format!("../data/raw_maps/{}.bin", map_name)
|
||||
}
|
||||
|
||||
pub fn path_fixes(name: &str) -> String {
|
||||
format!("../data/fixes/{}.bin", name)
|
||||
}
|
||||
|
||||
pub fn path_camera_state(map_name: &str) -> String {
|
||||
format!("../data/camera_state/{}.json", map_name)
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ pub struct Map {
|
||||
impl Map {
|
||||
pub fn new(path: &str, timer: &mut Timer) -> Result<Map, io::Error> {
|
||||
let mut data: raw_data::Map = abstutil::read_binary(path, timer)?;
|
||||
data.apply_fixes(&raw_data::MapFixes::load(), timer);
|
||||
data.apply_fixes(&raw_data::MapFixes::load(timer), timer);
|
||||
// Do this after applying fixes, which might split off pieces of the map.
|
||||
make::remove_disconnected_roads(&mut data, timer);
|
||||
Ok(Map::create_from_raw(data, timer))
|
||||
|
@ -5,7 +5,7 @@ use abstutil::Timer;
|
||||
use geom::{Distance, GPSBounds, LonLat, Polygon, Pt2D};
|
||||
use gtfs::Route;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt;
|
||||
|
||||
// Stable IDs don't get compacted as we merge and delete things.
|
||||
@ -88,66 +88,69 @@ impl Map {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn apply_fixes(&mut self, fixes: &MapFixes, timer: &mut Timer) {
|
||||
let mut applied = 0;
|
||||
let mut skipped = 0;
|
||||
pub fn apply_fixes(&mut self, all_fixes: &BTreeMap<String, MapFixes>, timer: &mut Timer) {
|
||||
for (name, fixes) in all_fixes {
|
||||
let mut applied = 0;
|
||||
let mut skipped = 0;
|
||||
|
||||
for orig in &fixes.delete_roads {
|
||||
if let Some(r) = self.find_r(*orig) {
|
||||
self.roads.remove(&r).unwrap();
|
||||
applied += 1;
|
||||
} else {
|
||||
skipped += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for orig in &fixes.delete_intersections {
|
||||
if let Some(i) = self.find_i(*orig) {
|
||||
self.intersections.remove(&i).unwrap();
|
||||
applied += 1;
|
||||
} else {
|
||||
skipped += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for i in &fixes.add_intersections {
|
||||
if self.gps_bounds.contains(i.orig_id.point) {
|
||||
let id = StableIntersectionID(self.intersections.keys().max().unwrap().0 + 1);
|
||||
self.intersections.insert(id, i.clone());
|
||||
applied += 1;
|
||||
} else {
|
||||
skipped += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for r in &fixes.add_roads {
|
||||
match (
|
||||
self.find_i(OriginalIntersection {
|
||||
point: r.orig_id.pt1,
|
||||
}),
|
||||
self.find_i(OriginalIntersection {
|
||||
point: r.orig_id.pt2,
|
||||
}),
|
||||
) {
|
||||
(Some(i1), Some(i2)) => {
|
||||
let mut road = r.clone();
|
||||
road.i1 = i1;
|
||||
road.i2 = i2;
|
||||
let id = StableRoadID(self.roads.keys().max().unwrap().0 + 1);
|
||||
self.roads.insert(id, road);
|
||||
for orig in &fixes.delete_roads {
|
||||
if let Some(r) = self.find_r(*orig) {
|
||||
self.roads.remove(&r).unwrap();
|
||||
applied += 1;
|
||||
}
|
||||
_ => {
|
||||
} else {
|
||||
skipped += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timer.note(format!(
|
||||
"Applied {} of {} fixes ",
|
||||
applied,
|
||||
applied + skipped
|
||||
));
|
||||
for orig in &fixes.delete_intersections {
|
||||
if let Some(i) = self.find_i(*orig) {
|
||||
self.intersections.remove(&i).unwrap();
|
||||
applied += 1;
|
||||
} else {
|
||||
skipped += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for i in &fixes.add_intersections {
|
||||
if self.gps_bounds.contains(i.orig_id.point) {
|
||||
let id = StableIntersectionID(self.intersections.keys().max().unwrap().0 + 1);
|
||||
self.intersections.insert(id, i.clone());
|
||||
applied += 1;
|
||||
} else {
|
||||
skipped += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for r in &fixes.add_roads {
|
||||
match (
|
||||
self.find_i(OriginalIntersection {
|
||||
point: r.orig_id.pt1,
|
||||
}),
|
||||
self.find_i(OriginalIntersection {
|
||||
point: r.orig_id.pt2,
|
||||
}),
|
||||
) {
|
||||
(Some(i1), Some(i2)) => {
|
||||
let mut road = r.clone();
|
||||
road.i1 = i1;
|
||||
road.i2 = i2;
|
||||
let id = StableRoadID(self.roads.keys().max().unwrap().0 + 1);
|
||||
self.roads.insert(id, road);
|
||||
applied += 1;
|
||||
}
|
||||
_ => {
|
||||
skipped += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
timer.note(format!(
|
||||
"Applied {} of {} fixes for {}",
|
||||
applied,
|
||||
applied + skipped,
|
||||
name
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,16 +297,51 @@ pub struct MapFixes {
|
||||
}
|
||||
|
||||
impl MapFixes {
|
||||
pub fn load() -> MapFixes {
|
||||
if let Ok(f) = abstutil::read_json::<MapFixes>("../data/fixes.json") {
|
||||
f
|
||||
} else {
|
||||
MapFixes {
|
||||
delete_roads: Vec::new(),
|
||||
delete_intersections: Vec::new(),
|
||||
add_intersections: Vec::new(),
|
||||
add_roads: Vec::new(),
|
||||
// The fixes should be applicable in any order, theoretically...
|
||||
pub fn load(timer: &mut Timer) -> BTreeMap<String, MapFixes> {
|
||||
// Make sure different groups of fixes don't conflict.
|
||||
let mut seen_roads = BTreeSet::new();
|
||||
let mut seen_intersections = BTreeSet::new();
|
||||
|
||||
let mut results = BTreeMap::new();
|
||||
for name in abstutil::list_all_objects("fixes", "") {
|
||||
let fixes: MapFixes =
|
||||
abstutil::read_binary(&abstutil::path_fixes(&name), timer).unwrap();
|
||||
let (new_roads, new_intersections) = fixes.all_touched_ids();
|
||||
if !seen_roads.is_disjoint(&new_roads) {
|
||||
// The error could be much better (which road and other MapFixes), but since we
|
||||
// guard against this happening in the first place, don't bother.
|
||||
panic!(
|
||||
"{} MapFixes and some other MapFixes both touch the same road!",
|
||||
name
|
||||
);
|
||||
}
|
||||
seen_roads.extend(new_roads);
|
||||
if !seen_intersections.is_disjoint(&new_intersections) {
|
||||
panic!(
|
||||
"{} MapFixes and some other MapFixes both touch the same intersection!",
|
||||
name
|
||||
);
|
||||
}
|
||||
seen_intersections.extend(new_intersections);
|
||||
|
||||
results.insert(name, fixes);
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
pub fn all_touched_ids(&self) -> (BTreeSet<OriginalRoad>, BTreeSet<OriginalIntersection>) {
|
||||
let mut roads: BTreeSet<OriginalRoad> = self.delete_roads.iter().cloned().collect();
|
||||
for r in &self.add_roads {
|
||||
roads.insert(r.orig_id);
|
||||
}
|
||||
|
||||
let mut intersections: BTreeSet<OriginalIntersection> =
|
||||
self.delete_intersections.iter().cloned().collect();
|
||||
for i in &self.add_intersections {
|
||||
intersections.insert(i.orig_id);
|
||||
}
|
||||
|
||||
(roads, intersections)
|
||||
}
|
||||
}
|
||||
|
@ -27,9 +27,14 @@ enum State {
|
||||
}
|
||||
|
||||
impl UI {
|
||||
fn new(load: Option<&String>, exclude_bldgs: bool, ctx: &EventCtx) -> UI {
|
||||
fn new(
|
||||
load: Option<&String>,
|
||||
exclude_bldgs: bool,
|
||||
edit_fixes: Option<String>,
|
||||
ctx: &EventCtx,
|
||||
) -> UI {
|
||||
let model = if let Some(path) = load {
|
||||
Model::import(path, exclude_bldgs, ctx.prerender)
|
||||
Model::import(path, exclude_bldgs, edit_fixes, ctx.prerender)
|
||||
} else {
|
||||
Model::blank()
|
||||
};
|
||||
@ -280,6 +285,14 @@ fn main() {
|
||||
UI::new(
|
||||
args.get(1),
|
||||
args.get(2) == Some(&"--nobldgs".to_string()),
|
||||
args.get(3).and_then(|s| {
|
||||
let parts: Vec<&str> = s.split('=').collect();
|
||||
if parts[0] == "--fixes" && parts.len() == 2 {
|
||||
Some(parts[1].to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
ctx,
|
||||
)
|
||||
});
|
||||
|
@ -2,9 +2,12 @@ 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::{MapFixes, StableBuildingID, StableIntersectionID, StableRoadID};
|
||||
use map_model::raw_data::{
|
||||
MapFixes, OriginalIntersection, OriginalRoad, StableBuildingID, StableIntersectionID,
|
||||
StableRoadID,
|
||||
};
|
||||
use map_model::{osm, raw_data, IntersectionType, LaneType, RoadSpec, LANE_THICKNESS};
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::mem;
|
||||
|
||||
const INTERSECTION_RADIUS: Distance = Distance::const_meters(5.0);
|
||||
@ -22,9 +25,10 @@ pub struct Model {
|
||||
roads_per_intersection: MultiMap<StableIntersectionID, StableRoadID>,
|
||||
// Never reuse IDs, and don't worry about being sequential
|
||||
id_counter: usize,
|
||||
fixes: MapFixes,
|
||||
all_fixes: BTreeMap<String, MapFixes>,
|
||||
|
||||
exclude_bldgs: bool,
|
||||
edit_fixes: Option<String>,
|
||||
world: World<ID>,
|
||||
}
|
||||
|
||||
@ -35,21 +39,41 @@ impl Model {
|
||||
map: raw_data::Map::blank(String::new()),
|
||||
roads_per_intersection: MultiMap::new(),
|
||||
id_counter: 0,
|
||||
// TODO Only if we're editing something, not if we're truly creating from scratch,
|
||||
// right?
|
||||
fixes: MapFixes::load(),
|
||||
all_fixes: BTreeMap::new(),
|
||||
|
||||
exclude_bldgs: false,
|
||||
edit_fixes: None,
|
||||
world: World::new(&Bounds::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import(path: &str, exclude_bldgs: bool, prerender: &Prerender) -> Model {
|
||||
pub fn import(
|
||||
path: &str,
|
||||
exclude_bldgs: bool,
|
||||
edit_fixes: Option<String>,
|
||||
prerender: &Prerender,
|
||||
) -> Model {
|
||||
let mut timer = Timer::new("import map");
|
||||
let mut model = Model::blank();
|
||||
model.all_fixes = MapFixes::load(&mut timer);
|
||||
model.exclude_bldgs = exclude_bldgs;
|
||||
model.edit_fixes = edit_fixes;
|
||||
model.map = read_binary(path, &mut timer).unwrap();
|
||||
model.map.apply_fixes(&model.fixes, &mut timer);
|
||||
model.map.apply_fixes(&model.all_fixes, &mut timer);
|
||||
|
||||
if let Some(ref name) = model.edit_fixes {
|
||||
if !model.all_fixes.contains_key(name) {
|
||||
model.all_fixes.insert(
|
||||
name.clone(),
|
||||
MapFixes {
|
||||
delete_roads: Vec::new(),
|
||||
delete_intersections: Vec::new(),
|
||||
add_intersections: Vec::new(),
|
||||
add_roads: Vec::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for id in model.map.buildings.keys() {
|
||||
model.id_counter = model.id_counter.max(id.0 + 1);
|
||||
@ -110,24 +134,48 @@ impl Model {
|
||||
println!("Exported {}", path);
|
||||
}
|
||||
|
||||
pub fn save_fixes(&self) {
|
||||
let mut fixes = self.fixes.clone();
|
||||
// It's easiest to just go back and detect all of these
|
||||
pub fn save_fixes(&mut self) {
|
||||
let name = if let Some(ref n) = self.edit_fixes {
|
||||
n.clone()
|
||||
} else {
|
||||
println!("Not editing any fixes, so can't save them");
|
||||
return;
|
||||
};
|
||||
let mut fixes = self.all_fixes.remove(&name).unwrap();
|
||||
|
||||
// It's easiest to just go back and detect all of the added roads and intersections. But we
|
||||
// have to avoid picking up changes from other fixes.
|
||||
// TODO Ideally fixes would have a Polygon of where they influence, and all of the polygons
|
||||
// would be disjoint. Nothing prevents fixes from being saved in the wrong group -- we're
|
||||
// just sure that a fix isn't repeated.
|
||||
let mut ignore_roads: BTreeSet<OriginalRoad> = BTreeSet::new();
|
||||
let mut ignore_intersections: BTreeSet<OriginalIntersection> = BTreeSet::new();
|
||||
for f in self.all_fixes.values() {
|
||||
let (r, i) = f.all_touched_ids();
|
||||
ignore_roads.extend(r);
|
||||
ignore_intersections.extend(i);
|
||||
}
|
||||
|
||||
fixes.add_intersections.clear();
|
||||
fixes.add_roads.clear();
|
||||
for i in self.map.intersections.values() {
|
||||
if i.synthetic {
|
||||
if i.synthetic && !ignore_intersections.contains(&i.orig_id) {
|
||||
fixes.add_intersections.push(i.clone());
|
||||
}
|
||||
}
|
||||
for r in self.map.roads.values() {
|
||||
if r.osm_tags.get(osm::SYNTHETIC) == Some(&"true".to_string()) {
|
||||
if r.osm_tags.get(osm::SYNTHETIC) == Some(&"true".to_string())
|
||||
&& !ignore_roads.contains(&r.orig_id)
|
||||
{
|
||||
fixes.add_roads.push(r.clone());
|
||||
}
|
||||
}
|
||||
|
||||
abstutil::write_json("../data/fixes.json", &fixes).unwrap();
|
||||
println!("Wrote ../data/fixes.json");
|
||||
let path = abstutil::path_fixes(&name);
|
||||
abstutil::write_binary(&path, &fixes).unwrap();
|
||||
println!("Wrote {}", path);
|
||||
|
||||
self.all_fixes.insert(name, fixes);
|
||||
}
|
||||
|
||||
fn compute_bounds(&self) -> Bounds {
|
||||
@ -284,7 +332,15 @@ impl Model {
|
||||
|
||||
self.world.delete(ID::Intersection(id));
|
||||
|
||||
self.fixes.delete_intersections.push(i.orig_id);
|
||||
if let Some(ref name) = self.edit_fixes {
|
||||
self.all_fixes
|
||||
.get_mut(name)
|
||||
.unwrap()
|
||||
.delete_intersections
|
||||
.push(i.orig_id);
|
||||
} else {
|
||||
println!("This won't be saved in any MapFixes!");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_i_center(&self, id: StableIntersectionID) -> Pt2D {
|
||||
@ -441,7 +497,15 @@ impl Model {
|
||||
self.roads_per_intersection.remove(r.i1, id);
|
||||
self.roads_per_intersection.remove(r.i2, id);
|
||||
|
||||
self.fixes.delete_roads.push(r.orig_id);
|
||||
if let Some(ref name) = self.edit_fixes {
|
||||
self.all_fixes
|
||||
.get_mut(name)
|
||||
.unwrap()
|
||||
.delete_roads
|
||||
.push(r.orig_id);
|
||||
} else {
|
||||
println!("This won't be saved in any MapFixes!");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_road_spec(&self, id: StableRoadID) -> String {
|
||||
|
Loading…
Reference in New Issue
Block a user