gistools/geometry/
id.rs

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