1 /** 2 A 64-bit unsigned integer that uniquely identifies a cell in S2 cell decomposition. 3 4 Copyright: 2005 Google Inc. All Rights Reserved. 5 6 License: 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 $(LINK http://www.apache.org/licenses/LICENSE-2.0) 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS-IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 19 Authors: ericv@google.com (Eric Veach), madric@gmail.com (Vijay Nayar) 20 */ 21 module s2.s2cell_id; 22 23 import s2.r1interval; 24 import s2.r2point; 25 import s2.r2rect; 26 import s2.s1angle; 27 import s2.s2latlng; 28 import s2.s2point; 29 import s2.util.coding.coder; 30 import s2coords = s2.s2coords; 31 32 import algorithm = std.algorithm; 33 import array = std.array; 34 import bitop = core.bitop; 35 import conv = std.conv; 36 import format = std.format; 37 import math = std.math; 38 import range = std.range; 39 40 /** 41 * An S2CellId is a 64-bit unsigned integer that uniquely identifies a 42 * cell in the S2 cell decomposition. 43 * 44 * It has the following format: 45 * 46 * id = [face][face_pos] 47 * 48 * face: a 3-bit number (range 0..5) encoding the cube face. 49 * 50 * face_pos: a 61-bit number encoding the position of the center of this 51 * cell along the Hilbert curve over this face (see the Wiki 52 * pages for details). 53 * 54 * Sequentially increasing cell ids follow a continuous space-filling curve 55 * over the entire sphere. They have the following properties: 56 * 57 * - The id of a cell at level k consists of a 3-bit face number followed 58 * by k bit pairs that recursively select one of the four children of 59 * each cell. The next bit is always 1, and all other bits are 0. 60 * Therefore, the level of a cell is determined by the position of its 61 * lowest-numbered bit that is turned on (for a cell at level k, this 62 * position is 2 * (MAX_LEVEL - k).) 63 * 64 * - The id of a parent cell is at the midpoint of the range of ids spanned 65 * by its children (or by its descendants at any level). 66 * 67 * Leaf cells are often used to represent points on the unit sphere, and 68 * this class provides methods for converting directly between these two 69 * representations. For cells that represent 2D regions rather than 70 * discrete point, it is better to use the S2Cell class. 71 * 72 * This class is intended to be copied by value as desired. It uses 73 * the default copy constructor and assignment operator. 74 */ 75 struct S2CellId { 76 private: 77 // The default constructor returns an invalid cell id. 78 ulong _id = 0; 79 80 public: 81 // The extra position bit (61 rather than 60) let us encode each cell as its 82 // Hilbert curve position at the cell center (which is halfway along the 83 // portion of the Hilbert curve that fills that cell). 84 static immutable int FACE_BITS = 3; 85 static immutable int NUM_FACES = 6; 86 static immutable int MAX_LEVEL = s2coords.MAX_CELL_LEVEL; // Valid levels: 0..MAX_LEVEL 87 static immutable int POS_BITS = 2 * MAX_LEVEL + 1; 88 static immutable int MAX_SIZE = 1 << MAX_LEVEL; 89 90 /// Construct a cell from a raw numerical S2 Cell ID. 91 this(ulong id) { 92 _id = id; 93 } 94 95 /** 96 * Construct a leaf cell containing the given point "p". Usually there is 97 * is exactly one such cell, but for points along the edge of a cell, any 98 * adjacent cell may be (deterministically) chosen. This is because 99 * S2CellIds are considered to be closed sets. The returned cell will 100 * always contain the given point, i.e. 101 * 102 * S2Cell(S2CellId(p)).Contains(p) 103 * 104 * is always true. The point "p" does not need to be normalized. 105 * 106 * If instead you want every point to be contained by exactly one S2Cell, 107 * you will need to convert the S2CellIds to S2Loops (which implement point 108 * containment this way). 109 */ 110 this(in S2Point p) { 111 double u, v; 112 int face = s2coords.XYZtoFaceUV(p, u, v); 113 int i = s2coords.STtoIJ(s2coords.UVtoST(u)); 114 int j = s2coords.STtoIJ(s2coords.UVtoST(v)); 115 _id = fromFaceIJ(face, i, j).id(); 116 } 117 118 /// Constructs a cell from a latitude-longitude coordinate. 119 this(in S2LatLng ll) { 120 this(ll.toS2Point()); 121 } 122 123 /// Returns an representative empty cell smaller than any valid cell.. 124 static S2CellId none() { 125 return S2CellId(); 126 } 127 128 /** 129 * Returns an invalid cell id guaranteed to be larger than any 130 * valid cell id. Useful for creating indexes. 131 */ 132 static S2CellId sentinel() { 133 return S2CellId(ulong.max); 134 } 135 136 /// Return the cell corresponding to a given S2 cube face. 137 static S2CellId fromFace(int face) { 138 return S2CellId((cast(ulong) face << POS_BITS) + lsbForLevel(0)); 139 } 140 141 /** 142 * Returns a cell given its face \(range 0..5\), Hilbert curve position within 143 * that face \(an unsigned integer with S2CellId::POS_BITS bits\), and level 144 * \(range 0..MAX_LEVEL\). The given position will be modified to correspond 145 * to the Hilbert curve position at the center of the returned cell. This 146 * is a static function rather than a constructor in order to indicate what 147 * the arguments represent. 148 */ 149 static S2CellId fromFacePosLevel(int face, ulong pos, int level) { 150 auto cell = S2CellId((cast(ulong) face << POS_BITS) + (pos | 1)); 151 return cell.parent(level); 152 } 153 154 /** 155 * Returns the direction vector corresponding to the center of the given 156 * cell. The vector returned by ToPointRaw is not necessarily unit length. 157 * This method returns the same result as S2Cell.getCenter(). 158 * 159 * The maximum directional error in ToPoint() (compared to the exact 160 * mathematical result) is 1.5 * double.epsilon radians, and the maximum length 161 * error is 2 * double.epsilon \(the same as Normalize\). 162 */ 163 S2Point toS2Point() const { 164 return toS2PointRaw().normalize(); 165 } 166 167 S2Point toS2PointRaw() const { 168 int si, ti; 169 int face = getCenterSiTi(si, ti); 170 return s2coords.FaceSiTitoXYZ(face, si, ti); 171 } 172 173 /// Returns the center of the cell in (s,t) coordinates (see s2coords.d). 174 R2Point getCenterST() const { 175 int si, ti; 176 getCenterSiTi(si, ti); 177 return R2Point(s2coords.SiTitoST(si), s2coords.SiTitoST(ti)); 178 } 179 180 /// Returns the edge length of this cell in (s,t)-space. 181 double getSizeST() const { 182 return getSizeST(level()); 183 } 184 185 186 /// Returns the edge length in (s,t)-space of cells at the given level. 187 static double getSizeST(int level) { 188 return s2coords.IJtoSTMin(getSizeIJ(level)); 189 } 190 191 /// Returns the bounds of this cell in (s,t)-space. 192 R2Rect getBoundST() const { 193 double size = getSizeST(); 194 return R2Rect.fromCenterSize(getCenterST(), R2Point(size, size)); 195 } 196 197 /** 198 * Returns the center of the cell in (u,v) coordinates (see s2coords.h). 199 * Note that the center of the cell is defined as the point at which it is 200 * recursively subdivided into four children; in general, it is not at the 201 * midpoint of the (u,v) rectangle covered by the cell. 202 */ 203 R2Point getCenterUV() const { 204 int si, ti; 205 getCenterSiTi(si, ti); 206 return R2Point(s2coords.STtoUV(s2coords.SiTitoST(si)), s2coords.STtoUV(s2coords.SiTitoST(ti))); 207 } 208 209 /// Returns the bounds of this cell in (u,v)-space. 210 R2Rect getBoundUV() const { 211 int[2] ij; 212 toFaceIJOrientation(ij[0], ij[1]); 213 return IJLevelToBoundUV(ij, level()); 214 } 215 216 /** 217 * Expands a rectangle in (u,v)-space so that it contains all points within 218 * the given distance of the boundary, and return the smallest such 219 * rectangle. If the distance is negative, then instead shrink this 220 * rectangle so that it excludes all points within the given absolute 221 * distance of the boundary. 222 * 223 * Distances are measured *on the sphere*, not in (u,v)-space. For example, 224 * you can use this method to expand the (u,v)-bound of an S2CellId so that 225 * it contains all points within 5km of the original cell. You can then 226 * test whether a point lies within the expanded bounds like this: 227 * 228 * R2Point uv; 229 * if (S2::FaceXYZtoUV(face, point, &uv) && bound.Contains(uv)) { ... } 230 * 231 * Limitations: 232 * 233 * - Because the rectangle is drawn on one of the six cube-face planes 234 * (i.e., {x,y,z} = +/-1), it can cover at most one hemisphere. This 235 * limits the maximum amount that a rectangle can be expanded. For 236 * example, S2CellId bounds can be expanded safely by at most 45 degrees 237 * (about 5000 km on the Earth's surface). 238 * 239 * - The implementation is not exact for negative distances. The resulting 240 * rectangle will exclude all points within the given distance of the 241 * boundary but may be slightly smaller than necessary. 242 */ 243 static R2Rect expandedByDistanceUV(in R2Rect uv, in S1Angle distance) { 244 // Expand each of the four sides of the rectangle just enough to include all 245 // points within the given distance of that side. (The rectangle may be 246 // expanded by a different amount in (u,v)-space on each side.) 247 double u0 = uv[0][0]; 248 double u1 = uv[0][1]; 249 double v0 = uv[1][0]; 250 double v1 = uv[1][1]; 251 double max_u = algorithm.max(math.abs(u0), math.abs(u1)); 252 double max_v = algorithm.max(math.abs(v0), math.abs(v1)); 253 double sin_dist = sin(distance); 254 return R2Rect( 255 R1Interval(expandEndpoint(u0, max_v, -sin_dist), expandEndpoint(u1, max_v, sin_dist)), 256 R1Interval(expandEndpoint(v0, max_u, -sin_dist), expandEndpoint(v1, max_u, sin_dist))); 257 } 258 259 /** 260 * This is a helper function for expandedByDistanceUV(). 261 * 262 * Given an edge of the form (u,v0)-(u,v1), let max_v = max(abs(v0), abs(v1)). 263 * This method returns a new u-coordinate u' such that the distance from the 264 * line u=u' to the given edge (u,v0)-(u,v1) is exactly the given distance 265 * (which is specified as the sine of the angle corresponding to the distance). 266 */ 267 private static double expandEndpoint(double u, double max_v, double sinDist) { 268 // This is based on solving a spherical right triangle, similar to the 269 // calculation in S2Cap::GetRectBound. 270 double sin_u_shift = sinDist * math.sqrt((1 + u * u + max_v * max_v) / (1 + u * u)); 271 double cos_u_shift = math.sqrt(1 - sin_u_shift * sin_u_shift); 272 // The following is an expansion of tan(atan(u) + asin(sin_u_shift)). 273 return (cos_u_shift * u + sin_u_shift) / (cos_u_shift - sin_u_shift * u); 274 } 275 276 /** 277 * Returns the (face, si, ti) coordinates of the center of the cell. Note 278 * that although (si,ti) coordinates span the range [0,2**31] in general, 279 * the cell center coordinates are always in the range [1,2**31-1] and 280 * therefore can be represented using a signed 32-bit integer. 281 */ 282 int getCenterSiTi(out int psi, out int pti) const { 283 // First we compute the discrete (i,j) coordinates of a leaf cell contained 284 // within the given cell. Given that cells are represented by the Hilbert 285 // curve position corresponding at their center, it turns out that the cell 286 // returned by ToFaceIJOrientation is always one of two leaf cells closest 287 // to the center of the cell (unless the given cell is a leaf cell itself, 288 // in which case there is only one possibility). 289 // 290 // Given a cell of size s >= 2 (i.e. not a leaf cell), and letting (imin, 291 // jmin) be the coordinates of its lower left-hand corner, the leaf cell 292 // returned by ToFaceIJOrientation() is either (imin + s/2, jmin + s/2) 293 // (imin + s/2 - 1, jmin + s/2 - 1). The first case is the one we want. 294 // We can distinguish these two cases by looking at the low bit of "i" or 295 // "j". In the second case the low bit is one, unless s == 2 (i.e. the 296 // level just above leaf cells) in which case the low bit is zero. 297 // 298 // In the code below, the expression ((i ^ (int(_id) >> 2)) & 1) is true 299 // if we are in the second case described above. 300 int i, j; 301 int face = toFaceIJOrientation(i, j); 302 int delta = isLeaf() ? 1 : ((i ^ (cast(int) _id >> 2)) & 1) ? 2 : 0; 303 304 // Note that (2 * {i,j} + delta) will never overflow a 32-bit integer. 305 psi = 2 * i + delta; 306 pti = 2 * j + delta; 307 return face; 308 } 309 310 /// Returns the S2LatLng corresponding to the center of the given cell. 311 S2LatLng toLatLng() const { 312 return S2LatLng(toS2PointRaw()); 313 } 314 315 /// The 64-bit unique identifier for this cell. 316 @property 317 ulong id() const { 318 return _id; 319 } 320 321 /** 322 * Returns true if id() represents a valid cell. 323 * 324 * All methods require isValid() to be true unless otherwise specified 325 * (although not all methods enforce this). 326 */ 327 bool isValid() const { 328 return (face() < NUM_FACES && (lsb() & 0x1555555555555555uL)); 329 } 330 331 /// Which cube face this cell belongs to, in the range 0..5. 332 int face() const { 333 return _id >> POS_BITS; 334 } 335 336 /** 337 * The position of the cell center along the Hilbert curve over this face, 338 * in the range 0..(2**POS_BITS-1). 339 */ 340 ulong pos() const { 341 return _id & (~0uL >> FACE_BITS); 342 } 343 344 /// Returns the subdivision level of the cell (range 0..MAX_LEVEL). 345 int level() const 346 in { 347 // We can't just DCHECK(isValid()) because we want level() to be to be 348 // defined for end-iterators, i.e. S2CellId::End(kLevel). However there is 349 // no good way to define S2CellId::None().level(), so we do prohibit that. 350 assert(_id != 0); 351 } do { 352 // A special case for leaf cells is not worthwhile. 353 return MAX_LEVEL - (bitop.bsf(_id) >> 1); 354 } 355 356 /// Returns the edge length of this cell in (i,j)-space. 357 int getSizeIJ() const { 358 return getSizeIJ(level()); 359 } 360 361 /// Like getSizeIJ(), but returns the size of cells at the given level. 362 static int getSizeIJ(int level) { 363 return 1 << (MAX_LEVEL - level); 364 } 365 366 /** 367 * Returns true if this is a leaf cell (more efficient than checking 368 * whether level() == MAX_LEVEL). 369 */ 370 bool isLeaf() const { 371 return cast(int) _id & 1; 372 } 373 374 /** 375 * Return true if this is a top-level face cell (more efficient than 376 * checking whether level() == 0). 377 */ 378 bool isFace() const { 379 return (_id & (lsbForLevel(0) - 1)) == 0; 380 } 381 382 /** 383 * Return the child position (0..3) of this cell within its parent. 384 * REQUIRES: level() >= 1. 385 */ 386 int childPosition() const { 387 // No need for a special implementation; the compiler optimizes this well. 388 return childPosition(level()); 389 } 390 391 /** 392 * Return the child position (0..3) of this cell's ancestor at the given 393 * level within its parent. For example, child_position(1) returns the 394 * position of this cell's level-1 ancestor within its top-level face cell. 395 * REQUIRES: 1 <= level <= this->level(). 396 */ 397 int childPosition(int level) const 398 in { 399 assert(isValid()); 400 assert(level >= 1); 401 assert(level <= this.level()); 402 } do { 403 return cast(int)(_id >> (2 * (MAX_LEVEL - level) + 1)) & 3; 404 } 405 406 /** 407 * These methods return the range of cell ids that are contained within this 408 * cell \(including itself\). 409 * 410 * The range is *inclusive* \(i.e. test using >= and <=\) and the return values of both methods 411 * are valid leaf cell ids. In other words, `a.contains(b)` if and only if 412 * --- 413 * (b >= a.range_min() && b <= a.range_max()) 414 * --- 415 * If you want to iterate through all the descendants of this cell at a 416 * particular level, use `childBegin(level)` and `childEnd(level)` instead. 417 * Also see `maximumTile()`, which can be used to iterate through a range of 418 * cells using S2CellIds at different levels that are as large as possible. 419 * 420 * If you need to convert the range to a semi-open interval \[min, limit\) 421 * \(e.g., in order to use a key-value store that only supports semi-open 422 * range queries\), do not attempt to define "limit" as `range_max.next()`. 423 * The problem is that leaf S2CellIds are 2 units apart, so the semi-open 424 * interval \[min, limit\) includes an additional value `(range_max.id() + 1)` 425 * which is happens to be a valid S2CellId about one-third of the time and 426 * is *never* contained by this cell. \(It always correpsonds to a cell that 427 * is larger than this one.\) You can define "limit" as `(range_max.id() + 1)` 428 * if necessary \(which is not always a valid S2CellId but can still be used 429 * with FromToken/ToToken\), or you can convert `rangeMax()` to the key space 430 * of your key-value store and define "limit" as `successor(key)`. 431 * 432 * Note that `Sentinel().rangeMin() == Sentinel.rangeMax() == Sentinel()`. 433 */ 434 S2CellId rangeMin() const { 435 return S2CellId(_id - (lsb() - 1)); 436 } 437 438 S2CellId rangeMax() const { 439 return S2CellId(_id + (lsb() - 1)); 440 } 441 442 /// Returns true if the given cell is contained within this one. 443 bool contains(S2CellId other) const 444 in { 445 assert(isValid(), "Invalid cell: " ~ toString()); 446 assert(other.isValid()); 447 } do { 448 return other >= rangeMin() && other <= rangeMax(); 449 } 450 451 452 /// Returns true if the given cell intersects this one. 453 bool intersects(S2CellId other) const 454 in { 455 assert(isValid()); 456 assert(other.isValid()); 457 } do { 458 return other.rangeMin() <= rangeMax() && other.rangeMax() >= rangeMin(); 459 } 460 461 /** 462 * Returns the cell at the previous level or at the given level (which must 463 * be less than or equal to the current level). 464 */ 465 S2CellId parent() const 466 in { 467 assert(isValid()); 468 assert(!isFace()); 469 } do { 470 ulong new_lsb = lsb() << 2; 471 return S2CellId((_id & (~new_lsb + 1)) | new_lsb); 472 } 473 474 S2CellId parent(int level) const 475 in { 476 assert(isValid(), format.format("Cannot get parent of invalid cell %s.\n id=%x, face=%d, lsb=%d", toString(), id(), face(), lsb())); 477 assert(level >= 0); 478 assert(level <= this.level()); 479 } do { 480 ulong new_lsb = lsbForLevel(level); 481 return S2CellId((_id & (~new_lsb + 1)) | new_lsb); 482 } 483 484 /** 485 * Returns the immediate child of this cell at the given traversal order 486 * position (in the range 0 to 3). This cell must not be a leaf cell. 487 */ 488 S2CellId child(int position) const 489 in { 490 assert(isValid()); 491 assert(!isLeaf()); 492 } do { 493 // To change the level, we need to move the least-significant bit two 494 // positions downward. We do this by subtracting (4 * new_lsb) and adding 495 // new_lsb. Then to advance to the given child cell, we add 496 // (2 * position * new_lsb). 497 ulong new_lsb = lsb() >> 2; 498 return S2CellId(_id + (2 * position + 1 - 4) * new_lsb); 499 } 500 501 /** 502 * Iterator-style methods for traversing the immediate children of a cell or 503 * all of the children at a given level (greater than or equal to the current 504 * level). Note that the end value is exclusive, just like standard STL 505 * iterators, and may not even be a valid cell id. You should iterate using 506 * code like this: 507 * 508 * for(S2CellId c = id.child_begin(); c != id.child_end(); c = c.next()) 509 * ... 510 * 511 * The convention for advancing the iterator is "c = c.next()" rather 512 * than "++c" to avoid possible confusion with incrementing the 513 * underlying 64-bit cell id. 514 */ 515 S2CellId childBegin() const 516 in { 517 assert(isValid()); 518 assert(!isLeaf()); 519 } do { 520 ulong old_lsb = lsb(); 521 return S2CellId(_id - old_lsb + (old_lsb >> 2)); 522 } 523 524 S2CellId childBegin(int level) const 525 in { 526 assert(isValid()); 527 assert(level >= this.level()); 528 assert(level <= MAX_LEVEL); 529 } do { 530 return S2CellId(_id - lsb() + lsbForLevel(level)); 531 } 532 533 S2CellId childEnd() const 534 in { 535 assert(isValid()); 536 assert(!isLeaf()); 537 } do { 538 ulong old_lsb = lsb(); 539 return S2CellId(_id + old_lsb + (old_lsb >> 2)); 540 } 541 542 S2CellId childEnd(int level) const 543 in { 544 assert(isValid()); 545 assert(level >= this.level()); 546 assert(level <= MAX_LEVEL); 547 } do { 548 return S2CellId(_id + lsb() + lsbForLevel(level)); 549 } 550 551 /** 552 * Returns the next/previous cell at the same level along the Hilbert curve. 553 * Works correctly when advancing from one face to the next, but 554 * does *not* wrap around from the last face to the first or vice versa. 555 */ 556 S2CellId next() const { 557 return S2CellId(_id + (lsb() << 1)); 558 } 559 560 S2CellId prev() const { 561 return S2CellId(_id - (lsb() << 1)); 562 } 563 564 /** 565 * This method advances or retreats the indicated number of steps along the 566 * Hilbert curve at the current level, and returns the new position. The 567 * position is never advanced past End() or before Begin(). 568 */ 569 S2CellId advance(long steps) const { 570 if (steps == 0) { 571 return this; 572 } 573 574 // We clamp the number of steps if necessary to ensure that we do not 575 // advance past the End() or before the Begin() of this level. Note that 576 // min_steps and max_steps always fit in a signed 64-bit integer. 577 578 int step_shift = 2 * (MAX_LEVEL - level()) + 1; 579 if (steps < 0) { 580 long min_steps = -cast(long)(_id >> step_shift); 581 if (steps < min_steps) steps = min_steps; 582 } else { 583 long max_steps = (WRAP_OFFSET + lsb() - _id) >> step_shift; 584 if (steps > max_steps) steps = max_steps; 585 } 586 // If steps is negative, then shifting it left has undefined behavior. 587 // Cast to uint64 for a 2's complement answer. 588 return S2CellId(_id + (cast(ulong) steps << step_shift)); 589 } 590 591 /** 592 * Returns the number of steps that this cell is from Begin(level()). The 593 * return value is always non-negative. 594 */ 595 long distanceFromBegin() const { 596 const int step_shift = 2 * (MAX_LEVEL - level()) + 1; 597 return _id >> step_shift; 598 } 599 600 /** 601 * Like next() and prev(), but these methods wrap around from the last face 602 * to the first and vice versa. They should *not* be used for iteration in 603 * conjunction with child_begin(), child_end(), Begin(), or End(). The 604 * input must be a valid cell id. 605 */ 606 S2CellId nextWrap() const 607 in { 608 assert(isValid()); 609 } do { 610 S2CellId n = next(); 611 if (n._id < WRAP_OFFSET) return n; 612 return S2CellId(n._id - WRAP_OFFSET); 613 } 614 615 S2CellId prevWrap() const 616 in { 617 assert(isValid()); 618 } do { 619 S2CellId p = prev(); 620 if (p._id < WRAP_OFFSET) return p; 621 return S2CellId(p._id + WRAP_OFFSET); 622 } 623 624 /** 625 * This method advances or retreats the indicated number of steps along the 626 * Hilbert curve at the current level, and returns the new position. The 627 * position wraps between the first and last faces as necessary. The input 628 * must be a valid cell id. 629 */ 630 S2CellId advanceWrap(long steps) const 631 in { 632 assert(isValid()); 633 } do { 634 if (steps == 0) { 635 return this; 636 } 637 638 int step_shift = 2 * (MAX_LEVEL - level()) + 1; 639 if (steps < 0) { 640 long min_steps = -cast(long)(_id >> step_shift); 641 if (steps < min_steps) { 642 long step_wrap = WRAP_OFFSET >> step_shift; 643 steps %= step_wrap; 644 if (steps < min_steps) steps += step_wrap; 645 } 646 } else { 647 // Unlike advance(), we don't want to return End(level). 648 long max_steps = (WRAP_OFFSET - _id) >> step_shift; 649 if (steps > max_steps) { 650 long step_wrap = WRAP_OFFSET >> step_shift; 651 steps %= step_wrap; 652 if (steps > max_steps) steps -= step_wrap; 653 } 654 } 655 return S2CellId(_id + (cast(ulong)(steps) << step_shift)); 656 } 657 658 /** 659 * Returns the largest cell with the same `range_min()` and such that 660 * `range_max() < limit.range_min()`. 661 * 662 * Returns "limit" if no such cell exists. This method can be used to generate a small set 663 * of S2CellIds that covers a given range \(a "tiling"\). This example shows how to 664 * generate a tiling for a semi-open range of leaf cells \[start, limit\): 665 * --- 666 * for (S2CellId id = start.maximum_tile(limit); 667 * id != limit; id = id.next().maximum_tile(limit)) { ... } 668 * --- 669 * Note that in general the cells in the tiling will be of different sizes; 670 * they gradually get larger \(near the middle of the range\) and then 671 * gradually get smaller \(as "limit" is approached\). 672 */ 673 S2CellId maximumTile(S2CellId limit) const { 674 S2CellId id = this; 675 S2CellId start = id.rangeMin(); 676 if (start >= limit.rangeMin()) { 677 return limit; 678 } 679 680 if (id.rangeMax() >= limit) { 681 // The cell is too large. Shrink it. Note that when generating coverings 682 // of S2CellId ranges, this loop usually executes only once. Also because 683 // id.range_min() < limit.range_min(), we will always exit the loop by the 684 // time we reach a leaf cell. 685 do { 686 id = id.child(0); 687 } while (id.rangeMax() >= limit); 688 return id; 689 } 690 // The cell may be too small. Grow it if necessary. Note that generally 691 // this loop only iterates once. 692 while (!id.isFace()) { 693 S2CellId parent = id.parent(); 694 if (parent.rangeMin() != start || parent.rangeMax() >= limit) { 695 break; 696 } 697 id = parent; 698 } 699 return id; 700 } 701 702 /** 703 * Returns the level of the lowest common ancestor of this cell and "other", 704 * that is, the maximum level such that parent(level) == other.parent(level). 705 * Returns -1 if the two cells do not have any common ancestor (i.e., they 706 * are from different faces). 707 */ 708 int getCommonAncestorLevel(S2CellId other) const { 709 // Basically we find the first bit position at which the two S2CellIds 710 // differ and convert that to a level. The max() below is necessary for the 711 // case where one S2CellId is a descendant of the other. 712 ulong bits = algorithm.max(id() ^ other.id(), algorithm.max(lsb(), other.lsb())); 713 assert(bits != 0); // Because lsb() is non-zero. 714 715 // Compute the position of the most significant bit, and then map the bit 716 // position as follows: 717 // {0} -> 30, {1,2} -> 29, {3,4} -> 28, ... , {59,60} -> 0, {61,62,63} -> -1. 718 return algorithm.max(60 - bitop.bsr(bits), -1) >> 1; 719 } 720 721 /** 722 * Iterator-style methods for traversing all the cells along the Hilbert 723 * curve at a given level (across all 6 faces of the cube). Note that the 724 * end value is exclusive (just like standard STL iterators), and is not a 725 * valid cell id. 726 */ 727 static S2CellId begin(int level) { 728 return fromFace(0).childBegin(level); 729 } 730 731 static S2CellId end(int level) { 732 return fromFace(5).childEnd(level); 733 } 734 735 /** 736 * Methods to encode and decode cell ids to compact text strings suitable 737 * for display or indexing. Cells at lower levels (i.e. larger cells) are 738 * encoded into fewer characters. The maximum token length is 16. 739 * 740 * Tokens preserve ordering, i.e. ToToken(x) < ToToken(y) iff x < y. 741 * 742 * ToToken() returns a string by value for convenience; the compiler 743 * does this without intermediate copying in most cases. 744 * 745 * These methods guarantee that FromToken(ToToken(x)) == x even when 746 * "x" is an invalid cell id. All tokens are alphanumeric strings. 747 * FromToken() returns S2CellId::None() for malformed inputs. 748 */ 749 string toToken() const { 750 // Simple implementation: print the id in hex without trailing zeros. 751 // Using hex has the advantage that the tokens are case-insensitive, all 752 // characters are alphanumeric, no characters require any special escaping 753 // in queries for most indexing systems, and it's easy to compare cell 754 // tokens against the feature ids of the corresponding features. 755 // 756 // Using base 64 would produce slightly shorter tokens, but for typical cell 757 // sizes used during indexing (up to level 15 or so) the average savings 758 // would be less than 2 bytes per cell which doesn't seem worth it. 759 760 // "0" with trailing 0s stripped is the empty string, which is not a 761 // reasonable token. Encode as "X". 762 if (_id == 0) { 763 return "X"; 764 } 765 const size_t num_zero_digits = bitop.bsf(_id) / 4; 766 return hexFormatString(_id >> (4 * num_zero_digits), 16 - num_zero_digits); 767 } 768 769 // Print the num_digits low order hex digits. 770 private static string hexFormatString(ulong val, size_t num_digits) { 771 char[] result = array.replicate([' '], num_digits); 772 for (; num_digits--; val >>= 4) { 773 result[num_digits] = "0123456789abcdef"[val & 0xF]; 774 } 775 return result.idup; 776 } 777 778 static S2CellId fromToken(string token) { 779 if (token.length > 16) { 780 return S2CellId.none(); 781 } 782 ulong id = 0; 783 for (int i = 0, pos = 60; i < token.length; ++i, pos -= 4) { 784 ulong d; 785 if ('0' <= token[i] && token[i] <= '9') { 786 d = token[i] - '0'; 787 } else if ('a' <= token[i] && token[i] <= 'f') { 788 d = token[i] - 'a' + 10; 789 } else if ('A' <= token[i] && token[i] <= 'F') { 790 d = token[i] - 'A' + 10; 791 } else { 792 return S2CellId.none(); 793 } 794 id |= d << pos; 795 } 796 return S2CellId(id); 797 } 798 799 /** 800 * Use encoder to generate a serialized representation of this cell id. 801 * Can also encode an invalid cell. 802 */ 803 void encode(ORangeT)(Encoder!ORangeT encoder) const { 804 encoder.ensure(ulong.sizeof); // A single uint64. 805 encoder.put64(_id); 806 } 807 808 /// Decodes an S2CellId encoded by Encode(). Returns true on success. 809 bool decode(IRangeT)(Decoder!IRangeT decoder) { 810 if (decoder.avail() < ulong.sizeof) return false; 811 _id = decoder.get64(); 812 return true; 813 } 814 815 /** 816 * Creates a human readable debug string. Used for << and available for 817 * direct usage as well. The format is "f/dd..d" where "f" is a digit in 818 * the range [0-5] representing the S2CellId face, and "dd..d" is a string 819 * of digits in the range [0-3] representing each child's position with 820 * respect to its parent. (Note that the latter string may be empty.) 821 * 822 * For example "4/" represents S2CellId::FromFace(4), and "3/02" represents 823 * S2CellId::FromFace(3).child(0).child(2). 824 */ 825 string toString() const { 826 if (!isValid()) { 827 return "Invalid: " ~ format.format("%016x", id()); 828 } 829 string s = conv.to!string(face()) ~ "/"; 830 for (int current_level = 1; current_level <= level(); ++current_level) { 831 // Avoid dependencies of SimpleItoA, and slowness of StrAppend & 832 // std::to_string. 833 s ~= "0123"[childPosition(current_level)]; 834 } 835 return s; 836 } 837 838 /** 839 * Converts a string in the format returned by ToString() to an S2CellId. 840 * Returns S2CellId::None() if the string could not be parsed. 841 * 842 * The method name includes "Debug" in order to avoid possible confusion 843 * with FromToken() above. 844 */ 845 static S2CellId fromDebugString(in string str) { 846 // This function is reasonably efficient, but is only intended for use in 847 // tests. 848 int level = cast(int)(str.length - 2); 849 if (level < 0 || level > MAX_LEVEL) { 850 return S2CellId.none(); 851 } 852 int face = str[0] - '0'; 853 if (face < 0 || face > 5 || str[1] != '/') { 854 return S2CellId.none(); 855 } 856 S2CellId id = S2CellId.fromFace(face); 857 for (int i = 2; i < str.length; ++i) { 858 int child_pos = str[i] - '0'; 859 if (child_pos < 0 || child_pos > 3) { 860 return S2CellId.none(); 861 } 862 id = id.child(child_pos); 863 } 864 return id; 865 } 866 867 /** 868 * Returns the four cells that are adjacent across the cell's four edges. 869 * Neighbors are returned in the order defined by S2Cell::GetEdge. All 870 * neighbors are guaranteed to be distinct. 871 */ 872 void getEdgeNeighbors(out S2CellId[4] neighbors) const { 873 int i, j; 874 int level = level(); 875 int size = getSizeIJ(level); 876 int face = toFaceIJOrientation(i, j); 877 878 // Edges 0, 1, 2, 3 are in the down, right, up, left directions. 879 neighbors[0] = fromFaceIJSame(face, i, j - size, j - size >= 0).parent(level); 880 neighbors[1] = fromFaceIJSame(face, i + size, j, i + size < MAX_SIZE).parent(level); 881 neighbors[2] = fromFaceIJSame(face, i, j + size, j + size < MAX_SIZE).parent(level); 882 neighbors[3] = fromFaceIJSame(face, i - size, j, i - size >= 0).parent(level); 883 } 884 885 /** 886 * Returns the neighbors of closest vertex to this cell at the given level, 887 * by appending them to "output". Normally there are four neighbors, but 888 * the closest vertex may only have three neighbors if it is one of the 8 889 * cube vertices. 890 * 891 * Requires: level < this->level(), so that we can determine which vertex is 892 * closest (in particular, level == MAX_LEVEL is not allowed). 893 */ 894 void appendVertexNeighbors(RangeT)(int level, ref RangeT output) const 895 if (range.isOutputRange!(RangeT, S2CellId)) 896 in { 897 // "level" must be strictly less than this cell's level so that we can 898 // determine which vertex this cell is closest to. 899 assert(level < this.level()); 900 } do { 901 int i, j; 902 int face = toFaceIJOrientation(i, j); 903 904 // Determine the i- and j-offsets to the closest neighboring cell in each 905 // direction. This involves looking at the next bit of "i" and "j" to 906 // determine which quadrant of this->parent(level) this cell lies in. 907 int halfsize = getSizeIJ(level + 1); 908 int size = halfsize << 1; 909 bool isame, jsame; 910 int ioffset, joffset; 911 if (i & halfsize) { 912 ioffset = size; 913 isame = (i + size) < MAX_SIZE; 914 } else { 915 ioffset = -size; 916 isame = (i - size) >= 0; 917 } 918 if (j & halfsize) { 919 joffset = size; 920 jsame = (j + size) < MAX_SIZE; 921 } else { 922 joffset = -size; 923 jsame = (j - size) >= 0; 924 } 925 926 output ~= parent(level); 927 output ~= fromFaceIJSame(face, i + ioffset, j, isame).parent(level); 928 output ~= fromFaceIJSame(face, i, j + joffset, jsame).parent(level); 929 // If i- and j- edge neighbors are *both* on a different face, then this 930 // vertex only has three neighbors (it is one of the 8 cube vertices). 931 if (isame || jsame) { 932 output ~= fromFaceIJSame(face, i + ioffset, j + joffset, isame && jsame).parent(level); 933 } 934 } 935 936 /** 937 * Append all neighbors of this cell at the given level to "output". Two 938 * cells X and Y are neighbors if their boundaries intersect but their 939 * interiors do not. In particular, two cells that intersect at a single 940 * point are neighbors. Note that for cells adjacent to a face vertex, the 941 * same neighbor may be appended more than once. 942 * 943 * REQUIRES: nbr_level >= this->level(). 944 */ 945 void appendAllNeighbors(ORangeT)(int nbr_level, ref ORangeT output) const 946 if (range.isOutputRange!(ORangeT, S2CellId)) 947 in { 948 assert(nbr_level >= level()); 949 } do { 950 int i, j; 951 int face = toFaceIJOrientation(i, j); 952 953 // Find the coordinates of the lower left-hand leaf cell. We need to 954 // normalize (i,j) to a known position within the cell because nbr_level 955 // may be larger than this cell's level. 956 int size = getSizeIJ(); 957 i &= -size; 958 j &= -size; 959 960 int nbr_size = getSizeIJ(nbr_level); 961 assert(nbr_size <= size); 962 963 // We compute the top-bottom, left-right, and diagonal neighbors in one 964 // pass. The loop test is at the end of the loop to avoid 32-bit overflow. 965 for (int k = -nbr_size; ; k += nbr_size) { 966 bool same_face; 967 if (k < 0) { 968 same_face = (j + k >= 0); 969 } else if (k >= size) { 970 same_face = (j + k < MAX_SIZE); 971 } else { 972 same_face = true; 973 // Top and bottom neighbors. 974 output ~= fromFaceIJSame(face, i + k, j - nbr_size, j - size >= 0).parent(nbr_level); 975 output ~= fromFaceIJSame(face, i + k, j + size, j + size < MAX_SIZE).parent(nbr_level); 976 } 977 // Left, right, and diagonal neighbors. 978 output ~= fromFaceIJSame(face, i - nbr_size, j + k, same_face && i - size >= 0) 979 .parent(nbr_level); 980 output ~= fromFaceIJSame(face, i + size, j + k, same_face && i + size < MAX_SIZE) 981 .parent(nbr_level); 982 if (k >= size) { 983 break; 984 } 985 } 986 } 987 988 ///////////////////////////////////////////////////////////////////// 989 // Low-level methods. 990 991 /** 992 * Returns a leaf cell given its cube face (range 0..5) and 993 * i- and j-coordinates (see s2coords.h). 994 */ 995 static S2CellId fromFaceIJ(int face, int i, int j) { 996 // Optimization notes: 997 // - Non-overlapping bit fields can be combined with either "+" or "|". 998 // Generally "+" seems to produce better code, but not always. 999 1000 // Note that this value gets shifted one bit to the left at the end 1001 // of the function. 1002 ulong n = cast(ulong)(face) << (POS_BITS - 1); 1003 1004 // Alternating faces have opposite Hilbert curve orientations; this 1005 // is necessary in order for all faces to have a right-handed 1006 // coordinate system. 1007 ulong bits = (face & s2coords.SWAP_MASK); 1008 1009 // Each iteration maps 4 bits of "i" and "j" into 8 bits of the Hilbert 1010 // curve position. The lookup table transforms a 10-bit key of the form 1011 // "iiiijjjjoo" to a 10-bit value of the form "ppppppppoo", where the 1012 // letters [ijpo] denote bits of "i", "j", Hilbert curve position, and 1013 // Hilbert curve orientation respectively. 1014 int mask; 1015 static foreach (k; range.iota(7, -1, -1)) { 1016 mask = (1 << LOOKUP_BITS) - 1; 1017 bits += ((i >> (k * LOOKUP_BITS)) & mask) << (LOOKUP_BITS + 2); 1018 bits += ((j >> (k * LOOKUP_BITS)) & mask) << 2; 1019 bits = LOOKUP_POS[bits]; 1020 n |= (bits >> 2) << (k * 2 * LOOKUP_BITS); 1021 bits &= (s2coords.SWAP_MASK | s2coords.INVERT_MASK); 1022 } 1023 1024 return S2CellId(n * 2 + 1); 1025 } 1026 1027 /** 1028 * Return the (face, i, j) coordinates for the leaf cell corresponding to 1029 * this cell id. Since cells are represented by the Hilbert curve position 1030 * at the center of the cell, the returned (i,j) for non-leaf cells will be 1031 * a leaf cell adjacent to the cell center. If "orientation" is non-nullptr, 1032 * also return the Hilbert curve orientation for the current cell. 1033 */ 1034 int toFaceIJOrientation(out int pi, out int pj) const { 1035 extractHilbertIJBits(pi, pj); 1036 return face(); 1037 } 1038 1039 int toFaceIJOrientation(out int pi, out int pj, out int orientation) const { 1040 int bits = extractHilbertIJBits(pi, pj); 1041 1042 // The position of a non-leaf cell at level "n" consists of a prefix of 1043 // 2*n bits that identifies the cell, followed by a suffix of 1044 // 2*(kMaxLevel-n)+1 bits of the form 10*. If n==kMaxLevel, the suffix is 1045 // just "1" and has no effect. Otherwise, it consists of "10", followed 1046 // by (kMaxLevel-n-1) repetitions of "00", followed by "0". The "10" has 1047 // no effect, while each occurrence of "00" has the effect of reversing 1048 // the kSwapMask bit. 1049 assert(0 == s2coords.POS_TO_ORIENTATION[2]); 1050 assert(s2coords.SWAP_MASK == s2coords.POS_TO_ORIENTATION[0]); 1051 if (lsb() & 0x1111111111111110uL) { 1052 bits ^= s2coords.SWAP_MASK; 1053 } 1054 orientation = bits; 1055 1056 return face(); 1057 } 1058 1059 private int extractHilbertIJBits(out int pi, out int pj) const { 1060 int i = 0, j = 0; 1061 int bits = (face() & s2coords.SWAP_MASK); 1062 1063 // Each iteration maps 8 bits of the Hilbert curve position into 1064 // 4 bits of "i" and "j". The lookup table transforms a key of the 1065 // form "ppppppppoo" to a value of the form "iiiijjjjoo", where the 1066 // letters [ijpo] represents bits of "i", "j", the Hilbert curve 1067 // position, and the Hilbert curve orientation respectively. 1068 // 1069 // On the first iteration we need to be careful to clear out the bits 1070 // representing the cube face. 1071 int nbits; 1072 static foreach (k; range.iota(7, -1, -1)) { 1073 nbits = (k == 7) ? (MAX_LEVEL - 7 * LOOKUP_BITS) : LOOKUP_BITS; 1074 bits += (cast(int)(_id >> (k * 2 * LOOKUP_BITS + 1)) & ((1 << (2 * nbits)) - 1)) << 2; 1075 bits = LOOKUP_IJ[bits]; 1076 i += (bits >> (LOOKUP_BITS + 2)) << (k * LOOKUP_BITS); 1077 j += ((bits >> 2) & ((1 << LOOKUP_BITS) - 1)) << (k * LOOKUP_BITS); 1078 bits &= (s2coords.SWAP_MASK | s2coords.INVERT_MASK); 1079 } 1080 1081 pi = i; 1082 pj = j; 1083 return bits; 1084 } 1085 1086 /** 1087 * Returns the lowest-numbered bit that is on for this cell id, which is 1088 * equal to (uint64(1) << (2 * (MAX_LEVEL - level))). So for example, 1089 * a.lsb() <= b.lsb() if and only if a.level() >= b.level(), but the 1090 * first test is more efficient. 1091 */ 1092 ulong lsb() const { 1093 return _id & (~_id + 1); 1094 } 1095 1096 /// Returns the lowest-numbered bit that is on for cells at the given level. 1097 static ulong lsbForLevel(int level) { 1098 return 1uL << (2 * (MAX_LEVEL - level)); 1099 } 1100 1101 /** 1102 * Returns the bound in (u,v)-space for the cell at the given level containing 1103 * the leaf cell with the given (i,j)-coordinates. 1104 */ 1105 static R2Rect IJLevelToBoundUV(int[2] ij, int level) { 1106 R2Rect bound; 1107 int cell_size = getSizeIJ(level); 1108 for (int d = 0; d < 2; ++d) { 1109 int ij_lo = ij[d] & -cell_size; 1110 int ij_hi = ij_lo + cell_size; 1111 1112 bound[d][0] = s2coords.STtoUV(s2coords.IJtoSTMin(ij_lo)); 1113 bound[d][1] = s2coords.STtoUV(s2coords.IJtoSTMin(ij_hi)); 1114 } 1115 return bound; 1116 } 1117 1118 /// Supports the == and != operators. 1119 bool opEquals(in S2CellId v) const { 1120 return id() == v.id(); 1121 } 1122 1123 /// Supports <, <=, >, and >= operators. 1124 int opCmp(in S2CellId v) const { 1125 if (id() == v.id()) { 1126 return 0; 1127 } 1128 return id() > v.id() ? 1 : -1; 1129 } 1130 1131 /// The ID serves as a good hash for associative arrays. 1132 size_t toHash() const @safe pure nothrow { 1133 return cast(size_t) _id; 1134 } 1135 1136 private: 1137 // This is the offset required to wrap around from the beginning of the 1138 // Hilbert curve to the end or vice versa; see next_wrap() and prev_wrap(). 1139 static immutable ulong WRAP_OFFSET = cast(ulong) NUM_FACES << POS_BITS; 1140 static immutable int LOOKUP_BITS = 4; 1141 static immutable int LOOKUP_ARRAY_SIZE = 1 << (2 * LOOKUP_BITS + 2); 1142 static immutable ushort[LOOKUP_ARRAY_SIZE] LOOKUP_POS; 1143 static immutable ushort[LOOKUP_ARRAY_SIZE] LOOKUP_IJ; 1144 1145 shared static this() { 1146 ushort[LOOKUP_ARRAY_SIZE] lookupPos; 1147 ushort[LOOKUP_ARRAY_SIZE] lookupIJ; 1148 initLookupCell(lookupPos, lookupIJ, 0, 0, 0, 0, 0, 0); 1149 initLookupCell(lookupPos, lookupIJ, 0, 0, 0, s2coords.SWAP_MASK, 0, s2coords.SWAP_MASK); 1150 initLookupCell(lookupPos, lookupIJ, 0, 0, 0, s2coords.INVERT_MASK, 0, s2coords.INVERT_MASK); 1151 initLookupCell(lookupPos, lookupIJ, 0, 0, 0, s2coords.SWAP_MASK | s2coords.INVERT_MASK, 0, 1152 s2coords.SWAP_MASK | s2coords.INVERT_MASK); 1153 LOOKUP_POS = lookupPos; 1154 LOOKUP_IJ = lookupIJ; 1155 } 1156 1157 /** 1158 * Given a face and a point (i,j) where either i or j is outside the valid 1159 * range [0..MAX_SIZE-1], this function first determines which neighboring 1160 * face "contains" (i,j), and then returns the leaf cell on that face which 1161 * is adjacent to the given face and whose distance from (i,j) is minimal. 1162 */ 1163 static S2CellId fromFaceIJWrap(int face, int i, int j) { 1164 // Convert i and j to the coordinates of a leaf cell just beyond the 1165 // boundary of this face. This prevents 32-bit overflow in the case 1166 // of finding the neighbors of a face cell. 1167 i = algorithm.max(-1, algorithm.min(MAX_SIZE, i)); 1168 j = algorithm.max(-1, algorithm.min(MAX_SIZE, j)); 1169 1170 // We want to wrap these coordinates onto the appropriate adjacent face. 1171 // The easiest way to do this is to convert the (i,j) coordinates to (x,y,z) 1172 // (which yields a point outside the normal face boundary), and then call 1173 // S2::XYZtoFaceUV() to project back onto the correct face. 1174 // 1175 // The code below converts (i,j) to (si,ti), and then (si,ti) to (u,v) using 1176 // the linear projection (u=2*s-1 and v=2*t-1). (The code further below 1177 // converts back using the inverse projection, s=0.5*(u+1) and t=0.5*(v+1). 1178 // Any projection would work here, so we use the simplest.) We also clamp 1179 // the (u,v) coordinates so that the point is barely outside the 1180 // [-1,1]x[-1,1] face rectangle, since otherwise the reprojection step 1181 // (which divides by the new z coordinate) might change the other 1182 // coordinates enough so that we end up in the wrong leaf cell. 1183 static const double SCALE = 1.0 / MAX_SIZE; 1184 static const double LIMIT = 1.0 + double.epsilon; 1185 // The arithmetic below is designed to avoid 32-bit integer overflows. 1186 assert(0 == MAX_SIZE % 2); 1187 double u = algorithm.max(-LIMIT, algorithm.min(LIMIT, SCALE * (2 * (i - MAX_SIZE / 2) + 1))); 1188 double v = algorithm.max(-LIMIT, algorithm.min(LIMIT, SCALE * (2 * (j - MAX_SIZE / 2) + 1))); 1189 1190 // Find the leaf cell coordinates on the adjacent face, and convert 1191 // them to a cell id at the appropriate level. 1192 face = s2coords.XYZtoFaceUV(s2coords.FaceUVtoXYZ(face, u, v), u, v); 1193 return fromFaceIJ(face, s2coords.STtoIJ(0.5*(u+1)), s2coords.STtoIJ(0.5*(v+1))); 1194 } 1195 1196 /** 1197 * Inline helper function that calls fromFaceIJ if "same_face" is true, 1198 * or fromFaceIJWrap if "same_face" is false. 1199 */ 1200 static S2CellId fromFaceIJSame(int face, int i, int j, bool same_face) { 1201 if (same_face) 1202 return S2CellId.fromFaceIJ(face, i, j); 1203 else 1204 return S2CellId.fromFaceIJWrap(face, i, j); 1205 } 1206 1207 static void initLookupCell( 1208 ref ushort[LOOKUP_ARRAY_SIZE] lookupPos, ref ushort[LOOKUP_ARRAY_SIZE] lookupIJ, 1209 int level, int i, int j, int orig_orientation, int pos, int orientation) { 1210 if (level == LOOKUP_BITS) { 1211 int ij = (i << LOOKUP_BITS) + j; 1212 lookupPos[(ij << 2) + orig_orientation] = cast(ushort)((pos << 2) + orientation); 1213 lookupIJ[(pos << 2) + orig_orientation] = cast(ushort)((ij << 2) + orientation); 1214 } else { 1215 level++; 1216 i <<= 1; 1217 j <<= 1; 1218 pos <<= 2; 1219 const int[4] r = s2coords.POS_TO_IJ[orientation]; 1220 initLookupCell( 1221 lookupPos, lookupIJ, 1222 level, i + (r[0] >> 1), j + (r[0] & 1), orig_orientation, 1223 pos, orientation ^ s2coords.POS_TO_ORIENTATION[0]); 1224 initLookupCell( 1225 lookupPos, lookupIJ, 1226 level, i + (r[1] >> 1), j + (r[1] & 1), orig_orientation, 1227 pos + 1, orientation ^ s2coords.POS_TO_ORIENTATION[1]); 1228 initLookupCell( 1229 lookupPos, lookupIJ, 1230 level, i + (r[2] >> 1), j + (r[2] & 1), orig_orientation, 1231 pos + 2, orientation ^ s2coords.POS_TO_ORIENTATION[2]); 1232 initLookupCell( 1233 lookupPos, lookupIJ, 1234 level, i + (r[3] >> 1), j + (r[3] & 1), orig_orientation, 1235 pos + 3, orientation ^ s2coords.POS_TO_ORIENTATION[3]); 1236 } 1237 } 1238 }