use std::collections::{BTreeMap, BTreeSet};
use std::fmt;
use serde::{Deserialize, Serialize};
use abstutil::{deserialize_usize, serialize_usize};
use geom::{Distance, Polygon};
use crate::{
osm, CompressedMovementID, DiagonalFilter, DirectedRoadID, IntersectionControl,
IntersectionKind, LaneID, Map, Movement, MovementID, PathConstraints, Road, RoadID, RoadSideID,
SideOfRoad, Turn, TurnID,
};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct IntersectionID(
#[serde(
serialize_with = "serialize_usize",
deserialize_with = "deserialize_usize"
)]
pub usize,
);
impl fmt::Display for IntersectionID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Intersection #{}", self.0)
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Intersection {
pub id: IntersectionID,
pub polygon: Polygon,
pub turns: Vec<Turn>,
pub elevation: Distance,
pub kind: IntersectionKind,
pub control: IntersectionControl,
pub orig_id: osm::NodeID,
pub incoming_lanes: Vec<LaneID>,
pub outgoing_lanes: Vec<LaneID>,
pub roads: Vec<RoadID>,
pub modal_filter: Option<DiagonalFilter>,
pub merged: bool,
#[serde(skip_serializing, skip_deserializing)]
pub movements: BTreeMap<MovementID, Movement>,
}
impl Intersection {
pub fn is_border(&self) -> bool {
self.kind == IntersectionKind::MapEdge
}
pub fn is_incoming_border(&self) -> bool {
self.kind == IntersectionKind::MapEdge && !self.outgoing_lanes.is_empty()
}
pub fn is_outgoing_border(&self) -> bool {
self.kind == IntersectionKind::MapEdge && !self.incoming_lanes.is_empty()
}
pub fn is_closed(&self) -> bool {
self.control == IntersectionControl::Construction
}
pub fn is_stop_sign(&self) -> bool {
self.control == IntersectionControl::Signed
}
pub fn is_traffic_signal(&self) -> bool {
self.control == IntersectionControl::Signalled
}
pub fn is_light_rail(&self, map: &Map) -> bool {
self.roads.iter().all(|r| map.get_r(*r).is_light_rail())
}
pub fn is_private(&self, map: &Map) -> bool {
self.roads.iter().all(|r| map.get_r(*r).is_private())
}
pub fn is_footway(&self, map: &Map) -> bool {
self.roads.iter().all(|r| map.get_r(*r).is_footway())
}
pub fn is_cycleway(&self, map: &Map) -> bool {
self.roads.iter().all(|r| map.get_r(*r).is_cycleway())
}
pub fn is_degenerate(&self) -> bool {
self.roads.len() == 2
}
pub fn is_deadend_for_driving(&self, map: &Map) -> bool {
self.roads
.iter()
.filter(|r| PathConstraints::Car.can_use_road(map.get_r(**r), map))
.count()
== 1
}
pub fn is_deadend_for_everyone(&self) -> bool {
self.roads.len() == 1
}
pub fn get_incoming_lanes(&self, map: &Map, constraints: PathConstraints) -> Vec<LaneID> {
self.incoming_lanes
.iter()
.filter(move |l| constraints.can_use(map.get_l(**l), map))
.cloned()
.collect()
}
pub fn get_outgoing_lanes(&self, map: &Map, constraints: PathConstraints) -> Vec<LaneID> {
constraints.filter_lanes(self.outgoing_lanes.clone(), map)
}
pub fn get_zorder(&self, map: &Map) -> isize {
self.roads
.iter()
.map(|r| map.get_r(*r).zorder)
.min()
.unwrap()
}
pub fn get_rank(&self, map: &Map) -> osm::RoadRank {
self.roads
.iter()
.map(|r| map.get_r(*r).get_rank())
.max()
.unwrap()
}
pub fn get_road_sides_sorted(&self, map: &Map) -> Vec<RoadSideID> {
let mut deduped = self.roads.clone();
deduped.dedup();
let mut sides = Vec::new();
for r in deduped {
let r = map.get_r(r);
if r.src_i == r.dst_i {
if r.center_pts.is_clockwise() {
sides.push(RoadSideID {
road: r.id,
side: SideOfRoad::Left,
});
} else {
sides.push(RoadSideID {
road: r.id,
side: SideOfRoad::Right,
});
}
continue;
}
if r.dst_i == self.id {
sides.push(RoadSideID {
road: r.id,
side: SideOfRoad::Right,
});
sides.push(RoadSideID {
road: r.id,
side: SideOfRoad::Left,
});
} else {
sides.push(RoadSideID {
road: r.id,
side: SideOfRoad::Left,
});
sides.push(RoadSideID {
road: r.id,
side: SideOfRoad::Right,
});
}
}
sides
}
pub fn get_sorted_incoming_roads(&self, map: &Map) -> Vec<RoadID> {
let mut roads = self.roads.clone();
roads.retain(|r| !map.get_r(*r).incoming_lanes(self.id).is_empty());
roads
}
pub fn some_outgoing_road(&self, map: &Map) -> Option<DirectedRoadID> {
self.outgoing_lanes
.get(0)
.map(|l| map.get_l(*l).get_directed_parent())
}
pub fn some_incoming_road(&self, map: &Map) -> Option<DirectedRoadID> {
self.incoming_lanes
.get(0)
.map(|l| map.get_l(*l).get_directed_parent())
}
pub fn name(&self, lang: Option<&String>, map: &Map) -> String {
let road_names = self
.roads
.iter()
.map(|r| map.get_r(*r).get_name(lang))
.collect::<BTreeSet<_>>();
abstutil::plain_list_names(road_names)
}
pub fn turn_to_movement(&self, turn: TurnID) -> (MovementID, CompressedMovementID) {
for (idx, m) in self.movements.values().enumerate() {
if m.members.contains(&turn) {
return (
m.id,
CompressedMovementID {
i: self.id,
idx: u8::try_from(idx).unwrap(),
},
);
}
}
panic!(
"{} doesn't belong to any movements in {} or is a SharedSidewalkCorner maybe",
turn, self.id
)
}
pub fn find_road_between<'a>(&self, other: IntersectionID, map: &'a Map) -> Option<&'a Road> {
for r in &self.roads {
let road = map.get_r(*r);
if road.other_endpt(self.id) == other {
return Some(road);
}
}
None
}
}