map_model/objects/
road.rs

1use std::collections::BTreeSet;
2use std::fmt;
3
4use anyhow::Result;
5use enumset::EnumSet;
6use serde::{Deserialize, Serialize};
7
8use abstutil::{deserialize_usize, serialize_usize, Tags};
9use geom::{Distance, PolyLine, Polygon, Speed};
10
11use crate::{
12    osm, AccessRestrictions, CommonEndpoint, CrossingType, Direction, DrivingSide, IntersectionID,
13    Lane, LaneID, LaneSpec, LaneType, Map, PathConstraints, RestrictionType, RoadFilter,
14    TransitStopID, Zone,
15};
16
17#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
18pub struct RoadID(
19    #[serde(
20        serialize_with = "serialize_usize",
21        deserialize_with = "deserialize_usize"
22    )]
23    pub usize,
24);
25
26impl fmt::Display for RoadID {
27    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28        write!(f, "Road #{}", self.0)
29    }
30}
31
32impl RoadID {
33    pub fn both_directions(self) -> Vec<DirectedRoadID> {
34        vec![
35            DirectedRoadID {
36                road: self,
37                dir: Direction::Fwd,
38            },
39            DirectedRoadID {
40                road: self,
41                dir: Direction::Back,
42            },
43        ]
44    }
45
46    pub fn both_sides(self) -> [RoadSideID; 2] {
47        [
48            RoadSideID {
49                road: self,
50                side: SideOfRoad::Right,
51            },
52            RoadSideID {
53                road: self,
54                side: SideOfRoad::Left,
55            },
56        ]
57    }
58}
59
60#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
61pub struct DirectedRoadID {
62    pub road: RoadID,
63    pub dir: Direction,
64}
65
66impl fmt::Display for DirectedRoadID {
67    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68        write!(f, "DirectedRoadID({}, {})", self.road.0, self.dir,)
69    }
70}
71
72impl DirectedRoadID {
73    pub fn src_i(self, map: &Map) -> IntersectionID {
74        let r = map.get_r(self.road);
75        if self.dir == Direction::Fwd {
76            r.src_i
77        } else {
78            r.dst_i
79        }
80    }
81
82    pub fn dst_i(self, map: &Map) -> IntersectionID {
83        let r = map.get_r(self.road);
84        if self.dir == Direction::Fwd {
85            r.dst_i
86        } else {
87            r.src_i
88        }
89    }
90
91    /// Strict for bikes. If there are bike lanes, not allowed to use other lanes.
92    pub fn lanes(self, constraints: PathConstraints, map: &Map) -> Vec<LaneID> {
93        let r = map.get_r(self.road);
94        constraints.filter_lanes(r.children(self.dir).iter().map(|(l, _)| *l).collect(), map)
95    }
96
97    /// Get the only sidewalk or shoulder on this side of the road, and panic otherwise.
98    pub fn must_get_sidewalk(self, map: &Map) -> LaneID {
99        let mut found = Vec::new();
100        for (l, lt) in map.get_r(self.road).children(self.dir) {
101            if lt.is_walkable() {
102                found.push(l);
103            }
104        }
105        if found.len() != 1 {
106            panic!(
107                "must_get_sidewalk broken by {} ({}). Found lanes {:?}",
108                self,
109                map.get_r(self.road).orig_id,
110                found
111            );
112        }
113        found[0]
114    }
115
116    /// Does this directed road have any lanes of a certain type?
117    pub fn has_lanes(self, lane_type: LaneType, map: &Map) -> bool {
118        for (_, lt) in map.get_r(self.road).children(self.dir) {
119            if lt == lane_type {
120                return true;
121            }
122        }
123        false
124    }
125}
126
127/// See https://wiki.openstreetmap.org/wiki/Forward_%26_backward,_left_%26_right.
128#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
129pub enum SideOfRoad {
130    Right,
131    Left,
132}
133
134#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
135pub struct RoadSideID {
136    pub road: RoadID,
137    pub side: SideOfRoad,
138}
139
140impl RoadSideID {
141    pub fn get_outermost_lane(self, map: &Map) -> &Lane {
142        let r = map.get_r(self.road);
143        match self.side {
144            SideOfRoad::Right => r.lanes.last().unwrap(),
145            SideOfRoad::Left => &r.lanes[0],
146        }
147    }
148
149    pub fn other_side(self) -> RoadSideID {
150        RoadSideID {
151            road: self.road,
152            side: if self.side == SideOfRoad::Left {
153                SideOfRoad::Right
154            } else {
155                SideOfRoad::Left
156            },
157        }
158    }
159}
160
161/// A Road represents a segment between exactly two Intersections. It contains Lanes as children.
162#[derive(Serialize, Deserialize, Clone, Debug)]
163pub struct Road {
164    pub id: RoadID,
165    pub osm_tags: Tags,
166    /// self is 'from'
167    pub turn_restrictions: Vec<(RestrictionType, RoadID)>,
168    /// self is 'from'. (via, to). Only BanTurns.
169    pub complicated_turn_restrictions: Vec<(RoadID, RoadID)>,
170    pub orig_id: OriginalRoad,
171    pub speed_limit: Speed,
172    pub access_restrictions: AccessRestrictions,
173    pub zorder: isize,
174    /// [-1.0, 1.0] theoretically, but in practice, about [-0.25, 0.25]. 0 is flat,
175    /// positive is uphill from src_i -> dst_i, negative is downhill.
176    pub percent_incline: f64,
177
178    /// Invariant: A road must contain at least one child. These are ordered from the left side of
179    /// the road to the right, with that orientation determined by the direction of `center_pts`.
180    pub lanes: Vec<Lane>,
181
182    /// The physical center of the road, including sidewalks, after trimming to account for the
183    /// intersection geometry. The order implies road orientation.
184    pub center_pts: PolyLine,
185    /// Like center_pts, but before any trimming for intersection geometry. This is preserved so
186    /// that when modifying road width, intersection polygons can be calculated correctly.
187    pub untrimmed_center_pts: PolyLine,
188    pub trim_start: Distance,
189    pub trim_end: Distance,
190
191    pub src_i: IntersectionID,
192    pub dst_i: IntersectionID,
193    /// Is there a tagged crosswalk near each end of the road?
194    pub crosswalk_forward: bool,
195    pub crosswalk_backward: bool,
196
197    /// Meaningless order
198    pub transit_stops: BTreeSet<TransitStopID>,
199
200    /// There's either a modal filter on this road or not
201    pub modal_filter: Option<RoadFilter>,
202
203    /// Some kind of modal filter or barrier this distance along center_pts.
204    pub barrier_nodes: Vec<Distance>,
205    /// Some kind of crossing this distance along center_pts.
206    // TODO Just use Crossing directly?
207    pub crossing_nodes: Vec<(Distance, CrossingType)>,
208    /// Sorted by increasing distance
209    pub crossings: Vec<Crossing>,
210}
211
212impl Road {
213    pub fn lane_specs(&self) -> Vec<LaneSpec> {
214        self.lanes
215            .iter()
216            .map(|l| LaneSpec {
217                lt: l.lane_type,
218                dir: l.dir,
219                width: l.width,
220                // TODO These get lost from osm2streets
221                allowed_turns: Default::default(),
222            })
223            .collect()
224    }
225
226    pub fn shift_from_left_side(&self, width_from_left_side: Distance) -> Result<PolyLine> {
227        self.center_pts
228            .shift_from_center(self.get_width(), width_from_left_side)
229    }
230
231    /// lane must belong to this road. Offset 0 is the centermost lane on each side of a road, then
232    /// it counts up from there. Note this is a different offset than `offset`!
233    pub(crate) fn dir_and_offset(&self, lane: LaneID) -> (Direction, usize) {
234        for dir in [Direction::Fwd, Direction::Back] {
235            if let Some(idx) = self.children(dir).iter().position(|pair| pair.0 == lane) {
236                return (dir, idx);
237            }
238        }
239        panic!("{} doesn't contain {}", self.id, lane);
240    }
241
242    pub fn parking_to_driving(&self, parking: LaneID) -> Option<LaneID> {
243        self.find_closest_lane(parking, |l| l.is_driving())
244    }
245
246    pub(crate) fn speed_limit_from_osm(&self) -> Speed {
247        if let Some(limit) = self.osm_tags.get("maxspeed") {
248            if let Some(speed) = if let Ok(kmph) = limit.parse::<f64>() {
249                Some(Speed::km_per_hour(kmph))
250            } else if let Some(mph) = limit
251                .strip_suffix(" mph")
252                .and_then(|x| x.parse::<f64>().ok())
253            {
254                Some(Speed::miles_per_hour(mph))
255            } else {
256                None
257            } {
258                if speed == Speed::ZERO {
259                    warn!("{} has a speed limit of 0", self.orig_id.osm_way_id);
260                    return Speed::miles_per_hour(1.0);
261                }
262                return speed;
263            }
264
265            // TODO Handle implicits, like PL:zone30
266        }
267
268        // These're half reasonable guesses. Better to explicitly tag in OSM.
269        if self
270            .osm_tags
271            .is_any(osm::HIGHWAY, vec!["primary", "secondary", "motorway_link"])
272        {
273            return Speed::miles_per_hour(40.0);
274        }
275        if self.osm_tags.is(osm::HIGHWAY, "living_street") {
276            // about 12mph
277            return Speed::km_per_hour(20.0);
278        }
279        if self.is_service() {
280            return Speed::miles_per_hour(10.0);
281        }
282        Speed::miles_per_hour(20.0)
283    }
284
285    /// Includes off-side
286    // TODO Specialize a variant for PathConstraints.can_use. Only one caller needs something
287    // fancier.
288    pub fn find_closest_lane<F: Fn(&Lane) -> bool>(
289        &self,
290        from: LaneID,
291        filter: F,
292    ) -> Option<LaneID> {
293        let our_idx = from.offset as isize;
294        self.lanes
295            .iter()
296            .enumerate()
297            .filter_map(|(idx, l)| {
298                if (idx as isize) != our_idx && filter(l) {
299                    Some((idx, l.id))
300                } else {
301                    None
302                }
303            })
304            .min_by_key(|(idx, _)| (our_idx - (*idx as isize)).abs())
305            .map(|(_, l)| l)
306    }
307
308    /// This is the FIRST yellow line where the direction of the road changes. If multiple direction
309    /// changes happen, the result is kind of arbitrary.
310    pub fn get_dir_change_pl(&self, map: &Map) -> PolyLine {
311        let mut found: Option<&Lane> = None;
312        for pair in self.lanes.windows(2) {
313            if pair[0].dir != pair[1].dir {
314                found = Some(&pair[0]);
315                break;
316            }
317        }
318        let lane = found.unwrap_or(&self.lanes[0]);
319        // There's a weird edge case with single lane light rail on left-handed maps...
320        let shifted = if map.get_config().driving_side == DrivingSide::Right || found.is_none() {
321            lane.lane_center_pts.must_shift_left(lane.width / 2.0)
322        } else {
323            lane.lane_center_pts.must_shift_right(lane.width / 2.0)
324        };
325        if lane.dir == Direction::Fwd {
326            shifted
327        } else {
328            shifted.reversed()
329        }
330    }
331
332    pub fn get_half_width(&self) -> Distance {
333        self.get_width() / 2.0
334    }
335
336    pub fn get_width(&self) -> Distance {
337        self.lanes.iter().map(|l| l.width).sum::<Distance>()
338    }
339
340    pub fn get_thick_polygon(&self) -> Polygon {
341        self.center_pts.make_polygons(self.get_width())
342    }
343
344    pub fn length(&self) -> Distance {
345        self.center_pts.length()
346    }
347
348    /// Creates the thick polygon representing one half of the road. For roads with multiple
349    /// direction changes (like a two-way cycletrack adjacent to a regular two-way road), the
350    /// results are probably weird.
351    pub fn get_half_polygon(&self, dir: Direction, map: &Map) -> Result<Polygon> {
352        let mut width_fwd = Distance::ZERO;
353        let mut width_back = Distance::ZERO;
354        for l in &self.lanes {
355            if l.dir == Direction::Fwd {
356                width_fwd += l.width;
357            } else {
358                width_back += l.width;
359            }
360        }
361        let center = self.get_dir_change_pl(map);
362
363        // TODO Test on UK maps...
364        let shift = if map.get_config().driving_side == DrivingSide::Right {
365            1.0
366        } else {
367            -1.0
368        };
369        if dir == Direction::Fwd {
370            Ok(center
371                .shift_right(shift * width_fwd / 2.0)?
372                .make_polygons(width_fwd))
373        } else {
374            Ok(center
375                .shift_left(shift * width_back / 2.0)?
376                .make_polygons(width_back))
377        }
378    }
379
380    pub fn get_name(&self, lang: Option<&String>) -> String {
381        if let Some(lang) = lang {
382            if let Some(name) = self.osm_tags.get(&format!("name:{}", lang)) {
383                return name.to_string();
384            }
385        }
386
387        if let Some(name) = self.osm_tags.get("name") {
388            if name.is_empty() {
389                return "???".to_string();
390            } else {
391                return name.to_string();
392            }
393        }
394        if let Some(name) = self.osm_tags.get("ref") {
395            return name.to_string();
396        }
397        if self
398            .osm_tags
399            .get(osm::HIGHWAY)
400            .map(|hwy| hwy.ends_with("_link"))
401            .unwrap_or(false)
402        {
403            if let Some(name) = self.osm_tags.get("destination:street") {
404                return format!("Exit for {}", name);
405            }
406            if let Some(name) = self.osm_tags.get("destination:ref") {
407                return format!("Exit for {}", name);
408            }
409            if let Some(name) = self.osm_tags.get("destination") {
410                return format!("Exit for {}", name);
411            }
412            // Sometimes 'directions' is filled out, but incorrectly...
413        }
414        "???".to_string()
415    }
416
417    pub fn get_rank(&self) -> osm::RoadRank {
418        if let Some(x) = self.osm_tags.get(osm::HIGHWAY) {
419            if x == "construction" {
420                // What exactly is under construction?
421                if let Some(x) = self.osm_tags.get("construction") {
422                    osm::RoadRank::from_highway(x)
423                } else {
424                    osm::RoadRank::Local
425                }
426            } else {
427                osm::RoadRank::from_highway(x)
428            }
429        } else {
430            osm::RoadRank::Local
431        }
432    }
433
434    pub fn get_detailed_rank(&self) -> usize {
435        self.osm_tags
436            .get(osm::HIGHWAY)
437            .map(|hwy| osm::RoadRank::detailed_from_highway(hwy))
438            .unwrap_or(0)
439    }
440
441    pub fn is_light_rail(&self) -> bool {
442        self.lanes.len() == 1 && self.lanes[0].lane_type == LaneType::LightRail
443    }
444
445    pub fn is_footway(&self) -> bool {
446        self.lanes.len() == 1
447            && matches!(
448                self.lanes[0].lane_type,
449                LaneType::Footway | LaneType::SharedUse
450            )
451    }
452
453    pub fn is_service(&self) -> bool {
454        self.osm_tags.is(osm::HIGHWAY, "service")
455    }
456
457    pub fn is_cycleway(&self) -> bool {
458        let mut bike = false;
459        for lane in &self.lanes {
460            if lane.lane_type == LaneType::Biking {
461                bike = true;
462            } else if !lane.is_walkable() {
463                return false;
464            }
465        }
466        bike
467    }
468
469    pub fn is_driveable(&self) -> bool {
470        self.lanes.iter().any(|l| l.is_driving())
471    }
472
473    pub fn common_endpoint(&self, other: &Road) -> CommonEndpoint {
474        CommonEndpoint::new((self.src_i, self.dst_i), (other.src_i, other.dst_i))
475    }
476
477    pub fn endpoints(&self) -> Vec<IntersectionID> {
478        vec![self.src_i, self.dst_i]
479    }
480
481    /// Returns the other intersection of this road, panicking if this road doesn't connect to the
482    /// input
483    /// TODO This should use CommonEndpoint
484    pub fn other_endpt(&self, i: IntersectionID) -> IntersectionID {
485        if self.src_i == i {
486            self.dst_i
487        } else if self.dst_i == i {
488            self.src_i
489        } else {
490            panic!("{} doesn't touch {}", self.id, i);
491        }
492    }
493
494    pub fn is_private(&self) -> bool {
495        self.access_restrictions != AccessRestrictions::new() && !self.is_light_rail()
496    }
497
498    pub(crate) fn access_restrictions_from_osm(&self) -> AccessRestrictions {
499        let allow_through_traffic = if self.osm_tags.is("access", "private") {
500            EnumSet::new()
501        } else if self.osm_tags.is(osm::HIGHWAY, "living_street") {
502            let mut allow = PathConstraints::Pedestrian | PathConstraints::Bike;
503            if self.osm_tags.is("psv", "yes") || self.osm_tags.is("bus", "yes") {
504                allow |= PathConstraints::Bus;
505            }
506            allow
507        } else {
508            EnumSet::all()
509        };
510        AccessRestrictions {
511            allow_through_traffic,
512        }
513    }
514
515    pub fn get_zone<'a>(&self, map: &'a Map) -> Option<&'a Zone> {
516        if !self.is_private() {
517            return None;
518        }
519        // Insist on it existing
520        Some(
521            map.zones
522                .iter()
523                .find(|z| z.members.contains(&self.id))
524                .unwrap(),
525        )
526    }
527
528    /// Many roads wind up with almost no length, due to their representation in OpenStreetMap. In
529    /// reality, these segments are likely located within the interior of an intersection. This
530    /// method uses a hardcoded threshold to detect these cases.
531    pub fn is_extremely_short(&self) -> bool {
532        self.length() < Distance::meters(2.0)
533    }
534
535    /// Get the DirectedRoadID pointing to the intersection. Panics if the intersection isn't an
536    /// endpoint.
537    pub fn directed_id_from(&self, i: IntersectionID) -> DirectedRoadID {
538        DirectedRoadID {
539            road: self.id,
540            dir: if self.src_i == i {
541                Direction::Fwd
542            } else if self.dst_i == i {
543                Direction::Back
544            } else {
545                panic!("{} doesn't point to {}", self.id, i);
546            },
547        }
548    }
549
550    /// Get the DirectedRoadID pointing from the intersection. Panics if the intersection isn't an
551    /// endpoint.
552    pub fn directed_id_to(&self, i: IntersectionID) -> DirectedRoadID {
553        let mut id = self.directed_id_from(i);
554        id.dir = id.dir.opposite();
555        id
556    }
557
558    pub(crate) fn recreate_lanes(&mut self, lane_specs_ltr: Vec<LaneSpec>) {
559        self.lanes.clear();
560
561        let total_width = lane_specs_ltr.iter().map(|x| x.width).sum();
562
563        let mut width_so_far = Distance::ZERO;
564        for lane in lane_specs_ltr {
565            let id = LaneID {
566                road: self.id,
567                offset: self.lanes.len(),
568            };
569
570            let (src_i, dst_i) = if lane.dir == Direction::Fwd {
571                (self.src_i, self.dst_i)
572            } else {
573                (self.dst_i, self.src_i)
574            };
575
576            width_so_far += lane.width / 2.0;
577            let pl = self
578                .center_pts
579                .shift_from_center(total_width, width_so_far)
580                .unwrap_or_else(|_| self.center_pts.clone());
581            width_so_far += lane.width / 2.0;
582
583            let lane_center_pts = if lane.dir == Direction::Fwd {
584                pl
585            } else {
586                pl.reversed()
587            };
588
589            self.lanes.push(Lane {
590                id,
591                lane_center_pts,
592                width: lane.width,
593                src_i,
594                dst_i,
595                lane_type: lane.lt,
596                dir: lane.dir,
597                driving_blackhole: false,
598                biking_blackhole: false,
599            });
600        }
601    }
602
603    /// Returns all lanes located between l1 and l2, exclusive.
604    pub fn get_lanes_between(&self, l1: LaneID, l2: LaneID) -> Vec<LaneID> {
605        let mut results = Vec::new();
606        let mut found_start = false;
607        for l in &self.lanes {
608            if found_start {
609                if l.id == l1 || l.id == l2 {
610                    return results;
611                }
612                results.push(l.id);
613            } else if l.id == l1 || l.id == l2 {
614                found_start = true;
615            }
616        }
617        panic!("{} doesn't contain both {} and {}", self.id, l1, l2);
618    }
619
620    /// A simple classification of if the directed road is stressful or not for cycling. Arterial
621    /// roads without a bike lane match this. Why arterial, instead of looking at speed limits?
622    /// Even on arterial roads with official speed limits lowered, in practice vehicles still
623    /// travel at the speed suggested by the design of the road.
624    // TODO Should elevation matter or not? Flat high-speed roads are still terrifying, but there's
625    // something about slogging up (or flying down!) a pothole-filled road inches from cars.
626    pub fn high_stress_for_bikes(&self, map: &Map, dir: Direction) -> bool {
627        let mut bike_lanes = false;
628        let mut can_use = false;
629        // Can a bike even use it, or is it a highway?
630        for l in &self.lanes {
631            if l.lane_type == LaneType::Biking && l.dir == dir {
632                bike_lanes = true;
633            }
634            if PathConstraints::Bike.can_use(l, map) {
635                can_use = true;
636            }
637        }
638        if !can_use || bike_lanes {
639            return false;
640        }
641        self.get_rank() != osm::RoadRank::Local
642    }
643
644    pub fn oneway_for_driving(&self) -> Option<Direction> {
645        LaneSpec::oneway_for_driving(&self.lane_specs())
646    }
647
648    /// Does either end of this road lead nowhere for cars?
649    /// (Asking this for a non-driveable road may be kind of meaningless)
650    pub fn is_deadend_for_driving(&self, map: &Map) -> bool {
651        map.get_i(self.src_i).is_deadend_for_driving(map)
652            || map.get_i(self.dst_i).is_deadend_for_driving(map)
653    }
654}
655
656// TODO All of this is kind of deprecated? Some callers seem to really need to still handle lanes
657// going outward from the "center" line. Should keep whittling this down, probably. These very much
658// don't handle multiple direction changes.
659impl Road {
660    /// These are ordered from closest to center lane (left-most when driving on the right) to
661    /// farthest (sidewalk)
662    pub(crate) fn children_forwards(&self) -> Vec<(LaneID, LaneType)> {
663        let mut result = Vec::new();
664        for l in &self.lanes {
665            if l.dir == Direction::Fwd {
666                result.push((l.id, l.lane_type));
667            }
668        }
669        result
670    }
671    pub(crate) fn children_backwards(&self) -> Vec<(LaneID, LaneType)> {
672        let mut result = Vec::new();
673        for l in &self.lanes {
674            if l.dir == Direction::Back {
675                result.push((l.id, l.lane_type));
676            }
677        }
678        result.reverse();
679        result
680    }
681
682    // TODO Deprecated
683    pub(crate) fn children(&self, dir: Direction) -> Vec<(LaneID, LaneType)> {
684        if dir == Direction::Fwd {
685            self.children_forwards()
686        } else {
687            self.children_backwards()
688        }
689    }
690
691    /// Returns lanes from the "center" going out
692    pub(crate) fn incoming_lanes(&self, i: IntersectionID) -> Vec<(LaneID, LaneType)> {
693        if self.src_i == i {
694            self.children_backwards()
695        } else if self.dst_i == i {
696            self.children_forwards()
697        } else {
698            panic!("{} doesn't have an endpoint at {}", self.id, i);
699        }
700    }
701}
702
703/// Refers to a road segment between two nodes, using OSM IDs. Note OSM IDs are not stable over
704/// time and the relationship between a road/intersection and way/node isn't 1:1 at all.
705#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
706pub struct OriginalRoad {
707    pub osm_way_id: osm::WayID,
708    pub i1: osm::NodeID,
709    pub i2: osm::NodeID,
710}
711
712impl fmt::Display for OriginalRoad {
713    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
714        write!(
715            f,
716            "OriginalRoad({} from {} to {}",
717            self.osm_way_id, self.i1, self.i2
718        )
719    }
720}
721impl fmt::Debug for OriginalRoad {
722    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
723        write!(f, "{}", self)
724    }
725}
726
727impl OriginalRoad {
728    pub fn new(way: i64, (i1, i2): (i64, i64)) -> OriginalRoad {
729        OriginalRoad {
730            osm_way_id: osm::WayID(way),
731            i1: osm::NodeID(i1),
732            i2: osm::NodeID(i2),
733        }
734    }
735}
736
737#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
738pub struct Crossing {
739    pub kind: CrossingType,
740    pub dist: Distance,
741}