Skip to content

Commit

Permalink
Overhaul LTN edits, to use map_model's MapEdits system. #1079
Browse files Browse the repository at this point in the history
1) Move a bunch of types into map_model, adding them to the edit
  commands (in a totally backwards incompatible way)

2) Rip out the ad-hoc proposal and undo system in the LTN tool, and just
   use MapEdits
  • Loading branch information
dabreegster committed May 1, 2023
1 parent b4ae5f1 commit 7c5bac1
Show file tree
Hide file tree
Showing 44 changed files with 1,035 additions and 1,186 deletions.
31 changes: 15 additions & 16 deletions apps/ltn/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ use map_gui::render::{DrawMap, DrawOptions};
use map_gui::tools::CameraState;
use map_gui::tools::DrawSimpleRoadLabels;
use map_gui::{AppLike, ID};
use map_model::{osm, CrossingType, IntersectionID, Map, RoutingParams};
use map_model::{osm, CrossingType, FilterType, IntersectionID, Map, MapEdits, RoutingParams};
use widgetry::tools::URLManager;
use widgetry::{Canvas, Drawable, EventCtx, GfxCtx, SharedAppState, State, Warper};

use crate::logic::Partitioning;
use crate::{logic, pages, render, Edits, FilterType, NeighbourhoodID};
use crate::{logic, pages, render, NeighbourhoodID};

pub type Transition = widgetry::Transition<App>;

Expand Down Expand Up @@ -62,18 +62,12 @@ impl PerMap {
// Do this before creating the default partitioning. Non-driveable roads in OSM get turned
// into driveable roads and a filter here, and we want the partitioning to "see" those
// roads.
let edits = logic::transform_existing_filters(&mut map, timer);
let mut proposals = crate::save::Proposals::new(&map, edits, timer);
logic::transform_existing(&mut map, timer);
let proposals = crate::save::Proposals::new(&map, timer);

let mut routing_params_before_changes = map.routing_params().clone();
proposals
.current_proposal
.edits
.update_routing_params(&mut routing_params_before_changes);
let routing_params_before_changes = map.routing_params_respecting_modal_filters();

let draw_all_filters = proposals.current_proposal.edits.draw(ctx, &map);

logic::populate_existing_crossings(&map, &mut proposals.current_proposal.edits);
let draw_all_filters = render::render_modal_filters(ctx, &map);

// Create DrawMap after transform_existing_filters, which modifies road widths
let draw_map = DrawMap::new(ctx, &map, opts, cs, timer);
Expand Down Expand Up @@ -281,11 +275,8 @@ impl App {
g.redraw(&self.per_map.draw_map.draw_all_building_outlines);
}

pub fn edits(&self) -> &Edits {
&self.per_map.proposals.current_proposal.edits
}
pub fn partitioning(&self) -> &Partitioning {
&self.per_map.proposals.current_proposal.partitioning
&self.per_map.proposals.get_current().partitioning
}

pub fn calculate_draw_all_local_road_labels(&mut self, ctx: &mut EventCtx) {
Expand All @@ -298,6 +289,14 @@ impl App {
));
}
}

pub fn apply_edits(&mut self, mut edits: MapEdits) {
// This may modify edits_name
self.per_map.proposals.before_edit(&mut edits);
self.per_map
.map
.must_apply_edits(edits, &mut Timer::throwaway());
}
}

struct SimpleWarper {
Expand Down
4 changes: 2 additions & 2 deletions apps/ltn/src/components/appwide_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl AppwidePanel {

fn launch_impact(ctx: &mut EventCtx, app: &mut App) -> Transition {
if &app.per_map.impact.map == app.per_map.map.get_name()
&& app.per_map.impact.change_key == app.edits().get_change_key()
&& app.per_map.impact.map_edit_key == app.per_map.map.get_edits_change_key()
{
return Transition::Replace(pages::ShowImpactResults::new_state(ctx, app));
}
Expand Down Expand Up @@ -256,7 +256,7 @@ fn make_left_panel(ctx: &mut EventCtx, app: &App, top_panel: &Panel, mode: Mode)
.build_widget(ctx, "hide proposals")
.align_right(),
);
col.push(app.per_map.proposals.to_widget_expanded(ctx, app));
col.push(app.per_map.proposals.to_widget_expanded(ctx));
} else {
col.push(
ctx.style()
Expand Down
14 changes: 7 additions & 7 deletions apps/ltn/src/components/layers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use geom::Polygon;
use map_gui::colors::ColorScheme;
use map_model::CrossingType;
use map_model::{CrossingType, FilterType};
use widgetry::tools::ColorLegend;
use widgetry::{
ButtonBuilder, Color, ControlState, EdgeInsets, EventCtx, GeomBatch, GfxCtx,
Expand All @@ -9,8 +9,8 @@ use widgetry::{
};

use crate::components::Mode;
use crate::render::colors;
use crate::{pages, App, FilterType, Transition};
use crate::render::{colors, filter_svg_path};
use crate::{pages, App, Transition};

// Partly copied from ungap/layers.s

Expand Down Expand Up @@ -299,19 +299,19 @@ impl Mode {
Widget::row(vec!["Cells:".text_widget(ctx), color_grid(ctx)]),
Widget::row(vec![
"Modal filters:".text_widget(ctx),
Image::from_path(FilterType::WalkCycleOnly.svg_path())
Image::from_path(filter_svg_path(FilterType::WalkCycleOnly))
.untinted()
.dims(30.0)
.into_widget(ctx),
Image::from_path(FilterType::NoEntry.svg_path())
Image::from_path(filter_svg_path(FilterType::NoEntry))
.untinted()
.dims(30.0)
.into_widget(ctx),
Image::from_path(FilterType::BusGate.svg_path())
Image::from_path(filter_svg_path(FilterType::BusGate))
.untinted()
.dims(30.0)
.into_widget(ctx),
Image::from_path(FilterType::SchoolStreet.svg_path())
Image::from_path(filter_svg_path(FilterType::SchoolStreet))
.untinted()
.dims(30.0)
.into_widget(ctx),
Expand Down
93 changes: 40 additions & 53 deletions apps/ltn/src/export.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
use anyhow::Result;
use geo::MapCoordsInPlace;
use geojson::{Feature, FeatureCollection, GeoJson, Value};

use geom::{PolyLine, Pt2D};
use osm2streets::Direction;

use crate::{render, App, Neighbourhood};

pub fn geojson_string(app: &App) -> Result<String> {
use geo::MapCoordsInPlace;
use geojson::{Feature, FeatureCollection, GeoJson, Geometry, Value};

let map = &app.per_map.map;
let gps_bounds = Some(map.get_gps_bounds());
let mut features = Vec::new();

// All neighbourhood boundaries
for (id, info) in app.partitioning().all_neighbourhoods() {
let mut feature = Feature::from(info.block.polygon.to_geojson(None));
let mut feature = Feature::from(info.block.polygon.to_geojson(gps_bounds));
feature.set_property("type", "neighbourhood");
features.push(feature);

// Cells per neighbourhood
let render_cells = render::RenderCells::new(map, &Neighbourhood::new(app, *id));
for (idx, multipolygon) in render_cells.to_multipolygons().into_iter().enumerate() {
for (idx, mut multipolygon) in render_cells.to_multipolygons().into_iter().enumerate() {
// Transform to WGS84
multipolygon.map_coords_in_place(|c| {
let gps = Pt2D::new(c.x, c.y).to_gps(map.get_gps_bounds());
(gps.x(), gps.y()).into()
});
let mut feature = Feature::from(Value::from(&multipolygon));
feature.set_property("type", "cell");
feature.set_property("fill", render_cells.colors[idx].as_hex());
Expand All @@ -29,82 +34,64 @@ pub fn geojson_string(app: &App) -> Result<String> {
}

// All modal filters
for (r, filter) in &app.edits().roads {
let road = map.get_r(*r);
for (road, filter) in map.all_roads_with_modal_filter() {
if let Ok((pt, angle)) = road.center_pts.dist_along(filter.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::from(pl.to_geojson(None));
let mut feature = Feature::from(pl.to_geojson(gps_bounds));
feature.set_property("type", "road filter");
feature.set_property("filter_type", format!("{:?}", filter.filter_type));
feature.set_property("user_modified", filter.user_modified);
feature.set_property("stroke", "red");
features.push(feature);
}
}
for (_, filter) in &app.edits().intersections {
let pl = filter.geometry(map).to_polyline();
let mut feature = Feature::from(pl.to_geojson(None));
feature.set_property("type", "diagonal filter");
feature.set_property("filter_type", format!("{:?}", filter.filter_type));
feature.set_property("stroke", "red");
features.push(feature);
for i in map.all_intersections() {
if let Some(ref filter) = i.modal_filter {
let pl = filter.geometry(map).to_polyline();
let mut feature = Feature::from(pl.to_geojson(gps_bounds));
feature.set_property("type", "diagonal filter");
feature.set_property("filter_type", format!("{:?}", filter.filter_type));
feature.set_property("stroke", "red");
features.push(feature);
}
}

for r in app.edits().one_ways.keys() {
let road = app.per_map.map.get_r(*r);
let mut feature = Feature::from(road.center_pts.to_geojson(None));
feature.set_property("type", "one-way change");
feature.set_property(
"direction",
match road.oneway_for_driving() {
Some(Direction::Fwd) => "one-way forwards",
Some(Direction::Back) => "one-way backwards",
None => "two-ways",
},
);
feature.set_property("stroke", "blue");
features.push(feature);
// TODO This includes all one-ways, not just changed ones. Weird?
for road in map.all_roads() {
if crate::is_driveable(road, map) {
let mut feature = Feature::from(road.center_pts.to_geojson(gps_bounds));
feature.set_property("type", "direction");
feature.set_property(
"direction",
match road.oneway_for_driving() {
Some(Direction::Fwd) => "one-way forwards",
Some(Direction::Back) => "one-way backwards",
None => "two-ways",
},
);
feature.set_property("stroke", "blue");
features.push(feature);
}
}

for (r, list) in &app.edits().crossings {
let road = app.per_map.map.get_r(*r);
for crossing in list {
for road in map.all_roads() {
for crossing in &road.crossings {
let mut feature = Feature::from(
road.center_pts
.must_dist_along(crossing.dist)
.0
.to_geojson(None),
.to_geojson(gps_bounds),
);
feature.set_property("type", "crossing");
feature.set_property("crossing_type", format!("{:?}", crossing.kind));
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, Point
let mut geom: geo::Geometry = feature.geometry.take().unwrap().value.try_into()?;

geom.map_coords_in_place(|c| {
let gps = Pt2D::new(c.x, c.y).to_gps(gps_bounds);
(gps.x(), gps.y()).into()
});

// geo to geojson
feature.geometry = Some(Geometry {
bbox: None,
value: Value::from(&geom),
foreign_members: None,
});
}

let gj = GeoJson::FeatureCollection(FeatureCollection {
features,
bbox: None,
Expand Down
Loading

0 comments on commit 7c5bac1

Please sign in to comment.