mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-28 12:12:00 +03:00
organizing map making code a little
This commit is contained in:
parent
849a132a6a
commit
69d6c4ab43
@ -69,3 +69,25 @@ timescale for a traffic simulation. :) So, let's switch to u32.
|
||||
easy to understand and test. Maybe the sim_ctrl pattern is nicer? A light
|
||||
adapter to control the thing from the UI? ezgui's textbox and menu are similar
|
||||
-- no rendering, some input handling.
|
||||
|
||||
## Map making
|
||||
|
||||
Stages are roughly:
|
||||
|
||||
- extract parcels inside a bbox from a .kml
|
||||
- load elevation into memory from a .hgt
|
||||
- get raw OSM ways and bbox from a .osm
|
||||
- (elevation, raw OSM ways) -> split up OSM stuff
|
||||
- merge in the parcels fitting the specific bbox
|
||||
- load traffic signal from a .shp and match to nearest intersection
|
||||
|
||||
- create finalish Intersection structs
|
||||
- * split roads into lanes based on lane specs. also update Intersections.
|
||||
- * trim road lines for each intersection
|
||||
- * make turns for each intersection
|
||||
- * make each building, finding the front path using lanes
|
||||
- map over parcels directly
|
||||
|
||||
The live edits will modify lane specs and turns. Will have to re-do starred
|
||||
items most likely. Should be straightforward to only redo small parts of those
|
||||
stages.
|
||||
|
@ -119,7 +119,8 @@ wait slow down even more -- before any of this change, lanes on adjacent roads s
|
||||
- some bldg paths are quite long.
|
||||
- make final Map serializable too
|
||||
- useful to precompute sidewalk paths
|
||||
- reorg map making
|
||||
- waiting on https://github.com/paholg/dimensioned/issues/31 to release
|
||||
- cant easily serialize ordered float, so move away from it first?
|
||||
- small geometry refactorings (like shifting polyline on opposite side, reversing pts)
|
||||
|
||||
|
||||
|
@ -1,11 +1,6 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
use LaneType;
|
||||
use Map;
|
||||
use Pt2D;
|
||||
use RoadID;
|
||||
use geo;
|
||||
use ordered_float::NotNaN;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// TODO reconsider pub usize. maybe outside world shouldnt know.
|
||||
@ -27,62 +22,3 @@ impl PartialEq for Building {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn find_front_path(
|
||||
bldg_points: &Vec<Pt2D>,
|
||||
bldg_osm_tags: &HashMap<String, String>,
|
||||
map: &Map,
|
||||
) -> Option<(Pt2D, Pt2D)> {
|
||||
use geo::prelude::{ClosestPoint, EuclideanDistance};
|
||||
|
||||
if let Some(street_name) = bldg_osm_tags.get("addr:street") {
|
||||
// TODO start from the side of the building, not the center
|
||||
let bldg_center = center(bldg_points);
|
||||
let center_pt = geo::Point::new(bldg_center.x(), bldg_center.y());
|
||||
|
||||
// Find all matching sidewalks with that street name, then find the closest point on
|
||||
// that sidewalk
|
||||
let candidates: Vec<(RoadID, geo::Point<f64>)> = map.all_roads()
|
||||
.iter()
|
||||
.filter_map(|r| {
|
||||
if r.lane_type == LaneType::Sidewalk && r.osm_tags.get("name") == Some(street_name)
|
||||
{
|
||||
if let geo::Closest::SinglePoint(pt) =
|
||||
road_to_line_string(r.id, map).closest_point(¢er_pt)
|
||||
{
|
||||
return Some((r.id, pt));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Some(closest) = candidates
|
||||
.iter()
|
||||
.min_by_key(|pair| NotNaN::new(pair.1.euclidean_distance(¢er_pt)).unwrap())
|
||||
{
|
||||
return Some((bldg_center, Pt2D::new(closest.1.x(), closest.1.y())));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn center(pts: &Vec<Pt2D>) -> Pt2D {
|
||||
let mut x = 0.0;
|
||||
let mut y = 0.0;
|
||||
for pt in pts {
|
||||
x += pt.x();
|
||||
y += pt.y();
|
||||
}
|
||||
let len = pts.len() as f64;
|
||||
Pt2D::new(x / len, y / len)
|
||||
}
|
||||
|
||||
fn road_to_line_string(r: RoadID, map: &Map) -> geo::LineString<f64> {
|
||||
let pts: Vec<geo::Point<f64>> = map.get_r(r)
|
||||
.lane_center_pts
|
||||
.iter()
|
||||
.map(|pt| geo::Point::new(pt.x(), pt.y()))
|
||||
.collect();
|
||||
pts.into()
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ extern crate vecmath;
|
||||
mod building;
|
||||
pub mod geometry;
|
||||
mod intersection;
|
||||
mod make;
|
||||
mod map;
|
||||
mod parcel;
|
||||
mod polyline;
|
||||
|
92
map_model/src/make/buildings.rs
Normal file
92
map_model/src/make/buildings.rs
Normal file
@ -0,0 +1,92 @@
|
||||
use Bounds;
|
||||
use Building;
|
||||
use BuildingID;
|
||||
use LaneType;
|
||||
use Pt2D;
|
||||
use Road;
|
||||
use RoadID;
|
||||
use geo;
|
||||
use geometry;
|
||||
use ordered_float::NotNaN;
|
||||
use raw_data;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub(crate) fn make_building(
|
||||
b: &raw_data::Building,
|
||||
id: BuildingID,
|
||||
bounds: &Bounds,
|
||||
roads: &Vec<Road>,
|
||||
) -> Building {
|
||||
// TODO consume data, so we dont have to clone tags?
|
||||
let points = b.points
|
||||
.iter()
|
||||
.map(|coord| geometry::gps_to_screen_space(&Pt2D::from(coord), bounds))
|
||||
.collect();
|
||||
let front_path = find_front_path(&points, &b.osm_tags, roads);
|
||||
|
||||
Building {
|
||||
points,
|
||||
front_path,
|
||||
id,
|
||||
osm_way_id: b.osm_way_id,
|
||||
osm_tags: b.osm_tags.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn find_front_path(
|
||||
bldg_points: &Vec<Pt2D>,
|
||||
bldg_osm_tags: &HashMap<String, String>,
|
||||
roads: &Vec<Road>,
|
||||
) -> Option<(Pt2D, Pt2D)> {
|
||||
use geo::prelude::{ClosestPoint, EuclideanDistance};
|
||||
|
||||
if let Some(street_name) = bldg_osm_tags.get("addr:street") {
|
||||
// TODO start from the side of the building, not the center
|
||||
let bldg_center = center(bldg_points);
|
||||
let center_pt = geo::Point::new(bldg_center.x(), bldg_center.y());
|
||||
|
||||
// Find all matching sidewalks with that street name, then find the closest point on
|
||||
// that sidewalk
|
||||
let candidates: Vec<(RoadID, geo::Point<f64>)> = roads
|
||||
.iter()
|
||||
.filter_map(|r| {
|
||||
if r.lane_type == LaneType::Sidewalk && r.osm_tags.get("name") == Some(street_name)
|
||||
{
|
||||
if let geo::Closest::SinglePoint(pt) =
|
||||
road_to_line_string(&roads[r.id.0]).closest_point(¢er_pt)
|
||||
{
|
||||
return Some((r.id, pt));
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Some(closest) = candidates
|
||||
.iter()
|
||||
.min_by_key(|pair| NotNaN::new(pair.1.euclidean_distance(¢er_pt)).unwrap())
|
||||
{
|
||||
return Some((bldg_center, Pt2D::new(closest.1.x(), closest.1.y())));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn center(pts: &Vec<Pt2D>) -> Pt2D {
|
||||
let mut x = 0.0;
|
||||
let mut y = 0.0;
|
||||
for pt in pts {
|
||||
x += pt.x();
|
||||
y += pt.y();
|
||||
}
|
||||
let len = pts.len() as f64;
|
||||
Pt2D::new(x / len, y / len)
|
||||
}
|
||||
|
||||
fn road_to_line_string(r: &Road) -> geo::LineString<f64> {
|
||||
let pts: Vec<geo::Point<f64>> = r.lane_center_pts
|
||||
.iter()
|
||||
.map(|pt| geo::Point::new(pt.x(), pt.y()))
|
||||
.collect();
|
||||
pts.into()
|
||||
}
|
87
map_model/src/make/lanes.rs
Normal file
87
map_model/src/make/lanes.rs
Normal file
@ -0,0 +1,87 @@
|
||||
use raw_data;
|
||||
use road::LaneType;
|
||||
use std::iter;
|
||||
|
||||
// (original direction, reversed direction)
|
||||
fn get_lanes(r: &raw_data::Road) -> (Vec<LaneType>, Vec<LaneType>) {
|
||||
let oneway = r.osm_tags.get("oneway") == Some(&"yes".to_string());
|
||||
// These seem to represent weird roundabouts
|
||||
let junction = r.osm_tags.get("junction") == Some(&"yes".to_string());
|
||||
let big_road = r.osm_tags.get("highway") == Some(&"primary".to_string())
|
||||
|| r.osm_tags.get("highway") == Some(&"secondary".to_string());
|
||||
// TODO debugging convenience
|
||||
let only_roads_for_debugging = false;
|
||||
|
||||
if junction {
|
||||
return (vec![LaneType::Driving], Vec::new());
|
||||
}
|
||||
|
||||
let num_driving_lanes = if big_road { 2 } else { 1 };
|
||||
let driving_lanes: Vec<LaneType> = iter::repeat(LaneType::Driving)
|
||||
.take(num_driving_lanes)
|
||||
.collect();
|
||||
if only_roads_for_debugging {
|
||||
if oneway {
|
||||
return (driving_lanes, Vec::new());
|
||||
} else {
|
||||
return (driving_lanes.clone(), driving_lanes);
|
||||
}
|
||||
}
|
||||
|
||||
let mut full_side = driving_lanes;
|
||||
full_side.push(LaneType::Parking);
|
||||
full_side.push(LaneType::Sidewalk);
|
||||
if oneway {
|
||||
(full_side, vec![LaneType::Sidewalk])
|
||||
} else {
|
||||
(full_side.clone(), full_side)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LaneSpec {
|
||||
pub lane_type: LaneType,
|
||||
pub offset: u8,
|
||||
pub reverse_pts: bool,
|
||||
pub offset_for_other_id: Option<isize>,
|
||||
}
|
||||
|
||||
pub(crate) fn get_lane_specs(r: &raw_data::Road) -> Vec<LaneSpec> {
|
||||
let mut specs: Vec<LaneSpec> = Vec::new();
|
||||
|
||||
let (side1_types, side2_types) = get_lanes(r);
|
||||
for (idx, lane_type) in side1_types.iter().enumerate() {
|
||||
// TODO this might be a bit wrong. add unit tests. :)
|
||||
let offset_for_other_id = if *lane_type != LaneType::Driving {
|
||||
None
|
||||
} else if !side2_types.contains(&LaneType::Driving) {
|
||||
None
|
||||
} else if side1_types == side2_types {
|
||||
Some(side1_types.len() as isize)
|
||||
} else {
|
||||
panic!("get_lane_specs case not handled yet");
|
||||
};
|
||||
|
||||
specs.push(LaneSpec {
|
||||
offset_for_other_id,
|
||||
lane_type: *lane_type,
|
||||
offset: idx as u8,
|
||||
reverse_pts: false,
|
||||
});
|
||||
}
|
||||
for (idx, lane_type) in side2_types.iter().enumerate() {
|
||||
let offset_for_other_id = if *lane_type != LaneType::Driving {
|
||||
None
|
||||
} else {
|
||||
Some(-1 * (side1_types.len() as isize))
|
||||
};
|
||||
|
||||
specs.push(LaneSpec {
|
||||
offset_for_other_id,
|
||||
lane_type: *lane_type,
|
||||
offset: idx as u8,
|
||||
reverse_pts: true,
|
||||
});
|
||||
}
|
||||
|
||||
specs
|
||||
}
|
9
map_model/src/make/mod.rs
Normal file
9
map_model/src/make/mod.rs
Normal file
@ -0,0 +1,9 @@
|
||||
mod buildings;
|
||||
mod lanes;
|
||||
mod trim_lines;
|
||||
mod turns;
|
||||
|
||||
pub(crate) use self::buildings::make_building;
|
||||
pub(crate) use self::lanes::get_lane_specs;
|
||||
pub(crate) use self::trim_lines::trim_lines;
|
||||
pub(crate) use self::turns::make_turns;
|
54
map_model/src/make/trim_lines.rs
Normal file
54
map_model/src/make/trim_lines.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use Pt2D;
|
||||
use dimensioned::si;
|
||||
use geometry;
|
||||
use intersection::Intersection;
|
||||
use road::{Road, RoadID};
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
pub(crate) fn trim_lines(roads: &mut Vec<Road>, i: &Intersection) {
|
||||
let mut shortest_first_line: HashMap<RoadID, (Pt2D, Pt2D, si::Meter<f64>)> = HashMap::new();
|
||||
let mut shortest_last_line: HashMap<RoadID, (Pt2D, Pt2D, si::Meter<f64>)> = HashMap::new();
|
||||
|
||||
fn update_shortest(
|
||||
m: &mut HashMap<RoadID, (Pt2D, Pt2D, si::Meter<f64>)>,
|
||||
r: RoadID,
|
||||
l: (Pt2D, Pt2D),
|
||||
) {
|
||||
let new_len = geometry::euclid_dist(l);
|
||||
|
||||
match m.entry(r) {
|
||||
Entry::Occupied(mut o) => {
|
||||
if new_len < o.get().2 {
|
||||
o.insert((l.0, l.1, new_len));
|
||||
}
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert((l.0, l.1, new_len));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For short first/last lines, this might not work well
|
||||
for incoming in &i.incoming_roads {
|
||||
for outgoing in &i.outgoing_roads {
|
||||
let l1 = roads[incoming.0].last_line();
|
||||
let l2 = roads[outgoing.0].first_line();
|
||||
if let Some(hit) = geometry::line_segment_intersection(l1, l2) {
|
||||
update_shortest(&mut shortest_last_line, *incoming, (l1.0, hit));
|
||||
update_shortest(&mut shortest_first_line, *outgoing, (hit, l2.1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the updates
|
||||
for (id, triple) in &shortest_first_line {
|
||||
roads[id.0].lane_center_pts[0] = triple.0;
|
||||
roads[id.0].lane_center_pts[1] = triple.1;
|
||||
}
|
||||
for (id, triple) in &shortest_last_line {
|
||||
let len = roads[id.0].lane_center_pts.len();
|
||||
roads[id.0].lane_center_pts[len - 2] = triple.0;
|
||||
roads[id.0].lane_center_pts[len - 1] = triple.1;
|
||||
}
|
||||
}
|
52
map_model/src/make/turns.rs
Normal file
52
map_model/src/make/turns.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use Map;
|
||||
use intersection::Intersection;
|
||||
use road::{LaneType, RoadID};
|
||||
use turn::{Turn, TurnID};
|
||||
|
||||
pub(crate) fn make_turns(i: &Intersection, m: &Map, turn_id_start: usize) -> Vec<Turn> {
|
||||
let incoming: Vec<RoadID> = i.incoming_roads
|
||||
.iter()
|
||||
// TODO why's this double borrow happen?
|
||||
.filter(|id| m.get_r(**id).lane_type == LaneType::Driving)
|
||||
.map(|id| *id)
|
||||
.collect();
|
||||
let outgoing: Vec<RoadID> = i.outgoing_roads
|
||||
.iter()
|
||||
.filter(|id| m.get_r(**id).lane_type == LaneType::Driving)
|
||||
.map(|id| *id)
|
||||
.collect();
|
||||
|
||||
// TODO: Figure out why this happens in the huge map
|
||||
if incoming.is_empty() {
|
||||
println!("WARNING: intersection {:?} has no incoming roads", i);
|
||||
return Vec::new();
|
||||
}
|
||||
if outgoing.is_empty() {
|
||||
println!("WARNING: intersection {:?} has no outgoing roads", i);
|
||||
return Vec::new();
|
||||
}
|
||||
let dead_end = incoming.len() == 1 && outgoing.len() == 1;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for src in &incoming {
|
||||
let src_r = m.get_r(*src);
|
||||
for dst in &outgoing {
|
||||
let dst_r = m.get_r(*dst);
|
||||
// Don't create U-turns unless it's a dead-end
|
||||
if src_r.other_side == Some(dst_r.id) && !dead_end {
|
||||
continue;
|
||||
}
|
||||
|
||||
let id = TurnID(turn_id_start + result.len());
|
||||
result.push(Turn {
|
||||
id,
|
||||
parent: i.id,
|
||||
src: *src,
|
||||
dst: *dst,
|
||||
src_pt: src_r.last_pt(),
|
||||
dst_pt: dst_r.first_pt(),
|
||||
});
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
@ -3,18 +3,17 @@
|
||||
use Bounds;
|
||||
use Pt2D;
|
||||
use abstutil;
|
||||
use building;
|
||||
use building::{Building, BuildingID};
|
||||
use dimensioned::si;
|
||||
use geometry;
|
||||
use intersection::{Intersection, IntersectionID};
|
||||
use make;
|
||||
use parcel::{Parcel, ParcelID};
|
||||
use raw_data;
|
||||
use road::{LaneType, Road, RoadID};
|
||||
use road::{Road, RoadID};
|
||||
use shift_polyline;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Error;
|
||||
use std::iter;
|
||||
use turn::{Turn, TurnID};
|
||||
|
||||
pub struct Map {
|
||||
@ -63,7 +62,8 @@ impl Map {
|
||||
|
||||
let mut counter = 0;
|
||||
for r in &data.roads {
|
||||
for lane in get_lane_specs(r) {
|
||||
// TODO move this to make/lanes.rs too
|
||||
for lane in make::get_lane_specs(r) {
|
||||
let id = RoadID(counter);
|
||||
counter += 1;
|
||||
let other_side = lane.offset_for_other_id
|
||||
@ -114,32 +114,20 @@ impl Map {
|
||||
}
|
||||
|
||||
for i in &m.intersections {
|
||||
trim_lines(&mut m.roads, i);
|
||||
make::trim_lines(&mut m.roads, i);
|
||||
}
|
||||
|
||||
for i in &m.intersections {
|
||||
let turns = make_turns(i, &m);
|
||||
let turns = make::make_turns(i, &m, m.turns.len());
|
||||
m.turns.extend(turns);
|
||||
}
|
||||
for t in &m.turns {
|
||||
m.intersections[t.parent.0].turns.push(t.id);
|
||||
}
|
||||
|
||||
// TODO consume data, so we dont have to clone tags?
|
||||
for (idx, b) in data.buildings.iter().enumerate() {
|
||||
let points = b.points
|
||||
.iter()
|
||||
.map(|coord| geometry::gps_to_screen_space(&Pt2D::from(coord), &bounds))
|
||||
.collect();
|
||||
let front_path = building::find_front_path(&points, &b.osm_tags, &m);
|
||||
|
||||
m.buildings.push(Building {
|
||||
points,
|
||||
front_path,
|
||||
id: BuildingID(idx),
|
||||
osm_way_id: b.osm_way_id,
|
||||
osm_tags: b.osm_tags.clone(),
|
||||
});
|
||||
m.buildings
|
||||
.push(make::make_building(b, BuildingID(idx), &bounds, &m.roads));
|
||||
}
|
||||
|
||||
for (idx, p) in data.parcels.iter().enumerate() {
|
||||
@ -236,184 +224,3 @@ impl Map {
|
||||
self.bounds.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO organize these differently
|
||||
fn make_turns(i: &Intersection, m: &Map) -> Vec<Turn> {
|
||||
let incoming: Vec<RoadID> = i.incoming_roads
|
||||
.iter()
|
||||
.filter(|id| m.roads[id.0].lane_type == LaneType::Driving)
|
||||
.map(|id| *id)
|
||||
.collect();
|
||||
let outgoing: Vec<RoadID> = i.outgoing_roads
|
||||
.iter()
|
||||
.filter(|id| m.roads[id.0].lane_type == LaneType::Driving)
|
||||
.map(|id| *id)
|
||||
.collect();
|
||||
|
||||
// TODO: Figure out why this happens in the huge map
|
||||
if incoming.is_empty() {
|
||||
println!("WARNING: intersection {:?} has no incoming roads", i);
|
||||
return Vec::new();
|
||||
}
|
||||
if outgoing.is_empty() {
|
||||
println!("WARNING: intersection {:?} has no outgoing roads", i);
|
||||
return Vec::new();
|
||||
}
|
||||
let dead_end = incoming.len() == 1 && outgoing.len() == 1;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for src in &incoming {
|
||||
let src_r = &m.roads[src.0];
|
||||
for dst in &outgoing {
|
||||
let dst_r = &m.roads[dst.0];
|
||||
// Don't create U-turns unless it's a dead-end
|
||||
if src_r.other_side == Some(dst_r.id) && !dead_end {
|
||||
continue;
|
||||
}
|
||||
|
||||
let id = TurnID(m.turns.len() + result.len());
|
||||
result.push(Turn {
|
||||
id,
|
||||
parent: i.id,
|
||||
src: *src,
|
||||
dst: *dst,
|
||||
src_pt: src_r.last_pt(),
|
||||
dst_pt: dst_r.first_pt(),
|
||||
});
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn trim_lines(roads: &mut Vec<Road>, i: &Intersection) {
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
let mut shortest_first_line: HashMap<RoadID, (Pt2D, Pt2D, si::Meter<f64>)> = HashMap::new();
|
||||
let mut shortest_last_line: HashMap<RoadID, (Pt2D, Pt2D, si::Meter<f64>)> = HashMap::new();
|
||||
|
||||
fn update_shortest(
|
||||
m: &mut HashMap<RoadID, (Pt2D, Pt2D, si::Meter<f64>)>,
|
||||
r: RoadID,
|
||||
l: (Pt2D, Pt2D),
|
||||
) {
|
||||
let new_len = geometry::euclid_dist(l);
|
||||
|
||||
match m.entry(r) {
|
||||
Entry::Occupied(mut o) => {
|
||||
if new_len < o.get().2 {
|
||||
o.insert((l.0, l.1, new_len));
|
||||
}
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
v.insert((l.0, l.1, new_len));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For short first/last lines, this might not work well
|
||||
for incoming in &i.incoming_roads {
|
||||
for outgoing in &i.outgoing_roads {
|
||||
let l1 = roads[incoming.0].last_line();
|
||||
let l2 = roads[outgoing.0].first_line();
|
||||
if let Some(hit) = geometry::line_segment_intersection(l1, l2) {
|
||||
update_shortest(&mut shortest_last_line, *incoming, (l1.0, hit));
|
||||
update_shortest(&mut shortest_first_line, *outgoing, (hit, l2.1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the updates
|
||||
for (id, triple) in &shortest_first_line {
|
||||
roads[id.0].lane_center_pts[0] = triple.0;
|
||||
roads[id.0].lane_center_pts[1] = triple.1;
|
||||
}
|
||||
for (id, triple) in &shortest_last_line {
|
||||
let len = roads[id.0].lane_center_pts.len();
|
||||
roads[id.0].lane_center_pts[len - 2] = triple.0;
|
||||
roads[id.0].lane_center_pts[len - 1] = triple.1;
|
||||
}
|
||||
}
|
||||
|
||||
// (original direction, reversed direction)
|
||||
fn get_lanes(r: &raw_data::Road) -> (Vec<LaneType>, Vec<LaneType>) {
|
||||
let oneway = r.osm_tags.get("oneway") == Some(&"yes".to_string());
|
||||
// These seem to represent weird roundabouts
|
||||
let junction = r.osm_tags.get("junction") == Some(&"yes".to_string());
|
||||
let big_road = r.osm_tags.get("highway") == Some(&"primary".to_string())
|
||||
|| r.osm_tags.get("highway") == Some(&"secondary".to_string());
|
||||
// TODO debugging convenience
|
||||
let only_roads_for_debugging = false;
|
||||
|
||||
if junction {
|
||||
return (vec![LaneType::Driving], Vec::new());
|
||||
}
|
||||
|
||||
let num_driving_lanes = if big_road { 2 } else { 1 };
|
||||
let driving_lanes: Vec<LaneType> = iter::repeat(LaneType::Driving)
|
||||
.take(num_driving_lanes)
|
||||
.collect();
|
||||
if only_roads_for_debugging {
|
||||
if oneway {
|
||||
return (driving_lanes, Vec::new());
|
||||
} else {
|
||||
return (driving_lanes.clone(), driving_lanes);
|
||||
}
|
||||
}
|
||||
|
||||
let mut full_side = driving_lanes;
|
||||
full_side.push(LaneType::Parking);
|
||||
full_side.push(LaneType::Sidewalk);
|
||||
if oneway {
|
||||
(full_side, vec![LaneType::Sidewalk])
|
||||
} else {
|
||||
(full_side.clone(), full_side)
|
||||
}
|
||||
}
|
||||
|
||||
struct LaneSpec {
|
||||
lane_type: LaneType,
|
||||
offset: u8,
|
||||
reverse_pts: bool,
|
||||
offset_for_other_id: Option<isize>,
|
||||
}
|
||||
|
||||
fn get_lane_specs(r: &raw_data::Road) -> Vec<LaneSpec> {
|
||||
let mut specs: Vec<LaneSpec> = Vec::new();
|
||||
|
||||
let (side1_types, side2_types) = get_lanes(r);
|
||||
for (idx, lane_type) in side1_types.iter().enumerate() {
|
||||
// TODO this might be a bit wrong. add unit tests. :)
|
||||
let offset_for_other_id = if *lane_type != LaneType::Driving {
|
||||
None
|
||||
} else if !side2_types.contains(&LaneType::Driving) {
|
||||
None
|
||||
} else if side1_types == side2_types {
|
||||
Some(side1_types.len() as isize)
|
||||
} else {
|
||||
panic!("get_lane_specs case not handled yet");
|
||||
};
|
||||
|
||||
specs.push(LaneSpec {
|
||||
offset_for_other_id,
|
||||
lane_type: *lane_type,
|
||||
offset: idx as u8,
|
||||
reverse_pts: false,
|
||||
});
|
||||
}
|
||||
for (idx, lane_type) in side2_types.iter().enumerate() {
|
||||
let offset_for_other_id = if *lane_type != LaneType::Driving {
|
||||
None
|
||||
} else {
|
||||
Some(-1 * (side1_types.len() as isize))
|
||||
};
|
||||
|
||||
specs.push(LaneSpec {
|
||||
offset_for_other_id,
|
||||
lane_type: *lane_type,
|
||||
offset: idx as u8,
|
||||
reverse_pts: true,
|
||||
});
|
||||
}
|
||||
|
||||
specs
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user