Parcourir la source

Spherical tile visibility, part II

Still not solved. Tried to approach by calculating properties of
elliptical arcs, but there are so many edge cases...
Johannes Hofmann il y a 7 ans
Parent
révision
c8f358ebc6
4 fichiers modifiés avec 126 ajouts et 24 suppressions
  1. 22
    6
      src/coord.rs
  2. 28
    5
      src/globe_tile_layer.rs
  3. 73
    12
      src/map_view.rs
  4. 3
    1
      src/map_view_gl.rs

+ 22
- 6
src/coord.rs Voir le fichier

@@ -50,16 +50,18 @@ impl LatLonRad {
50 50
         }
51 51
     }
52 52
 
53
-    pub fn to_sphere_xyz(&self, radius: f64) -> SphereXYZ {
53
+    /// Convert to 3D Point on unit sphere.
54
+    pub fn to_sphere_xyz(&self) -> SphereXYZ {
54 55
         SphereXYZ {
55
-            x: radius * self.lat.cos() * self.lon.cos(),
56
-            y: radius * self.lat.sin(),
57
-            z: radius * self.lat.cos() * self.lon.sin(),
56
+            x: self.lat.cos() * self.lon.cos(),
57
+            y: self.lat.sin(),
58
+            z: self.lat.cos() * self.lon.sin(),
58 59
         }
59 60
     }
60 61
 
61
-    pub fn to_sphere_point3(&self, radius: f64) -> Point3<f32> {
62
-        let p = self.to_sphere_xyz(radius);
62
+    /// Convert to 3D Point on unit sphere.
63
+    pub fn to_sphere_point3(&self) -> Point3<f32> {
64
+        let p = self.to_sphere_xyz();
63 65
         Point3::new(
64 66
             p.x as f32,
65 67
             p.y as f32,
@@ -355,6 +357,13 @@ impl TileCoord {
355 357
         }
356 358
     }
357 359
 
360
+    /// Return the coordinate inside the tile that is nearest to the given coordinate.
361
+    /// This function uses spherical topology.
362
+    pub fn nearest_inside_point(&self, other: MapCoord) -> MapCoord {
363
+        //TODO insert real implemenation here
364
+        self.map_coord_north_west()
365
+    }
366
+
358 367
     pub fn children(&self) -> [(TileCoord, SubTileCoord); 4] {
359 368
         [
360 369
             (
@@ -640,4 +649,11 @@ mod tests {
640 649
         assert_eq!(TileCoord::new(2, 0, -1).globe_norm(), TileCoord::new(2, 2, 0));
641 650
         assert_eq!(TileCoord::new(2, 0, -5).globe_norm(), TileCoord::new(2, 0, 3));
642 651
     }
652
+
653
+    #[test]
654
+    fn nearest_inside_point() {
655
+        assert_eq!(TileCoord::new(0, 0, 0).nearest_inside_point(MapCoord::new(0.5, 0.25)), MapCoord::new(0.5, 0.25));
656
+        assert_eq!(TileCoord::new(2, 0, 0).nearest_inside_point(MapCoord::new(0.5, 0.5)), MapCoord::new(0.25, 0.25));
657
+        //TODO Add more test cases
658
+    }
643 659
 }

+ 28
- 5
src/globe_tile_layer.rs Voir le fichier

@@ -2,7 +2,7 @@ use std::ffi::CStr;
2 2
 use buffer::{Buffer, DrawMode};
3 3
 use cgmath::Transform;
4 4
 use context::Context;
5
-use coord::{LatLonRad, TileCoord, View};
5
+use coord::{LatLonRad, ScreenCoord, TileCoord, View};
6 6
 use map_view::MapView;
7 7
 use program::Program;
8 8
 use tile_atlas::TileAtlas;
@@ -17,6 +17,29 @@ pub struct GlobeTileLayer {
17 17
     buffer: Buffer,
18 18
 }
19 19
 
20
+#[derive(Copy, Clone, Debug)]
21
+pub struct LatScreenEllipse {
22
+    pub center: ScreenCoord,
23
+    pub radius_x: f64,
24
+    pub radius_y: f64,
25
+    /// longitude angle in radians at center + radius_y
26
+    pub ref_angle: f64,
27
+}
28
+
29
+impl LatScreenEllipse {
30
+    fn new(view_center: LatLonRad, viewport_size: (u32, u32), globe_radius: f64, lat: f64) -> Self {
31
+        LatScreenEllipse {
32
+            center: ScreenCoord {
33
+                x: viewport_size.0 as f64 * 0.5,
34
+                y: viewport_size.1 as f64 * 0.5 * (lat - view_center.lat).sin() * globe_radius,
35
+            },
36
+            radius_x: lat.cos() * globe_radius,
37
+            radius_y: lat.cos() * -view_center.lat.sin() * globe_radius,
38
+            ref_angle: view_center.lon,
39
+        }
40
+    }
41
+}
42
+
20 43
 
21 44
 impl GlobeTileLayer {
22 45
     pub fn new(
@@ -110,10 +133,10 @@ impl GlobeTileLayer {
110 133
                 let ll_ne = LatLonRad::new(ll_nw.lat, ll_se.lon);
111 134
                 let ll_sw = LatLonRad::new(ll_se.lat, ll_nw.lon);
112 135
 
113
-                let p1 = ll_nw.to_sphere_point3(1.0);
114
-                let p2 = ll_ne.to_sphere_point3(1.0);
115
-                let p3 = ll_se.to_sphere_point3(1.0);
116
-                let p4 = ll_sw.to_sphere_point3(1.0);
136
+                let p1 = ll_nw.to_sphere_point3();
137
+                let p2 = ll_ne.to_sphere_point3();
138
+                let p3 = ll_se.to_sphere_point3();
139
+                let p4 = ll_sw.to_sphere_point3();
117 140
 
118 141
                 let p1 = transform.transform_point(p1);
119 142
                 let p2 = transform.transform_point(p2);

+ 73
- 12
src/map_view.rs Voir le fichier

@@ -147,6 +147,7 @@ impl MapView {
147 147
     }
148 148
 
149 149
     //TODO Put this in a new module with other "sphere things"
150
+    //TODO Return the transformation matrix that is used here to avoid redundant calculation.
150 151
     /// Returns a `Vec` of all tiles that are visible in the current viewport.
151 152
     pub fn visible_globe_tiles(&self) -> Vec<TileCoord> {
152 153
         let uzoom = self.tile_zoom();
@@ -166,26 +167,86 @@ impl MapView {
166 167
 
167 168
         let center_tile = self.center.on_tile_at_zoom(uzoom).globe_norm();
168 169
 
170
+        let transform = self.globe_transformation_matrix();
171
+
172
+        let add_tile_if_visible = |tc: TileCoord, vec: &mut Vec<TileCoord>| -> bool {
173
+            let nearest = tc.nearest_inside_point(self.center).to_latlon_rad().to_sphere_point3();
174
+            let screen_coord = transform.transform_point(nearest);
175
+
176
+            let visible = screen_coord.x >= -1.0 && screen_coord.x <= 1.0 &&
177
+                screen_coord.y >= -1.0 && screen_coord.y <= 1.0;
178
+
179
+            if visible {
180
+                vec.push(tc);
181
+                true
182
+            } else {
183
+                false
184
+            }
185
+        };
169 186
 
170 187
         let mut tiles = vec![];
171
-        tiles.push(center_tile);
172 188
 
173
-        // Check rings of tiles with the same Chebyshev distance to the center_tile
174 189
         {
175 190
             let zoom_level_tiles = TileCoord::get_zoom_level_tiles(uzoom);
176
-            let max_full_rings = (zoom_level_tiles - 1) / 2;
177 191
 
178
-            for radius in 1..3.min(max_full_rings + 1) {
179
-                let (rx1, rx2) = (center_tile.x - radius, center_tile.x + radius);
180
-                let (ry1, ry2) = (center_tile.y - radius, center_tile.y + radius);
192
+            for dx in 0..(zoom_level_tiles / 2) {
193
+                let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x + dx, center_tile.y), &mut tiles);
194
+                if !v {
195
+                    break;
196
+                }
197
+            }
198
+            for dx in 1..(1 + zoom_level_tiles / 2) {
199
+                let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x - dx, center_tile.y), &mut tiles);
200
+                if !v {
201
+                    break;
202
+                }
203
+            }
204
+
205
+            // move south
206
+            for y in (center_tile.y + 1)..zoom_level_tiles {
207
+                let mut visible = false;
181 208
 
182
-                for x in rx1..rx2+1 {
183
-                    tiles.push(TileCoord::new(uzoom, x, ry1).globe_norm());
184
-                    tiles.push(TileCoord::new(uzoom, x, ry2).globe_norm());
209
+                for dx in 0..(zoom_level_tiles / 2) {
210
+                    let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x + dx, y), &mut tiles);
211
+                    visible = visible || v;
212
+                    if !v {
213
+                        break;
214
+                    }
185 215
                 }
186
-                for y in ry1+1..ry2 {
187
-                    tiles.push(TileCoord::new(uzoom, rx1, y).globe_norm());
188
-                    tiles.push(TileCoord::new(uzoom, rx2, y).globe_norm());
216
+                for dx in 1..(1 + zoom_level_tiles / 2) {
217
+                    let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x - dx, y), &mut tiles);
218
+                    visible = visible || v;
219
+                    if !v {
220
+                        break;
221
+                    }
222
+                }
223
+
224
+                if !visible {
225
+                    break;
226
+                }
227
+            }
228
+
229
+            // move north
230
+            for y in (0..center_tile.y).rev() {
231
+                let mut visible = false;
232
+
233
+                for dx in 0..(zoom_level_tiles / 2) {
234
+                    let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x + dx, y), &mut tiles);
235
+                    visible = visible || v;
236
+                    if !v {
237
+                        break;
238
+                    }
239
+                }
240
+                for dx in 1..(1 + zoom_level_tiles / 2) {
241
+                    let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x - dx, y), &mut tiles);
242
+                    visible = visible || v;
243
+                    if !v {
244
+                        break;
245
+                    }
246
+                }
247
+
248
+                if !visible {
249
+                    break;
189 250
                 }
190 251
             }
191 252
         }

+ 3
- 1
src/map_view_gl.rs Voir le fichier

@@ -110,7 +110,9 @@ impl MapViewGl {
110 110
     pub fn map_covers_viewport(&self) -> bool {
111 111
         match self.view_mode {
112 112
             ViewMode::Flat => self.map_view.map_covers_viewport(),
113
-            ViewMode::Globe => self.map_view.globe_covers_viewport(),
113
+            //TODO uncomment
114
+            //ViewMode::Globe => self.map_view.globe_covers_viewport(),
115
+            ViewMode::Globe => false,
114 116
         }
115 117
     }
116 118