Skip to main content

map_tile_toolkit/
tile.rs

1//! Tile addressing on the integer grid.
2//!
3//! A tile of side `extent` covers the closed integer square `[x·extent, x·extent + extent − 1]` on
4//! each axis, so the boundary between tiles `k−1` and `k` sits at `k·extent − 0.5` (between two
5//! integer coordinates). Integer vertices therefore never land exactly on a tile edge — every vertex
6//! belongs to exactly one tile.
7
8use geo_types::Coord;
9
10/// A tile address on the integer grid. Coordinates may be negative.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub struct TileId {
13    pub x: i32,
14    pub y: i32,
15}
16
17impl TileId {
18    #[must_use]
19    pub fn new(x: i32, y: i32) -> Self {
20        Self { x, y }
21    }
22
23    /// This tile's origin in global coordinates — `self · extent`, the global position its local
24    /// `[0, 0]` corner maps to. **Add** it to a tile-local coordinate to lift it into the global
25    /// frame, or **subtract** it to drop a global coordinate into this tile's local frame.
26    ///
27    /// Returns `None` if `self · extent` overflows `i32` (a tile far outside the representable range
28    /// for this `extent`).
29    ///
30    /// ```
31    /// # use map_tile_toolkit::TileId;
32    /// # use geo_types::Coord;
33    /// let origin = TileId::new(2, 3).origin(25).expect("in range");
34    /// assert_eq!(origin, Coord { x: 50, y: 75 });
35    /// assert_eq!(Coord { x: 4, y: 1 } + origin, Coord { x: 54, y: 76 }); // local → global
36    /// assert_eq!(Coord { x: 54, y: 76 } - origin, Coord { x: 4, y: 1 }); // global → local
37    /// ```
38    #[must_use]
39    pub fn origin(self, extent: u32) -> Option<Coord<i32>> {
40        let e = i64::from(extent);
41        Some(Coord {
42            x: i32::try_from(i64::from(self.x) * e).ok()?,
43            y: i32::try_from(i64::from(self.y) * e).ok()?,
44        })
45    }
46}
47
48impl From<(i32, i32)> for TileId {
49    fn from((x, y): (i32, i32)) -> Self {
50        Self { x, y }
51    }
52}
53
54/// The tile that owns coordinate `c` for the given tile side `extent`.
55pub(crate) fn tile_of(c: Coord<i32>, extent: i32) -> TileId {
56    TileId::new(c.x.div_euclid(extent), c.y.div_euclid(extent))
57}