|
|
@@ -17,9 +17,16 @@ impl LatLon {
|
|
17
|
17
|
pub fn new(lat: f64, lon: f64) -> Self {
|
|
18
|
18
|
LatLon { lat, lon }
|
|
19
|
19
|
}
|
|
|
20
|
+
|
|
|
21
|
+ pub fn to_radians(&self) -> LatLonRad {
|
|
|
22
|
+ let f = PI / 180.0;
|
|
|
23
|
+ LatLonRad {
|
|
|
24
|
+ lat: self.lat * f,
|
|
|
25
|
+ lon: self.lon * f,
|
|
|
26
|
+ }
|
|
|
27
|
+ }
|
|
20
|
28
|
}
|
|
21
|
29
|
|
|
22
|
|
-//TODO Add more conversions from/to LatLonRad
|
|
23
|
30
|
/// A position in latitude, longitude.
|
|
24
|
31
|
/// Values are in radians and usually in these intervals:
|
|
25
|
32
|
/// latitude: [-0.5 * π, 0.5 * π]
|
|
|
@@ -35,6 +42,14 @@ impl LatLonRad {
|
|
35
|
42
|
LatLonRad { lat, lon }
|
|
36
|
43
|
}
|
|
37
|
44
|
|
|
|
45
|
+ pub fn to_degrees(&self) -> LatLon {
|
|
|
46
|
+ let f = 180.0 * FRAC_1_PI;
|
|
|
47
|
+ LatLon {
|
|
|
48
|
+ lat: self.lat * f,
|
|
|
49
|
+ lon: self.lon * f,
|
|
|
50
|
+ }
|
|
|
51
|
+ }
|
|
|
52
|
+
|
|
38
|
53
|
pub fn to_sphere_xyz(&self, radius: f64) -> SphereXYZ {
|
|
39
|
54
|
SphereXYZ {
|
|
40
|
55
|
x: radius * self.lat.cos() * self.lon.cos(),
|
|
|
@@ -80,6 +95,18 @@ impl From<LatLon> for MapCoord
|
|
80
|
95
|
let x = pos.lon * (1.0 / 360.0) + 0.5;
|
|
81
|
96
|
let pi_lat = pos.lat * (PI / 180.0);
|
|
82
|
97
|
let y = f64::ln(f64::tan(pi_lat) + 1.0 / f64::cos(pi_lat)) * (-0.5 * FRAC_1_PI) + 0.5;
|
|
|
98
|
+ debug_assert!(y.is_finite());
|
|
|
99
|
+
|
|
|
100
|
+ MapCoord { x, y }
|
|
|
101
|
+ }
|
|
|
102
|
+}
|
|
|
103
|
+
|
|
|
104
|
+impl From<LatLonRad> for MapCoord
|
|
|
105
|
+{
|
|
|
106
|
+ fn from(pos: LatLonRad) -> MapCoord {
|
|
|
107
|
+ let x = pos.lon * (0.5 * FRAC_1_PI) + 0.5;
|
|
|
108
|
+ let y = f64::ln(f64::tan(pos.lat) + 1.0 / f64::cos(pos.lat)) * (-0.5 * FRAC_1_PI) + 0.5;
|
|
|
109
|
+ debug_assert!(y.is_finite());
|
|
83
|
110
|
|
|
84
|
111
|
MapCoord { x, y }
|
|
85
|
112
|
}
|
|
|
@@ -118,6 +145,13 @@ impl MapCoord {
|
|
118
|
145
|
lon: self.x * (2.0 * PI) - PI,
|
|
119
|
146
|
}
|
|
120
|
147
|
}
|
|
|
148
|
+
|
|
|
149
|
+ pub fn to_latlon_deg(&self) -> LatLon {
|
|
|
150
|
+ LatLon {
|
|
|
151
|
+ lat: (PI - self.y * (2.0 * PI)).sinh().atan() * (180.0 * FRAC_1_PI),
|
|
|
152
|
+ lon: self.x * 360.0 - 180.0,
|
|
|
153
|
+ }
|
|
|
154
|
+ }
|
|
121
|
155
|
}
|
|
122
|
156
|
|
|
123
|
157
|
/// A position on the screen in pixels. Top-left corner is (0.0, 0.0).
|
|
|
@@ -424,4 +458,52 @@ mod tests {
|
|
424
|
458
|
assert_eq!(TileCoord::new(3, 1, 0).to_quadkey(), Some("001".to_string()));
|
|
425
|
459
|
assert_eq!(TileCoord::new(30, 0, 1).to_quadkey(), Some("000000000000000000000000000002".to_string()));
|
|
426
|
460
|
}
|
|
|
461
|
+
|
|
|
462
|
+ fn approx_eq(a: f64, b: f64) -> bool {
|
|
|
463
|
+ (a - b).abs() < 1e-10
|
|
|
464
|
+ }
|
|
|
465
|
+
|
|
|
466
|
+ #[test]
|
|
|
467
|
+ fn approx_eq_test() {
|
|
|
468
|
+ assert!(approx_eq(1.0, 1.0));
|
|
|
469
|
+ assert!(approx_eq(0.0, 0.0));
|
|
|
470
|
+ assert!(approx_eq(0.0, -0.0));
|
|
|
471
|
+ assert!(approx_eq(1e20, 1e20 + 1.0));
|
|
|
472
|
+ assert!(approx_eq(1e20, 1e20 - 1.0));
|
|
|
473
|
+ assert!(!approx_eq(1000.0, 1000.1));
|
|
|
474
|
+ }
|
|
|
475
|
+
|
|
|
476
|
+ #[test]
|
|
|
477
|
+ fn degree_radians() {
|
|
|
478
|
+ {
|
|
|
479
|
+ let rad = LatLon::new(0.0, 0.0).to_radians();
|
|
|
480
|
+ assert!(approx_eq(rad.lat, 0.0));
|
|
|
481
|
+ assert!(approx_eq(rad.lon, 0.0));
|
|
|
482
|
+ let deg = rad.to_degrees();
|
|
|
483
|
+ assert!(approx_eq(deg.lat, 0.0));
|
|
|
484
|
+ assert!(approx_eq(deg.lon, 0.0));
|
|
|
485
|
+ }
|
|
|
486
|
+ {
|
|
|
487
|
+ let rad = LatLon::new(-45.0, 180.0).to_radians();
|
|
|
488
|
+ assert!(approx_eq(rad.lat, -PI / 4.0));
|
|
|
489
|
+ assert!(approx_eq(rad.lon, PI));
|
|
|
490
|
+ let deg = rad.to_degrees();
|
|
|
491
|
+ assert!(approx_eq(deg.lat, -45.0));
|
|
|
492
|
+ assert!(approx_eq(deg.lon, 180.0));
|
|
|
493
|
+ }
|
|
|
494
|
+
|
|
|
495
|
+ {
|
|
|
496
|
+ let mc = MapCoord::from(LatLon::new(23.45, 123.45));
|
|
|
497
|
+ let deg = mc.to_latlon_rad().to_degrees();
|
|
|
498
|
+ assert!(approx_eq(deg.lat, 23.45));
|
|
|
499
|
+ assert!(approx_eq(deg.lon, 123.45));
|
|
|
500
|
+ }
|
|
|
501
|
+
|
|
|
502
|
+ {
|
|
|
503
|
+ let mc = MapCoord::from(LatLonRad::new(-0.345 * PI, -0.987 * PI));
|
|
|
504
|
+ let rad = mc.to_latlon_deg().to_radians();
|
|
|
505
|
+ assert!(approx_eq(rad.lat, -0.345 * PI));
|
|
|
506
|
+ assert!(approx_eq(rad.lon, -0.987 * PI));
|
|
|
507
|
+ }
|
|
|
508
|
+ }
|
|
427
|
509
|
}
|