1use std::collections::HashMap;
2use std::io::Write;
3
4use abstio::{CityName, MapName};
5use abstutil::{Tags, Timer};
6use geom::{
7 Circle, Distance, FindClosest, GPSBounds, HashablePt2D, LonLat, PolyLine, Polygon, Pt2D,
8};
9use osm2streets::{osm, IntersectionControl, IntersectionID, IntersectionKind, Road, RoadID};
10use raw_map::{RawBuilding, RawMap};
11use widgetry::mapspace::{ObjectID, World};
12use widgetry::{Color, EventCtx, GeomBatch, Key};
13
14const INTERSECTION_RADIUS: Distance = Distance::const_meters(2.5);
15const BUILDING_LENGTH: Distance = Distance::const_meters(30.0);
16
17pub struct Model {
19 pub map: RawMap,
22 showing_pts: Option<RoadID>,
23 pub world: World<ID>,
24
25 pub include_bldgs: bool,
26 pub intersection_geom: bool,
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
30pub enum ID {
31 Building(osm::OsmID),
32 Intersection(IntersectionID),
33 Road(RoadID),
34 RoadPoint(RoadID, usize),
35}
36impl ObjectID for ID {}
37
38impl Model {
40 pub fn blank() -> Model {
41 Model {
42 map: RawMap::blank(MapName {
43 city: CityName {
44 country: String::new(),
45 city: String::new(),
46 },
47 map: String::new(),
48 }),
49 showing_pts: None,
50
51 include_bldgs: false,
52 world: World::new(),
53 intersection_geom: false,
54 }
55 }
56
57 pub fn from_map(ctx: &EventCtx, map: RawMap, include_bldgs: bool, timer: &mut Timer) -> Model {
58 let mut model = Model::blank();
59 model.include_bldgs = include_bldgs;
60 model.map = map;
61 model.recreate_world(ctx, timer);
62 model
63 }
64
65 pub fn recreate_world(&mut self, ctx: &EventCtx, timer: &mut Timer) {
66 self.showing_pts = None;
67 self.world = World::new();
68
69 if self.include_bldgs {
70 for id in self.map.buildings.keys().cloned().collect::<Vec<_>>() {
71 self.bldg_added(ctx, id);
72 }
73 }
74 timer.start_iter(
75 "fill out world with intersections",
76 self.map.streets.intersections.len(),
77 );
78 for id in self
79 .map
80 .streets
81 .intersections
82 .keys()
83 .cloned()
84 .collect::<Vec<_>>()
85 {
86 timer.next();
87 self.intersection_added(ctx, id);
88 }
89 timer.start_iter("fill out world with roads", self.map.streets.roads.len());
90 for id in self.map.streets.roads.keys().cloned().collect::<Vec<_>>() {
91 timer.next();
92 self.road_added(ctx, id);
93 }
94
95 self.world.initialize_hover(ctx);
96 }
98}
99
100impl Model {
102 pub fn export_to_osm(&mut self) {
103 dump_to_osm(&self.map).unwrap();
104 }
105
106 pub fn set_boundary(&mut self, ctx: &EventCtx, top_left: Pt2D, bottom_right: Pt2D) {
107 for b in self.map.buildings.values_mut() {
109 b.polygon = b.polygon.translate(-top_left.x(), -top_left.y());
110 }
111 for i in self.map.streets.intersections.values_mut() {
112 i.polygon = i.polygon.translate(-top_left.x(), -top_left.y());
113 }
114 for r in self.map.streets.roads.values_mut() {
115 r.reference_line = PolyLine::must_new(
116 r.reference_line
117 .points()
118 .iter()
119 .map(|pt| pt.offset(-top_left.x(), -top_left.y()))
120 .collect(),
121 );
122 r.update_center_line(self.map.streets.config.driving_side);
123 }
124 let pt1 = Pt2D::new(0.0, 0.0);
125 let pt2 = bottom_right.offset(-top_left.x(), -top_left.y());
126
127 self.map.streets.boundary_polygon = Polygon::rectangle_two_corners(pt1, pt2).unwrap();
128
129 let mut seattle_bounds = GPSBounds::new();
131 seattle_bounds.update(LonLat::new(-122.453224, 47.723277));
132 seattle_bounds.update(LonLat::new(-122.240505, 47.495342));
133
134 self.map.streets.gps_bounds = GPSBounds::new();
135 self.map
136 .streets
137 .gps_bounds
138 .update(pt1.to_gps(&seattle_bounds));
139 self.map
140 .streets
141 .gps_bounds
142 .update(pt2.to_gps(&seattle_bounds));
143
144 self.recreate_world(ctx, &mut Timer::throwaway());
145 }
146}
147
148impl Model {
150 fn intersection_added(&mut self, ctx: &EventCtx, id: IntersectionID) {
151 let i = &self.map.streets.intersections[&id];
152 let color = if i.kind == IntersectionKind::MapEdge {
153 Color::BLUE
154 } else {
155 match i.control {
156 IntersectionControl::Signalled => Color::GREEN,
157 IntersectionControl::Signed | IntersectionControl::Uncontrolled => Color::RED,
158 IntersectionControl::Construction => Color::ORANGE,
159 }
160 };
161
162 let poly =
163 if self.intersection_geom && !self.map.streets.roads_per_intersection(id).is_empty() {
164 self.map.streets.intersections[&id].polygon.clone()
165 } else {
166 Circle::new(i.polygon.center(), INTERSECTION_RADIUS).to_polygon()
167 };
168
169 self.world
170 .add(ID::Intersection(id))
171 .hitbox(poly)
172 .zorder(1)
173 .draw_color(color)
174 .hover_alpha(0.5)
175 .draggable()
176 .hotkey(Key::R, "start a road here")
177 .hotkey(Key::Backspace, "delete")
178 .hotkey(Key::T, "toggle stop sign / traffic signal")
179 .hotkey(Key::P, "debug intersection geometry")
180 .hotkey(Key::D, "debug in OSM")
181 .build(ctx);
182 }
183
184 pub fn create_i(&mut self, ctx: &EventCtx, point: Pt2D) {
185 let id = self.map.streets.insert_intersection(
186 Vec::new(),
187 point,
188 IntersectionKind::Intersection,
190 IntersectionControl::Signed,
191 );
192 self.intersection_added(ctx, id);
193 }
194
195 pub fn move_i(&mut self, ctx: &EventCtx, id: IntersectionID, point: Pt2D) {
196 self.world.delete_before_replacement(ID::Intersection(id));
197
198 let i = self.map.streets.intersections.get_mut(&id).unwrap();
199 let old_center = i.polygon.center();
200 i.polygon = i
201 .polygon
202 .translate(point.x() - old_center.x(), point.y() - old_center.y());
203
204 let mut fixed = Vec::new();
206 for r in &self.map.streets.intersections[&id].roads {
207 fixed.push(*r);
208 let road = self.map.streets.roads.get_mut(r).unwrap();
209 let mut pts = road.reference_line.clone().into_points();
210 if road.src_i == id {
211 pts[0] = point;
212 } else {
213 assert_eq!(road.dst_i, id);
214 *pts.last_mut().unwrap() = point;
215 }
216 road.reference_line = PolyLine::must_new(pts);
218 road.update_center_line(self.map.streets.config.driving_side);
219 }
220
221 for r in fixed {
222 self.road_deleted(r);
223 self.road_added(ctx, r);
224 }
225 self.intersection_added(ctx, id);
226 }
227
228 pub fn delete_i(&mut self, id: IntersectionID) {
229 if !self.map.streets.intersections[&id].roads.is_empty() {
230 error!("Can't delete intersection used by roads");
231 return;
232 }
233 self.map.streets.remove_intersection(id);
234 self.world.delete(ID::Intersection(id));
235 }
236
237 pub fn toggle_i(&mut self, ctx: &EventCtx, id: IntersectionID) {
238 self.world.delete_before_replacement(ID::Intersection(id));
239
240 let i = self.map.streets.intersections.get_mut(&id).unwrap();
241 if i.control == IntersectionControl::Signalled {
242 i.control = IntersectionControl::Signed;
243 } else if i.control == IntersectionControl::Signed {
244 i.control = IntersectionControl::Signalled;
245 }
246
247 self.intersection_added(ctx, id);
248 }
249
250 pub fn show_intersection_geometry(&mut self, ctx: &mut EventCtx, show: bool) {
251 self.intersection_geom = show;
252
253 ctx.loading_screen("show intersection geometry", |ctx, timer| {
254 if self.intersection_geom {
255 }
257
258 timer.start_iter(
259 "intersection geometry",
260 self.map.streets.intersections.len(),
261 );
262 for id in self
263 .map
264 .streets
265 .intersections
266 .keys()
267 .cloned()
268 .collect::<Vec<_>>()
269 {
270 timer.next();
271 self.world.delete_before_replacement(ID::Intersection(id));
272 self.intersection_added(ctx, id);
273 }
274 });
275 }
276}
277
278impl Model {
280 pub fn road_added(&mut self, ctx: &EventCtx, id: RoadID) {
281 let road = &self.map.streets.roads[&id];
282 let center = road.center_line.clone();
283 let total_width = road.total_width();
284 let hitbox = center.make_polygons(total_width);
285 let mut draw = GeomBatch::new();
286 draw.push(
287 if road.internal_junction_road {
288 Color::PINK
289 } else {
290 Color::grey(0.8)
291 },
292 hitbox.clone(),
293 );
294 if let Some(outline) = center.to_thick_boundary(total_width, Distance::meters(1.0)) {
295 draw.push(Color::BLACK, outline);
296 }
297
298 self.world
299 .add(ID::Road(id))
300 .hitbox(hitbox)
301 .zorder(0)
302 .draw(draw)
303 .hover_alpha(0.5)
304 .clickable()
305 .hotkey(Key::Backspace, "delete")
306 .hotkey(Key::P, "insert a new point here")
307 .hotkey(Key::X, "remove interior points")
308 .hotkey(Key::M, "merge")
309 .hotkey(Key::J, "mark/unmark as a junction")
310 .hotkey(Key::D, "debug in OSM")
311 .build(ctx);
312 }
313
314 pub fn road_deleted(&mut self, id: RoadID) {
315 self.world.delete(ID::Road(id));
316 }
317
318 pub fn create_r(&mut self, ctx: &EventCtx, i1: IntersectionID, i2: IntersectionID) {
319 if self
321 .map
322 .streets
323 .roads
324 .values()
325 .any(|r| (r.src_i == i1 && r.dst_i == i2) || (r.src_i == i2 && r.dst_i == i1))
326 {
327 error!("Road already exists");
328 return;
329 }
330
331 let mut osm_tags = Tags::empty();
332 osm_tags.insert("highway", "residential");
333 osm_tags.insert("parking:both:lane", "parallel");
334 osm_tags.insert("sidewalk", "both");
335 osm_tags.insert("lanes", "2");
336 osm_tags.insert("name", "Streety McStreetFace");
338 osm_tags.insert("maxspeed", "25 mph");
339
340 let reference_line = match PolyLine::new(vec![
341 self.map.streets.intersections[&i1].polygon.center(),
342 self.map.streets.intersections[&i2].polygon.center(),
343 ]) {
344 Ok(pl) => pl,
345 Err(err) => {
346 error!("Can't create road: {err}");
347 return;
348 }
349 };
350
351 self.world.delete_before_replacement(ID::Intersection(i1));
352 self.world.delete_before_replacement(ID::Intersection(i2));
353
354 let id = self.map.streets.next_road_id();
355 self.map.streets.insert_road(Road::new(
356 id,
357 Vec::new(),
358 i1,
359 i2,
360 reference_line,
361 osm_tags,
362 &self.map.streets.config,
363 ));
364 self.road_added(ctx, id);
365
366 self.intersection_added(ctx, i1);
367 self.intersection_added(ctx, i2);
368 }
369
370 pub fn delete_r(&mut self, ctx: &EventCtx, id: RoadID) {
371 self.stop_showing_pts(id);
372 self.road_deleted(id);
373 let road = self.map.streets.remove_road(id);
374 self.world
375 .delete_before_replacement(ID::Intersection(road.src_i));
376 self.world
377 .delete_before_replacement(ID::Intersection(road.dst_i));
378
379 self.intersection_added(ctx, road.src_i);
380 self.intersection_added(ctx, road.dst_i);
381 }
382
383 pub fn show_r_points(&mut self, ctx: &EventCtx, id: RoadID) {
384 if self.showing_pts == Some(id) {
385 return;
386 }
387 if let Some(other) = self.showing_pts {
388 self.stop_showing_pts(other);
389 }
390 self.showing_pts = Some(id);
391
392 let r = &self.map.streets.roads[&id];
393 for (idx, pt) in r.reference_line.points().iter().enumerate() {
394 if idx != 0 && idx != r.reference_line.points().len() - 1 {
396 self.world
397 .add(ID::RoadPoint(id, idx))
398 .hitbox(Circle::new(*pt, INTERSECTION_RADIUS / 2.0).to_polygon())
399 .zorder(3)
400 .draw_color(Color::GREEN)
401 .hover_alpha(0.5)
402 .draggable()
403 .hotkey(Key::Backspace, "delete")
404 .build(ctx);
405 }
406 }
407 }
408
409 pub fn stop_showing_pts(&mut self, id: RoadID) {
410 if self.showing_pts != Some(id) {
411 return;
412 }
413 self.showing_pts = None;
414 for idx in 1..=self.map.streets.roads[&id].reference_line.points().len() - 2 {
415 self.world.delete(ID::RoadPoint(id, idx));
416 }
417 }
418
419 pub fn move_r_pt(&mut self, ctx: &EventCtx, id: RoadID, idx: usize, point: Pt2D) {
420 assert_eq!(self.showing_pts, Some(id));
421 self.showing_pts = None;
423 for idx in 1..=self.map.streets.roads[&id].reference_line.points().len() - 2 {
424 self.world.delete_before_replacement(ID::RoadPoint(id, idx));
425 }
426
427 self.road_deleted(id);
428 let endpts = self.map.streets.roads[&id].endpoints();
429 self.world
430 .delete_before_replacement(ID::Intersection(endpts[0]));
431 self.world
432 .delete_before_replacement(ID::Intersection(endpts[1]));
433
434 let road = self.map.streets.roads.get_mut(&id).unwrap();
435 let mut pts = road.reference_line.clone().into_points();
436 pts[idx] = point;
437 road.reference_line = PolyLine::must_new(pts);
438 road.update_center_line(self.map.streets.config.driving_side);
439
440 self.road_added(ctx, id);
441 self.intersection_added(ctx, endpts[0]);
442 self.intersection_added(ctx, endpts[1]);
443 self.show_r_points(ctx, id);
444 }
445
446 fn change_r_points<F: FnMut(&mut Vec<Pt2D>)>(
447 &mut self,
448 ctx: &EventCtx,
449 id: RoadID,
450 mut transform: F,
451 ) {
452 assert_eq!(self.showing_pts, Some(id));
453
454 self.stop_showing_pts(id);
455 self.road_deleted(id);
456 let endpts = self.map.streets.roads[&id].endpoints();
457 self.world
458 .delete_before_replacement(ID::Intersection(endpts[0]));
459 self.world
460 .delete_before_replacement(ID::Intersection(endpts[1]));
461
462 let road = self.map.streets.roads.get_mut(&id).unwrap();
463 let mut pts = road.reference_line.clone().into_points();
464 transform(&mut pts);
465 road.reference_line = PolyLine::must_new(pts);
466 road.update_center_line(self.map.streets.config.driving_side);
467
468 self.road_added(ctx, id);
469 self.intersection_added(ctx, endpts[0]);
470 self.intersection_added(ctx, endpts[1]);
471 self.show_r_points(ctx, id);
472 }
473
474 pub fn delete_r_pt(&mut self, ctx: &EventCtx, id: RoadID, idx: usize) {
475 self.change_r_points(ctx, id, |pts| {
476 pts.remove(idx);
477 });
478 }
479
480 pub fn insert_r_pt(&mut self, ctx: &EventCtx, id: RoadID, pt: Pt2D) {
481 let mut closest = FindClosest::new();
482 self.change_r_points(ctx, id, move |pts| {
483 for (idx, pair) in pts.windows(2).enumerate() {
484 closest.add(idx + 1, &[pair[0], pair[1]]);
485 }
486 if let Some((idx, _)) = closest.closest_pt(pt, Distance::meters(5.0)) {
487 pts.insert(idx, pt);
488 } else {
489 warn!("Couldn't figure out where to insert new point");
490 }
491 });
492 }
493
494 pub fn clear_r_pts(&mut self, ctx: &EventCtx, id: RoadID) {
495 self.change_r_points(ctx, id, |pts| {
496 *pts = vec![pts[0], *pts.last().unwrap()];
497 });
498 }
499
500 pub fn merge_r(&mut self, ctx: &EventCtx, id: RoadID) {
501 if let Err(err) = self.map.streets.collapse_short_road(id) {
502 warn!("Can't merge this road: {}", err);
503 return;
504 }
505 info!("Merged {id}");
506
507 self.recreate_world(ctx, &mut Timer::throwaway());
509 }
510
511 pub fn toggle_junction(&mut self, ctx: &EventCtx, id: RoadID) {
512 self.road_deleted(id);
513
514 let road = self.map.streets.roads.get_mut(&id).unwrap();
515 road.internal_junction_road = !road.internal_junction_road;
516
517 self.road_added(ctx, id);
518 }
519}
520
521impl Model {
523 fn bldg_added(&mut self, ctx: &EventCtx, id: osm::OsmID) {
524 let b = &self.map.buildings[&id];
525 self.world
526 .add(ID::Building(id))
527 .hitbox(b.polygon.clone())
528 .zorder(2)
529 .draw_color(Color::BLUE)
530 .hover_alpha(0.5)
531 .draggable()
532 .hotkey(Key::Backspace, "delete")
533 .build(ctx);
534 }
535
536 pub fn create_b(&mut self, ctx: &EventCtx, center: Pt2D) -> ID {
537 let id = osm::OsmID::Way(osm::WayID(-1 * self.map.buildings.len() as i64));
539 self.map.buildings.insert(
540 id,
541 RawBuilding {
542 polygon: Polygon::rectangle_centered(center, BUILDING_LENGTH, BUILDING_LENGTH),
543 osm_tags: Tags::empty(),
544 public_garage_name: None,
545 num_parking_spots: 0,
546 amenities: Vec::new(),
547 },
548 );
549 self.bldg_added(ctx, id);
550 ID::Building(id)
551 }
552
553 pub fn move_b(&mut self, ctx: &EventCtx, id: osm::OsmID, dx: f64, dy: f64) {
554 self.world.delete_before_replacement(ID::Building(id));
555
556 let b = self.map.buildings.get_mut(&id).unwrap();
557 b.polygon = b.polygon.translate(dx, dy);
558
559 self.bldg_added(ctx, id);
560 }
561
562 pub fn delete_b(&mut self, id: osm::OsmID) {
563 self.world.delete(ID::Building(id));
564 self.map.buildings.remove(&id).unwrap();
565 }
566}
567
568fn dump_to_osm(map: &RawMap) -> Result<(), std::io::Error> {
575 let mut f = fs_err::File::create("synthetic_export.osm")?;
576 writeln!(f, r#"<?xml version='1.0' encoding='UTF-8'?>"#)?;
577 writeln!(f, r#"<osm>"#)?;
578 writeln!(
579 f,
580 r#"<!-- If you couldn't tell, this is a fake .osm file not representing the real world. -->"#
581 )?;
582 let b = &map.streets.gps_bounds;
583 writeln!(
584 f,
585 r#" <bounds minlon="{}" maxlon="{}" minlat="{}" maxlat="{}"/>"#,
586 b.min_lon, b.max_lon, b.min_lat, b.max_lat
587 )?;
588
589 let mut pt_to_node: HashMap<HashablePt2D, osm::NodeID> = HashMap::new();
590 for r in map.streets.roads.values() {
591 for pt in r.reference_line.points() {
592 let id = osm::NodeID(pt_to_node.len() as i64);
593 pt_to_node.insert(pt.to_hashable(), id);
594 writeln!(
595 f,
596 r#" <node id="{}" lon="{}" lat="{}"/>"#,
597 id.0,
598 pt.x(),
599 pt.y()
600 )?;
601 }
602 }
603
604 for (id, r) in &map.streets.roads {
605 writeln!(f, r#" <way id="{}">"#, id.0)?;
606 for pt in r.reference_line.points() {
607 writeln!(
608 f,
609 r#" <nd ref="{}"/>"#,
610 pt_to_node[&pt.to_hashable()].0
611 )?;
612 }
613 if let Some(tags) = map.road_to_osm_tags(*id) {
615 for (k, v) in tags.inner() {
616 writeln!(f, r#" <tag k="{}" v="{}"/>"#, k, v)?;
617 }
618 }
619 writeln!(f, r#" </way>"#)?;
620 }
621 writeln!(f, r#"</osm>"#)?;
622 Ok(())
623}