WIP removing disconnected chunks of map. leaves orphaned intersections

still, so disabled.
This commit is contained in:
Dustin Carlino 2018-07-21 09:24:44 -07:00
parent 7d1ec62a1d
commit 1c85188a64
11 changed files with 138 additions and 11 deletions

View File

@ -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)
}
}

View File

@ -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!(

View 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(&current) {
continue;
}
unvisited_roads.remove(&current);
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());
}

View File

@ -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)
}

View File

@ -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?

View File

@ -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 = "*"

View File

@ -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;

View File

@ -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)
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -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,