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