Преглед на файлове

{mercator,orthografic}_view: Convert ScreenCoords

Add functions to convert screen coordinates to MapCoord/LatLonRad.
Johannes Hofmann преди 7 години
родител
ревизия
15c9e053e9
променени са 2 файла, в които са добавени 82 реда и са изтрити 3 реда
  1. 16
    0
      src/mercator_view.rs
  2. 66
    3
      src/orthografic_view.rs

+ 16
- 0
src/mercator_view.rs Целия файл

@@ -61,6 +61,22 @@ impl MercatorView {
61 61
         }
62 62
     }
63 63
 
64
+    /// Returns the map coordinate that corresponds to the given screen coordinate.
65
+    pub fn screen_to_map_coord(map_view: &MapView, screen_coord: ScreenCoord) -> MapCoord {
66
+        let scale = f64::powf(2.0, -map_view.zoom) / f64::from(map_view.tile_size);
67
+
68
+        let delta_x = screen_coord.x - map_view.width * 0.5;
69
+        let delta_y = screen_coord.y - map_view.height * 0.5;
70
+
71
+        let mut m = MapCoord {
72
+            x: map_view.center.x + delta_x * scale,
73
+            y: map_view.center.y + delta_y * scale,
74
+        };
75
+
76
+        m.normalize_x();
77
+        m
78
+    }
79
+
64 80
     /// Returns true if the viewport rectangle is fully inside the map.
65 81
     pub fn covers_viewport(map_view: &MapView) -> bool {
66 82
         let scale = f64::powf(2.0, -map_view.zoom) / f64::from(map_view.tile_size);

+ 66
- 3
src/orthografic_view.rs Целия файл

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