h3 3.5.1 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/Gemfile.lock +3 -3
  4. data/ext/h3/src/.travis.yml +15 -4
  5. data/ext/h3/src/CHANGELOG.md +7 -0
  6. data/ext/h3/src/CMakeLists.txt +15 -0
  7. data/ext/h3/src/README.md +5 -6
  8. data/ext/h3/src/VERSION +1 -1
  9. data/ext/h3/src/docs/api/hierarchy.md +8 -0
  10. data/ext/h3/src/docs/api/misc.md +18 -0
  11. data/ext/h3/src/scripts/coverage.sh.in +7 -3
  12. data/ext/h3/src/src/apps/miscapps/generateBaseCellNeighbors.c +2 -2
  13. data/ext/h3/src/src/apps/miscapps/generateNumHexagons.c +0 -2
  14. data/ext/h3/src/src/apps/testapps/testCompact.c +12 -0
  15. data/ext/h3/src/src/apps/testapps/testH3Distance.c +1 -50
  16. data/ext/h3/src/src/apps/testapps/testH3DistanceExhaustive.c +83 -0
  17. data/ext/h3/src/src/apps/testapps/testH3Line.c +1 -84
  18. data/ext/h3/src/src/apps/testapps/testH3LineExhaustive.c +114 -0
  19. data/ext/h3/src/src/apps/testapps/testH3ToCenterChild.c +67 -0
  20. data/ext/h3/src/src/apps/testapps/testH3ToChildren.c +14 -2
  21. data/ext/h3/src/src/apps/testapps/testH3ToLocalIj.c +12 -230
  22. data/ext/h3/src/src/apps/testapps/testH3ToLocalIjExhaustive.c +264 -0
  23. data/ext/h3/src/src/apps/testapps/testPentagonIndexes.c +57 -0
  24. data/ext/h3/src/src/h3lib/include/constants.h +2 -0
  25. data/ext/h3/src/src/h3lib/include/h3api.h.in +20 -0
  26. data/ext/h3/src/src/h3lib/lib/algos.c +5 -5
  27. data/ext/h3/src/src/h3lib/lib/faceijk.c +3 -3
  28. data/ext/h3/src/src/h3lib/lib/h3Index.c +69 -6
  29. data/ext/h3/src/src/h3lib/lib/localij.c +4 -4
  30. data/ext/h3/src/src/h3lib/lib/polygon.c +1 -2
  31. data/h3.gemspec +1 -1
  32. data/lib/h3/bindings/private.rb +1 -0
  33. data/lib/h3/hierarchy.rb +15 -0
  34. data/lib/h3/miscellaneous.rb +25 -0
  35. data/lib/h3/version.rb +1 -1
  36. data/spec/hierarchy_spec.rb +10 -0
  37. data/spec/miscellaneous_spec.rb +28 -0
  38. metadata +9 -4
@@ -0,0 +1,264 @@
1
+ /*
2
+ * Copyright 2019 Uber Technologies, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /** @file
17
+ * @brief tests H3 index to local IJ and IJK+ grid functions using
18
+ * tests over a large number of indexes.
19
+ *
20
+ * usage: `testH3ToLocalIjExhaustive`
21
+ */
22
+
23
+ #include <h3api.h>
24
+ #include <stdio.h>
25
+ #include <stdlib.h>
26
+ #include <string.h>
27
+ #include "algos.h"
28
+ #include "baseCells.h"
29
+ #include "constants.h"
30
+ #include "h3Index.h"
31
+ #include "h3api.h"
32
+ #include "localij.h"
33
+ #include "test.h"
34
+ #include "utility.h"
35
+
36
+ static const int MAX_DISTANCES[] = {1, 2, 5, 12, 19, 26};
37
+
38
+ // The same traversal constants from algos.c (for hexRange) here reused as local
39
+ // IJ vectors.
40
+ static const CoordIJ DIRECTIONS[6] = {{0, 1}, {-1, 0}, {-1, -1},
41
+ {0, -1}, {1, 0}, {1, 1}};
42
+
43
+ static const CoordIJ NEXT_RING_DIRECTION = {1, 0};
44
+
45
+ /**
46
+ * Test that the local coordinates for an index map to itself.
47
+ */
48
+ void localIjToH3_identity_assertions(H3Index h3) {
49
+ CoordIJ ij;
50
+ t_assert(H3_EXPORT(experimentalH3ToLocalIj)(h3, h3, &ij) == 0,
51
+ "able to setup localIjToH3 test");
52
+
53
+ H3Index retrieved;
54
+ t_assert(H3_EXPORT(experimentalLocalIjToH3)(h3, &ij, &retrieved) == 0,
55
+ "got an index back from localIjTOh3");
56
+ t_assert(h3 == retrieved, "round trip through local IJ space works");
57
+ }
58
+
59
+ /**
60
+ * Test that coordinates for an index match some simple rules about index
61
+ * digits, when using the index as its own origin. That is, that the IJ
62
+ * coordinates are in the coordinate space of the origin's base cell.
63
+ */
64
+ void h3ToLocalIj_coordinates_assertions(H3Index h3) {
65
+ int r = H3_GET_RESOLUTION(h3);
66
+
67
+ CoordIJ ij;
68
+ t_assert(H3_EXPORT(experimentalH3ToLocalIj)(h3, h3, &ij) == 0,
69
+ "get ij for origin");
70
+ CoordIJK ijk;
71
+ ijToIjk(&ij, &ijk);
72
+ if (r == 0) {
73
+ t_assert(_ijkMatches(&ijk, &UNIT_VECS[0]) == 1, "res 0 cell at 0,0,0");
74
+ } else if (r == 1) {
75
+ t_assert(_ijkMatches(&ijk, &UNIT_VECS[H3_GET_INDEX_DIGIT(h3, 1)]) == 1,
76
+ "res 1 cell at expected coordinates");
77
+ } else if (r == 2) {
78
+ CoordIJK expected = UNIT_VECS[H3_GET_INDEX_DIGIT(h3, 1)];
79
+ _downAp7r(&expected);
80
+ _neighbor(&expected, H3_GET_INDEX_DIGIT(h3, 2));
81
+ t_assert(_ijkMatches(&ijk, &expected) == 1,
82
+ "res 2 cell at expected coordinates");
83
+ } else {
84
+ t_assert(0, "resolution supported by test function (coordinates)");
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Test the the immediate neighbors of an index are at the expected locations in
90
+ * the local IJ coordinate space.
91
+ */
92
+ void h3ToLocalIj_neighbors_assertions(H3Index h3) {
93
+ CoordIJ origin = {0};
94
+ t_assert(H3_EXPORT(experimentalH3ToLocalIj)(h3, h3, &origin) == 0,
95
+ "got ij for origin");
96
+ CoordIJK originIjk;
97
+ ijToIjk(&origin, &originIjk);
98
+
99
+ for (Direction d = K_AXES_DIGIT; d < INVALID_DIGIT; d++) {
100
+ if (d == K_AXES_DIGIT && H3_EXPORT(h3IsPentagon)(h3)) {
101
+ continue;
102
+ }
103
+
104
+ int rotations = 0;
105
+ H3Index offset = h3NeighborRotations(h3, d, &rotations);
106
+
107
+ CoordIJ ij = {0};
108
+ t_assert(H3_EXPORT(experimentalH3ToLocalIj)(h3, offset, &ij) == 0,
109
+ "got ij for destination");
110
+ CoordIJK ijk;
111
+ ijToIjk(&ij, &ijk);
112
+ CoordIJK invertedIjk = {0};
113
+ _neighbor(&invertedIjk, d);
114
+ for (int i = 0; i < 3; i++) {
115
+ _ijkRotate60ccw(&invertedIjk);
116
+ }
117
+ _ijkAdd(&invertedIjk, &ijk, &ijk);
118
+ _ijkNormalize(&ijk);
119
+
120
+ t_assert(_ijkMatches(&ijk, &originIjk), "back to origin");
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Test that the neighbors (k-ring), if they can be found in the local IJ
126
+ * coordinate space, can be converted back to indexes.
127
+ */
128
+ void localIjToH3_kRing_assertions(H3Index h3) {
129
+ int r = H3_GET_RESOLUTION(h3);
130
+ t_assert(r <= 5, "resolution supported by test function (kRing)");
131
+ int maxK = MAX_DISTANCES[r];
132
+
133
+ int sz = H3_EXPORT(maxKringSize)(maxK);
134
+ H3Index *neighbors = calloc(sz, sizeof(H3Index));
135
+ int *distances = calloc(sz, sizeof(int));
136
+
137
+ H3_EXPORT(kRingDistances)(h3, maxK, neighbors, distances);
138
+
139
+ for (int i = 0; i < sz; i++) {
140
+ if (neighbors[i] == 0) {
141
+ continue;
142
+ }
143
+
144
+ CoordIJ ij;
145
+ // Don't consider indexes which we can't unfold in the first place
146
+ if (H3_EXPORT(experimentalH3ToLocalIj)(h3, neighbors[i], &ij) == 0) {
147
+ H3Index retrieved;
148
+ t_assert(
149
+ H3_EXPORT(experimentalLocalIjToH3)(h3, &ij, &retrieved) == 0,
150
+ "retrieved index for unfolded coordinates");
151
+ t_assert(retrieved == neighbors[i],
152
+ "round trip neighboring index matches expected");
153
+ }
154
+ }
155
+
156
+ free(distances);
157
+ free(neighbors);
158
+ }
159
+
160
+ void localIjToH3_traverse_assertions(H3Index h3) {
161
+ int r = H3_GET_RESOLUTION(h3);
162
+ t_assert(r <= 5, "resolution supported by test function (traverse)");
163
+ int k = MAX_DISTANCES[r];
164
+
165
+ CoordIJ ij;
166
+ t_assert(H3_EXPORT(experimentalH3ToLocalIj)(h3, h3, &ij) == 0,
167
+ "Got origin coordinates");
168
+
169
+ // This logic is from hexRangeDistances.
170
+ // 0 < ring <= k, current ring
171
+ int ring = 1;
172
+ // 0 <= direction < 6, current side of the ring
173
+ int direction = 0;
174
+ // 0 <= i < ring, current position on the side of the ring
175
+ int i = 0;
176
+
177
+ while (ring <= k) {
178
+ if (direction == 0 && i == 0) {
179
+ ij.i += NEXT_RING_DIRECTION.i;
180
+ ij.j += NEXT_RING_DIRECTION.j;
181
+ }
182
+
183
+ ij.i += DIRECTIONS[direction].i;
184
+ ij.j += DIRECTIONS[direction].j;
185
+
186
+ H3Index testH3;
187
+
188
+ int failed = H3_EXPORT(experimentalLocalIjToH3)(h3, &ij, &testH3);
189
+ if (!failed) {
190
+ t_assert(H3_EXPORT(h3IsValid)(testH3),
191
+ "test coordinates result in valid index");
192
+
193
+ CoordIJ expectedIj;
194
+ int reverseFailed =
195
+ H3_EXPORT(experimentalH3ToLocalIj)(h3, testH3, &expectedIj);
196
+ // If it doesn't give a coordinate for this origin,index pair that's
197
+ // OK.
198
+ if (!reverseFailed) {
199
+ if (expectedIj.i != ij.i || expectedIj.j != ij.j) {
200
+ // Multiple coordinates for the same index can happen due to
201
+ // pentagon distortion. In that case, the other coordinates
202
+ // should also belong to the same index.
203
+ H3Index testTestH3;
204
+ t_assert(H3_EXPORT(experimentalLocalIjToH3)(
205
+ h3, &expectedIj, &testTestH3) == 0,
206
+ "converted coordinates again");
207
+ t_assert(testH3 == testTestH3,
208
+ "index has normalizable coordinates in "
209
+ "local IJ");
210
+ }
211
+ }
212
+ }
213
+
214
+ i++;
215
+ // Check if end of this side of the k-ring
216
+ if (i == ring) {
217
+ i = 0;
218
+ direction++;
219
+ // Check if end of this ring.
220
+ if (direction == 6) {
221
+ direction = 0;
222
+ ring++;
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ SUITE(h3ToLocalIj) {
229
+ TEST(localIjToH3_identity) {
230
+ iterateAllIndexesAtRes(0, localIjToH3_identity_assertions);
231
+ iterateAllIndexesAtRes(1, localIjToH3_identity_assertions);
232
+ iterateAllIndexesAtRes(2, localIjToH3_identity_assertions);
233
+ }
234
+
235
+ TEST(h3ToLocalIj_coordinates) {
236
+ iterateAllIndexesAtRes(0, h3ToLocalIj_coordinates_assertions);
237
+ iterateAllIndexesAtRes(1, h3ToLocalIj_coordinates_assertions);
238
+ iterateAllIndexesAtRes(2, h3ToLocalIj_coordinates_assertions);
239
+ }
240
+
241
+ TEST(h3ToLocalIj_neighbors) {
242
+ iterateAllIndexesAtRes(0, h3ToLocalIj_neighbors_assertions);
243
+ iterateAllIndexesAtRes(1, h3ToLocalIj_neighbors_assertions);
244
+ iterateAllIndexesAtRes(2, h3ToLocalIj_neighbors_assertions);
245
+ }
246
+
247
+ TEST(localIjToH3_kRing) {
248
+ iterateAllIndexesAtRes(0, localIjToH3_kRing_assertions);
249
+ iterateAllIndexesAtRes(1, localIjToH3_kRing_assertions);
250
+ iterateAllIndexesAtRes(2, localIjToH3_kRing_assertions);
251
+ // Don't iterate all of res 3, to save time
252
+ iterateAllIndexesAtResPartial(3, localIjToH3_kRing_assertions, 27);
253
+ // Further resolutions aren't tested to save time.
254
+ }
255
+
256
+ TEST(localIjToH3_traverse) {
257
+ iterateAllIndexesAtRes(0, localIjToH3_traverse_assertions);
258
+ iterateAllIndexesAtRes(1, localIjToH3_traverse_assertions);
259
+ iterateAllIndexesAtRes(2, localIjToH3_traverse_assertions);
260
+ // Don't iterate all of res 3, to save time
261
+ iterateAllIndexesAtResPartial(3, localIjToH3_traverse_assertions, 27);
262
+ // Further resolutions aren't tested to save time.
263
+ }
264
+ }
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Copyright 2019 Uber Technologies, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #include <stdlib.h>
18
+ #include "h3api.h"
19
+ #include "test.h"
20
+
21
+ #define PADDED_COUNT 16
22
+
23
+ SUITE(getPentagonIndexes) {
24
+ TEST(propertyTests) {
25
+ int expectedCount = H3_EXPORT(pentagonIndexCount)();
26
+
27
+ for (int res = 0; res <= 15; res++) {
28
+ H3Index h3Indexes[PADDED_COUNT] = {0};
29
+ H3_EXPORT(getPentagonIndexes)(res, h3Indexes);
30
+
31
+ int numFound = 0;
32
+
33
+ for (int i = 0; i < PADDED_COUNT; i++) {
34
+ H3Index h3Index = h3Indexes[i];
35
+ if (h3Index) {
36
+ numFound++;
37
+ t_assert(H3_EXPORT(h3IsValid(h3Index)),
38
+ "index should be valid");
39
+ t_assert(H3_EXPORT(h3IsPentagon(h3Index)),
40
+ "index should be pentagon");
41
+ t_assert(H3_EXPORT(h3GetResolution(h3Index)) == res,
42
+ "index should have correct resolution");
43
+
44
+ // verify uniqueness
45
+ for (int j = i + 1; j < PADDED_COUNT; j++) {
46
+ if (h3Indexes[j] == h3Index) {
47
+ t_assert(false, "index should be seen only once");
48
+ }
49
+ }
50
+ }
51
+ }
52
+
53
+ t_assert(numFound == expectedCount,
54
+ "there should be exactly 12 pentagons");
55
+ }
56
+ }
57
+ }
@@ -74,6 +74,8 @@
74
74
  #define NUM_HEX_VERTS 6
75
75
  /** The number of vertices in a pentagon */
76
76
  #define NUM_PENT_VERTS 5
77
+ /** The number of pentagons per resolution **/
78
+ #define NUM_PENTAGONS 12
77
79
 
78
80
  /** H3 index modes */
79
81
  #define H3_HEXAGON_MODE 1
@@ -293,6 +293,17 @@ int H3_EXPORT(res0IndexCount)();
293
293
  void H3_EXPORT(getRes0Indexes)(H3Index *out);
294
294
  /** @} */
295
295
 
296
+ /** @defgroup getPentagonIndexes getPentagonIndexes
297
+ * Functions for getPentagonIndexes
298
+ * @{
299
+ */
300
+ /** @brief returns the number of pentagons per resolution */
301
+ int H3_EXPORT(pentagonIndexCount)();
302
+
303
+ /** @brief generates all pentagons at the specified resolution */
304
+ void H3_EXPORT(getPentagonIndexes)(int res, H3Index *out);
305
+ /** @} */
306
+
296
307
  /** @defgroup h3GetResolution h3GetResolution
297
308
  * Functions for h3GetResolution
298
309
  * @{
@@ -355,6 +366,15 @@ int H3_EXPORT(maxH3ToChildrenSize)(H3Index h, int childRes);
355
366
  void H3_EXPORT(h3ToChildren)(H3Index h, int childRes, H3Index *children);
356
367
  /** @} */
357
368
 
369
+ /** @defgroup h3ToCenterChild h3ToCenterChild
370
+ * Functions for h3ToCenterChild
371
+ * @{
372
+ */
373
+ /** @brief returns the center child of the given hexagon at the specified
374
+ * resolution */
375
+ H3Index H3_EXPORT(h3ToCenterChild)(H3Index h, int childRes);
376
+ /** @} */
377
+
358
378
  /** @defgroup compact compact
359
379
  * Functions for compact
360
380
  * @{
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2016-2018 Uber Technologies, Inc.
2
+ * Copyright 2016-2019 Uber Technologies, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -469,7 +469,7 @@ int H3_EXPORT(hexRangeDistances)(H3Index origin, int k, H3Index* out,
469
469
  // the end of this ring.
470
470
  origin =
471
471
  h3NeighborRotations(origin, NEXT_RING_DIRECTION, &rotations);
472
- if (origin == 0) {
472
+ if (origin == 0) { // LCOV_EXCL_BR_LINE
473
473
  // Should not be possible because `origin` would have to be a
474
474
  // pentagon
475
475
  return HEX_RANGE_K_SUBSEQUENCE; // LCOV_EXCL_LINE
@@ -482,7 +482,7 @@ int H3_EXPORT(hexRangeDistances)(H3Index origin, int k, H3Index* out,
482
482
  }
483
483
 
484
484
  origin = h3NeighborRotations(origin, DIRECTIONS[direction], &rotations);
485
- if (origin == 0) {
485
+ if (origin == 0) { // LCOV_EXCL_BR_LINE
486
486
  // Should not be possible because `origin` would have to be a
487
487
  // pentagon
488
488
  return HEX_RANGE_K_SUBSEQUENCE; // LCOV_EXCL_LINE
@@ -569,7 +569,7 @@ int H3_EXPORT(hexRing)(H3Index origin, int k, H3Index* out) {
569
569
 
570
570
  for (int ring = 0; ring < k; ring++) {
571
571
  origin = h3NeighborRotations(origin, NEXT_RING_DIRECTION, &rotations);
572
- if (origin == 0) {
572
+ if (origin == 0) { // LCOV_EXCL_BR_LINE
573
573
  // Should not be possible because `origin` would have to be a
574
574
  // pentagon
575
575
  return HEX_RANGE_K_SUBSEQUENCE; // LCOV_EXCL_LINE
@@ -589,7 +589,7 @@ int H3_EXPORT(hexRing)(H3Index origin, int k, H3Index* out) {
589
589
  for (int pos = 0; pos < k; pos++) {
590
590
  origin =
591
591
  h3NeighborRotations(origin, DIRECTIONS[direction], &rotations);
592
- if (origin == 0) {
592
+ if (origin == 0) { // LCOV_EXCL_BR_LINE
593
593
  // Should not be possible because `origin` would have to be a
594
594
  // pentagon
595
595
  return HEX_RANGE_K_SUBSEQUENCE; // LCOV_EXCL_LINE
@@ -684,14 +684,14 @@ void _faceIjkToGeoBoundary(const FaceIJK* h, int res, int isPentagon,
684
684
  // edge-crossing vertices as needed
685
685
  g->numVerts = 0;
686
686
  int lastFace = -1;
687
- int lastOverage = 0; // 0: none; 1: edge; 2: overage
687
+ Overage lastOverage = NO_OVERAGE;
688
688
  for (int vert = 0; vert < NUM_HEX_VERTS + 1; vert++) {
689
689
  int v = vert % NUM_HEX_VERTS;
690
690
 
691
691
  FaceIJK fijk = fijkVerts[v];
692
692
 
693
693
  int pentLeading4 = 0;
694
- int overage = _adjustOverageClassII(&fijk, adjRes, pentLeading4, 1);
694
+ Overage overage = _adjustOverageClassII(&fijk, adjRes, pentLeading4, 1);
695
695
 
696
696
  /*
697
697
  Check for edge-crossing. Each face of the underlying icosahedron is a
@@ -703,7 +703,7 @@ void _faceIjkToGeoBoundary(const FaceIJK* h, int res, int isPentagon,
703
703
  edge, with no edge line intersections.
704
704
  */
705
705
  if (isResClassIII(res) && vert > 0 && fijk.face != lastFace &&
706
- lastOverage != 1) {
706
+ lastOverage != FACE_EDGE) {
707
707
  // find hex2d of the two vertexes on original face
708
708
  int lastV = (v + 5) % NUM_HEX_VERTS;
709
709
  Vec2d orig2d0;
@@ -145,6 +145,22 @@ H3Index H3_EXPORT(h3ToParent)(H3Index h, int parentRes) {
145
145
  return parentH;
146
146
  }
147
147
 
148
+ /**
149
+ * Determines whether one resolution is a valid child resolution of another.
150
+ * Each resolution is considered a valid child resolution of itself.
151
+ *
152
+ * @param parentRes int resolution of the parent
153
+ * @param childRes int resolution of the child
154
+ *
155
+ * @return The validity of the child resolution
156
+ */
157
+ static bool _isValidChildRes(int parentRes, int childRes) {
158
+ if (childRes < parentRes || childRes > MAX_H3_RES) {
159
+ return false;
160
+ }
161
+ return true;
162
+ }
163
+
148
164
  /**
149
165
  * maxH3ToChildrenSize returns the maximum number of children possible for a
150
166
  * given child level.
@@ -157,7 +173,7 @@ H3Index H3_EXPORT(h3ToParent)(H3Index h, int parentRes) {
157
173
  */
158
174
  int H3_EXPORT(maxH3ToChildrenSize)(H3Index h, int childRes) {
159
175
  int parentRes = H3_GET_RESOLUTION(h);
160
- if (parentRes > childRes) {
176
+ if (!_isValidChildRes(parentRes, childRes)) {
161
177
  return 0;
162
178
  }
163
179
  return _ipow(7, (childRes - parentRes));
@@ -191,7 +207,7 @@ H3Index makeDirectChild(H3Index h, int cellNumber) {
191
207
  */
192
208
  void H3_EXPORT(h3ToChildren)(H3Index h, int childRes, H3Index* children) {
193
209
  int parentRes = H3_GET_RESOLUTION(h);
194
- if (parentRes > childRes) {
210
+ if (!_isValidChildRes(parentRes, childRes)) {
195
211
  return;
196
212
  } else if (parentRes == childRes) {
197
213
  *children = h;
@@ -214,6 +230,29 @@ void H3_EXPORT(h3ToChildren)(H3Index h, int childRes, H3Index* children) {
214
230
  }
215
231
  }
216
232
 
233
+ /**
234
+ * h3ToCenterChild produces the center child index for a given H3 index at
235
+ * the specified resolution
236
+ *
237
+ * @param h H3Index to find center child of
238
+ * @param childRes The resolution to switch to
239
+ *
240
+ * @return H3Index of the center child, or 0 if you actually asked for a parent
241
+ */
242
+ H3Index H3_EXPORT(h3ToCenterChild)(H3Index h, int childRes) {
243
+ int parentRes = H3_GET_RESOLUTION(h);
244
+ if (!_isValidChildRes(parentRes, childRes)) {
245
+ return H3_INVALID_INDEX;
246
+ } else if (childRes == parentRes) {
247
+ return h;
248
+ }
249
+ H3Index child = H3_SET_RESOLUTION(h, childRes);
250
+ for (int i = parentRes + 1; i <= childRes; i++) {
251
+ H3_SET_INDEX_DIGIT(child, i, 0);
252
+ }
253
+ return child;
254
+ }
255
+
217
256
  /**
218
257
  * compact takes a set of hexagons all at the same resolution and compresses
219
258
  * them by pruning full child branches to the parent level. This is also done
@@ -254,7 +293,7 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
254
293
  int loc = (int)(parent % numRemainingHexes);
255
294
  int loopCount = 0;
256
295
  while (hashSetArray[loc] != 0) {
257
- if (loopCount > numRemainingHexes) {
296
+ if (loopCount > numRemainingHexes) { // LCOV_EXCL_BR_LINE
258
297
  // LCOV_EXCL_START
259
298
  // This case should not be possible because at most one
260
299
  // index is placed into hashSetArray per
@@ -330,7 +369,7 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
330
369
  int loopCount = 0;
331
370
  bool isUncompactable = true;
332
371
  do {
333
- if (loopCount > numRemainingHexes) {
372
+ if (loopCount > numRemainingHexes) { // LCOV_EXCL_BR_LINE
334
373
  // LCOV_EXCL_START
335
374
  // This case should not be possible because at most one
336
375
  // index is placed into hashSetArray per input hexagon.
@@ -393,7 +432,7 @@ int H3_EXPORT(uncompact)(const H3Index* compactedSet, const int numHexes,
393
432
  return -1;
394
433
  }
395
434
  int currentRes = H3_GET_RESOLUTION(compactedSet[i]);
396
- if (currentRes > res) {
435
+ if (!_isValidChildRes(currentRes, res)) {
397
436
  // Nonsensical. Abort.
398
437
  return -2;
399
438
  }
@@ -431,7 +470,7 @@ int H3_EXPORT(maxUncompactSize)(const H3Index* compactedSet, const int numHexes,
431
470
  for (int i = 0; i < numHexes; i++) {
432
471
  if (compactedSet[i] == 0) continue;
433
472
  int currentRes = H3_GET_RESOLUTION(compactedSet[i]);
434
- if (currentRes > res) {
473
+ if (!_isValidChildRes(currentRes, res)) {
435
474
  // Nonsensical. Abort.
436
475
  return -1;
437
476
  }
@@ -860,6 +899,30 @@ void H3_EXPORT(h3GetFaces)(H3Index h3, int* out) {
860
899
  }
861
900
  }
862
901
 
902
+ /**
903
+ * pentagonIndexCount returns the number of pentagons (same at any resolution)
904
+ *
905
+ * @return int count of pentagon indexes
906
+ */
907
+ int H3_EXPORT(pentagonIndexCount)() { return NUM_PENTAGONS; }
908
+
909
+ /**
910
+ * Generates all pentagons at the specified resolution
911
+ *
912
+ * @param res The resolution to produce pentagons at.
913
+ * @param out Output array. Must be of size pentagonIndexCount().
914
+ */
915
+ void H3_EXPORT(getPentagonIndexes)(int res, H3Index* out) {
916
+ int i = 0;
917
+ for (int bc = 0; bc < NUM_BASE_CELLS; bc++) {
918
+ if (_isBaseCellPentagon(bc)) {
919
+ H3Index pentagon;
920
+ setH3Index(&pentagon, res, bc, 0);
921
+ out[i++] = pentagon;
922
+ }
923
+ }
924
+ }
925
+
863
926
  /**
864
927
  * Returns whether or not a resolution is a Class III grid. Note that odd
865
928
  * resolutions are Class III and even resolutions are Class II.