Pārlūkot izejas kodu

Fix code that assumes that tiles touch the poles

Web-mercator tiles don't touch the poles.
Johannes Hofmann 7 gadus atpakaļ
vecāks
revīzija
41c480b1d1
3 mainītis faili ar 68 papildinājumiem un 80 dzēšanām
  1. 44
    76
      src/coord.rs
  2. 1
    1
      src/mercator_view.rs
  3. 23
    3
      src/orthografic_view.rs

+ 44
- 76
src/coord.rs Parādīt failu

@@ -255,7 +255,7 @@ impl SubTileCoord {
255 255
 /// A tile position in a tile pyramid.
256 256
 /// Each zoom level has 2<sup>zoom</sup> by 2<sup>zoom</sup> tiles.
257 257
 /// `x` and `y` are allowed to be negative or >= 2<sup>zoom</sup> but then they will not correspond to a tile
258
-/// and `is_on_planet` will return false.
258
+/// and `is_valid` will return false.
259 259
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
260 260
 pub struct TileCoord {
261 261
     pub zoom: u32,
@@ -272,12 +272,23 @@ impl TileCoord {
272 272
         }
273 273
     }
274 274
 
275
-    pub fn is_on_planet(&self) -> bool {
275
+    pub fn is_valid(&self) -> bool {
276 276
         let num_tiles = Self::get_zoom_level_tiles(self.zoom);
277 277
         self.y >= 0 && self.y < num_tiles &&
278 278
         self.x >= 0 && self.x < num_tiles
279 279
     }
280 280
 
281
+    /// Returns the nearest valid `TileCoord`. This wraps the x coordinate and clamps the y
282
+    /// coordinate.
283
+    pub fn nearest_valid(&self) -> Self {
284
+        let num_tiles = Self::get_zoom_level_tiles(self.zoom);
285
+        TileCoord {
286
+            zoom: self.zoom,
287
+            x: ((self.x % num_tiles) + num_tiles) % num_tiles,
288
+            y: self.y.min(num_tiles - 1).max(0),
289
+        }
290
+    }
291
+
281 292
     // Return the MapCoord of the top left corner of the current tile.
282 293
     pub fn map_coord_north_west(&self) -> MapCoord {
283 294
         let inv_zoom_factor = f64::powi(2.0, -(self.zoom as i32));
@@ -288,22 +299,10 @@ impl TileCoord {
288 299
     pub fn latlon_rad_north_west(&self) -> LatLonRad {
289 300
         let factor = f64::powi(2.0, -(self.zoom as i32)) * (2.0 * PI);
290 301
 
291
-        if self.y == 0 {
292
-            LatLonRad::new(
293
-                0.5 * PI,
294
-                f64::from(self.x) * factor - PI,
295
-            )
296
-        } else if self.y == Self::get_zoom_level_tiles(self.zoom) {
297
-            LatLonRad::new(
298
-                -0.5 * PI,
299
-                f64::from(self.x) * factor - PI,
300
-            )
301
-        } else {
302
-            LatLonRad::new(
303
-                (PI - f64::from(self.y) * factor).sinh().atan(),
304
-                f64::from(self.x) * factor - PI,
305
-            )
306
-        }
302
+        LatLonRad::new(
303
+            (PI - f64::from(self.y) * factor).sinh().atan(),
304
+            f64::from(self.x) * factor - PI,
305
+        )
307 306
     }
308 307
 
309 308
     // Return the LatLonRad coordinate of the bottom right corner of the current tile.
@@ -412,28 +411,6 @@ impl TileCoord {
412 411
         ((coord % max) + max) % max
413 412
     }
414 413
 
415
-    /// Wrap around in x-direction.
416
-    /// Values for y that are out-of-bounds "rotate" around the globe and also influence the
417
-    /// x-coordinate.
418
-    pub fn globe_norm(&self) -> Self {
419
-        let max = Self::get_zoom_level_tiles(self.zoom);
420
-        let period = max * 2;
421
-
422
-        let yp = ((self.y % period) + period) % period;
423
-        let side = yp / max;
424
-
425
-        let x = self.x + side * (max / 2);
426
-        let x = ((x % max) + max) % max;
427
-
428
-        let y = (1 - side) * yp + side * (period - 1 - yp);
429
-
430
-        TileCoord {
431
-            zoom: self.zoom,
432
-            x,
433
-            y,
434
-        }
435
-    }
436
-
437 414
     #[inline]
438 415
     pub fn get_zoom_level_tiles(zoom: u32) -> i32 {
439 416
         //TODO throw error when zoom too big
@@ -542,6 +519,33 @@ mod tests {
542 519
         assert_eq!(TileCoord::new(30, 0, 1).to_quadkey(), Some("000000000000000000000000000002".to_string()));
543 520
     }
544 521
 
522
+    fn tc(zoom: u32, x: i32, y: i32) -> TileCoord {
523
+        TileCoord { zoom, x, y }
524
+    }
525
+
526
+    #[test]
527
+    fn nearest_valid() {
528
+        assert_eq!(tc(0, 0, 0).nearest_valid(), tc(0, 0, 0));
529
+        assert_eq!(tc(0, -1, 0).nearest_valid(), tc(0, 0, 0));
530
+        assert_eq!(tc(0, 1, 0).nearest_valid(), tc(0, 0, 0));
531
+        assert_eq!(tc(0, 0, -1).nearest_valid(), tc(0, 0, 0));
532
+        assert_eq!(tc(0, 0, 1).nearest_valid(), tc(0, 0, 0));
533
+
534
+        assert_eq!(tc(1, 0, 0).nearest_valid(), tc(1, 0, 0));
535
+        assert_eq!(tc(1, -1, 0).nearest_valid(), tc(1, 1, 0));
536
+        assert_eq!(tc(1, 1, 0).nearest_valid(), tc(1, 1, 0));
537
+        assert_eq!(tc(1, 0, -1).nearest_valid(), tc(1, 0, 0));
538
+        assert_eq!(tc(1, 0, 1).nearest_valid(), tc(1, 0, 1));
539
+        assert_eq!(tc(1, 0, 2).nearest_valid(), tc(1, 0, 1));
540
+
541
+        assert_eq!(tc(2, 0, 0).nearest_valid(), tc(2, 0, 0));
542
+        assert_eq!(tc(2, 4, 0).nearest_valid(), tc(2, 0, 0));
543
+        assert_eq!(tc(2, 5, 0).nearest_valid(), tc(2, 1, 0));
544
+        assert_eq!(tc(2, -1, 0).nearest_valid(), tc(2, 3, 0));
545
+        assert_eq!(tc(2, 0, 4).nearest_valid(), tc(2, 0, 3));
546
+        assert_eq!(tc(2, 0, -1).nearest_valid(), tc(2, 0, 0));
547
+    }
548
+
545 549
     fn approx_eq(a: f64, b: f64) -> bool {
546 550
         (a - b).abs() < 1e-10
547 551
     }
@@ -590,18 +594,6 @@ mod tests {
590 594
         }
591 595
     }
592 596
 
593
-    #[test]
594
-    fn tile_to_latlon() {
595
-        // Test edge cases at the poles where the longitude is technically undefined.
596
-        let t = TileCoord::new(0, 0, 0);
597
-        let deg = t.latlon_rad_north_west();
598
-        assert!(approx_eq(deg.lat, 0.5 * PI));
599
-        assert!(approx_eq(deg.lon, -PI));
600
-        let deg = t.latlon_rad_south_east();
601
-        assert!(approx_eq(deg.lat, -0.5 * PI));
602
-        assert!(approx_eq(deg.lon, PI));
603
-    }
604
-
605 597
     #[test]
606 598
     fn tile_children() {
607 599
         let t = TileCoord::new(2, 1, 2);
@@ -614,28 +606,4 @@ mod tests {
614 606
         assert_eq!(t.children_iter(1).count(), 4);
615 607
         assert_eq!(t.children_iter(2).count(), 16);
616 608
     }
617
-
618
-    #[test]
619
-    fn globe_norm() {
620
-        assert_eq!(TileCoord::new(0, 0, 0).globe_norm(), TileCoord::new(0, 0, 0));
621
-        assert_eq!(TileCoord::new(0, -1, 0).globe_norm(), TileCoord::new(0, 0, 0));
622
-        assert_eq!(TileCoord::new(0, -1, -1).globe_norm(), TileCoord::new(0, 0, 0));
623
-        assert_eq!(TileCoord::new(0, 0, 1).globe_norm(), TileCoord::new(0, 0, 0));
624
-
625
-        assert_eq!(TileCoord::new(2, 0, 0).globe_norm(), TileCoord::new(2, 0, 0));
626
-        assert_eq!(TileCoord::new(2, 0, 3).globe_norm(), TileCoord::new(2, 0, 3));
627
-        assert_eq!(TileCoord::new(2, 0, 4).globe_norm(), TileCoord::new(2, 2, 3));
628
-        assert_eq!(TileCoord::new(2, 0, 5).globe_norm(), TileCoord::new(2, 2, 2));
629
-        assert_eq!(TileCoord::new(2, 0, 8).globe_norm(), TileCoord::new(2, 0, 0));
630
-
631
-        assert_eq!(TileCoord::new(2, 3, 0).globe_norm(), TileCoord::new(2, 3, 0));
632
-        assert_eq!(TileCoord::new(2, 3, 3).globe_norm(), TileCoord::new(2, 3, 3));
633
-        assert_eq!(TileCoord::new(2, 3, 4).globe_norm(), TileCoord::new(2, 1, 3));
634
-        assert_eq!(TileCoord::new(2, 3, 5).globe_norm(), TileCoord::new(2, 1, 2));
635
-        assert_eq!(TileCoord::new(2, 3, 8).globe_norm(), TileCoord::new(2, 3, 0));
636
-
637
-        assert_eq!(TileCoord::new(2, -1, 0).globe_norm(), TileCoord::new(2, 3, 0));
638
-        assert_eq!(TileCoord::new(2, 0, -1).globe_norm(), TileCoord::new(2, 2, 0));
639
-        assert_eq!(TileCoord::new(2, 0, -5).globe_norm(), TileCoord::new(2, 0, 3));
640
-    }
641 609
 }

+ 1
- 1
src/mercator_view.rs Parādīt failu

@@ -116,7 +116,7 @@ impl MercatorView {
116 116
         for y in 0..num_tiles_y {
117 117
             for x in 0..num_tiles_x {
118 118
                 let t = TileCoord::new(uzoom, start_tile_x + x, start_tile_y + y);
119
-                if t.is_on_planet() {
119
+                if t.is_valid() {
120 120
                     visible_tiles.push(
121 121
                         VisibleTile {
122 122
                             tile: t,

+ 23
- 3
src/orthografic_view.rs Parādīt failu

@@ -76,6 +76,16 @@ pub fn tile_neighbors(origin: TileCoord, result: &mut Vec<TileNeighbor>) {
76 76
                     origin.x,
77 77
                     origin.y + 1,
78 78
                 )),
79
+                TileNeighbor::Coord(TileCoord::new(
80
+                    origin.zoom,
81
+                    (origin.x + 1) % zoom_level_tiles,
82
+                    origin.y,
83
+                )),
84
+                TileNeighbor::Coord(TileCoord::new(
85
+                    origin.zoom,
86
+                    (origin.x + zoom_level_tiles - 1) % zoom_level_tiles,
87
+                    origin.y,
88
+                )),
79 89
             ]);
80 90
         },
81 91
         (_, y) if y == zoom_level_tiles - 1 => {
@@ -86,6 +96,16 @@ pub fn tile_neighbors(origin: TileCoord, result: &mut Vec<TileNeighbor>) {
86 96
                     origin.x,
87 97
                     origin.y - 1,
88 98
                 )),
99
+                TileNeighbor::Coord(TileCoord::new(
100
+                    origin.zoom,
101
+                    (origin.x + 1) % zoom_level_tiles,
102
+                    origin.y,
103
+                )),
104
+                TileNeighbor::Coord(TileCoord::new(
105
+                    origin.zoom,
106
+                    (origin.x + zoom_level_tiles - 1) % zoom_level_tiles,
107
+                    origin.y,
108
+                )),
89 109
             ]);
90 110
         },
91 111
         _ => {
@@ -155,7 +175,7 @@ impl OrthograficView {
155 175
             _ => {},
156 176
         }
157 177
 
158
-        let center_tile = map_view.center.on_tile_at_zoom(uzoom).globe_norm();
178
+        let center_tile = map_view.center.on_tile_at_zoom(uzoom).nearest_valid();
159 179
 
160 180
         let transform = Self::transformation_matrix(map_view);
161 181
 
@@ -367,11 +387,11 @@ mod tests {
367 387
         assert!(result.iter().find(|&&x| x == TileNeighbor::Coord(TileCoord::new(1, 1, 1))).is_none());
368 388
 
369 389
         tile_neighbors(TileCoord::new(2, 0, 0), &mut result);
370
-        assert_eq!(result.len(), 2);
390
+        assert_eq!(result.len(), 4);
371 391
         assert!(result.iter().find(|&&x| x == TileNeighbor::NorthPole).is_some());
372 392
 
373 393
         tile_neighbors(TileCoord::new(2, 0, 3), &mut result);
374
-        assert_eq!(result.len(), 2);
394
+        assert_eq!(result.len(), 4);
375 395
         assert!(result.iter().find(|&&x| x == TileNeighbor::SouthPole).is_some());
376 396
 
377 397
         tile_neighbors(TileCoord::new(2, 3, 1), &mut result);