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}