making a general helper for finding closest stuff... using it to match extra shapes to road center lines

This commit is contained in:
Dustin Carlino 2018-11-21 14:18:43 -08:00
parent 117adb7f1a
commit 48709b60a0
4 changed files with 113 additions and 33 deletions

View File

@ -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 {

View File

@ -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));

View 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()
}

View File

@ -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;