Skip to main content

utiles_core/
zoom.rs

1//! Zoom levels, zoom-collections and ranges oh my!
2use std::num::ParseIntError;
3use std::ops::BitAnd;
4
5use serde::Serialize;
6
7use crate::UtilesCoreError;
8use crate::UtilesCoreError::InvalidZoom;
9use crate::constants::MAX_ZOOM;
10use crate::errors::UtilesCoreResult;
11
12/// Return max-xy for a zoom level
13#[must_use]
14pub fn zoom_max_xy(zoom: u8) -> u32 {
15    2_u32.pow(u32::from(zoom)) - 1
16}
17
18/// `ZoomSet` is a bit-set of zoom levels represented as a `u32`.
19///
20/// Each bit represents a zoom level (0 <= z <= 30). BY DEFAULT: The
21/// least significant bit represents zoom level 0 and the SECOND most significant
22/// bit represents zoom level 30. BUT if the MOST significant bit is 1, then the
23/// order is reversed.
24///
25/// # Examples
26/// ```
27/// use utiles_core::zoom::ZoomSet;
28/// let zset_int_fwd = ZoomSet::new(0b0000_0000_0000_0000_0000_0000_0000_0111);
29/// //                                ^ is 1 so the order is forward/default
30/// let zset_int_rev = ZoomSet::new(0b1111_0000_0000_0000_0000_0000_0000_0000);
31/// //                                ^ is 1 so the order is reversed
32/// let zooms_fwd_vec: Vec<u8> = zset_int_fwd.into();
33/// assert_eq!(zooms_fwd_vec, vec![0, 1, 2]);
34/// let zooms_rev_vec: Vec<u8> = zset_int_rev.into();
35/// assert_eq!(zooms_rev_vec, vec![0, 1, 2]);
36/// ```
37#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Serialize)]
38pub struct ZoomSet(u32);
39
40/// Return a vector of zoom levels from a zoom-set u32
41///
42/// # Examples
43///
44/// ```
45/// use utiles_core::zoom::zset2zvec;
46/// let zset: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0111;
47/// let zvec: Vec<u8> = zset2zvec(zset);
48/// assert_eq!(zvec, vec![0, 1, 2]);
49/// ```
50///
51/// ```
52/// use utiles_core::zoom::zset2zvec;
53/// let zset: u32 = 0b1111_0000_0000_0000_0000_0000_0000_0000;
54/// let zvec: Vec<u8> = zset2zvec(zset);
55/// assert_eq!(zvec, vec![0, 1, 2]);
56/// ```
57#[must_use]
58pub fn zset2zvec(zset: u32) -> Vec<u8> {
59    // if the most significant bit is 1, then the order is reversed
60    if zset & (1 << 31) != 0 {
61        zset2zvec(zset.reverse_bits() >> 1)
62    } else {
63        (0u8..31).filter(|&i| (zset & (1u32 << i)) != 0).collect()
64    }
65}
66
67/// Return a zoom-set u32 from a vector of zoom levels
68///
69/// # Examples
70/// ```
71/// use utiles_core::zoom::zvec2zset;
72/// let zvec: Vec<u8> = vec![0, 1, 2];
73/// let zset_int: u32 = zvec2zset(&zvec);
74/// assert_eq!(zset_int, 0b0000_0000_0000_0000_0000_0000_0000_0111);
75/// ```
76#[must_use]
77pub fn zvec2zset(zvec: &[u8]) -> u32 {
78    zvec.iter().fold(0, |acc, &z| acc | (1 << z))
79}
80
81/// Return a vector of zoom levels from a reversed-zoom-set u32
82///
83#[must_use]
84pub fn zset2zvec_rev(zset: u32) -> Vec<u8> {
85    zset2zvec(zset.reverse_bits() >> 1)
86}
87
88/// Return a zoom-set u32 from a vector of zoom levels
89#[must_use]
90pub fn zvec2zset_rev(zvec: &[u8]) -> u32 {
91    zvec.iter().fold(0, |acc, &z| acc | (1 << (31 - z)))
92}
93
94/// `ZoomSet` implementation
95impl ZoomSet {
96    /// Create a new `ZoomSet` from a u32
97    #[must_use]
98    pub const fn new(zset: u32) -> Self {
99        Self(zset)
100    }
101
102    /// Create a new `ZoomSet` from a vector of zoom levels
103    #[must_use]
104    pub fn from_zooms(zooms: &[u8]) -> Self {
105        Self(zvec2zset(zooms))
106    }
107
108    /// Return a vector of zoom levels from a zoom-set u32
109    #[must_use]
110    pub fn to_zooms(&self) -> Vec<u8> {
111        zset2zvec(self.0)
112    }
113
114    #[must_use]
115    pub const fn all() -> Self {
116        Self(0b0111_1111_1111_1111_1111_1111_1111_1111)
117    }
118
119    #[must_use]
120    pub fn zoom_ranges(&self) -> Vec<ZoomRange> {
121        let mut ranges: Vec<ZoomRange> = vec![];
122        let mut min: u8 = 0;
123        let mut max: u8 = 0;
124        let mut i: u8 = 0;
125        while i < MAX_ZOOM {
126            if self.0 & (1 << i) != 0 {
127                if min == 0 {
128                    min = i;
129                }
130                max = i;
131            } else if min != 0 {
132                ranges.push(ZoomRange::new(min, max));
133                min = 0;
134                max = 0;
135            }
136            i += 1;
137        }
138        if min != 0 {
139            ranges.push(ZoomRange::new(min, max));
140        }
141        ranges
142    }
143}
144
145impl From<u8> for ZoomSet {
146    fn from(zoom: u8) -> Self {
147        Self(1 << zoom)
148    }
149}
150
151impl From<u32> for ZoomSet {
152    fn from(zset: u32) -> Self {
153        Self(zset)
154    }
155}
156
157impl From<ZoomSet> for Vec<u8> {
158    fn from(zset: ZoomSet) -> Self {
159        zset2zvec(zset.0)
160    }
161}
162
163impl TryFrom<Vec<u8>> for ZoomSet {
164    type Error = UtilesCoreError;
165
166    fn try_from(zvec: Vec<u8>) -> Result<Self, Self::Error> {
167        let result = zvec.iter().try_fold(0u32, |acc, &z| {
168            if z > MAX_ZOOM {
169                Err(InvalidZoom(z.to_string()))
170            } else {
171                Ok(acc | (1 << z))
172            }
173        })?;
174        Ok(Self::new(result)) // Replace with actual construction method
175    }
176}
177
178impl BitAnd for ZoomSet {
179    type Output = Self;
180
181    fn bitand(self, rhs: Self) -> Self::Output {
182        Self(self.0 & rhs.0)
183    }
184}
185
186impl BitAnd<u32> for ZoomSet {
187    type Output = Self;
188
189    fn bitand(self, rhs: u32) -> Self::Output {
190        Self(self.0 & rhs)
191    }
192}
193
194impl BitAnd<ZoomSet> for u32 {
195    type Output = ZoomSet;
196
197    fn bitand(self, rhs: ZoomSet) -> Self::Output {
198        ZoomSet(self & rhs.0)
199    }
200}
201
202impl BitAnd<u8> for ZoomSet {
203    type Output = Self;
204
205    fn bitand(self, rhs: u8) -> Self::Output {
206        Self(self.0 & (1 << (31 - rhs)))
207    }
208}
209
210type ZoomsSetInt = u32;
211
212/// Struct representing zoom level range (min, max)
213#[derive(Debug, Clone, Eq, PartialEq)]
214pub struct ZoomRange {
215    /// Minimum zoom level
216    pub min: u8,
217    /// Maximum zoom level
218    pub max: u8,
219}
220
221// default zoom range
222impl Default for ZoomRange {
223    fn default() -> Self {
224        Self {
225            min: 0,
226            max: MAX_ZOOM,
227        }
228    }
229}
230
231impl ZoomRange {
232    /// Create a new `ZoomRange`
233    #[must_use]
234    pub const fn new(min: u8, max: u8) -> Self {
235        Self { min, max }
236    }
237
238    /// Create a new `ZoomRange` from a maximum zoom level (0 to max)
239    #[must_use]
240    pub const fn from_max(max: u8) -> Self {
241        Self { min: 0, max }
242    }
243
244    /// Create a new `ZoomRange` from a minimum zoom level (min to 30)
245    #[must_use]
246    pub const fn from_min(min: u8) -> Self {
247        Self { min, max: 30 }
248    }
249}
250
251impl IntoIterator for ZoomRange {
252    type Item = u8;
253    type IntoIter = std::ops::RangeInclusive<u8>;
254
255    fn into_iter(self) -> Self::IntoIter {
256        self.min..=self.max
257    }
258}
259
260/// Convert range of zoom levels to a set of zoom levels
261///
262/// # Examples
263/// ```
264/// use utiles_core::zoom::{ZoomRange, ZoomSet};
265/// let zrange = ZoomRange::new(0, 7);
266/// let zset: ZoomSet = zrange.into();
267/// assert_eq!(zset, ZoomSet::new(0b0000_0000_0000_0000_0000_0000_1111_1111));
268/// ```
269impl From<ZoomRange> for ZoomSet {
270    fn from(zoom_range: ZoomRange) -> Self {
271        Self(zoom_range.into_iter().fold(0, |acc, z| acc | (1 << z)))
272    }
273}
274
275/// Parse zoom levels from a string
276///
277/// # Examples
278/// ```
279/// use utiles_core::zoom::parse_zooms;
280/// let zstr = "0,1,2,3,4,5,6,7";
281/// let zvec = parse_zooms(zstr).unwrap();
282/// assert_eq!(zvec, vec![0, 1, 2, 3, 4, 5, 6, 7]);
283/// ```
284///
285/// # Errors
286///
287/// Returns an error if the zoom levels are not between 0 and 30
288pub fn parse_zooms(zstr: &str) -> UtilesCoreResult<Vec<u8>> {
289    let mut zvec: Vec<u8> = vec![];
290    for z in zstr.split(',') {
291        if z.contains('-') {
292            let zrange: Result<Vec<u8>, ParseIntError> =
293                z.split('-').map(str::parse).collect::<Result<Vec<_>, _>>();
294
295            let zrange = match zrange {
296                Ok(zrange) => match zrange.len() {
297                    1 => vec![zrange[0]],
298                    2 => (zrange[0]..=zrange[1]).collect(),
299                    _ => vec![],
300                },
301                Err(_) => return Err(InvalidZoom(z.to_string())),
302            };
303            zvec.extend(zrange);
304        } else {
305            match z.parse::<u8>() {
306                Ok(num) => zvec.push(num),
307                Err(_) => return Err(InvalidZoom(z.to_string())),
308            }
309        }
310    }
311    // make sure zooms are between 0 and 32
312    for z in &zvec {
313        if *z > 32 {
314            return Err(InvalidZoom((*z).to_string()));
315        }
316    }
317    // unique and sort zooms
318    zvec.sort_unstable();
319    zvec.dedup();
320    Ok(zvec)
321}
322
323/// Enum representing a single zoom level or a vec of zoom levels
324pub enum ZoomOrZooms {
325    /// A single zoom level
326    Zoom(u8),
327
328    /// A vec of zoom levels
329    Zooms(Vec<u8>),
330}
331
332impl From<u8> for ZoomOrZooms {
333    fn from(zoom: u8) -> Self {
334        Self::Zoom(zoom)
335    }
336}
337
338impl From<Vec<u8>> for ZoomOrZooms {
339    fn from(zooms: Vec<u8>) -> Self {
340        Self::Zooms(zooms)
341    }
342}
343
344impl From<ZoomOrZooms> for ZoomsSetInt {
345    fn from(zoom_or_zooms: ZoomOrZooms) -> Self {
346        match zoom_or_zooms {
347            ZoomOrZooms::Zoom(zoom) => 1 << (31 - zoom),
348            ZoomOrZooms::Zooms(zooms) => zvec2zset_rev(&zooms),
349        }
350    }
351}
352
353#[cfg(test)]
354mod tests {
355    use super::*;
356
357    #[test]
358    fn zset2zvec_none() {
359        let zset: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0000;
360        let zvec: Vec<u8> = vec![];
361        assert_eq!(zset2zvec(zset), zvec);
362        assert_eq!(zset2zvec_rev(zset), zvec);
363        assert_eq!(zset, 0);
364    }
365
366    #[test]
367    fn zset2zvec_0_1_2() {
368        let zset_fwd: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0111;
369        let zset_rev: u32 = 0b1111_0000_0000_0000_0000_0000_0000_0000;
370        let zvec: Vec<u8> = vec![0, 1, 2];
371        assert_eq!(zset2zvec(zset_fwd), zvec);
372        assert_eq!(zset_fwd, 7);
373        assert_eq!(zset2zvec_rev(zset_rev), zvec);
374    }
375
376    #[test]
377    fn zset2zvec_all() {
378        let zset_int_fwd: u32 = 0b0111_1111_1111_1111_1111_1111_1111_1111;
379        let zset_int_rev: u32 = 0b1111_1111_1111_1111_1111_1111_1111_1111;
380        let zvec: Vec<u8> = vec![
381            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
382            21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
383        ];
384        assert_eq!(zset2zvec(zset_int_fwd), zvec);
385        assert_eq!(zset2zvec_rev(zset_int_rev), zvec);
386    }
387
388    #[test]
389    fn zvec2zset_none() {
390        let zset_int: u32 = 0b0000_0000_0000_0000_0000_0000_0000_0000;
391        assert!(zset2zvec(zset_int).is_empty());
392        assert!(zset2zvec_rev(zset_int).is_empty());
393    }
394
395    #[test]
396    fn zvec2zset_0_1_2() {
397        let zset_int: u32 = 0b1110_0000_0000_0000_0000_0000_0000_0000;
398        let zvec: Vec<u8> = vec![0, 1, 2];
399
400        assert_eq!(zvec2zset_rev(&zvec), zset_int);
401    }
402
403    #[test]
404    fn zvec2zset_0_1_2_3_4_5_6_7() {
405        let zset_int: u32 = 0b1111_1111_0000_0000_0000_0000_0000_0000;
406        let zvec: Vec<u8> = vec![0, 1, 2, 3, 4, 5, 6, 7];
407        let zset_from_zvec = zvec2zset_rev(&zvec);
408        assert_eq!(zset_from_zvec, zset_int);
409    }
410
411    #[test]
412    fn zoom_set_into_zoom_vec() {
413        let zset_int_fwd: u32 = 0b0000_0000_0000_0000_0000_0000_1111_1111;
414        let zet_fwd_vec: Vec<u8> = ZoomSet::from(zset_int_fwd).into();
415        assert_eq!(zet_fwd_vec, vec![0, 1, 2, 3, 4, 5, 6, 7]);
416        let zset_int_rev: u32 = 0b1111_1111_1000_0000_0000_0000_0000_0000;
417        let zset: ZoomSet = zset_int_rev.into();
418        let zvec = Vec::from(zset);
419        assert_eq!(zvec, vec![0, 1, 2, 3, 4, 5, 6, 7]);
420    }
421}