dismantle importing for neighborhoods. long unused. just retain a simple

osmosis polygon editor.
This commit is contained in:
Dustin Carlino 2020-05-01 14:11:41 -07:00
parent 270c7d0712
commit 02c5b6617b
15 changed files with 191 additions and 634 deletions

12
Cargo.lock generated
View File

@ -421,7 +421,6 @@ version = "0.1.0"
dependencies = [
"abstutil 0.1.0",
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"geojson 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
"geom 0.1.0",
"gtfs 0.1.0",
"kml 0.1.0",
@ -1070,16 +1069,6 @@ dependencies = [
"rstar 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "geojson"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "geom"
version = "0.1.0"
@ -3903,7 +3892,6 @@ dependencies = [
"checksum geo-booleanop 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70960413106c9090485c939666475f7862fa13cef7d07f866320a5833b3ca871"
"checksum geo-offset 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "563c564777dda10493aed3ff3d92f9a5e40b924b3d63a4c1540b47c97dbd47b2"
"checksum geo-types 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "866e8f6dbd2218b05ea8a25daa1bfac32b0515fe7e0a37cb6a7b9ed0ed82a07e"
"checksum geojson 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4ac03428b3276fc7f1756eba0c76c7c0c91ef77e1c43fbdd47a460238419cb9"
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
"checksum git2 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c1af51ea8a906616af45a4ce78eacf25860f7a13ae7bf8a814693f0f4037a26"
"checksum gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ca98bbde17256e02d17336a6bdb5a50f7d0ccacee502e191d3e3d0ec2f96f84a"

View File

@ -149,16 +149,6 @@ pub fn path_fixes(city: &str, map: &str) -> String {
format!("../data/input/{}/fixes/{}.json", city, map)
}
pub fn path_neighborhood(city_name: &str, map_name: &str, neighborhood: &str) -> String {
format!(
"../data/input/{}/neighborhoods/{}/{}.json",
city_name, map_name, neighborhood
)
}
pub fn path_all_neighborhoods(city_name: &str, map_name: &str) -> String {
format!("../data/input/{}/neighborhoods/{}", city_name, map_name)
}
pub fn path_pending_screenshots(map_name: &str) -> String {
format!("../data/input/screenshots/pending_{}", map_name)
}

View File

@ -7,7 +7,6 @@ edition = "2018"
[dependencies]
abstutil = { path = "../abstutil" }
byteorder = "1.3.4"
geojson = "0.17.0"
geom = { path = "../geom" }
gtfs = { path = "../gtfs" }
kml = { path = "../kml" }

View File

@ -1,5 +1,4 @@
mod clip;
mod neighborhoods;
mod osm_reader;
mod split_ways;
mod srtm;
@ -24,7 +23,6 @@ pub struct Options {
pub private_offstreet_parking: PrivateOffstreetParking,
pub sidewalks: Option<String>,
pub gtfs: Option<String>,
pub neighborhoods: Option<String>,
pub elevation: Option<String>,
pub clip: Option<String>,
pub drive_on_right: bool,
@ -81,17 +79,6 @@ pub fn convert(opts: Options, timer: &mut abstutil::Timer) -> RawMap {
use_elevation(&mut map, path, timer);
}
if let Some(ref path) = opts.neighborhoods {
timer.start("convert neighborhood polygons");
neighborhoods::convert(
path.clone(),
map.city_name.clone(),
map.name.clone(),
&map.gps_bounds,
);
timer.stop("convert neighborhood polygons");
}
map
}

View File

@ -1,69 +0,0 @@
use abstutil::Timer;
use geojson::{GeoJson, PolygonType, Value};
use geom::{GPSBounds, LonLat};
use map_model::NeighborhoodBuilder;
pub fn convert(geojson_path: String, city_name: String, map_name: String, gps_bounds: &GPSBounds) {
println!("Extracting neighborhoods from {}...", geojson_path);
let document: GeoJson = abstutil::read_json(geojson_path, &mut Timer::throwaway());
match document {
GeoJson::FeatureCollection(c) => {
for f in c.features.into_iter() {
let name = f.properties.unwrap()["name"].as_str().unwrap().to_string();
match f.geometry.unwrap().value {
Value::Polygon(p) => {
convert_polygon(p, name, city_name.clone(), map_name.clone(), gps_bounds);
}
Value::MultiPolygon(polygons) => {
for (idx, p) in polygons.into_iter().enumerate() {
convert_polygon(
p,
format!("{} portion #{}", name, idx + 1),
city_name.clone(),
map_name.clone(),
gps_bounds,
);
}
}
x => panic!("Unexpected GeoJson value {:?}", x),
}
}
}
_ => panic!("Unexpected GeoJson root {:?}", document),
}
}
fn convert_polygon(
input: PolygonType,
name: String,
city_name: String,
map_name: String,
gps_bounds: &GPSBounds,
) {
if input.len() > 1 {
println!("{} has a polygon with an inner ring, skipping", name);
return;
}
let mut points: Vec<LonLat> = Vec::new();
for raw_pt in &input[0] {
assert_eq!(raw_pt.len(), 2);
let pt = LonLat::new(raw_pt[0], raw_pt[1]);
if gps_bounds.contains(pt) {
points.push(pt);
} else {
println!(
"Neighborhood polygon \"{}\" is out-of-bounds, skipping",
name
);
return;
}
}
NeighborhoodBuilder {
city_name,
map_name,
name,
points,
}
.save();
}

View File

@ -8,87 +8,8 @@ ab122e013e18d9bfe923162ad27deb54 data/input/seattle/osm/ballard.osm
f17e480bd803309c92d58c96bebb0ca1 data/input/seattle/osm/caphill.osm
c3198bbcdf00c4cdcc429d4679d68202 data/input/seattle/osm/lakeslice.osm
0052ec465666b0c49ef1d1221c9e3a53 data/input/seattle/osm/Seattle.osm
46d8ebc909717b5ddfe921cf487f975d data/input/seattle/neighborhoods/ballard/West Woodland.json
ba915a6237156522eac2f380e978bb25 data/input/seattle/neighborhoods/ballard/Adams.json
ae6e3c5801460075cebe736e56d98b01 data/input/seattle/neighborhoods/ballard/Phinney Ridge.json
886cff1b9508af1917d76d746336c961 data/input/seattle/neighborhoods/ballard/Fremont.json
ae8a868fe3eac6c67340f174dc24866a data/input/seattle/neighborhoods/ballard/Lawton Park.json
1e2ff5d0083b0e5aab885d38be992ad7 data/input/seattle/neighborhoods/intl_district/International District.json
721155dd7662b9aaaf68b222aa731560 data/input/seattle/neighborhoods/x1/Harrison - Denny-Blaine.json
2e9dbd878640b904307dcadc1fd7334f data/input/seattle/neighborhoods/x1/Madison Park.json
f2b26082d44df01339f30d1cc2eb4178 data/input/seattle/neighborhoods/x1/Portage Bay.json
197ecdfed33e300e6f64275a896b2905 data/input/seattle/neighborhoods/x1/Eastlake.json
33128005e7f6caaab5d321558965f125 data/input/seattle/neighborhoods/x1/Broadway.json
a5dca5dd4a4b78efb17e69c2d557ab1b data/input/seattle/neighborhoods/x1/Stevens.json
519cbc4a6c2038fab9df172f2db63085 data/input/seattle/neighborhoods/x1/Montlake.json
ae65aaa3a0ddb7bb990972c9832369b6 data/input/seattle/neighborhoods/23rd/Mann.json
d9e0bc911a52aa15f51cb49956d2aef5 data/input/seattle/neighborhoods/lakeslice/Leschi.json
1ac65f96b8f4a2b2d365949032966402 data/input/seattle/neighborhoods/lakeslice/Harrison - Denny-Blaine.json
7b5a6df2a313b997e1ac575f6cec03d6 data/input/seattle/neighborhoods/lakeslice/Mann.json
eb073e33d652161805fd42c982856703 data/input/seattle/neighborhoods/lakeslice/Madrona.json
e62769afe799c43dbbe73e164e6bd620 data/input/seattle/neighborhoods/downtown/Pike-Market.json
d02cb65749922832091b18ad45a4400e data/input/seattle/neighborhoods/downtown/First Hill.json
64073270987303abf1532e6abaaa2676 data/input/seattle/neighborhoods/downtown/Central Business District.json
2374dd1b296f7bffa436e14ff2ddcd36 data/input/seattle/neighborhoods/downtown/Belltown.json
796f5d8eb1a5f1bfe85b2945239e14de data/input/seattle/neighborhoods/huge_seattle/West Woodland.json
8f586abbc7c18ee104493f7f83d88b20 data/input/seattle/neighborhoods/huge_seattle/Westlake.json
778beb76a0f0f2c4e5f71e56f6a37076 data/input/seattle/neighborhoods/huge_seattle/North College Park.json
21376d86306c45e82ddbbc4acd62a3ce data/input/seattle/neighborhoods/huge_seattle/Lower Queen Anne.json
90e78679efe05bb5c51c095bd56df8db data/input/seattle/neighborhoods/huge_seattle/Adams.json
7d2570e9728af1313c0af70b35ff7c12 data/input/seattle/neighborhoods/huge_seattle/Leschi.json
02e25d6d76d0a9b8823e8783d01d499b data/input/seattle/neighborhoods/huge_seattle/University District.json
4c9c0bbd2c4d30166cc82b6c9291de78 data/input/seattle/neighborhoods/huge_seattle/Pike-Market.json
2ff1b7b76f126ed69227923d40ecdaa3 data/input/seattle/neighborhoods/huge_seattle/Harrison - Denny-Blaine.json
a65a48ca68f15635fd86e7fdd06ddaea data/input/seattle/neighborhoods/huge_seattle/Roosevelt.json
57103a4893df8c1667674d8de47bee0b data/input/seattle/neighborhoods/huge_seattle/Whittier Heights.json
9cb7a0ef7a3bcdb634681e21d2f6fea3 data/input/seattle/neighborhoods/huge_seattle/Laurelhurst.json
cb93fd55d3b42979a96dc45e5571f5ac data/input/seattle/neighborhoods/huge_seattle/International District.json
9f04e6fa311b2274a42ee4b29f11d4e8 data/input/seattle/neighborhoods/huge_seattle/Maple Leaf.json
a8d1df39941171daa16195cc98692907 data/input/seattle/neighborhoods/huge_seattle/First Hill.json
b7fcb8b06ce4f571849fd33a277d21af data/input/seattle/neighborhoods/huge_seattle/Madison Park.json
c45186b872050db02e053afcf4a7424e data/input/seattle/neighborhoods/huge_seattle/Portage Bay.json
b4f6a4f24dab7eed34347069748ed6ce data/input/seattle/neighborhoods/huge_seattle/Central Business District.json
ffcca5e408c97cb81d1f739816809e01 data/input/seattle/neighborhoods/huge_seattle/South Lake Union.json
db4b1be3343681f5bcd8115181f95f08 data/input/seattle/neighborhoods/huge_seattle/Greenwood.json
6d50b3ae6f11de62b44e6109a435d002 data/input/seattle/neighborhoods/huge_seattle/Eastlake.json
b809cb0eb9a77a138c447d92d2a036ad data/input/seattle/neighborhoods/huge_seattle/Sunset Hill.json
1c27ea07ae9f046e79bdd79a0dbc4863 data/input/seattle/neighborhoods/huge_seattle/Broadway.json
a205318630171b21d3d8172c44a973d1 data/input/seattle/neighborhoods/huge_seattle/Phinney Ridge.json
02c097d984b9e039b7b9912f03ddf717 data/input/seattle/neighborhoods/huge_seattle/Ravenna.json
fc6dc3658c5d9cb01d778db3026731c6 data/input/seattle/neighborhoods/huge_seattle/Fremont.json
14b37651f6259ba8ed8f7dc04a93245b data/input/seattle/neighborhoods/huge_seattle/Yesler Terrace.json
a3b74ad210d5ae94c8fb8a23ffd7b521 data/input/seattle/neighborhoods/huge_seattle/Wedgwood.json
f60c9d7699b39ed6d99109ff0a4b7ee9 data/input/seattle/neighborhoods/huge_seattle/Sand Point.json
7957979d53d4a6364b9020a119c1f9e9 data/input/seattle/neighborhoods/huge_seattle/Wallingford.json
c642b472850a444e1d436a3dc8de5c57 data/input/seattle/neighborhoods/huge_seattle/Interbay.json
b19598d61c1301e4573edd927fb4ba38 data/input/seattle/neighborhoods/huge_seattle/West Queen Anne.json
331b23ca4b2d94d6e38026dfd7dfc386 data/input/seattle/neighborhoods/huge_seattle/Southeast Magnolia.json
b89543b001bc1e6f7ec5afda712a9532 data/input/seattle/neighborhoods/huge_seattle/North Beach - Blue Ridge.json
0f8511e1759b3150d77752aa9608ddd5 data/input/seattle/neighborhoods/huge_seattle/Windermere.json
c9fe0cec21b4506b9449f8e286707f62 data/input/seattle/neighborhoods/huge_seattle/Stevens.json
8f1b6913ee7ddee6c3ad6762077e1940 data/input/seattle/neighborhoods/huge_seattle/East Queen Anne.json
3f1e1d2473fcd9909ed48885156d8ad9 data/input/seattle/neighborhoods/huge_seattle/Belltown.json
7148acd286b453d20db96ad3a8461727 data/input/seattle/neighborhoods/huge_seattle/Loyal Heights.json
14c4caa74a3aab96b3270c9add7c8019 data/input/seattle/neighborhoods/huge_seattle/Mann.json
af28f6e4b3ef367fed0d828a47e5208d data/input/seattle/neighborhoods/huge_seattle/View Ridge.json
05ca8b996e144d45ae49961638e37135 data/input/seattle/neighborhoods/huge_seattle/Minor.json
4b0347aeed2ad2e3cf3b71a07efba3e0 data/input/seattle/neighborhoods/huge_seattle/Madrona.json
baa4ebb40bc293c6176a3239620e7183 data/input/seattle/neighborhoods/huge_seattle/Lawton Park.json
78b4d30d5bb4e7e8a40c7728124fd890 data/input/seattle/neighborhoods/huge_seattle/Montlake.json
271892c6453b6e0d512f948d9b7de62a data/input/seattle/neighborhoods/huge_seattle/Briarcliff.json
92d4c74df6478e29cdb0281ba4ff7894 data/input/seattle/neighborhoods/huge_seattle/North Queen Anne.json
9666b4689a2927797fe865becd6f61b7 data/input/seattle/neighborhoods/huge_seattle/Pioneer Square.json
18ae58793ddede2c087dbc7f9876266a data/input/seattle/neighborhoods/huge_seattle/Crown Hill.json
d729d8ef4f797d51dbb53096a4aac233 data/input/seattle/neighborhoods/huge_seattle/Green Lake.json
4be236f94749a0325f9696d0ed051e85 data/input/seattle/neighborhoods/huge_seattle/Bryant.json
1e608dafe6199b70929bb6bae84ead30 data/input/seattle/neighborhoods/caphill/Portage Bay.json
82c31490f1c0b5f7ab2e75766c8a9e04 data/input/seattle/neighborhoods/caphill/Eastlake.json
790ca481f2ea517acd63c97cc58b4d10 data/input/seattle/neighborhoods/caphill/Broadway.json
2b5fe53d1566708e1484181f026a77db data/input/seattle/neighborhoods/caphill/Stevens.json
96794d4b0d55abbacc48ff2df9941230 data/input/seattle/neighborhoods/caphill/Montlake.json
19e8073a9f6c807b4492681b2c7570de data/input/seattle/blockface.bin
db63d7d606e8702d12f9399e87e6a00f data/input/seattle/parcels_urbansim.txt
2bc84e4d194d7cea6007ae3b93f3b11b data/input/seattle/neighborhoods.geojson
7cbf604cb6d080292a5e69fdf0caf3b3 data/input/seattle/popdat.bin
428bc2e92ea02089cedbb614ce1d8f25 data/input/seattle/polygons/caphill.poly
4f291bbe84ac32a98d7d100be79ddc4b data/input/seattle/polygons/huge_seattle.poly

View File

@ -1,5 +1,5 @@
mod blocks;
mod neighborhood;
mod polygon;
mod scenario;
use crate::app::App;
@ -19,18 +19,14 @@ impl DevToolsMode {
"Internal dev tools",
vec![],
vec![
(hotkey(Key::N), "manage neighborhoods"),
(hotkey(Key::P), "draw a polygon"),
(hotkey(Key::W), "load scenario"),
],
))
.cb("X", Box::new(|_, _| Some(Transition::Pop)))
.cb(
"manage neighborhoods",
Box::new(|_, _| {
Some(Transition::Push(Box::new(
neighborhood::NeighborhoodPicker::new(),
)))
}),
"draw a polygon",
Box::new(|ctx, app| Some(Transition::Push(polygon::PolygonEditor::new(ctx, app)))),
)
.cb(
"load scenario",

View File

@ -1,228 +0,0 @@
use crate::app::App;
use crate::common::CommonState;
use crate::game::{State, Transition};
use crate::managed::WrappedComposite;
use ezgui::{
hotkey, Choice, Color, Composite, EventCtx, GfxCtx, Key, Line, Outcome, Text, Wizard,
WrappedWizard,
};
use geom::{Circle, Distance, Polygon, Pt2D};
use map_model::{Map, NeighborhoodBuilder};
const POINT_RADIUS: Distance = Distance::const_meters(10.0);
// Localized and internal, so don't put in ColorScheme.
const POINT_COLOR: Color = Color::RED;
const POLYGON_COLOR: Color = Color::BLUE.alpha(0.6);
const POINT_TO_MOVE: Color = Color::CYAN;
const LAST_PLACED_POINT: Color = Color::GREEN;
// This shouldn't get subsumed by WizardState, since it has such an interesting draw().
pub struct NeighborhoodPicker {
wizard: Wizard,
}
impl NeighborhoodPicker {
pub fn new() -> NeighborhoodPicker {
NeighborhoodPicker {
wizard: Wizard::new(),
}
}
}
impl State for NeighborhoodPicker {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
ctx.canvas_movement();
if let Some(n) = pick_neighborhood(&app.primary.map, self.wizard.wrap(ctx)) {
self.wizard = Wizard::new();
return Transition::Push(Box::new(NeighborhoodEditor {
composite: WrappedComposite::quick_menu(
ctx,
app,
format!("Neighborhood Editor for {}", n.name),
vec![],
vec![
(hotkey(Key::S), "save"),
(hotkey(Key::X), "export as an Osmosis polygon filter"),
],
),
neighborhood: n,
mouseover_pt: None,
moving_pt: false,
}));
} else if self.wizard.aborted() {
return Transition::Pop;
}
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, app: &App) {
// TODO is this order wrong?
self.wizard.draw(g);
if let Some(neighborhood) = self.wizard.current_menu_choice::<NeighborhoodBuilder>() {
g.draw_polygon(
POLYGON_COLOR,
&Polygon::new(
&app.primary
.map
.get_gps_bounds()
.must_convert(&neighborhood.points),
),
);
}
}
}
struct NeighborhoodEditor {
composite: Composite,
neighborhood: NeighborhoodBuilder,
mouseover_pt: Option<usize>,
moving_pt: bool,
}
impl State for NeighborhoodEditor {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
let gps_bounds = app.primary.map.get_gps_bounds();
ctx.canvas_movement();
if self.moving_pt {
if let Some(pt) = ctx
.canvas
.get_cursor_in_map_space()
.and_then(|c| c.to_gps(gps_bounds))
{
self.neighborhood.points[self.mouseover_pt.unwrap()] = pt;
}
if ctx.input.key_released(Key::LeftControl) {
self.moving_pt = false;
}
return Transition::Keep;
}
match self.composite.event(ctx) {
Some(Outcome::Clicked(x)) => match x.as_ref() {
"X" => {
return Transition::Pop;
}
"save" => {
if self.neighborhood.points.len() >= 3 {
self.neighborhood.save();
}
}
"export as an Osmosis polygon filter" => {
if self.neighborhood.points.len() >= 3 {
self.neighborhood.save_as_osmosis().unwrap();
}
}
_ => unreachable!(),
},
None => {}
}
if let Some(cursor) = ctx.canvas.get_cursor_in_map_space() {
self.mouseover_pt = self.neighborhood.points.iter().position(|pt| {
Circle::new(
Pt2D::from_gps(*pt, gps_bounds).unwrap(),
POINT_RADIUS / ctx.canvas.cam_zoom,
)
.contains_pt(cursor)
});
} else {
self.mouseover_pt = None;
}
// TODO maybe click-and-drag is more intuitive
if self.mouseover_pt.is_some() {
if ctx
.input
.key_pressed(Key::LeftControl, "hold to move this point")
{
self.moving_pt = true;
}
} else if let Some(pt) = ctx
.canvas
.get_cursor_in_map_space()
.and_then(|c| c.to_gps(gps_bounds))
{
if app.per_obj.left_click(ctx, "add a new point") {
self.neighborhood.points.push(pt);
}
}
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, app: &App) {
let pts: Vec<Pt2D> = app
.primary
.map
.get_gps_bounds()
.must_convert(&self.neighborhood.points);
if pts.len() == 2 {
g.draw_line(
POINT_COLOR,
POINT_RADIUS / 2.0,
&geom::Line::new(pts[0], pts[1]),
);
}
if pts.len() >= 3 {
g.draw_polygon(POLYGON_COLOR, &Polygon::new(&pts));
}
for (idx, pt) in pts.iter().enumerate() {
let color = if Some(idx) == self.mouseover_pt {
POINT_TO_MOVE
} else if idx == pts.len() - 1 {
LAST_PLACED_POINT
} else {
POINT_COLOR
};
g.draw_circle(color, &Circle::new(*pt, POINT_RADIUS / g.canvas.cam_zoom));
}
self.composite.draw(g);
if self.mouseover_pt.is_some() {
CommonState::draw_custom_osd(
g,
app,
Text::from(Line("hold left Control to move point")),
);
} else {
CommonState::draw_osd(g, app, &None);
}
}
}
fn pick_neighborhood(map: &Map, mut wizard: WrappedWizard) -> Option<NeighborhoodBuilder> {
let load_existing = "Load existing neighborhood";
let create_new = "Create new neighborhood";
if wizard.choose_string("What neighborhood to edit?", || {
vec![load_existing, create_new]
})? == load_existing
{
load_neighborhood_builder(map, &mut wizard, "Load which neighborhood?")
} else {
let name = wizard.input_string("Name the neighborhood")?;
Some(NeighborhoodBuilder {
city_name: map.get_city_name().to_string(),
name,
map_name: map.get_name().to_string(),
points: Vec::new(),
})
}
}
fn load_neighborhood_builder(
map: &Map,
wizard: &mut WrappedWizard,
query: &str,
) -> Option<NeighborhoodBuilder> {
wizard
.choose(query, || {
Choice::from(abstutil::load_all_objects(
abstutil::path_all_neighborhoods(map.get_city_name(), map.get_name()),
))
})
.map(|(_, n)| n)
}

View File

@ -0,0 +1,165 @@
use crate::app::App;
use crate::common::CommonState;
use crate::game::{State, Transition};
use crate::managed::WrappedComposite;
use ezgui::{hotkey, Color, Composite, EventCtx, GfxCtx, Key, Line, Outcome, Text};
use geom::{Circle, Distance, LonLat, Polygon, Pt2D};
use std::fs::File;
use std::io::{Error, Write};
const POINT_RADIUS: Distance = Distance::const_meters(10.0);
// Localized and internal, so don't put in ColorScheme.
const POINT_COLOR: Color = Color::RED;
const POLYGON_COLOR: Color = Color::BLUE.alpha(0.6);
const POINT_TO_MOVE: Color = Color::CYAN;
const LAST_PLACED_POINT: Color = Color::GREEN;
pub struct PolygonEditor {
composite: Composite,
points: Vec<LonLat>,
mouseover_pt: Option<usize>,
moving_pt: bool,
}
impl PolygonEditor {
pub fn new(ctx: &mut EventCtx, app: &App) -> Box<dyn State> {
Box::new(PolygonEditor {
composite: WrappedComposite::quick_menu(
ctx,
app,
"Polygon editor",
vec![],
vec![(hotkey(Key::X), "export as an Osmosis polygon filter")],
),
points: Vec::new(),
mouseover_pt: None,
moving_pt: false,
})
}
}
impl State for PolygonEditor {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
let gps_bounds = app.primary.map.get_gps_bounds();
ctx.canvas_movement();
if self.moving_pt {
if let Some(pt) = ctx
.canvas
.get_cursor_in_map_space()
.and_then(|c| c.to_gps(gps_bounds))
{
self.points[self.mouseover_pt.unwrap()] = pt;
}
if ctx.input.key_released(Key::LeftControl) {
self.moving_pt = false;
}
return Transition::Keep;
}
match self.composite.event(ctx) {
Some(Outcome::Clicked(x)) => match x.as_ref() {
"X" => {
return Transition::Pop;
}
"export as an Osmosis polygon filter" => {
if self.points.len() >= 3 {
save_as_osmosis(&self.points);
}
}
_ => unreachable!(),
},
None => {}
}
if let Some(cursor) = ctx.canvas.get_cursor_in_map_space() {
self.mouseover_pt = self.points.iter().position(|pt| {
Circle::new(
Pt2D::from_gps(*pt, gps_bounds).unwrap(),
POINT_RADIUS / ctx.canvas.cam_zoom,
)
.contains_pt(cursor)
});
} else {
self.mouseover_pt = None;
}
// TODO maybe click-and-drag is more intuitive
if self.mouseover_pt.is_some() {
if ctx
.input
.key_pressed(Key::LeftControl, "hold to move this point")
{
self.moving_pt = true;
}
} else if let Some(pt) = ctx
.canvas
.get_cursor_in_map_space()
.and_then(|c| c.to_gps(gps_bounds))
{
if app.per_obj.left_click(ctx, "add a new point") {
self.points.push(pt);
}
}
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, app: &App) {
let pts: Vec<Pt2D> = app.primary.map.get_gps_bounds().must_convert(&self.points);
if pts.len() == 2 {
g.draw_line(
POINT_COLOR,
POINT_RADIUS / 2.0,
&geom::Line::new(pts[0], pts[1]),
);
}
if pts.len() >= 3 {
g.draw_polygon(POLYGON_COLOR, &Polygon::new(&pts));
}
for (idx, pt) in pts.iter().enumerate() {
let color = if Some(idx) == self.mouseover_pt {
POINT_TO_MOVE
} else if idx == pts.len() - 1 {
LAST_PLACED_POINT
} else {
POINT_COLOR
};
g.draw_circle(color, &Circle::new(*pt, POINT_RADIUS / g.canvas.cam_zoom));
}
self.composite.draw(g);
if self.mouseover_pt.is_some() {
CommonState::draw_custom_osd(
g,
app,
Text::from(Line("hold left Control to move point")),
);
} else {
CommonState::draw_osd(g, app, &None);
}
}
}
// https://wiki.openstreetmap.org/wiki/Osmosis/Polygon_Filter_File_Format
fn save_as_osmosis(pts: &Vec<LonLat>) -> Result<(), Error> {
let path = "bounding_boy.poly";
let mut f = File::create(&path)?;
writeln!(f, "name goes here")?;
writeln!(f, "1")?;
for gps in pts {
writeln!(f, " {} {}", gps.x(), gps.y())?;
}
// Have to repeat the first point
{
writeln!(f, " {} {}", pts[0].x(), pts[0].y())?;
}
writeln!(f, "END")?;
writeln!(f, "END")?;
println!("Exported {}", path);
Ok(())
}

View File

@ -30,7 +30,6 @@ pub fn osm_to_raw(name: &str) {
private_offstreet_parking: convert_osm::PrivateOffstreetParking::OnePerBldg,
sidewalks: None,
gtfs: None,
neighborhoods: None,
elevation: None,
clip: Some(format!("../data/input/austin/polygons/{}.poly", name)),
drive_on_right: true,

View File

@ -30,7 +30,6 @@ pub fn osm_to_raw(name: &str) {
private_offstreet_parking: convert_osm::PrivateOffstreetParking::OnePerBldg,
sidewalks: None,
gtfs: None,
neighborhoods: None,
elevation: None,
clip: Some(format!("../data/input/los_angeles/polygons/{}.poly", name)),
drive_on_right: true,

View File

@ -5,8 +5,6 @@ fn input() {
"../data/input/seattle/google_transit/",
"https://metro.kingcounty.gov/GTFS/google_transit.zip",
);
// Like https://data.seattle.gov/dataset/Neighborhoods/2mbt-aqqx, but in GeoJSON, not SHP
download("../data/input/seattle/neighborhoods.geojson", "https://github.com/seattleio/seattle-boundaries-data/raw/master/data/neighborhoods.geojson");
download(
"../data/input/seattle/N47W122.hgt",
"https://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_01/N47W122.hgt.zip",
@ -42,7 +40,6 @@ pub fn osm_to_raw(name: &str) {
format!("../data/input/seattle/polygons/{}.poly", name),
format!("../data/input/seattle/osm/{}.osm", name),
);
rm(format!("../data/input/seattle/neighborhoods/{}", name));
rm(format!("../data/system/maps/{}.bin", name));
println!("- Running convert_osm");
@ -62,7 +59,6 @@ pub fn osm_to_raw(name: &str) {
// TODO These're buggy.
sidewalks: None,
gtfs: Some("../data/input/seattle/google_transit".to_string()),
neighborhoods: Some("../data/input/seattle/neighborhoods.geojson".to_string()),
elevation: Some("../data/input/seattle/N47W122.hgt".to_string()),
clip: Some(format!("../data/input/seattle/polygons/{}.poly", name)),
drive_on_right: true,

View File

@ -7,7 +7,6 @@ mod intersection;
mod lane;
mod make;
mod map;
mod neighborhood;
pub mod osm;
mod pathfind;
pub mod raw;
@ -25,7 +24,6 @@ pub use crate::intersection::{Intersection, IntersectionID, IntersectionType};
pub use crate::lane::{Lane, LaneID, LaneType, PARKING_SPOT_LENGTH};
pub use crate::make::RoadSpec;
pub use crate::map::Map;
pub use crate::neighborhood::{FullNeighborhoodInfo, Neighborhood, NeighborhoodBuilder};
pub use crate::pathfind::{Path, PathConstraints, PathRequest, PathStep};
pub use crate::road::{DirectedRoadID, Road, RoadID};
pub use crate::stop_signs::{ControlStopSign, RoadWithStopSign};
@ -44,6 +42,4 @@ impl Cloneable for ControlTrafficSignal {}
impl Cloneable for IntersectionID {}
impl Cloneable for LaneType {}
impl Cloneable for MapEdits {}
impl Cloneable for Neighborhood {}
impl Cloneable for NeighborhoodBuilder {}
impl Cloneable for raw::RestrictionType {}

View File

@ -1,157 +0,0 @@
use crate::{BuildingID, Map, RoadID};
use aabb_quadtree::QuadTree;
use geom::{GPSBounds, LonLat, Polygon, Pt2D};
use serde_derive::{Deserialize, Serialize};
use std::collections::{BTreeSet, HashMap};
use std::fs::File;
use std::io::{Error, Write};
// This form is used by the editor plugin to edit and for serialization. Storing points in GPS is
// more compatible with slight changes to the bounding box of a map over time.
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct NeighborhoodBuilder {
pub city_name: String,
pub map_name: String,
pub name: String,
pub points: Vec<LonLat>,
}
impl NeighborhoodBuilder {
pub fn finalize(&self, gps_bounds: &GPSBounds) -> Neighborhood {
assert!(self.points.len() >= 3);
Neighborhood {
city_name: self.city_name.clone(),
map_name: self.map_name.clone(),
name: self.name.clone(),
polygon: Polygon::new(
&self
.points
.iter()
.map(|pt| {
Pt2D::from_gps(*pt, gps_bounds)
.expect(&format!("Polygon {} has bad pt {}", self.name, pt))
})
.collect(),
),
}
}
pub fn save(&self) {
abstutil::write_json(
abstutil::path_neighborhood(&self.city_name, &self.map_name, &self.name),
self,
);
}
// https://wiki.openstreetmap.org/wiki/Osmosis/Polygon_Filter_File_Format
pub fn save_as_osmosis(&self) -> Result<(), Error> {
let path = abstutil::path_polygon(&self.city_name, &self.name);
let mut f = File::create(&path)?;
writeln!(f, "{}", self.name)?;
writeln!(f, "1")?;
for gps in &self.points {
writeln!(f, " {} {}", gps.x(), gps.y())?;
}
// Have to repeat the first point
{
writeln!(f, " {} {}", self.points[0].x(), self.points[0].y())?;
}
writeln!(f, "END")?;
writeln!(f, "END")?;
println!("Exported {}", path);
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct Neighborhood {
pub city_name: String,
pub map_name: String,
pub name: String,
pub polygon: Polygon,
}
impl Neighborhood {
pub fn load_all(
city_name: &str,
map_name: &str,
gps_bounds: &GPSBounds,
) -> Vec<(String, Neighborhood)> {
abstutil::load_all_objects::<NeighborhoodBuilder>(abstutil::path_all_neighborhoods(
city_name, map_name,
))
.into_iter()
.map(|(name, builder)| (name, builder.finalize(gps_bounds)))
.collect()
}
fn make_everywhere(map: &Map) -> Neighborhood {
Neighborhood {
city_name: map.get_city_name().to_string(),
map_name: map.get_name().to_string(),
name: "_everywhere_".to_string(),
polygon: map.get_bounds().get_rectangle(),
}
}
}
pub struct FullNeighborhoodInfo {
pub name: String,
pub buildings: Vec<BuildingID>,
pub roads: BTreeSet<RoadID>,
}
impl FullNeighborhoodInfo {
pub fn load_all(map: &Map) -> HashMap<String, FullNeighborhoodInfo> {
let mut neighborhoods =
Neighborhood::load_all(map.get_city_name(), map.get_name(), map.get_gps_bounds());
neighborhoods.push((
"_everywhere_".to_string(),
Neighborhood::make_everywhere(map),
));
let mut bldg_quadtree = QuadTree::default(map.get_bounds().as_bbox());
for b in map.all_buildings() {
bldg_quadtree.insert_with_box(b.id, b.polygon.get_bounds().as_bbox());
}
let mut road_quadtree = QuadTree::default(map.get_bounds().as_bbox());
for r in map.all_roads() {
road_quadtree.insert_with_box(
r.id,
r.get_thick_polygon(map).unwrap().get_bounds().as_bbox(),
);
}
let mut full_info = HashMap::new();
for (name, n) in &neighborhoods {
let mut info = FullNeighborhoodInfo {
name: name.to_string(),
buildings: Vec::new(),
roads: BTreeSet::new(),
};
for &(id, _, _) in &bldg_quadtree.query(n.polygon.get_bounds().as_bbox()) {
// TODO Polygon containment is hard; just see if the center is inside.
if n.polygon.contains_pt(map.get_b(*id).polygon.center()) {
info.buildings.push(*id);
}
}
for &(id, _, _) in &road_quadtree.query(n.polygon.get_bounds().as_bbox()) {
// TODO Polygon containment is hard; just see if the "center" of each endpoint is
// inside.
let r = map.get_r(*id);
let pt1 = r.center_pts.first_pt();
let pt2 = r.center_pts.last_pt();
if n.polygon.contains_pt(pt1) && n.polygon.contains_pt(pt2) {
info.roads.insert(*id);
}
}
full_info.insert(name.to_string(), info);
}
full_info
}
}

View File

@ -1,12 +1,12 @@
use crate::{DrivingGoal, IndividTrip, PersonID, PersonSpec, Scenario, SidewalkSpot, SpawnTrip};
use abstutil::Timer;
use geom::{Duration, Time};
use map_model::{BuildingID, DirectedRoadID, FullNeighborhoodInfo, Map, PathConstraints};
use map_model::{BuildingID, DirectedRoadID, Map, PathConstraints};
use rand::seq::SliceRandom;
use rand::Rng;
use rand_xorshift::XorShiftRng;
use serde_derive::{Deserialize, Serialize};
use std::collections::{BTreeSet, HashMap};
use std::collections::BTreeSet;
// A way to generate Scenarios
#[derive(Clone, Serialize, Deserialize, Debug)]
@ -27,7 +27,6 @@ pub struct SpawnOverTime {
// TODO use https://docs.rs/rand/0.5.5/rand/distributions/struct.Normal.html
pub start_time: Time,
pub stop_time: Time,
pub start_from_neighborhood: String,
pub goal: OriginDestination,
pub percent_driving: f64,
pub percent_biking: f64,
@ -55,32 +54,23 @@ impl ScenarioGenerator {
timer.start(format!("Generating scenario {}", self.scenario_name));
timer.start("load full neighborhood info");
let neighborhoods = FullNeighborhoodInfo::load_all(map);
timer.stop("load full neighborhood info");
for s in &self.spawn_over_time {
if !neighborhoods.contains_key(&s.start_from_neighborhood) {
panic!("Neighborhood {} isn't defined", s.start_from_neighborhood);
}
timer.start_iter("SpawnOverTime each agent", s.num_agents);
for _ in 0..s.num_agents {
timer.next();
s.spawn_agent(rng, &mut scenario, &neighborhoods, map, timer);
s.spawn_agent(rng, &mut scenario, map, timer);
}
}
timer.start_iter("BorderSpawnOverTime", self.border_spawn_over_time.len());
for s in &self.border_spawn_over_time {
timer.next();
s.spawn_peds(rng, &mut scenario, &neighborhoods, map, timer);
s.spawn_peds(rng, &mut scenario, map, timer);
s.spawn_vehicles(
s.num_cars,
PathConstraints::Car,
rng,
&mut scenario,
&neighborhoods,
map,
timer,
);
@ -89,7 +79,6 @@ impl ScenarioGenerator {
PathConstraints::Bike,
rng,
&mut scenario,
&neighborhoods,
map,
timer,
);
@ -107,8 +96,7 @@ impl ScenarioGenerator {
num_agents: 100,
start_time: Time::START_OF_DAY,
stop_time: Time::START_OF_DAY + Duration::seconds(5.0),
start_from_neighborhood: "_everywhere_".to_string(),
goal: OriginDestination::Neighborhood("_everywhere_".to_string()),
goal: OriginDestination::Anywhere,
percent_driving: 0.5,
percent_biking: 0.5,
percent_use_transit: 0.5,
@ -125,7 +113,7 @@ impl ScenarioGenerator {
start_time: Time::START_OF_DAY,
stop_time: Time::START_OF_DAY + Duration::seconds(5.0),
start_from_border: i.some_outgoing_road(map),
goal: OriginDestination::Neighborhood("_everywhere_".to_string()),
goal: OriginDestination::Anywhere,
percent_use_transit: 0.5,
})
.collect(),
@ -135,7 +123,6 @@ impl ScenarioGenerator {
num_agents: 10,
start_time: Time::START_OF_DAY,
stop_time: Time::START_OF_DAY + Duration::seconds(5.0),
start_from_neighborhood: "_everywhere_".to_string(),
goal: OriginDestination::EndOfRoad(i.some_incoming_road(map)),
percent_driving: 0.5,
percent_biking: 0.5,
@ -163,8 +150,7 @@ impl ScenarioGenerator {
num_agents: num_agents,
start_time: Time::START_OF_DAY,
stop_time: Time::START_OF_DAY + Duration::seconds(5.0),
start_from_neighborhood: "_everywhere_".to_string(),
goal: OriginDestination::Neighborhood("_everywhere_".to_string()),
goal: OriginDestination::Anywhere,
percent_driving: 0.5,
percent_biking: 0.5,
percent_use_transit: 0.5,
@ -179,23 +165,19 @@ impl SpawnOverTime {
&self,
rng: &mut XorShiftRng,
scenario: &mut Scenario,
neighborhoods: &HashMap<String, FullNeighborhoodInfo>,
map: &Map,
timer: &mut Timer,
) {
let depart = rand_time(rng, self.start_time, self.stop_time);
// Note that it's fine for agents to start/end at the same building. Later we might
// want a better assignment of people per household, or workers per office building.
let from_bldg = *neighborhoods[&self.start_from_neighborhood]
.buildings
.choose(rng)
.unwrap();
let from_bldg = map.all_buildings().choose(rng).unwrap().id;
let id = PersonID(scenario.people.len());
if rng.gen_bool(self.percent_driving) {
if let Some(goal) =
self.goal
.pick_driving_goal(PathConstraints::Car, map, &neighborhoods, rng, timer)
if let Some(goal) = self
.goal
.pick_driving_goal(PathConstraints::Car, map, rng, timer)
{
scenario.people.push(PersonSpec {
id,
@ -212,9 +194,9 @@ impl SpawnOverTime {
let start_spot = SidewalkSpot::building(from_bldg, map);
if rng.gen_bool(self.percent_biking) {
if let Some(goal) =
self.goal
.pick_driving_goal(PathConstraints::Bike, map, &neighborhoods, rng, timer)
if let Some(goal) = self
.goal
.pick_driving_goal(PathConstraints::Bike, map, rng, timer)
{
scenario.people.push(PersonSpec {
id,
@ -228,7 +210,7 @@ impl SpawnOverTime {
}
}
if let Some(goal) = self.goal.pick_walking_goal(map, &neighborhoods, rng, timer) {
if let Some(goal) = self.goal.pick_walking_goal(map, rng, timer) {
if start_spot == goal {
timer.warn("Skipping walking trip between same two buildings".to_string());
return;
@ -272,7 +254,6 @@ impl BorderSpawnOverTime {
&self,
rng: &mut XorShiftRng,
scenario: &mut Scenario,
neighborhoods: &HashMap<String, FullNeighborhoodInfo>,
map: &Map,
timer: &mut Timer,
) {
@ -295,7 +276,7 @@ impl BorderSpawnOverTime {
for _ in 0..self.num_peds {
let depart = rand_time(rng, self.start_time, self.stop_time);
let id = PersonID(scenario.people.len());
if let Some(goal) = self.goal.pick_walking_goal(map, &neighborhoods, rng, timer) {
if let Some(goal) = self.goal.pick_walking_goal(map, rng, timer) {
if rng.gen_bool(self.percent_use_transit) {
// TODO This throws away some work. It also sequentially does expensive
// work right here.
@ -338,16 +319,12 @@ impl BorderSpawnOverTime {
constraints: PathConstraints,
rng: &mut XorShiftRng,
scenario: &mut Scenario,
neighborhoods: &HashMap<String, FullNeighborhoodInfo>,
map: &Map,
timer: &mut Timer,
) {
for _ in 0..num {
let depart = rand_time(rng, self.start_time, self.stop_time);
if let Some(goal) =
self.goal
.pick_driving_goal(constraints, map, &neighborhoods, rng, timer)
{
if let Some(goal) = self.goal.pick_driving_goal(constraints, map, rng, timer) {
let id = PersonID(scenario.people.len());
scenario.people.push(PersonSpec {
id,
@ -369,7 +346,7 @@ impl BorderSpawnOverTime {
#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum OriginDestination {
Neighborhood(String),
Anywhere,
EndOfRoad(DirectedRoadID),
GotoBldg(BuildingID),
}
@ -379,13 +356,12 @@ impl OriginDestination {
&self,
constraints: PathConstraints,
map: &Map,
neighborhoods: &HashMap<String, FullNeighborhoodInfo>,
rng: &mut XorShiftRng,
timer: &mut Timer,
) -> Option<DrivingGoal> {
match self {
OriginDestination::Neighborhood(ref n) => Some(DrivingGoal::ParkNear(
*neighborhoods[n].buildings.choose(rng).unwrap(),
OriginDestination::Anywhere => Some(DrivingGoal::ParkNear(
map.all_buildings().choose(rng).unwrap().id,
)),
OriginDestination::GotoBldg(b) => Some(DrivingGoal::ParkNear(*b)),
OriginDestination::EndOfRoad(dr) => {
@ -404,13 +380,12 @@ impl OriginDestination {
fn pick_walking_goal(
&self,
map: &Map,
neighborhoods: &HashMap<String, FullNeighborhoodInfo>,
rng: &mut XorShiftRng,
timer: &mut Timer,
) -> Option<SidewalkSpot> {
match self {
OriginDestination::Neighborhood(ref n) => Some(SidewalkSpot::building(
*neighborhoods[n].buildings.choose(rng).unwrap(),
OriginDestination::Anywhere => Some(SidewalkSpot::building(
map.all_buildings().choose(rng).unwrap().id,
map,
)),
OriginDestination::EndOfRoad(dr) => {