mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 15:33:44 +03:00
moving parcel grouping into convert_osm, which is run with --release anyway
This commit is contained in:
parent
e31061860b
commit
82902199a2
@ -7,6 +7,7 @@ authors = ["Dustin Carlino <dabreegster@gmail.com>"]
|
|||||||
abstutil = { path = "../abstutil" }
|
abstutil = { path = "../abstutil" }
|
||||||
byteorder = "1.2.1"
|
byteorder = "1.2.1"
|
||||||
dimensioned = { git = "https://github.com/paholg/dimensioned", rev = "0e1076ebfa5128d1ee544bdc9754c948987b6fe3", features = ["serde"] }
|
dimensioned = { git = "https://github.com/paholg/dimensioned", rev = "0e1076ebfa5128d1ee544bdc9754c948987b6fe3", features = ["serde"] }
|
||||||
|
geo = "0.9.1"
|
||||||
geom = { path = "../geom" }
|
geom = { path = "../geom" }
|
||||||
ordered-float = "0.5.0"
|
ordered-float = "0.5.0"
|
||||||
osm-xml = "0.5.1"
|
osm-xml = "0.5.1"
|
||||||
|
97
convert_osm/src/group_parcels.rs
Normal file
97
convert_osm/src/group_parcels.rs
Normal file
@ -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<raw_data::Parcel>) {
|
||||||
|
// First compute which parcels intersect
|
||||||
|
let mut adjacency: MultiMap<ParcelIdx, ParcelIdx> = 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<ParcelIdx, ParcelIdx>) -> BTreeSet<ParcelIdx> {
|
||||||
|
let mut visited: BTreeSet<ParcelIdx> = BTreeSet::new();
|
||||||
|
let mut queue: Vec<ParcelIdx> = 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<Option<usize>> = 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<LonLat>, pts2: &Vec<LonLat>) -> 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)
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
extern crate abstutil;
|
extern crate abstutil;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
extern crate dimensioned;
|
extern crate dimensioned;
|
||||||
|
extern crate geo;
|
||||||
extern crate geom;
|
extern crate geom;
|
||||||
extern crate map_model;
|
extern crate map_model;
|
||||||
extern crate ordered_float;
|
extern crate ordered_float;
|
||||||
@ -9,6 +10,7 @@ extern crate shp;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate structopt;
|
extern crate structopt;
|
||||||
|
|
||||||
|
mod group_parcels;
|
||||||
mod osm;
|
mod osm;
|
||||||
mod remove_disconnected;
|
mod remove_disconnected;
|
||||||
mod split_ways;
|
mod split_ways;
|
||||||
@ -69,6 +71,7 @@ pub fn convert(flags: &Flags) -> raw_data::Map {
|
|||||||
map.parcels.push(p);
|
map.parcels.push(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
group_parcels::group_parcels(&mut map.parcels);
|
||||||
|
|
||||||
for coord in
|
for coord in
|
||||||
&traffic_signals::extract(&flags.traffic_signals).expect("loading traffic signals failed")
|
&traffic_signals::extract(&flags.traffic_signals).expect("loading traffic signals failed")
|
||||||
|
@ -36,7 +36,10 @@ pub fn load(
|
|||||||
// interpret parsing failures appropriately though...
|
// interpret parsing failures appropriately though...
|
||||||
if text.contains(" ") {
|
if text.contains(" ") {
|
||||||
let mut ok = true;
|
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(" ") {
|
for pt in text.split(" ") {
|
||||||
if let Some((lon, lat)) = parse_pt(pt) {
|
if let Some((lon, lat)) = parse_pt(pt) {
|
||||||
if b.contains(lon, lat) {
|
if b.contains(lon, lat) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
use aabb_quadtree::geom::{Point, Rect};
|
use aabb_quadtree::geom::{Point, Rect};
|
||||||
use geo;
|
|
||||||
use geom::{Angle, Bounds, PolyLine, Pt2D};
|
use geom::{Angle, Bounds, PolyLine, Pt2D};
|
||||||
use graphics::math::Vec2d;
|
use graphics::math::Vec2d;
|
||||||
use std::f64;
|
use std::f64;
|
||||||
@ -115,19 +114,3 @@ pub fn regular_polygon(center: Pt2D, sides: usize, length: f64) -> Vec<Pt2D> {
|
|||||||
pts.push(first_pt);
|
pts.push(first_pt);
|
||||||
pts
|
pts
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn polygons_intersect(pts1: &Vec<Pt2D>, pts2: &Vec<Pt2D>) -> 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)
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
mod buildings;
|
mod buildings;
|
||||||
mod lanes;
|
mod lanes;
|
||||||
mod parcels;
|
|
||||||
mod trim_lines;
|
mod trim_lines;
|
||||||
mod turns;
|
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::lanes::get_lane_specs;
|
||||||
pub(crate) use self::trim_lines::trim_lines;
|
pub(crate) use self::trim_lines::trim_lines;
|
||||||
pub(crate) use self::turns::make_all_turns;
|
pub(crate) use self::turns::make_all_turns;
|
||||||
pub(crate) use self::parcels::group_parcels;
|
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
use {Parcel, ParcelID, geometry};
|
|
||||||
use std::collections::BTreeSet;
|
|
||||||
use abstutil::MultiMap;
|
|
||||||
|
|
||||||
pub fn group_parcels(parcels: &mut Vec<Parcel>) {
|
|
||||||
// First compute which parcels intersect
|
|
||||||
let mut adjacency: MultiMap<ParcelID, ParcelID> = 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<ParcelID, ParcelID>) -> BTreeSet<ParcelID> {
|
|
||||||
let mut visited: BTreeSet<ParcelID> = BTreeSet::new();
|
|
||||||
let mut queue: Vec<ParcelID> = 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<Option<usize>> = 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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -161,10 +161,9 @@ impl Map {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|coord| Pt2D::from_gps(coord, &bounds))
|
.map(|coord| Pt2D::from_gps(coord, &bounds))
|
||||||
.collect(),
|
.collect(),
|
||||||
block: 0,
|
block: p.block,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
make::group_parcels(&mut m.parcels);
|
|
||||||
|
|
||||||
Ok(m)
|
Ok(m)
|
||||||
}
|
}
|
||||||
|
@ -83,4 +83,5 @@ pub struct Parcel {
|
|||||||
// last point never the first?
|
// last point never the first?
|
||||||
pub points: Vec<LonLat>,
|
pub points: Vec<LonLat>,
|
||||||
// TODO decide what metadata from the shapefile is useful
|
// TODO decide what metadata from the shapefile is useful
|
||||||
|
pub block: usize,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user