mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-28 08:53:26 +03:00
Start a simple GeoJSON export for LTNs. Cells are missing.
This commit is contained in:
parent
412e0d585d
commit
509217b024
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1248,6 +1248,7 @@ dependencies = [
|
||||
"downcast-rs",
|
||||
"enumset",
|
||||
"futures-channel",
|
||||
"geo",
|
||||
"geojson",
|
||||
"geom",
|
||||
"getrandom",
|
||||
|
@ -27,6 +27,7 @@ csv = "1.1.4"
|
||||
downcast-rs = "1.2.0"
|
||||
enumset = "1.0.3"
|
||||
futures-channel = { version = "0.3.12"}
|
||||
geo = "0.18"
|
||||
geojson = { version = "0.22.0", features = ["geo-types"] }
|
||||
geom = { path = "../geom" }
|
||||
getrandom = { version = "0.2.3", optional = true }
|
||||
|
@ -1,8 +1,10 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use abstutil::Timer;
|
||||
use geom::Distance;
|
||||
use map_gui::tools::{CityPicker, DrawRoadLabels, Navigator, URLManager};
|
||||
use geom::{Distance, PolyLine, Pt2D};
|
||||
use map_gui::tools::{CityPicker, DrawRoadLabels, Navigator, PopupMsg, URLManager};
|
||||
use widgetry::mapspace::{ToggleZoomed, World, WorldOutcome};
|
||||
use widgetry::{
|
||||
lctrl, Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, State, TextExt,
|
||||
@ -42,6 +44,10 @@ impl BrowseNeighborhoods {
|
||||
.align_right(),
|
||||
]),
|
||||
Toggle::checkbox(ctx, "highlight boundary roads", Key::H, true),
|
||||
ctx.style()
|
||||
.btn_outline
|
||||
.text("Export to GeoJSON")
|
||||
.build_def(ctx),
|
||||
]))
|
||||
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
||||
.build(ctx);
|
||||
@ -77,6 +83,18 @@ impl State<App> for BrowseNeighborhoods {
|
||||
"search" => {
|
||||
return Transition::Push(Navigator::new_state(ctx, app));
|
||||
}
|
||||
"Export to GeoJSON" => {
|
||||
return Transition::Push(match export_geojson(app) {
|
||||
Ok(path) => PopupMsg::new_state(
|
||||
ctx,
|
||||
"LTNs exported",
|
||||
vec![format!("Data exported to {}", path)],
|
||||
),
|
||||
Err(err) => {
|
||||
PopupMsg::new_state(ctx, "Export failed", vec![err.to_string()])
|
||||
}
|
||||
});
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@ -165,3 +183,100 @@ fn draw_boundary_roads(ctx: &EventCtx, app: &App) -> ToggleZoomed {
|
||||
}
|
||||
batch.build(ctx)
|
||||
}
|
||||
|
||||
fn export_geojson(app: &App) -> Result<String> {
|
||||
if cfg!(target_arch = "wasm32") {
|
||||
bail!("Export only supported in the installed version");
|
||||
}
|
||||
|
||||
use geo::algorithm::map_coords::MapCoordsInplace;
|
||||
use geojson::{Feature, FeatureCollection, GeoJson, Geometry, Value};
|
||||
use std::io::Write;
|
||||
|
||||
let map = &app.primary.map;
|
||||
let mut features = Vec::new();
|
||||
|
||||
// All neighborhood boundaries
|
||||
for (_, (block, color)) in &app.session.partitioning.neighborhoods {
|
||||
let mut feature = Feature {
|
||||
bbox: None,
|
||||
geometry: Some(block.polygon.to_geojson(None)),
|
||||
id: None,
|
||||
properties: None,
|
||||
foreign_members: None,
|
||||
};
|
||||
feature.set_property("type", "neighborhood");
|
||||
feature.set_property("fill", color.as_hex());
|
||||
features.push(feature);
|
||||
}
|
||||
|
||||
// TODO Cells per neighborhood -- contouring the gridded version is hard!
|
||||
|
||||
// All modal filters
|
||||
for (r, dist) in &app.session.modal_filters.roads {
|
||||
let road = map.get_r(*r);
|
||||
if let Ok((pt, angle)) = road.center_pts.dist_along(*dist) {
|
||||
let road_width = road.get_width();
|
||||
let pl = PolyLine::must_new(vec![
|
||||
pt.project_away(0.8 * road_width, angle.rotate_degs(90.0)),
|
||||
pt.project_away(0.8 * road_width, angle.rotate_degs(-90.0)),
|
||||
]);
|
||||
let mut feature = Feature {
|
||||
bbox: None,
|
||||
geometry: Some(pl.to_geojson(None)),
|
||||
id: None,
|
||||
properties: None,
|
||||
foreign_members: None,
|
||||
};
|
||||
feature.set_property("type", "road filter");
|
||||
feature.set_property("stroke", "red");
|
||||
features.push(feature);
|
||||
}
|
||||
}
|
||||
for (_, filter) in &app.session.modal_filters.intersections {
|
||||
let pl = filter.geometry(map).to_polyline();
|
||||
let mut feature = Feature {
|
||||
bbox: None,
|
||||
geometry: Some(pl.to_geojson(None)),
|
||||
id: None,
|
||||
properties: None,
|
||||
foreign_members: None,
|
||||
};
|
||||
feature.set_property("type", "diagonal filter");
|
||||
feature.set_property("stroke", "red");
|
||||
features.push(feature);
|
||||
}
|
||||
|
||||
// Transform to WGS84
|
||||
let gps_bounds = map.get_gps_bounds();
|
||||
for feature in &mut features {
|
||||
// geojson to geo
|
||||
// This could be a Polygon, MultiPolygon, LineString
|
||||
let mut geom: geo::Geometry<f64> = feature.geometry.take().unwrap().value.try_into()?;
|
||||
|
||||
geom.map_coords_inplace(|c| {
|
||||
let gps = Pt2D::new(c.0, c.1).to_gps(gps_bounds);
|
||||
(gps.x(), gps.y())
|
||||
});
|
||||
|
||||
// geo to geojson
|
||||
feature.geometry = Some(Geometry {
|
||||
bbox: None,
|
||||
value: Value::from(&geom),
|
||||
foreign_members: None,
|
||||
});
|
||||
}
|
||||
|
||||
let gj = GeoJson::FeatureCollection(FeatureCollection {
|
||||
features,
|
||||
bbox: None,
|
||||
foreign_members: None,
|
||||
});
|
||||
|
||||
// Don't use abstio::write_json; it writes to local storage in web, where we want to eventually
|
||||
// make the browser download something
|
||||
let path = format!("ltn_{}.geojson", map.get_name().map);
|
||||
let mut file = std::fs::File::create(&path)?;
|
||||
write!(file, "{}", serde_json::to_string_pretty(&gj)?)?;
|
||||
Ok(path)
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ impl DiagonalFilter {
|
||||
}
|
||||
|
||||
/// Physically where is the filter placed?
|
||||
fn geometry(&self, map: &Map) -> Line {
|
||||
pub fn geometry(&self, map: &Map) -> Line {
|
||||
let r1 = map.get_r(self.r1);
|
||||
let r2 = map.get_r(self.r2);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user