From 82902199a2af14eedd13d374281c11096eea90dd Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Mon, 6 Aug 2018 12:58:49 -0700 Subject: [PATCH] moving parcel grouping into convert_osm, which is run with --release anyway --- convert_osm/Cargo.toml | 1 + convert_osm/src/group_parcels.rs | 97 ++++++++++++++++++++++++++++++++ convert_osm/src/lib.rs | 3 + kml/src/kml.rs | 5 +- map_model/src/geometry.rs | 17 ------ map_model/src/make/mod.rs | 2 - map_model/src/make/parcels.rs | 64 --------------------- map_model/src/map.rs | 3 +- map_model/src/raw_data.rs | 1 + 9 files changed, 107 insertions(+), 86 deletions(-) create mode 100644 convert_osm/src/group_parcels.rs delete mode 100644 map_model/src/make/parcels.rs diff --git a/convert_osm/Cargo.toml b/convert_osm/Cargo.toml index 9a862b70b0..219e75a73d 100644 --- a/convert_osm/Cargo.toml +++ b/convert_osm/Cargo.toml @@ -7,6 +7,7 @@ authors = ["Dustin Carlino "] abstutil = { path = "../abstutil" } byteorder = "1.2.1" dimensioned = { git = "https://github.com/paholg/dimensioned", rev = "0e1076ebfa5128d1ee544bdc9754c948987b6fe3", features = ["serde"] } +geo = "0.9.1" geom = { path = "../geom" } ordered-float = "0.5.0" osm-xml = "0.5.1" diff --git a/convert_osm/src/group_parcels.rs b/convert_osm/src/group_parcels.rs new file mode 100644 index 0000000000..9eb0798cc4 --- /dev/null +++ b/convert_osm/src/group_parcels.rs @@ -0,0 +1,97 @@ +use abstutil::MultiMap; +use geo; +use geom::LonLat; +use map_model::raw_data; +use std::collections::BTreeSet; + +// Slight cheat +type ParcelIdx = usize; + +pub fn group_parcels(parcels: &mut Vec) { + // First compute which parcels intersect + let mut adjacency: MultiMap = MultiMap::new(); + // TODO could use quadtree to prune + println!( + "Precomputing adjacency between {} parcels...", + parcels.len() + ); + let mut adj_counter = 0; + for (p1_idx, p1) in parcels.iter().enumerate() { + for (p2_idx, p2) in parcels.iter().enumerate() { + if p1_idx < p2_idx && polygons_intersect(&p1.points, &p2.points) { + // TODO could do something more clever later to avoid double memory + adjacency.insert(p1_idx, p2_idx); + adjacency.insert(p2_idx, p1_idx); + adj_counter += 1; + } + } + } + println!( + "{} adjacencies, now doing floodfilling to group them", + adj_counter + ); + + // Union-find might also be good inspiration. + fn floodfill(from: ParcelIdx, adj: &MultiMap) -> BTreeSet { + let mut visited: BTreeSet = BTreeSet::new(); + let mut queue: Vec = vec![from]; + while !queue.is_empty() { + let current = queue.pop().unwrap(); + if visited.contains(¤t) { + continue; + } + visited.insert(current); + for next in adj.get(current).iter() { + queue.push(*next); + } + } + visited + } + + let mut block_per_parcel: Vec> = Vec::new(); + for _ in parcels.iter() { + block_per_parcel.push(None); + } + + let mut block_counter = 0; + for base_idx in 0..parcels.len() { + // A previous iteration might have filled it out + if block_per_parcel[base_idx].is_some() { + continue; + } + + let new_block = Some(block_counter); + block_counter += 1; + for idx in floodfill(base_idx, &adjacency).iter() { + assert!(!block_per_parcel[*idx].is_some()); + block_per_parcel[*idx] = new_block; + } + } + println!( + "{} parcels grouped into {} blocks", + parcels.len(), + block_counter + ); + + for (idx, block) in block_per_parcel.iter().enumerate() { + parcels[idx].block = block.unwrap(); + } +} + +fn polygons_intersect(pts1: &Vec, pts2: &Vec) -> bool { + use geo::prelude::Intersects; + + let poly1 = geo::Polygon::new( + pts1.iter() + .map(|pt| geo::Point::new(pt.longitude, pt.latitude)) + .collect(), + Vec::new(), + ); + let poly2 = geo::Polygon::new( + pts2.iter() + .map(|pt| geo::Point::new(pt.longitude, pt.latitude)) + .collect(), + Vec::new(), + ); + poly1.intersects(&poly2) +} diff --git a/convert_osm/src/lib.rs b/convert_osm/src/lib.rs index bb307ad964..375d8e262b 100644 --- a/convert_osm/src/lib.rs +++ b/convert_osm/src/lib.rs @@ -1,6 +1,7 @@ extern crate abstutil; extern crate byteorder; extern crate dimensioned; +extern crate geo; extern crate geom; extern crate map_model; extern crate ordered_float; @@ -9,6 +10,7 @@ extern crate shp; #[macro_use] extern crate structopt; +mod group_parcels; mod osm; mod remove_disconnected; mod split_ways; @@ -69,6 +71,7 @@ pub fn convert(flags: &Flags) -> raw_data::Map { map.parcels.push(p); } } + group_parcels::group_parcels(&mut map.parcels); for coord in &traffic_signals::extract(&flags.traffic_signals).expect("loading traffic signals failed") diff --git a/kml/src/kml.rs b/kml/src/kml.rs index c719c23b4e..f0f7a6c461 100644 --- a/kml/src/kml.rs +++ b/kml/src/kml.rs @@ -36,7 +36,10 @@ pub fn load( // interpret parsing failures appropriately though... if text.contains(" ") { let mut ok = true; - let mut parcel = map_model::raw_data::Parcel { points: Vec::new() }; + let mut parcel = map_model::raw_data::Parcel { + points: Vec::new(), + block: 0, + }; for pt in text.split(" ") { if let Some((lon, lat)) = parse_pt(pt) { if b.contains(lon, lat) { diff --git a/map_model/src/geometry.rs b/map_model/src/geometry.rs index d3ef021846..500ed46f7e 100644 --- a/map_model/src/geometry.rs +++ b/map_model/src/geometry.rs @@ -1,7 +1,6 @@ // Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0 use aabb_quadtree::geom::{Point, Rect}; -use geo; use geom::{Angle, Bounds, PolyLine, Pt2D}; use graphics::math::Vec2d; use std::f64; @@ -115,19 +114,3 @@ pub fn regular_polygon(center: Pt2D, sides: usize, length: f64) -> Vec { pts.push(first_pt); pts } - -pub fn polygons_intersect(pts1: &Vec, pts2: &Vec) -> bool { - use geo::prelude::Intersects; - - let poly1 = geo::Polygon::new( - pts1.iter() - .map(|pt| geo::Point::new(pt.x(), pt.y())) - .collect(), - Vec::new()); - let poly2 = geo::Polygon::new( - pts2.iter() - .map(|pt| geo::Point::new(pt.x(), pt.y())) - .collect(), - Vec::new()); - poly1.intersects(&poly2) -} diff --git a/map_model/src/make/mod.rs b/map_model/src/make/mod.rs index 564cf08fae..20b22fbce0 100644 --- a/map_model/src/make/mod.rs +++ b/map_model/src/make/mod.rs @@ -1,6 +1,5 @@ mod buildings; mod lanes; -mod parcels; mod trim_lines; mod turns; @@ -8,4 +7,3 @@ pub(crate) use self::buildings::make_building; pub(crate) use self::lanes::get_lane_specs; pub(crate) use self::trim_lines::trim_lines; pub(crate) use self::turns::make_all_turns; -pub(crate) use self::parcels::group_parcels; diff --git a/map_model/src/make/parcels.rs b/map_model/src/make/parcels.rs deleted file mode 100644 index 1b077d0c22..0000000000 --- a/map_model/src/make/parcels.rs +++ /dev/null @@ -1,64 +0,0 @@ -use {Parcel, ParcelID, geometry}; -use std::collections::BTreeSet; -use abstutil::MultiMap; - -pub fn group_parcels(parcels: &mut Vec) { - // First compute which parcels intersect - let mut adjacency: MultiMap = MultiMap::new(); - // TODO could use quadtree to prune - println!("Precomputing adjacency between {} parcels...", parcels.len()); - let mut adj_counter = 0; - for p1 in parcels.iter() { - for p2 in parcels.iter() { - if p1.id < p2.id && geometry::polygons_intersect(&p1.points, &p2.points) { - // TODO could do something more clever later to avoid double memory - adjacency.insert(p1.id, p2.id); - adjacency.insert(p2.id, p1.id); - adj_counter += 1; - } - } - } - println!("{} adjacencies, now doing floodfilling to group them", adj_counter); - - // Union-find might also be good inspiration. - fn floodfill(from: ParcelID, adj: &MultiMap) -> BTreeSet { - let mut visited: BTreeSet = BTreeSet::new(); - let mut queue: Vec = vec![from]; - while !queue.is_empty() { - let current = queue.pop().unwrap(); - if visited.contains(¤t) { - continue; - } - visited.insert(current); - for next in adj.get(current).iter() { - queue.push(*next); - } - } - visited - } - - let mut block_per_parcel: Vec> = Vec::new(); - for _ in parcels.iter() { - block_per_parcel.push(None); - } - - let mut block_counter = 0; - for base_p in parcels.iter() { - // A previous iteration might have filled it out - if block_per_parcel[base_p.id.0].is_some() { - continue; - } - - let new_block = Some(block_counter); - block_counter += 1; - for p in floodfill(base_p.id, &adjacency).iter() { - assert!(!block_per_parcel[p.0].is_some()); - block_per_parcel[p.0] = new_block; - } - } - println!("{} parcels grouped into {} blocks", parcels.len(), block_counter); - - for (idx, block) in block_per_parcel.iter().enumerate() { - parcels[idx].block = block.unwrap(); - } -} diff --git a/map_model/src/map.rs b/map_model/src/map.rs index 9ce9aecb4d..82e2678730 100644 --- a/map_model/src/map.rs +++ b/map_model/src/map.rs @@ -161,10 +161,9 @@ impl Map { .iter() .map(|coord| Pt2D::from_gps(coord, &bounds)) .collect(), - block: 0, + block: p.block, }); } - make::group_parcels(&mut m.parcels); Ok(m) } diff --git a/map_model/src/raw_data.rs b/map_model/src/raw_data.rs index 423877ff46..6f19535c56 100644 --- a/map_model/src/raw_data.rs +++ b/map_model/src/raw_data.rs @@ -83,4 +83,5 @@ pub struct Parcel { // last point never the first? pub points: Vec, // TODO decide what metadata from the shapefile is useful + pub block: usize, }