switching to a simpler GPS<->Pt2D scheme that's bijective

This commit is contained in:
Dustin Carlino 2018-10-29 14:19:41 -07:00
parent bb9c98b54e
commit f8f2ea3f95
5 changed files with 45 additions and 25 deletions

View File

@ -1,6 +1,6 @@
# Physics-related design notes
## Floating point and units ##
## Floating point and units
Currently using si::Second<f64> for time, which means comparing sim state by
deriving Eq is a headache. Since the timestep size is fixed anyway, this should
@ -64,3 +64,8 @@ Options:
- acceleration
- can be negative
- what should the resolution be?
## Coordinate system
Switching to something that's more easily bijective, but
https://crates.io/crates/flat_projection could be a good candidate too.

View File

@ -1,6 +1,6 @@
use aabb_quadtree::geom::{Point, Rect};
use std::f64;
use HashablePt2D;
use {HashablePt2D, Pt2D};
// longitude is x, latitude is y
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
@ -42,10 +42,10 @@ impl LonLat {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct GPSBounds {
pub min_lon: f64,
pub min_lat: f64,
pub max_lon: f64,
pub max_lat: f64,
pub(crate) min_lon: f64,
pub(crate) min_lat: f64,
pub(crate) max_lon: f64,
pub(crate) max_lat: f64,
// TODO hack to easily construct test maps
pub represents_world_space: bool,
@ -88,4 +88,13 @@ impl GPSBounds {
},
}
}
// TODO cache this
pub fn get_max_world_pt(&self) -> Pt2D {
let width = LonLat::new(self.min_lon, self.min_lat)
.gps_dist_meters(LonLat::new(self.max_lon, self.min_lat));
let height = LonLat::new(self.min_lon, self.min_lat)
.gps_dist_meters(LonLat::new(self.min_lon, self.max_lat));
Pt2D::new(width, height)
}
}

View File

@ -39,21 +39,27 @@ impl Pt2D {
return None;
}
// Invert y, so that the northernmost latitude is 0. Screen drawing order, not Cartesian grid.
let base = LonLat::new(b.min_lon, b.max_lat);
let (width, height) = {
let pt = b.get_max_world_pt();
(pt.x, pt.y)
};
// Apparently the aabb_quadtree can't handle 0, so add a bit.
// TODO epsilon or epsilon - 1.0?
let dx = base.gps_dist_meters(LonLat::new(gps.longitude, base.latitude)) + f64::EPSILON;
let dy = base.gps_dist_meters(LonLat::new(base.longitude, gps.latitude)) + f64::EPSILON;
// By default, 1 meter is one pixel. Normal zooming can change that. If we did scaling here,
// then we'd have to update all of the other constants too.
Some(Pt2D::new(dx, dy))
let x = (gps.longitude - b.min_lon) / (b.max_lon - b.min_lon) * width;
// Invert y, so that the northernmost latitude is 0. Screen drawing order, not Cartesian grid.
let y = height - ((gps.latitude - b.min_lat) / (b.max_lat - b.min_lat) * height);
Some(Pt2D::new(x, y))
}
pub fn to_gps(&self, _gps_bounds: &GPSBounds) -> LonLat {
// TODO wait, this is going to be more complicated
LonLat::new(0.0, 0.0)
pub fn to_gps(&self, b: &GPSBounds) -> LonLat {
let (width, height) = {
let pt = b.get_max_world_pt();
(pt.x, pt.y)
};
let lon = (self.x / width * (b.max_lon - b.min_lon)) + b.min_lon;
let lat = b.min_lat + ((b.max_lat - b.min_lat) * (height - self.y) / height);
LonLat::new(lon, lat)
}
pub fn x(&self) -> f64 {

View File

@ -3,7 +3,7 @@
use abstutil;
use abstutil::{Error, Timer};
use edits::RoadEdits;
use geom::{Bounds, GPSBounds, HashablePt2D, LonLat, PolyLine, Pt2D};
use geom::{Bounds, GPSBounds, HashablePt2D, PolyLine, Pt2D};
use make;
use raw_data;
use std::collections::{BTreeMap, BTreeSet, HashMap};
@ -59,14 +59,9 @@ impl Map {
let gps_bounds = data.get_gps_bounds();
let bounds = {
// min_y here due to the wacky y inversion
let max_screen_pt = Pt2D::from_gps(
LonLat::new(gps_bounds.max_lon, gps_bounds.min_lat),
&gps_bounds,
).unwrap();
let mut b = Bounds::new();
b.update(Pt2D::new(0.0, 0.0));
b.update(max_screen_pt);
b.update(gps_bounds.get_max_world_pt());
b
};

View File

@ -98,6 +98,11 @@ impl Neighborhood {
let gps = pt.to_gps(gps_bounds);
write!(f, " {} {}\n", gps.longitude, gps.latitude);
}
// Have to repeat the first point
{
let gps = self.points[0].to_gps(gps_bounds);
write!(f, " {} {}\n", gps.longitude, gps.latitude);
}
write!(f, "END\n");
write!(f, "END\n");