mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +03:00
making a general helper for finding closest stuff... using it to match extra shapes to road center lines
This commit is contained in:
parent
117adb7f1a
commit
48709b60a0
@ -2,6 +2,7 @@ use dimensioned::si;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::{Bounds, Circle, GPSBounds, PolyLine, Polygon, Pt2D};
|
||||
use kml::ExtraShape;
|
||||
use map_model::{FindClosest, RoadID};
|
||||
use objects::{Ctx, ID};
|
||||
use render::{RenderOptions, Renderable, EXTRA_SHAPE_POINT_RADIUS, EXTRA_SHAPE_THICKNESS};
|
||||
use std::collections::BTreeMap;
|
||||
@ -27,37 +28,49 @@ pub struct DrawExtraShape {
|
||||
pub id: ExtraShapeID,
|
||||
shape: Shape,
|
||||
pub attributes: BTreeMap<String, String>,
|
||||
road: Option<RoadID>,
|
||||
}
|
||||
|
||||
impl DrawExtraShape {
|
||||
pub fn new(id: ExtraShapeID, s: ExtraShape, gps_bounds: &GPSBounds) -> Option<DrawExtraShape> {
|
||||
Some(DrawExtraShape {
|
||||
id: id,
|
||||
shape: if s.points.len() == 1 {
|
||||
Shape::Circle(Circle::new(
|
||||
Pt2D::from_gps(s.points[0], gps_bounds)?,
|
||||
EXTRA_SHAPE_POINT_RADIUS,
|
||||
))
|
||||
pub fn new(
|
||||
id: ExtraShapeID,
|
||||
s: ExtraShape,
|
||||
gps_bounds: &GPSBounds,
|
||||
closest: &FindClosest<RoadID>,
|
||||
) -> Option<DrawExtraShape> {
|
||||
let mut pts: Vec<Pt2D> = Vec::new();
|
||||
for pt in s.points.into_iter() {
|
||||
pts.push(Pt2D::from_gps(pt, gps_bounds)?);
|
||||
}
|
||||
|
||||
if pts.len() == 1 {
|
||||
Some(DrawExtraShape {
|
||||
id,
|
||||
shape: Shape::Circle(Circle::new(pts[0], EXTRA_SHAPE_POINT_RADIUS)),
|
||||
attributes: s.attributes,
|
||||
road: None,
|
||||
})
|
||||
} else {
|
||||
let width = get_sidewalk_width(&s.attributes)
|
||||
.unwrap_or(EXTRA_SHAPE_THICKNESS * si::M)
|
||||
.value_unsafe;
|
||||
let pl = PolyLine::new(pts);
|
||||
let road = closest.match_pts(&pl);
|
||||
if let Some(p) = pl.make_polygons(width) {
|
||||
Some(DrawExtraShape {
|
||||
id,
|
||||
shape: Shape::Polygon(p),
|
||||
attributes: s.attributes,
|
||||
road,
|
||||
})
|
||||
} else {
|
||||
let width = get_sidewalk_width(&s.attributes)
|
||||
.unwrap_or(EXTRA_SHAPE_THICKNESS * si::M)
|
||||
.value_unsafe;
|
||||
let mut pts: Vec<Pt2D> = Vec::new();
|
||||
for pt in s.points.into_iter() {
|
||||
pts.push(Pt2D::from_gps(pt, gps_bounds)?);
|
||||
}
|
||||
if let Some(p) = PolyLine::new(pts).make_polygons(width) {
|
||||
Shape::Polygon(p)
|
||||
} else {
|
||||
warn!(
|
||||
"Discarding ExtraShape because its geometry was broken: {:?}",
|
||||
s.attributes
|
||||
);
|
||||
return None;
|
||||
}
|
||||
},
|
||||
attributes: s.attributes,
|
||||
})
|
||||
warn!(
|
||||
"Discarding ExtraShape because its geometry was broken: {:?}",
|
||||
s.attributes
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn center(&self) -> Pt2D {
|
||||
|
@ -6,7 +6,8 @@ use control::ControlMap;
|
||||
use geom::{Bounds, Pt2D};
|
||||
use kml::ExtraShape;
|
||||
use map_model::{
|
||||
AreaID, BuildingID, BusStopID, IntersectionID, Lane, LaneID, Map, ParcelID, Turn, TurnID,
|
||||
AreaID, BuildingID, BusStopID, FindClosest, IntersectionID, Lane, LaneID, Map, ParcelID,
|
||||
RoadID, Turn, TurnID,
|
||||
};
|
||||
use objects::ID;
|
||||
use plugins::hider::Hider;
|
||||
@ -83,14 +84,25 @@ impl DrawMap {
|
||||
.map(|p| DrawParcel::new(p))
|
||||
.collect();
|
||||
|
||||
let gps_bounds = map.get_gps_bounds();
|
||||
let mut extra_shapes: Vec<DrawExtraShape> = Vec::new();
|
||||
for s in raw_extra_shapes.into_iter() {
|
||||
if let Some(es) = DrawExtraShape::new(ExtraShapeID(extra_shapes.len()), s, &gps_bounds)
|
||||
{
|
||||
extra_shapes.push(es);
|
||||
if !raw_extra_shapes.is_empty() {
|
||||
// Match shapes with the nearest road
|
||||
let mut closest: FindClosest<RoadID> = map_model::FindClosest::new(&map.get_bounds());
|
||||
// TODO double each road into two sides...
|
||||
for r in map.all_roads().iter() {
|
||||
closest.add(r.id, &r.center_pts);
|
||||
}
|
||||
|
||||
let gps_bounds = map.get_gps_bounds();
|
||||
for s in raw_extra_shapes.into_iter() {
|
||||
if let Some(es) =
|
||||
DrawExtraShape::new(ExtraShapeID(extra_shapes.len()), s, &gps_bounds, &closest)
|
||||
{
|
||||
extra_shapes.push(es);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut bus_stops: HashMap<BusStopID, DrawBusStop> = HashMap::new();
|
||||
for s in map.all_bus_stops().values() {
|
||||
bus_stops.insert(s.id, DrawBusStop::new(s, map));
|
||||
|
53
map_model/src/find_closest.rs
Normal file
53
map_model/src/find_closest.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use aabb_quadtree::QuadTree;
|
||||
use geo;
|
||||
use geo::prelude::EuclideanDistance;
|
||||
use geom::{Bounds, PolyLine};
|
||||
use ordered_float::NotNaN;
|
||||
use std::collections::HashMap;
|
||||
|
||||
// TODO Refactor and generalize all of this...
|
||||
|
||||
pub struct FindClosest<K> {
|
||||
// TODO maybe any type of geo:: thing
|
||||
geometries: HashMap<K, geo::LineString<f64>>,
|
||||
quadtree: QuadTree<K>,
|
||||
}
|
||||
|
||||
impl<K> FindClosest<K>
|
||||
where
|
||||
K: Clone + std::cmp::Eq + std::hash::Hash + std::fmt::Debug,
|
||||
{
|
||||
pub fn new(bounds: &Bounds) -> FindClosest<K> {
|
||||
FindClosest {
|
||||
geometries: HashMap::new(),
|
||||
quadtree: QuadTree::default(bounds.as_bbox()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, key: K, pts: &PolyLine) {
|
||||
self.geometries.insert(key.clone(), pts_to_line_string(pts));
|
||||
self.quadtree
|
||||
.insert_with_box(key, pts.get_bounds().as_bbox());
|
||||
}
|
||||
|
||||
pub fn match_pts(&self, pts: &PolyLine) -> Option<K> {
|
||||
let query_geom = pts_to_line_string(pts);
|
||||
let query_bbox = pts.get_bounds().as_bbox();
|
||||
|
||||
self.quadtree
|
||||
.query(query_bbox)
|
||||
.into_iter()
|
||||
.min_by_key(|(key, _, _)| {
|
||||
NotNaN::new(query_geom.euclidean_distance(&self.geometries[key])).unwrap()
|
||||
}).map(|(key, _, _)| key.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn pts_to_line_string(pts: &PolyLine) -> geo::LineString<f64> {
|
||||
let pts: Vec<geo::Point<f64>> = pts
|
||||
.points()
|
||||
.iter()
|
||||
.map(|pt| geo::Point::new(pt.x(), pt.y()))
|
||||
.collect();
|
||||
pts.into()
|
||||
}
|
@ -21,6 +21,7 @@ mod area;
|
||||
mod building;
|
||||
mod bus_stop;
|
||||
mod edits;
|
||||
mod find_closest;
|
||||
mod intersection;
|
||||
mod lane;
|
||||
mod make;
|
||||
@ -37,6 +38,7 @@ pub use area::{Area, AreaID, AreaType};
|
||||
pub use building::{Building, BuildingID, FrontPath};
|
||||
pub use bus_stop::{BusRoute, BusStop, BusStopID};
|
||||
pub use edits::{EditReason, RoadEdits};
|
||||
pub use find_closest::FindClosest;
|
||||
pub use intersection::{Intersection, IntersectionID, IntersectionType};
|
||||
pub use lane::{Lane, LaneID, LaneType, PARKING_SPOT_LENGTH};
|
||||
pub use make::RoadSpec;
|
||||
|
Loading…
Reference in New Issue
Block a user