Quellcode durchsuchen

Restructure modules

* Split up map_view module into
  * map_view
  * mercator_view
  * orthografic_view
Johannes Hofmann vor 7 Jahren
Ursprung
Commit
cf7f21acc3

+ 15
- 0
shader/map_tile_border.frag Datei anzeigen

@@ -0,0 +1,15 @@
1
+#version 100
2
+precision mediump float;
3
+
4
+varying vec2 v_tex;
5
+varying vec4 v_tex_minmax;
6
+uniform sampler2D tex_map;
7
+
8
+void main() {
9
+    vec2 mid = 0.5 * (v_tex_minmax.zw + v_tex_minmax.xy);
10
+    vec2 scale = 1.0 / (v_tex_minmax.zw - v_tex_minmax.xy);
11
+    vec2 dist = abs((v_tex - mid) * scale);
12
+    float shade = 1.0 - step(0.49, max(dist.x, dist.y)) * 0.5;
13
+    float add = step(0.4975, max(dist.x, dist.y)) * 0.5;
14
+    gl_FragColor = vec4(texture2D(tex_map, clamp(v_tex.xy, v_tex_minmax.xy, v_tex_minmax.zw)).rgb * shade + vec3(add), 1.0);
15
+}

shader/globe_tile.frag → shader/ortho_tile.frag Datei anzeigen


shader/globe_tile.vert → shader/ortho_tile.vert Datei anzeigen


+ 0
- 14
src/coord.rs Datei anzeigen

@@ -357,13 +357,6 @@ impl TileCoord {
357 357
         }
358 358
     }
359 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
-
367 360
     pub fn children(&self) -> [(TileCoord, SubTileCoord); 4] {
368 361
         [
369 362
             (
@@ -649,11 +642,4 @@ mod tests {
649 642
         assert_eq!(TileCoord::new(2, 0, -1).globe_norm(), TileCoord::new(2, 2, 0));
650 643
         assert_eq!(TileCoord::new(2, 0, -5).globe_norm(), TileCoord::new(2, 0, 3));
651 644
     }
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
-    }
659 645
 }

+ 3
- 1
src/main.rs Datei anzeigen

@@ -23,10 +23,12 @@ pub mod config;
23 23
 #[macro_use]
24 24
 pub mod context;
25 25
 pub mod coord;
26
-pub mod globe_tile_layer;
26
+pub mod ortho_tile_layer;
27 27
 pub mod map_view;
28 28
 pub mod map_view_gl;
29 29
 pub mod marker_layer;
30
+pub mod mercator_view;
31
+pub mod orthografic_view;
30 32
 pub mod program;
31 33
 pub mod search;
32 34
 pub mod session;

+ 4
- 305
src/map_view.rs Datei anzeigen

@@ -1,10 +1,7 @@
1
-use cgmath::{Matrix3, Point3, Transform, vec3};
2
-use coord::{MapCoord, ScreenCoord, ScreenRect, TileCoord};
3
-use std::f32::consts::{PI, FRAC_1_PI};
4
-use std::f64;
1
+use coord::MapCoord;
5 2
 
6 3
 
7
-/// A view of a tiled map with a rectangular viewport and a zoom.
4
+/// A view of a map with a rectangular viewport and a zoom.
8 5
 #[derive(Clone, Debug)]
9 6
 pub struct MapView {
10 7
     /// Width of the viewport.
@@ -23,13 +20,6 @@ pub struct MapView {
23 20
     pub tile_zoom_offset: f64,
24 21
 }
25 22
 
26
-/// The position and size of a specific tile on the screen.
27
-#[derive(Clone, Debug)]
28
-pub struct VisibleTile {
29
-    pub tile: TileCoord,
30
-    pub rect: ScreenRect,
31
-}
32
-
33 23
 impl MapView {
34 24
     /// Constructs a new `MapView`.
35 25
     pub fn new(width: f64, height: f64, tile_size: u32, center: MapCoord, zoom: f64) -> MapView {
@@ -43,267 +33,9 @@ impl MapView {
43 33
         }
44 34
     }
45 35
 
46
-    /// Constructs a new `MapView` centered at Null Island with an integer zoom that fills a screen
47
-    /// with the given dimensions.
48
-    pub fn with_filling_zoom(width: f64, height: f64, tile_size: u32) -> MapView {
49
-        let min_dimension = width.min(height);
50
-        let zoom = (min_dimension / f64::from(tile_size)).log2().ceil();
51
-        MapView {
52
-            width,
53
-            height,
54
-            tile_size,
55
-            center: MapCoord::new(0.5, 0.5),
56
-            zoom,
57
-            tile_zoom_offset: 0.0,
58
-        }
59
-    }
60
-
61
-    /// Returns the map coordinate that corresponds to the top-left corner of the viewport.
62
-    pub fn top_left_coord(&self) -> MapCoord {
63
-        let scale = f64::powf(2.0, -self.zoom) / f64::from(self.tile_size);
64
-
65
-        let x = self.center.x + -0.5 * self.width * scale;
66
-        let y = self.center.y + -0.5 * self.height * scale;
67
-
68
-        MapCoord::new(x, y)
69
-    }
70
-
71
-    /// Returns the screen coordinate that corresponds to the given map coordinate.
72
-    pub fn map_to_screen_coord(&self, map_coord: MapCoord) -> ScreenCoord {
73
-        let scale = f64::powf(2.0, self.zoom) * f64::from(self.tile_size);
74
-
75
-        let delta_x = map_coord.x - self.center.x;
76
-        let delta_y = map_coord.y - self.center.y;
77
-
78
-        ScreenCoord {
79
-            x: 0.5 * self.width + delta_x * scale,
80
-            y: 0.5 * self.height + delta_y * scale,
81
-        }
82
-    }
83
-
84
-    /// Returns true if the viewport rectangle is fully inside the map.
85
-    pub fn map_covers_viewport(&self) -> bool {
86
-        let scale = f64::powf(2.0, -self.zoom) / f64::from(self.tile_size);
87
-
88
-        let y_top = self.center.y + -0.5 * self.height * scale;
89
-        let y_bottom = self.center.y + 0.5 * self.height * scale;
90
-
91
-        y_top >= 0.0 && y_bottom <= 1.0
92
-    }
93
-
94
-    /// Returns true if the globe rendering covers the whole viewport.
95
-    pub fn globe_covers_viewport(&self) -> bool {
96
-        //TODO Add a little safety margin since the rendered globe is not a perfect sphere and its
97
-        // screen area is underestimated by the tesselation.
98
-        let globe_diameter = 2.0f64.powf(self.zoom) *
99
-            (f64::consts::FRAC_1_PI * self.tile_size as f64);
100
-
101
-        return (self.width * self.width) + (self.height * self.height) < globe_diameter * globe_diameter;
102
-    }
103
-
104
-    /// Returns the screen coordinate of the top-left corner of a tile.
105
-    pub fn tile_screen_position(&self, tile: &TileCoord) -> ScreenCoord {
106
-        self.map_to_screen_coord(tile.map_coord_north_west())
107
-    }
108
-
109
-    /// Returns a `Vec` of all tiles that are visible in the current viewport.
110
-    pub fn visible_tiles(&self, snap_to_pixel: bool) -> Vec<VisibleTile> {
111
-        let uzoom = self.tile_zoom();
112
-        let top_left_tile = self.top_left_coord().on_tile_at_zoom(uzoom);
113
-        let mut top_left_tile_screen_coord = self.tile_screen_position(&top_left_tile);
114
-        let tile_screen_size = f64::powf(2.0, self.zoom - f64::from(uzoom)) * f64::from(self.tile_size);
115
-
116
-        if snap_to_pixel {
117
-            top_left_tile_screen_coord.snap_to_pixel();
118
-        }
119
-
120
-        let start_tile_x = top_left_tile.x;
121
-        let start_tile_y = top_left_tile.y;
122
-        let num_tiles_x = ((self.width - top_left_tile_screen_coord.x) / tile_screen_size).ceil().max(0.0) as i32;
123
-        let num_tiles_y = ((self.height - top_left_tile_screen_coord.y) / tile_screen_size).ceil().max(0.0) as i32;
124
-
125
-        let mut visible_tiles = Vec::with_capacity(num_tiles_x as usize * num_tiles_y as usize);
126
-
127
-        for y in 0..num_tiles_y {
128
-            for x in 0..num_tiles_x {
129
-                let t = TileCoord::new(uzoom, start_tile_x + x, start_tile_y + y);
130
-                if t.is_on_planet() {
131
-                    visible_tiles.push(
132
-                        VisibleTile {
133
-                            tile: t,
134
-                            rect: ScreenRect {
135
-                                x: top_left_tile_screen_coord.x + tile_screen_size * f64::from(x),
136
-                                y: top_left_tile_screen_coord.y + tile_screen_size * f64::from(y),
137
-                                width: tile_screen_size,
138
-                                height: tile_screen_size,
139
-                            }
140
-                        }
141
-                    );
142
-                }
143
-            }
144
-        }
145
-
146
-        visible_tiles
147
-    }
148
-
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.
151
-    /// Returns a `Vec` of all tiles that are visible in the current viewport.
152
-    pub fn visible_globe_tiles(&self) -> Vec<TileCoord> {
153
-        let uzoom = self.tile_zoom();
154
-
155
-        match uzoom {
156
-            0 => return vec![TileCoord::new(0, 0, 0)],
157
-            1 => {
158
-                // return every tile
159
-                return vec![
160
-                    TileCoord::new(1, 0, 0),
161
-                    TileCoord::new(1, 0, 1),
162
-                    TileCoord::new(1, 1, 0),
163
-                    TileCoord::new(1, 1, 1),
164
-                ]},
165
-            _ => {},
166
-        }
167
-
168
-        let center_tile = self.center.on_tile_at_zoom(uzoom).globe_norm();
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
-        };
186
-
187
-        let mut tiles = vec![];
188
-
189
-        {
190
-            let zoom_level_tiles = TileCoord::get_zoom_level_tiles(uzoom);
191
-
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;
208
-
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
-                    }
215
-                }
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;
250
-                }
251
-            }
252
-        }
253
-
254
-        tiles
255
-    }
256
-
257
-    pub fn globe_transformation_matrix(&self) -> Matrix3<f32> {
258
-        let (scale_x, scale_y) = {
259
-            let factor = 2.0f32.powf(self.zoom as f32) *
260
-                (FRAC_1_PI * self.tile_size as f32);
261
-            (factor / self.width as f32, factor / self.height as f32)
262
-        };
263
-
264
-        let scale_mat: Matrix3<f32> = Matrix3::from_cols(
265
-            vec3(scale_x, 0.0, 0.0),
266
-            vec3(0.0, scale_y, 0.0),
267
-            vec3(0.0, 0.0, 1.0),
268
-        );
269
-
270
-        let rot_mat_x: Matrix3<f32> = {
271
-            let center_latlon = self.center.to_latlon_rad();
272
-            let alpha = center_latlon.lon as f32 + (PI * 0.5);
273
-            let cosa = alpha.cos();
274
-            let sina = alpha.sin();
275
-                Matrix3::from_cols(
276
-                vec3(cosa, 0.0, -sina),
277
-                vec3(0.0, 1.0, 0.0),
278
-                vec3(sina, 0.0, cosa),
279
-            )
280
-        };
281
-
282
-        let rot_mat_y: Matrix3<f32> = {
283
-            let center_latlon = self.center.to_latlon_rad();
284
-            let alpha = (-center_latlon.lat) as f32;
285
-            let cosa = alpha.cos();
286
-            let sina = alpha.sin();
287
-                Matrix3::from_cols(
288
-                vec3(1.0, 0.0, 0.0),
289
-                vec3(0.0, cosa, sina),
290
-                vec3(0.0, -sina, cosa),
291
-            )
292
-        };
293
-
294
-        let transform = Transform::<Point3<f32>>::concat(&rot_mat_y, &rot_mat_x);
295
-        let transform = Transform::<Point3<f32>>::concat(&scale_mat, &transform);
296
-        transform
297
-    }
298
-
299
-    /// Returns the tile zoom value that is used for rendering with the current zoom.
300
-    pub fn tile_zoom(&self) -> u32 {
301
-        (self.zoom + self.tile_zoom_offset).floor().max(0.0) as u32
302
-    }
303
-
304 36
     /// Returns the tile zoom offset.
305
-    pub fn tile_zoom_offset(&self) -> f64 {
306
-        self.tile_zoom_offset
37
+    pub fn tile_zoom_offset(map_view: &MapView) -> f64 {
38
+        map_view.tile_zoom_offset
307 39
     }
308 40
 
309 41
     /// Set the tile zoom offset.
@@ -326,37 +58,4 @@ impl MapView {
326 58
     pub fn zoom(&mut self, zoom_delta: f64) {
327 59
         self.zoom += zoom_delta;
328 60
     }
329
-
330
-    /// Change zoom value by `zoom_delta` and zoom to a position given in screen coordinates.
331
-    pub fn zoom_at(&mut self, pos: ScreenCoord, zoom_delta: f64) {
332
-        let delta_x = pos.x - self.width * 0.5;
333
-        let delta_y = pos.y - self.height * 0.5;
334
-
335
-        let scale =
336
-            (f64::powf(2.0, -self.zoom) - f64::powf(2.0, -self.zoom - zoom_delta))
337
-            / f64::from(self.tile_size);
338
-        self.zoom += zoom_delta;
339
-
340
-        self.center.x += delta_x * scale;
341
-        self.center.y += delta_y * scale;
342
-    }
343
-
344
-    /// Set a zoom value and zoom to a `position` given in screen coordinates.
345
-    pub fn set_zoom_at(&mut self, pos: ScreenCoord, zoom: f64) {
346
-        let delta_x = pos.x - self.width * 0.5;
347
-        let delta_y = pos.y - self.height * 0.5;
348
-
349
-        let scale = (f64::powf(2.0, -self.zoom) - f64::powf(2.0, -zoom)) / f64::from(self.tile_size);
350
-        self.zoom = zoom;
351
-
352
-        self.center.x += delta_x * scale;
353
-        self.center.y += delta_y * scale;
354
-    }
355
-
356
-    /// Move the center of the viewport by (`delta_x`, `delta_y`) in screen coordinates.
357
-    pub fn move_pixel(&mut self, delta_x: f64, delta_y: f64) {
358
-        let scale = f64::powf(2.0, -self.zoom) / f64::from(self.tile_size);
359
-        self.center.x += delta_x * scale;
360
-        self.center.y += delta_y * scale;
361
-    }
362 61
 }

+ 22
- 18
src/map_view_gl.rs Datei anzeigen

@@ -1,8 +1,10 @@
1 1
 use context::Context;
2 2
 use coord::{MapCoord, ScreenCoord};
3
-use globe_tile_layer::GlobeTileLayer;
3
+use ortho_tile_layer::OrthoTileLayer;
4 4
 use map_view::MapView;
5 5
 use marker_layer::MarkerLayer;
6
+use mercator_view::MercatorView;
7
+use orthografic_view::OrthograficView;
6 8
 use session::Session;
7 9
 use texture::{Texture, TextureFormat};
8 10
 use tile_atlas::TileAtlas;
@@ -22,7 +24,7 @@ pub struct MapViewGl {
22 24
     tile_atlas: TileAtlas,
23 25
     tile_layer: TileLayer,
24 26
     marker_layer: MarkerLayer,
25
-    globe_tile_layer: GlobeTileLayer,
27
+    ortho_tile_layer: OrthoTileLayer,
26 28
     projection: Projection,
27 29
     last_draw_type: DrawType,
28 30
 }
@@ -40,7 +42,7 @@ enum DrawType {
40 42
     Null,
41 43
     Tiles,
42 44
     Markers,
43
-    Globe,
45
+    OrthoTiles,
44 46
 }
45 47
 
46 48
 impl MapViewGl {
@@ -55,7 +57,7 @@ impl MapViewGl {
55 57
     {
56 58
         let tile_size = 256;
57 59
 
58
-        let mut map_view = MapView::with_filling_zoom(f64::from(initial_size.0), f64::from(initial_size.1), tile_size);
60
+        let mut map_view = MercatorView::initial_map_view(f64::from(initial_size.0), f64::from(initial_size.1), tile_size);
59 61
 
60 62
         if map_view.zoom < MIN_ZOOM_LEVEL {
61 63
             map_view.zoom = MIN_ZOOM_LEVEL;
@@ -93,7 +95,7 @@ impl MapViewGl {
93 95
             tile_atlas,
94 96
             tile_layer,
95 97
             marker_layer: MarkerLayer::new(cx),
96
-            globe_tile_layer: GlobeTileLayer::new(cx),
98
+            ortho_tile_layer: OrthoTileLayer::new(cx),
97 99
             projection: Projection::Mercator,
98 100
             last_draw_type: DrawType::Null,
99 101
         }
@@ -111,9 +113,9 @@ impl MapViewGl {
111 113
 
112 114
     pub fn map_covers_viewport(&self) -> bool {
113 115
         match self.projection {
114
-            Projection::Mercator => self.map_view.map_covers_viewport(),
116
+            Projection::Mercator => MercatorView::covers_viewport(&self.map_view),
115 117
             //TODO uncomment
116
-            //Projection::Orthografic => self.map_view.globe_covers_viewport(),
118
+            //Projection::Orthografic => OrthograficView::covers_viewport(&self.map_view),
117 119
             Projection::Orthografic => false,
118 120
         }
119 121
     }
@@ -159,13 +161,13 @@ impl MapViewGl {
159 161
         self.marker_layer.draw(cx, &self.map_view, self.viewport_size, snap_to_pixel);
160 162
     }
161 163
 
162
-    fn draw_globe(&mut self, cx: &mut Context, source: &TileSource) {
163
-        if self.last_draw_type != DrawType::Globe {
164
-            self.last_draw_type = DrawType::Globe;
165
-            self.globe_tile_layer.prepare_draw(cx, &self.tile_atlas);
164
+    fn draw_ortho_tiles(&mut self, cx: &mut Context, source: &TileSource) {
165
+        if self.last_draw_type != DrawType::OrthoTiles {
166
+            self.last_draw_type = DrawType::OrthoTiles;
167
+            self.ortho_tile_layer.prepare_draw(cx, &self.tile_atlas);
166 168
         }
167 169
 
168
-        self.globe_tile_layer.draw(
170
+        self.ortho_tile_layer.draw(
169 171
             cx,
170 172
             &self.map_view,
171 173
             source,
@@ -190,7 +192,7 @@ impl MapViewGl {
190 192
                 ret
191 193
             },
192 194
             Projection::Orthografic => {
193
-                self.draw_globe(cx, source);
195
+                self.draw_ortho_tiles(cx, source);
194 196
                 Ok(1)
195 197
             },
196 198
         }
@@ -220,23 +222,25 @@ impl MapViewGl {
220 222
     }
221 223
 
222 224
     pub fn zoom_at(&mut self, pos: ScreenCoord, zoom_delta: f64) {
225
+        //TODO implement for OrthograficView
223 226
         if self.map_view.zoom + zoom_delta < MIN_ZOOM_LEVEL {
224
-            self.map_view.set_zoom_at(pos, MIN_ZOOM_LEVEL);
227
+            MercatorView::set_zoom_at(&mut self.map_view, pos, MIN_ZOOM_LEVEL);
225 228
         } else if self.map_view.zoom + zoom_delta > MAX_ZOOM_LEVEL {
226
-            self.map_view.set_zoom_at(pos, MAX_ZOOM_LEVEL);
229
+            MercatorView::set_zoom_at(&mut self.map_view, pos, MAX_ZOOM_LEVEL);
227 230
         } else {
228
-            self.map_view.zoom_at(pos, zoom_delta);
231
+            MercatorView::zoom_at(&mut self.map_view, pos, zoom_delta);
229 232
         }
230 233
         self.map_view.center.normalize_xy();
231 234
     }
232 235
 
233 236
     pub fn change_tile_zoom_offset(&mut self, delta_offset: f64) {
234
-        let offset = self.map_view.tile_zoom_offset();
237
+        let offset = self.map_view.tile_zoom_offset;
235 238
         self.map_view.set_tile_zoom_offset(offset + delta_offset);
236 239
     }
237 240
 
238 241
     pub fn move_pixel(&mut self, delta_x: f64, delta_y: f64) {
239
-        self.map_view.move_pixel(delta_x, delta_y);
242
+        //TODO implement for OrthograficView
243
+        MercatorView::move_pixel(&mut self.map_view, delta_x, delta_y);
240 244
         self.map_view.center.normalize_xy();
241 245
     }
242 246
 

+ 3
- 2
src/marker_layer.rs Datei anzeigen

@@ -5,6 +5,7 @@ use context::Context;
5 5
 use coord::{MapCoord, ScreenRect};
6 6
 use image;
7 7
 use map_view::MapView;
8
+use mercator_view::MercatorView;
8 9
 use program::Program;
9 10
 use texture::Texture;
10 11
 use vertex_attrib::VertexAttribParams;
@@ -115,9 +116,9 @@ impl MarkerLayer {
115 116
 
116 117
         for map_pos in &self.positions {
117 118
             let screen_pos = {
118
-                let mut sp = map_view.map_to_screen_coord(*map_pos);
119
+                let mut sp = MercatorView::map_to_screen_coord(map_view, *map_pos);
119 120
                 if snap_to_pixel {
120
-                    let topleft = map_view.map_to_screen_coord(MapCoord::new(0.0, 0.0));
121
+                    let topleft = MercatorView::map_to_screen_coord(map_view, MapCoord::new(0.0, 0.0));
121 122
                     let mut snapped = topleft;
122 123
                     snapped.snap_to_pixel();
123 124
 

+ 151
- 0
src/mercator_view.rs Datei anzeigen

@@ -0,0 +1,151 @@
1
+use coord::{MapCoord, ScreenCoord, ScreenRect, TileCoord};
2
+use map_view::MapView;
3
+
4
+
5
+/// A view of a tiled map with a rectangular viewport and a zoom.
6
+#[derive(Clone, Debug)]
7
+pub struct MercatorView {
8
+}
9
+
10
+/// The position and size of a specific tile on the screen.
11
+#[derive(Clone, Debug)]
12
+pub struct VisibleTile {
13
+    pub tile: TileCoord,
14
+    pub rect: ScreenRect,
15
+}
16
+
17
+impl MercatorView {
18
+    /// Constructs a `MapView` centered at Null Island with an integer zoom that fills a screen
19
+    /// with the given dimensions.
20
+    pub fn initial_map_view(width: f64, height: f64, tile_size: u32) -> MapView {
21
+        let min_dimension = width.min(height);
22
+        let zoom = (min_dimension / f64::from(tile_size)).log2().ceil();
23
+        MapView {
24
+            width,
25
+            height,
26
+            tile_size,
27
+            center: MapCoord::new(0.5, 0.5),
28
+            zoom,
29
+            tile_zoom_offset: 0.0,
30
+        }
31
+    }
32
+
33
+    /// Returns the map coordinate that corresponds to the top-left corner of the viewport.
34
+    pub fn top_left_coord(map_view: &MapView) -> MapCoord {
35
+        let scale = f64::powf(2.0, -map_view.zoom) / f64::from(map_view.tile_size);
36
+
37
+        let x = map_view.center.x + -0.5 * map_view.width * scale;
38
+        let y = map_view.center.y + -0.5 * map_view.height * scale;
39
+
40
+        MapCoord::new(x, y)
41
+    }
42
+
43
+    /// Returns the screen coordinate that corresponds to the given map coordinate.
44
+    pub fn map_to_screen_coord(map_view: &MapView, map_coord: MapCoord) -> ScreenCoord {
45
+        let scale = f64::powf(2.0, map_view.zoom) * f64::from(map_view.tile_size);
46
+
47
+        let delta_x = map_coord.x - map_view.center.x;
48
+        let delta_y = map_coord.y - map_view.center.y;
49
+
50
+        ScreenCoord {
51
+            x: 0.5 * map_view.width + delta_x * scale,
52
+            y: 0.5 * map_view.height + delta_y * scale,
53
+        }
54
+    }
55
+
56
+    /// Returns true if the viewport rectangle is fully inside the map.
57
+    pub fn covers_viewport(map_view: &MapView) -> bool {
58
+        let scale = f64::powf(2.0, -map_view.zoom) / f64::from(map_view.tile_size);
59
+
60
+        let y_top = map_view.center.y + -0.5 * map_view.height * scale;
61
+        let y_bottom = map_view.center.y + 0.5 * map_view.height * scale;
62
+
63
+        y_top >= 0.0 && y_bottom <= 1.0
64
+    }
65
+
66
+    /// Returns the screen coordinate of the top-left corner of a tile.
67
+    pub fn tile_screen_position(map_view: &MapView, tile: &TileCoord) -> ScreenCoord {
68
+        Self::map_to_screen_coord(map_view, tile.map_coord_north_west())
69
+    }
70
+
71
+    /// Returns a `Vec` of all tiles that are visible in the current viewport.
72
+    pub fn visible_tiles(map_view: &MapView, snap_to_pixel: bool) -> Vec<VisibleTile> {
73
+        let uzoom = Self::tile_zoom(map_view);
74
+        let top_left_tile = Self::top_left_coord(map_view).on_tile_at_zoom(uzoom);
75
+        let mut top_left_tile_screen_coord = Self::tile_screen_position(map_view, &top_left_tile);
76
+        let tile_screen_size = f64::powf(2.0, map_view.zoom - f64::from(uzoom)) *
77
+            f64::from(map_view.tile_size);
78
+
79
+        if snap_to_pixel {
80
+            top_left_tile_screen_coord.snap_to_pixel();
81
+        }
82
+
83
+        let start_tile_x = top_left_tile.x;
84
+        let start_tile_y = top_left_tile.y;
85
+        let num_tiles_x = ((map_view.width - top_left_tile_screen_coord.x) /
86
+                           tile_screen_size).ceil().max(0.0) as i32;
87
+        let num_tiles_y = ((map_view.height - top_left_tile_screen_coord.y) /
88
+                           tile_screen_size).ceil().max(0.0) as i32;
89
+
90
+        let mut visible_tiles = Vec::with_capacity(num_tiles_x as usize * num_tiles_y as usize);
91
+
92
+        for y in 0..num_tiles_y {
93
+            for x in 0..num_tiles_x {
94
+                let t = TileCoord::new(uzoom, start_tile_x + x, start_tile_y + y);
95
+                if t.is_on_planet() {
96
+                    visible_tiles.push(
97
+                        VisibleTile {
98
+                            tile: t,
99
+                            rect: ScreenRect {
100
+                                x: top_left_tile_screen_coord.x + tile_screen_size * f64::from(x),
101
+                                y: top_left_tile_screen_coord.y + tile_screen_size * f64::from(y),
102
+                                width: tile_screen_size,
103
+                                height: tile_screen_size,
104
+                            }
105
+                        }
106
+                    );
107
+                }
108
+            }
109
+        }
110
+
111
+        visible_tiles
112
+    }
113
+
114
+    /// Returns the tile zoom value that is used for rendering with the current zoom.
115
+    pub fn tile_zoom(map_view: &MapView) -> u32 {
116
+        (map_view.zoom + map_view.tile_zoom_offset).floor().max(0.0) as u32
117
+    }
118
+
119
+    /// Change zoom value by `zoom_delta` and zoom to a position given in screen coordinates.
120
+    pub fn zoom_at(map_view: &mut MapView, pos: ScreenCoord, zoom_delta: f64) {
121
+        let delta_x = pos.x - map_view.width * 0.5;
122
+        let delta_y = pos.y - map_view.height * 0.5;
123
+
124
+        let scale = (f64::powf(2.0, -map_view.zoom) - f64::powf(2.0, -map_view.zoom - zoom_delta))
125
+            / f64::from(map_view.tile_size);
126
+
127
+        map_view.zoom += zoom_delta;
128
+        map_view.center.x += delta_x * scale;
129
+        map_view.center.y += delta_y * scale;
130
+    }
131
+
132
+    /// Set a zoom value and zoom to a `position` given in screen coordinates.
133
+    pub fn set_zoom_at(map_view: &mut MapView, pos: ScreenCoord, zoom: f64) {
134
+        let delta_x = pos.x - map_view.width * 0.5;
135
+        let delta_y = pos.y - map_view.height * 0.5;
136
+
137
+        let scale = (f64::powf(2.0, -map_view.zoom) - f64::powf(2.0, -zoom)) /
138
+            f64::from(map_view.tile_size);
139
+
140
+        map_view.zoom = zoom;
141
+        map_view.center.x += delta_x * scale;
142
+        map_view.center.y += delta_y * scale;
143
+    }
144
+
145
+    /// Move the center of the viewport by (`delta_x`, `delta_y`) in screen coordinates.
146
+    pub fn move_pixel(map_view: &mut MapView, delta_x: f64, delta_y: f64) {
147
+        let scale = f64::powf(2.0, -map_view.zoom) / f64::from(map_view.tile_size);
148
+        map_view.center.x += delta_x * scale;
149
+        map_view.center.y += delta_y * scale;
150
+    }
151
+}

src/globe_tile_layer.rs → src/ortho_tile_layer.rs Datei anzeigen

@@ -1,10 +1,11 @@
1
-use std::ffi::CStr;
2 1
 use buffer::{Buffer, DrawMode};
3 2
 use cgmath::Transform;
4 3
 use context::Context;
5 4
 use coord::{LatLonRad, ScreenCoord, TileCoord, View};
6 5
 use map_view::MapView;
6
+use orthografic_view::OrthograficView;
7 7
 use program::Program;
8
+use std::ffi::CStr;
8 9
 use tile_atlas::TileAtlas;
9 10
 use tile_cache::TileCache;
10 11
 use tile_source::TileSource;
@@ -12,7 +13,7 @@ use vertex_attrib::VertexAttribParams;
12 13
 
13 14
 
14 15
 #[derive(Debug)]
15
-pub struct GlobeTileLayer {
16
+pub struct OrthoTileLayer {
16 17
     program: Program,
17 18
     buffer: Buffer,
18 19
 }
@@ -27,24 +28,24 @@ pub struct LatScreenEllipse {
27 28
 }
28 29
 
29 30
 impl LatScreenEllipse {
30
-    fn new(view_center: LatLonRad, viewport_size: (u32, u32), globe_radius: f64, lat: f64) -> Self {
31
+    fn new(view_center: LatLonRad, viewport_size: (u32, u32), sphere_radius: f64, lat: f64) -> Self {
31 32
         LatScreenEllipse {
32 33
             center: ScreenCoord {
33 34
                 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
+                y: viewport_size.1 as f64 * 0.5 * (lat - view_center.lat).sin() * sphere_radius,
35 36
             },
36
-            radius_x: lat.cos() * globe_radius,
37
-            radius_y: lat.cos() * -view_center.lat.sin() * globe_radius,
37
+            radius_x: lat.cos() * sphere_radius,
38
+            radius_y: lat.cos() * -view_center.lat.sin() * sphere_radius,
38 39
             ref_angle: view_center.lon,
39 40
         }
40 41
     }
41 42
 }
42 43
 
43 44
 
44
-impl GlobeTileLayer {
45
+impl OrthoTileLayer {
45 46
     pub fn new(
46 47
         cx: &mut Context,
47
-    ) -> GlobeTileLayer
48
+    ) -> OrthoTileLayer
48 49
     {
49 50
         let buffer = Buffer::new(cx, &[], 0);
50 51
         check_gl_errors!(cx);
@@ -52,8 +53,8 @@ impl GlobeTileLayer {
52 53
 
53 54
         let mut program = Program::new(
54 55
             cx,
55
-            include_bytes!("../shader/globe_tile.vert"),
56
-            include_bytes!("../shader/globe_tile.frag"),
56
+            include_bytes!("../shader/ortho_tile.vert"),
57
+            include_bytes!("../shader/ortho_tile.frag"),
57 58
         ).unwrap();
58 59
         check_gl_errors!(cx);
59 60
 
@@ -74,7 +75,7 @@ impl GlobeTileLayer {
74 75
         );
75 76
         check_gl_errors!(cx);
76 77
 
77
-        GlobeTileLayer {
78
+        OrthoTileLayer {
78 79
             program,
79 80
             buffer,
80 81
         }
@@ -98,17 +99,17 @@ impl GlobeTileLayer {
98 99
         //TODO Add distance function to TileCache that takes topology of the sphere into account.
99 100
         cache.set_view_location(View {
100 101
             source_id: source.id(),
101
-            zoom: map_view.tile_zoom(),
102
+            zoom: OrthograficView::tile_zoom(map_view),
102 103
             center: map_view.center,
103 104
         });
104 105
 
105 106
         let mut vertex_data = vec![];
106 107
 
107
-        let transform = map_view.globe_transformation_matrix();
108
+        let transform = OrthograficView::transformation_matrix(map_view);
108 109
 
109 110
         let (inset_x, inset_y) = tile_atlas.texture_margins();
110 111
 
111
-        for tile_coord in map_view.visible_globe_tiles().into_iter() {
112
+        for tile_coord in OrthograficView::visible_tiles(map_view).into_iter() {
112 113
             let slot = tile_atlas.store(cx, tile_coord, source, cache, true)
113 114
                 .unwrap_or_else(TileAtlas::default_slot);
114 115
             let texrect = tile_atlas.slot_to_texture_rect(slot);

+ 177
- 0
src/orthografic_view.rs Datei anzeigen

@@ -0,0 +1,177 @@
1
+use cgmath::{Matrix3, Point3, Transform, vec3};
2
+use coord::TileCoord;
3
+use map_view::MapView;
4
+use std::f32::consts::{PI, FRAC_1_PI};
5
+use std::f64;
6
+
7
+
8
+#[derive(Clone, Debug)]
9
+pub struct OrthograficView {
10
+}
11
+
12
+impl OrthograficView {
13
+    /// Returns true if the rendering covers the whole viewport.
14
+    pub fn covers_viewport(map_view: &MapView) -> bool {
15
+        //TODO Add a little safety margin since the rendered globe is not a perfect sphere and its
16
+        // screen area is underestimated by the tesselation.
17
+        let sphere_diameter = 2.0f64.powf(map_view.zoom) *
18
+            (f64::consts::FRAC_1_PI * map_view.tile_size as f64);
19
+
20
+        return map_view.width.hypot(map_view.height) < sphere_diameter;
21
+    }
22
+
23
+    /// Returns the tile zoom value that is used for rendering with the current zoom.
24
+    //TODO Insert real implementation. Add TileCoord parameter -> lower resolution at the poles
25
+    pub fn tile_zoom(map_view: &MapView) -> u32 {
26
+        (map_view.zoom + map_view.tile_zoom_offset).floor().max(0.0) as u32
27
+    }
28
+
29
+    //TODO Return the transformation matrix that is used here to avoid redundant calculation.
30
+    /// Returns a `Vec` of all tiles that are visible in the current viewport.
31
+    pub fn visible_tiles(map_view: &MapView) -> Vec<TileCoord> {
32
+        let uzoom = Self::tile_zoom(map_view);
33
+
34
+        match uzoom {
35
+            0 => return vec![TileCoord::new(0, 0, 0)],
36
+            1 => {
37
+                // return every tile
38
+                return vec![
39
+                    TileCoord::new(1, 0, 0),
40
+                    TileCoord::new(1, 0, 1),
41
+                    TileCoord::new(1, 1, 0),
42
+                    TileCoord::new(1, 1, 1),
43
+                ]},
44
+            _ => {},
45
+        }
46
+
47
+        let center_tile = map_view.center.on_tile_at_zoom(uzoom).globe_norm();
48
+
49
+        let transform = Self::transformation_matrix(map_view);
50
+
51
+        let add_tile_if_visible = |tc: TileCoord, vec: &mut Vec<TileCoord>| -> bool {
52
+            let test_point = tc.latlon_rad_north_west().to_sphere_point3();
53
+            let test_point = transform.transform_point(test_point);
54
+
55
+            let visible = test_point.x >= -1.0 && test_point.x <= 1.0 &&
56
+                test_point.y >= -1.0 && test_point.y <= 1.0;
57
+
58
+            if visible {
59
+                vec.push(tc);
60
+                true
61
+            } else {
62
+                false
63
+            }
64
+        };
65
+
66
+        let mut tiles = vec![];
67
+
68
+        {
69
+            let zoom_level_tiles = TileCoord::get_zoom_level_tiles(uzoom);
70
+
71
+            for dx in 0..(zoom_level_tiles / 2) {
72
+                let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x + dx, center_tile.y), &mut tiles);
73
+                if !v {
74
+                    break;
75
+                }
76
+            }
77
+            for dx in 1..(1 + zoom_level_tiles / 2) {
78
+                let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x - dx, center_tile.y), &mut tiles);
79
+                if !v {
80
+                    break;
81
+                }
82
+            }
83
+
84
+            // move south
85
+            for y in (center_tile.y + 1)..zoom_level_tiles {
86
+                let mut visible = false;
87
+
88
+                for dx in 0..(zoom_level_tiles / 2) {
89
+                    let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x + dx, y), &mut tiles);
90
+                    visible = visible || v;
91
+                    if !v {
92
+                        break;
93
+                    }
94
+                }
95
+                for dx in 1..(1 + zoom_level_tiles / 2) {
96
+                    let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x - dx, y), &mut tiles);
97
+                    visible = visible || v;
98
+                    if !v {
99
+                        break;
100
+                    }
101
+                }
102
+
103
+                if !visible {
104
+                    break;
105
+                }
106
+            }
107
+
108
+            // move north
109
+            for y in (0..center_tile.y).rev() {
110
+                let mut visible = false;
111
+
112
+                for dx in 0..(zoom_level_tiles / 2) {
113
+                    let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x + dx, y), &mut tiles);
114
+                    visible = visible || v;
115
+                    if !v {
116
+                        break;
117
+                    }
118
+                }
119
+                for dx in 1..(1 + zoom_level_tiles / 2) {
120
+                    let v = add_tile_if_visible(TileCoord::new(uzoom, center_tile.x - dx, y), &mut tiles);
121
+                    visible = visible || v;
122
+                    if !v {
123
+                        break;
124
+                    }
125
+                }
126
+
127
+                if !visible {
128
+                    break;
129
+                }
130
+            }
131
+        }
132
+
133
+        tiles
134
+    }
135
+
136
+    pub fn transformation_matrix(map_view: &MapView) -> Matrix3<f32> {
137
+        let (scale_x, scale_y) = {
138
+            let factor = 2.0f32.powf(map_view.zoom as f32) *
139
+                (FRAC_1_PI * map_view.tile_size as f32);
140
+            (factor / map_view.width as f32, factor / map_view.height as f32)
141
+        };
142
+
143
+        let scale_mat: Matrix3<f32> = Matrix3::from_cols(
144
+            vec3(scale_x, 0.0, 0.0),
145
+            vec3(0.0, scale_y, 0.0),
146
+            vec3(0.0, 0.0, 1.0),
147
+        );
148
+
149
+        let rot_mat_x: Matrix3<f32> = {
150
+            let center_latlon = map_view.center.to_latlon_rad();
151
+            let alpha = center_latlon.lon as f32 + (PI * 0.5);
152
+            let cosa = alpha.cos();
153
+            let sina = alpha.sin();
154
+                Matrix3::from_cols(
155
+                vec3(cosa, 0.0, -sina),
156
+                vec3(0.0, 1.0, 0.0),
157
+                vec3(sina, 0.0, cosa),
158
+            )
159
+        };
160
+
161
+        let rot_mat_y: Matrix3<f32> = {
162
+            let center_latlon = map_view.center.to_latlon_rad();
163
+            let alpha = (-center_latlon.lat) as f32;
164
+            let cosa = alpha.cos();
165
+            let sina = alpha.sin();
166
+                Matrix3::from_cols(
167
+                vec3(1.0, 0.0, 0.0),
168
+                vec3(0.0, cosa, sina),
169
+                vec3(0.0, -sina, cosa),
170
+            )
171
+        };
172
+
173
+        let transform = Transform::<Point3<f32>>::concat(&rot_mat_y, &rot_mat_x);
174
+        let transform = Transform::<Point3<f32>>::concat(&scale_mat, &transform);
175
+        transform
176
+    }
177
+}

+ 1
- 1
src/tile_atlas.rs Datei anzeigen

@@ -2,7 +2,7 @@ use context::Context;
2 2
 use coord::{ScreenRect, SubTileCoord, TileCoord, TextureRect};
3 3
 use image;
4 4
 use linked_hash_map::LinkedHashMap;
5
-use map_view::VisibleTile;
5
+use mercator_view::VisibleTile;
6 6
 use std::collections::HashMap;
7 7
 use std::collections::hash_map::Entry;
8 8
 use texture::Texture;

+ 3
- 2
src/tile_layer.rs Datei anzeigen

@@ -3,6 +3,7 @@ use buffer::{Buffer, DrawMode};
3 3
 use context::Context;
4 4
 use coord::View;
5 5
 use map_view::MapView;
6
+use mercator_view::MercatorView;
6 7
 use program::Program;
7 8
 use tile_atlas::TileAtlas;
8 9
 use tile_cache::TileCache;
@@ -79,11 +80,11 @@ impl TileLayer {
79 80
     ) -> Result<usize, usize> {
80 81
         cache.set_view_location(View {
81 82
             source_id: source.id(),
82
-            zoom: map_view.tile_zoom(),
83
+            zoom: MercatorView::tile_zoom(map_view),
83 84
             center: map_view.center,
84 85
         });
85 86
 
86
-        let visible_tiles = map_view.visible_tiles(snap_to_pixel);
87
+        let visible_tiles = MercatorView::visible_tiles(map_view, snap_to_pixel);
87 88
         let mut remainder = visible_tiles.as_slice();
88 89
         let mut num_draws = 0;
89 90
         let mut max_tiles_to_use = cache.max_tiles();