utiles_core/
zoom.rs

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