A simple map viewer

mercator_view.rs 6.6KB

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