gistools/geometry/s2/
cellid.rs

1extern crate alloc;
2
3use libm::{fmax, fmin};
4
5use alloc::{string::String, vec::Vec};
6
7use crate::{
8    face_si_ti_to_xyz, face_uv_to_xyz, ij_to_st, si_ti_to_st, st_to_ij, xyz_to_face_uv, BBox,
9    LonLat, S2Point, K_INVERT_MASK, K_MAX_CELL_LEVEL, K_SWAP_MASK, LOOKUP_POS, ST_TO_UV, UV_TO_ST,
10};
11
12use super::LOOKUP_IJ;
13
14/// Cell ID works with both S2 and WM with a common interface
15pub type CellId = S2CellId;
16
17// The following lookup tables are used to convert efficiently between an
18// (i,j) cell index and the corresponding position along the Hilbert curve.
19// "lookupPos" maps 4 bits of "i", 4 bits of "j", and 2 bits representing the
20// orientation of the current cell into 8 bits representing the order in which
21// that subcell is visited by the Hilbert curve, plus 2 bits indicating the
22// new orientation of the Hilbert curve within that subcell.  (Cell
23// orientations are represented as combination of K_SWAP_MASK and kInvertMask.)
24//
25// "lookupIJ" is an inverted table used for mapping in the opposite
26// direction.
27//
28// We also experimented with looking up 16 bits at a time (14 bits of position
29// plus 2 of orientation) but found that smaller lookup tables gave better
30// performance. (2KB fits easily in the primary cache.)
31
32// Values for these constants are *declared* in the *.h file. Even though
33// the declaration specifies a value for the constant, that declaration
34// is not a *definition* of storage for the value. Because the values are
35// supplied in the declaration, we don't need the values here. Failing to
36// define storage causes link errors for any code that tries to take the
37// address of one of these values.
38
39// Although only 60 bits are needed to represent the index of a leaf cell, the
40// extra position bit lets us encode each cell as its Hilbert curve position
41// at the cell center, which is halfway along the portion of the Hilbert curve
42// that fills that cell.
43
44/// The number of bits used to encode the face of the cell.
45pub const K_FACE_BITS: u8 = 3;
46/// The number of faces in the S2 cell projection
47pub const K_NUM_FACES: u8 = 6;
48/// The maximum level in the S2 cell decomposition
49pub const K_MAX_LEVEL: u64 = K_MAX_CELL_LEVEL as u64; // Valid levels: 0..K_MAX_LEVEL
50/// The number of bits used to encode the position along the Hilbert curve
51pub const K_POS_BITS: u64 = 2 * K_MAX_LEVEL + 1;
52/// The maximum number of cells in the S2 cell decomposition
53pub const K_MAX_SIZE: u32 = 1 << K_MAX_LEVEL;
54/// The number of bits used to encode the orientation of the Hilbert curve
55pub const K_LOOKUP_BITS: u8 = 4;
56
57/// This is the offset required to wrap around from the beginning of the
58/// Hilbert curve to the end or vice versa; see nextWrap() and prevWrap().
59/// SWIG doesn't understand uint64{}, so use static_cast.
60pub const K_WRAP_OFFSET: u64 = (K_NUM_FACES as u64) << K_POS_BITS;
61
62/// An S2CellId is a 64-bit unsigned integer that uniquely identifies a
63/// cell in the S2 cell decomposition.  It has the following format:
64///
65///   id = [face][face_pos]
66///
67///   face:     a 3-bit number (range 0..5) encoding the cube face.
68///
69///   face_pos: a 61-bit number encoding the position of the center of this
70///             cell along the Hilbert curve over this face (see the Wiki
71///             pages for details).
72///
73/// Sequentially increasing cell ids follow a continuous space-filling curve
74/// over the entire sphere.  They have the following properties:
75///
76///  - The id of a cell at level k consists of a 3-bit face number followed
77///    by k bit pairs that recursively select one of the four children of
78///    each cell.  The next bit is always 1, and all other bits are 0.
79///    Therefore, the level of a cell is determined by the position of its
80///    lowest-numbered bit that is turned on (for a cell at level k, this
81///    position is 2 * (K_MAX_LEVEL - k).)
82///
83///  - The id of a parent cell is at the midpoint of the range of ids spanned
84///    by its children (or by its descendants at any level).
85///
86/// Leaf cells are often used to represent points on the unit sphere, and
87/// this class provides methods for converting directly between these two
88/// representations.  For cells that represent 2D regions rather than
89/// discrete point, it is better to use the S2Cell class.
90///
91/// This class is intended to be copied by value as desired.  It uses
92/// the default copy constructor and assignment operator.
93#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
94#[repr(C)]
95pub struct S2CellId {
96    /// the id contains the face, s, and t components
97    pub id: u64,
98}
99impl S2CellId {
100    /// Construct a cell id from the given 64-bit integer.
101    pub fn new(id: u64) -> Self {
102        S2CellId { id }
103    }
104
105    /// Returns an empty cell id.
106    pub fn none() -> Self {
107        0_u64.into()
108    }
109
110    /// Returns an invalid cell id guaranteed to be larger than any
111    /// valid cell id.  Useful for creating indexes.
112    pub fn sentinel() -> Self {
113        (!0_u64).into()
114    }
115
116    /// Return the cell corresponding to a given S2 cube face.
117    pub fn from_face(face: u8) -> S2CellId {
118        (((face as u64) << K_POS_BITS) + lsb_for_level(0)).into()
119    }
120
121    /// Construct a leaf cell containing the given normalized S2LatLng.
122    pub fn from_lon_lat(ll: &LonLat) -> S2CellId {
123        S2CellId::from_s2_point(&ll.to_point())
124    }
125
126    /// Construct a leaf cell containing the given point "p".  Usually there is
127    /// exactly one such cell, but for points along the edge of a cell, any
128    /// adjacent cell may be (deterministically) chosen.  This is because
129    /// S2CellIds are considered to be closed sets.  The returned cell will
130    /// always contain the given point, i.e.
131    ///
132    /// S2Cell(S2CellId(p)).contains(p)
133    ///
134    /// is always true.  The point "p" does not need to be normalized.
135    ///
136    /// If instead you want every point to be contained by exactly one S2Cell,
137    /// you will need to convert the S2CellIds to S2Loops (which implement point
138    /// containment this way).
139    pub fn from_s2_point(p: &S2Point) -> Self {
140        let (face, u, v) = xyz_to_face_uv(p);
141        let i: u32 = st_to_ij(UV_TO_ST(u));
142        let j: u32 = st_to_ij(UV_TO_ST(v));
143        Self::from_face_ij(face, i, j, None)
144    }
145
146    /// Construct a leaf cell given its face and (u,v) coordinates.
147    pub fn from_face_uv(face: u8, u: f64, v: f64) -> Self {
148        S2CellId::from_face_st(face, UV_TO_ST(u), UV_TO_ST(v))
149    }
150
151    /// Construct a leaf cell given its face and (s,t) coordinates.
152    pub fn from_face_st(face: u8, s: f64, t: f64) -> Self {
153        let i: u32 = st_to_ij(s);
154        let j: u32 = st_to_ij(t);
155        Self::from_face_ij(face, i, j, None)
156    }
157
158    /// Return a leaf cell given its cube face (range 0..5) and
159    /// i- and j-coordinates (see s2coords.h).
160    pub fn from_face_ij(face: u8, i: u32, j: u32, level: Option<u8>) -> S2CellId {
161        let mut i = i as u64;
162        let mut j = j as u64;
163        if let Some(level) = level {
164            i <<= K_MAX_LEVEL - level as u64;
165            j <<= K_MAX_LEVEL - level as u64;
166        }
167        // Optimization notes:
168        //  - Non-overlapping bit fields can be combined with either "+" or "|".
169        //    Generally "+" seems to produce better code, but not always.
170
171        // Note that this value gets shifted one bit to the left at the end
172        // of the function.
173        let mut n: u64 = (face as u64) << (K_POS_BITS - 1);
174
175        // Alternating faces have opposite Hilbert curve orientations; this
176        // is necessary in order for all faces to have a right-handed
177        // coordinate system.
178        let mut bits: u64 = (face & K_SWAP_MASK) as u64;
179
180        // Each iteration maps 4 bits of "i" and "j" into 8 bits of the Hilbert
181        // curve position.  The lookup table transforms a 10-bit key of the form
182        // "iiiijjjjoo" to a 10-bit value of the form "ppppppppoo", where the
183        // letters [ijpo] denote bits of "i", "j", Hilbert curve position, and
184        // Hilbert curve orientation respectively.
185        let mut k: u8 = 7;
186        loop {
187            let mask: u64 = (1 << 4) - 1;
188            bits += ((i >> (k * 4)) & mask) << (4 + 2);
189            bits += ((j >> (k * 4)) & mask) << 2;
190            bits = LOOKUP_POS[bits as usize] as u64;
191            n |= (bits >> 2) << (k * 2 * 4);
192            bits &= (K_SWAP_MASK | K_INVERT_MASK) as u64;
193            if k == 0 {
194                break;
195            }
196            k -= 1;
197        }
198
199        let id: S2CellId = ((n << 1) + 1).into();
200
201        if let Some(level) = level {
202            id.parent(Some(level))
203        } else {
204            id
205        }
206    }
207
208    /// Given a distance and optional zoom level, construct a cell ID at that distance
209    pub fn from_distance(distance: u64, level: Option<u8>) -> S2CellId {
210        let mut level: u64 = level.unwrap_or(K_MAX_LEVEL as u8) as u64;
211        level = 2 * (K_MAX_LEVEL - level);
212        ((distance << (level + 1)) + (1 << level)).into()
213    }
214
215    /// convert an id to a zoom-i-j after setting it to a new parent zoom
216    pub fn to_zoom_ij(&self, level: Option<u8>) -> (u8, u32, u32) {
217        let (face, i, j, _or) = self.to_face_ij_orientation(level);
218        (face, i, j)
219    }
220
221    /// Return the (face, i, j) coordinates for the leaf cell corresponding to
222    /// this cell id.  Since cells are represented by the Hilbert curve position
223    /// at the center of the cell, the returned (i,j) for non-leaf cells will be
224    /// a leaf cell adjacent to the cell center.  If "orientation" is non-nullptr,
225    /// also return the Hilbert curve orientation for the current cell.
226    /// Returns (face, i, j, orientation)
227    pub fn to_face_ij_orientation(&self, level: Option<u8>) -> (u8, u32, u32, u8) {
228        let mut i: u32 = 0;
229        let mut j: u32 = 0;
230        let face: u8 = (self.id >> K_POS_BITS) as u8;
231        let mut bits: u64 = (face & 1) as u64;
232
233        // Each iteration maps 8 bits of the Hilbert curve position into
234        // 4 bits of "i" and "j".  The lookup table transforms a key of the
235        // form "ppppppppoo" to a value of the form "iiiijjjjoo", where the
236        // letters [ijpo] represents bits of "i", "j", the Hilbert curve
237        // position, and the Hilbert curve orientation respectively.
238        //
239        // On the first iteration we need to be careful to clear out the bits
240        // representing the cube face.
241        let mut k = 7;
242        while k >= 0 {
243            let nbits: u64 = if k == 7 { 2 } else { 4 };
244            let kk: u64 = k as u64;
245            bits += ((self.id >> (kk * 8 + 1)) & ((1 << (2 * nbits)) - 1)) << 2;
246            bits = LOOKUP_IJ[bits as usize] as u64;
247            i += (bits as u32 >> K_NUM_FACES as u32) << (kk * 4);
248            j += (((bits >> 2) & 15) << (kk * 4)) as u32;
249            bits &= K_FACE_BITS as u64;
250            k -= 1;
251        }
252
253        // adjust bits to the orientation
254        let lsb = self.id & (!self.id + 1);
255        if (lsb & 1229782938247303424) != 0 {
256            bits ^= 1;
257        }
258
259        if let Some(level) = level {
260            i >>= K_MAX_LEVEL as u32 - level as u32;
261            j >>= K_MAX_LEVEL as u32 - level as u32;
262        }
263
264        (face, i, j, bits as u8)
265    }
266
267    /// Return the (face, si, ti) coordinates of the center of the cell.  Note
268    /// that although (si,ti) coordinates span the range [0,2**31] in general,
269    /// the cell center coordinates are always in the range [1,2**31-1] and
270    /// therefore can be represented using a signed 32-bit integer.
271    /// Returns (face, si, ti)
272    pub fn get_center_si_ti(&self) -> (u8, u32, u32) {
273        // First we compute the discrete (i,j) coordinates of a leaf cell contained
274        // within the given cell.  Given that cells are represented by the Hilbert
275        // curve position corresponding at their center, it turns out that the cell
276        // returned by to_face_ij_orientation is always one of two leaf cells closest
277        // to the center of the cell (unless the given cell is a leaf cell itself,
278        // in which case there is only one possibility).
279        //
280        // Given a cell of size s >= 2 (i.e. not a leaf cell), and letting (imin,
281        // jmin) be the coordinates of its lower left-hand corner, the leaf cell
282        // returned by to_face_ij_orientation() is either (imin + s/2, jmin + s/2)
283        // (imin + s/2 - 1, jmin + s/2 - 1).  The first case is the one we want.
284        // We can distinguish these two cases by looking at the low bit of "i" or
285        // "j".  In the second case the low bit is one, unless s == 2 (i.e. the
286        // level just above leaf cells) in which case the low bit is zero.
287        //
288        // In the code below, the expression ((i ^ (int(id_) >> 2)) & 1) is true
289        // if we are in the second case described above.
290        let (face, i, j, _or) = self.to_face_ij_orientation(None);
291        let mut delta: u32 = 0;
292        if self.is_leaf() {
293            delta = 1;
294        } else if ((i ^ (self.id >> 2) as u32) & 1) != 0 {
295            delta = 2;
296        }
297
298        // Note that (2 * {i,j} + delta) will never overflow a 32-bit integer.
299        let psi = 2 * i + delta;
300        let pti = 2 * j + delta;
301
302        (face, psi, pti)
303    }
304
305    /// Creates a human readable debug string.  Used for << and available for
306    /// direct usage as well.  The format is "f/dd..d" where "f" is a digit in
307    /// the range [0-5] representing the S2CellId face, and "dd..d" is a string
308    /// of digits in the range [0-3] representing each child's position with
309    /// respect to its parent.  (Note that the latter string may be empty.)
310    ///
311    /// For example "4/" represents S2CellId::from_face(4), and "3/02" represents
312    /// S2CellId::from_face(3).child(0).child(2).
313    pub fn display_name(&self) -> String {
314        let mut out = String::new();
315        if !self.is_valid() {
316            out.push_str("Invalid");
317            return out;
318        }
319        out.push((self.face() + 48) as char);
320        out += "/";
321        let mut cur_level: u8 = 1;
322        while cur_level <= self.level() {
323            out.push((self.child_position(cur_level) + 48) as char);
324            cur_level += 1;
325        }
326
327        out
328    }
329
330    /// Return the child position (0..3) of this cell's ancestor at the given
331    /// level within its parent.  For example, childPosition(1) returns the
332    /// position of this cell's level-1 ancestor within its top-level face cell.
333    /// REQUIRES: 1 <= level <= this->level().
334    pub fn child_position(&self, level: u8) -> u8 {
335        if !self.is_valid() || level > self.level() {
336            unreachable!();
337        }
338        // return @as(u3, @intCast(self.id >> (2 * (K_MAX_LEVEL - @as(u6, level)) + 1) & 3));
339        ((self.id >> (2 * (K_MAX_LEVEL - level as u64) + 1)) & 3) as u8
340    }
341
342    /// Converts a string in the format returned by ToString() to an S2CellId.
343    /// Returns S2CellId.None() if the string could not be parsed.
344    ///
345    /// The method name includes "Debug" in order to avoid possible confusion
346    /// with FromToken() above.
347    pub fn from_string(val: &str) -> S2CellId {
348        // This function is reasonably efficient, but is only intended for use in tests.
349        let val_bytes = val.as_bytes();
350        let level = (val.len() - 2) as u64;
351        if !(0..=K_MAX_LEVEL).contains(&level) {
352            return S2CellId::none();
353        }
354        let face: u8 = val_bytes[0] - b'0';
355        if !(0..=5).contains(&face) || val_bytes[1] != b'/' {
356            return S2CellId::none();
357        }
358        let mut id = S2CellId::from_face(face);
359        let mut i = 2;
360        while i < val.len() {
361            let child_pos = val_bytes[i] - b'0';
362            if !(0..=3).contains(&child_pos) {
363                return S2CellId::none();
364            }
365            id = id.child(child_pos);
366            i += 1;
367        }
368
369        id
370    }
371
372    /// Return the lowest-numbered bit that is on for this cell id, which is
373    /// equal to (uint64{1} << (2 * (K_MAX_LEVEL - level))).  So for example,
374    /// a.lsb() <= b.lsb() if and only if a.level() >= b.level(), but the
375    /// first test is more efficient.
376    pub fn lsb(&self) -> u64 {
377        if self.id == 0 {
378            return 0;
379        }
380        self.id & (!self.id + 1_u64)
381    }
382
383    /// Return the immediate child of this cell at the given traversal order
384    /// position (in the range 0 to 3). This cell must not be a leaf cell.
385    pub fn child(&self, position: u8) -> S2CellId {
386        if !self.is_valid() || self.is_leaf() || position > 3 {
387            unreachable!();
388        }
389        // To change the level, we need to move the least-significant bit two
390        // positions downward.  We do this by subtracting (4 * new_lsb) and adding
391        // new_lsb.  Then to advance to the given child cell, we add
392        // (2 * position * new_lsb).
393        let new_lsb = self.lsb() >> 2;
394        (self.id - (3 * new_lsb) + (2 * (position as u64) * new_lsb)).into()
395    }
396
397    /// Which cube face this cell belongs to, in the range 0..5.
398    pub fn face(&self) -> u8 {
399        (self.id >> K_POS_BITS) as u8
400    }
401
402    /// The position of the cell center along the Hilbert curve over this face,
403    /// in the range 0..(2**K_POS_BITS-1).
404    pub fn pos(&self) -> u64 {
405        self.id & (0u64 >> K_FACE_BITS)
406    }
407
408    /// Return the subdivision level of the cell (range 0..K_MAX_LEVEL).
409    pub fn level(&self) -> u8 {
410        // We can't just S2_DCHECK(isValid()) because we want level() to be
411        // defined for end-iterators, i.e. S2CellId::End(kLevel).  However there is
412        // no good way to define S2CellId::None().level(), so we do prohibit that.
413        if self.id == 0 {
414            unreachable!();
415        }
416
417        K_MAX_LEVEL as u8 - (self.id.trailing_zeros() as u8 >> 1u8)
418    }
419
420    /// Return true if id represents a valid cell.
421    ///
422    /// All methods require isValid() to be true unless otherwise specified
423    /// (although not all methods enforce this).
424    pub fn is_valid(&self) -> bool {
425        self.face() < K_NUM_FACES && (self.lsb() & 0x1555555555555555) != 0_u64
426    }
427
428    /// Return true if this is a leaf cell (more efficient than checking
429    /// whether level() == K_MAX_LEVEL).
430    pub fn is_leaf(&self) -> bool {
431        (self.id & 1u64) != 0
432    }
433
434    /// Convert an S2CellID to an S2Point
435    pub fn to_point_raw(&self) -> S2Point {
436        let (face, si, ti) = self.get_center_si_ti();
437        face_si_ti_to_xyz(face, si, ti)
438    }
439
440    /// Convert an S2CellID to an S2Point in normalized vector coordinates
441    pub fn to_point(&self) -> S2Point {
442        let mut p = self.to_point_raw();
443        p.normalize();
444        p
445    }
446
447    /// Convert an S2CellID to an Face-S-T coordinate
448    /// Returns (face, s, t)
449    pub fn to_st(&self) -> (u8, f64, f64) {
450        let (face, i, j, _or) = self.to_face_ij_orientation(None);
451        let s = ij_to_st(i);
452        let t = ij_to_st(j);
453
454        (face, s, t)
455    }
456
457    /// Convert an S2CellID to an Face-U-V coordinate
458    /// Returns (face, u, v)
459    pub fn to_uv(&self) -> (u8, f64, f64) {
460        let (face, s, t) = self.to_st();
461        let u = ST_TO_UV(s);
462        let v = ST_TO_UV(t);
463        (face, u, v)
464    }
465
466    /// Given an S2CellID, check if it is a Face Cell.
467    pub fn is_face(&self) -> bool {
468        (self.id & ((1 << 60) - 1)) == 0
469    }
470
471    /// Given an S2CellID, get the distance it spans (or length it covers)
472    pub fn distance(&self, lev: Option<u8>) -> f64 {
473        let level = lev.unwrap_or(self.level());
474        (self.id >> (2 * (30 - level) + 1)) as f64
475    }
476
477    /// Given an S2CellID, get all the quad children tiles
478    pub fn children(&self, orientation: Option<u8>) -> [S2CellId; 4] {
479        let mut childs = [self.child(0), self.child(3), self.child(2), self.child(1)];
480
481        if let Some(orientation) = orientation {
482            if orientation == 0 {
483                childs.swap(1, 3);
484            }
485        }
486
487        childs
488    }
489
490    /// Given an S2CellID, get the quad children tiles using a face-zoom-ij input
491    pub fn children_ij(&self, face: u8, level: u8, i: u32, j: u32) -> [Self; 4] {
492        let i = i << 1;
493        let j = j << 1;
494        let level = level + 1;
495
496        [
497            S2CellId::from_face_ij(face, i, j, Some(level)),
498            S2CellId::from_face_ij(face, i + 1, j, Some(level)),
499            S2CellId::from_face_ij(face, i, j + 1, Some(level)),
500            S2CellId::from_face_ij(face, i + 1, j + 1, Some(level)),
501        ]
502    }
503
504    /// Given an S2CellID, get the parent quad tile
505    pub fn parent(&self, level: Option<u8>) -> S2CellId {
506        let new_lsb = match level {
507            Some(level) => 1 << (2 * (K_MAX_LEVEL - level as u64)),
508            None => (self.id & (!self.id + 1)) << 2,
509        };
510
511        ((self.id & (!new_lsb + 1)) | new_lsb).into()
512    }
513
514    /// Given an S2CellID, get the hilbert range it spans
515    /// returns (min, max)
516    pub fn range(&self) -> (Self, Self) {
517        let id = self.id;
518        let lsb = id & (!id + 1);
519
520        ((id - (lsb - 1)).into(), (id + (lsb - 1)).into())
521    }
522
523    /// Check if the first S2CellID contains the second.
524    pub fn contains(&self, other: &S2CellId) -> bool {
525        let (min, max) = self.range();
526        *other >= min && *other <= max
527    }
528
529    /// Check if an S2CellID intersects another. This includes edges touching.
530    pub fn intersects(&self, other: &S2CellId) -> bool {
531        let (min_self, max_self) = self.range();
532        let (min_other, max_other) = other.range();
533        min_other <= max_self && max_other >= min_self
534    }
535
536    /// Get the next S2CellID in the hilbert space
537    pub fn next(&self) -> S2CellId {
538        let id = self.id;
539        let n = id + ((id & (!id + 1)) << 1);
540        if n < K_WRAP_OFFSET {
541            n.into()
542        } else {
543            (n - K_WRAP_OFFSET).into()
544        }
545    }
546
547    /// Get the previous S2CellID in the hilbert space
548    pub fn prev(&self) -> S2CellId {
549        let id = self.id;
550        let p = id - ((id & (!id + 1)) << 1);
551        if p < K_WRAP_OFFSET {
552            p.into()
553        } else {
554            (p + K_WRAP_OFFSET).into()
555        }
556    }
557
558    /// Given an S2CellID and level (zoom), get the center point of that cell in S-T space
559    /// returns (face, s, t)
560    pub fn center_st(&self) -> (u8, f64, f64) {
561        let id = self.id;
562        let (face, i, j, _or) = self.to_face_ij_orientation(None);
563        let delta = if (id & 1) != 0 {
564            1
565        } else if ((i as u64 ^ (id >> 2)) & 1) != 0 {
566            2
567        } else {
568            0
569        };
570        // Note that (2 * {i,j} + delta) will never overflow a 32-bit integer.
571        let si = 2 * i + delta;
572        let ti = 2 * j + delta;
573
574        (face, si_ti_to_st(si), si_ti_to_st(ti))
575    }
576
577    /// Given an S2CellID and level (zoom), get the S-T bounding range of that cell
578    /// returns (s_min, t_min, s_max, t_max)
579    pub fn bounds_st(&self, level: Option<u8>) -> BBox {
580        let level = level.unwrap_or(self.level());
581
582        let (_face, s, t) = self.center_st();
583        let half_size = size_st(level) * 0.5;
584        BBox::new(s - half_size, t - half_size, s + half_size, t + half_size)
585    }
586
587    /// Given an S2CellID, find the neighboring S2CellIDs
588    /// returns neighbors: [up, right, down, left]
589    pub fn neighbors(&self) -> [S2CellId; 4] {
590        let level = self.level();
591        let size = size_ij(level);
592        let (face, i, j, _or) = self.to_face_ij_orientation(None);
593
594        [
595            S2CellId::from_ij_same(face, i, j - size, j as i32 - size as i32 >= 0)
596                .parent(Some(level)),
597            S2CellId::from_ij_same(face, i + size, j, i + size < K_MAX_SIZE).parent(Some(level)),
598            S2CellId::from_ij_same(face, i, j + size, j + size < K_MAX_SIZE).parent(Some(level)),
599            S2CellId::from_ij_same(face, i - size, j, i as i32 - size as i32 >= 0)
600                .parent(Some(level)),
601        ]
602    }
603
604    /// Given a Face-I-J and a desired level (zoom), find the neighboring S2CellIDs
605    /// return neighbors: [down, right, up, left]
606    pub fn neighbors_ij(&self, face: u8, i: u32, j: u32, level: u8) -> [S2CellId; 4] {
607        let size = size_ij(level);
608
609        [
610            S2CellId::from_ij_same(face, i, j - size, j as i32 - size as i32 >= 0)
611                .parent(Some(level)),
612            S2CellId::from_ij_same(face, i + size, j, i + size < K_MAX_SIZE).parent(Some(level)),
613            S2CellId::from_ij_same(face, i, j + size, j + size < K_MAX_SIZE).parent(Some(level)),
614            S2CellId::from_ij_same(face, i - size, j, i as i32 - size as i32 >= 0)
615                .parent(Some(level)),
616        ]
617    }
618
619    /// Build an S2CellID given a Face-I-J, but ensure the face is the same if desired
620    pub fn from_ij_same(face: u8, i: u32, j: u32, same_face: bool) -> S2CellId {
621        if same_face {
622            S2CellId::from_face_ij(face, i, j, None)
623        } else {
624            S2CellId::from_face_ij_wrap(face, i, j)
625        }
626    }
627
628    /// Build an S2CellID given a Face-I-J, but ensure it's a legal value, otherwise wrap before creation
629    pub fn from_face_ij_wrap(face: u8, i: u32, j: u32) -> S2CellId {
630        // Convert i and j to the coordinates of a leaf cell just beyond the
631        // boundary of this face.  This prevents 32-bit overflow in the case
632        // of finding the neighbors of a face cell.
633        let i = i.clamp(0, K_MAX_SIZE);
634        let j = j.clamp(0, K_MAX_SIZE);
635
636        // We want to wrap these coordinates onto the appropriate adjacent face.
637        // The easiest way to do this is to convert the (i,j) coordinates to (x,y,z)
638        // (which yields a point outside the normal face boundary), and then call
639        // S2::XYZtoFaceUV() to project back onto the correct face.
640        //
641        // The code below converts (i,j) to (si,ti), and then (si,ti) to (u,v) using
642        // the linear projection (u=2*s-1 and v=2*t-1).  (The code further below
643        // converts back using the inverse projection, s=0.5*(u+1) and t=0.5*(v+1).
644        // Any projection would work here, so we use the simplest.)  We also clamp
645        // the (u,v) coordinates so that the point is barely outside the
646        // [-1,1]x[-1,1] face rectangle, since otherwise the reprojection step
647        // (which divides by the new z coordinate) might change the other
648        // coordinates enough so that we end up in the wrong leaf cell.
649        let k_scale = 1. / K_MAX_SIZE as f64;
650        let k_limit = 1. + 2.220_446_049_250_313e-16;
651        let u = fmax(
652            -k_limit,
653            fmin(k_limit, k_scale * (2. * (i as f64 - (K_MAX_SIZE as f64) / 2.) + 1.)),
654        );
655        let v = fmax(
656            -k_limit,
657            fmin(k_limit, k_scale * (2. * (j as f64 - (K_MAX_SIZE as f64) / 2.) + 1.)),
658        );
659
660        // Find the leaf cell coordinates on the adjacent face, and convert
661        // them to a cell id at the appropriate level.
662        let (n_face, nu, nv) = xyz_to_face_uv(&face_uv_to_xyz(face, u, v));
663        S2CellId::from_face_ij(n_face, st_to_ij(0.5 * (nu + 1.)), st_to_ij(0.5 * (nv + 1.)), None)
664    }
665
666    /// Given an S2CellID, find it's nearest neighbors associated with it
667    pub fn vertex_neighbors(&self, level: Option<u8>) -> Vec<S2CellId> {
668        let level = level.unwrap_or(self.level());
669        let mut neighbors: Vec<S2CellId> = Vec::new();
670
671        let (face, i, j, _or) = self.to_face_ij_orientation(None);
672
673        // Determine the i- and j-offsets to the closest neighboring cell in each
674        // direction.  This involves looking at the next bit of "i" and "j" to
675        // determine which quadrant of this->parent(level) this cell lies in.
676        let halfsize = size_ij(level + 1);
677        let size = halfsize << 1;
678        let isame;
679        let jsame;
680        let ioffset;
681        let joffset;
682
683        if (i & halfsize) != 0 {
684            ioffset = size;
685            isame = i + size < K_MAX_SIZE;
686        } else {
687            ioffset = -(size as i32) as u32;
688            isame = i as i32 - size as i32 >= 0;
689        }
690        if (j & halfsize) != 0 {
691            joffset = size;
692            jsame = j + size < K_MAX_SIZE;
693        } else {
694            joffset = -(size as i32) as u32;
695            jsame = j as i32 - size as i32 >= 0;
696        }
697
698        neighbors.push(self.parent(Some(level)));
699        neighbors.push(S2CellId::from_ij_same(face, i + ioffset, j, isame).parent(Some(level)));
700        neighbors.push(S2CellId::from_ij_same(face, i, j + joffset, jsame).parent(Some(level)));
701        if isame || jsame {
702            neighbors.push(
703                S2CellId::from_ij_same(face, i + ioffset, j + joffset, isame && jsame)
704                    .parent(Some(level)),
705            );
706        }
707
708        neighbors
709    }
710
711    /// Return the low 32 bits of the cell id
712    pub fn low_bits(&self) -> u32 {
713        self.id as u32
714    }
715
716    /// Return the high 32 bits of the cell id
717    pub fn high_bits(&self) -> u32 {
718        (self.id >> 32) as u32
719    }
720}
721impl From<u64> for S2CellId {
722    fn from(value: u64) -> Self {
723        S2CellId::new(value)
724    }
725}
726impl From<LonLat> for S2CellId {
727    fn from(value: LonLat) -> Self {
728        S2CellId::from_lon_lat(&value)
729    }
730}
731impl From<S2Point> for S2CellId {
732    fn from(value: S2Point) -> Self {
733        S2CellId::from_s2_point(&value)
734    }
735}
736impl From<String> for S2CellId {
737    fn from(s: String) -> Self {
738        S2CellId::from_string(&s)
739    }
740}
741impl From<&str> for S2CellId {
742    fn from(s: &str) -> Self {
743        S2CellId::from_string(s)
744    }
745}
746
747/// Return the lowest-numbered bit that is on for cells at the given level.
748pub fn lsb_for_level(level: u8) -> u64 {
749    1_u64 << (2 * (K_MAX_LEVEL - (level as u64)))
750}
751
752/// Return the range maximum of a level (zoom) in S-T space
753pub fn size_st(level: u8) -> f64 {
754    ij_to_st(size_ij(level))
755}
756
757/// Return the range maximum of a level (zoom) in I-J space
758pub fn size_ij(level: u8) -> u32 {
759    1 << (30 - level)
760}