| 1 | // Copyright 2005 Google Inc. All Rights Reserved. |
| 2 | |
| 3 | #include "s2cellid.h" |
| 4 | |
| 5 | #include <pthread.h> |
| 6 | |
| 7 | #include <algorithm> |
| 8 | using std::min; |
| 9 | using std::max; |
| 10 | using std::swap; |
| 11 | using std::reverse; |
| 12 | |
| 13 | #include <iomanip> |
| 14 | using std::setprecision; |
| 15 | |
| 16 | #include <vector> |
| 17 | using std::vector; |
| 18 | |
| 19 | |
| 20 | #include "base/integral_types.h" |
| 21 | #include "base/logging.h" |
| 22 | #include "strings/strutil.h" |
| 23 | #include "s2.h" |
| 24 | #include "s2latlng.h" |
| 25 | #include "util/math/mathutil.h" |
| 26 | #include "util/math/vector2-inl.h" |
| 27 | |
| 28 | // The following lookup tables are used to convert efficiently between an |
| 29 | // (i,j) cell index and the corresponding position along the Hilbert curve. |
| 30 | // "lookup_pos" maps 4 bits of "i", 4 bits of "j", and 2 bits representing the |
| 31 | // orientation of the current cell into 8 bits representing the order in which |
| 32 | // that subcell is visited by the Hilbert curve, plus 2 bits indicating the |
| 33 | // new orientation of the Hilbert curve within that subcell. (Cell |
| 34 | // orientations are represented as combination of kSwapMask and kInvertMask.) |
| 35 | // |
| 36 | // "lookup_ij" is an inverted table used for mapping in the opposite |
| 37 | // direction. |
| 38 | // |
| 39 | // We also experimented with looking up 16 bits at a time (14 bits of position |
| 40 | // plus 2 of orientation) but found that smaller lookup tables gave better |
| 41 | // performance. (2KB fits easily in the primary cache.) |
| 42 | |
| 43 | |
| 44 | // Values for these constants are *declared* in the *.h file. Even though |
| 45 | // the declaration specifies a value for the constant, that declaration |
| 46 | // is not a *definition* of storage for the value. Because the values are |
| 47 | // supplied in the declaration, we don't need the values here. Failing to |
| 48 | // define storage causes link errors for any code that tries to take the |
| 49 | // address of one of these values. |
| 50 | int const S2CellId::kFaceBits; |
| 51 | int const S2CellId::kNumFaces; |
| 52 | int const S2CellId::kMaxLevel; |
| 53 | int const S2CellId::kPosBits; |
| 54 | int const S2CellId::kMaxSize; |
| 55 | |
| 56 | static int const kLookupBits = 4; |
| 57 | static int const kSwapMask = 0x01; |
| 58 | static int const kInvertMask = 0x02; |
| 59 | |
| 60 | static uint16 lookup_pos[1 << (2 * kLookupBits + 2)]; |
| 61 | static uint16 lookup_ij[1 << (2 * kLookupBits + 2)]; |
| 62 | |
| 63 | static void InitLookupCell(int level, int i, int j, int orig_orientation, |
| 64 | int pos, int orientation) { |
| 65 | if (level == kLookupBits) { |
| 66 | int ij = (i << kLookupBits) + j; |
| 67 | lookup_pos[(ij << 2) + orig_orientation] = (pos << 2) + orientation; |
| 68 | lookup_ij[(pos << 2) + orig_orientation] = (ij << 2) + orientation; |
| 69 | } else { |
| 70 | level++; |
| 71 | i <<= 1; |
| 72 | j <<= 1; |
| 73 | pos <<= 2; |
| 74 | int const* r = S2::kPosToIJ[orientation]; |
| 75 | InitLookupCell(level, i + (r[0] >> 1), j + (r[0] & 1), orig_orientation, |
| 76 | pos, orientation ^ S2::kPosToOrientation[0]); |
| 77 | InitLookupCell(level, i + (r[1] >> 1), j + (r[1] & 1), orig_orientation, |
| 78 | pos + 1, orientation ^ S2::kPosToOrientation[1]); |
| 79 | InitLookupCell(level, i + (r[2] >> 1), j + (r[2] & 1), orig_orientation, |
| 80 | pos + 2, orientation ^ S2::kPosToOrientation[2]); |
| 81 | InitLookupCell(level, i + (r[3] >> 1), j + (r[3] & 1), orig_orientation, |
| 82 | pos + 3, orientation ^ S2::kPosToOrientation[3]); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | static void Init() { |
| 87 | InitLookupCell(0, 0, 0, 0, 0, 0); |
| 88 | InitLookupCell(0, 0, 0, kSwapMask, 0, kSwapMask); |
| 89 | InitLookupCell(0, 0, 0, kInvertMask, 0, kInvertMask); |
| 90 | InitLookupCell(0, 0, 0, kSwapMask|kInvertMask, 0, kSwapMask|kInvertMask); |
| 91 | } |
| 92 | |
| 93 | static pthread_once_t init_once = PTHREAD_ONCE_INIT; |
| 94 | inline static void MaybeInit() { |
| 95 | pthread_once(&init_once, Init); |
| 96 | } |
| 97 | |
| 98 | int S2CellId::level() const { |
| 99 | // Fast path for leaf cells. |
| 100 | if (is_leaf()) return kMaxLevel; |
| 101 | |
| 102 | uint32 x = static_cast<uint32>(id_); |
| 103 | int level = -1; |
| 104 | if (x != 0) { |
| 105 | level += 16; |
| 106 | } else { |
| 107 | x = static_cast<uint32>(id_ >> 32); |
| 108 | } |
| 109 | // We only need to look at even-numbered bits to determine the |
| 110 | // level of a valid cell id. |
| 111 | x &= -x; // Get lowest bit. |
| 112 | if (x & 0x00005555) level += 8; |
| 113 | if (x & 0x00550055) level += 4; |
| 114 | if (x & 0x05050505) level += 2; |
| 115 | if (x & 0x11111111) level += 1; |
| 116 | DCHECK_GE(level, 0); |
| 117 | DCHECK_LE(level, kMaxLevel); |
| 118 | return level; |
| 119 | } |
| 120 | |
| 121 | S2CellId S2CellId::advance(int64 steps) const { |
| 122 | if (steps == 0) return *this; |
| 123 | |
| 124 | // We clamp the number of steps if necessary to ensure that we do not |
| 125 | // advance past the End() or before the Begin() of this level. Note that |
| 126 | // min_steps and max_steps always fit in a signed 64-bit integer. |
| 127 | |
| 128 | int step_shift = 2 * (kMaxLevel - level()) + 1; |
| 129 | if (steps < 0) { |
| 130 | int64 min_steps = -static_cast<int64>(id_ >> step_shift); |
| 131 | if (steps < min_steps) steps = min_steps; |
| 132 | } else { |
| 133 | int64 max_steps = (kWrapOffset + lsb() - id_) >> step_shift; |
| 134 | if (steps > max_steps) steps = max_steps; |
| 135 | } |
| 136 | return S2CellId(id_ + (steps << step_shift)); |
| 137 | } |
| 138 | |
| 139 | S2CellId S2CellId::advance_wrap(int64 steps) const { |
| 140 | DCHECK(is_valid()); |
| 141 | if (steps == 0) return *this; |
| 142 | |
| 143 | int step_shift = 2 * (kMaxLevel - level()) + 1; |
| 144 | if (steps < 0) { |
| 145 | int64 min_steps = -static_cast<int64>(id_ >> step_shift); |
| 146 | if (steps < min_steps) { |
| 147 | int64 step_wrap = kWrapOffset >> step_shift; |
| 148 | steps %= step_wrap; |
| 149 | if (steps < min_steps) steps += step_wrap; |
| 150 | } |
| 151 | } else { |
| 152 | // Unlike advance(), we don't want to return End(level). |
| 153 | int64 max_steps = (kWrapOffset - id_) >> step_shift; |
| 154 | if (steps > max_steps) { |
| 155 | int64 step_wrap = kWrapOffset >> step_shift; |
| 156 | steps %= step_wrap; |
| 157 | if (steps > max_steps) steps -= step_wrap; |
| 158 | } |
| 159 | } |
| 160 | return S2CellId(id_ + (steps << step_shift)); |
| 161 | } |
| 162 | |
| 163 | S2CellId S2CellId::FromFacePosLevel(int face, uint64 pos, int level) { |
| 164 | S2CellId cell((static_cast<uint64>(face) << kPosBits) + (pos | 1)); |
| 165 | return cell.parent(level); |
| 166 | } |
| 167 | |
| 168 | string S2CellId::ToToken() const { |
| 169 | // Simple implementation: convert the id to hex and strip trailing zeros. |
| 170 | // Using hex has the advantage that the tokens are case-insensitive, all |
| 171 | // characters are alphanumeric, no characters require any special escaping |
| 172 | // in Mustang queries, and it's easy to compare cell tokens against the |
| 173 | // feature ids of the corresponding features. |
| 174 | // |
| 175 | // Using base 64 would produce slightly shorter tokens, but for typical cell |
| 176 | // sizes used during indexing (up to level 15 or so) the average savings |
| 177 | // would be less than 2 bytes per cell which doesn't seem worth it. |
| 178 | |
| 179 | char digits[17]; |
| 180 | FastHex64ToBuffer(id_, digits); |
| 181 | for (int len = 16; len > 0; --len) { |
| 182 | if (digits[len-1] != '0') { |
| 183 | return string(digits, len); |
| 184 | } |
| 185 | } |
| 186 | return "X" ; // Invalid hex string. |
| 187 | } |
| 188 | |
| 189 | S2CellId S2CellId::FromToken(string const& token) { |
| 190 | if (token.size() > 16) return S2CellId::None(); |
| 191 | char digits[17] = "0000000000000000" ; |
| 192 | memcpy(digits, token.data(), token.size()); |
| 193 | return S2CellId(ParseLeadingHex64Value(digits, 0)); |
| 194 | } |
| 195 | |
| 196 | inline int S2CellId::STtoIJ(double s) { |
| 197 | // Converting from floating-point to integers via static_cast is very slow |
| 198 | // on Intel processors because it requires changing the rounding mode. |
| 199 | // Rounding to the nearest integer using FastIntRound() is much faster. |
| 200 | |
| 201 | return max(0, min(kMaxSize - 1, MathUtil::FastIntRound(kMaxSize * s - 0.5))); |
| 202 | } |
| 203 | |
| 204 | |
| 205 | S2CellId S2CellId::FromFaceIJ(int face, int i, int j) { |
| 206 | // Initialization if not done yet |
| 207 | MaybeInit(); |
| 208 | |
| 209 | // Optimization notes: |
| 210 | // - Non-overlapping bit fields can be combined with either "+" or "|". |
| 211 | // Generally "+" seems to produce better code, but not always. |
| 212 | |
| 213 | // gcc doesn't have very good code generation for 64-bit operations. |
| 214 | // We optimize this by computing the result as two 32-bit integers |
| 215 | // and combining them at the end. Declaring the result as an array |
| 216 | // rather than local variables helps the compiler to do a better job |
| 217 | // of register allocation as well. Note that the two 32-bits halves |
| 218 | // get shifted one bit to the left when they are combined. |
| 219 | uint32 n[2] = { 0, static_cast<uint32>(face << (kPosBits - 33)) }; |
| 220 | |
| 221 | // Alternating faces have opposite Hilbert curve orientations; this |
| 222 | // is necessary in order for all faces to have a right-handed |
| 223 | // coordinate system. |
| 224 | int bits = (face & kSwapMask); |
| 225 | |
| 226 | // Each iteration maps 4 bits of "i" and "j" into 8 bits of the Hilbert |
| 227 | // curve position. The lookup table transforms a 10-bit key of the form |
| 228 | // "iiiijjjjoo" to a 10-bit value of the form "ppppppppoo", where the |
| 229 | // letters [ijpo] denote bits of "i", "j", Hilbert curve position, and |
| 230 | // Hilbert curve orientation respectively. |
| 231 | #define GET_BITS(k) do { \ |
| 232 | int const mask = (1 << kLookupBits) - 1; \ |
| 233 | bits += ((i >> (k * kLookupBits)) & mask) << (kLookupBits + 2); \ |
| 234 | bits += ((j >> (k * kLookupBits)) & mask) << 2; \ |
| 235 | bits = lookup_pos[bits]; \ |
| 236 | n[k >> 2] |= (bits >> 2) << ((k & 3) * 2 * kLookupBits); \ |
| 237 | bits &= (kSwapMask | kInvertMask); \ |
| 238 | } while (0) |
| 239 | |
| 240 | GET_BITS(7); |
| 241 | GET_BITS(6); |
| 242 | GET_BITS(5); |
| 243 | GET_BITS(4); |
| 244 | GET_BITS(3); |
| 245 | GET_BITS(2); |
| 246 | GET_BITS(1); |
| 247 | GET_BITS(0); |
| 248 | #undef GET_BITS |
| 249 | |
| 250 | return S2CellId(((static_cast<uint64>(n[1]) << 32) + n[0]) * 2 + 1); |
| 251 | } |
| 252 | |
| 253 | S2CellId S2CellId::FromPoint(S2Point const& p) { |
| 254 | double u, v; |
| 255 | int face = S2::XYZtoFaceUV(p, &u, &v); |
| 256 | int i = STtoIJ(S2::UVtoST(u)); |
| 257 | int j = STtoIJ(S2::UVtoST(v)); |
| 258 | return FromFaceIJ(face, i, j); |
| 259 | } |
| 260 | |
| 261 | S2CellId S2CellId::FromLatLng(S2LatLng const& ll) { |
| 262 | return FromPoint(ll.ToPoint()); |
| 263 | } |
| 264 | |
| 265 | int S2CellId::ToFaceIJOrientation(int* pi, int* pj, int* orientation) const { |
| 266 | // Initialization if not done yet |
| 267 | MaybeInit(); |
| 268 | |
| 269 | int i = 0, j = 0; |
| 270 | int face = this->face(); |
| 271 | int bits = (face & kSwapMask); |
| 272 | |
| 273 | // Each iteration maps 8 bits of the Hilbert curve position into |
| 274 | // 4 bits of "i" and "j". The lookup table transforms a key of the |
| 275 | // form "ppppppppoo" to a value of the form "iiiijjjjoo", where the |
| 276 | // letters [ijpo] represents bits of "i", "j", the Hilbert curve |
| 277 | // position, and the Hilbert curve orientation respectively. |
| 278 | // |
| 279 | // On the first iteration we need to be careful to clear out the bits |
| 280 | // representing the cube face. |
| 281 | #define GET_BITS(k) do { \ |
| 282 | int const nbits = (k == 7) ? (kMaxLevel - 7 * kLookupBits) : kLookupBits; \ |
| 283 | bits += (static_cast<int>(id_ >> (k * 2 * kLookupBits + 1)) \ |
| 284 | & ((1 << (2 * nbits)) - 1)) << 2; \ |
| 285 | bits = lookup_ij[bits]; \ |
| 286 | i += (bits >> (kLookupBits + 2)) << (k * kLookupBits); \ |
| 287 | j += ((bits >> 2) & ((1 << kLookupBits) - 1)) << (k * kLookupBits); \ |
| 288 | bits &= (kSwapMask | kInvertMask); \ |
| 289 | } while (0) |
| 290 | |
| 291 | GET_BITS(7); |
| 292 | GET_BITS(6); |
| 293 | GET_BITS(5); |
| 294 | GET_BITS(4); |
| 295 | GET_BITS(3); |
| 296 | GET_BITS(2); |
| 297 | GET_BITS(1); |
| 298 | GET_BITS(0); |
| 299 | #undef GET_BITS |
| 300 | |
| 301 | *pi = i; |
| 302 | *pj = j; |
| 303 | |
| 304 | if (orientation != NULL) { |
| 305 | // The position of a non-leaf cell at level "n" consists of a prefix of |
| 306 | // 2*n bits that identifies the cell, followed by a suffix of |
| 307 | // 2*(kMaxLevel-n)+1 bits of the form 10*. If n==kMaxLevel, the suffix is |
| 308 | // just "1" and has no effect. Otherwise, it consists of "10", followed |
| 309 | // by (kMaxLevel-n-1) repetitions of "00", followed by "0". The "10" has |
| 310 | // no effect, while each occurrence of "00" has the effect of reversing |
| 311 | // the kSwapMask bit. |
| 312 | DCHECK_EQ(0, S2::kPosToOrientation[2]); |
| 313 | DCHECK_EQ(S2::kSwapMask, S2::kPosToOrientation[0]); |
| 314 | if (lsb() & GG_ULONGLONG(0x1111111111111110)) { |
| 315 | bits ^= S2::kSwapMask; |
| 316 | } |
| 317 | *orientation = bits; |
| 318 | } |
| 319 | return face; |
| 320 | } |
| 321 | |
| 322 | inline int S2CellId::GetCenterSiTi(int* psi, int* pti) const { |
| 323 | // First we compute the discrete (i,j) coordinates of a leaf cell contained |
| 324 | // within the given cell. Given that cells are represented by the Hilbert |
| 325 | // curve position corresponding at their center, it turns out that the cell |
| 326 | // returned by ToFaceIJOrientation is always one of two leaf cells closest |
| 327 | // to the center of the cell (unless the given cell is a leaf cell itself, |
| 328 | // in which case there is only one possibility). |
| 329 | // |
| 330 | // Given a cell of size s >= 2 (i.e. not a leaf cell), and letting (imin, |
| 331 | // jmin) be the coordinates of its lower left-hand corner, the leaf cell |
| 332 | // returned by ToFaceIJOrientation() is either (imin + s/2, jmin + s/2) |
| 333 | // (imin + s/2 - 1, jmin + s/2 - 1). The first case is the one we want. |
| 334 | // We can distinguish these two cases by looking at the low bit of "i" or |
| 335 | // "j". In the second case the low bit is one, unless s == 2 (i.e. the |
| 336 | // level just above leaf cells) in which case the low bit is zero. |
| 337 | // |
| 338 | // In the code below, the expression ((i ^ (int(id_) >> 2)) & 1) is true |
| 339 | // if we are in the second case described above. |
| 340 | int i, j; |
| 341 | int face = ToFaceIJOrientation(&i, &j, NULL); |
| 342 | int delta = is_leaf() ? 1 : ((i ^ (static_cast<int>(id_) >> 2)) & 1) ? 2 : 0; |
| 343 | |
| 344 | // Note that (2 * {i,j} + delta) will never overflow a 32-bit integer. |
| 345 | *psi = 2 * i + delta; |
| 346 | *pti = 2 * j + delta; |
| 347 | return face; |
| 348 | } |
| 349 | |
| 350 | S2Point S2CellId::ToPointRaw() const { |
| 351 | // This code would be slightly shorter if we called GetCenterUV(), |
| 352 | // but this method is heavily used and it's 25% faster to include |
| 353 | // the method inline. |
| 354 | int si, ti; |
| 355 | int face = GetCenterSiTi(&si, &ti); |
| 356 | return S2::FaceUVtoXYZ(face, |
| 357 | S2::STtoUV((0.5 / kMaxSize) * si), |
| 358 | S2::STtoUV((0.5 / kMaxSize) * ti)); |
| 359 | } |
| 360 | |
| 361 | S2LatLng S2CellId::ToLatLng() const { |
| 362 | return S2LatLng(ToPointRaw()); |
| 363 | } |
| 364 | |
| 365 | Vector2_d S2CellId::GetCenterST() const { |
| 366 | int si, ti; |
| 367 | GetCenterSiTi(&si, &ti); |
| 368 | return Vector2_d((0.5 / kMaxSize) * si, (0.5 / kMaxSize) * ti); |
| 369 | } |
| 370 | |
| 371 | Vector2_d S2CellId::GetCenterUV() const { |
| 372 | int si, ti; |
| 373 | GetCenterSiTi(&si, &ti); |
| 374 | return Vector2_d(S2::STtoUV((0.5 / kMaxSize) * si), |
| 375 | S2::STtoUV((0.5 / kMaxSize) * ti)); |
| 376 | } |
| 377 | |
| 378 | S2CellId S2CellId::FromFaceIJWrap(int face, int i, int j) { |
| 379 | // Convert i and j to the coordinates of a leaf cell just beyond the |
| 380 | // boundary of this face. This prevents 32-bit overflow in the case |
| 381 | // of finding the neighbors of a face cell. |
| 382 | i = max(-1, min(kMaxSize, i)); |
| 383 | j = max(-1, min(kMaxSize, j)); |
| 384 | |
| 385 | // Find the (u,v) coordinates corresponding to the center of cell (i,j). |
| 386 | // For our purposes it's sufficient to always use the linear projection |
| 387 | // from (s,t) to (u,v): u=2*s-1 and v=2*t-1. |
| 388 | static const double kScale = 1.0 / kMaxSize; |
| 389 | double u = kScale * ((i << 1) + 1 - kMaxSize); |
| 390 | double v = kScale * ((j << 1) + 1 - kMaxSize); |
| 391 | |
| 392 | // Find the leaf cell coordinates on the adjacent face, and convert |
| 393 | // them to a cell id at the appropriate level. We convert from (u,v) |
| 394 | // back to (s,t) using s=0.5*(u+1), t=0.5*(v+1). |
| 395 | face = S2::XYZtoFaceUV(S2::FaceUVtoXYZ(face, u, v), &u, &v); |
| 396 | return FromFaceIJ(face, STtoIJ(0.5*(u+1)), STtoIJ(0.5*(v+1))); |
| 397 | } |
| 398 | |
| 399 | inline S2CellId S2CellId::FromFaceIJSame(int face, int i, int j, |
| 400 | bool same_face) { |
| 401 | if (same_face) |
| 402 | return S2CellId::FromFaceIJ(face, i, j); |
| 403 | else |
| 404 | return S2CellId::FromFaceIJWrap(face, i, j); |
| 405 | } |
| 406 | |
| 407 | void S2CellId::GetEdgeNeighbors(S2CellId neighbors[4]) const { |
| 408 | int i, j; |
| 409 | int level = this->level(); |
| 410 | int size = GetSizeIJ(level); |
| 411 | int face = ToFaceIJOrientation(&i, &j, NULL); |
| 412 | |
| 413 | // Edges 0, 1, 2, 3 are in the S, E, N, W directions. |
| 414 | neighbors[0] = FromFaceIJSame(face, i, j - size, j - size >= 0) |
| 415 | .parent(level); |
| 416 | neighbors[1] = FromFaceIJSame(face, i + size, j, i + size < kMaxSize) |
| 417 | .parent(level); |
| 418 | neighbors[2] = FromFaceIJSame(face, i, j + size, j + size < kMaxSize) |
| 419 | .parent(level); |
| 420 | neighbors[3] = FromFaceIJSame(face, i - size, j, i - size >= 0) |
| 421 | .parent(level); |
| 422 | } |
| 423 | |
| 424 | void S2CellId::AppendVertexNeighbors(int level, |
| 425 | vector<S2CellId>* output) const { |
| 426 | // "level" must be strictly less than this cell's level so that we can |
| 427 | // determine which vertex this cell is closest to. |
| 428 | DCHECK_LT(level, this->level()); |
| 429 | int i, j; |
| 430 | int face = ToFaceIJOrientation(&i, &j, NULL); |
| 431 | |
| 432 | // Determine the i- and j-offsets to the closest neighboring cell in each |
| 433 | // direction. This involves looking at the next bit of "i" and "j" to |
| 434 | // determine which quadrant of this->parent(level) this cell lies in. |
| 435 | int halfsize = GetSizeIJ(level + 1); |
| 436 | int size = halfsize << 1; |
| 437 | bool isame, jsame; |
| 438 | int ioffset, joffset; |
| 439 | if (i & halfsize) { |
| 440 | ioffset = size; |
| 441 | isame = (i + size) < kMaxSize; |
| 442 | } else { |
| 443 | ioffset = -size; |
| 444 | isame = (i - size) >= 0; |
| 445 | } |
| 446 | if (j & halfsize) { |
| 447 | joffset = size; |
| 448 | jsame = (j + size) < kMaxSize; |
| 449 | } else { |
| 450 | joffset = -size; |
| 451 | jsame = (j - size) >= 0; |
| 452 | } |
| 453 | |
| 454 | output->push_back(parent(level)); |
| 455 | output->push_back(FromFaceIJSame(face, i + ioffset, j, isame).parent(level)); |
| 456 | output->push_back(FromFaceIJSame(face, i, j + joffset, jsame).parent(level)); |
| 457 | // If i- and j- edge neighbors are *both* on a different face, then this |
| 458 | // vertex only has three neighbors (it is one of the 8 cube vertices). |
| 459 | if (isame || jsame) { |
| 460 | output->push_back(FromFaceIJSame(face, i + ioffset, j + joffset, |
| 461 | isame && jsame).parent(level)); |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | void S2CellId::AppendAllNeighbors(int nbr_level, |
| 466 | vector<S2CellId>* output) const { |
| 467 | int i, j; |
| 468 | int face = ToFaceIJOrientation(&i, &j, NULL); |
| 469 | |
| 470 | // Find the coordinates of the lower left-hand leaf cell. We need to |
| 471 | // normalize (i,j) to a known position within the cell because nbr_level |
| 472 | // may be larger than this cell's level. |
| 473 | int size = GetSizeIJ(); |
| 474 | i &= -size; |
| 475 | j &= -size; |
| 476 | |
| 477 | int nbr_size = GetSizeIJ(nbr_level); |
| 478 | DCHECK_LE(nbr_size, size); |
| 479 | |
| 480 | // We compute the N-S, E-W, and diagonal neighbors in one pass. |
| 481 | // The loop test is at the end of the loop to avoid 32-bit overflow. |
| 482 | for (int k = -nbr_size; ; k += nbr_size) { |
| 483 | bool same_face; |
| 484 | if (k < 0) { |
| 485 | same_face = (j + k >= 0); |
| 486 | } else if (k >= size) { |
| 487 | same_face = (j + k < kMaxSize); |
| 488 | } else { |
| 489 | same_face = true; |
| 490 | // North and South neighbors. |
| 491 | output->push_back(FromFaceIJSame(face, i + k, j - nbr_size, |
| 492 | j - size >= 0).parent(nbr_level)); |
| 493 | output->push_back(FromFaceIJSame(face, i + k, j + size, |
| 494 | j + size < kMaxSize).parent(nbr_level)); |
| 495 | } |
| 496 | // East, West, and Diagonal neighbors. |
| 497 | output->push_back(FromFaceIJSame(face, i - nbr_size, j + k, |
| 498 | same_face && i - size >= 0) |
| 499 | .parent(nbr_level)); |
| 500 | output->push_back(FromFaceIJSame(face, i + size, j + k, |
| 501 | same_face && i + size < kMaxSize) |
| 502 | .parent(nbr_level)); |
| 503 | if (k >= size) break; |
| 504 | } |
| 505 | } |
| 506 | |
| 507 | string S2CellId::ToString() const { |
| 508 | if (!is_valid()) { |
| 509 | return StringPrintf("Invalid: %016llx" , id()); |
| 510 | } |
| 511 | string out = IntToString(face(), "%d/" ); |
| 512 | for (int current_level = 1; current_level <= level(); ++current_level) { |
| 513 | out += IntToString(child_position(current_level), "%d" ); |
| 514 | } |
| 515 | return out; |
| 516 | } |
| 517 | |
| 518 | ostream& operator<<(ostream& os, S2CellId const& id) { |
| 519 | return os << id.ToString(); |
| 520 | } |
| 521 | |