h3 3.5.1 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
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.