github.com/liucxer/courier@v1.7.1/h3/h3_index.go (about) 1 package h3 2 3 import ( 4 "math" 5 "strconv" 6 ) 7 8 type H3Index uint64 9 10 // define's of constants and macros for bitwise manipulation of H3Index's. 11 12 /** The number of bits in an H3 index. */ 13 const H3_NUM_BITS = 64 14 15 /** The bit offset of the max resolution digit in an H3 index. */ 16 const H3_MAX_OFFSET = 63 17 18 /** The bit offset of the mode in an H3 index. */ 19 const H3_MODE_OFFSET = 59 20 21 /** The bit offset of the base cell in an H3 index. */ 22 const H3_BC_OFFSET = 45 23 24 /** The bit offset of the resolution in an H3 index. */ 25 const H3_RES_OFFSET = 52 26 27 /** The bit offset of the reserved bits in an H3 index. */ 28 const H3_RESERVED_OFFSET = 56 29 30 /** The number of bits in a single H3 resolution digit. */ 31 const H3_PER_DIGIT_OFFSET = 3 32 33 /** 1's in the 4 mode bits, 0's everywhere else. */ 34 const H3_MODE_MASK = (uint64)(15) << H3_MODE_OFFSET 35 36 /** 0's in the 4 mode bits, 1's everywhere else. */ 37 const H3_MODE_MASK_NEGATIVE = ^H3_MODE_MASK 38 39 /** 1's in the 7 base cell bits, 0's everywhere else. */ 40 const H3_BC_MASK = (uint64)(127) << H3_BC_OFFSET 41 42 /** 0's in the 7 base cell bits, 1's everywhere else. */ 43 const H3_BC_MASK_NEGATIVE = ^H3_BC_MASK 44 45 /** 1's in the 4 resolution bits, 0's everywhere else. */ 46 const H3_RES_MASK = uint64(15) << H3_RES_OFFSET 47 48 /** 0's in the 4 resolution bits, 1's everywhere else. */ 49 const H3_RES_MASK_NEGATIVE = ^H3_RES_MASK 50 51 /** 1's in the 3 reserved bits, 0's everywhere else. */ 52 const H3_RESERVED_MASK = (uint64)(7) << H3_RESERVED_OFFSET 53 54 /** 0's in the 3 reserved bits, 1's everywhere else. */ 55 const H3_RESERVED_MASK_NEGATIVE = ^H3_RESERVED_MASK 56 57 /** 1's in the 3 bits of res 15 digit bits, 0's everywhere else. */ 58 const H3_DIGIT_MASK = Direction(7) 59 60 /** 0's in the 7 base cell bits, 1's everywhere else. */ 61 //const H3_DIGIT_MASK_NEGATIVE = ^H3_DIGIT_MASK_NEGATIVE 62 63 /** H3 index with mode 0, res 0, base cell 0, and 7 for all index digits. */ 64 const H3_INIT = H3Index(35184372088831) 65 66 /** 67 * Gets the integer mode of h3. 68 */ 69 func H3_GET_MODE(h3 H3Index) H3Mode { 70 return H3Mode(uint64(h3)&H3_MODE_MASK) >> H3_MODE_OFFSET 71 } 72 73 /** 74 * Sets the integer mode of h3 to v. 75 */ 76 func H3_SET_MODE(h3 *H3Index, v H3Mode) { 77 *h3 = H3Index((uint64(*h3) & H3_MODE_MASK_NEGATIVE) | uint64(v)<<H3_MODE_OFFSET) 78 } 79 80 /** 81 * Gets the integer base cell of h3. 82 */ 83 func H3_GET_BASE_CELL(h3 H3Index) int { 84 return int(uint64(h3)&H3_BC_MASK) >> H3_BC_OFFSET 85 } 86 87 /** 88 * Sets the integer base cell of h3 to bc. 89 */ 90 func H3_SET_BASE_CELL(h3 *H3Index, bc int) { 91 *h3 = H3Index((uint64(*h3) & H3_BC_MASK_NEGATIVE) | (uint64(bc) << H3_BC_OFFSET)) 92 } 93 94 /** 95 * Gets the integer resolution of h3. 96 */ 97 func H3_GET_RESOLUTION(h3 H3Index) int { 98 return int(uint64(h3)&H3_RES_MASK) >> H3_RES_OFFSET 99 } 100 101 /** 102 * Sets the integer resolution of h3. 103 */ 104 func H3_SET_RESOLUTION(h3 *H3Index, res int) { 105 *h3 = H3Index((uint64(*h3) & H3_RES_MASK_NEGATIVE) | (uint64(res))<<H3_RES_OFFSET) 106 } 107 108 /** 109 * Gets the resolution res integer digit (0-7) of h3. 110 */ 111 func H3_GET_INDEX_DIGIT(h3 H3Index, res int) Direction { 112 return Direction((uint64(h3) >> ((MAX_H3_RES - (res)) * H3_PER_DIGIT_OFFSET)) & uint64(H3_DIGIT_MASK)) 113 } 114 115 /** 116 * Sets a value in the reserved space. Setting to non-zero may produce invalid 117 * indexes. 118 */ 119 func H3_SET_RESERVED_BITS(h3 *H3Index, v int) { 120 *h3 = H3Index((uint64(*h3) & H3_RESERVED_MASK_NEGATIVE) | (((uint64)(v)) << H3_RESERVED_OFFSET)) 121 } 122 123 /** 124 * Gets a value in the reserved space. Should always be zero for valid indexes. 125 */ 126 func H3_GET_RESERVED_BITS(h3 H3Index) int { 127 return int((uint64(h3) & H3_RESERVED_MASK) >> H3_RESERVED_OFFSET) 128 } 129 130 /** 131 * Sets the resolution res digit of h3 to the integer digit (0-7) 132 */ 133 func H3_SET_INDEX_DIGIT(h3 *H3Index, res int, digit Direction) { 134 *h3 = H3Index((uint64(*h3) & ^(uint64(H3_DIGIT_MASK) << ((MAX_H3_RES - (res)) * H3_PER_DIGIT_OFFSET))) | (uint64(digit) << ((MAX_H3_RES - (res)) * H3_PER_DIGIT_OFFSET))) 135 } 136 137 /** 138 * Invalid index used to indicate an error from geoToH3 and related functions. 139 */ 140 const H3_INVALID_INDEX = H3Index(0) 141 142 /** 143 * Returns the H3 resolution of an H3 index. 144 * @param h The H3 index. 145 * @return The resolution of the H3 index argument. 146 */ 147 func h3GetResolution(h H3Index) int { return H3_GET_RESOLUTION(h) } 148 149 /** 150 * Returns the H3 base cell number of an H3 index. 151 * @param h The H3 index. 152 * @return The base cell of the H3 index argument. 153 */ 154 func h3GetBaseCell(h H3Index) int { return H3_GET_BASE_CELL(h) } 155 156 /** 157 * Converts a string representation of an H3 index into an H3 index. 158 * @param str The string representation of an H3 index. 159 * @return The H3 index corresponding to the string argument, or 0 if invalid. 160 */ 161 func stringToH3(str string) H3Index { 162 h := H3_INVALID_INDEX 163 // If failed, h will be unmodified and we should return 0 anyways. 164 165 i, err := strconv.ParseUint(str, 16, 64) 166 if err == nil { 167 h = H3Index(i) 168 } 169 170 return h 171 } 172 173 /** 174 * Converts an H3 index into a string representation. 175 * @param h The H3 index to convert. 176 */ 177 func h3ToString(h H3Index) string { 178 return strconv.FormatUint(uint64(h), 16) 179 } 180 181 /** 182 * Returns whether or not an H3 index is valid. 183 * @param h The H3 index to validate. 184 */ 185 func h3IsValid(h H3Index) bool { 186 if H3_GET_MODE(h) != H3_HEXAGON_MODE { 187 return false 188 } 189 baseCell := H3_GET_BASE_CELL(h) 190 if baseCell < 0 || baseCell >= NUM_BASE_CELLS { 191 return false 192 } 193 res := H3_GET_RESOLUTION(h) 194 if res < 0 || res > MAX_H3_RES { 195 return false 196 } 197 foundFirstNonZeroDigit := false 198 for r := 1; r <= res; r++ { 199 digit := H3_GET_INDEX_DIGIT(h, r) 200 if !foundFirstNonZeroDigit && digit != CENTER_DIGIT { 201 foundFirstNonZeroDigit = true 202 if _isBaseCellPentagon(baseCell) && digit == K_AXES_DIGIT { 203 return false 204 } 205 } 206 207 if digit < CENTER_DIGIT || digit >= NUM_DIGITS { 208 return false 209 } 210 } 211 212 for r := res + 1; r <= MAX_H3_RES; r++ { 213 digit := H3_GET_INDEX_DIGIT(h, r) 214 if digit != INVALID_DIGIT { 215 return false 216 } 217 } 218 219 return true 220 } 221 222 /** 223 * Initializes an H3 index. 224 * @param hp The H3 index to initialize. 225 * @param res The H3 resolution to initialize the index to. 226 * @param baseCell The H3 base cell to initialize the index to. 227 * @param initDigit The H3 digit (0-7) to initialize all of the index digits to. 228 */ 229 func setH3Index(hp *H3Index, res int, baseCell int, initDigit Direction) { 230 h := H3_INIT 231 H3_SET_MODE(&h, H3_HEXAGON_MODE) 232 H3_SET_RESOLUTION(&h, res) 233 H3_SET_BASE_CELL(&h, baseCell) 234 for r := 1; r <= res; r++ { 235 H3_SET_INDEX_DIGIT(&h, r, initDigit) 236 } 237 *hp = h 238 } 239 240 /** 241 * h3ToParent produces the parent index for a given H3 index 242 * 243 * @param h to H3Index find parent of 244 * @param parentRes The resolution to switch to (parent, grandparent, etc) 245 * 246 * @return of H3Index the parent, or 0 if you actually asked for a child 247 */ 248 func h3ToParent(h H3Index, parentRes int) H3Index { 249 childRes := H3_GET_RESOLUTION(h) 250 if parentRes > childRes { 251 return H3_INVALID_INDEX 252 } else if parentRes == childRes { 253 return h 254 } else if parentRes < 0 || parentRes > MAX_H3_RES { 255 return H3_INVALID_INDEX 256 } 257 parentH := h 258 H3_SET_RESOLUTION(&parentH, parentRes) 259 for i := parentRes + 1; i <= childRes; i++ { 260 H3_SET_INDEX_DIGIT(&parentH, i, H3_DIGIT_MASK) 261 } 262 return parentH 263 } 264 265 /** 266 * Determines whether one resolution is a valid child resolution of another. 267 * Each resolution is considered a valid child resolution of itself. 268 * 269 * @param parentRes resolution int of the parent 270 * @param childRes resolution int of the child 271 * 272 * @return The validity of the child resolution 273 */ 274 func _isValidChildRes(parentRes int, childRes int) bool { 275 if childRes < parentRes || childRes > MAX_H3_RES { 276 return false 277 } 278 return true 279 } 280 281 /** 282 * maxH3ToChildrenSize returns the maximum number of children possible for a 283 * given child level. 284 * 285 * @param h to H3Index find the number of children of 286 * @param childRes The resolution of the child level you're interested in 287 * 288 * @return count int of maximum number of children (equal for hexagons, less for 289 * pentagons 290 */ 291 func maxH3ToChildrenSize(h H3Index, childRes int) int { 292 parentRes := H3_GET_RESOLUTION(h) 293 if !_isValidChildRes(parentRes, childRes) { 294 return 0 295 } 296 return _ipow(7, childRes-parentRes) 297 } 298 299 /** 300 * makeDirectChild takes an index and immediately returns the immediate child 301 * index based on the specified cell number. Bit operations only, could generate 302 * invalid indexes if not careful (deleted cell under a pentagon). 303 * 304 * @param h to H3Index find the direct child of 305 * @param cellNumber id int of the direct child (0-6) 306 * 307 * @return The new for H3Index the child 308 */ 309 func makeDirectChild(h H3Index, cellNumber Direction) H3Index { 310 childRes := H3_GET_RESOLUTION(h) + 1 311 childH := h 312 H3_SET_RESOLUTION(&childH, childRes) 313 H3_SET_INDEX_DIGIT(&childH, childRes, cellNumber) 314 return childH 315 } 316 317 /** 318 * h3ToChildren takes the given hexagon id and generates all of the children 319 * at the specified resolution storing them into the provided memory pointer. 320 * It's assumed that maxH3ToChildrenSize was used to determine the allocation. 321 * 322 * @param h to H3Index find the children of 323 * @param childRes the int child level to produce 324 * @param children H3Index* the memory to store the resulting addresses in 325 */ 326 func h3ToChildren(h H3Index, childRes int, children *[]H3Index) { 327 parentRes := H3_GET_RESOLUTION(h) 328 329 if !_isValidChildRes(parentRes, childRes) { 330 return 331 } 332 333 if parentRes == childRes { 334 *children = append(*children, h) 335 return 336 } 337 338 bufferSize := maxH3ToChildrenSize(h, childRes) 339 bufferChildStep := bufferSize / 7 340 isAPentagon := h3IsPentagon(h) 341 342 for i := Direction(0); i < 7; i++ { 343 if isAPentagon && i == K_AXES_DIGIT { 344 nextChild := make([]H3Index, bufferChildStep) 345 for len(*children) < len(nextChild) { 346 *children = append(*children, H3_INVALID_INDEX) 347 } 348 } else { 349 h3ToChildren(makeDirectChild(h, i), childRes, children) 350 } 351 } 352 } 353 354 /** 355 * h3ToCenterChild produces the center child index for a given H3 index at 356 * the specified resolution 357 * 358 * @param h to H3Index find center child of 359 * @param childRes The resolution to switch to 360 * 361 * @return of H3Index the center child, or 0 if you actually asked for a parent 362 */ 363 func h3ToCenterChild(h H3Index, childRes int) H3Index { 364 parentRes := H3_GET_RESOLUTION(h) 365 if !_isValidChildRes(parentRes, childRes) { 366 return H3_INVALID_INDEX 367 } 368 if childRes == parentRes { 369 return h 370 } 371 child := h 372 H3_SET_RESOLUTION(&child, childRes) 373 for i := parentRes + 1; i <= childRes; i++ { 374 H3_SET_INDEX_DIGIT(&child, i, 0) 375 } 376 return child 377 } 378 379 /** 380 * compact takes a set of hexagons all at the same resolution and compresses 381 * them by pruning full child branches to the parent level. This is also done 382 * for all parents recursively to get the minimum number of hex addresses that 383 * perfectly cover the defined space. 384 * @param h3Set Set of hexagons 385 * @param compactedSet The output array of compressed hexagons (preallocated) 386 * @param numHexes The size of the input and output arrays (possible that no 387 * contiguous regions exist in the set at all and no compression possible) 388 * @return an error code on bad input data 389 */ 390 func compact(h3Set []H3Index, compactedSet []H3Index, numHexes int) int { 391 if numHexes == 0 { 392 return 0 393 } 394 res := H3_GET_RESOLUTION(h3Set[0]) 395 if res == 0 { 396 // No compaction possible, just copy the set to output 397 for i := 0; i < numHexes; i++ { 398 compactedSet[i] = h3Set[i] 399 } 400 return 0 401 } 402 remainingHexes := make([]H3Index, numHexes) 403 copy(remainingHexes, h3Set) 404 405 hashSetArray := make([]H3Index, numHexes) 406 compactedSetOffset := compactedSet 407 numRemainingHexes := numHexes 408 for numRemainingHexes != 0 { 409 res = H3_GET_RESOLUTION(remainingHexes[0]) 410 parentRes := res - 1 411 // Put the parents of the hexagons into the temp array 412 // via a hashing mechanism, and use the reserved bits 413 // to track how many times a parent is duplicated 414 for i := 0; i < numRemainingHexes; i++ { 415 currIndex := remainingHexes[i] 416 if currIndex != 0 { 417 parent := h3ToParent(currIndex, parentRes) 418 // Modulus hash the parent into the temp array 419 loc := (int)(uint64(parent) % uint64(numRemainingHexes)) 420 loopCount := 0 421 for hashSetArray[loc] != 0 { 422 if loopCount > numRemainingHexes { // LCOV_EXCL_BR_LINE 423 // LCOV_EXCL_START 424 // This case should not be possible because at most one 425 // index is placed into hashSetArray per 426 // numRemainingHexes. 427 remainingHexes = nil 428 hashSetArray = nil 429 return -1 430 // LCOV_EXCL_STOP 431 } 432 tempIndex := H3Index(uint64(hashSetArray[loc]) & H3_RESERVED_MASK_NEGATIVE) 433 if tempIndex == parent { 434 count := H3_GET_RESERVED_BITS(hashSetArray[loc]) + 1 435 if count > 7 { 436 // Only possible on duplicate input 437 remainingHexes = nil 438 hashSetArray = nil 439 return -2 440 } 441 H3_SET_RESERVED_BITS(&parent, count) 442 hashSetArray[loc] = H3_INVALID_INDEX 443 } else { 444 loc = (loc + 1) % numRemainingHexes 445 } 446 loopCount++ 447 } 448 hashSetArray[loc] = parent 449 } 450 } 451 // Determine which parent hexagons have a complete set 452 // of children and put them in the compactableHexes array 453 compactableCount := 0 454 maxCompactableCount := numRemainingHexes / 6 // Somehow all pentagons; conservative 455 if maxCompactableCount == 0 { 456 copy(compactedSetOffset, remainingHexes) 457 break 458 } 459 460 compactableHexes := make([]H3Index, maxCompactableCount) 461 for i := 0; i < numRemainingHexes; i++ { 462 if hashSetArray[i] == 0 { 463 continue 464 } 465 count := H3_GET_RESERVED_BITS(hashSetArray[i]) + 1 466 // Include the deleted direction for pentagons as implicitly "there" 467 if h3IsPentagon(H3Index(uint64(hashSetArray[i]) & H3_RESERVED_MASK_NEGATIVE)) { 468 // We need this later on, no need to recalculate 469 H3_SET_RESERVED_BITS(&hashSetArray[i], count) 470 // Increment count after setting the reserved bits, 471 // since count is already incremented above, so it 472 // will be the expected value for a complete hexagon. 473 count++ 474 } 475 if count == 7 { 476 // Bingo! Full set! 477 compactableHexes[compactableCount] = H3Index(uint64(hashSetArray[i]) & H3_RESERVED_MASK_NEGATIVE) 478 compactableCount++ 479 } 480 } 481 // Uncompactable hexes are immediately copied into the 482 // output compactedSetOffset 483 uncompactableCount := 0 484 for i := 0; i < numRemainingHexes; i++ { 485 currIndex := remainingHexes[i] 486 if currIndex != H3_INVALID_INDEX { 487 parent := h3ToParent(currIndex, parentRes) 488 // Modulus hash the parent into the temp array 489 // to determine if this index was included in 490 // the compactableHexes array 491 loc := (int)(uint64(parent) % uint64(numRemainingHexes)) 492 loopCount := 0 493 isUncompactable := true 494 for { 495 if loopCount > numRemainingHexes { // LCOV_EXCL_BR_LINE 496 // LCOV_EXCL_START 497 // This case should not be possible because at most one 498 // index is placed into hashSetArray per input hexagon. 499 compactableHexes = nil 500 remainingHexes = nil 501 hashSetArray = nil 502 return -1 // Only possible on duplicate input 503 // LCOV_EXCL_STOP 504 } 505 tempIndex := H3Index(uint64(hashSetArray[loc]) & H3_RESERVED_MASK_NEGATIVE) 506 if tempIndex == parent { 507 count := H3_GET_RESERVED_BITS(hashSetArray[loc]) + 1 508 if count == 7 { 509 isUncompactable = false 510 } 511 break 512 } else { 513 loc = (loc + 1) % numRemainingHexes 514 } 515 loopCount++ 516 if hashSetArray[loc] != parent { 517 break 518 } 519 } 520 if isUncompactable { 521 compactedSetOffset[uncompactableCount] = remainingHexes[i] 522 uncompactableCount++ 523 } 524 } 525 } 526 // Set up for the next loop 527 //memset(hashSetArray, 0, numHexes * sizeof(H3Index)); 528 //compactedSetOffset += uncompactableCount; 529 530 copy(remainingHexes, compactableHexes) 531 numRemainingHexes = compactableCount 532 compactableHexes = nil 533 } 534 535 remainingHexes = nil 536 hashSetArray = nil 537 538 return 0 539 } 540 541 /** 542 * uncompact takes a compressed set of hexagons and expands back to the 543 * original set of hexagons. 544 * @param compactedSet Set of hexagons 545 * @param numHexes The number of hexes in the input set 546 * @param h3Set Output array of decompressed hexagons (preallocated) 547 * @param maxHexes The size of the output array to bound check against 548 * @param res The hexagon resolution to decompress to 549 * @return An error code if output array is too small or any hexagon is 550 * smaller than the output resolution. 551 */ 552 func uncompact(compactedSet []H3Index, numHexes int, h3Set []H3Index, maxHexes int, res int) int { 553 outOffset := 0 554 for i := 0; i < numHexes; i++ { 555 if compactedSet[i] == 0 { 556 continue 557 } 558 if outOffset >= maxHexes { 559 // We went too far, abort! 560 return -1 561 } 562 currentRes := H3_GET_RESOLUTION(compactedSet[i]) 563 if !_isValidChildRes(currentRes, res) { 564 // Nonsensical. Abort. 565 return -2 566 } 567 if currentRes == res { 568 // Just copy and move along 569 h3Set[outOffset] = compactedSet[i] 570 outOffset++ 571 } else { 572 // Bigger hexagon to reduce in size 573 numHexesToGen := maxH3ToChildrenSize(compactedSet[i], res) 574 if outOffset+numHexesToGen > maxHexes { 575 // We're about to go too far, abort! 576 return -1 577 } 578 // todo fix 579 //h3ToChildren(compactedSet[i], res, h3Set+outOffset) 580 outOffset += numHexesToGen 581 } 582 } 583 return 0 584 } 585 586 /** 587 * maxUncompactSize takes a compacted set of hexagons are provides an 588 * upper-bound estimate of the size of the uncompacted set of hexagons. 589 * @param compactedSet Set of hexagons 590 * @param numHexes The number of hexes in the input set 591 * @param res The hexagon resolution to decompress to 592 * @return The number of hexagons to allocate memory for, or a negative 593 * number if an error occurs. 594 */ 595 func maxUncompactSize(compactedSet []H3Index, numHexes int, res int) int { 596 maxNumHexagons := 0 597 for i := 0; i < numHexes; i++ { 598 if compactedSet[i] == 0 { 599 continue 600 } 601 602 currentRes := H3_GET_RESOLUTION(compactedSet[i]) 603 if !_isValidChildRes(currentRes, res) { 604 // Nonsensical. Abort. 605 return -1 606 } 607 if currentRes == res { 608 maxNumHexagons++ 609 } else { 610 // Bigger hexagon to reduce in size 611 numHexesToGen := maxH3ToChildrenSize(compactedSet[i], res) 612 maxNumHexagons += numHexesToGen 613 } 614 } 615 return maxNumHexagons 616 } 617 618 /** 619 * h3IsResClassIII takes a hexagon ID and determines if it is in a 620 * Class III resolution (rotated versus the icosahedron and subject 621 * to shape distortion adding extra points on icosahedron edges, making 622 * them not true hexagons). 623 * @param h The to H3Index check. 624 * @return Returns 1 if the hexagon is class III, otherwise 0. 625 */ 626 func h3IsResClassIII(h H3Index) bool { return H3_GET_RESOLUTION(h)%2 != 0 } 627 628 // 629 630 /** 631 * h3IsPentagon takes an and H3Index determines if it is actually a 632 * pentagon. 633 * @param h The to H3Index check. 634 * @return Returns 1 if it is a pentagon, otherwise 0. 635 */ 636 func h3IsPentagon(h H3Index) bool { 637 return _isBaseCellPentagon(H3_GET_BASE_CELL(h)) && _h3LeadingNonZeroDigit(h) == 0 638 } 639 640 /** 641 * Returns the highest resolution non-zero digit in an H3Index. 642 * @param h The H3Index. 643 * @return The highest resolution non-zero digit in the H3Index. 644 */ 645 func _h3LeadingNonZeroDigit(h H3Index) Direction { 646 for r := 1; r <= H3_GET_RESOLUTION(h); r++ { 647 d := H3_GET_INDEX_DIGIT(h, r) 648 if d != 0 { 649 return d 650 } 651 } 652 // if we're here it's all 0's 653 return CENTER_DIGIT 654 } 655 656 /** 657 * Rotate an 60 H3Index degrees counter-clockwise about a pentagonal center. 658 * @param h The H3Index. 659 */ 660 func _h3RotatePent60ccw(h H3Index) H3Index { 661 // rotate in place; skips any leading 1 digits (k-axis) 662 663 foundFirstNonZeroDigit := 0 664 res := H3_GET_RESOLUTION(h) 665 for r := 1; r <= res; r++ { 666 // rotate this digit 667 H3_SET_INDEX_DIGIT(&h, r, _rotate60ccw(H3_GET_INDEX_DIGIT(h, r))) 668 669 // look for the first non-zero digit so we 670 // can adjust for deleted k-axes sequence 671 // if necessary 672 if foundFirstNonZeroDigit == 0 && H3_GET_INDEX_DIGIT(h, r) != 0 { 673 foundFirstNonZeroDigit = 1 674 675 // adjust for deleted k-axes sequence 676 if _h3LeadingNonZeroDigit(h) == K_AXES_DIGIT { 677 h = _h3Rotate60ccw(h) 678 } 679 } 680 } 681 return h 682 } 683 684 /** 685 * Rotate an 60 H3Index degrees clockwise about a pentagonal center. 686 * @param h The H3Index. 687 */ 688 func _h3RotatePent60cw(h H3Index) H3Index { 689 // rotate in place; skips any leading 1 digits (k-axis) 690 foundFirstNonZeroDigit := 0 691 res := H3_GET_RESOLUTION(h) 692 for r := 1; r <= res; r++ { 693 // rotate this digit 694 H3_SET_INDEX_DIGIT(&h, r, _rotate60cw(H3_GET_INDEX_DIGIT(h, r))) 695 696 // look for the first non-zero digit so we 697 // can adjust for deleted k-axes sequence 698 // if necessary 699 if foundFirstNonZeroDigit == 0 && H3_GET_INDEX_DIGIT(h, r) != 0 { 700 foundFirstNonZeroDigit = 1 701 // adjust for deleted k-axes sequence 702 if _h3LeadingNonZeroDigit(h) == K_AXES_DIGIT { 703 h = _h3Rotate60cw(h) 704 } 705 } 706 } 707 return h 708 } 709 710 /** 711 * Rotate an 60 H3Index degrees counter-clockwise. 712 * @param h The H3Index. 713 */ 714 func _h3Rotate60ccw(h H3Index) H3Index { 715 res := H3_GET_RESOLUTION(h) 716 for r := 1; r <= res; r++ { 717 oldDigit := H3_GET_INDEX_DIGIT(h, r) 718 H3_SET_INDEX_DIGIT(&h, r, _rotate60ccw(oldDigit)) 719 } 720 721 return h 722 } 723 724 /** 725 * Rotate an 60 H3Index degrees clockwise. 726 * @param h The H3Index. 727 */ 728 func _h3Rotate60cw(h H3Index) H3Index { 729 res := H3_GET_RESOLUTION(h) 730 731 for r := 1; r <= res; r++ { 732 H3_SET_INDEX_DIGIT(&h, r, _rotate60cw(H3_GET_INDEX_DIGIT(h, r))) 733 } 734 735 return h 736 } 737 738 /** 739 * Convert an FaceIJK address to the corresponding H3Index. 740 * @param fijk The FaceIJK address. 741 * @param res The cell resolution. 742 * @return The encoded H3Index (or 0 on failure). 743 */ 744 func _faceIjkToH3(fijk *FaceIJK, res int) H3Index { 745 // initialize the index 746 h := H3_INIT 747 H3_SET_MODE(&h, H3_HEXAGON_MODE) 748 H3_SET_RESOLUTION(&h, res) 749 750 // check for res 0/base cell 751 if res == 0 { 752 if fijk.coord.i > MAX_FACE_COORD || fijk.coord.j > MAX_FACE_COORD || fijk.coord.k > MAX_FACE_COORD { 753 // out of range input 754 return H3_INVALID_INDEX 755 } 756 757 H3_SET_BASE_CELL(&h, _faceIjkToBaseCell(fijk)) 758 return h 759 } 760 761 // we need to find the correct base cell FaceIJK for this H3 index; 762 // start with the passed in face and resolution res ijk coordinates 763 // in that face's coordinate system 764 fijkBC := *fijk 765 766 // build the from H3Index finest res up 767 // adjust r for the fact that the res 0 base cell offsets the indexing 768 // digits 769 ijk := &fijkBC.coord 770 for r := res - 1; r >= 0; r-- { 771 lastIJK := *ijk 772 var lastCenter CoordIJK 773 if isResClassIII(r + 1) { 774 // rotate ccw 775 _upAp7(ijk) 776 lastCenter = *ijk 777 _downAp7(&lastCenter) 778 } else { 779 // rotate cw 780 _upAp7r(ijk) 781 lastCenter = *ijk 782 _downAp7r(&lastCenter) 783 } 784 785 var diff CoordIJK 786 _ijkSub(&lastIJK, &lastCenter, &diff) 787 _ijkNormalize(&diff) 788 H3_SET_INDEX_DIGIT(&h, r+1, _unitIjkToDigit(&diff)) 789 } 790 791 // fijkBC should now hold the IJK of the base cell in the 792 // coordinate system of the current face 793 794 if fijkBC.coord.i > MAX_FACE_COORD || fijkBC.coord.j > MAX_FACE_COORD || 795 fijkBC.coord.k > MAX_FACE_COORD { 796 // out of range input 797 return H3_INVALID_INDEX 798 } 799 800 // lookup the correct base cell 801 baseCell := _faceIjkToBaseCell(&fijkBC) 802 H3_SET_BASE_CELL(&h, baseCell) 803 804 // rotate if necessary to get canonical base cell orientation 805 // for this base cell 806 numRots := _faceIjkToBaseCellCCWrot60(&fijkBC) 807 if _isBaseCellPentagon(baseCell) { 808 // force rotation out of missing k-axes sub-sequence 809 if _h3LeadingNonZeroDigit(h) == K_AXES_DIGIT { 810 // check for a cw/ccw offset face; default is ccw 811 if _baseCellIsCwOffset(baseCell, fijkBC.face) { 812 h = _h3Rotate60cw(h) 813 } else { 814 h = _h3Rotate60ccw(h) 815 } 816 } 817 818 for i := 0; i < numRots; i++ { 819 h = _h3RotatePent60ccw(h) 820 } 821 } else { 822 for i := 0; i < numRots; i++ { 823 h = _h3Rotate60ccw(h) 824 } 825 } 826 827 return h 828 } 829 830 /** 831 * Encodes a coordinate on the sphere to the H3 index of the containing cell at 832 * the specified resolution. 833 * 834 * Returns 0 on invalid input. 835 * 836 * @param g The spherical coordinates to encode. 837 * @param res The desired H3 resolution for the encoding. 838 * @return The encoded H3Index (or 0 on failure). 839 */ 840 func geoToH3(g *GeoCoord, res int) H3Index { 841 if res < 0 || res > MAX_H3_RES { 842 return H3_INVALID_INDEX 843 } 844 845 if math.IsInf(g.Lat, 0) || math.IsInf(g.Lon, 0) { 846 return H3_INVALID_INDEX 847 } 848 849 fijk := FaceIJK{} 850 _geoToFaceIjk(g, res, &fijk) 851 return _faceIjkToH3(&fijk, res) 852 } 853 854 /** 855 * Convert an to H3Index the FaceIJK address on a specified icosahedral face. 856 * @param h The H3Index. 857 * @param fijk The FaceIJK address, initialized with the desired face 858 * and normalized base cell coordinates. 859 * @return Returns 1 if the possibility of overage exists, otherwise 0. 860 */ 861 func _h3ToFaceIjkWithInitializedFijk(h H3Index, fijk *FaceIJK) int { 862 ijk := &fijk.coord 863 res := H3_GET_RESOLUTION(h) 864 865 // center base cell hierarchy is entirely on this face 866 possibleOverage := 1 867 if !_isBaseCellPentagon(H3_GET_BASE_CELL(h)) && (res == 0 || (fijk.coord.i == 0 && fijk.coord.j == 0 && fijk.coord.k == 0)) { 868 possibleOverage = 0 869 } 870 for r := 1; r <= res; r++ { 871 if isResClassIII(r) { 872 // Class III == rotate ccw 873 _downAp7(ijk) 874 } else { 875 // Class II == rotate cw 876 _downAp7r(ijk) 877 } 878 879 _neighbor(ijk, H3_GET_INDEX_DIGIT(h, r)) 880 } 881 882 return possibleOverage 883 } 884 885 /** 886 * Convert an to H3Index a FaceIJK address. 887 * @param h The H3Index. 888 * @param fijk The corresponding FaceIJK address. 889 */ 890 func _h3ToFaceIjk(h H3Index, fijk *FaceIJK) { 891 baseCell := H3_GET_BASE_CELL(h) 892 // adjust for the pentagonal missing sequence; all of sub-sequence 5 needs 893 // to be adjusted (and some of sub-sequence 4 below) 894 if _isBaseCellPentagon(baseCell) && _h3LeadingNonZeroDigit(h) == 5 { 895 h = _h3Rotate60cw(h) 896 } 897 898 // start with the "home" face and ijk+ coordinates for the base cell of c 899 *fijk = baseCellData[baseCell].homeFijk 900 901 if _h3ToFaceIjkWithInitializedFijk(h, fijk) == 0 { 902 return // no overage is possible; h lies on this face 903 } 904 905 // if we're here we have the potential for an "overage"; i.e., it is 906 // possible that c lies on an adjacent face 907 908 origIJK := fijk.coord 909 910 // if we're in Class III, drop into the next finer Class II grid 911 res := H3_GET_RESOLUTION(h) 912 if isResClassIII(res) { 913 // Class III 914 _downAp7r(&fijk.coord) 915 res++ 916 } 917 918 // adjust for overage if needed 919 // a pentagon base cell with a leading 4 digit requires special handling 920 pentLeading4 := 0 921 922 if _isBaseCellPentagon(baseCell) && _h3LeadingNonZeroDigit(h) == 4 { 923 pentLeading4 = 1 924 } 925 926 if _adjustOverageClassII(fijk, res, pentLeading4, 0) != NO_OVERAGE { 927 // if the base cell is a pentagon we have the potential for secondary 928 // overages 929 if _isBaseCellPentagon(baseCell) { 930 for _adjustOverageClassII(fijk, res, 0, 0) != NO_OVERAGE { 931 932 } 933 } 934 935 if res != H3_GET_RESOLUTION(h) { 936 _upAp7r(&fijk.coord) 937 } 938 939 } else if res != H3_GET_RESOLUTION(h) { 940 fijk.coord = origIJK 941 } 942 } 943 944 /** 945 * Determines the spherical coordinates of the center poof int an H3 index. 946 * 947 * @param h3 The H3 index. 948 * @param g The spherical coordinates of the H3 cell center. 949 */ 950 func h3ToGeo(h3 H3Index, g *GeoCoord) { 951 var fijk FaceIJK 952 _h3ToFaceIjk(h3, &fijk) 953 _faceIjkToGeo(&fijk, H3_GET_RESOLUTION(h3), g) 954 } 955 956 /** 957 * Determines the cell boundary in spherical coordinates for an H3 index. 958 * 959 * @param h3 The H3 index. 960 * @param gb The boundary of the H3 cell in spherical coordinates. 961 */ 962 func h3ToGeoBoundary(h3 H3Index, gb *GeoBoundary) { 963 var fijk FaceIJK 964 _h3ToFaceIjk(h3, &fijk) 965 _faceIjkToGeoBoundary(&fijk, H3_GET_RESOLUTION(h3), h3IsPentagon(h3), gb) 966 } 967 968 /** 969 * Returns the max number of possible icosahedron faces an H3 index 970 * may intersect. 971 * 972 * @return count int of faces 973 */ 974 func maxFaceCount(h3 H3Index) int { 975 // a pentagon always intersects 5 faces, a hexagon never intersects more 976 // than 2 (but may only intersect 1) 977 if h3IsPentagon(h3) { 978 return 5 979 } 980 return 2 981 } 982 983 /** 984 * Find all icosahedron faces intersected by a given H3 index, represented 985 * as integers from 0-19. The array is sparse; since 0 is a valid value, 986 * invalid array values are represented as -1. It is the responsibility of 987 * the caller to filter out invalid values. 988 * 989 * @param h3 The H3 index 990 * @param out Output array. Must be of size maxFaceCount(h3). 991 */ 992 func h3GetFaces(h3 H3Index, out []int) { 993 res := H3_GET_RESOLUTION(h3) 994 isPentagon := h3IsPentagon(h3) 995 996 // We can't use the vertex-based approach here for class II pentagons, 997 // because all their vertices are on the icosahedron edges. Their 998 // direct child pentagons cross the same faces, so use those instead. 999 if isPentagon && !isResClassIII(res) { 1000 // Note that this would not work for res 15, but this is only run on 1001 // Class II pentagons, it should never be invoked for a res 15 index. 1002 childPentagon := makeDirectChild(h3, 0) 1003 h3GetFaces(childPentagon, out) 1004 return 1005 } 1006 1007 // convert to FaceIJK 1008 var fijk FaceIJK 1009 _h3ToFaceIjk(h3, &fijk) 1010 1011 // Get all vertices as FaceIJK addresses. For simplicity, always 1012 // initialize the array with 6 Verts, ignoring the last one for pentagons 1013 var fijkVerts []FaceIJK 1014 var vertexCount int 1015 1016 if isPentagon { 1017 vertexCount = NUM_PENT_VERTS 1018 _faceIjkPentToVerts(&fijk, &res, fijkVerts) 1019 } else { 1020 vertexCount = NUM_HEX_VERTS 1021 _faceIjkToVerts(&fijk, &res, fijkVerts) 1022 } 1023 1024 // We may not use all of the slots in the output array, 1025 // so fill with invalid values to indicate unused slots 1026 faceCount := maxFaceCount(h3) 1027 for i := 0; i < faceCount; i++ { 1028 out[i] = INVALID_FACE 1029 } 1030 1031 // add each vertex face, using the output array as a hash set 1032 for i := 0; i < vertexCount; i++ { 1033 vert := &fijkVerts[i] 1034 1035 // Adjust overage, determining whether this vertex is 1036 // on another face 1037 if isPentagon { 1038 _adjustPentVertOverage(vert, res) 1039 } else { 1040 _adjustOverageClassII(vert, res, 0, 1) 1041 } 1042 1043 // Save the face to the output array 1044 face := vert.face 1045 pos := 0 1046 // Find the first empty output position, or the first position 1047 // matching the current face 1048 for out[pos] != INVALID_FACE && out[pos] != face { 1049 pos++ 1050 } 1051 out[pos] = face 1052 } 1053 } 1054 1055 /** 1056 * pentagonIndexCount returns the number of pentagons (same at any resolution) 1057 * 1058 * @return count int of pentagon indexes 1059 */ 1060 func pentagonIndexCount() int { 1061 return NUM_PENTAGONS 1062 } 1063 1064 /** 1065 * Generates all pentagons at the specified resolution 1066 * 1067 * @param res The resolution to produce pentagons at. 1068 * @param out Output array. Must be of size pentagonIndexCount(). 1069 */ 1070 func getPentagonIndexes(res int, out *[]H3Index) { 1071 for bc := 0; bc < NUM_BASE_CELLS; bc++ { 1072 if _isBaseCellPentagon(bc) { 1073 var pentagon H3Index 1074 setH3Index(&pentagon, res, bc, 0) 1075 *out = append(*out, pentagon) 1076 } 1077 } 1078 } 1079 1080 /** 1081 * Returns whether or not a resolution is a Class III grid. Note that odd 1082 * resolutions are Class III and even resolutions are Class II. 1083 * @param res The H3 resolution. 1084 * @return 1 if the resolution is a Class III grid, and 0 if the resolution is 1085 * a Class II grid. 1086 */ 1087 func isResClassIII(res int) bool { 1088 return res%2 == 1 1089 }