splitting map model into more modules

This commit is contained in:
Dustin Carlino 2018-06-25 07:53:39 -07:00
parent 2b9eb66ba1
commit d9e46c5960
8 changed files with 446 additions and 382 deletions

View File

@ -83,3 +83,20 @@ the transition is hard:
so really do this in two parts: so really do this in two parts:
1) current structure with weird intermediate stuff, but new geometry libs 1) current structure with weird intermediate stuff, but new geometry libs
2) careful reorg 2) careful reorg
wait slow down even more -- before any of this change, lanes on adjacent roads smoosh into each other. main road doesnt, but other parts do.
- at every intersection, find corresponding lanes and trim back center lines
- do we need to do this at the level of the polygons?!
- follow aorta's multi phase map construction better.
- FIRST: move geom things into the Map structs directly. get rid of that crate.
---> option 1: module per object type, geometry and graph squished together
- option 2: try to separate the graph/geom stuff within map model.
- THEN: express the proto -> runtime map loading as a sequence of phases
- keep doing the current road trimming for the moment
- later, this could be the same as the OSM conversion. just
like aorta's map make. but instead, be able to restart from
any point, by the magic of easy serialization.
- get rid of the protobuf

21
map_model/src/building.rs Normal file
View File

@ -0,0 +1,21 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
use Pt2D;
// TODO reconsider pub usize. maybe outside world shouldnt know.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct BuildingID(pub usize);
#[derive(Debug)]
pub struct Building {
pub id: BuildingID,
pub points: Vec<Pt2D>,
pub osm_tags: Vec<String>,
pub osm_way_id: i64,
}
impl PartialEq for Building {
fn eq(&self, other: &Building) -> bool {
self.id == other.id
}
}

View File

@ -0,0 +1,23 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
use Pt2D;
use TurnID;
// TODO reconsider pub usize. maybe outside world shouldnt know.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct IntersectionID(pub usize);
#[derive(Debug)]
pub struct Intersection {
pub id: IntersectionID,
pub point: Pt2D,
pub turns: Vec<TurnID>,
pub elevation_meters: f64,
pub has_traffic_signal: bool,
}
impl PartialEq for Intersection {
fn eq(&self, other: &Intersection) -> bool {
self.id == other.id
}
}

View File

@ -7,16 +7,27 @@ extern crate serde;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
mod building;
mod intersection;
mod map;
mod parcel;
pub mod pb; pub mod pb;
mod polyline; mod polyline;
mod road;
mod turn;
pub use building::{Building, BuildingID};
pub use intersection::{Intersection, IntersectionID};
pub use map::Map;
use ordered_float::NotNaN; use ordered_float::NotNaN;
pub use parcel::{Parcel, ParcelID};
pub use polyline::{polygons_for_polyline, shift_polyline}; pub use polyline::{polygons_for_polyline, shift_polyline};
use protobuf::error::ProtobufError; use protobuf::error::ProtobufError;
use protobuf::{CodedInputStream, CodedOutputStream, Message}; use protobuf::{CodedInputStream, CodedOutputStream, Message};
use std::collections::HashMap; pub use road::{LaneType, Road, RoadID};
use std::f64; use std::f64;
use std::fs::File; use std::fs::File;
pub use turn::{Turn, TurnID};
pub fn write_pb(map: &pb::Map, path: &str) -> Result<(), ProtobufError> { pub fn write_pb(map: &pb::Map, path: &str) -> Result<(), ProtobufError> {
let mut file = File::create(path)?; let mut file = File::create(path)?;
@ -128,387 +139,6 @@ impl Bounds {
} }
} }
// TODO reconsider pub usize. maybe outside world shouldnt know.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct RoadID(pub usize);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct IntersectionID(pub usize);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct TurnID(pub usize);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct BuildingID(pub usize);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct ParcelID(pub usize);
pub struct Map {
roads: Vec<Road>,
intersections: Vec<Intersection>,
turns: Vec<Turn>,
buildings: Vec<Building>,
parcels: Vec<Parcel>,
pt_to_intersection: HashMap<Pt2D, IntersectionID>,
intersection_to_roads: HashMap<IntersectionID, Vec<RoadID>>,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum LaneType {
Driving,
Parking,
Sidewalk,
}
#[derive(Debug)]
pub struct Road {
pub id: RoadID,
pub osm_tags: Vec<String>,
pub osm_way_id: i64,
pub lane_type: LaneType,
// Ideally all of these would just become translated center points immediately, but this is
// hard due to the polyline problem.
// All roads are two-way (since even one-way streets have sidewalks on both sides). Offset 0 is
// the centermost lane on each side, then it counts up.
pub offset: u8,
// The orientation is implied by the order of these points
pub points: Vec<Pt2D>,
// Should this lane own the drawing of the yellow center lines? For two-way roads, this is
// arbitrarily grouped with one of the lanes. Ideally it would be owned by something else.
pub use_yellow_center_lines: bool,
// Need to remember this just for detecting U-turns here.
other_side: Option<RoadID>,
}
impl PartialEq for Road {
fn eq(&self, other: &Road) -> bool {
self.id == other.id
}
}
#[derive(Debug)]
pub struct Intersection {
pub id: IntersectionID,
pub point: Pt2D,
pub turns: Vec<TurnID>,
pub elevation_meters: f64,
pub has_traffic_signal: bool,
}
impl PartialEq for Intersection {
fn eq(&self, other: &Intersection) -> bool {
self.id == other.id
}
}
#[derive(Debug)]
pub struct Turn {
pub id: TurnID,
pub parent: IntersectionID,
pub src: RoadID,
pub dst: RoadID,
}
impl PartialEq for Turn {
fn eq(&self, other: &Turn) -> bool {
self.id == other.id
}
}
#[derive(Debug)]
pub struct Building {
pub id: BuildingID,
pub points: Vec<Pt2D>,
pub osm_tags: Vec<String>,
pub osm_way_id: i64,
}
impl PartialEq for Building {
fn eq(&self, other: &Building) -> bool {
self.id == other.id
}
}
#[derive(Debug)]
pub struct Parcel {
pub id: ParcelID,
pub points: Vec<Pt2D>,
}
impl PartialEq for Parcel {
fn eq(&self, other: &Parcel) -> bool {
self.id == other.id
}
}
impl Map {
pub fn new(data: &pb::Map) -> Map {
let mut m = Map {
roads: Vec::new(),
intersections: Vec::new(),
turns: Vec::new(),
buildings: Vec::new(),
parcels: Vec::new(),
pt_to_intersection: HashMap::new(),
intersection_to_roads: HashMap::new(),
};
for (idx, i) in data.get_intersections().iter().enumerate() {
let id = IntersectionID(idx);
let pt = Pt2D::from(i.get_point());
m.intersections.push(Intersection {
id,
point: pt,
turns: Vec::new(),
elevation_meters: i.get_elevation_meters(),
// TODO use the data again!
//has_traffic_signal: i.get_has_traffic_signal(),
has_traffic_signal: idx % 2 == 0,
});
m.pt_to_intersection.insert(pt, id);
}
let mut counter = 0;
for r in data.get_roads() {
let oneway = r.get_osm_tags().contains(&String::from("oneway=yes"));
let orig_direction = true;
let reverse_direction = false;
// lane_type, offset, reverse the points or not, offset to get the other_side's ID
let mut lanes: Vec<(LaneType, u8, bool, Option<isize>)> = vec![
(
LaneType::Driving,
0,
orig_direction,
if oneway { None } else { Some(3) },
),
(LaneType::Parking, 1, orig_direction, None),
(LaneType::Sidewalk, 2, orig_direction, None),
];
if oneway {
lanes.push((LaneType::Sidewalk, 0, reverse_direction, None));
} else {
lanes.extend(vec![
(LaneType::Driving, 0, reverse_direction, Some(-3)),
(LaneType::Parking, 1, reverse_direction, None),
(LaneType::Sidewalk, 2, reverse_direction, None),
]);
}
for lane in &lanes {
let id = RoadID(counter);
counter += 1;
let other_side = lane.3
.map(|offset| RoadID(((id.0 as isize) + offset) as usize));
let pts: Vec<Pt2D> = if lane.2 == orig_direction {
r.get_points().iter().map(Pt2D::from).collect()
} else {
r.get_points().iter().rev().map(Pt2D::from).collect()
};
let i1 = m.pt_to_intersection[&pts[0]];
let i2 = m.pt_to_intersection[pts.last().unwrap()];
m.intersection_to_roads
.entry(i1)
.or_insert_with(Vec::new)
.push(id);
m.intersection_to_roads
.entry(i2)
.or_insert_with(Vec::new)
.push(id);
m.roads.push(Road {
id,
other_side,
osm_tags: r.get_osm_tags().to_vec(),
osm_way_id: r.get_osm_way_id(),
lane_type: lane.0,
offset: lane.1,
points: pts,
use_yellow_center_lines: if let Some(other) = other_side {
id.0 < other.0
} else {
lane.1 == 0
},
});
}
}
for i in &m.intersections {
// TODO: Figure out why this happens in the huge map
if m.intersection_to_roads.get(&i.id).is_none() {
println!("WARNING: intersection {:?} has no roads", i);
continue;
}
let incident_roads: Vec<RoadID> = m.intersection_to_roads[&i.id]
.iter()
.filter(|id| m.roads[id.0].lane_type == LaneType::Driving)
.map(|id| *id)
.collect();
for src in &incident_roads {
let src_r = &m.roads[src.0];
if i.point != *src_r.points.last().unwrap() {
continue;
}
for dst in &incident_roads {
let dst_r = &m.roads[dst.0];
if i.point != dst_r.points[0] {
continue;
}
// Don't create U-turns unless it's a dead-end
if src_r.other_side == Some(dst_r.id) && incident_roads.len() > 2 {
continue;
}
let id = TurnID(m.turns.len());
m.turns.push(Turn {
id,
parent: i.id,
src: *src,
dst: *dst,
});
}
}
}
for t in &m.turns {
m.intersections[t.parent.0].turns.push(t.id);
}
for (idx, b) in data.get_buildings().iter().enumerate() {
m.buildings.push(Building {
id: BuildingID(idx),
points: b.get_points().iter().map(Pt2D::from).collect(),
osm_tags: b.get_osm_tags().to_vec(),
osm_way_id: b.get_osm_way_id(),
});
}
for (idx, p) in data.get_parcels().iter().enumerate() {
m.parcels.push(Parcel {
id: ParcelID(idx),
points: p.get_points().iter().map(Pt2D::from).collect(),
});
}
m
}
pub fn get_roads_to_intersection(&self, id: IntersectionID) -> Vec<&Road> {
self.intersection_to_roads[&id]
.iter()
.map(|id| &self.roads[id.0])
.filter(|r| *r.points.last().unwrap() == self.get_i(id).point)
.collect()
}
pub fn get_roads_from_intersection(&self, id: IntersectionID) -> Vec<&Road> {
self.intersection_to_roads[&id]
.iter()
.map(|id| &self.roads[id.0])
.filter(|r| r.points[0] == self.get_i(id).point)
.collect()
}
pub fn all_roads(&self) -> &Vec<Road> {
&self.roads
}
pub fn all_intersections(&self) -> &Vec<Intersection> {
&self.intersections
}
pub fn all_turns(&self) -> &Vec<Turn> {
&self.turns
}
pub fn all_buildings(&self) -> &Vec<Building> {
&self.buildings
}
pub fn all_parcels(&self) -> &Vec<Parcel> {
&self.parcels
}
pub fn get_r(&self, id: RoadID) -> &Road {
&self.roads[id.0]
}
pub fn get_i(&self, id: IntersectionID) -> &Intersection {
&self.intersections[id.0]
}
pub fn get_t(&self, id: TurnID) -> &Turn {
&self.turns[id.0]
}
pub fn get_b(&self, id: BuildingID) -> &Building {
&self.buildings[id.0]
}
pub fn get_p(&self, id: ParcelID) -> &Parcel {
&self.parcels[id.0]
}
// All these helpers should take IDs and return objects.
pub fn get_source_intersection(&self, r: RoadID) -> &Intersection {
self.get_i(self.pt_to_intersection[&self.get_r(r).points[0]])
}
pub fn get_destination_intersection(&self, r: RoadID) -> &Intersection {
self.get_i(self.pt_to_intersection[self.get_r(r).points.last().unwrap()])
}
pub fn get_turns_in_intersection(&self, id: IntersectionID) -> Vec<&Turn> {
self.get_i(id)
.turns
.iter()
.map(|t| self.get_t(*t))
.collect()
}
pub fn get_turns_from_road(&self, id: RoadID) -> Vec<&Turn> {
let i = self.get_destination_intersection(id);
// TODO can't filter on get_turns_in_intersection... winds up being Vec<&&Turn>
i.turns
.iter()
.map(|t| self.get_t(*t))
.filter(|t| t.src == id)
.collect()
}
pub fn get_next_roads(&self, from: RoadID) -> Vec<&Road> {
// TODO assumes no duplicates
self.get_turns_from_road(from)
.iter()
.map(|t| self.get_r(t.dst))
.collect()
}
pub fn get_gps_bounds(&self) -> Bounds {
let mut b = Bounds::new();
for r in &self.roads {
for pt in &r.points {
b.update_pt(pt);
}
}
for i in &self.intersections {
b.update_pt(&i.point);
}
for bldg in &self.buildings {
for pt in &bldg.points {
b.update_pt(pt);
}
}
for p in &self.parcels {
for pt in &p.points {
b.update_pt(pt);
}
}
b
}
}
pub fn has_osm_tag(tags: &Vec<String>, key: &str, value: &str) -> bool { pub fn has_osm_tag(tags: &Vec<String>, key: &str, value: &str) -> bool {
tags.contains(&format!("{}={}", key, value)) tags.contains(&format!("{}={}", key, value))
} }

290
map_model/src/map.rs Normal file
View File

@ -0,0 +1,290 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
use Bounds;
use Pt2D;
use building::{Building, BuildingID};
use intersection::{Intersection, IntersectionID};
use parcel::{Parcel, ParcelID};
use pb;
use road::{LaneType, Road, RoadID};
use std::collections::HashMap;
use turn::{Turn, TurnID};
pub struct Map {
roads: Vec<Road>,
intersections: Vec<Intersection>,
turns: Vec<Turn>,
buildings: Vec<Building>,
parcels: Vec<Parcel>,
pt_to_intersection: HashMap<Pt2D, IntersectionID>,
intersection_to_roads: HashMap<IntersectionID, Vec<RoadID>>,
}
impl Map {
pub fn new(data: &pb::Map) -> Map {
let mut m = Map {
roads: Vec::new(),
intersections: Vec::new(),
turns: Vec::new(),
buildings: Vec::new(),
parcels: Vec::new(),
pt_to_intersection: HashMap::new(),
intersection_to_roads: HashMap::new(),
};
for (idx, i) in data.get_intersections().iter().enumerate() {
let id = IntersectionID(idx);
let pt = Pt2D::from(i.get_point());
m.intersections.push(Intersection {
id,
point: pt,
turns: Vec::new(),
elevation_meters: i.get_elevation_meters(),
// TODO use the data again!
//has_traffic_signal: i.get_has_traffic_signal(),
has_traffic_signal: idx % 2 == 0,
});
m.pt_to_intersection.insert(pt, id);
}
let mut counter = 0;
for r in data.get_roads() {
let oneway = r.get_osm_tags().contains(&String::from("oneway=yes"));
let orig_direction = true;
let reverse_direction = false;
// lane_type, offset, reverse the points or not, offset to get the other_side's ID
let mut lanes: Vec<(LaneType, u8, bool, Option<isize>)> = vec![
(
LaneType::Driving,
0,
orig_direction,
if oneway { None } else { Some(3) },
),
(LaneType::Parking, 1, orig_direction, None),
(LaneType::Sidewalk, 2, orig_direction, None),
];
if oneway {
lanes.push((LaneType::Sidewalk, 0, reverse_direction, None));
} else {
lanes.extend(vec![
(LaneType::Driving, 0, reverse_direction, Some(-3)),
(LaneType::Parking, 1, reverse_direction, None),
(LaneType::Sidewalk, 2, reverse_direction, None),
]);
}
for lane in &lanes {
let id = RoadID(counter);
counter += 1;
let other_side = lane.3
.map(|offset| RoadID(((id.0 as isize) + offset) as usize));
let pts: Vec<Pt2D> = if lane.2 == orig_direction {
r.get_points().iter().map(Pt2D::from).collect()
} else {
r.get_points().iter().rev().map(Pt2D::from).collect()
};
let i1 = m.pt_to_intersection[&pts[0]];
let i2 = m.pt_to_intersection[pts.last().unwrap()];
m.intersection_to_roads
.entry(i1)
.or_insert_with(Vec::new)
.push(id);
m.intersection_to_roads
.entry(i2)
.or_insert_with(Vec::new)
.push(id);
m.roads.push(Road {
id,
other_side,
osm_tags: r.get_osm_tags().to_vec(),
osm_way_id: r.get_osm_way_id(),
lane_type: lane.0,
offset: lane.1,
points: pts,
use_yellow_center_lines: if let Some(other) = other_side {
id.0 < other.0
} else {
lane.1 == 0
},
});
}
}
for i in &m.intersections {
// TODO: Figure out why this happens in the huge map
if m.intersection_to_roads.get(&i.id).is_none() {
println!("WARNING: intersection {:?} has no roads", i);
continue;
}
let incident_roads: Vec<RoadID> = m.intersection_to_roads[&i.id]
.iter()
.filter(|id| m.roads[id.0].lane_type == LaneType::Driving)
.map(|id| *id)
.collect();
for src in &incident_roads {
let src_r = &m.roads[src.0];
if i.point != *src_r.points.last().unwrap() {
continue;
}
for dst in &incident_roads {
let dst_r = &m.roads[dst.0];
if i.point != dst_r.points[0] {
continue;
}
// Don't create U-turns unless it's a dead-end
if src_r.other_side == Some(dst_r.id) && incident_roads.len() > 2 {
continue;
}
let id = TurnID(m.turns.len());
m.turns.push(Turn {
id,
parent: i.id,
src: *src,
dst: *dst,
});
}
}
}
for t in &m.turns {
m.intersections[t.parent.0].turns.push(t.id);
}
for (idx, b) in data.get_buildings().iter().enumerate() {
m.buildings.push(Building {
id: BuildingID(idx),
points: b.get_points().iter().map(Pt2D::from).collect(),
osm_tags: b.get_osm_tags().to_vec(),
osm_way_id: b.get_osm_way_id(),
});
}
for (idx, p) in data.get_parcels().iter().enumerate() {
m.parcels.push(Parcel {
id: ParcelID(idx),
points: p.get_points().iter().map(Pt2D::from).collect(),
});
}
m
}
pub fn get_roads_to_intersection(&self, id: IntersectionID) -> Vec<&Road> {
self.intersection_to_roads[&id]
.iter()
.map(|id| &self.roads[id.0])
.filter(|r| *r.points.last().unwrap() == self.get_i(id).point)
.collect()
}
pub fn get_roads_from_intersection(&self, id: IntersectionID) -> Vec<&Road> {
self.intersection_to_roads[&id]
.iter()
.map(|id| &self.roads[id.0])
.filter(|r| r.points[0] == self.get_i(id).point)
.collect()
}
pub fn all_roads(&self) -> &Vec<Road> {
&self.roads
}
pub fn all_intersections(&self) -> &Vec<Intersection> {
&self.intersections
}
pub fn all_turns(&self) -> &Vec<Turn> {
&self.turns
}
pub fn all_buildings(&self) -> &Vec<Building> {
&self.buildings
}
pub fn all_parcels(&self) -> &Vec<Parcel> {
&self.parcels
}
pub fn get_r(&self, id: RoadID) -> &Road {
&self.roads[id.0]
}
pub fn get_i(&self, id: IntersectionID) -> &Intersection {
&self.intersections[id.0]
}
pub fn get_t(&self, id: TurnID) -> &Turn {
&self.turns[id.0]
}
pub fn get_b(&self, id: BuildingID) -> &Building {
&self.buildings[id.0]
}
pub fn get_p(&self, id: ParcelID) -> &Parcel {
&self.parcels[id.0]
}
// All these helpers should take IDs and return objects.
pub fn get_source_intersection(&self, r: RoadID) -> &Intersection {
self.get_i(self.pt_to_intersection[&self.get_r(r).points[0]])
}
pub fn get_destination_intersection(&self, r: RoadID) -> &Intersection {
self.get_i(self.pt_to_intersection[self.get_r(r).points.last().unwrap()])
}
pub fn get_turns_in_intersection(&self, id: IntersectionID) -> Vec<&Turn> {
self.get_i(id)
.turns
.iter()
.map(|t| self.get_t(*t))
.collect()
}
pub fn get_turns_from_road(&self, id: RoadID) -> Vec<&Turn> {
let i = self.get_destination_intersection(id);
// TODO can't filter on get_turns_in_intersection... winds up being Vec<&&Turn>
i.turns
.iter()
.map(|t| self.get_t(*t))
.filter(|t| t.src == id)
.collect()
}
pub fn get_next_roads(&self, from: RoadID) -> Vec<&Road> {
// TODO assumes no duplicates
self.get_turns_from_road(from)
.iter()
.map(|t| self.get_r(t.dst))
.collect()
}
pub fn get_gps_bounds(&self) -> Bounds {
let mut b = Bounds::new();
for r in &self.roads {
for pt in &r.points {
b.update_pt(pt);
}
}
for i in &self.intersections {
b.update_pt(&i.point);
}
for bldg in &self.buildings {
for pt in &bldg.points {
b.update_pt(pt);
}
}
for p in &self.parcels {
for pt in &p.points {
b.update_pt(pt);
}
}
b
}
}

19
map_model/src/parcel.rs Normal file
View File

@ -0,0 +1,19 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
use Pt2D;
// TODO reconsider pub usize. maybe outside world shouldnt know.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct ParcelID(pub usize);
#[derive(Debug)]
pub struct Parcel {
pub id: ParcelID,
pub points: Vec<Pt2D>,
}
impl PartialEq for Parcel {
fn eq(&self, other: &Parcel) -> bool {
self.id == other.id
}
}

42
map_model/src/road.rs Normal file
View File

@ -0,0 +1,42 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
use Pt2D;
// TODO reconsider pub usize. maybe outside world shouldnt know.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct RoadID(pub usize);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum LaneType {
Driving,
Parking,
Sidewalk,
}
#[derive(Debug)]
pub struct Road {
pub id: RoadID,
pub osm_tags: Vec<String>,
pub osm_way_id: i64,
pub lane_type: LaneType,
// Ideally all of these would just become translated center points immediately, but this is
// hard due to the polyline problem.
// All roads are two-way (since even one-way streets have sidewalks on both sides). Offset 0 is
// the centermost lane on each side, then it counts up.
pub offset: u8,
// The orientation is implied by the order of these points
pub points: Vec<Pt2D>,
// Should this lane own the drawing of the yellow center lines? For two-way roads, this is
// arbitrarily grouped with one of the lanes. Ideally it would be owned by something else.
pub use_yellow_center_lines: bool,
// Need to remember this just for detecting U-turns here.
pub(crate) other_side: Option<RoadID>,
}
impl PartialEq for Road {
fn eq(&self, other: &Road) -> bool {
self.id == other.id
}
}

22
map_model/src/turn.rs Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
use IntersectionID;
use RoadID;
// TODO reconsider pub usize. maybe outside world shouldnt know.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct TurnID(pub usize);
#[derive(Debug)]
pub struct Turn {
pub id: TurnID,
pub parent: IntersectionID,
pub src: RoadID,
pub dst: RoadID,
}
impl PartialEq for Turn {
fn eq(&self, other: &Turn) -> bool {
self.id == other.id
}
}