Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Struct for zoom level #118

Merged
merged 7 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions maplibre/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::coords::{Zoom, TILE_SIZE};
use crate::coords::{Zoom, ZoomLevel, TILE_SIZE};
use crate::io::shared_thread_state::SharedThreadState;
use crate::io::tile_cache::TileCache;
use crate::io::TessellateMessage;
Expand Down Expand Up @@ -43,7 +43,7 @@ impl ViewState {
self.camera.calc_view_proj(&self.perspective)
}

pub fn visible_level(&self) -> u8 {
pub fn visible_level(&self) -> ZoomLevel {
self.zoom.level()
}

Expand Down
173 changes: 123 additions & 50 deletions maplibre/src/coords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ const fn create_zoom_bounds<const DIM: usize>() -> [u32; DIM] {
///
/// TODO: We can optimize the quadkey and store the keys on 2 bits instead of 8
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Copy)]
pub struct Quadkey([u8; MAX_ZOOM]);
pub struct Quadkey([ZoomLevel; MAX_ZOOM]);

impl Quadkey {
pub fn new(quad_encoded: &[u8]) -> Self {
let mut key = [0u8; MAX_ZOOM];
key[0] = quad_encoded.len() as u8;
pub fn new(quad_encoded: &[ZoomLevel]) -> Self {
let mut key = [ZoomLevel::default(); MAX_ZOOM];
key[0] = ZoomLevel::new(quad_encoded.len() as u8);
ybiletskyi marked this conversation as resolved.
Show resolved Hide resolved
for (i, part) in quad_encoded.iter().enumerate() {
key[i + 1] = *part;
}
Expand All @@ -48,14 +48,65 @@ impl Quadkey {

impl fmt::Debug for Quadkey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let len = self.0[0] as usize;
let len = self.0[0].0 as usize;
ybiletskyi marked this conversation as resolved.
Show resolved Hide resolved
for part in &self.0[0..len] {
write!(f, "{:?}", part)?;
}
Ok(())
}
}

#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone, Debug)]
pub struct ZoomLevel(u8);

impl ZoomLevel {
pub fn new(zoom_level: u8) -> Self {
ZoomLevel(zoom_level)
}
ybiletskyi marked this conversation as resolved.
Show resolved Hide resolved
}

impl Default for ZoomLevel {
fn default() -> Self {
ZoomLevel(0)
}
}

impl std::ops::Add<i32> for ZoomLevel {
type Output = ZoomLevel;

fn add(self, rhs: i32) -> Self::Output {
ZoomLevel(self.0 + rhs as u8)
}
}

impl std::ops::Sub<i32> for ZoomLevel {
type Output = ZoomLevel;

fn sub(self, rhs: i32) -> Self::Output {
ZoomLevel(self.0 - rhs as u8)
ybiletskyi marked this conversation as resolved.
Show resolved Hide resolved
}
}

impl std::ops::Mul<i32> for ZoomLevel {
type Output = ZoomLevel;

fn mul(self, rhs: i32) -> Self::Output {
ZoomLevel(self.0 * rhs as u8)
}
}
maxammann marked this conversation as resolved.
Show resolved Hide resolved

impl fmt::Display for ZoomLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}

impl Into<u8> for ZoomLevel {
fn into(self) -> u8 {
self.0
}
}

/// `Zoom` is an exponential scale that defines the zoom of the camera on the map.
/// We can derive the `ZoomLevel` from `Zoom` by using the `[crate::coords::ZOOM_BOUNDS]`.
#[derive(Copy, Clone, Debug)]
Expand All @@ -67,6 +118,12 @@ impl Zoom {
}
}

impl Zoom {
pub fn from(zoom_level: ZoomLevel) -> Self {
Zoom(zoom_level.0 as f64)
}
}

impl Default for Zoom {
fn default() -> Self {
Zoom(0.0)
Expand Down Expand Up @@ -97,19 +154,19 @@ impl std::ops::Sub for Zoom {

impl Zoom {
pub fn scale_to_tile(&self, coords: &WorldTileCoords) -> f64 {
2.0_f64.powf(coords.z as f64 - self.0)
2.0_f64.powf(coords.z.0 as f64 - self.0)
}

pub fn scale_to_zoom_level(&self, z: u8) -> f64 {
2.0_f64.powf(z as f64 - self.0)
pub fn scale_to_zoom_level(&self, z: ZoomLevel) -> f64 {
2.0_f64.powf(z.0 as f64 - self.0)
}

pub fn scale_delta(&self, zoom: &Zoom) -> f64 {
2.0_f64.powf(zoom.0 - self.0)
}

pub fn level(&self) -> u8 {
self.0.floor() as u8
pub fn level(&self) -> ZoomLevel {
ZoomLevel::new(self.0.floor() as u8)
}
}

Expand Down Expand Up @@ -144,7 +201,7 @@ pub struct InnerCoords {
pub struct TileCoords {
pub x: u32,
pub y: u32,
pub z: u8,
pub z: ZoomLevel,
}

impl TileCoords {
Expand All @@ -158,7 +215,7 @@ impl TileCoords {
pub fn into_world_tile(self, scheme: TileAddressingScheme) -> Option<WorldTileCoords> {
// FIXME: MAX_ZOOM is 32, which means max bound is 2^32, which wouldn't fit in u32 or i32
// Note that unlike WorldTileCoords, values are signed (no idea why)
let bounds = ZOOM_BOUNDS[self.z as usize] as i32;
let bounds = ZOOM_BOUNDS[self.z.0 as usize] as i32;
let x = self.x as i32;
let y = self.y as i32;

Expand All @@ -182,7 +239,7 @@ impl From<(u32, u32, u8)> for TileCoords {
TileCoords {
x: tuple.0,
y: tuple.1,
z: tuple.2,
z: ZoomLevel::new(tuple.2),
}
}
}
Expand All @@ -198,7 +255,7 @@ impl From<(u32, u32, u8)> for TileCoords {
pub struct WorldTileCoords {
pub x: i32,
pub y: i32,
pub z: u8,
pub z: ZoomLevel,
}

impl WorldTileCoords {
Expand All @@ -211,7 +268,7 @@ impl WorldTileCoords {
/// `x=5,y=5` at zoom level `z=0`.
pub fn into_tile(self, scheme: TileAddressingScheme) -> Option<TileCoords> {
// FIXME: MAX_ZOOM is 32, which means max bound is 2^32, which wouldn't fit in u32 or i32
let bounds = ZOOM_BOUNDS[self.z as usize];
let bounds = ZOOM_BOUNDS[self.z.0 as usize];
let x = self.x as u32;
let y = self.y as u32;

Expand Down Expand Up @@ -239,7 +296,7 @@ impl WorldTileCoords {
If tile.z > zoom:
=> scale < 512
*/
let tile_scale = TILE_SIZE * Zoom::new(self.z as f64).scale_delta(&zoom);
let tile_scale = TILE_SIZE * Zoom::from(self.z).scale_delta(&zoom);

let translate = Matrix4::from_translation(Vector3::new(
self.x as f64 * tile_scale,
Expand All @@ -264,19 +321,19 @@ impl WorldTileCoords {

/// Adopted from [tilebelt](https://github.com/mapbox/tilebelt)
pub fn build_quad_key(&self) -> Option<Quadkey> {
let bounds = ZOOM_BOUNDS[self.z as usize];
let bounds = ZOOM_BOUNDS[self.z.0 as usize];
let x = self.x as u32;
let y = self.y as u32;

if x >= bounds || y >= bounds {
return None;
}

let mut key = [0u8; MAX_ZOOM];
let mut key = [ZoomLevel::default(); MAX_ZOOM];

key[0] = self.z;

for z in 1..self.z + 1 {
for z in 1..self.z.0 + 1 {
let mut b = 0;
let mask: i32 = 1 << (z - 1);
if (self.x & mask) != 0 {
Expand All @@ -285,7 +342,7 @@ impl WorldTileCoords {
if (self.y & mask) != 0 {
b += 2u8;
}
key[z as usize] = b;
key[z as usize] = ZoomLevel::new(b);
}
Some(Quadkey(key))
}
Expand Down Expand Up @@ -318,7 +375,7 @@ impl WorldTileCoords {

/// Get the tile which is one zoom level lower and contains this one
pub fn get_parent(&self) -> Option<WorldTileCoords> {
if self.z == 0 {
if self.z == ZoomLevel::default() {
ybiletskyi marked this conversation as resolved.
Show resolved Hide resolved
return None;
}

Expand All @@ -335,7 +392,7 @@ impl From<(i32, i32, u8)> for WorldTileCoords {
WorldTileCoords {
x: tuple.0,
y: tuple.1,
z: tuple.2,
z: ZoomLevel::new(tuple.2),
}
}
}
Expand Down Expand Up @@ -402,7 +459,7 @@ impl WorldCoords {
Self { x, y }
}

pub fn into_world_tile(self, z: u8, zoom: Zoom) -> WorldTileCoords {
pub fn into_world_tile(self, z: ZoomLevel, zoom: Zoom) -> WorldTileCoords {
let tile_scale = zoom.scale_to_zoom_level(z) / TILE_SIZE; // TODO: Deduplicate
let x = self.x * tile_scale;
let y = self.y * tile_scale;
Expand Down Expand Up @@ -447,12 +504,12 @@ impl From<Point3<f64>> for WorldCoords {
pub struct ViewRegion {
min_tile: WorldTileCoords,
max_tile: WorldTileCoords,
z: u8,
z: ZoomLevel,
padding: i32,
}

impl ViewRegion {
pub fn new(view_region: Aabb2<f64>, padding: i32, zoom: Zoom, z: u8) -> Self {
pub fn new(view_region: Aabb2<f64>, padding: i32, zoom: Zoom, z: ZoomLevel) -> Self {
let min_world: WorldCoords = WorldCoords::at_ground(view_region.min.x, view_region.min.y);
let min_world_tile: WorldTileCoords = min_world.into_world_tile(z, zoom);
let max_world: WorldCoords = WorldCoords::at_ground(view_region.max.x, view_region.max.y);
Expand All @@ -466,7 +523,7 @@ impl ViewRegion {
}
}

pub fn zoom_level(&self) -> u8 {
pub fn zoom_level(&self) -> ZoomLevel {
self.z
}

Expand All @@ -481,15 +538,15 @@ impl ViewRegion {
pub fn iter(&self) -> impl Iterator<Item = WorldTileCoords> + '_ {
(self.min_tile.x - self.padding..self.max_tile.x + 1 + self.padding).flat_map(move |x| {
(self.min_tile.y - self.padding..self.max_tile.y + 1 + self.padding).map(move |y| {
let tile_coord: WorldTileCoords = (x, y, self.z as u8).into();
let tile_coord: WorldTileCoords = (x, y, self.z.0 as u8).into();
ybiletskyi marked this conversation as resolved.
Show resolved Hide resolved
tile_coord
})
})
}
}

impl fmt::Display for TileCoords {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"T(x={x},y={y},z={z})",
Expand Down Expand Up @@ -524,7 +581,7 @@ mod tests {
use crate::style::source::TileAddressingScheme;

use crate::coords::{
Quadkey, TileCoords, ViewRegion, WorldCoords, WorldTileCoords, Zoom, EXTENT,
Quadkey, TileCoords, ViewRegion, WorldCoords, WorldTileCoords, Zoom, ZoomLevel, EXTENT,
};
use crate::util::math::Aabb2;

Expand Down Expand Up @@ -553,32 +610,48 @@ mod tests {
#[test]
fn test_quad_key() {
assert_eq!(
TileCoords { x: 0, y: 0, z: 1 }
.into_world_tile(TileAddressingScheme::TMS)
.unwrap()
.build_quad_key(),
Some(Quadkey::new(&[2]))
TileCoords {
x: 0,
y: 0,
z: ZoomLevel::new(1)
}
.into_world_tile(TileAddressingScheme::TMS)
.unwrap()
.build_quad_key(),
Some(Quadkey::new(&[ZoomLevel::new(2)]))
);
assert_eq!(
TileCoords { x: 0, y: 1, z: 1 }
.into_world_tile(TileAddressingScheme::TMS)
.unwrap()
.build_quad_key(),
Some(Quadkey::new(&[0]))
TileCoords {
x: 0,
y: 1,
z: ZoomLevel::new(1)
}
.into_world_tile(TileAddressingScheme::TMS)
.unwrap()
.build_quad_key(),
Some(Quadkey::new(&[ZoomLevel::new(0)]))
);
assert_eq!(
TileCoords { x: 1, y: 1, z: 1 }
.into_world_tile(TileAddressingScheme::TMS)
.unwrap()
.build_quad_key(),
Some(Quadkey::new(&[1]))
TileCoords {
x: 1,
y: 1,
z: ZoomLevel::new(1)
}
.into_world_tile(TileAddressingScheme::TMS)
.unwrap()
.build_quad_key(),
Some(Quadkey::new(&[ZoomLevel::new(1)]))
);
assert_eq!(
TileCoords { x: 1, y: 0, z: 1 }
.into_world_tile(TileAddressingScheme::TMS)
.unwrap()
.build_quad_key(),
Some(Quadkey::new(&[3]))
TileCoords {
x: 1,
y: 0,
z: ZoomLevel::new(1)
}
.into_world_tile(TileAddressingScheme::TMS)
.unwrap()
.build_quad_key(),
Some(Quadkey::new(&[ZoomLevel::new(3)]))
);
}

Expand All @@ -588,7 +661,7 @@ mod tests {
Aabb2::new(Point2::new(0.0, 0.0), Point2::new(2000.0, 2000.0)),
1,
Zoom::default(),
0,
ZoomLevel::default(),
)
.iter()
{
Expand Down
Loading