mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +03:00
using quadtrees to MASSIVELY speed up sidewalk matching
This commit is contained in:
parent
65d8c92f63
commit
15da2fe9cd
@ -3,7 +3,7 @@
|
||||
use aabb_quadtree::geom::{Point, Rect};
|
||||
use aabb_quadtree::QuadTree;
|
||||
use control::ControlMap;
|
||||
use geom::{Bounds, LonLat, Pt2D};
|
||||
use geom::{Bounds, Pt2D};
|
||||
use kml::{ExtraShape, ExtraShapeID};
|
||||
use map_model::{
|
||||
AreaID, BuildingID, BusStopID, IntersectionID, Lane, LaneID, Map, ParcelID, Turn, TurnID,
|
||||
@ -83,17 +83,8 @@ impl DrawMap {
|
||||
}
|
||||
let areas: Vec<DrawArea> = map.all_areas().iter().map(|a| DrawArea::new(a)).collect();
|
||||
|
||||
// min_y here due to the wacky y inversion
|
||||
let bounds = map.get_gps_bounds();
|
||||
let max_screen_pt =
|
||||
Pt2D::from_gps(LonLat::new(bounds.max_x, bounds.min_y), &bounds).unwrap();
|
||||
let map_bbox = Rect {
|
||||
top_left: Point { x: 0.0, y: 0.0 },
|
||||
bottom_right: Point {
|
||||
x: max_screen_pt.x() as f32,
|
||||
y: max_screen_pt.y() as f32,
|
||||
},
|
||||
};
|
||||
let bounds = map.get_bounds();
|
||||
let map_bbox = get_bbox(bounds);
|
||||
|
||||
let mut quadtree = QuadTree::default(map_bbox);
|
||||
// TODO use iter chain if everything was boxed as a renderable...
|
||||
@ -129,7 +120,7 @@ impl DrawMap {
|
||||
bus_stops,
|
||||
areas,
|
||||
|
||||
center_pt: Pt2D::new(max_screen_pt.x() / 2.0, max_screen_pt.y() / 2.0),
|
||||
center_pt: Pt2D::new(bounds.max_x / 2.0, bounds.max_y / 2.0),
|
||||
|
||||
quadtree,
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use aabb_quadtree::geom::{Point, Rect};
|
||||
use aabb_quadtree::QuadTree;
|
||||
use ezgui::{Color, GfxCtx};
|
||||
use geom::{Bounds, Line, LonLat, Polygon, Pt2D};
|
||||
use geom::{Bounds, Line, Polygon, Pt2D};
|
||||
use map_model::{Building, BuildingID, Map, Road, RoadID, LANE_THICKNESS};
|
||||
|
||||
// black
|
||||
@ -26,15 +26,12 @@ pub struct DrawMap {
|
||||
impl DrawMap {
|
||||
pub fn new(map: Map) -> DrawMap {
|
||||
// TODO This stuff is common!
|
||||
// min_y here due to the wacky y inversion
|
||||
let bounds = map.get_gps_bounds();
|
||||
let max_screen_pt =
|
||||
Pt2D::from_gps(LonLat::new(bounds.max_x, bounds.min_y), &bounds).unwrap();
|
||||
let bounds = map.get_bounds();
|
||||
let map_bbox = Rect {
|
||||
top_left: Point { x: 0.0, y: 0.0 },
|
||||
bottom_right: Point {
|
||||
x: max_screen_pt.x() as f32,
|
||||
y: max_screen_pt.y() as f32,
|
||||
x: bounds.max_x as f32,
|
||||
y: bounds.max_y as f32,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
authors = ["Dustin Carlino <dabreegster@gmail.com>"]
|
||||
|
||||
[dependencies]
|
||||
aabb-quadtree = "0.1.0"
|
||||
abstutil = { path = "../abstutil" }
|
||||
dimensioned = { git = "https://github.com/paholg/dimensioned", rev = "0e1076ebfa5128d1ee544bdc9754c948987b6fe3", features = ["serde"] }
|
||||
flame = "0.2.2"
|
||||
|
@ -1,3 +1,4 @@
|
||||
extern crate aabb_quadtree;
|
||||
extern crate abstutil;
|
||||
extern crate dimensioned;
|
||||
extern crate flame;
|
||||
|
@ -9,6 +9,7 @@ use {Building, BuildingID, FrontPath, Lane};
|
||||
pub fn make_all_buildings(
|
||||
results: &mut Vec<Building>,
|
||||
input: &Vec<raw_data::Building>,
|
||||
gps_bounds: &Bounds,
|
||||
bounds: &Bounds,
|
||||
lanes: &Vec<Lane>,
|
||||
) {
|
||||
@ -21,7 +22,7 @@ pub fn make_all_buildings(
|
||||
let pts = b
|
||||
.points
|
||||
.iter()
|
||||
.map(|coord| Pt2D::from_gps(*coord, bounds).unwrap())
|
||||
.map(|coord| Pt2D::from_gps(*coord, gps_bounds).unwrap())
|
||||
.collect();
|
||||
let center: HashablePt2D = Pt2D::center(&pts).into();
|
||||
pts_per_bldg.push(pts);
|
||||
@ -29,34 +30,31 @@ pub fn make_all_buildings(
|
||||
query.insert(center);
|
||||
}
|
||||
|
||||
let sidewalk_pts = find_sidewalk_points(query, lanes);
|
||||
// Skip buildings that're too far away from their sidewalk
|
||||
let sidewalk_pts = find_sidewalk_points(bounds, query, lanes, 100.0 * si::M);
|
||||
|
||||
let mut progress = Progress::new("create building front paths", pts_per_bldg.len());
|
||||
for (idx, points) in pts_per_bldg.into_iter().enumerate() {
|
||||
progress.next();
|
||||
let bldg_center = center_per_bldg[idx];
|
||||
let (sidewalk, dist_along) = sidewalk_pts[&bldg_center];
|
||||
let (sidewalk_pt, _) = lanes[sidewalk.0].dist_along(dist_along);
|
||||
let line = trim_front_path(&points, Line::new(bldg_center.into(), sidewalk_pt));
|
||||
if let Some((sidewalk, dist_along)) = sidewalk_pts.get(&bldg_center) {
|
||||
let (sidewalk_pt, _) = lanes[sidewalk.0].dist_along(*dist_along);
|
||||
let line = trim_front_path(&points, Line::new(bldg_center.into(), sidewalk_pt));
|
||||
|
||||
// Trim buildings that are too far away from their sidewalk
|
||||
if line.length() > 100.0 * si::M {
|
||||
continue;
|
||||
let id = BuildingID(results.len());
|
||||
results.push(Building {
|
||||
id,
|
||||
points,
|
||||
osm_tags: input[idx].osm_tags.clone(),
|
||||
osm_way_id: input[idx].osm_way_id,
|
||||
front_path: FrontPath {
|
||||
bldg: id,
|
||||
sidewalk: *sidewalk,
|
||||
line,
|
||||
dist_along_sidewalk: *dist_along,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let id = BuildingID(results.len());
|
||||
results.push(Building {
|
||||
id,
|
||||
points,
|
||||
osm_tags: input[idx].osm_tags.clone(),
|
||||
osm_way_id: input[idx].osm_way_id,
|
||||
front_path: FrontPath {
|
||||
bldg: id,
|
||||
sidewalk: sidewalk,
|
||||
line,
|
||||
dist_along_sidewalk: dist_along,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let discarded = input.len() - results.len();
|
||||
|
@ -12,13 +12,14 @@ pub fn make_bus_stops(
|
||||
lanes: &mut Vec<Lane>,
|
||||
roads: &Vec<Road>,
|
||||
bus_routes: &Vec<gtfs::Route>,
|
||||
gps_bounds: &Bounds,
|
||||
bounds: &Bounds,
|
||||
) -> (BTreeMap<BusStopID, BusStop>, Vec<BusRoute>) {
|
||||
let mut bus_stop_pts: HashSet<HashablePt2D> = HashSet::new();
|
||||
let mut route_lookups: MultiMap<String, HashablePt2D> = MultiMap::new();
|
||||
for route in bus_routes {
|
||||
for gps in &route.stops {
|
||||
if let Some(pt) = Pt2D::from_gps(*gps, bounds) {
|
||||
if let Some(pt) = Pt2D::from_gps(*gps, gps_bounds) {
|
||||
let hash_pt: HashablePt2D = pt.into();
|
||||
bus_stop_pts.insert(hash_pt);
|
||||
route_lookups.insert(route.name.to_string(), hash_pt);
|
||||
@ -27,7 +28,9 @@ pub fn make_bus_stops(
|
||||
}
|
||||
|
||||
let mut stops_per_sidewalk: MultiMap<LaneID, (si::Meter<f64>, HashablePt2D)> = MultiMap::new();
|
||||
for (pt, (lane, dist_along)) in find_sidewalk_points(bus_stop_pts, lanes).iter() {
|
||||
for (pt, (lane, dist_along)) in
|
||||
find_sidewalk_points(bounds, bus_stop_pts, lanes, 10.0 * si::M).iter()
|
||||
{
|
||||
stops_per_sidewalk.insert(*lane, (*dist_along, *pt));
|
||||
}
|
||||
let mut point_to_stop_id: HashMap<HashablePt2D, BusStopID> = HashMap::new();
|
||||
|
@ -1,5 +1,5 @@
|
||||
use dimensioned::si;
|
||||
use geom::{Bounds, HashablePt2D, Line, Pt2D};
|
||||
use geom::{Bounds, HashablePt2D, Pt2D};
|
||||
use make::sidewalk_finder::find_sidewalk_points;
|
||||
use raw_data;
|
||||
use std::collections::HashSet;
|
||||
@ -8,6 +8,7 @@ use {Lane, Parcel, ParcelID};
|
||||
pub fn make_all_parcels(
|
||||
results: &mut Vec<Parcel>,
|
||||
input: &Vec<raw_data::Parcel>,
|
||||
gps_bounds: &Bounds,
|
||||
bounds: &Bounds,
|
||||
lanes: &Vec<Lane>,
|
||||
) {
|
||||
@ -18,7 +19,7 @@ pub fn make_all_parcels(
|
||||
let pts = p
|
||||
.points
|
||||
.iter()
|
||||
.map(|coord| Pt2D::from_gps(*coord, bounds).unwrap())
|
||||
.map(|coord| Pt2D::from_gps(*coord, gps_bounds).unwrap())
|
||||
.collect();
|
||||
let center: HashablePt2D = Pt2D::center(&pts).into();
|
||||
pts_per_parcel.push(pts);
|
||||
@ -26,23 +27,18 @@ pub fn make_all_parcels(
|
||||
query.insert(center);
|
||||
}
|
||||
|
||||
let sidewalk_pts = find_sidewalk_points(query, lanes);
|
||||
// Trim parcels that are too far away from the nearest sidewalk
|
||||
let sidewalk_pts = find_sidewalk_points(bounds, query, lanes, 100.0 * si::M);
|
||||
|
||||
for (idx, center) in center_per_parcel.into_iter().enumerate() {
|
||||
let (sidewalk, dist_along) = sidewalk_pts[¢er];
|
||||
let (sidewalk_pt, _) = lanes[sidewalk.0].dist_along(dist_along);
|
||||
let line = Line::new(center.into(), sidewalk_pt);
|
||||
// Trim parcels that are too far away from the nearest sidewalk
|
||||
if line.length() > 100.0 * si::M {
|
||||
continue;
|
||||
if sidewalk_pts.contains_key(¢er) {
|
||||
let id = ParcelID(results.len());
|
||||
results.push(Parcel {
|
||||
id,
|
||||
points: pts_per_parcel[idx].clone(),
|
||||
block: input[idx].block,
|
||||
});
|
||||
}
|
||||
|
||||
let id = ParcelID(results.len());
|
||||
results.push(Parcel {
|
||||
id,
|
||||
points: pts_per_parcel[idx].clone(),
|
||||
block: input[idx].block,
|
||||
});
|
||||
}
|
||||
let discarded = input.len() - results.len();
|
||||
if discarded > 0 {
|
||||
|
@ -1,50 +1,70 @@
|
||||
use aabb_quadtree::geom::{Point, Rect};
|
||||
use aabb_quadtree::QuadTree;
|
||||
use abstutil::Progress;
|
||||
use dimensioned::si;
|
||||
use geo;
|
||||
use geo::prelude::{ClosestPoint, EuclideanDistance};
|
||||
use geom::{HashablePt2D, Pt2D};
|
||||
use geom::{Bounds, HashablePt2D, Pt2D};
|
||||
use ordered_float::NotNaN;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use {Lane, LaneID};
|
||||
|
||||
// If the result doesn't contain a requested point, then there was no matching sidewalk close
|
||||
// enough.
|
||||
pub fn find_sidewalk_points(
|
||||
bounds: &Bounds,
|
||||
pts: HashSet<HashablePt2D>,
|
||||
lanes: &Vec<Lane>,
|
||||
max_dist_away: si::Meter<f64>,
|
||||
) -> HashMap<HashablePt2D, (LaneID, si::Meter<f64>)> {
|
||||
// Get LineStrings of all lanes once.
|
||||
// Convert all sidewalks to LineStrings and index them with a quadtree.
|
||||
let mut lane_lines_quadtree: QuadTree<usize> = QuadTree::default(get_bbox(bounds));
|
||||
let mut lane_lines: Vec<(LaneID, geo::LineString<f64>)> = Vec::new();
|
||||
let mut progress = Progress::new("lanes to LineStrings", lanes.len());
|
||||
let line_strings: Vec<(LaneID, geo::LineString<f64>)> = lanes
|
||||
.iter()
|
||||
.filter_map(|l| {
|
||||
progress.next();
|
||||
if l.is_sidewalk() {
|
||||
Some((l.id, lane_to_line_string(l)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect();
|
||||
for l in lanes {
|
||||
progress.next();
|
||||
if l.is_sidewalk() {
|
||||
lane_lines.push((l.id, lane_to_line_string(l)));
|
||||
lane_lines_quadtree.insert_with_box(lane_lines.len() - 1, lane_to_rect(l));
|
||||
}
|
||||
}
|
||||
|
||||
// For each point, find the closest point to any sidewalk
|
||||
// For each point, find the closest point to any sidewalk, using the quadtree to prune the
|
||||
// search.
|
||||
let mut results: HashMap<HashablePt2D, (LaneID, si::Meter<f64>)> = HashMap::new();
|
||||
let mut progress = Progress::new("find closest sidewalk point", pts.len());
|
||||
for query_pt in pts {
|
||||
progress.next();
|
||||
let query_geo_pt = geo::Point::new(query_pt.x(), query_pt.y());
|
||||
let (sidewalk, raw_pt) = line_strings
|
||||
.iter()
|
||||
.filter_map(|(id, lines)| {
|
||||
let query_bbox = Rect {
|
||||
top_left: Point {
|
||||
x: (query_pt.x() - max_dist_away.value_unsafe) as f32,
|
||||
y: (query_pt.y() - max_dist_away.value_unsafe) as f32,
|
||||
},
|
||||
bottom_right: Point {
|
||||
x: (query_pt.x() + max_dist_away.value_unsafe) as f32,
|
||||
y: (query_pt.y() + max_dist_away.value_unsafe) as f32,
|
||||
},
|
||||
};
|
||||
|
||||
if let Some((sidewalk, raw_pt)) = lane_lines_quadtree
|
||||
.query(query_bbox)
|
||||
.into_iter()
|
||||
.filter_map(|(idx, _, _)| {
|
||||
let (id, lines) = &lane_lines[*idx];
|
||||
if let geo::Closest::SinglePoint(pt) = lines.closest_point(&query_geo_pt) {
|
||||
Some((id, pt))
|
||||
Some((*id, pt))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).min_by_key(|(_, pt)| NotNaN::new(pt.euclidean_distance(&query_geo_pt)).unwrap())
|
||||
.unwrap();
|
||||
let sidewalk_pt = Pt2D::new(raw_pt.x(), raw_pt.y());
|
||||
if let Some(dist_along) = lanes[sidewalk.0].dist_along_of_point(sidewalk_pt) {
|
||||
results.insert(query_pt.into(), (*sidewalk, dist_along));
|
||||
} else {
|
||||
panic!("{} isn't on {} according to dist_along_of_point, even though closest_point thinks it is.\n{}", sidewalk_pt, sidewalk, lanes[sidewalk.0].lane_center_pts);
|
||||
{
|
||||
let sidewalk_pt = Pt2D::new(raw_pt.x(), raw_pt.y());
|
||||
if let Some(dist_along) = lanes[sidewalk.0].dist_along_of_point(sidewalk_pt) {
|
||||
results.insert(query_pt.into(), (sidewalk, dist_along));
|
||||
} else {
|
||||
panic!("{} isn't on {} according to dist_along_of_point, even though closest_point thinks it is.\n{}", sidewalk_pt, sidewalk, lanes[sidewalk.0].lane_center_pts);
|
||||
}
|
||||
}
|
||||
}
|
||||
results
|
||||
@ -59,3 +79,24 @@ fn lane_to_line_string(l: &Lane) -> geo::LineString<f64> {
|
||||
.collect();
|
||||
pts.into()
|
||||
}
|
||||
|
||||
fn lane_to_rect(l: &Lane) -> Rect {
|
||||
let mut b = Bounds::new();
|
||||
for pt in l.lane_center_pts.points() {
|
||||
b.update_pt(*pt);
|
||||
}
|
||||
get_bbox(&b)
|
||||
}
|
||||
|
||||
fn get_bbox(b: &Bounds) -> Rect {
|
||||
Rect {
|
||||
top_left: Point {
|
||||
x: b.min_x as f32,
|
||||
y: b.min_y as f32,
|
||||
},
|
||||
bottom_right: Point {
|
||||
x: b.max_x as f32,
|
||||
y: b.max_y as f32,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use abstutil;
|
||||
use abstutil::{Error, Progress};
|
||||
use edits::RoadEdits;
|
||||
use flame;
|
||||
use geom::{Bounds, HashablePt2D, PolyLine, Pt2D};
|
||||
use geom::{Bounds, HashablePt2D, LonLat, PolyLine, Pt2D};
|
||||
use make;
|
||||
use raw_data;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
@ -28,7 +28,7 @@ pub struct Map {
|
||||
areas: Vec<Area>,
|
||||
|
||||
// TODO maybe dont need to retain GPS stuff later
|
||||
bounds: Bounds,
|
||||
gps_bounds: Bounds,
|
||||
|
||||
name: String,
|
||||
road_edits: RoadEdits,
|
||||
@ -55,11 +55,12 @@ impl Map {
|
||||
}
|
||||
|
||||
pub fn create_from_raw(name: String, data: raw_data::Map, road_edits: RoadEdits) -> Map {
|
||||
let bounds = data.get_gps_bounds();
|
||||
let gps_bounds = data.get_gps_bounds();
|
||||
let bounds = gps_to_map_bounds(&gps_bounds);
|
||||
let mut m = Map {
|
||||
name,
|
||||
road_edits,
|
||||
bounds,
|
||||
gps_bounds,
|
||||
roads: Vec::new(),
|
||||
lanes: Vec::new(),
|
||||
intersections: Vec::new(),
|
||||
@ -75,7 +76,7 @@ impl Map {
|
||||
|
||||
for (idx, i) in data.intersections.iter().enumerate() {
|
||||
let id = IntersectionID(idx);
|
||||
let pt = Pt2D::from_gps(i.point, &bounds).unwrap();
|
||||
let pt = Pt2D::from_gps(i.point, &gps_bounds).unwrap();
|
||||
m.intersections.push(Intersection {
|
||||
id,
|
||||
point: pt,
|
||||
@ -94,7 +95,7 @@ impl Map {
|
||||
let road_center_pts = PolyLine::new(
|
||||
r.points
|
||||
.iter()
|
||||
.map(|coord| Pt2D::from_gps(*coord, &bounds).unwrap())
|
||||
.map(|coord| Pt2D::from_gps(*coord, &gps_bounds).unwrap())
|
||||
.collect(),
|
||||
);
|
||||
|
||||
@ -166,8 +167,13 @@ impl Map {
|
||||
}
|
||||
}
|
||||
|
||||
let (stops, routes) =
|
||||
make::make_bus_stops(&mut m.lanes, &m.roads, &data.bus_routes, &bounds);
|
||||
let (stops, routes) = make::make_bus_stops(
|
||||
&mut m.lanes,
|
||||
&m.roads,
|
||||
&data.bus_routes,
|
||||
&gps_bounds,
|
||||
&bounds,
|
||||
);
|
||||
m.bus_stops = stops;
|
||||
|
||||
for i in &m.intersections {
|
||||
@ -182,7 +188,13 @@ impl Map {
|
||||
|
||||
{
|
||||
let _guard = flame::start_guard(format!("make {} buildings", data.buildings.len()));
|
||||
make::make_all_buildings(&mut m.buildings, &data.buildings, &bounds, &m.lanes);
|
||||
make::make_all_buildings(
|
||||
&mut m.buildings,
|
||||
&data.buildings,
|
||||
&gps_bounds,
|
||||
&bounds,
|
||||
&m.lanes,
|
||||
);
|
||||
for b in &m.buildings {
|
||||
m.lanes[b.front_path.sidewalk.0].building_paths.push(b.id);
|
||||
}
|
||||
@ -190,7 +202,13 @@ impl Map {
|
||||
|
||||
{
|
||||
let _guard = flame::start_guard(format!("make {} parcels", data.parcels.len()));
|
||||
make::make_all_parcels(&mut m.parcels, &data.parcels, &bounds, &m.lanes);
|
||||
make::make_all_parcels(
|
||||
&mut m.parcels,
|
||||
&data.parcels,
|
||||
&gps_bounds,
|
||||
&bounds,
|
||||
&m.lanes,
|
||||
);
|
||||
}
|
||||
|
||||
for (idx, a) in data.areas.iter().enumerate() {
|
||||
@ -200,7 +218,7 @@ impl Map {
|
||||
points: a
|
||||
.points
|
||||
.iter()
|
||||
.map(|coord| Pt2D::from_gps(*coord, &bounds).unwrap())
|
||||
.map(|coord| Pt2D::from_gps(*coord, &gps_bounds).unwrap())
|
||||
.collect(),
|
||||
osm_tags: a.osm_tags.clone(),
|
||||
osm_way_id: a.osm_way_id,
|
||||
@ -397,7 +415,11 @@ impl Map {
|
||||
|
||||
// TODO can we return a borrow?
|
||||
pub fn get_gps_bounds(&self) -> Bounds {
|
||||
self.bounds.clone()
|
||||
self.gps_bounds.clone()
|
||||
}
|
||||
|
||||
pub fn get_bounds(&self) -> Bounds {
|
||||
gps_to_map_bounds(&self.gps_bounds)
|
||||
}
|
||||
|
||||
pub fn get_driving_lane_from_bldg(&self, bldg: BuildingID) -> Result<LaneID, Error> {
|
||||
@ -470,3 +492,12 @@ impl Map {
|
||||
println!("Saved {}", path);
|
||||
}
|
||||
}
|
||||
|
||||
fn gps_to_map_bounds(gps: &Bounds) -> Bounds {
|
||||
// min_y here due to the wacky y inversion
|
||||
let max_screen_pt = Pt2D::from_gps(LonLat::new(gps.max_x, gps.min_y), gps).unwrap();
|
||||
let mut b = Bounds::new();
|
||||
b.update_pt(Pt2D::new(0.0, 0.0));
|
||||
b.update_pt(max_screen_pt);
|
||||
b
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use abstutil;
|
||||
use geom::{LonLat, Polygon, Pt2D};
|
||||
use geom::{Polygon, Pt2D};
|
||||
use map_model::{BuildingID, Map, RoadID};
|
||||
use rand::Rng;
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
@ -70,18 +70,16 @@ impl Neighborhood {
|
||||
}
|
||||
|
||||
fn make_everywhere(map: &Map) -> Neighborhood {
|
||||
// min_y here due to the wacky y inversion
|
||||
let bounds = map.get_gps_bounds();
|
||||
let max = Pt2D::from_gps(LonLat::new(bounds.max_x, bounds.min_y), &bounds).unwrap();
|
||||
let bounds = map.get_bounds();
|
||||
|
||||
Neighborhood {
|
||||
map_name: map.get_name().to_string(),
|
||||
name: "_everywhere_".to_string(),
|
||||
points: vec![
|
||||
Pt2D::new(0.0, 0.0),
|
||||
Pt2D::new(max.x(), 0.0),
|
||||
max,
|
||||
Pt2D::new(0.0, max.y()),
|
||||
Pt2D::new(bounds.max_x, 0.0),
|
||||
Pt2D::new(bounds.max_x, bounds.max_y),
|
||||
Pt2D::new(0.0, bounds.max_y),
|
||||
Pt2D::new(0.0, 0.0),
|
||||
],
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user