mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 17:37:22 +03:00
WIP removing disconnected chunks of map. leaves orphaned intersections
still, so disabled.
This commit is contained in:
parent
7d1ec62a1d
commit
1c85188a64
@ -4,6 +4,7 @@ extern crate serde_json;
|
||||
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs::File;
|
||||
use std::io::{Error, ErrorKind, Read, Write};
|
||||
|
||||
@ -31,3 +32,39 @@ pub fn read_binary<T: DeserializeOwned>(path: &str) -> Result<T, Error> {
|
||||
let obj: T = serde_cbor::from_reader(file).map_err(|err| Error::new(ErrorKind::Other, err))?;
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
pub struct MultiMap<K, V> {
|
||||
map: HashMap<K, HashSet<V>>,
|
||||
empty: HashSet<V>,
|
||||
}
|
||||
|
||||
impl<K, V> MultiMap<K, V>
|
||||
where
|
||||
K: std::cmp::Eq + std::hash::Hash,
|
||||
V: std::cmp::Eq + std::hash::Hash,
|
||||
{
|
||||
pub fn new() -> MultiMap<K, V> {
|
||||
MultiMap {
|
||||
map: HashMap::new(),
|
||||
empty: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, key: K, value: V) {
|
||||
self.map.entry(key).or_insert(HashSet::new()).insert(value);
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, key: K, value: V) {
|
||||
if !self.map.contains_key(&key) {
|
||||
return;
|
||||
}
|
||||
self.map.get_mut(&key).unwrap().remove(&value);
|
||||
if self.map[&key].is_empty() {
|
||||
self.map.remove(&key);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, key: K) -> &HashSet<V> {
|
||||
self.map.get(&key).unwrap_or(&self.empty)
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ extern crate shp;
|
||||
extern crate structopt;
|
||||
|
||||
mod osm;
|
||||
mod remove_disconnected;
|
||||
mod split_ways;
|
||||
mod srtm;
|
||||
mod traffic_signals;
|
||||
@ -53,6 +54,10 @@ fn main() {
|
||||
let elevation = Elevation::new(&flags.elevation).expect("loading .hgt failed");
|
||||
let (map, bounds) = osm::osm_to_raw_roads(&flags.osm);
|
||||
let mut map = split_ways::split_up_roads(&map, &elevation);
|
||||
// TODO get bounds here instead
|
||||
remove_disconnected::remove_disconnected_roads(&mut map);
|
||||
|
||||
println!("Loading parcels from {}", flags.parcels);
|
||||
let parcels_map: raw_data::Map =
|
||||
abstutil::read_binary(&flags.parcels).expect("loading parcels failed");
|
||||
println!(
|
||||
|
62
convert_osm/src/remove_disconnected.rs
Normal file
62
convert_osm/src/remove_disconnected.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use abstutil::MultiMap;
|
||||
use geom::HashablePt2D;
|
||||
use map_model::raw_data;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn remove_disconnected_roads(map: &mut raw_data::Map) {
|
||||
println!("finding disconnected chunks of road");
|
||||
// This is a simple floodfill, not Tarjan's. Assumes all roads bidirectional.
|
||||
// All the usizes are indices into the original list of roads
|
||||
|
||||
let mut next_roads: MultiMap<HashablePt2D, usize> = MultiMap::new();
|
||||
for (idx, r) in map.roads.iter().enumerate() {
|
||||
next_roads.insert(r.first_pt(), idx);
|
||||
next_roads.insert(r.last_pt(), idx);
|
||||
}
|
||||
|
||||
let mut partitions: Vec<Vec<usize>> = Vec::new();
|
||||
let mut unvisited_roads: HashSet<usize> = HashSet::new();
|
||||
for i in 0..map.roads.len() {
|
||||
unvisited_roads.insert(i);
|
||||
}
|
||||
|
||||
while !unvisited_roads.is_empty() {
|
||||
let mut queue_roads: Vec<usize> = vec![*unvisited_roads.iter().next().unwrap()];
|
||||
let mut current_partition: Vec<usize> = Vec::new();
|
||||
while !queue_roads.is_empty() {
|
||||
let current = queue_roads.pop().unwrap();
|
||||
if !unvisited_roads.contains(¤t) {
|
||||
continue;
|
||||
}
|
||||
unvisited_roads.remove(¤t);
|
||||
current_partition.push(current);
|
||||
|
||||
let current_r = &map.roads[current];
|
||||
for other_r in next_roads.get(current_r.first_pt()).iter() {
|
||||
queue_roads.push(*other_r);
|
||||
}
|
||||
for other_r in next_roads.get(current_r.last_pt()).iter() {
|
||||
queue_roads.push(*other_r);
|
||||
}
|
||||
}
|
||||
partitions.push(current_partition);
|
||||
}
|
||||
|
||||
partitions.sort_by_key(|roads| roads.len());
|
||||
partitions.reverse();
|
||||
println!("Main partition has {} roads", partitions[0].len());
|
||||
for p in partitions.iter().skip(1) {
|
||||
println!("Removing disconnected partition with {} roads", p.len());
|
||||
for idx in p {
|
||||
println!("TODO: remove road {:?}", map.roads[*idx].osm_tags);
|
||||
|
||||
// TODO this is expensive
|
||||
/*let old_r = map.roads.remove(*idx);
|
||||
next_roads.remove(old_r.first_pt(), *idx);
|
||||
next_roads.remove(old_r.last_pt(), *idx);*/ }
|
||||
}
|
||||
|
||||
// Remove intersections without any roads
|
||||
map.intersections
|
||||
.retain(|i| !next_roads.get(i.point.to_hashable()).is_empty());
|
||||
}
|
@ -11,7 +11,7 @@ pub fn split_up_roads(input: &raw_data::Map, elevation: &srtm::Elevation) -> raw
|
||||
let mut intersections: HashSet<HashablePt2D> = HashSet::new();
|
||||
for r in &input.roads {
|
||||
for (idx, raw_pt) in r.points.iter().enumerate() {
|
||||
let pt = hash_pt(raw_pt);
|
||||
let pt = raw_pt.to_hashable();
|
||||
counts_per_pt.entry(pt).or_insert(0);
|
||||
let count = counts_per_pt[&pt] + 1;
|
||||
counts_per_pt.insert(pt, count);
|
||||
@ -45,7 +45,7 @@ pub fn split_up_roads(input: &raw_data::Map, elevation: &srtm::Elevation) -> raw
|
||||
|
||||
for pt in &orig_road.points {
|
||||
r.points.push(pt.clone());
|
||||
if r.points.len() > 1 && intersections.contains(&hash_pt(pt)) {
|
||||
if r.points.len() > 1 && intersections.contains(&pt.to_hashable()) {
|
||||
// Start a new road
|
||||
map.roads.push(r.clone());
|
||||
r.points.clear();
|
||||
@ -59,7 +59,3 @@ pub fn split_up_roads(input: &raw_data::Map, elevation: &srtm::Elevation) -> raw
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
fn hash_pt(pt: &LonLat) -> HashablePt2D {
|
||||
HashablePt2D::new(pt.longitude, pt.latitude)
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
# TODO for Phase 1 (Basemap)
|
||||
|
||||
- remove disconnected parts of the map (to speed up spawning things)
|
||||
- map conversion isn't deterministic. make a test to verify
|
||||
- it's hard to relate roads and lanes. of course. :)
|
||||
|
||||
- road with many lanes
|
||||
- maybe also the time to split into different lane types? what's similar/not between them?
|
||||
- graph querying?
|
||||
|
@ -13,7 +13,6 @@ generator = "0.6"
|
||||
geo = "0.9.1"
|
||||
geom = { path = "../geom" }
|
||||
map_model = { path = "../map_model" }
|
||||
multimap = "0.4.0"
|
||||
ordered-float = "0.5.0"
|
||||
piston = "*"
|
||||
piston2d-graphics = "*"
|
||||
|
@ -12,7 +12,6 @@ extern crate geom;
|
||||
extern crate glutin_window;
|
||||
extern crate graphics;
|
||||
extern crate map_model;
|
||||
extern crate multimap;
|
||||
extern crate opengl_graphics;
|
||||
extern crate ordered_float;
|
||||
extern crate piston;
|
||||
|
@ -1,3 +1,5 @@
|
||||
use pt::HashablePt2D;
|
||||
|
||||
// longitude is x, latitude is y
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct LonLat {
|
||||
@ -30,4 +32,8 @@ impl LonLat {
|
||||
let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
|
||||
earth_radius_m * c
|
||||
}
|
||||
|
||||
pub fn to_hashable(&self) -> HashablePt2D {
|
||||
HashablePt2D::new(self.longitude, self.latitude)
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,17 @@ pub(crate) fn make_turns(i: &Intersection, m: &Map, turn_id_start: usize) -> Vec
|
||||
|
||||
// TODO: Figure out why this happens in the huge map
|
||||
if incoming.is_empty() {
|
||||
println!("WARNING: intersection {:?} has no incoming roads", i);
|
||||
println!(
|
||||
"WARNING: intersection {:?} has no incoming driving lanes",
|
||||
i
|
||||
);
|
||||
return Vec::new();
|
||||
}
|
||||
if outgoing.is_empty() {
|
||||
println!("WARNING: intersection {:?} has no outgoing roads", i);
|
||||
println!(
|
||||
"WARNING: intersection {:?} has no outgoing driving lanes",
|
||||
i
|
||||
);
|
||||
return Vec::new();
|
||||
}
|
||||
let dead_end = incoming.len() == 1 && outgoing.len() == 1;
|
||||
|
@ -121,6 +121,9 @@ impl Map {
|
||||
|
||||
for i in &m.intersections {
|
||||
make::trim_lines(&mut m.roads, i);
|
||||
if i.incoming_roads.is_empty() && i.outgoing_roads.is_empty() {
|
||||
panic!("{:?} is orphaned!", i);
|
||||
}
|
||||
}
|
||||
|
||||
for i in &m.intersections {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use geom::{Bounds, LonLat};
|
||||
use geom::{Bounds, HashablePt2D, LonLat};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@ -52,6 +52,16 @@ pub struct Road {
|
||||
pub osm_way_id: i64,
|
||||
}
|
||||
|
||||
impl Road {
|
||||
pub fn first_pt(&self) -> HashablePt2D {
|
||||
self.points[0].to_hashable()
|
||||
}
|
||||
|
||||
pub fn last_pt(&self) -> HashablePt2D {
|
||||
self.points.last().unwrap().to_hashable()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Intersection {
|
||||
pub point: LonLat,
|
||||
|
Loading…
Reference in New Issue
Block a user