mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 17:37:22 +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 ezgui::{Color, GfxCtx};
|
||||||
use geom::{Bounds, Circle, GPSBounds, PolyLine, Polygon, Pt2D};
|
use geom::{Bounds, Circle, GPSBounds, PolyLine, Polygon, Pt2D};
|
||||||
use kml::ExtraShape;
|
use kml::ExtraShape;
|
||||||
|
use map_model::{FindClosest, RoadID};
|
||||||
use objects::{Ctx, ID};
|
use objects::{Ctx, ID};
|
||||||
use render::{RenderOptions, Renderable, EXTRA_SHAPE_POINT_RADIUS, EXTRA_SHAPE_THICKNESS};
|
use render::{RenderOptions, Renderable, EXTRA_SHAPE_POINT_RADIUS, EXTRA_SHAPE_THICKNESS};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@ -27,37 +28,49 @@ pub struct DrawExtraShape {
|
|||||||
pub id: ExtraShapeID,
|
pub id: ExtraShapeID,
|
||||||
shape: Shape,
|
shape: Shape,
|
||||||
pub attributes: BTreeMap<String, String>,
|
pub attributes: BTreeMap<String, String>,
|
||||||
|
road: Option<RoadID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DrawExtraShape {
|
impl DrawExtraShape {
|
||||||
pub fn new(id: ExtraShapeID, s: ExtraShape, gps_bounds: &GPSBounds) -> Option<DrawExtraShape> {
|
pub fn new(
|
||||||
Some(DrawExtraShape {
|
id: ExtraShapeID,
|
||||||
id: id,
|
s: ExtraShape,
|
||||||
shape: if s.points.len() == 1 {
|
gps_bounds: &GPSBounds,
|
||||||
Shape::Circle(Circle::new(
|
closest: &FindClosest<RoadID>,
|
||||||
Pt2D::from_gps(s.points[0], gps_bounds)?,
|
) -> Option<DrawExtraShape> {
|
||||||
EXTRA_SHAPE_POINT_RADIUS,
|
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 {
|
} else {
|
||||||
let width = get_sidewalk_width(&s.attributes)
|
warn!(
|
||||||
.unwrap_or(EXTRA_SHAPE_THICKNESS * si::M)
|
"Discarding ExtraShape because its geometry was broken: {:?}",
|
||||||
.value_unsafe;
|
s.attributes
|
||||||
let mut pts: Vec<Pt2D> = Vec::new();
|
);
|
||||||
for pt in s.points.into_iter() {
|
None
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn center(&self) -> Pt2D {
|
pub fn center(&self) -> Pt2D {
|
||||||
|
@ -6,7 +6,8 @@ use control::ControlMap;
|
|||||||
use geom::{Bounds, Pt2D};
|
use geom::{Bounds, Pt2D};
|
||||||
use kml::ExtraShape;
|
use kml::ExtraShape;
|
||||||
use map_model::{
|
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 objects::ID;
|
||||||
use plugins::hider::Hider;
|
use plugins::hider::Hider;
|
||||||
@ -83,14 +84,25 @@ impl DrawMap {
|
|||||||
.map(|p| DrawParcel::new(p))
|
.map(|p| DrawParcel::new(p))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let gps_bounds = map.get_gps_bounds();
|
|
||||||
let mut extra_shapes: Vec<DrawExtraShape> = Vec::new();
|
let mut extra_shapes: Vec<DrawExtraShape> = Vec::new();
|
||||||
for s in raw_extra_shapes.into_iter() {
|
if !raw_extra_shapes.is_empty() {
|
||||||
if let Some(es) = DrawExtraShape::new(ExtraShapeID(extra_shapes.len()), s, &gps_bounds)
|
// Match shapes with the nearest road
|
||||||
{
|
let mut closest: FindClosest<RoadID> = map_model::FindClosest::new(&map.get_bounds());
|
||||||
extra_shapes.push(es);
|
// 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();
|
let mut bus_stops: HashMap<BusStopID, DrawBusStop> = HashMap::new();
|
||||||
for s in map.all_bus_stops().values() {
|
for s in map.all_bus_stops().values() {
|
||||||
bus_stops.insert(s.id, DrawBusStop::new(s, map));
|
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 building;
|
||||||
mod bus_stop;
|
mod bus_stop;
|
||||||
mod edits;
|
mod edits;
|
||||||
|
mod find_closest;
|
||||||
mod intersection;
|
mod intersection;
|
||||||
mod lane;
|
mod lane;
|
||||||
mod make;
|
mod make;
|
||||||
@ -37,6 +38,7 @@ pub use area::{Area, AreaID, AreaType};
|
|||||||
pub use building::{Building, BuildingID, FrontPath};
|
pub use building::{Building, BuildingID, FrontPath};
|
||||||
pub use bus_stop::{BusRoute, BusStop, BusStopID};
|
pub use bus_stop::{BusRoute, BusStop, BusStopID};
|
||||||
pub use edits::{EditReason, RoadEdits};
|
pub use edits::{EditReason, RoadEdits};
|
||||||
|
pub use find_closest::FindClosest;
|
||||||
pub use intersection::{Intersection, IntersectionID, IntersectionType};
|
pub use intersection::{Intersection, IntersectionID, IntersectionType};
|
||||||
pub use lane::{Lane, LaneID, LaneType, PARKING_SPOT_LENGTH};
|
pub use lane::{Lane, LaneID, LaneType, PARKING_SPOT_LENGTH};
|
||||||
pub use make::RoadSpec;
|
pub use make::RoadSpec;
|
||||||
|
Loading…
Reference in New Issue
Block a user