h3 3.6.0 → 3.7.2

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 (175) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby_ci.yml +30 -0
  3. data/.rubocop.yml +1 -1
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +39 -0
  6. data/Gemfile.lock +6 -24
  7. data/LICENSE.md +1 -1
  8. data/README.md +2 -3
  9. data/ext/h3/src/.github/workflows/test-linux.yml +118 -0
  10. data/ext/h3/src/.github/workflows/test-macos.yml +42 -0
  11. data/ext/h3/src/.github/workflows/test-website.yml +32 -0
  12. data/ext/h3/src/.github/workflows/test-windows.yml +44 -0
  13. data/ext/h3/src/.gitignore +5 -0
  14. data/ext/h3/src/.travis.yml +20 -42
  15. data/ext/h3/src/CHANGELOG.md +57 -0
  16. data/ext/h3/src/CMakeLists.txt +135 -33
  17. data/ext/h3/src/CONTRIBUTING.md +1 -1
  18. data/ext/h3/src/README.md +61 -11
  19. data/ext/h3/src/RELEASE.md +3 -1
  20. data/ext/h3/src/VERSION +1 -1
  21. data/ext/h3/src/dev-docs/RFCs/rfc-template.md +21 -0
  22. data/ext/h3/src/dev-docs/RFCs/v4.0.0/error-handling-rfc.md +21 -0
  23. data/ext/h3/src/dev-docs/RFCs/v4.0.0/names_for_concepts_types_functions.md +276 -0
  24. data/ext/h3/src/dev-docs/RFCs/v4.0.0/overrideable-allocators-rfc.md +141 -0
  25. data/ext/h3/src/dev-docs/RFCs/v4.0.0/polyfill-modes-rfc.md +21 -0
  26. data/ext/h3/src/dev-docs/RFCs/v4.0.0/vertex-mode-rfc.md +50 -0
  27. data/ext/h3/src/dev-docs/build_windows.md +6 -1
  28. data/ext/h3/src/dev-docs/creating_bindings.md +3 -3
  29. data/ext/h3/src/dev-docs/custom_alloc.md +27 -0
  30. data/ext/h3/src/docs/{overview/mainpage.md → README.md} +2 -3
  31. data/ext/h3/src/docs/api/misc.md +76 -0
  32. data/ext/h3/src/docs/community/applications.md +1 -0
  33. data/ext/h3/src/docs/community/bindings.md +10 -0
  34. data/ext/h3/src/docs/community/tutorials.md +8 -3
  35. data/ext/h3/src/docs/core-library/coordsystems.md +5 -4
  36. data/ext/h3/src/docs/core-library/filters.md +8 -9
  37. data/ext/h3/src/docs/core-library/geoToH3desc.md +2 -3
  38. data/ext/h3/src/docs/core-library/h3ToGeoBoundaryDesc.md +4 -5
  39. data/ext/h3/src/docs/core-library/h3ToGeoDesc.md +3 -4
  40. data/ext/h3/src/docs/core-library/h3indexing.md +26 -17
  41. data/ext/h3/src/docs/core-library/overview.md +2 -3
  42. data/ext/h3/src/docs/core-library/restable.md +1 -2
  43. data/ext/h3/src/docs/core-library/usage.md +1 -2
  44. data/ext/h3/src/docs/table-of-contents.json +47 -0
  45. data/ext/h3/src/docs/{overview/usecases.md → usecases.md} +6 -11
  46. data/ext/h3/src/scripts/binding_functions.sh +1 -1
  47. data/ext/h3/src/scripts/coverage.sh.in +1 -1
  48. data/ext/h3/src/scripts/update_version.sh +2 -2
  49. data/ext/h3/src/src/apps/applib/include/args.h +1 -0
  50. data/ext/h3/src/src/apps/applib/include/test.h +1 -0
  51. data/ext/h3/src/src/apps/applib/include/utility.h +7 -1
  52. data/ext/h3/src/src/apps/applib/lib/args.c +2 -0
  53. data/ext/h3/src/src/apps/applib/lib/kml.c +2 -0
  54. data/ext/h3/src/src/apps/applib/lib/test.c +1 -0
  55. data/ext/h3/src/src/apps/applib/lib/utility.c +133 -2
  56. data/ext/h3/src/src/apps/benchmarks/benchmarkH3Api.c +1 -1
  57. data/ext/h3/src/{website/html.config.js → src/apps/benchmarks/benchmarkH3UniEdge.c} +15 -12
  58. data/ext/h3/src/src/apps/filters/h3ToComponents.c +1 -0
  59. data/ext/h3/src/src/apps/filters/h3ToGeo.c +1 -0
  60. data/ext/h3/src/src/apps/filters/h3ToGeoBoundary.c +1 -0
  61. data/ext/h3/src/src/apps/filters/h3ToLocalIj.c +1 -0
  62. data/ext/h3/src/src/apps/filters/hexRange.c +1 -0
  63. data/ext/h3/src/src/apps/filters/kRing.c +1 -0
  64. data/ext/h3/src/src/apps/filters/localIjToH3.c +1 -0
  65. data/ext/h3/src/src/apps/miscapps/generateFaceCenterPoint.c +1 -0
  66. data/ext/h3/src/src/apps/miscapps/generateNumHexagons.c +1 -0
  67. data/ext/h3/src/src/apps/miscapps/generatePentagonDirectionFaces.c +67 -0
  68. data/ext/h3/src/src/apps/miscapps/h3ToGeoBoundaryHier.c +1 -0
  69. data/ext/h3/src/src/apps/miscapps/h3ToGeoHier.c +1 -0
  70. data/ext/h3/src/src/apps/miscapps/h3ToHier.c +1 -0
  71. data/ext/h3/src/src/apps/testapps/mkRandGeo.c +1 -0
  72. data/ext/h3/src/src/apps/testapps/mkRandGeoBoundary.c +1 -0
  73. data/ext/h3/src/src/apps/testapps/testBBox.c +1 -0
  74. data/ext/h3/src/src/apps/testapps/testBaseCells.c +15 -1
  75. data/ext/h3/src/src/apps/testapps/testCompact.c +109 -2
  76. data/ext/h3/src/src/apps/testapps/testCoordIj.c +1 -0
  77. data/ext/h3/src/src/apps/testapps/testGeoCoord.c +47 -8
  78. data/ext/h3/src/src/apps/testapps/testGeoToH3.c +1 -0
  79. data/ext/h3/src/src/apps/testapps/testH3Api.c +1 -0
  80. data/ext/h3/src/src/apps/testapps/testH3CellArea.c +47 -0
  81. data/ext/h3/src/src/apps/testapps/testH3CellAreaExhaustive.c +180 -0
  82. data/ext/h3/src/src/apps/testapps/testH3Distance.c +1 -0
  83. data/ext/h3/src/src/apps/testapps/testH3DistanceExhaustive.c +1 -0
  84. data/ext/h3/src/src/apps/testapps/testH3GetFaces.c +1 -0
  85. data/ext/h3/src/src/apps/testapps/testH3Index.c +33 -3
  86. data/ext/h3/src/src/apps/testapps/testH3Line.c +1 -0
  87. data/ext/h3/src/src/apps/testapps/testH3LineExhaustive.c +1 -0
  88. data/ext/h3/src/src/apps/testapps/testH3Memory.c +175 -0
  89. data/ext/h3/src/src/apps/testapps/testH3NeighborRotations.c +1 -0
  90. data/ext/h3/src/src/apps/testapps/testH3SetToLinkedGeo.c +1 -0
  91. data/ext/h3/src/src/apps/testapps/testH3SetToVertexGraph.c +1 -0
  92. data/ext/h3/src/src/apps/testapps/testH3ToCenterChild.c +1 -0
  93. data/ext/h3/src/src/apps/testapps/testH3ToChildren.c +1 -0
  94. data/ext/h3/src/src/apps/testapps/testH3ToGeo.c +1 -0
  95. data/ext/h3/src/src/apps/testapps/testH3ToGeoBoundary.c +1 -0
  96. data/ext/h3/src/src/apps/testapps/testH3ToLocalIj.c +12 -6
  97. data/ext/h3/src/src/apps/testapps/testH3ToLocalIjExhaustive.c +1 -0
  98. data/ext/h3/src/src/apps/testapps/testH3ToParent.c +1 -0
  99. data/ext/h3/src/src/apps/testapps/testH3UniEdge.c +45 -16
  100. data/ext/h3/src/src/apps/testapps/testH3UniEdgeExhaustive.c +111 -0
  101. data/ext/h3/src/src/apps/testapps/testHexRanges.c +1 -0
  102. data/ext/h3/src/src/apps/testapps/testHexRing.c +1 -0
  103. data/ext/h3/src/src/apps/testapps/testKRing.c +19 -0
  104. data/ext/h3/src/src/apps/testapps/testLinkedGeo.c +1 -0
  105. data/ext/h3/src/src/apps/testapps/testMaxH3ToChildrenSize.c +1 -0
  106. data/ext/h3/src/src/apps/testapps/testPentagonIndexes.c +1 -0
  107. data/ext/h3/src/src/apps/testapps/testPolyfill.c +72 -9
  108. data/ext/h3/src/src/apps/testapps/testPolyfillReported.c +157 -0
  109. data/ext/h3/src/src/apps/testapps/testPolygon.c +27 -1
  110. data/ext/h3/src/src/apps/testapps/testVec2d.c +1 -0
  111. data/ext/h3/src/src/apps/testapps/testVec3d.c +1 -0
  112. data/ext/h3/src/src/apps/testapps/testVertex.c +66 -0
  113. data/ext/h3/src/src/apps/testapps/testVertexGraph.c +1 -0
  114. data/ext/h3/src/src/h3lib/include/algos.h +8 -0
  115. data/ext/h3/src/src/h3lib/include/alloc.h +40 -0
  116. data/ext/h3/src/src/h3lib/include/baseCells.h +4 -0
  117. data/ext/h3/src/src/h3lib/include/bbox.h +4 -1
  118. data/ext/h3/src/src/h3lib/include/faceijk.h +3 -2
  119. data/ext/h3/src/src/h3lib/include/geoCoord.h +2 -3
  120. data/ext/h3/src/src/h3lib/include/h3Index.h +37 -4
  121. data/ext/h3/src/src/h3lib/include/h3api.h.in +65 -17
  122. data/ext/h3/src/src/h3lib/include/linkedGeo.h +1 -0
  123. data/ext/h3/src/src/h3lib/include/polygon.h +1 -0
  124. data/ext/h3/src/src/h3lib/include/polygonAlgos.h +1 -0
  125. data/ext/h3/src/src/h3lib/include/vertex.h +44 -0
  126. data/ext/h3/src/src/h3lib/include/vertexGraph.h +1 -0
  127. data/ext/h3/src/src/h3lib/lib/algos.c +304 -76
  128. data/ext/h3/src/src/h3lib/lib/baseCells.c +26 -4
  129. data/ext/h3/src/src/h3lib/lib/bbox.c +56 -27
  130. data/ext/h3/src/src/h3lib/lib/coordijk.c +2 -0
  131. data/ext/h3/src/src/h3lib/lib/faceijk.c +32 -21
  132. data/ext/h3/src/src/h3lib/lib/geoCoord.c +162 -44
  133. data/ext/h3/src/src/h3lib/lib/h3Index.c +83 -42
  134. data/ext/h3/src/src/h3lib/lib/h3UniEdge.c +42 -57
  135. data/ext/h3/src/src/h3lib/lib/linkedGeo.c +20 -15
  136. data/ext/h3/src/src/h3lib/lib/localij.c +1 -1
  137. data/ext/h3/src/src/h3lib/lib/polygon.c +2 -0
  138. data/ext/h3/src/src/h3lib/lib/vec2d.c +1 -0
  139. data/ext/h3/src/src/h3lib/lib/vec3d.c +1 -0
  140. data/ext/h3/src/src/h3lib/lib/vertex.c +134 -0
  141. data/ext/h3/src/src/h3lib/lib/vertexGraph.c +8 -5
  142. data/ext/h3/src/website/.eslintignore +2 -0
  143. data/ext/h3/src/website/.gitignore +57 -0
  144. data/ext/h3/src/website/.nvmrc +1 -0
  145. data/ext/h3/src/website/README.md +8 -6
  146. data/ext/h3/src/website/gatsby-config.js +83 -0
  147. data/ext/h3/src/website/package.json +20 -12
  148. data/ext/h3/src/website/scripts/build-to-gh-pages.sh +7 -5
  149. data/ext/h3/src/website/src/.gitkeep +0 -0
  150. data/ext/h3/src/website/templates/documentation.jsx +129 -0
  151. data/ext/h3/src/website/yarn.lock +13723 -0
  152. data/h3.gemspec +2 -2
  153. data/lib/h3/bindings/base.rb +14 -4
  154. data/lib/h3/bindings/private.rb +12 -9
  155. data/lib/h3/hierarchy.rb +0 -18
  156. data/lib/h3/indexing.rb +0 -18
  157. data/lib/h3/inspection.rb +3 -59
  158. data/lib/h3/miscellaneous.rb +119 -14
  159. data/lib/h3/regions.rb +3 -0
  160. data/lib/h3/traversal.rb +0 -18
  161. data/lib/h3/unidirectional_edges.rb +5 -60
  162. data/lib/h3/version.rb +1 -1
  163. data/spec/geo_json_spec.rb +8 -0
  164. data/spec/miscellaneous_spec.rb +117 -0
  165. data/spec/{region_spec.rb → regions_spec.rb} +1 -1
  166. data/spec/spec_helper.rb +2 -2
  167. metadata +44 -36
  168. data/.travis.yml +0 -11
  169. data/ext/h3/src/.ycm_extra_conf.py +0 -92
  170. data/ext/h3/src/appveyor.yml +0 -50
  171. data/ext/h3/src/website/src/config.js +0 -46
  172. data/ext/h3/src/website/src/mdRoutes.js +0 -151
  173. data/ext/h3/src/website/src/styles/_variables.scss +0 -16
  174. data/ext/h3/src/website/src/styles/index.scss +0 -3
  175. data/ext/h3/src/website/static/index.html +0 -15
@@ -18,11 +18,14 @@
18
18
  * (see h3api.h for the main library entry functions)
19
19
  */
20
20
  #include "h3Index.h"
21
+
21
22
  #include <faceijk.h>
22
23
  #include <inttypes.h>
23
24
  #include <math.h>
24
25
  #include <stdlib.h>
25
26
  #include <string.h>
27
+
28
+ #include "alloc.h"
26
29
  #include "baseCells.h"
27
30
  #include "faceijk.h"
28
31
  #include "mathExtensions.h"
@@ -35,20 +38,25 @@
35
38
  int H3_EXPORT(h3GetResolution)(H3Index h) { return H3_GET_RESOLUTION(h); }
36
39
 
37
40
  /**
38
- * Returns the H3 base cell number of an H3 index.
39
- * @param h The H3 index.
40
- * @return The base cell of the H3 index argument.
41
+ * Returns the H3 base cell "number" of an H3 cell (hexagon or pentagon).
42
+ *
43
+ * Note: Technically works on H3 edges, but will return base cell of the
44
+ * origin cell.
45
+ *
46
+ * @param h The H3 cell.
47
+ * @return The base cell "number" of the H3 cell argument.
41
48
  */
42
49
  int H3_EXPORT(h3GetBaseCell)(H3Index h) { return H3_GET_BASE_CELL(h); }
43
50
 
44
51
  /**
45
52
  * Converts a string representation of an H3 index into an H3 index.
46
53
  * @param str The string representation of an H3 index.
47
- * @return The H3 index corresponding to the string argument, or 0 if invalid.
54
+ * @return The H3 index corresponding to the string argument, or H3_NULL if
55
+ * invalid.
48
56
  */
49
57
  H3Index H3_EXPORT(stringToH3)(const char* str) {
50
- H3Index h = H3_INVALID_INDEX;
51
- // If failed, h will be unmodified and we should return 0 anyways.
58
+ H3Index h = H3_NULL;
59
+ // If failed, h will be unmodified and we should return H3_NULL anyways.
52
60
  sscanf(str, "%" PRIx64, &h);
53
61
  return h;
54
62
  }
@@ -70,13 +78,17 @@ void H3_EXPORT(h3ToString)(H3Index h, char* str, size_t sz) {
70
78
  }
71
79
 
72
80
  /**
73
- * Returns whether or not an H3 index is valid.
81
+ * Returns whether or not an H3 index is a valid cell (hexagon or pentagon).
74
82
  * @param h The H3 index to validate.
75
83
  * @return 1 if the H3 index if valid, and 0 if it is not.
76
84
  */
77
85
  int H3_EXPORT(h3IsValid)(H3Index h) {
86
+ if (H3_GET_HIGH_BIT(h) != 0) return 0;
87
+
78
88
  if (H3_GET_MODE(h) != H3_HEXAGON_MODE) return 0;
79
89
 
90
+ if (H3_GET_RESERVED_BITS(h) != 0) return 0;
91
+
80
92
  int baseCell = H3_GET_BASE_CELL(h);
81
93
  if (baseCell < 0 || baseCell >= NUM_BASE_CELLS) return 0;
82
94
 
@@ -127,16 +139,16 @@ void setH3Index(H3Index* hp, int res, int baseCell, Direction initDigit) {
127
139
  * @param h H3Index to find parent of
128
140
  * @param parentRes The resolution to switch to (parent, grandparent, etc)
129
141
  *
130
- * @return H3Index of the parent, or 0 if you actually asked for a child
142
+ * @return H3Index of the parent, or H3_NULL if you actually asked for a child
131
143
  */
132
144
  H3Index H3_EXPORT(h3ToParent)(H3Index h, int parentRes) {
133
145
  int childRes = H3_GET_RESOLUTION(h);
134
146
  if (parentRes > childRes) {
135
- return H3_INVALID_INDEX;
147
+ return H3_NULL;
136
148
  } else if (parentRes == childRes) {
137
149
  return h;
138
150
  } else if (parentRes < 0 || parentRes > MAX_H3_RES) {
139
- return H3_INVALID_INDEX;
151
+ return H3_NULL;
140
152
  }
141
153
  H3Index parentH = H3_SET_RESOLUTION(h, parentRes);
142
154
  for (int i = parentRes + 1; i <= childRes; i++) {
@@ -220,7 +232,7 @@ void H3_EXPORT(h3ToChildren)(H3Index h, int childRes, H3Index* children) {
220
232
  if (isAPentagon && i == K_AXES_DIGIT) {
221
233
  H3Index* nextChild = children + bufferChildStep;
222
234
  while (children < nextChild) {
223
- *children = H3_INVALID_INDEX;
235
+ *children = H3_NULL;
224
236
  children++;
225
237
  }
226
238
  } else {
@@ -237,12 +249,13 @@ void H3_EXPORT(h3ToChildren)(H3Index h, int childRes, H3Index* children) {
237
249
  * @param h H3Index to find center child of
238
250
  * @param childRes The resolution to switch to
239
251
  *
240
- * @return H3Index of the center child, or 0 if you actually asked for a parent
252
+ * @return H3Index of the center child, or H3_NULL if you actually asked for a
253
+ * parent
241
254
  */
242
255
  H3Index H3_EXPORT(h3ToCenterChild)(H3Index h, int childRes) {
243
256
  int parentRes = H3_GET_RESOLUTION(h);
244
257
  if (!_isValidChildRes(parentRes, childRes)) {
245
- return H3_INVALID_INDEX;
258
+ return H3_NULL;
246
259
  } else if (childRes == parentRes) {
247
260
  return h;
248
261
  }
@@ -266,17 +279,27 @@ H3Index H3_EXPORT(h3ToCenterChild)(H3Index h, int childRes) {
266
279
  */
267
280
  int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
268
281
  const int numHexes) {
282
+ if (numHexes == 0) {
283
+ return COMPACT_SUCCESS;
284
+ }
269
285
  int res = H3_GET_RESOLUTION(h3Set[0]);
270
286
  if (res == 0) {
271
287
  // No compaction possible, just copy the set to output
272
288
  for (int i = 0; i < numHexes; i++) {
273
289
  compactedSet[i] = h3Set[i];
274
290
  }
275
- return 0;
291
+ return COMPACT_SUCCESS;
292
+ }
293
+ H3Index* remainingHexes = H3_MEMORY(malloc)(numHexes * sizeof(H3Index));
294
+ if (!remainingHexes) {
295
+ return COMPACT_ALLOC_FAILED;
276
296
  }
277
- H3Index* remainingHexes = malloc(numHexes * sizeof(H3Index));
278
297
  memcpy(remainingHexes, h3Set, numHexes * sizeof(H3Index));
279
- H3Index* hashSetArray = calloc(numHexes, sizeof(H3Index));
298
+ H3Index* hashSetArray = H3_MEMORY(calloc)(numHexes, sizeof(H3Index));
299
+ if (!hashSetArray) {
300
+ H3_MEMORY(free)(remainingHexes);
301
+ return COMPACT_ALLOC_FAILED;
302
+ }
280
303
  H3Index* compactedSetOffset = compactedSet;
281
304
  int numRemainingHexes = numHexes;
282
305
  while (numRemainingHexes) {
@@ -298,23 +321,31 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
298
321
  // This case should not be possible because at most one
299
322
  // index is placed into hashSetArray per
300
323
  // numRemainingHexes.
301
- free(remainingHexes);
302
- free(hashSetArray);
303
- return -1;
324
+ H3_MEMORY(free)(remainingHexes);
325
+ H3_MEMORY(free)(hashSetArray);
326
+ return COMPACT_LOOP_EXCEEDED;
304
327
  // LCOV_EXCL_STOP
305
328
  }
306
329
  H3Index tempIndex =
307
330
  hashSetArray[loc] & H3_RESERVED_MASK_NEGATIVE;
308
331
  if (tempIndex == parent) {
309
332
  int count = H3_GET_RESERVED_BITS(hashSetArray[loc]) + 1;
310
- if (count > 7) {
333
+ int limitCount = 7;
334
+ if (H3_EXPORT(h3IsPentagon)(
335
+ tempIndex & H3_RESERVED_MASK_NEGATIVE)) {
336
+ limitCount--;
337
+ }
338
+ // One is added to count for this check to match one
339
+ // being added to count later in this function when
340
+ // checking for all children being present.
341
+ if (count + 1 > limitCount) {
311
342
  // Only possible on duplicate input
312
- free(remainingHexes);
313
- free(hashSetArray);
314
- return -2;
343
+ H3_MEMORY(free)(remainingHexes);
344
+ H3_MEMORY(free)(hashSetArray);
345
+ return COMPACT_DUPLICATE;
315
346
  }
316
347
  H3_SET_RESERVED_BITS(parent, count);
317
- hashSetArray[loc] = H3_INVALID_INDEX;
348
+ hashSetArray[loc] = H3_NULL;
318
349
  } else {
319
350
  loc = (loc + 1) % numRemainingHexes;
320
351
  }
@@ -334,7 +365,12 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
334
365
  break;
335
366
  }
336
367
  H3Index* compactableHexes =
337
- malloc(maxCompactableCount * sizeof(H3Index));
368
+ H3_MEMORY(calloc)(maxCompactableCount, sizeof(H3Index));
369
+ if (!compactableHexes) {
370
+ H3_MEMORY(free)(remainingHexes);
371
+ H3_MEMORY(free)(hashSetArray);
372
+ return COMPACT_ALLOC_FAILED;
373
+ }
338
374
  for (int i = 0; i < numRemainingHexes; i++) {
339
375
  if (hashSetArray[i] == 0) continue;
340
376
  int count = H3_GET_RESERVED_BITS(hashSetArray[i]) + 1;
@@ -360,7 +396,7 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
360
396
  int uncompactableCount = 0;
361
397
  for (int i = 0; i < numRemainingHexes; i++) {
362
398
  H3Index currIndex = remainingHexes[i];
363
- if (currIndex != H3_INVALID_INDEX) {
399
+ if (currIndex != H3_NULL) {
364
400
  H3Index parent = H3_EXPORT(h3ToParent)(currIndex, parentRes);
365
401
  // Modulus hash the parent into the temp array
366
402
  // to determine if this index was included in
@@ -373,10 +409,10 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
373
409
  // LCOV_EXCL_START
374
410
  // This case should not be possible because at most one
375
411
  // index is placed into hashSetArray per input hexagon.
376
- free(compactableHexes);
377
- free(remainingHexes);
378
- free(hashSetArray);
379
- return -1; // Only possible on duplicate input
412
+ H3_MEMORY(free)(compactableHexes);
413
+ H3_MEMORY(free)(remainingHexes);
414
+ H3_MEMORY(free)(hashSetArray);
415
+ return COMPACT_LOOP_EXCEEDED;
380
416
  // LCOV_EXCL_STOP
381
417
  }
382
418
  H3Index tempIndex =
@@ -404,11 +440,11 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
404
440
  memcpy(remainingHexes, compactableHexes,
405
441
  compactableCount * sizeof(H3Index));
406
442
  numRemainingHexes = compactableCount;
407
- free(compactableHexes);
443
+ H3_MEMORY(free)(compactableHexes);
408
444
  }
409
- free(remainingHexes);
410
- free(hashSetArray);
411
- return 0;
445
+ H3_MEMORY(free)(remainingHexes);
446
+ H3_MEMORY(free)(hashSetArray);
447
+ return COMPACT_SUCCESS;
412
448
  }
413
449
 
414
450
  /**
@@ -600,7 +636,7 @@ H3Index _h3Rotate60cw(H3Index h) {
600
636
  * Convert an FaceIJK address to the corresponding H3Index.
601
637
  * @param fijk The FaceIJK address.
602
638
  * @param res The cell resolution.
603
- * @return The encoded H3Index (or 0 on failure).
639
+ * @return The encoded H3Index (or H3_NULL on failure).
604
640
  */
605
641
  H3Index _faceIjkToH3(const FaceIJK* fijk, int res) {
606
642
  // initialize the index
@@ -613,7 +649,7 @@ H3Index _faceIjkToH3(const FaceIJK* fijk, int res) {
613
649
  if (fijk->coord.i > MAX_FACE_COORD || fijk->coord.j > MAX_FACE_COORD ||
614
650
  fijk->coord.k > MAX_FACE_COORD) {
615
651
  // out of range input
616
- return H3_INVALID_INDEX;
652
+ return H3_NULL;
617
653
  }
618
654
 
619
655
  H3_SET_BASE_CELL(h, _faceIjkToBaseCell(fijk));
@@ -657,7 +693,7 @@ H3Index _faceIjkToH3(const FaceIJK* fijk, int res) {
657
693
  if (fijkBC.coord.i > MAX_FACE_COORD || fijkBC.coord.j > MAX_FACE_COORD ||
658
694
  fijkBC.coord.k > MAX_FACE_COORD) {
659
695
  // out of range input
660
- return H3_INVALID_INDEX;
696
+ return H3_NULL;
661
697
  }
662
698
 
663
699
  // lookup the correct base cell
@@ -696,14 +732,14 @@ H3Index _faceIjkToH3(const FaceIJK* fijk, int res) {
696
732
  *
697
733
  * @param g The spherical coordinates to encode.
698
734
  * @param res The desired H3 resolution for the encoding.
699
- * @return The encoded H3Index (or 0 on failure).
735
+ * @return The encoded H3Index (or H3_NULL on failure).
700
736
  */
701
737
  H3Index H3_EXPORT(geoToH3)(const GeoCoord* g, int res) {
702
738
  if (res < 0 || res > MAX_H3_RES) {
703
- return H3_INVALID_INDEX;
739
+ return H3_NULL;
704
740
  }
705
741
  if (!isfinite(g->lat) || !isfinite(g->lon)) {
706
- return H3_INVALID_INDEX;
742
+ return H3_NULL;
707
743
  }
708
744
 
709
745
  FaceIJK fijk;
@@ -813,8 +849,13 @@ void H3_EXPORT(h3ToGeo)(H3Index h3, GeoCoord* g) {
813
849
  void H3_EXPORT(h3ToGeoBoundary)(H3Index h3, GeoBoundary* gb) {
814
850
  FaceIJK fijk;
815
851
  _h3ToFaceIjk(h3, &fijk);
816
- _faceIjkToGeoBoundary(&fijk, H3_GET_RESOLUTION(h3),
817
- H3_EXPORT(h3IsPentagon)(h3), gb);
852
+ if (H3_EXPORT(h3IsPentagon)(h3)) {
853
+ _faceIjkPentToGeoBoundary(&fijk, H3_GET_RESOLUTION(h3), 0,
854
+ NUM_PENT_VERTS, gb);
855
+ } else {
856
+ _faceIjkToGeoBoundary(&fijk, H3_GET_RESOLUTION(h3), 0, NUM_HEX_VERTS,
857
+ gb);
858
+ }
818
859
  }
819
860
 
820
861
  /**
@@ -19,11 +19,13 @@
19
19
 
20
20
  #include <inttypes.h>
21
21
  #include <stdbool.h>
22
+
22
23
  #include "algos.h"
23
24
  #include "constants.h"
24
25
  #include "coordijk.h"
25
26
  #include "geoCoord.h"
26
27
  #include "h3Index.h"
28
+ #include "vertex.h"
27
29
 
28
30
  /**
29
31
  * Returns whether or not the provided H3Indexes are neighbors.
@@ -96,25 +98,30 @@ int H3_EXPORT(h3IndexesAreNeighbors)(H3Index origin, H3Index destination) {
96
98
  * destination
97
99
  * @param origin The origin H3 hexagon index
98
100
  * @param destination The destination H3 hexagon index
99
- * @return The unidirectional edge H3Index, or 0 on failure.
101
+ * @return The unidirectional edge H3Index, or H3_NULL on failure.
100
102
  */
101
103
  H3Index H3_EXPORT(getH3UnidirectionalEdge)(H3Index origin,
102
104
  H3Index destination) {
103
105
  // Short-circuit and return an invalid index value if they are not neighbors
104
106
  if (H3_EXPORT(h3IndexesAreNeighbors)(origin, destination) == 0) {
105
- return H3_INVALID_INDEX;
107
+ return H3_NULL;
106
108
  }
107
109
 
108
110
  // Otherwise, determine the IJK direction from the origin to the destination
109
111
  H3Index output = origin;
110
112
  H3_SET_MODE(output, H3_UNIEDGE_MODE);
111
113
 
114
+ bool isPentagon = H3_EXPORT(h3IsPentagon)(origin);
115
+
112
116
  // Checks each neighbor, in order, to determine which direction the
113
117
  // destination neighbor is located. Skips CENTER_DIGIT since that
114
118
  // would be this index.
115
119
  H3Index neighbor;
116
- for (Direction direction = K_AXES_DIGIT; direction < NUM_DIGITS;
117
- direction++) {
120
+ // Excluding from branch coverage as we never hit the end condition
121
+ // LCOV_EXCL_BR_START
122
+ for (Direction direction = isPentagon ? J_AXES_DIGIT : K_AXES_DIGIT;
123
+ direction < NUM_DIGITS; direction++) {
124
+ // LCOV_EXCL_BR_STOP
118
125
  int rotations = 0;
119
126
  neighbor = h3NeighborRotations(origin, direction, &rotations);
120
127
  if (neighbor == destination) {
@@ -123,18 +130,18 @@ H3Index H3_EXPORT(getH3UnidirectionalEdge)(H3Index origin,
123
130
  }
124
131
  }
125
132
 
126
- // This should be impossible, return an invalid H3Index in this case;
127
- return H3_INVALID_INDEX; // LCOV_EXCL_LINE
133
+ // This should be impossible, return H3_NULL in this case;
134
+ return H3_NULL; // LCOV_EXCL_LINE
128
135
  }
129
136
 
130
137
  /**
131
138
  * Returns the origin hexagon from the unidirectional edge H3Index
132
139
  * @param edge The edge H3 index
133
- * @return The origin H3 hexagon index
140
+ * @return The origin H3 hexagon index, or H3_NULL on failure
134
141
  */
135
142
  H3Index H3_EXPORT(getOriginH3IndexFromUnidirectionalEdge)(H3Index edge) {
136
143
  if (H3_GET_MODE(edge) != H3_UNIEDGE_MODE) {
137
- return H3_INVALID_INDEX;
144
+ return H3_NULL;
138
145
  }
139
146
  H3Index origin = edge;
140
147
  H3_SET_MODE(origin, H3_HEXAGON_MODE);
@@ -145,11 +152,11 @@ H3Index H3_EXPORT(getOriginH3IndexFromUnidirectionalEdge)(H3Index edge) {
145
152
  /**
146
153
  * Returns the destination hexagon from the unidirectional edge H3Index
147
154
  * @param edge The edge H3 index
148
- * @return The destination H3 hexagon index
155
+ * @return The destination H3 hexagon index, or H3_NULL on failure
149
156
  */
150
157
  H3Index H3_EXPORT(getDestinationH3IndexFromUnidirectionalEdge)(H3Index edge) {
151
158
  if (H3_GET_MODE(edge) != H3_UNIEDGE_MODE) {
152
- return H3_INVALID_INDEX;
159
+ return H3_NULL;
153
160
  }
154
161
  Direction direction = H3_GET_RESERVED_BITS(edge);
155
162
  int rotations = 0;
@@ -211,7 +218,7 @@ void H3_EXPORT(getH3UnidirectionalEdgesFromHexagon)(H3Index origin,
211
218
  // which is zeroed.
212
219
  for (int i = 0; i < 6; i++) {
213
220
  if (isPentagon && i == 0) {
214
- edges[i] = H3_INVALID_INDEX;
221
+ edges[i] = H3_NULL;
215
222
  } else {
216
223
  edges[i] = origin;
217
224
  H3_SET_MODE(edges[i], H3_UNIEDGE_MODE);
@@ -220,59 +227,37 @@ void H3_EXPORT(getH3UnidirectionalEdgesFromHexagon)(H3Index origin,
220
227
  }
221
228
  }
222
229
 
223
- /**
224
- * Whether the given coordinate has a matching vertex in the given geo boundary.
225
- * @param vertex Coordinate to check
226
- * @param boundary Geo boundary to look in
227
- * @return Whether a match was found
228
- */
229
- static bool _hasMatchingVertex(const GeoCoord* vertex,
230
- const GeoBoundary* boundary) {
231
- for (int i = 0; i < boundary->numVerts; i++) {
232
- if (geoAlmostEqualThreshold(vertex, &boundary->verts[i], 0.000001)) {
233
- return true;
234
- }
235
- }
236
- return false;
237
- }
238
-
239
230
  /**
240
231
  * Provides the coordinates defining the unidirectional edge.
241
232
  * @param edge The unidirectional edge H3Index
242
233
  * @param gb The geoboundary object to store the edge coordinates.
243
234
  */
244
235
  void H3_EXPORT(getH3UnidirectionalEdgeBoundary)(H3Index edge, GeoBoundary* gb) {
245
- // TODO: More efficient solution :)
246
- GeoBoundary origin = {0};
247
- GeoBoundary destination = {0};
248
- GeoCoord postponedVertex = {0};
249
- bool hasPostponedVertex = false;
250
-
251
- H3_EXPORT(h3ToGeoBoundary)
252
- (H3_EXPORT(getOriginH3IndexFromUnidirectionalEdge)(edge), &origin);
253
- H3_EXPORT(h3ToGeoBoundary)
254
- (H3_EXPORT(getDestinationH3IndexFromUnidirectionalEdge)(edge),
255
- &destination);
236
+ // Get the origin and neighbor direction from the edge
237
+ Direction direction = H3_GET_RESERVED_BITS(edge);
238
+ H3Index origin = H3_EXPORT(getOriginH3IndexFromUnidirectionalEdge)(edge);
256
239
 
257
- int k = 0;
258
- for (int i = 0; i < origin.numVerts; i++) {
259
- if (_hasMatchingVertex(&origin.verts[i], &destination)) {
260
- // If we are on vertex 0, we need to handle the case where it's the
261
- // end of the edge, not the beginning.
262
- if (i == 0 &&
263
- !_hasMatchingVertex(&origin.verts[i + 1], &destination)) {
264
- postponedVertex = origin.verts[i];
265
- hasPostponedVertex = true;
266
- } else {
267
- gb->verts[k] = origin.verts[i];
268
- k++;
269
- }
270
- }
240
+ // Get the start vertex for the edge
241
+ int startVertex = vertexNumForDirection(origin, direction);
242
+ if (startVertex == INVALID_VERTEX_NUM) {
243
+ // This is not actually an edge (i.e. no valid direction),
244
+ // so return no vertices.
245
+ gb->numVerts = 0;
246
+ return;
271
247
  }
272
- // If we postponed adding the last vertex, add it now
273
- if (hasPostponedVertex) {
274
- gb->verts[k] = postponedVertex;
275
- k++;
248
+
249
+ // Get the geo boundary for the appropriate vertexes of the origin. Note
250
+ // that while there are always 2 topological vertexes per edge, the
251
+ // resulting edge boundary may have an additional distortion vertex if it
252
+ // crosses an edge of the icosahedron.
253
+ FaceIJK fijk;
254
+ _h3ToFaceIjk(origin, &fijk);
255
+ int res = H3_GET_RESOLUTION(origin);
256
+ int isPentagon = H3_EXPORT(h3IsPentagon)(origin);
257
+
258
+ if (isPentagon) {
259
+ _faceIjkPentToGeoBoundary(&fijk, res, startVertex, 2, gb);
260
+ } else {
261
+ _faceIjkToGeoBoundary(&fijk, res, startVertex, 2, gb);
276
262
  }
277
- gb->numVerts = k;
278
263
  }