1use abstio::MapName;
2use abstutil::Timer;
3use geom::{Duration, Pt2D, Time};
4use map_gui::colors::ColorScheme;
5use map_gui::load::MapLoader;
6use map_gui::options::Options;
7use map_gui::render::{DrawMap, DrawOptions};
8use map_gui::tools::CameraState;
9use map_gui::tools::DrawSimpleRoadLabels;
10use map_gui::{AppLike, ID};
11use map_model::{osm, CrossingType, FilterType, IntersectionID, Map, MapEdits, RoutingParams};
12use widgetry::tools::URLManager;
13use widgetry::{Canvas, Drawable, EventCtx, GfxCtx, SharedAppState, State, Warper};
14
15use crate::logic::Partitioning;
16use crate::{logic, pages, render, NeighbourhoodID};
17
18pub type Transition = widgetry::Transition<App>;
19
20pub struct App {
21 pub per_map: PerMap,
22 pub cs: ColorScheme,
23 pub opts: Options,
24
25 pub session: Session,
26}
27
28pub struct PerMap {
29 pub map: Map,
30 pub draw_map: DrawMap,
31
32 pub current_neighbourhood: Option<NeighbourhoodID>,
34
35 pub routing_params_before_changes: RoutingParams,
39 pub proposals: crate::save::Proposals,
40 pub impact: logic::Impact,
41
42 pub consultation: Option<NeighbourhoodID>,
43 pub consultation_id: Option<String>,
44
45 pub draw_all_filters: render::Toggle3Zoomed,
46 pub draw_major_road_labels: DrawSimpleRoadLabels,
47 pub draw_all_local_road_labels: Option<DrawSimpleRoadLabels>,
48 pub draw_poi_icons: Drawable,
49 pub draw_bus_routes: Drawable,
50 pub draw_turn_restrictions: Drawable,
51
52 pub current_trip_name: Option<String>,
53}
54
55impl PerMap {
56 fn new(
57 ctx: &mut EventCtx,
58 mut map: Map,
59 opts: &Options,
60 cs: &ColorScheme,
61 timer: &mut Timer,
62 ) -> Self {
63 logic::transform_existing(&mut map, timer);
67 let proposals = crate::save::Proposals::new(&map, timer);
68
69 let routing_params_before_changes = map.routing_params_respecting_modal_filters();
70
71 let draw_all_filters = render::render_modal_filters(ctx, &map);
72
73 let draw_map = DrawMap::new(ctx, &map, opts, cs, timer);
75 let draw_poi_icons = render::render_poi_icons(ctx, &map);
76 let draw_bus_routes = render::render_bus_routes(ctx, &map);
77 let draw_turn_restrictions = render::render_turn_restrictions(ctx, &map);
78
79 let per_map = Self {
80 map,
81 draw_map,
82
83 current_neighbourhood: None,
84
85 routing_params_before_changes,
86 proposals,
87 impact: logic::Impact::empty(ctx),
88
89 consultation: None,
90 consultation_id: None,
91
92 draw_all_filters,
93 draw_major_road_labels: DrawSimpleRoadLabels::empty(ctx),
94 draw_all_local_road_labels: None,
95 draw_poi_icons,
96 draw_bus_routes,
97 draw_turn_restrictions,
98
99 current_trip_name: None,
100 };
101
102 if !CameraState::load(ctx, per_map.map.get_name()) {
103 ctx.canvas.cam_zoom = ctx.canvas.min_zoom();
106 ctx.canvas
107 .center_on_map_pt(per_map.map.get_boundary_polygon().center());
108 }
109 per_map
110 }
111}
112
113pub struct Session {
114 pub edit_mode: pages::EditMode,
115 pub filter_type: FilterType,
116 pub crossing_type: CrossingType,
117
118 pub draw_neighbourhood_style: pages::PickAreaStyle,
121 pub main_road_penalty: f64,
123 pub show_walking_cycling_routes: bool,
124 pub add_intermediate_blocks: bool,
126
127 pub layers: crate::components::Layers,
129 pub manage_proposals: bool,
130}
131
132impl AppLike for App {
133 #[inline]
134 fn map(&self) -> &Map {
135 &self.per_map.map
136 }
137 #[inline]
138 fn cs(&self) -> &ColorScheme {
139 &self.cs
140 }
141 #[inline]
142 fn mut_cs(&mut self) -> &mut ColorScheme {
143 &mut self.cs
144 }
145 #[inline]
146 fn draw_map(&self) -> &DrawMap {
147 &self.per_map.draw_map
148 }
149 #[inline]
150 fn mut_draw_map(&mut self) -> &mut DrawMap {
151 &mut self.per_map.draw_map
152 }
153 #[inline]
154 fn opts(&self) -> &Options {
155 &self.opts
156 }
157 #[inline]
158 fn mut_opts(&mut self) -> &mut Options {
159 &mut self.opts
160 }
161
162 fn map_switched(&mut self, ctx: &mut EventCtx, map: Map, timer: &mut Timer) {
163 CameraState::save(ctx.canvas, self.per_map.map.get_name());
164 self.per_map = PerMap::new(ctx, map, &self.opts, &self.cs, timer);
165 self.per_map.draw_major_road_labels =
166 DrawSimpleRoadLabels::only_major_roads(ctx, self, render::colors::MAIN_ROAD_LABEL);
167 self.opts.units.metric = self.per_map.map.get_name().city.uses_metric();
168 }
169
170 fn draw_with_opts(&self, g: &mut GfxCtx, _l: DrawOptions) {
171 self.draw_with_layering(g, |_| {});
172 }
173 fn make_warper(
174 &mut self,
175 ctx: &EventCtx,
176 pt: Pt2D,
177 target_cam_zoom: Option<f64>,
178 _: Option<ID>,
179 ) -> Box<dyn State<App>> {
180 Box::new(SimpleWarper {
181 warper: Warper::new(ctx, pt, target_cam_zoom),
182 })
183 }
184
185 fn sim_time(&self) -> Time {
186 Time::START_OF_DAY
187 }
188
189 fn current_stage_and_remaining_time(&self, _: IntersectionID) -> (usize, Duration) {
190 (0, Duration::ZERO)
191 }
192}
193
194impl SharedAppState for App {
195 fn draw_default(&self, g: &mut GfxCtx) {
196 self.draw_with_opts(g, DrawOptions::new());
197 }
198
199 fn dump_before_abort(&self, canvas: &Canvas) {
200 CameraState::save(canvas, self.per_map.map.get_name());
201 }
202
203 fn before_quit(&self, canvas: &Canvas) {
204 CameraState::save(canvas, self.per_map.map.get_name());
205 }
206
207 fn free_memory(&mut self) {
208 self.per_map.draw_map.free_memory();
209 }
210}
211
212impl App {
213 pub fn new<F: 'static + Fn(&mut EventCtx, &mut App) -> Vec<Box<dyn State<App>>>>(
214 ctx: &mut EventCtx,
215 opts: Options,
216 map_name: MapName,
217 cam: Option<String>,
218 init_states: F,
219 ) -> (App, Vec<Box<dyn State<App>>>) {
220 abstutil::logger::setup();
221 ctx.canvas.settings = opts.canvas_settings.clone();
222
223 let session = Session {
224 edit_mode: pages::EditMode::Filters,
225 filter_type: FilterType::WalkCycleOnly,
226 crossing_type: CrossingType::Unsignalized,
227
228 draw_neighbourhood_style: pages::PickAreaStyle::Simple,
229 main_road_penalty: 1.0,
230 show_walking_cycling_routes: false,
231 add_intermediate_blocks: true,
232
233 layers: crate::components::Layers::new(ctx),
234 manage_proposals: false,
235 };
236
237 let cs = ColorScheme::new(ctx, opts.color_scheme);
238 let app = App {
239 per_map: PerMap::new(
241 ctx,
242 Map::almost_blank(),
243 &opts,
244 &cs,
245 &mut Timer::throwaway(),
246 ),
247 cs,
248 opts,
249 session,
250 };
251
252 let states = vec![MapLoader::new_state(
253 ctx,
254 &app,
255 map_name,
256 Box::new(move |ctx, app| {
257 URLManager::change_camera(ctx, cam.as_ref(), app.map().get_gps_bounds());
258 Transition::Clear(init_states(ctx, app))
259 }),
260 )];
261 (app, states)
262 }
263
264 pub fn draw_with_layering<F: Fn(&mut GfxCtx)>(&self, g: &mut GfxCtx, custom: F) {
266 g.clear(self.cs.void_background);
267 g.redraw(&self.per_map.draw_map.boundary_polygon);
268 g.redraw(&self.per_map.draw_map.draw_all_areas);
269 custom(g);
270 g.redraw(&self.per_map.draw_map.draw_all_unzoomed_parking_lots);
271 g.redraw(
272 &self
273 .per_map
274 .draw_map
275 .draw_all_unzoomed_roads_and_intersections,
276 );
277 g.redraw(&self.per_map.draw_map.draw_all_buildings);
278 g.redraw(&self.per_map.draw_map.draw_all_building_outlines);
279 }
280
281 pub fn partitioning(&self) -> &Partitioning {
282 &self.per_map.proposals.get_current().partitioning
283 }
284
285 pub fn calculate_draw_all_local_road_labels(&mut self, ctx: &mut EventCtx) {
286 if self.per_map.draw_all_local_road_labels.is_none() {
287 self.per_map.draw_all_local_road_labels = Some(DrawSimpleRoadLabels::new(
288 ctx,
289 self,
290 render::colors::LOCAL_ROAD_LABEL,
291 Box::new(|r| r.get_rank() == osm::RoadRank::Local && !r.is_light_rail()),
292 ));
293 }
294 }
295
296 pub fn apply_edits(&mut self, edits: MapEdits) {
297 self.per_map.proposals.before_edit(edits.clone());
299 self.per_map
300 .map
301 .must_apply_edits(edits, &mut Timer::throwaway());
302 }
303}
304
305struct SimpleWarper {
306 warper: Warper,
307}
308
309impl State<App> for SimpleWarper {
310 fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
311 if self.warper.event(ctx) {
312 Transition::Keep
313 } else {
314 Transition::Pop
315 }
316 }
317
318 fn draw(&self, _: &mut GfxCtx, _: &App) {}
319}