|
|
@@ -1,5 +1,5 @@
|
|
1
|
1
|
use cgmath::{Matrix3, Point3, Transform, vec3};
|
|
2
|
|
-use coord::{LatLonRad, TextureRect, TileCoord};
|
|
|
2
|
+use coord::{LatLonRad, ScreenCoord, TextureRect, TileCoord};
|
|
3
|
3
|
use map_view::MapView;
|
|
4
|
4
|
use std::collections::HashSet;
|
|
5
|
5
|
use std::f64::consts::{PI, FRAC_1_PI};
|
|
|
@@ -227,8 +227,9 @@ impl OrthograficView {
|
|
227
|
227
|
vec3(0.0, 0.0, 1.0),
|
|
228
|
228
|
);
|
|
229
|
229
|
|
|
|
230
|
+ let center_latlon = map_view.center.to_latlon_rad();
|
|
|
231
|
+
|
|
230
|
232
|
let rot_mat_x: Matrix3<f64> = {
|
|
231
|
|
- let center_latlon = map_view.center.to_latlon_rad();
|
|
232
|
233
|
let alpha = center_latlon.lon + (PI * 0.5);
|
|
233
|
234
|
let cosa = alpha.cos();
|
|
234
|
235
|
let sina = alpha.sin();
|
|
|
@@ -240,7 +241,6 @@ impl OrthograficView {
|
|
240
|
241
|
};
|
|
241
|
242
|
|
|
242
|
243
|
let rot_mat_y: Matrix3<f64> = {
|
|
243
|
|
- let center_latlon = map_view.center.to_latlon_rad();
|
|
244
|
244
|
let alpha = -center_latlon.lat;
|
|
245
|
245
|
let cosa = alpha.cos();
|
|
246
|
246
|
let sina = alpha.sin();
|
|
|
@@ -256,6 +256,69 @@ impl OrthograficView {
|
|
256
|
256
|
&Transform::<Point3<f64>>::concat(&rot_mat_y, &rot_mat_x)
|
|
257
|
257
|
)
|
|
258
|
258
|
}
|
|
|
259
|
+
|
|
|
260
|
+ // Returns the inverse rotation matrix of the given view.
|
|
|
261
|
+ pub fn inv_rotation_matrix(map_view: &MapView) -> Matrix3<f64> {
|
|
|
262
|
+ let center_latlon = map_view.center.to_latlon_rad();
|
|
|
263
|
+
|
|
|
264
|
+ let rot_mat_x: Matrix3<f64> = {
|
|
|
265
|
+ let alpha = -center_latlon.lon - (PI * 0.5);
|
|
|
266
|
+ let cosa = alpha.cos();
|
|
|
267
|
+ let sina = alpha.sin();
|
|
|
268
|
+ Matrix3::from_cols(
|
|
|
269
|
+ vec3(cosa, 0.0, -sina),
|
|
|
270
|
+ vec3(0.0, 1.0, 0.0),
|
|
|
271
|
+ vec3(sina, 0.0, cosa),
|
|
|
272
|
+ )
|
|
|
273
|
+ };
|
|
|
274
|
+
|
|
|
275
|
+ let rot_mat_y: Matrix3<f64> = {
|
|
|
276
|
+ let alpha = center_latlon.lat;
|
|
|
277
|
+ let cosa = alpha.cos();
|
|
|
278
|
+ let sina = alpha.sin();
|
|
|
279
|
+ Matrix3::from_cols(
|
|
|
280
|
+ vec3(1.0, 0.0, 0.0),
|
|
|
281
|
+ vec3(0.0, cosa, sina),
|
|
|
282
|
+ vec3(0.0, -sina, cosa),
|
|
|
283
|
+ )
|
|
|
284
|
+ };
|
|
|
285
|
+
|
|
|
286
|
+ Transform::<Point3<f64>>::concat(&rot_mat_x, &rot_mat_y)
|
|
|
287
|
+ }
|
|
|
288
|
+
|
|
|
289
|
+ // Returns the coordinates of the location that is nearest to the given `ScreenCoord`.
|
|
|
290
|
+ pub fn screen_coord_to_latlonrad(map_view: &MapView, screen_coord: ScreenCoord) -> LatLonRad {
|
|
|
291
|
+ // Point on unit sphere
|
|
|
292
|
+ let sphere_point = {
|
|
|
293
|
+ let recip_radius = 2.0 * Self::radius_physical_pixels(map_view).recip();
|
|
|
294
|
+ let sx = (screen_coord.x - map_view.width * 0.5) * recip_radius;
|
|
|
295
|
+ let sy = (screen_coord.y - map_view.height * 0.5) * -recip_radius;
|
|
|
296
|
+ let t = 1.0 - sx * sx - sy * sy;
|
|
|
297
|
+ if t >= 0.0 {
|
|
|
298
|
+ // screen_coord is on the sphere
|
|
|
299
|
+ Point3::new(
|
|
|
300
|
+ sx,
|
|
|
301
|
+ sy,
|
|
|
302
|
+ -(t.sqrt()),
|
|
|
303
|
+ )
|
|
|
304
|
+ } else {
|
|
|
305
|
+ // screen_coord is outside of sphere -> pick nearest.
|
|
|
306
|
+ let scale = sx.hypot(sy).recip();
|
|
|
307
|
+ Point3::new(
|
|
|
308
|
+ sx * scale,
|
|
|
309
|
+ sy * scale,
|
|
|
310
|
+ 0.0,
|
|
|
311
|
+ )
|
|
|
312
|
+ }
|
|
|
313
|
+ };
|
|
|
314
|
+
|
|
|
315
|
+ // Rotate
|
|
|
316
|
+ let inv_trans = Self::inv_rotation_matrix(map_view);
|
|
|
317
|
+ let p = inv_trans.transform_point(sphere_point);
|
|
|
318
|
+
|
|
|
319
|
+ // Transform to latitude, longitude
|
|
|
320
|
+ LatLonRad::new(p.y.asin(), p.z.atan2(p.x))
|
|
|
321
|
+ }
|
|
259
|
322
|
}
|
|
260
|
323
|
|
|
261
|
324
|
#[cfg(test)]
|