h3 3.5.0 → 3.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +45 -0
  3. data/Gemfile.lock +9 -7
  4. data/README.md +8 -8
  5. data/ext/h3/src/.github/workflows/test-linux.yml +118 -0
  6. data/ext/h3/src/.github/workflows/test-macos.yml +42 -0
  7. data/ext/h3/src/.github/workflows/test-website.yml +32 -0
  8. data/ext/h3/src/.github/workflows/test-windows.yml +44 -0
  9. data/ext/h3/src/.gitignore +5 -0
  10. data/ext/h3/src/.travis.yml +21 -32
  11. data/ext/h3/src/CHANGELOG.md +60 -0
  12. data/ext/h3/src/CMakeLists.txt +150 -33
  13. data/ext/h3/src/CONTRIBUTING.md +1 -1
  14. data/ext/h3/src/README.md +65 -16
  15. data/ext/h3/src/RELEASE.md +3 -1
  16. data/ext/h3/src/VERSION +1 -1
  17. data/ext/h3/src/dev-docs/RFCs/rfc-template.md +21 -0
  18. data/ext/h3/src/dev-docs/RFCs/v4.0.0/error-handling-rfc.md +21 -0
  19. data/ext/h3/src/dev-docs/RFCs/v4.0.0/names_for_concepts_types_functions.md +276 -0
  20. data/ext/h3/src/dev-docs/RFCs/v4.0.0/overrideable-allocators-rfc.md +141 -0
  21. data/ext/h3/src/dev-docs/RFCs/v4.0.0/polyfill-modes-rfc.md +21 -0
  22. data/ext/h3/src/dev-docs/RFCs/v4.0.0/vertex-mode-rfc.md +50 -0
  23. data/ext/h3/src/dev-docs/build_windows.md +6 -1
  24. data/ext/h3/src/dev-docs/creating_bindings.md +3 -3
  25. data/ext/h3/src/dev-docs/custom_alloc.md +27 -0
  26. data/ext/h3/src/docs/{overview/mainpage.md → README.md} +2 -3
  27. data/ext/h3/src/docs/api/hierarchy.md +8 -0
  28. data/ext/h3/src/docs/api/misc.md +94 -0
  29. data/ext/h3/src/docs/community/applications.md +1 -0
  30. data/ext/h3/src/docs/community/bindings.md +10 -0
  31. data/ext/h3/src/docs/community/tutorials.md +8 -3
  32. data/ext/h3/src/docs/core-library/coordsystems.md +5 -4
  33. data/ext/h3/src/docs/core-library/filters.md +8 -9
  34. data/ext/h3/src/docs/core-library/geoToH3desc.md +2 -3
  35. data/ext/h3/src/docs/core-library/h3ToGeoBoundaryDesc.md +4 -5
  36. data/ext/h3/src/docs/core-library/h3ToGeoDesc.md +3 -4
  37. data/ext/h3/src/docs/core-library/h3indexing.md +26 -17
  38. data/ext/h3/src/docs/core-library/overview.md +2 -3
  39. data/ext/h3/src/docs/core-library/restable.md +1 -2
  40. data/ext/h3/src/docs/core-library/usage.md +1 -2
  41. data/ext/h3/src/docs/table-of-contents.json +47 -0
  42. data/ext/h3/src/docs/{overview/usecases.md → usecases.md} +6 -11
  43. data/ext/h3/src/scripts/binding_functions.sh +1 -1
  44. data/ext/h3/src/scripts/coverage.sh.in +8 -4
  45. data/ext/h3/src/scripts/update_version.sh +2 -2
  46. data/ext/h3/src/src/apps/applib/include/args.h +1 -0
  47. data/ext/h3/src/src/apps/applib/include/test.h +1 -0
  48. data/ext/h3/src/src/apps/applib/include/utility.h +7 -1
  49. data/ext/h3/src/src/apps/applib/lib/args.c +2 -0
  50. data/ext/h3/src/src/apps/applib/lib/kml.c +2 -0
  51. data/ext/h3/src/src/apps/applib/lib/test.c +1 -0
  52. data/ext/h3/src/src/apps/applib/lib/utility.c +133 -2
  53. data/ext/h3/src/src/apps/benchmarks/benchmarkH3Api.c +1 -1
  54. data/ext/h3/src/{website/html.config.js → src/apps/benchmarks/benchmarkH3UniEdge.c} +15 -12
  55. data/ext/h3/src/src/apps/filters/h3ToComponents.c +1 -0
  56. data/ext/h3/src/src/apps/filters/h3ToGeo.c +1 -0
  57. data/ext/h3/src/src/apps/filters/h3ToGeoBoundary.c +1 -0
  58. data/ext/h3/src/src/apps/filters/h3ToLocalIj.c +1 -0
  59. data/ext/h3/src/src/apps/filters/hexRange.c +1 -0
  60. data/ext/h3/src/src/apps/filters/kRing.c +1 -0
  61. data/ext/h3/src/src/apps/filters/localIjToH3.c +1 -0
  62. data/ext/h3/src/src/apps/miscapps/generateBaseCellNeighbors.c +2 -2
  63. data/ext/h3/src/src/apps/miscapps/generateFaceCenterPoint.c +1 -0
  64. data/ext/h3/src/src/apps/miscapps/generateNumHexagons.c +1 -2
  65. data/ext/h3/src/src/apps/miscapps/generatePentagonDirectionFaces.c +67 -0
  66. data/ext/h3/src/src/apps/miscapps/h3ToGeoBoundaryHier.c +1 -0
  67. data/ext/h3/src/src/apps/miscapps/h3ToGeoHier.c +1 -0
  68. data/ext/h3/src/src/apps/miscapps/h3ToHier.c +1 -0
  69. data/ext/h3/src/src/apps/testapps/mkRandGeo.c +1 -0
  70. data/ext/h3/src/src/apps/testapps/mkRandGeoBoundary.c +1 -0
  71. data/ext/h3/src/src/apps/testapps/testBBox.c +1 -0
  72. data/ext/h3/src/src/apps/testapps/testBaseCells.c +15 -1
  73. data/ext/h3/src/src/apps/testapps/testCompact.c +121 -2
  74. data/ext/h3/src/src/apps/testapps/testCoordIj.c +1 -0
  75. data/ext/h3/src/src/apps/testapps/testGeoCoord.c +47 -8
  76. data/ext/h3/src/src/apps/testapps/testGeoToH3.c +1 -0
  77. data/ext/h3/src/src/apps/testapps/testH3Api.c +1 -0
  78. data/ext/h3/src/src/apps/testapps/testH3CellArea.c +47 -0
  79. data/ext/h3/src/src/apps/testapps/testH3CellAreaExhaustive.c +180 -0
  80. data/ext/h3/src/src/apps/testapps/testH3Distance.c +2 -50
  81. data/ext/h3/src/src/apps/testapps/testH3DistanceExhaustive.c +84 -0
  82. data/ext/h3/src/src/apps/testapps/testH3GetFaces.c +1 -0
  83. data/ext/h3/src/src/apps/testapps/testH3Index.c +33 -3
  84. data/ext/h3/src/src/apps/testapps/testH3Line.c +2 -84
  85. data/ext/h3/src/src/apps/testapps/testH3LineExhaustive.c +115 -0
  86. data/ext/h3/src/src/apps/testapps/testH3Memory.c +175 -0
  87. data/ext/h3/src/src/apps/testapps/testH3NeighborRotations.c +1 -0
  88. data/ext/h3/src/src/apps/testapps/testH3SetToLinkedGeo.c +1 -0
  89. data/ext/h3/src/src/apps/testapps/testH3SetToVertexGraph.c +1 -0
  90. data/ext/h3/src/src/apps/testapps/testH3ToCenterChild.c +68 -0
  91. data/ext/h3/src/src/apps/testapps/testH3ToChildren.c +15 -2
  92. data/ext/h3/src/src/apps/testapps/testH3ToGeo.c +1 -0
  93. data/ext/h3/src/src/apps/testapps/testH3ToGeoBoundary.c +1 -0
  94. data/ext/h3/src/src/apps/testapps/testH3ToLocalIj.c +24 -236
  95. data/ext/h3/src/src/apps/testapps/testH3ToLocalIjExhaustive.c +265 -0
  96. data/ext/h3/src/src/apps/testapps/testH3ToParent.c +1 -0
  97. data/ext/h3/src/src/apps/testapps/testH3UniEdge.c +45 -16
  98. data/ext/h3/src/src/apps/testapps/testH3UniEdgeExhaustive.c +111 -0
  99. data/ext/h3/src/src/apps/testapps/testHexRanges.c +1 -0
  100. data/ext/h3/src/src/apps/testapps/testHexRing.c +1 -0
  101. data/ext/h3/src/src/apps/testapps/testKRing.c +1 -0
  102. data/ext/h3/src/src/apps/testapps/testLinkedGeo.c +1 -0
  103. data/ext/h3/src/src/apps/testapps/testMaxH3ToChildrenSize.c +1 -0
  104. data/ext/h3/src/src/apps/testapps/testPentagonIndexes.c +58 -0
  105. data/ext/h3/src/src/apps/testapps/testPolyfill.c +72 -9
  106. data/ext/h3/src/src/apps/testapps/testPolyfillReported.c +157 -0
  107. data/ext/h3/src/src/apps/testapps/testPolygon.c +27 -1
  108. data/ext/h3/src/src/apps/testapps/testVec2d.c +1 -0
  109. data/ext/h3/src/src/apps/testapps/testVec3d.c +1 -0
  110. data/ext/h3/src/src/apps/testapps/testVertex.c +66 -0
  111. data/ext/h3/src/src/apps/testapps/testVertexGraph.c +1 -0
  112. data/ext/h3/src/src/h3lib/include/algos.h +8 -0
  113. data/ext/h3/src/src/h3lib/include/alloc.h +40 -0
  114. data/ext/h3/src/src/h3lib/include/baseCells.h +4 -0
  115. data/ext/h3/src/src/h3lib/include/bbox.h +4 -1
  116. data/ext/h3/src/src/h3lib/include/constants.h +2 -0
  117. data/ext/h3/src/src/h3lib/include/faceijk.h +3 -2
  118. data/ext/h3/src/src/h3lib/include/geoCoord.h +2 -3
  119. data/ext/h3/src/src/h3lib/include/h3Index.h +37 -4
  120. data/ext/h3/src/src/h3lib/include/h3api.h.in +85 -17
  121. data/ext/h3/src/src/h3lib/include/linkedGeo.h +1 -0
  122. data/ext/h3/src/src/h3lib/include/polygon.h +1 -0
  123. data/ext/h3/src/src/h3lib/include/polygonAlgos.h +1 -0
  124. data/ext/h3/src/src/h3lib/include/vertex.h +44 -0
  125. data/ext/h3/src/src/h3lib/include/vertexGraph.h +1 -0
  126. data/ext/h3/src/src/h3lib/lib/algos.c +305 -80
  127. data/ext/h3/src/src/h3lib/lib/baseCells.c +26 -4
  128. data/ext/h3/src/src/h3lib/lib/bbox.c +56 -27
  129. data/ext/h3/src/src/h3lib/lib/coordijk.c +2 -0
  130. data/ext/h3/src/src/h3lib/lib/faceijk.c +35 -24
  131. data/ext/h3/src/src/h3lib/lib/geoCoord.c +162 -44
  132. data/ext/h3/src/src/h3lib/lib/h3Index.c +150 -46
  133. data/ext/h3/src/src/h3lib/lib/h3UniEdge.c +42 -57
  134. data/ext/h3/src/src/h3lib/lib/linkedGeo.c +20 -15
  135. data/ext/h3/src/src/h3lib/lib/localij.c +5 -5
  136. data/ext/h3/src/src/h3lib/lib/polygon.c +3 -2
  137. data/ext/h3/src/src/h3lib/lib/vec2d.c +1 -0
  138. data/ext/h3/src/src/h3lib/lib/vec3d.c +1 -0
  139. data/ext/h3/src/src/h3lib/lib/vertex.c +134 -0
  140. data/ext/h3/src/src/h3lib/lib/vertexGraph.c +8 -5
  141. data/ext/h3/src/website/.eslintignore +2 -0
  142. data/ext/h3/src/website/.gitignore +57 -0
  143. data/ext/h3/src/website/.nvmrc +1 -0
  144. data/ext/h3/src/website/README.md +8 -6
  145. data/ext/h3/src/website/gatsby-config.js +83 -0
  146. data/ext/h3/src/website/package.json +20 -12
  147. data/ext/h3/src/website/scripts/build-to-gh-pages.sh +7 -5
  148. data/ext/h3/src/website/src/.gitkeep +0 -0
  149. data/ext/h3/src/website/templates/documentation.jsx +129 -0
  150. data/ext/h3/src/website/yarn.lock +13723 -0
  151. data/h3.gemspec +1 -0
  152. data/lib/h3.rb +8 -23
  153. data/lib/h3/bindings/base.rb +15 -4
  154. data/lib/h3/bindings/private.rb +13 -9
  155. data/lib/h3/geo_json.rb +1 -1
  156. data/lib/h3/hierarchy.rb +24 -9
  157. data/lib/h3/indexing.rb +7 -7
  158. data/lib/h3/inspection.rb +22 -26
  159. data/lib/h3/miscellaneous.rb +157 -9
  160. data/lib/h3/regions.rb +3 -0
  161. data/lib/h3/traversal.rb +9 -9
  162. data/lib/h3/unidirectional_edges.rb +18 -18
  163. data/lib/h3/version.rb +1 -1
  164. data/spec/geo_json_spec.rb +8 -0
  165. data/spec/hierarchy_spec.rb +23 -13
  166. data/spec/indexing_spec.rb +15 -15
  167. data/spec/inspection_spec.rb +17 -17
  168. data/spec/miscellaneous_spec.rb +151 -6
  169. data/spec/{region_spec.rb → regions_spec.rb} +1 -1
  170. data/spec/traversal_spec.rb +6 -6
  171. data/spec/unidirectional_edges_spec.rb +18 -18
  172. metadata +55 -15
  173. data/ext/h3/src/.ycm_extra_conf.py +0 -92
  174. data/ext/h3/src/appveyor.yml +0 -50
  175. data/ext/h3/src/website/src/config.js +0 -46
  176. data/ext/h3/src/website/src/mdRoutes.js +0 -151
  177. data/ext/h3/src/website/src/styles/_variables.scss +0 -16
  178. data/ext/h3/src/website/src/styles/index.scss +0 -3
  179. data/ext/h3/src/website/static/index.html +0 -15
  180. data/lib/h3/bindings.rb +0 -12
@@ -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++) {
@@ -145,6 +157,22 @@ H3Index H3_EXPORT(h3ToParent)(H3Index h, int parentRes) {
145
157
  return parentH;
146
158
  }
147
159
 
160
+ /**
161
+ * Determines whether one resolution is a valid child resolution of another.
162
+ * Each resolution is considered a valid child resolution of itself.
163
+ *
164
+ * @param parentRes int resolution of the parent
165
+ * @param childRes int resolution of the child
166
+ *
167
+ * @return The validity of the child resolution
168
+ */
169
+ static bool _isValidChildRes(int parentRes, int childRes) {
170
+ if (childRes < parentRes || childRes > MAX_H3_RES) {
171
+ return false;
172
+ }
173
+ return true;
174
+ }
175
+
148
176
  /**
149
177
  * maxH3ToChildrenSize returns the maximum number of children possible for a
150
178
  * given child level.
@@ -157,7 +185,7 @@ H3Index H3_EXPORT(h3ToParent)(H3Index h, int parentRes) {
157
185
  */
158
186
  int H3_EXPORT(maxH3ToChildrenSize)(H3Index h, int childRes) {
159
187
  int parentRes = H3_GET_RESOLUTION(h);
160
- if (parentRes > childRes) {
188
+ if (!_isValidChildRes(parentRes, childRes)) {
161
189
  return 0;
162
190
  }
163
191
  return _ipow(7, (childRes - parentRes));
@@ -191,7 +219,7 @@ H3Index makeDirectChild(H3Index h, int cellNumber) {
191
219
  */
192
220
  void H3_EXPORT(h3ToChildren)(H3Index h, int childRes, H3Index* children) {
193
221
  int parentRes = H3_GET_RESOLUTION(h);
194
- if (parentRes > childRes) {
222
+ if (!_isValidChildRes(parentRes, childRes)) {
195
223
  return;
196
224
  } else if (parentRes == childRes) {
197
225
  *children = h;
@@ -204,7 +232,7 @@ void H3_EXPORT(h3ToChildren)(H3Index h, int childRes, H3Index* children) {
204
232
  if (isAPentagon && i == K_AXES_DIGIT) {
205
233
  H3Index* nextChild = children + bufferChildStep;
206
234
  while (children < nextChild) {
207
- *children = H3_INVALID_INDEX;
235
+ *children = H3_NULL;
208
236
  children++;
209
237
  }
210
238
  } else {
@@ -214,6 +242,30 @@ void H3_EXPORT(h3ToChildren)(H3Index h, int childRes, H3Index* children) {
214
242
  }
215
243
  }
216
244
 
245
+ /**
246
+ * h3ToCenterChild produces the center child index for a given H3 index at
247
+ * the specified resolution
248
+ *
249
+ * @param h H3Index to find center child of
250
+ * @param childRes The resolution to switch to
251
+ *
252
+ * @return H3Index of the center child, or H3_NULL if you actually asked for a
253
+ * parent
254
+ */
255
+ H3Index H3_EXPORT(h3ToCenterChild)(H3Index h, int childRes) {
256
+ int parentRes = H3_GET_RESOLUTION(h);
257
+ if (!_isValidChildRes(parentRes, childRes)) {
258
+ return H3_NULL;
259
+ } else if (childRes == parentRes) {
260
+ return h;
261
+ }
262
+ H3Index child = H3_SET_RESOLUTION(h, childRes);
263
+ for (int i = parentRes + 1; i <= childRes; i++) {
264
+ H3_SET_INDEX_DIGIT(child, i, 0);
265
+ }
266
+ return child;
267
+ }
268
+
217
269
  /**
218
270
  * compact takes a set of hexagons all at the same resolution and compresses
219
271
  * them by pruning full child branches to the parent level. This is also done
@@ -227,17 +279,27 @@ void H3_EXPORT(h3ToChildren)(H3Index h, int childRes, H3Index* children) {
227
279
  */
228
280
  int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
229
281
  const int numHexes) {
282
+ if (numHexes == 0) {
283
+ return COMPACT_SUCCESS;
284
+ }
230
285
  int res = H3_GET_RESOLUTION(h3Set[0]);
231
286
  if (res == 0) {
232
287
  // No compaction possible, just copy the set to output
233
288
  for (int i = 0; i < numHexes; i++) {
234
289
  compactedSet[i] = h3Set[i];
235
290
  }
236
- return 0;
291
+ return COMPACT_SUCCESS;
292
+ }
293
+ H3Index* remainingHexes = H3_MEMORY(malloc)(numHexes * sizeof(H3Index));
294
+ if (!remainingHexes) {
295
+ return COMPACT_ALLOC_FAILED;
237
296
  }
238
- H3Index* remainingHexes = malloc(numHexes * sizeof(H3Index));
239
297
  memcpy(remainingHexes, h3Set, numHexes * sizeof(H3Index));
240
- 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
+ }
241
303
  H3Index* compactedSetOffset = compactedSet;
242
304
  int numRemainingHexes = numHexes;
243
305
  while (numRemainingHexes) {
@@ -254,28 +316,36 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
254
316
  int loc = (int)(parent % numRemainingHexes);
255
317
  int loopCount = 0;
256
318
  while (hashSetArray[loc] != 0) {
257
- if (loopCount > numRemainingHexes) {
319
+ if (loopCount > numRemainingHexes) { // LCOV_EXCL_BR_LINE
258
320
  // LCOV_EXCL_START
259
321
  // This case should not be possible because at most one
260
322
  // index is placed into hashSetArray per
261
323
  // numRemainingHexes.
262
- free(remainingHexes);
263
- free(hashSetArray);
264
- return -1;
324
+ H3_MEMORY(free)(remainingHexes);
325
+ H3_MEMORY(free)(hashSetArray);
326
+ return COMPACT_LOOP_EXCEEDED;
265
327
  // LCOV_EXCL_STOP
266
328
  }
267
329
  H3Index tempIndex =
268
330
  hashSetArray[loc] & H3_RESERVED_MASK_NEGATIVE;
269
331
  if (tempIndex == parent) {
270
332
  int count = H3_GET_RESERVED_BITS(hashSetArray[loc]) + 1;
271
- 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) {
272
342
  // Only possible on duplicate input
273
- free(remainingHexes);
274
- free(hashSetArray);
275
- return -2;
343
+ H3_MEMORY(free)(remainingHexes);
344
+ H3_MEMORY(free)(hashSetArray);
345
+ return COMPACT_DUPLICATE;
276
346
  }
277
347
  H3_SET_RESERVED_BITS(parent, count);
278
- hashSetArray[loc] = H3_INVALID_INDEX;
348
+ hashSetArray[loc] = H3_NULL;
279
349
  } else {
280
350
  loc = (loc + 1) % numRemainingHexes;
281
351
  }
@@ -295,7 +365,12 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
295
365
  break;
296
366
  }
297
367
  H3Index* compactableHexes =
298
- 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
+ }
299
374
  for (int i = 0; i < numRemainingHexes; i++) {
300
375
  if (hashSetArray[i] == 0) continue;
301
376
  int count = H3_GET_RESERVED_BITS(hashSetArray[i]) + 1;
@@ -321,7 +396,7 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
321
396
  int uncompactableCount = 0;
322
397
  for (int i = 0; i < numRemainingHexes; i++) {
323
398
  H3Index currIndex = remainingHexes[i];
324
- if (currIndex != H3_INVALID_INDEX) {
399
+ if (currIndex != H3_NULL) {
325
400
  H3Index parent = H3_EXPORT(h3ToParent)(currIndex, parentRes);
326
401
  // Modulus hash the parent into the temp array
327
402
  // to determine if this index was included in
@@ -330,14 +405,14 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
330
405
  int loopCount = 0;
331
406
  bool isUncompactable = true;
332
407
  do {
333
- if (loopCount > numRemainingHexes) {
408
+ if (loopCount > numRemainingHexes) { // LCOV_EXCL_BR_LINE
334
409
  // LCOV_EXCL_START
335
410
  // This case should not be possible because at most one
336
411
  // index is placed into hashSetArray per input hexagon.
337
- free(compactableHexes);
338
- free(remainingHexes);
339
- free(hashSetArray);
340
- 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;
341
416
  // LCOV_EXCL_STOP
342
417
  }
343
418
  H3Index tempIndex =
@@ -365,11 +440,11 @@ int H3_EXPORT(compact)(const H3Index* h3Set, H3Index* compactedSet,
365
440
  memcpy(remainingHexes, compactableHexes,
366
441
  compactableCount * sizeof(H3Index));
367
442
  numRemainingHexes = compactableCount;
368
- free(compactableHexes);
443
+ H3_MEMORY(free)(compactableHexes);
369
444
  }
370
- free(remainingHexes);
371
- free(hashSetArray);
372
- return 0;
445
+ H3_MEMORY(free)(remainingHexes);
446
+ H3_MEMORY(free)(hashSetArray);
447
+ return COMPACT_SUCCESS;
373
448
  }
374
449
 
375
450
  /**
@@ -393,7 +468,7 @@ int H3_EXPORT(uncompact)(const H3Index* compactedSet, const int numHexes,
393
468
  return -1;
394
469
  }
395
470
  int currentRes = H3_GET_RESOLUTION(compactedSet[i]);
396
- if (currentRes > res) {
471
+ if (!_isValidChildRes(currentRes, res)) {
397
472
  // Nonsensical. Abort.
398
473
  return -2;
399
474
  }
@@ -431,7 +506,7 @@ int H3_EXPORT(maxUncompactSize)(const H3Index* compactedSet, const int numHexes,
431
506
  for (int i = 0; i < numHexes; i++) {
432
507
  if (compactedSet[i] == 0) continue;
433
508
  int currentRes = H3_GET_RESOLUTION(compactedSet[i]);
434
- if (currentRes > res) {
509
+ if (!_isValidChildRes(currentRes, res)) {
435
510
  // Nonsensical. Abort.
436
511
  return -1;
437
512
  }
@@ -561,7 +636,7 @@ H3Index _h3Rotate60cw(H3Index h) {
561
636
  * Convert an FaceIJK address to the corresponding H3Index.
562
637
  * @param fijk The FaceIJK address.
563
638
  * @param res The cell resolution.
564
- * @return The encoded H3Index (or 0 on failure).
639
+ * @return The encoded H3Index (or H3_NULL on failure).
565
640
  */
566
641
  H3Index _faceIjkToH3(const FaceIJK* fijk, int res) {
567
642
  // initialize the index
@@ -574,7 +649,7 @@ H3Index _faceIjkToH3(const FaceIJK* fijk, int res) {
574
649
  if (fijk->coord.i > MAX_FACE_COORD || fijk->coord.j > MAX_FACE_COORD ||
575
650
  fijk->coord.k > MAX_FACE_COORD) {
576
651
  // out of range input
577
- return H3_INVALID_INDEX;
652
+ return H3_NULL;
578
653
  }
579
654
 
580
655
  H3_SET_BASE_CELL(h, _faceIjkToBaseCell(fijk));
@@ -618,7 +693,7 @@ H3Index _faceIjkToH3(const FaceIJK* fijk, int res) {
618
693
  if (fijkBC.coord.i > MAX_FACE_COORD || fijkBC.coord.j > MAX_FACE_COORD ||
619
694
  fijkBC.coord.k > MAX_FACE_COORD) {
620
695
  // out of range input
621
- return H3_INVALID_INDEX;
696
+ return H3_NULL;
622
697
  }
623
698
 
624
699
  // lookup the correct base cell
@@ -657,14 +732,14 @@ H3Index _faceIjkToH3(const FaceIJK* fijk, int res) {
657
732
  *
658
733
  * @param g The spherical coordinates to encode.
659
734
  * @param res The desired H3 resolution for the encoding.
660
- * @return The encoded H3Index (or 0 on failure).
735
+ * @return The encoded H3Index (or H3_NULL on failure).
661
736
  */
662
737
  H3Index H3_EXPORT(geoToH3)(const GeoCoord* g, int res) {
663
738
  if (res < 0 || res > MAX_H3_RES) {
664
- return H3_INVALID_INDEX;
739
+ return H3_NULL;
665
740
  }
666
741
  if (!isfinite(g->lat) || !isfinite(g->lon)) {
667
- return H3_INVALID_INDEX;
742
+ return H3_NULL;
668
743
  }
669
744
 
670
745
  FaceIJK fijk;
@@ -774,8 +849,13 @@ void H3_EXPORT(h3ToGeo)(H3Index h3, GeoCoord* g) {
774
849
  void H3_EXPORT(h3ToGeoBoundary)(H3Index h3, GeoBoundary* gb) {
775
850
  FaceIJK fijk;
776
851
  _h3ToFaceIjk(h3, &fijk);
777
- _faceIjkToGeoBoundary(&fijk, H3_GET_RESOLUTION(h3),
778
- 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
+ }
779
859
  }
780
860
 
781
861
  /**
@@ -860,6 +940,30 @@ void H3_EXPORT(h3GetFaces)(H3Index h3, int* out) {
860
940
  }
861
941
  }
862
942
 
943
+ /**
944
+ * pentagonIndexCount returns the number of pentagons (same at any resolution)
945
+ *
946
+ * @return int count of pentagon indexes
947
+ */
948
+ int H3_EXPORT(pentagonIndexCount)() { return NUM_PENTAGONS; }
949
+
950
+ /**
951
+ * Generates all pentagons at the specified resolution
952
+ *
953
+ * @param res The resolution to produce pentagons at.
954
+ * @param out Output array. Must be of size pentagonIndexCount().
955
+ */
956
+ void H3_EXPORT(getPentagonIndexes)(int res, H3Index* out) {
957
+ int i = 0;
958
+ for (int bc = 0; bc < NUM_BASE_CELLS; bc++) {
959
+ if (_isBaseCellPentagon(bc)) {
960
+ H3Index pentagon;
961
+ setH3Index(&pentagon, res, bc, 0);
962
+ out[i++] = pentagon;
963
+ }
964
+ }
965
+ }
966
+
863
967
  /**
864
968
  * Returns whether or not a resolution is a Class III grid. Note that odd
865
969
  * resolutions are Class III and even resolutions are Class II.
@@ -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
  }