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}