diff --git a/CMakeLists.txt b/CMakeLists.txt index 19b4ddb8d..aea5828a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -303,7 +303,6 @@ if(ENABLE_TESTS) add_h3_test(testH3ToChildren src/apps/testapps/testH3ToChildren.c) add_h3_test(testMaxH3ToChildrenSize src/apps/testapps/testMaxH3ToChildrenSize.c) add_h3_test(testH3Index src/apps/testapps/testH3Index.c) - add_h3_test(testH3IndexFat src/apps/testapps/testH3IndexFat.c) add_h3_test(testH3Api src/apps/testapps/testH3Api.c) add_h3_test(testH3SetToLinkedGeo src/apps/testapps/testH3SetToLinkedGeo.c) add_h3_test(testH3SetToVertexGraph src/apps/testapps/testH3SetToVertexGraph.c) diff --git a/docs/doxyfiles/geoToH3desc.md b/docs/doxyfiles/geoToH3desc.md index c32536c20..9bcf662b7 100644 --- a/docs/doxyfiles/geoToH3desc.md +++ b/docs/doxyfiles/geoToH3desc.md @@ -7,10 +7,8 @@ The conversion is performed as a series of coordinate system conversions describ 1. The input latitude/longitude coordinate is first converted into the containing icosahedron face and a _Hex2d_ coordinate on that face using function ::\_geoToHex2d, which determines the correct face and then performs a face-centered gnomonic projection into face-centered polar coordinates. These polar coordinates are then scaled appropriately to a _Hex2d_ coordinate on the input grid resolution _r_. 2. The _Hex2d_ coordinate is converted into resolution _r_ normalized _ijk_ coordinates using function ::\_hex2dToCoordIJK. -3. The face and face-centered _ijk_ coordinates are then converted into an `H3IndexFat` representation using the following steps: +3. The face and face-centered _ijk_ coordinates are then converted into an `H3Index` representation using the following steps: * the _H3_ index digits are calculated from resolution _r_ up to 0, adjusting the _ijk_ coordinates at each successively coarser resolution. * when resolution 0 is reached, if the remaining _ijk_ coordinates are (0,0,0) then the base cell centered on the face is chosen for the index * if the remaining resolution 0 _ijk_ coordinates are not (0,0,0), then a lookup operation is performed to find the appropriate base cell and the required rotation (if any) to orient the cell in that base cell's coordinate system. The index is then translated and rotated into the coordinate system centered on the new base cell. - -4. The `H3FatIndex` representation is converted into an `H3Index` representation using function ::h3FatToH3. diff --git a/docs/doxyfiles/h3ToGeoBoundaryDesc.md b/docs/doxyfiles/h3ToGeoBoundaryDesc.md index 2d7606992..4c305901c 100644 --- a/docs/doxyfiles/h3ToGeoBoundaryDesc.md +++ b/docs/doxyfiles/h3ToGeoBoundaryDesc.md @@ -5,7 +5,6 @@ This operation is performed by function ::h3ToGeoBoundary. See the comments in t The conversion is performed as a series of coordinate system conversions described below. See the page Coordinate Systems used by the __H3 Core Library__ for more information on each of these coordinate systems. -* The `H3Index` representation is converted into an `H3FatIndex` representation using function ::h3ToH3Fat. * We note that the cell vertices are the center points of cells in an aperture 3 grid one resolution finer than the cell resolution, which we term a _substrate_ grid. We precalculate the substrate _ijk_ coordinates of a cell with _ijk_ coordinates (0,0,0), adding additional aperture 3 and aperture 7 (if required, by Class III cell grid) substrate grid resolutions as required to transform the vertex coordinates into a Class II substrate grid. \image html substrate3.png diff --git a/docs/doxyfiles/h3ToGeoDesc.md b/docs/doxyfiles/h3ToGeoDesc.md index 0a9c71780..42069f271 100644 --- a/docs/doxyfiles/h3ToGeoDesc.md +++ b/docs/doxyfiles/h3ToGeoDesc.md @@ -5,8 +5,7 @@ This operation is performed by function ::h3ToGeo. See the comments in the funct The conversion is performed as a series of coordinate system conversions described below. See the page Coordinate Systems used by the __H3 Core Library__ for more information on each of these coordinate systems. -* The `H3Index` representation is converted into an `H3FatIndex` representation using function ::h3ToH3Fat. -* The function ::\_h3FatToFaceIjk then converts the _H3_ index to the appropriate icosahedron face number and normalized _ijk_ coordinate's on that face's coordinate system as follows: +* The function ::\_h3ToFaceIjk then converts the _H3_ index to the appropriate icosahedron face number and normalized _ijk_ coordinate's on that face's coordinate system as follows: * We start by assuming that the cell center point falls on the same icosahedron face as its base cell. * It is possible that the the cell center point lies on an adjacent face (termed an _overage_ in the code), in which case we would need to use a projection centered on that adjacent face instead. We recall that normalized _ijk_ coordinates have at most two non-zero components, and that in a face-centered Class II system the sum of those components is a resolution-specific constant value for cells that lie on the edge of that icosahedral face. We determine whether an overage exists by taking the sum of the _ijk_ components, and if there is an overage the positive _ijk_ components indicate which adjacent face the cell center lies on. A lookup operation is then performed to find the appropriate rotation and translation to transform the _ijk_ coordinates into the adjacent face-centered _ijk_ system. diff --git a/docs/doxyfiles/h3indexing.md b/docs/doxyfiles/h3indexing.md index be8fa1e68..a920a7490 100644 --- a/docs/doxyfiles/h3indexing.md +++ b/docs/doxyfiles/h3indexing.md @@ -97,8 +97,3 @@ The layout of an __H3Index__ is shown below in table form. The interpretation of Digit 15 - -H3IndexFat Representation ---- - -The __H3 Core Library__ sometimes manipulates __H3__ indexes internally in the form of the structure type __H3IndexFat__. This heavyweight representation stores an integer mode, base cell and resolution, along with an array of integer digits. It is not meant to be used outside of the C library. diff --git a/src/apps/filters/h3ToGeoBoundary.c b/src/apps/filters/h3ToGeoBoundary.c index f7f661bee..130959d94 100644 --- a/src/apps/filters/h3ToGeoBoundary.c +++ b/src/apps/filters/h3ToGeoBoundary.c @@ -49,7 +49,6 @@ #include "coordijk.h" #include "geoCoord.h" #include "h3Index.h" -#include "h3IndexFat.h" #include "h3api.h" #include "kml.h" #include "utility.h" diff --git a/src/apps/miscapps/generateBaseCellNeighbors.c b/src/apps/miscapps/generateBaseCellNeighbors.c index 32a7978a1..42fa49e2a 100644 --- a/src/apps/miscapps/generateBaseCellNeighbors.c +++ b/src/apps/miscapps/generateBaseCellNeighbors.c @@ -26,7 +26,6 @@ */ #include -#include #include const int NUM_DIRS = 6; diff --git a/src/apps/miscapps/h3ToGeoBoundaryHier.c b/src/apps/miscapps/h3ToGeoBoundaryHier.c index 90b0a4d22..f7581b617 100644 --- a/src/apps/miscapps/h3ToGeoBoundaryHier.c +++ b/src/apps/miscapps/h3ToGeoBoundaryHier.c @@ -54,15 +54,12 @@ #include "coordijk.h" #include "geoCoord.h" #include "h3Index.h" -#include "h3IndexFat.h" #include "h3api.h" #include "kml.h" #include "utility.h" #include "vec2d.h" -void doCell(const H3IndexFat* hf, int isKmlOut) { - H3Index h = h3FatToH3(hf); - +void doCell(H3Index h, int isKmlOut) { GeoBoundary b; H3_EXPORT(h3ToGeoBoundary)(h, &b); @@ -77,19 +74,22 @@ void doCell(const H3IndexFat* hf, int isKmlOut) { } } -void recursiveH3IndexToGeo(H3IndexFat* c, int res, int isKmlOut) { +void recursiveH3IndexToGeo(H3Index h, int res, int isKmlOut) { for (int d = 0; d < 7; d++) { - c->index[res - 1] = d; + H3_SET_INDEX_DIGIT(h, res, d); // skip the pentagonal deleted subsequence - if (_isBaseCellPentagon(c->baseCell) && _leadingNonZeroDigit(c) == 1) + if (_isBaseCellPentagon(H3_GET_BASE_CELL(h)) && + _h3LeadingNonZeroDigit(h) == 1) { continue; + } - if (res == c->res) - doCell(c, isKmlOut); - else - recursiveH3IndexToGeo(c, res + 1, isKmlOut); + if (res == H3_GET_RESOLUTION(h)) { + doCell(h, isKmlOut); + } else { + recursiveH3IndexToGeo(h, res + 1, isKmlOut); + } } } @@ -101,10 +101,11 @@ int main(int argc, char* argv[]) { } H3Index rootCell = H3_EXPORT(stringToH3)(argv[1]); - H3IndexFat rootCellHf; - h3ToH3Fat(rootCell, &rootCellHf); - if (rootCellHf.baseCell < 0 || rootCellHf.baseCell >= NUM_BASE_CELLS) + int baseCell = H3_GET_BASE_CELL(rootCell); + int rootRes = H3_GET_RESOLUTION(rootCell); + if (baseCell < 0 || baseCell >= NUM_BASE_CELLS) { error("invalid base cell number"); + } int res = 0; int isKmlOut = 0; @@ -129,7 +130,7 @@ int main(int argc, char* argv[]) { H3_EXPORT(h3ToString)(rootCell, index, BUFF_SIZE); sprintf(name, "Cell %s Res %d", index, - ((res <= rootCellHf.res) ? rootCellHf.res : res)); + ((res <= rootRes) ? rootRes : res)); sprintf(desc, "cell boundary"); kmlBoundaryHeader(name, desc); @@ -139,12 +140,11 @@ int main(int argc, char* argv[]) { // generate the points - if (res <= rootCellHf.res) { - doCell(&rootCellHf, isKmlOut); + if (res <= rootRes) { + doCell(rootCell, isKmlOut); } else { - int rootRes = rootCellHf.res; - rootCellHf.res = res; - recursiveH3IndexToGeo(&rootCellHf, rootRes + 1, isKmlOut); + H3_SET_RESOLUTION(rootCell, res); + recursiveH3IndexToGeo(rootCell, rootRes + 1, isKmlOut); } if (isKmlOut) kmlBoundaryFooter(); diff --git a/src/apps/miscapps/h3ToGeoHier.c b/src/apps/miscapps/h3ToGeoHier.c index 9fc83ca09..579b57a5d 100644 --- a/src/apps/miscapps/h3ToGeoHier.c +++ b/src/apps/miscapps/h3ToGeoHier.c @@ -55,15 +55,12 @@ #include "coordijk.h" #include "geoCoord.h" #include "h3Index.h" -#include "h3IndexFat.h" #include "h3api.h" #include "kml.h" #include "utility.h" #include "vec2d.h" -void doCell(const H3IndexFat* hf, int isKmlOut) { - H3Index h = h3FatToH3(hf); - +void doCell(H3Index h, int isKmlOut) { GeoCoord g; H3_EXPORT(h3ToGeo)(h, &g); @@ -78,19 +75,22 @@ void doCell(const H3IndexFat* hf, int isKmlOut) { } } -void recursiveH3IndexToGeo(H3IndexFat* c, int res, int isKmlOut) { +void recursiveH3IndexToGeo(H3Index h, int res, int isKmlOut) { for (int d = 0; d < 7; d++) { - c->index[res - 1] = d; + H3_SET_INDEX_DIGIT(h, res, d); // skip the pentagonal deleted subsequence - if (_isBaseCellPentagon(c->baseCell) && _leadingNonZeroDigit(c) == 1) + if (_isBaseCellPentagon(H3_GET_BASE_CELL(h)) && + _h3LeadingNonZeroDigit(h) == 1) { continue; + } - if (res == c->res) - doCell(c, isKmlOut); - else - recursiveH3IndexToGeo(c, res + 1, isKmlOut); + if (res == H3_GET_RESOLUTION(h)) { + doCell(h, isKmlOut); + } else { + recursiveH3IndexToGeo(h, res + 1, isKmlOut); + } } } @@ -102,10 +102,11 @@ int main(int argc, char* argv[]) { } H3Index rootCell = H3_EXPORT(stringToH3)(argv[1]); - H3IndexFat rootCellHf; - h3ToH3Fat(rootCell, &rootCellHf); - if (rootCellHf.baseCell < 0 || rootCellHf.baseCell >= NUM_BASE_CELLS) + int baseCell = H3_GET_BASE_CELL(rootCell); + int rootRes = H3_GET_RESOLUTION(rootCell); + if (baseCell < 0 || baseCell >= NUM_BASE_CELLS) { error("invalid base cell number"); + } int res = 0; int isKmlOut = 0; @@ -130,7 +131,7 @@ int main(int argc, char* argv[]) { H3_EXPORT(h3ToString)(rootCell, index, BUFF_SIZE); sprintf(name, "Cell %s Res %d", index, - ((res <= rootCellHf.res) ? rootCellHf.res : res)); + ((res <= rootRes) ? rootRes : res)); sprintf(desc, "cell boundary"); kmlBoundaryHeader(name, desc); @@ -140,12 +141,11 @@ int main(int argc, char* argv[]) { // generate the points - if (res <= rootCellHf.res) - doCell(&rootCellHf, isKmlOut); - else { - int rootRes = rootCellHf.res; - rootCellHf.res = res; - recursiveH3IndexToGeo(&rootCellHf, rootRes + 1, isKmlOut); + if (res <= rootRes) { + doCell(rootCell, isKmlOut); + } else { + H3_SET_RESOLUTION(rootCell, res); + recursiveH3IndexToGeo(rootCell, rootRes + 1, isKmlOut); } if (isKmlOut) kmlBoundaryFooter(); diff --git a/src/apps/testapps/testCompact.c b/src/apps/testapps/testCompact.c index eedc9f242..77a9653f5 100644 --- a/src/apps/testapps/testCompact.c +++ b/src/apps/testapps/testCompact.c @@ -15,6 +15,7 @@ */ #include +#include "constants.h" #include "h3Index.h" #include "test.h" diff --git a/src/apps/testapps/testH3Index.c b/src/apps/testapps/testH3Index.c index 318f94128..05c0024df 100644 --- a/src/apps/testapps/testH3Index.c +++ b/src/apps/testapps/testH3Index.c @@ -22,6 +22,7 @@ #include #include #include +#include "constants.h" #include "h3Index.h" #include "test.h" #include "utility.h" @@ -47,6 +48,28 @@ TEST(h3IsValidDigits) { "h3IsValid failed on invalid unused digits"); } +TEST(h3IsValidBaseCell) { + for (int i = 0; i < NUM_BASE_CELLS; i++) { + H3Index h = H3_INIT; + H3_SET_MODE(h, H3_HEXAGON_MODE); + H3_SET_BASE_CELL(h, i); + char failureMessage[BUFF_SIZE]; + sprintf(failureMessage, "h3IsValid failed on base cell %d", i); + t_assert(h3IsValid(h), failureMessage); + + t_assert(H3_EXPORT(h3GetBaseCell)(h) == i, + "failed to recover base cell"); + } +} + +TEST(h3FatIsValidBaseCellInvalid) { + H3Index hWrongBaseCell = H3_INIT; + H3_SET_MODE(hWrongBaseCell, H3_HEXAGON_MODE); + H3_SET_BASE_CELL(hWrongBaseCell, NUM_BASE_CELLS); + t_assert(!h3IsValid(hWrongBaseCell), + "h3IsValid failed on invalid base cell"); +} + TEST(h3ToString) { const size_t bufSz = 17; char buf[17] = {0}; diff --git a/src/apps/testapps/testH3IndexFat.c b/src/apps/testapps/testH3IndexFat.c deleted file mode 100644 index 2e3c734ea..000000000 --- a/src/apps/testapps/testH3IndexFat.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2017 Uber Technologies, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** @file - * @brief tests H3 functions for manipulating H3 indexes - * - * usage: `testH3IndexFat` - */ - -#include "h3Index.h" -#include "h3IndexFat.h" -#include "test.h" -#include "utility.h" - -BEGIN_TESTS(h3IndexFat); - -TEST(h3FatIsValidWithMode) { - for (int i = 0; i <= 0xf; i++) { - H3IndexFat hf; - setH3IndexFat(&hf, 0, 0, 0); - hf.mode = i; - char failureMessage[BUFF_SIZE]; - sprintf(failureMessage, "h3FatIsValid failed on mode %d", i); - t_assert(!h3FatIsValid(&hf) || i == 1, failureMessage); - } -} - -TEST(h3FatNegativeResInvalid) { - H3IndexFat hfNegativeRes; - setH3IndexFat(&hfNegativeRes, -1, 0, 0); - t_assert(!h3FatIsValid(&hfNegativeRes), - "h3FatIsValid failed on negative resolution"); -} - -TEST(h3FatLargeResolutionInvalid) { - H3IndexFat hfLargeRes; - setH3IndexFat(&hfLargeRes, 15, 0, 0); - // Can't set resolution to 16 using setH3IndexFat - // since it would corrupt the stack. - hfLargeRes.res = 16; - t_assert(!h3FatIsValid(&hfLargeRes), - "h3FatIsValid failed on large resolution"); -} - -TEST(h3FatUnusedDigitInvalid) { - H3IndexFat hf; - initH3IndexFat(&hf, 0); - hf.baseCell = 1; - hf.index[0] = 0; - t_assert(!h3FatIsValid(&hf), - "h3FatIsValid failed on invalid index digit (0)"); - - hf.index[0] = 7; - hf.index[14] = 2; - t_assert(!h3FatIsValid(&hf), - "h3FatIsValid failed on invalid index digit (14)"); - - hf.res = 1; - hf.index[14] = 7; - hf.index[1] = 1; - t_assert(!h3FatIsValid(&hf), - "h3FatIsValid failed on invalid index digit (1)"); -} - -TEST(h3FatBadDigitInvalid) { - H3IndexFat hfNegative; - setH3IndexFat(&hfNegative, 1, 0, -1); - t_assert(!h3FatIsValid(&hfNegative), - "h3FatIsValid failed on negative digit"); - - H3IndexFat hfLarge; - setH3IndexFat(&hfLarge, 1, 0, 7); - t_assert(!h3FatIsValid(&hfLarge), "h3FatIsValid failed on too large digit"); -} - -TEST(h3FatIsValidBaseCell) { - for (int i = 0; i < NUM_BASE_CELLS; i++) { - H3IndexFat hf; - initH3IndexFat(&hf, 0); - hf.mode = 1; - hf.baseCell = i; - char failureMessage[BUFF_SIZE]; - sprintf(failureMessage, "h3FatIsValid failed on base cell %d", i); - t_assert(h3FatIsValid(&hf), failureMessage); - - H3Index h = h3FatToH3(&hf); - t_assert(H3_EXPORT(h3GetBaseCell)(h) == i, - "failed to recover base cell"); - } -} - -TEST(h3FatIsValidNegativeBaseCellInvalid) { - H3IndexFat hfNegativeBaseCell; - initH3IndexFat(&hfNegativeBaseCell, 0); - hfNegativeBaseCell.baseCell = -1; - t_assert(!h3FatIsValid(&hfNegativeBaseCell), - "h3FatIsValid failed on negative base cell"); -} - -TEST(h3FatIsValidBaseCellInvalud) { - H3IndexFat hfWrongBaseCell; - initH3IndexFat(&hfWrongBaseCell, 0); - hfWrongBaseCell.baseCell = NUM_BASE_CELLS; - t_assert(!h3FatIsValid(&hfWrongBaseCell), - "h3FatIsValid failed on invalid base cell"); -} - -TEST(setH3IndexFat) { - H3IndexFat hf; - setH3IndexFat(&hf, 5, 12, 1); - t_assert(hf.res == 5, "resolution as expected"); - t_assert(hf.baseCell == 12, "base cell as expected"); - t_assert(hf.mode == H3_HEXAGON_MODE, "mode as expected"); - for (int i = 0; i < 5; i++) { - t_assert(hf.index[i] == 1, "digit as expected"); - } - for (int i = 5; i < MAX_H3_RES; i++) { - t_assert(hf.index[i] == 7, "blanked digit as expected"); - } - - H3IndexFat comparison = { - H3_HEXAGON_MODE, 5, 12, {1, 1, 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7}}; - t_assert(h3FatEquals(&hf, &comparison), "equals expected value"); - - H3IndexFat wrongComparison = { - H3_HEXAGON_MODE, 5, 11, {1, 1, 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7}}; - t_assert(!h3FatEquals(&hf, &wrongComparison), - "equals wrong value (base cell)"); - - H3IndexFat wrongComparison2 = { - H3_HEXAGON_MODE, 5, 12, {1, 1, 1, 2, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7}}; - t_assert(!h3FatEquals(&hf, &wrongComparison2), - "equals wrong value (digit)"); -} - -END_TESTS(); diff --git a/src/apps/testapps/testH3NeighborRotations.c b/src/apps/testapps/testH3NeighborRotations.c index 6c45b41a2..51f500733 100644 --- a/src/apps/testapps/testH3NeighborRotations.c +++ b/src/apps/testapps/testH3NeighborRotations.c @@ -47,9 +47,7 @@ typedef struct { int ret2; } TestOutput; -void doCell(const H3IndexFat* hf, int maxK, TestOutput* testOutput) { - H3Index h = h3FatToH3(hf); - +void doCell(H3Index h, int maxK, TestOutput* testOutput) { for (int k = 0; k < maxK; k++) { int maxSz = H3_EXPORT(maxKringSize)(k); H3Index* kRingInternalOutput = calloc(sizeof(H3Index), maxSz); @@ -99,9 +97,7 @@ void doCell(const H3IndexFat* hf, int maxK, TestOutput* testOutput) { testOutput->ret1++; int foundPent = 0; for (int i = 0; i < maxSz; i++) { - H3IndexFat possiblePent; - h3ToH3Fat(kRingInternalOutput[i], &possiblePent); - if (isPentagon(&possiblePent)) { + if (h3IsPentagon(kRingInternalOutput[i])) { foundPent = 1; break; } @@ -122,20 +118,23 @@ void doCell(const H3IndexFat* hf, int maxK, TestOutput* testOutput) { } } -void recursiveH3IndexToGeo(H3IndexFat* c, int res, int maxK, +void recursiveH3IndexToGeo(H3Index h, int res, int maxK, TestOutput* testOutput) { for (int d = 0; d < 7; d++) { - c->index[res - 1] = d; + H3_SET_INDEX_DIGIT(h, res, d); // skip the pentagonal deleted subsequence - if (_isBaseCellPentagon(c->baseCell) && _leadingNonZeroDigit(c) == 1) + if (_isBaseCellPentagon(H3_GET_BASE_CELL(h)) && + _h3LeadingNonZeroDigit(h) == 1) { continue; + } - if (res == c->res) - doCell(c, maxK, testOutput); - else - recursiveH3IndexToGeo(c, res + 1, maxK, testOutput); + if (res == H3_GET_RESOLUTION(h)) { + doCell(h, maxK, testOutput); + } else { + recursiveH3IndexToGeo(h, res + 1, maxK, testOutput); + } } } @@ -158,16 +157,16 @@ int main(int argc, char* argv[]) { // generate the test cases for (int bc = 0; bc < NUM_BASE_CELLS; bc++) { - H3IndexFat rootCellHf; - initH3IndexFat(&rootCellHf, 0); - rootCellHf.baseCell = bc; - - if (res <= rootCellHf.res) - doCell(&rootCellHf, maxK, &testOutput); - else { - int rootRes = rootCellHf.res; - rootCellHf.res = res; - recursiveH3IndexToGeo(&rootCellHf, rootRes + 1, maxK, &testOutput); + H3Index rootCell = H3_INIT; + H3_SET_MODE(rootCell, H3_HEXAGON_MODE); + H3_SET_BASE_CELL(rootCell, bc); + + if (res == 0) { + doCell(rootCell, maxK, &testOutput); + } else { + int rootRes = H3_GET_RESOLUTION(rootCell); + H3_SET_RESOLUTION(rootCell, res); + recursiveH3IndexToGeo(rootCell, rootRes + 1, maxK, &testOutput); } } diff --git a/src/apps/testapps/testH3ToGeo.c b/src/apps/testapps/testH3ToGeo.c index a2bc30dd9..d95ce660d 100644 --- a/src/apps/testapps/testH3ToGeo.c +++ b/src/apps/testapps/testH3ToGeo.c @@ -26,6 +26,7 @@ #include #include +#include "constants.h" #include "geoCoord.h" #include "h3Index.h" #include "test.h" diff --git a/src/apps/testapps/testHexRing.c b/src/apps/testapps/testHexRing.c index 7ec32cc69..3989feef6 100644 --- a/src/apps/testapps/testHexRing.c +++ b/src/apps/testapps/testHexRing.c @@ -16,6 +16,7 @@ #include #include "algos.h" +#include "constants.h" #include "h3Index.h" #include "test.h" diff --git a/src/apps/testapps/testPolyfill.c b/src/apps/testapps/testPolyfill.c index c7cdc20fa..7f89647ca 100644 --- a/src/apps/testapps/testPolyfill.c +++ b/src/apps/testapps/testPolyfill.c @@ -16,6 +16,7 @@ #include #include "algos.h" +#include "constants.h" #include "geoCoord.h" #include "h3Index.h" #include "test.h" diff --git a/src/h3lib/include/h3Index.h b/src/h3lib/include/h3Index.h index e76f18606..5d50c7c2f 100644 --- a/src/h3lib/include/h3Index.h +++ b/src/h3lib/include/h3Index.h @@ -21,7 +21,6 @@ #define H3INDEX_H #include "faceijk.h" -#include "h3IndexFat.h" #include "h3api.h" // define's of constants and macros for bitwise manipulation of H3Index's. @@ -142,9 +141,8 @@ (((uint64_t)(digit)) \ << ((MAX_H3_RES - (res)) * H3_PER_DIGIT_OFFSET))) -void h3ToH3Fat(H3Index h, H3IndexFat* hf); -H3Index h3FatToH3(const H3IndexFat* hf); void setH3Index(H3Index* h, int res, int baseCell, int initDigit); +int isResClassIII(int res); // Internal functions diff --git a/src/h3lib/include/h3IndexFat.h b/src/h3lib/include/h3IndexFat.h deleted file mode 100644 index 82ac855a0..000000000 --- a/src/h3lib/include/h3IndexFat.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2016-2017 Uber Technologies, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** @file h3IndexFat.h - * @brief H3IndexFat functions and definitions. - */ - -#ifndef H3INDEXFAT_H -#define H3INDEXFAT_H - -#include "constants.h" -#include "coordijk.h" -#include "faceijk.h" -#include "geoCoord.h" -#include "vec2d.h" - -/** @struct H3IndexFat - * @brief heavyweight H3 index representation - */ -typedef struct { - int mode; ///< index mode - int res; ///< cell resolution - int baseCell; ///< base cell number 0-121 - int index[MAX_H3_RES]; ///< index digits (offset by 1 (0..res-1) due to - /// baseCell res 0) -} H3IndexFat; - -// find the H3 index h3 of the resolution res cell containing the lat/lon g -void geoToH3Fat(const GeoCoord* g, int res, H3IndexFat* h3); - -// find the lat/lon center point g of the cell h3 -void h3FatToGeo(const H3IndexFat* h3, GeoCoord* g); - -// give the cell boundary in lat/lon coordinates for the cell h3 -void h3FatToGeoBoundary(const H3IndexFat* h3, GeoBoundary* gp); - -void initH3IndexFat(H3IndexFat* c, int res); -void setH3IndexFat(H3IndexFat* c, int res, int baseCell, int digit); -void copyH3IndexFat(const H3IndexFat* orig, H3IndexFat* copy); -int h3FatIsValid(const H3IndexFat* c); -int isResClassIII(int res); -int isPentagon(const H3IndexFat* c); -int h3FatEquals(const H3IndexFat* c1, const H3IndexFat* c2); - -// Internal functions - -void _faceIjkToH3Fat(const FaceIJK* ijk, int res, H3IndexFat* c); -int _h3FatToFaceIjkWithInitializedFijk(const H3IndexFat* c, FaceIJK* fijk); -int _leadingNonZeroDigit(const H3IndexFat* c); -void _h3FatToFaceIjk(const H3IndexFat* cOrig, FaceIJK* fijk); -void _h3FatRotatePent60ccw(H3IndexFat* c); -void _h3FatRotate60ccw(H3IndexFat* c); -void _h3FatRotate60cw(H3IndexFat* c); - -#endif diff --git a/src/h3lib/lib/algos.c b/src/h3lib/lib/algos.c index f4f0723c9..830640739 100644 --- a/src/h3lib/lib/algos.c +++ b/src/h3lib/lib/algos.c @@ -27,7 +27,6 @@ #include "faceijk.h" #include "geoCoord.h" #include "h3Index.h" -#include "h3IndexFat.h" #include "h3api.h" #include "linkedGeo.h" #include "vertexGraph.h" diff --git a/src/h3lib/lib/coordijk.c b/src/h3lib/lib/coordijk.c index d6f4247ac..ca72774a6 100644 --- a/src/h3lib/lib/coordijk.c +++ b/src/h3lib/lib/coordijk.c @@ -23,8 +23,8 @@ #include #include #include +#include "constants.h" #include "geoCoord.h" -#include "h3IndexFat.h" /** 1.0/sqrt(7) */ #define M_1_SQRT7 0.3779644730092272272145165362341800608157L diff --git a/src/h3lib/lib/faceijk.c b/src/h3lib/lib/faceijk.c index 1349b3407..f845f7355 100644 --- a/src/h3lib/lib/faceijk.c +++ b/src/h3lib/lib/faceijk.c @@ -23,9 +23,10 @@ #include #include #include +#include "constants.h" #include "coordijk.h" #include "geoCoord.h" -#include "h3IndexFat.h" +#include "h3Index.h" /** square root of 7 */ #define M_SQRT7 2.6457513110645905905016157536392604257102L diff --git a/src/h3lib/lib/h3Index.c b/src/h3lib/lib/h3Index.c index fb397c83f..e7329723e 100644 --- a/src/h3lib/lib/h3Index.c +++ b/src/h3lib/lib/h3Index.c @@ -19,10 +19,11 @@ */ #include "h3Index.h" #include +#include #include #include +#include "baseCells.h" #include "faceijk.h" -#include "h3IndexFat.h" #include "mathExtensions.h" /** @@ -67,43 +68,31 @@ void H3_EXPORT(h3ToString)(H3Index h, char* str, size_t sz) { sprintf(str, "%" PRIx64, h); } -/** - * Converts an H3 index into an H3Fat representation. - * @param h The H3 index to convert. - * @param hf The corresponding H3Fat representation. - */ -void h3ToH3Fat(H3Index h, H3IndexFat* hf) { - initH3IndexFat(hf, H3_GET_RESOLUTION(h)); - hf->mode = H3_GET_MODE(h); - hf->baseCell = H3_GET_BASE_CELL(h); - for (int r = 1; r <= MAX_H3_RES; r++) - hf->index[r - 1] = H3_GET_INDEX_DIGIT(h, r); -} - -/** - * Converts an H3Fat index into an H3 index. - * @param hf The H3Fat index to convert. - * @return The H3 index corresponding to the H3Fat index. - */ -H3Index h3FatToH3(const H3IndexFat* hf) { - H3Index h = H3_INIT; - H3_SET_MODE(h, hf->mode); - H3_SET_RESOLUTION(h, hf->res); - H3_SET_BASE_CELL(h, hf->baseCell); - for (int r = 1; r < hf->res + 1; r++) - H3_SET_INDEX_DIGIT(h, r, hf->index[r - 1]); - return h; -} - /** * Returns whether or not an H3 index is valid. * @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) { - H3IndexFat hf; - h3ToH3Fat(h, &hf); - return h3FatIsValid(&hf); + if (H3_GET_MODE(h) != H3_HEXAGON_MODE) 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; + + for (int r = 1; r <= res; r++) { + int digit = H3_GET_INDEX_DIGIT(h, r); + if (digit < 0 || digit > 6) return 0; + } + + for (int r = res + 1; r <= MAX_H3_RES; r++) { + int digit = H3_GET_INDEX_DIGIT(h, r); + if (digit != 7) return 0; + } + + return 1; } /** @@ -200,9 +189,7 @@ void H3_EXPORT(h3ToChildren)(H3Index h, int childRes, H3Index* children) { } int bufferSize = H3_EXPORT(maxH3ToChildrenSize)(h, childRes); int bufferChildStep = (bufferSize / 7); - H3IndexFat hFat; - h3ToH3Fat(h, &hFat); - int isAPentagon = isPentagon(&hFat); + int isAPentagon = h3IsPentagon(h); for (int i = 0; i < 7; i++) { if (!isAPentagon || i != K_AXES_DIGIT) { H3_EXPORT(h3ToChildren)(makeDirectChild(h, i), childRes, children); @@ -293,10 +280,8 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet, for (int i = 0; i < numRemainingHexes; i++) { if (hashSetArray[i] == 0) continue; int count = H3_GET_RESERVED_BITS(hashSetArray[i]) + 1; - H3IndexFat tempFat = {0}; - h3ToH3Fat(hashSetArray[i] & H3_RESERVED_MASK_NEGATIVE, &tempFat); // Include the deleted direction for pentagons as implicitly "there" - if (isPentagon(&tempFat)) { + if (h3IsPentagon(hashSetArray[i] & H3_RESERVED_MASK_NEGATIVE)) { // We need this later on, no need to recalculate H3_SET_RESERVED_BITS(hashSetArray[i], count); // Increment count after setting the reserved bits, @@ -462,9 +447,8 @@ int H3_EXPORT(h3IsResClassIII)(H3Index h) { return H3_GET_RESOLUTION(h) % 2; } * @return Returns 1 if it is a pentagon, otherwise 0. */ int H3_EXPORT(h3IsPentagon)(H3Index h) { - H3IndexFat hFat; - h3ToH3Fat(h, &hFat); - return isPentagon(&hFat); + return _isBaseCellPentagon(H3_GET_BASE_CELL(h)) && + !_h3LeadingNonZeroDigit(h); } /** @@ -559,3 +543,220 @@ H3Index _h3Rotate60cw(H3Index h) { return h; } + +/** + * Convert an FaceIJK address to the corresponding H3Index. + * @param fijk The FaceIJK address. + * @param res The cell resolution. + */ +H3Index _faceIjkToH3(const FaceIJK* fijk, int res) { + // initialize the index + H3Index h = H3_INIT; + H3_SET_MODE(h, H3_HEXAGON_MODE); + H3_SET_RESOLUTION(h, res); + + // check for res 0/base cell + if (res == 0) { + H3_SET_BASE_CELL(h, _faceIjkToBaseCell(fijk)); + return h; + } + + // we need to find the correct base cell FaceIJK for this H3 index; + // start with the passed in face and resolution res ijk coordinates + // in that face's coordinate system + FaceIJK fijkBC = *fijk; + + // build the H3Index from finest res up + // adjust r for the fact that the res 0 base cell offsets the index array + CoordIJK* ijk = &fijkBC.coord; + for (int r = res - 1; r >= 0; r--) { + CoordIJK lastIJK = *ijk; + CoordIJK lastCenter; + if (isResClassIII(r + 1)) { + // rotate ccw + _upAp7(ijk); + lastCenter = *ijk; + _downAp7(&lastCenter); + } else { + // rotate cw + _upAp7r(ijk); + lastCenter = *ijk; + _downAp7r(&lastCenter); + } + + CoordIJK diff; + _ijkSub(&lastIJK, &lastCenter, &diff); + _ijkNormalize(&diff); + + H3_SET_INDEX_DIGIT(h, r + 1, _unitIjkToDigit(&diff)); + } + + // fijkBC should now hold the IJK of the base cell in the + // coordinate system of the current face + + // lookup the correct base cell + int baseCell = _faceIjkToBaseCell(&fijkBC); + H3_SET_BASE_CELL(h, baseCell); + + // rotate if necessary to get canonical base cell orientation + // for this base cell + int numRots = _faceIjkToBaseCellCCWrot60(&fijkBC); + if (_isBaseCellPentagon(baseCell)) { + // force rotation out of missing k-axes sub-sequence + if (_h3LeadingNonZeroDigit(h) == K_AXES_DIGIT) { + // check for a cw/ccw offset face; default is ccw + if (baseCellData[baseCell].cwOffsetPent[0] == fijkBC.face || + baseCellData[baseCell].cwOffsetPent[1] == fijkBC.face) { + h = _h3Rotate60cw(h); + } else { + h = _h3Rotate60ccw(h); + } + } + + for (int i = 0; i < numRots; i++) h = _h3RotatePent60ccw(h); + } else { + for (int i = 0; i < numRots; i++) { + h = _h3Rotate60ccw(h); + } + } + + return h; +} + +/** + * Encodes a coordinate on the sphere to the H3 index of the containing cell at + * the specified resolution. + * + * 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). + */ +H3Index H3_EXPORT(geoToH3)(const GeoCoord* g, int res) { + if (res < 0 || res > MAX_H3_RES) { + return 0; + } + if (!isfinite(g->lat) || !isfinite(g->lon)) { + return 0; + } + + FaceIJK fijk; + _geoToFaceIjk(g, res, &fijk); + return _faceIjkToH3(&fijk, res); +} + +/** + * Convert an H3Index to the FaceIJK address on a specified icosahedral face. + * @param h The H3Index. + * @param fijk The FaceIJK address, initialized with the desired face + * and normalized base cell coordinates. + * @return Returns 1 if the possibility of overage exists, otherwise 0. + */ +int _h3ToFaceIjkWithInitializedFijk(H3Index h, FaceIJK* fijk) { + CoordIJK* ijk = &fijk->coord; + int res = H3_GET_RESOLUTION(h); + + // center base cell hierarchy is entirely on this face + int possibleOverage = 1; + if (!_isBaseCellPentagon(H3_GET_BASE_CELL(h)) && + (res == 0 || + (fijk->coord.i == 0 && fijk->coord.j == 0 && fijk->coord.k == 0))) + possibleOverage = 0; + + for (int r = 1; r <= res; r++) { + if (isResClassIII(r)) { + // Class III == rotate ccw + _downAp7(ijk); + } else { + // Class II == rotate cw + _downAp7r(ijk); + } + + _neighbor(ijk, H3_GET_INDEX_DIGIT(h, r)); + } + + return possibleOverage; +} + +/** + * Convert an H3Index to a FaceIJK address. + * @param h The H3Index. + * @param fijk The corresponding FaceIJK address. + */ +void _h3ToFaceIjk(H3Index h, FaceIJK* fijk) { + int baseCell = H3_GET_BASE_CELL(h); + // adjust for the pentagonal missing sequence; all of sub-sequence 5 needs + // to be adjusted (and some of sub-sequence 4 below) + if (_isBaseCellPentagon(baseCell) && _h3LeadingNonZeroDigit(h) == 5) + h = _h3Rotate60cw(h); + + // start with the "home" face and ijk+ coordinates for the base cell of c + *fijk = baseCellData[baseCell].homeFijk; + if (!_h3ToFaceIjkWithInitializedFijk(h, fijk)) + return; // no overage is possible; h lies on this face + + // if we're here we have the potential for an "overage"; i.e., it is + // possible that c lies on an adjacent face + + CoordIJK origIJK = fijk->coord; + + // if we're in Class III, drop into the next finer Class II grid + int res = H3_GET_RESOLUTION(h); + if (isResClassIII(res)) { + // Class III + _downAp7r(&fijk->coord); + res++; + } + + // adjust for overage if needed + // a pentagon base cell with a leading 4 digit requires special handling + int pentLeading4 = + (_isBaseCellPentagon(baseCell) && _h3LeadingNonZeroDigit(h) == 4); + if (_adjustOverageClassII(fijk, res, pentLeading4, 0)) { + // if the base cell is a pentagon we have the potential for secondary + // overages + if (_isBaseCellPentagon(baseCell)) { + while (1) { + if (!_adjustOverageClassII(fijk, res, 0, 0)) break; + } + } + + if (res != H3_GET_RESOLUTION(h)) _upAp7r(&fijk->coord); + } else if (res != H3_GET_RESOLUTION(h)) { + fijk->coord = origIJK; + } +} + +/** + * Determines the spherical coordinates of the center point of an H3 index. + * + * @param h3 The H3 index. + * @param g The spherical coordinates of the H3 cell center. + */ +void H3_EXPORT(h3ToGeo)(H3Index h3, GeoCoord* g) { + FaceIJK fijk; + _h3ToFaceIjk(h3, &fijk); + _faceIjkToGeo(&fijk, H3_GET_RESOLUTION(h3), g); +} + +/** + * Determines the cell boundary in spherical coordinates for an H3 index. + * + * @param h3 The H3 index. + * @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), h3IsPentagon(h3), gb); +} + +/** + * Returns whether or not a resolution is a Class III grid. Note that odd + * resolutions are Class III and even resolutions are Class II. + * @param res The H3 resolution. + * @return 1 if the resolution is a Class III grid, and 0 if the resolution is + * a Class II grid. + */ +int isResClassIII(int res) { return res % 2; } diff --git a/src/h3lib/lib/h3IndexFat.c b/src/h3lib/lib/h3IndexFat.c deleted file mode 100644 index 0e5908b61..000000000 --- a/src/h3lib/lib/h3IndexFat.c +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright 2016-2017 Uber Technologies, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** @file h3IndexFat.c - * @brief H3IndexFat functions including conversion from lat/lon. - */ - -#include "h3IndexFat.h" -#include "baseCells.h" - -/** @brief An H3IndexFat with all digits set to 7. */ -static const H3IndexFat emptyH3IndexFat = { - -1, -1, -1, {7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}}; - -/** - * Encodes a coordinate on the sphere to the H3Fat index of the containing cell - * at the specified resolution. - * - * @param g The spherical coordinates to encode. - * @param res The desired H3 resolution for the encoding. - * @param c The encoded H3Fat index. - */ -void geoToH3Fat(const GeoCoord* g, int res, H3IndexFat* c) { - FaceIJK h; - _geoToFaceIjk(g, res, &h); - _faceIjkToH3Fat(&h, res, c); -} - -/** - * Determines the spherical coordinates of the center point of an H3Fat index. - * - * @param c The H3Fat index. - * @param g The spherical coordinates of the H3 cell center. - */ -void h3FatToGeo(const H3IndexFat* c, GeoCoord* g) { - FaceIJK fijk; - _h3FatToFaceIjk(c, &fijk); - _faceIjkToGeo(&fijk, c->res, g); -} - -/** - * Determines the cell boundary in spherical coordinates for an H3Fat index. - * - * @param c The H3Fat index. - * @param gp The boundary of the H3 cell in spherical coordinates. - */ -void h3FatToGeoBoundary(const H3IndexFat* c, GeoBoundary* gp) { - FaceIJK fijk; - _h3FatToFaceIjk(c, &fijk); - _faceIjkToGeoBoundary(&fijk, c->res, isPentagon(c), gp); -} - -/** - * Initialize an H3Fat index to default values. - * - * @param c The H3Fat index to initialize. - * @param res The desired H3 resolution. - */ -void initH3IndexFat(H3IndexFat* c, int res) { - *c = emptyH3IndexFat; - c->res = res; - c->mode = H3_HEXAGON_MODE; -} - -/** - * Initialize an H3Fat index to specified values. - * - * @param c The H3Fat index to initialize. - * @param res The desired H3 resolution. - * @param baseCell The desired H3 base cell. - * @param digit The desired H3 digit to initialize all resolutions to. - */ -void setH3IndexFat(H3IndexFat* c, int res, int baseCell, int digit) { - initH3IndexFat(c, res); - c->baseCell = baseCell; - for (int r = 0; r < c->res; r++) c->index[r] = digit; -} - -/** - * Returns whether or not an H3Fat index is valid. - * @param c The H3Fat index to validate. - * @return 1 if the H3Fat index if valid, and 0 if it is not. - */ -int h3FatIsValid(const H3IndexFat* c) { - if (c->mode != H3_HEXAGON_MODE) return 0; - - if (c->baseCell < 0 || c->baseCell >= NUM_BASE_CELLS) return 0; - - if (c->res < 0 || c->res > MAX_H3_RES) return 0; - - for (int r = 0; r < c->res; r++) - if (c->index[r] < 0 || c->index[r] > 6) return 0; - - for (int r = c->res; r < MAX_H3_RES; r++) - if (c->index[r] != 7) return 0; - - return 1; -} - -/** - * Make a copy of an H3Fat index. - * @param orig The H3Fat source index. - * @param copy The H3Fat index copy. - */ -void copyH3IndexFat(const H3IndexFat* orig, H3IndexFat* copy) { - initH3IndexFat(copy, orig->res); - copy->baseCell = orig->baseCell; - for (int r = 0; r < orig->res; r++) copy->index[r] = orig->index[r]; -} - -/** - * Convert an FaceIJK address to the corresponding H3Fat index. - * @param fijk The FaceIJK address. - * @param res The cell resolution. - * @param c The corresponding H3Fat index. - */ -void _faceIjkToH3Fat(const FaceIJK* fijk, int res, H3IndexFat* c) { - // initialize the index - initH3IndexFat(c, res); - - // check for res 0/base cell - if (res == 0) { - c->baseCell = _faceIjkToBaseCell(fijk); - c->res = 0; - return; - } - - // we need to find the correct base cell FaceIJK for this H3 index; - // start with the passed in face and resolution res ijk coordinates - // in that face's coordinate system - FaceIJK fijkBC = *fijk; - - // build the H3Fat index from finest res up - // adjust r for the fact that the res 0 base cell offsets the index array - CoordIJK* ijk = &fijkBC.coord; - for (int r = res - 1; r >= 0; r--) { - CoordIJK lastIJK = *ijk; - CoordIJK lastCenter; - if (isResClassIII(r + 1)) // Class III == rotate ccw - { - _upAp7(ijk); - lastCenter = *ijk; - _downAp7(&lastCenter); - } else // Class II == rotate cw - { - _upAp7r(ijk); - lastCenter = *ijk; - _downAp7r(&lastCenter); - } - - CoordIJK diff; - _ijkSub(&lastIJK, &lastCenter, &diff); - _ijkNormalize(&diff); - - c->index[r] = _unitIjkToDigit(&diff); - } - - // fijkBC should now hold the IJK of the base cell in the - // coordinate system of the current face - - // lookup the correct base cell - c->baseCell = _faceIjkToBaseCell(&fijkBC); - - // rotate if necessary to get canonical base cell orientation - // for this base cell - int numRots = _faceIjkToBaseCellCCWrot60(&fijkBC); - if (_isBaseCellPentagon(c->baseCell)) { - // force rotation out of missing k-axes sub-sequence - if (_leadingNonZeroDigit(c) == K_AXES_DIGIT) { - // check for a cw/ccw offset face; default is ccw - if (baseCellData[c->baseCell].cwOffsetPent[0] == fijkBC.face || - baseCellData[c->baseCell].cwOffsetPent[1] == fijkBC.face) - _h3FatRotate60cw(c); - else - _h3FatRotate60ccw(c); - } - - for (int i = 0; i < numRots; i++) _h3FatRotatePent60ccw(c); - } else { - for (int i = 0; i < numRots; i++) _h3FatRotate60ccw(c); - } -} - -/** - * Convert an H3Fat index to the FaceIJK address on a specified icosahedral - * face. - * @param c The H3Fat index. - * @param fijk The FaceIJK address, initialized with the desired face - * and normalized base cell coordinates. - * @return Returns 1 if the possibility of overage exists, otherwise 0. - */ -int _h3FatToFaceIjkWithInitializedFijk(const H3IndexFat* c, FaceIJK* fijk) { - CoordIJK* ijk = &fijk->coord; - - // center base cell hierarchy is entirely on this face - int possibleOverage = 1; - if (!_isBaseCellPentagon(c->baseCell) && - (c->res == 0 || - (fijk->coord.i == 0 && fijk->coord.j == 0 && fijk->coord.k == 0))) - possibleOverage = 0; - - for (int r = 0; r < c->res; r++) { - if (isResClassIII(r + 1)) // Class III == rotate ccw - _downAp7(ijk); - else // Class II == rotate cw - _downAp7r(ijk); - - _neighbor(ijk, c->index[r]); - } - - return possibleOverage; -} - -/** - * Returns the highest resolution non-zero digit in an H3Fat index. - * @param c The H3Fat index. - * @return The highest resolution non-zero digit in the H3Fat index. - */ -int _leadingNonZeroDigit(const H3IndexFat* c) { - for (int r = 0; r < c->res; r++) - if (c->index[r]) return c->index[r]; - - // if we're here it's all 0's - return 0; -} - -/** - * Returns whether or not a resolution is a Class III grid. Note that odd - * resolutions are Class III and even resolutions are Class II. - * @param res The H3 resolution. - * @return 1 if the resolution is a Class III grid, and 0 if the resolution is - * a Class II grid. - */ -int isResClassIII(int res) { return res % 2; } - -/** - * Convert an H3Fat index to a FaceIJK address. - * @param cIn The H3Fat index. - * @param fijk The corresponding FaceIJK address. - */ -void _h3FatToFaceIjk(const H3IndexFat* cIn, FaceIJK* fijk) { - // make a mutable copy of the incoming index - H3IndexFat c; - copyH3IndexFat(cIn, &c); - - // adjust for the pentagonal missing sequence; all of sub-sequence 5 needs - // to be adjusted (and some of sub-sequence 4 below) - if (_isBaseCellPentagon(c.baseCell) && _leadingNonZeroDigit(&c) == 5) - _h3FatRotate60cw(&c); - - // start with the "home" face and ijk+ coordinates for the base cell of c - *fijk = baseCellData[c.baseCell].homeFijk; - if (!_h3FatToFaceIjkWithInitializedFijk(&c, fijk)) - return; // no overage is possible; c lies on this face - - // if we're here we have the potential for an "overage"; i.e., it is - // possible that c lies on an adjacent face - - CoordIJK origIJK = fijk->coord; - - // if we're in Class III, drop into the next finer Class II grid - int res = c.res; - if (isResClassIII(res)) // Class III - { - _downAp7r(&fijk->coord); - res++; - } - - // adjust for overage if needed - // a pentagon base cell with a leading 4 digit requires special handling - int pentLeading4 = - (_isBaseCellPentagon(c.baseCell) && _leadingNonZeroDigit(&c) == 4); - if (_adjustOverageClassII(fijk, res, pentLeading4, 0)) { - // if the base cell is a pentagon we have the potential for secondary - // overages - if (_isBaseCellPentagon(c.baseCell)) { - while (1) { - if (!_adjustOverageClassII(fijk, res, 0, 0)) break; - } - } - - if (res != c.res) _upAp7r(&fijk->coord); - } else if (res != c.res) - fijk->coord = origIJK; -} - -/** - * Rotate an H3Fat index 60 degrees counter-clockwise about a pentagonal center. - * Works in place. - * @param c The H3Fat index. - */ -void _h3FatRotatePent60ccw(H3IndexFat* c) { - // rotate in place; skips any leading 1 digits (k-axis) - const int rotDigit[] = { - 0, // original digit 0 - 5, // original digit 1 - 3, // original digit 2 - 1, // original digit 3 - 6, // original digit 4 - 4, // original digit 5 - 2 // original digit 6 - }; - - int foundFirstNonZeroDigit = 0; - for (int r = 0; r < c->res; r++) { - // rotate this digit - c->index[r] = rotDigit[c->index[r]]; - - // look for the first non-zero digit so we - // can adjust for deleted k-axes sequence - // if neccessary - if (!foundFirstNonZeroDigit && c->index[r] != 0) { - foundFirstNonZeroDigit = 1; - - // adjust for deleted k-axes sequence - if (_leadingNonZeroDigit(c) == K_AXES_DIGIT) _h3FatRotate60ccw(c); - } - } -} - -/** - * Rotate an H3Fat index 60 degrees counter-clockwise. Works in place. - * @param c The H3Fat index. - */ -void _h3FatRotate60ccw(H3IndexFat* c) { - const int rotDigit[] = { - 0, // original digit 0 - 5, // original digit 1 - 3, // original digit 2 - 1, // original digit 3 - 6, // original digit 4 - 4, // original digit 5 - 2 // original digit 6 - }; - - for (int r = 0; r < c->res; r++) c->index[r] = rotDigit[c->index[r]]; -} - -/** - * Determine whether an H3Fat index is a pentagon. - * @param c The H3Fat index. - * @return 1 if the H3Fat index is a pentagon, and 0 otherwise. - */ -int isPentagon(const H3IndexFat* c) { - // a pentagon has a pentagon base cell and all zero digits - return _isBaseCellPentagon(c->baseCell) && !_leadingNonZeroDigit(c); -} - -/** - * Determine whether two H3Fat indexes are equal. - * @param c1 The first H3Fat index. - * @param c2 The second H3Fat index. - * @return 1 if the two indexes are equal, and 0 otherwise. - */ -int h3FatEquals(const H3IndexFat* c1, const H3IndexFat* c2) { - if (c1->res != c2->res || c1->baseCell != c2->baseCell) return 0; - - for (int r = 0; r < c1->res; r++) - if (c1->index[r] != c2->index[r]) return 0; - - return 1; -} - -/** - * Rotate an H3Fat index 60 degrees clockwise. Works in place. - * @param c The H3Fat index. - */ -void _h3FatRotate60cw(H3IndexFat* c) { - const int rotDigit[] = { - 0, // original digit 0 - 3, // original digit 1 - 6, // original digit 2 - 2, // original digit 3 - 5, // original digit 4 - 1, // original digit 5 - 4 // original digit 6 - }; - - for (int r = 0; r < c->res; r++) c->index[r] = rotDigit[c->index[r]]; -} diff --git a/src/h3lib/lib/h3api.c b/src/h3lib/lib/h3api.c deleted file mode 100644 index 385c98141..000000000 --- a/src/h3lib/lib/h3api.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2016-2017 Uber Technologies, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/** @file h3api.c - * @brief Major H3 core library entry points. - */ - -#include "h3api.h" -#include -#include -#include "h3Index.h" - -/** - * Encodes a coordinate on the sphere to the H3 index of the containing cell at - * the specified resolution. - * - * 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). - */ -H3Index H3_EXPORT(geoToH3)(const GeoCoord* g, int res) { - H3IndexFat hf; - - if (res < 0 || res > MAX_H3_RES) { - return 0; - } - if (!isfinite(g->lat) || !isfinite(g->lon)) { - return 0; - } - - geoToH3Fat(g, res, &hf); - return h3FatToH3(&hf); -} - -/** - * Determines the spherical coordinates of the center point of an H3 index. - * - * @param h3 The H3 index. - * @param g The spherical coordinates of the H3 cell center. - */ -void H3_EXPORT(h3ToGeo)(H3Index h3, GeoCoord* g) { - H3IndexFat hf; - h3ToH3Fat(h3, &hf); - h3FatToGeo(&hf, g); -} - -/** - * Determines the cell boundary in spherical coordinates for an H3 index. - * - * @param h3 The H3 index. - * @param gp The boundary of the H3 cell in spherical coordinates. - */ -void H3_EXPORT(h3ToGeoBoundary)(H3Index h3, GeoBoundary* gp) { - H3IndexFat hf; - h3ToH3Fat(h3, &hf); - h3FatToGeoBoundary(&hf, gp); -}