|
|
@@ -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
|
}
|