ext/h3/src/src/h3lib/lib/h3Index.c in h3-3.6.2 vs ext/h3/src/src/h3lib/lib/h3Index.c in h3-3.7.1

- old
+ new

@@ -16,15 +16,18 @@ /** @file h3Index.c * @brief H3Index utility functions * (see h3api.h for the main library entry functions) */ #include "h3Index.h" + #include <faceijk.h> #include <inttypes.h> #include <math.h> #include <stdlib.h> #include <string.h> + +#include "alloc.h" #include "baseCells.h" #include "faceijk.h" #include "mathExtensions.h" /** @@ -33,24 +36,29 @@ * @return The resolution of the H3 index argument. */ int H3_EXPORT(h3GetResolution)(H3Index h) { return H3_GET_RESOLUTION(h); } /** - * Returns the H3 base cell number of an H3 index. - * @param h The H3 index. - * @return The base cell of the H3 index argument. + * Returns the H3 base cell "number" of an H3 cell (hexagon or pentagon). + * + * Note: Technically works on H3 edges, but will return base cell of the + * origin cell. + * + * @param h The H3 cell. + * @return The base cell "number" of the H3 cell argument. */ int H3_EXPORT(h3GetBaseCell)(H3Index h) { return H3_GET_BASE_CELL(h); } /** * Converts a string representation of an H3 index into an H3 index. * @param str The string representation of an H3 index. - * @return The H3 index corresponding to the string argument, or 0 if invalid. + * @return The H3 index corresponding to the string argument, or H3_NULL if + * invalid. */ H3Index H3_EXPORT(stringToH3)(const char* str) { - H3Index h = H3_INVALID_INDEX; - // If failed, h will be unmodified and we should return 0 anyways. + H3Index h = H3_NULL; + // If failed, h will be unmodified and we should return H3_NULL anyways. sscanf(str, "%" PRIx64, &h); return h; } /** @@ -68,17 +76,21 @@ } sprintf(str, "%" PRIx64, h); } /** - * Returns whether or not an H3 index is valid. + * Returns whether or not an H3 index is a valid cell (hexagon or pentagon). * @param h The H3 index to validate. * @return 1 if the H3 index if valid, and 0 if it is not. */ int H3_EXPORT(h3IsValid)(H3Index h) { + if (H3_GET_HIGH_BIT(h) != 0) return 0; + if (H3_GET_MODE(h) != H3_HEXAGON_MODE) return 0; + if (H3_GET_RESERVED_BITS(h) != 0) return 0; + int baseCell = H3_GET_BASE_CELL(h); if (baseCell < 0 || baseCell >= NUM_BASE_CELLS) return 0; int res = H3_GET_RESOLUTION(h); if (res < 0 || res > MAX_H3_RES) return 0; @@ -125,20 +137,20 @@ * h3ToParent produces the parent index for a given H3 index * * @param h H3Index to find parent of * @param parentRes The resolution to switch to (parent, grandparent, etc) * - * @return H3Index of the parent, or 0 if you actually asked for a child + * @return H3Index of the parent, or H3_NULL if you actually asked for a child */ H3Index H3_EXPORT(h3ToParent)(H3Index h, int parentRes) { int childRes = H3_GET_RESOLUTION(h); if (parentRes > childRes) { - return H3_INVALID_INDEX; + return H3_NULL; } else if (parentRes == childRes) { return h; } else if (parentRes < 0 || parentRes > MAX_H3_RES) { - return H3_INVALID_INDEX; + return H3_NULL; } H3Index parentH = H3_SET_RESOLUTION(h, parentRes); for (int i = parentRes + 1; i <= childRes; i++) { H3_SET_INDEX_DIGIT(parentH, i, H3_DIGIT_MASK); } @@ -218,11 +230,11 @@ int isAPentagon = H3_EXPORT(h3IsPentagon)(h); for (int i = 0; i < 7; i++) { if (isAPentagon && i == K_AXES_DIGIT) { H3Index* nextChild = children + bufferChildStep; while (children < nextChild) { - *children = H3_INVALID_INDEX; + *children = H3_NULL; children++; } } else { H3_EXPORT(h3ToChildren)(makeDirectChild(h, i), childRes, children); children += bufferChildStep; @@ -235,16 +247,17 @@ * the specified resolution * * @param h H3Index to find center child of * @param childRes The resolution to switch to * - * @return H3Index of the center child, or 0 if you actually asked for a parent + * @return H3Index of the center child, or H3_NULL if you actually asked for a + * parent */ H3Index H3_EXPORT(h3ToCenterChild)(H3Index h, int childRes) { int parentRes = H3_GET_RESOLUTION(h); if (!_isValidChildRes(parentRes, childRes)) { - return H3_INVALID_INDEX; + return H3_NULL; } else if (childRes == parentRes) { return h; } H3Index child = H3_SET_RESOLUTION(h, childRes); for (int i = parentRes + 1; i <= childRes; i++) { @@ -265,23 +278,30 @@ * @return an error code on bad input data */ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet, const int numHexes) { if (numHexes == 0) { - return 0; + return COMPACT_SUCCESS; } int res = H3_GET_RESOLUTION(h3Set[0]); if (res == 0) { // No compaction possible, just copy the set to output for (int i = 0; i < numHexes; i++) { compactedSet[i] = h3Set[i]; } - return 0; + return COMPACT_SUCCESS; } - H3Index* remainingHexes = malloc(numHexes * sizeof(H3Index)); + H3Index* remainingHexes = H3_MEMORY(malloc)(numHexes * sizeof(H3Index)); + if (!remainingHexes) { + return COMPACT_ALLOC_FAILED; + } memcpy(remainingHexes, h3Set, numHexes * sizeof(H3Index)); - H3Index* hashSetArray = calloc(numHexes, sizeof(H3Index)); + H3Index* hashSetArray = H3_MEMORY(calloc)(numHexes, sizeof(H3Index)); + if (!hashSetArray) { + H3_MEMORY(free)(remainingHexes); + return COMPACT_ALLOC_FAILED; + } H3Index* compactedSetOffset = compactedSet; int numRemainingHexes = numHexes; while (numRemainingHexes) { res = H3_GET_RESOLUTION(remainingHexes[0]); int parentRes = res - 1; @@ -299,27 +319,35 @@ if (loopCount > numRemainingHexes) { // LCOV_EXCL_BR_LINE // LCOV_EXCL_START // This case should not be possible because at most one // index is placed into hashSetArray per // numRemainingHexes. - free(remainingHexes); - free(hashSetArray); - return -1; + H3_MEMORY(free)(remainingHexes); + H3_MEMORY(free)(hashSetArray); + return COMPACT_LOOP_EXCEEDED; // LCOV_EXCL_STOP } H3Index tempIndex = hashSetArray[loc] & H3_RESERVED_MASK_NEGATIVE; if (tempIndex == parent) { int count = H3_GET_RESERVED_BITS(hashSetArray[loc]) + 1; - if (count > 7) { + int limitCount = 7; + if (H3_EXPORT(h3IsPentagon)( + tempIndex & H3_RESERVED_MASK_NEGATIVE)) { + limitCount--; + } + // One is added to count for this check to match one + // being added to count later in this function when + // checking for all children being present. + if (count + 1 > limitCount) { // Only possible on duplicate input - free(remainingHexes); - free(hashSetArray); - return -2; + H3_MEMORY(free)(remainingHexes); + H3_MEMORY(free)(hashSetArray); + return COMPACT_DUPLICATE; } H3_SET_RESERVED_BITS(parent, count); - hashSetArray[loc] = H3_INVALID_INDEX; + hashSetArray[loc] = H3_NULL; } else { loc = (loc + 1) % numRemainingHexes; } loopCount++; } @@ -335,11 +363,16 @@ memcpy(compactedSetOffset, remainingHexes, numRemainingHexes * sizeof(remainingHexes[0])); break; } H3Index* compactableHexes = - malloc(maxCompactableCount * sizeof(H3Index)); + H3_MEMORY(calloc)(maxCompactableCount, sizeof(H3Index)); + if (!compactableHexes) { + H3_MEMORY(free)(remainingHexes); + H3_MEMORY(free)(hashSetArray); + return COMPACT_ALLOC_FAILED; + } for (int i = 0; i < numRemainingHexes; i++) { if (hashSetArray[i] == 0) continue; int count = H3_GET_RESERVED_BITS(hashSetArray[i]) + 1; // Include the deleted direction for pentagons as implicitly "there" if (H3_EXPORT(h3IsPentagon)(hashSetArray[i] & @@ -361,11 +394,11 @@ // Uncompactable hexes are immediately copied into the // output compactedSetOffset int uncompactableCount = 0; for (int i = 0; i < numRemainingHexes; i++) { H3Index currIndex = remainingHexes[i]; - if (currIndex != H3_INVALID_INDEX) { + if (currIndex != H3_NULL) { H3Index parent = H3_EXPORT(h3ToParent)(currIndex, parentRes); // Modulus hash the parent into the temp array // to determine if this index was included in // the compactableHexes array int loc = (int)(parent % numRemainingHexes); @@ -374,14 +407,14 @@ do { if (loopCount > numRemainingHexes) { // LCOV_EXCL_BR_LINE // LCOV_EXCL_START // This case should not be possible because at most one // index is placed into hashSetArray per input hexagon. - free(compactableHexes); - free(remainingHexes); - free(hashSetArray); - return -1; // Only possible on duplicate input + H3_MEMORY(free)(compactableHexes); + H3_MEMORY(free)(remainingHexes); + H3_MEMORY(free)(hashSetArray); + return COMPACT_LOOP_EXCEEDED; // LCOV_EXCL_STOP } H3Index tempIndex = hashSetArray[loc] & H3_RESERVED_MASK_NEGATIVE; if (tempIndex == parent) { @@ -405,15 +438,15 @@ memset(hashSetArray, 0, numHexes * sizeof(H3Index)); compactedSetOffset += uncompactableCount; memcpy(remainingHexes, compactableHexes, compactableCount * sizeof(H3Index)); numRemainingHexes = compactableCount; - free(compactableHexes); + H3_MEMORY(free)(compactableHexes); } - free(remainingHexes); - free(hashSetArray); - return 0; + H3_MEMORY(free)(remainingHexes); + H3_MEMORY(free)(hashSetArray); + return COMPACT_SUCCESS; } /** * uncompact takes a compressed set of hexagons and expands back to the * original set of hexagons. @@ -601,11 +634,11 @@ /** * Convert an FaceIJK address to the corresponding H3Index. * @param fijk The FaceIJK address. * @param res The cell resolution. - * @return The encoded H3Index (or 0 on failure). + * @return The encoded H3Index (or H3_NULL on failure). */ H3Index _faceIjkToH3(const FaceIJK* fijk, int res) { // initialize the index H3Index h = H3_INIT; H3_SET_MODE(h, H3_HEXAGON_MODE); @@ -614,11 +647,11 @@ // check for res 0/base cell if (res == 0) { if (fijk->coord.i > MAX_FACE_COORD || fijk->coord.j > MAX_FACE_COORD || fijk->coord.k > MAX_FACE_COORD) { // out of range input - return H3_INVALID_INDEX; + return H3_NULL; } H3_SET_BASE_CELL(h, _faceIjkToBaseCell(fijk)); return h; } @@ -658,11 +691,11 @@ // coordinate system of the current face if (fijkBC.coord.i > MAX_FACE_COORD || fijkBC.coord.j > MAX_FACE_COORD || fijkBC.coord.k > MAX_FACE_COORD) { // out of range input - return H3_INVALID_INDEX; + return H3_NULL; } // lookup the correct base cell int baseCell = _faceIjkToBaseCell(&fijkBC); H3_SET_BASE_CELL(h, baseCell); @@ -697,18 +730,18 @@ * * Returns 0 on invalid input. * * @param g The spherical coordinates to encode. * @param res The desired H3 resolution for the encoding. - * @return The encoded H3Index (or 0 on failure). + * @return The encoded H3Index (or H3_NULL on failure). */ H3Index H3_EXPORT(geoToH3)(const GeoCoord* g, int res) { if (res < 0 || res > MAX_H3_RES) { - return H3_INVALID_INDEX; + return H3_NULL; } if (!isfinite(g->lat) || !isfinite(g->lon)) { - return H3_INVALID_INDEX; + return H3_NULL; } FaceIJK fijk; _geoToFaceIjk(g, res, &fijk); return _faceIjkToH3(&fijk, res); @@ -814,11 +847,16 @@ * @param gb The boundary of the H3 cell in spherical coordinates. */ void H3_EXPORT(h3ToGeoBoundary)(H3Index h3, GeoBoundary* gb) { FaceIJK fijk; _h3ToFaceIjk(h3, &fijk); - _faceIjkToGeoBoundary(&fijk, H3_GET_RESOLUTION(h3), - H3_EXPORT(h3IsPentagon)(h3), gb); + if (H3_EXPORT(h3IsPentagon)(h3)) { + _faceIjkPentToGeoBoundary(&fijk, H3_GET_RESOLUTION(h3), 0, + NUM_PENT_VERTS, gb); + } else { + _faceIjkToGeoBoundary(&fijk, H3_GET_RESOLUTION(h3), 0, NUM_HEX_VERTS, + gb); + } } /** * Returns the max number of possible icosahedron faces an H3 index * may intersect.