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  }