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
@@ -21,6 +21,7 @@
21
21
  #define LINKED_GEO_H
22
22
 
23
23
  #include <stdlib.h>
24
+
24
25
  #include "bbox.h"
25
26
  #include "geoCoord.h"
26
27
  #include "h3api.h"
@@ -21,6 +21,7 @@
21
21
  #define POLYGON_H
22
22
 
23
23
  #include <stdbool.h>
24
+
24
25
  #include "bbox.h"
25
26
  #include "geoCoord.h"
26
27
  #include "h3api.h"
@@ -25,6 +25,7 @@
25
25
  #include <float.h>
26
26
  #include <math.h>
27
27
  #include <stdbool.h>
28
+
28
29
  #include "bbox.h"
29
30
  #include "constants.h"
30
31
  #include "geoCoord.h"
@@ -0,0 +1,44 @@
1
+ /*
2
+ * Copyright 2020 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 vertex.h
17
+ * @brief Functions for working with cell vertexes.
18
+ */
19
+
20
+ #ifndef H3VERTEX_H
21
+ #define H3VERTEX_H
22
+
23
+ #include "h3Index.h"
24
+ #include "faceijk.h"
25
+
26
+ /** @struct PentagonDirectionFaces
27
+ * @brief The faces in each axial direction of a given pentagon base cell
28
+ */
29
+ typedef struct {
30
+ int baseCell; ///< base cell number
31
+ int faces[NUM_PENT_VERTS]; ///< face numbers for each axial direction,
32
+ /// in order, starting with J
33
+ } PentagonDirectionFaces;
34
+
35
+ /** Invalid vertex number */
36
+ #define INVALID_VERTEX_NUM -1
37
+
38
+ /** Max number of faces a base cell's descendants may appear on */
39
+ #define MAX_BASE_CELL_FACES 5
40
+
41
+ int vertexRotations(H3Index cell);
42
+ int vertexNumForDirection(const H3Index origin, const Direction direction);
43
+
44
+ #endif
@@ -22,6 +22,7 @@
22
22
 
23
23
  #include <stdint.h>
24
24
  #include <stdlib.h>
25
+
25
26
  #include "geoCoord.h"
26
27
 
27
28
  /** @struct VertexNode
@@ -18,12 +18,15 @@
18
18
  */
19
19
 
20
20
  #include "algos.h"
21
+
21
22
  #include <assert.h>
22
23
  #include <float.h>
23
24
  #include <math.h>
24
25
  #include <stdbool.h>
25
26
  #include <stdlib.h>
26
27
  #include <string.h>
28
+
29
+ #include "alloc.h"
27
30
  #include "baseCells.h"
28
31
  #include "bbox.h"
29
32
  #include "faceijk.h"
@@ -41,6 +44,9 @@
41
44
  #define HEX_RANGE_SUCCESS 0
42
45
  #define HEX_RANGE_PENTAGON 1
43
46
  #define HEX_RANGE_K_SUBSEQUENCE 2
47
+ #define MAX_ONE_RING_SIZE 7
48
+ #define HEX_HASH_OVERFLOW -1
49
+ #define POLYFILL_BUFFER 12
44
50
 
45
51
  /**
46
52
  * Directions used for traversing a hexagonal ring counterclockwise around
@@ -150,74 +156,86 @@ static const Direction NEW_ADJUSTMENT_III[7][7] = {
150
156
  CENTER_DIGIT, IJ_AXES_DIGIT}};
151
157
 
152
158
  /**
153
- * Maximum number of indices that result from the kRing algorithm with the given
159
+ * Maximum number of cells that result from the kRing algorithm with the given
154
160
  * k. Formula source and proof: https://oeis.org/A003215
155
161
  *
156
- * @param k k value, k >= 0.
162
+ * @param k k value, k >= 0.
157
163
  */
158
164
  int H3_EXPORT(maxKringSize)(int k) { return 3 * k * (k + 1) + 1; }
159
165
 
160
166
  /**
161
- * k-rings produces indices within k distance of the origin index.
167
+ * Produce cells within grid distance k of the origin cell.
162
168
  *
163
- * k-ring 0 is defined as the origin index, k-ring 1 is defined as k-ring 0 and
164
- * all neighboring indices, and so on.
169
+ * k-ring 0 is defined as the origin cell, k-ring 1 is defined as k-ring 0 and
170
+ * all neighboring cells, and so on.
165
171
  *
166
172
  * Output is placed in the provided array in no particular order. Elements of
167
173
  * the output array may be left zero, as can happen when crossing a pentagon.
168
174
  *
169
- * @param origin Origin location.
170
- * @param k k >= 0
171
- * @param out Zero-filled array which must be of size maxKringSize(k).
175
+ * @param origin origin cell
176
+ * @param k k >= 0
177
+ * @param out zero-filled array which must be of size maxKringSize(k)
172
178
  */
173
179
  void H3_EXPORT(kRing)(H3Index origin, int k, H3Index* out) {
174
- int maxIdx = H3_EXPORT(maxKringSize)(k);
175
- int* distances = malloc(maxIdx * sizeof(int));
176
- H3_EXPORT(kRingDistances)(origin, k, out, distances);
177
- free(distances);
180
+ H3_EXPORT(kRingDistances)(origin, k, out, NULL);
178
181
  }
179
182
 
180
183
  /**
181
- * k-rings produces indices within k distance of the origin index.
184
+ * Produce cells and their distances from the given origin cell, up to
185
+ * distance k.
182
186
  *
183
- * k-ring 0 is defined as the origin index, k-ring 1 is defined as k-ring 0 and
184
- * all neighboring indices, and so on.
187
+ * k-ring 0 is defined as the origin cell, k-ring 1 is defined as k-ring 0 and
188
+ * all neighboring cells, and so on.
185
189
  *
186
190
  * Output is placed in the provided array in no particular order. Elements of
187
191
  * the output array may be left zero, as can happen when crossing a pentagon.
188
192
  *
189
- * @param origin Origin location.
190
- * @param k k >= 0
191
- * @param out Zero-filled array which must be of size maxKringSize(k).
192
- * @param distances Zero-filled array which must be of size maxKringSize(k).
193
+ * @param origin origin cell
194
+ * @param k k >= 0
195
+ * @param out zero-filled array which must be of size maxKringSize(k)
196
+ * @param distances NULL or a zero-filled array which must be of size
197
+ * maxKringSize(k)
193
198
  */
194
199
  void H3_EXPORT(kRingDistances)(H3Index origin, int k, H3Index* out,
195
200
  int* distances) {
196
- const int maxIdx = H3_EXPORT(maxKringSize)(k);
197
201
  // Optimistically try the faster hexRange algorithm first
198
202
  const bool failed = H3_EXPORT(hexRangeDistances)(origin, k, out, distances);
199
203
  if (failed) {
204
+ const int maxIdx = H3_EXPORT(maxKringSize)(k);
200
205
  // Fast algo failed, fall back to slower, correct algo
201
206
  // and also wipe out array because contents untrustworthy
202
- memset(out, 0, maxIdx * sizeof(out[0]));
203
- memset(distances, 0, maxIdx * sizeof(distances[0]));
204
- _kRingInternal(origin, k, out, distances, maxIdx, 0);
207
+ memset(out, 0, maxIdx * sizeof(H3Index));
208
+
209
+ if (distances == NULL) {
210
+ distances = H3_MEMORY(calloc)(maxIdx, sizeof(int));
211
+ if (!distances) {
212
+ // TODO: Return an error code when this is not void
213
+ return;
214
+ }
215
+ _kRingInternal(origin, k, out, distances, maxIdx, 0);
216
+ H3_MEMORY(free)(distances);
217
+ } else {
218
+ memset(distances, 0, maxIdx * sizeof(int));
219
+ _kRingInternal(origin, k, out, distances, maxIdx, 0);
220
+ }
205
221
  }
206
222
  }
207
223
 
208
224
  /**
209
225
  * Internal helper function called recursively for kRingDistances.
210
226
  *
211
- * Adds the origin index to the output set (treating it as a hash set)
227
+ * Adds the origin cell to the output set (treating it as a hash set)
212
228
  * and recurses to its neighbors, if needed.
213
229
  *
214
- * @param origin
215
- * @param k Maximum distance to move from the origin.
216
- * @param out Array treated as a hash set, elements being either H3Index or 0.
217
- * @param distances Scratch area, with elements paralleling the out array.
218
- * Elements indicate ijk distance from the origin index to the output index.
219
- * @param maxIdx Size of out and scratch arrays (must be maxKringSize(k))
220
- * @param curK Current distance from the origin.
230
+ * @param origin Origin cell
231
+ * @param k Maximum distance to move from the origin
232
+ * @param out Array treated as a hash set, elements being either
233
+ * H3Index or 0.
234
+ * @param distances Scratch area, with elements paralleling the out array.
235
+ * Elements indicate ijk distance from the origin cell to
236
+ * the output cell
237
+ * @param maxIdx Size of out and scratch arrays (must be maxKringSize(k))
238
+ * @param curK Current distance from the origin
221
239
  */
222
240
  void _kRingInternal(H3Index origin, int k, H3Index* out, int* distances,
223
241
  int maxIdx, int curK) {
@@ -260,7 +278,7 @@ void _kRingInternal(H3Index origin, int k, H3Index* out, int* distances,
260
278
  * @param rotations Number of ccw rotations to perform to reorient the
261
279
  * translation vector. Will be modified to the new number of
262
280
  * rotations to perform (such as when crossing a face edge.)
263
- * @return H3Index of the specified neighbor or 0 if deleted k-subsequence
281
+ * @return H3Index of the specified neighbor or H3_NULL if deleted k-subsequence
264
282
  * distortion is encountered.
265
283
  */
266
284
  H3Index h3NeighborRotations(H3Index origin, Direction dir, int* rotations) {
@@ -299,7 +317,10 @@ H3Index h3NeighborRotations(H3Index origin, Direction dir, int* rotations) {
299
317
  } else {
300
318
  Direction oldDigit = H3_GET_INDEX_DIGIT(out, r + 1);
301
319
  Direction nextDir;
302
- if (isResClassIII(r + 1)) {
320
+ if (oldDigit == INVALID_DIGIT) {
321
+ // Only possible on invalid input
322
+ return H3_NULL;
323
+ } else if (isResClassIII(r + 1)) {
303
324
  H3_SET_INDEX_DIGIT(out, r + 1, NEW_DIGIT_II[oldDigit][dir]);
304
325
  nextDir = NEW_ADJUSTMENT_II[oldDigit][dir];
305
326
  } else {
@@ -345,7 +366,7 @@ H3Index h3NeighborRotations(H3Index origin, Direction dir, int* rotations) {
345
366
  // base cell.
346
367
  if (oldLeadingDigit == CENTER_DIGIT) {
347
368
  // Undefined: the k direction is deleted from here
348
- return H3_INVALID_INDEX;
369
+ return H3_NULL;
349
370
  } else if (oldLeadingDigit == JK_AXES_DIGIT) {
350
371
  // Rotate out of the deleted k subsequence
351
372
  // We also need an additional change to the direction we're
@@ -360,7 +381,7 @@ H3Index h3NeighborRotations(H3Index origin, Direction dir, int* rotations) {
360
381
  *rotations = *rotations + 5;
361
382
  } else {
362
383
  // Should never occur
363
- return H3_INVALID_INDEX; // LCOV_EXCL_LINE
384
+ return H3_NULL; // LCOV_EXCL_LINE
364
385
  }
365
386
  }
366
387
  }
@@ -410,7 +431,7 @@ H3Index h3NeighborRotations(H3Index origin, Direction dir, int* rotations) {
410
431
  * @return 0 if no pentagon or pentagonal distortion area was encountered.
411
432
  */
412
433
  int H3_EXPORT(hexRange)(H3Index origin, int k, H3Index* out) {
413
- return H3_EXPORT(hexRangeDistances)(origin, k, out, 0);
434
+ return H3_EXPORT(hexRangeDistances)(origin, k, out, NULL);
414
435
  }
415
436
 
416
437
  /**
@@ -623,9 +644,8 @@ int H3_EXPORT(hexRing)(H3Index origin, int k, H3Index* out) {
623
644
  * maxPolyfillSize returns the number of hexagons to allocate space for when
624
645
  * performing a polyfill on the given GeoJSON-like data structure.
625
646
  *
626
- * Currently a laughably padded response, being a k-ring that wholly contains
627
- * a bounding box of the GeoJSON, but still less wasted memory than initializing
628
- * a Python application? ;)
647
+ * The size is the maximum of either the number of points in the geofence or the
648
+ * number of hexagons in the bounding box of the geofence.
629
649
  *
630
650
  * @param geoPolygon A GeoJSON-like data structure indicating the poly to fill
631
651
  * @param res Hexagon resolution (0-15)
@@ -634,12 +654,23 @@ int H3_EXPORT(hexRing)(H3Index origin, int k, H3Index* out) {
634
654
  int H3_EXPORT(maxPolyfillSize)(const GeoPolygon* geoPolygon, int res) {
635
655
  // Get the bounding box for the GeoJSON-like struct
636
656
  BBox bbox;
637
- bboxFromGeofence(&geoPolygon->geofence, &bbox);
638
- int minK = bboxHexRadius(&bbox, res);
639
-
640
- // The total number of hexagons to allocate can now be determined by
641
- // the k-ring hex allocation helper function.
642
- return H3_EXPORT(maxKringSize)(minK);
657
+ const Geofence geofence = geoPolygon->geofence;
658
+ bboxFromGeofence(&geofence, &bbox);
659
+ int numHexagons = bboxHexEstimate(&bbox, res);
660
+ // This algorithm assumes that the number of vertices is usually less than
661
+ // the number of hexagons, but when it's wrong, this will keep it from
662
+ // failing
663
+ int totalVerts = geofence.numVerts;
664
+ for (int i = 0; i < geoPolygon->numHoles; i++) {
665
+ totalVerts += geoPolygon->holes[i].numVerts;
666
+ }
667
+ if (numHexagons < totalVerts) numHexagons = totalVerts;
668
+ // When the polygon is very small, near an icosahedron edge and is an odd
669
+ // resolution, the line tracing needs an extra buffer than the estimator
670
+ // function provides (but beefing that up to cover causes most situations to
671
+ // overallocate memory)
672
+ numHexagons += POLYFILL_BUFFER;
673
+ return numHexagons;
643
674
  }
644
675
 
645
676
  /**
@@ -647,15 +678,109 @@ int H3_EXPORT(maxPolyfillSize)(const GeoPolygon* geoPolygon, int res) {
647
678
  * zeroed memory, and fills it with the hexagons that are contained by
648
679
  * the GeoJSON-like data structure.
649
680
  *
650
- * The current implementation is very primitive and slow, but correct,
651
- * performing a point-in-poly operation on every hexagon in a k-ring defined
652
- * around the given geofence.
681
+ * This implementation traces the GeoJSON geofence(s) in cartesian space with
682
+ * hexagons, tests them and their neighbors to be contained by the geofence(s),
683
+ * and then any newly found hexagons are used to test again until no new
684
+ * hexagons are found.
653
685
  *
654
686
  * @param geoPolygon The geofence and holes defining the relevant area
655
687
  * @param res The Hexagon resolution (0-15)
656
688
  * @param out The slab of zeroed memory to write to. Assumed to be big enough.
657
689
  */
658
690
  void H3_EXPORT(polyfill)(const GeoPolygon* geoPolygon, int res, H3Index* out) {
691
+ // TODO: Eliminate this wrapper with the H3 4.0.0 release
692
+ int failure = _polyfillInternal(geoPolygon, res, out);
693
+ // The polyfill algorithm can theoretically fail if the allocated memory is
694
+ // not large enough for the polygon, but this should be impossible given the
695
+ // conservative overestimation of the number of hexagons possible.
696
+ // LCOV_EXCL_START
697
+ if (failure) {
698
+ int numHexagons = H3_EXPORT(maxPolyfillSize)(geoPolygon, res);
699
+ for (int i = 0; i < numHexagons; i++) out[i] = H3_NULL;
700
+ }
701
+ // LCOV_EXCL_STOP
702
+ }
703
+
704
+ /**
705
+ * _getEdgeHexagons takes a given geofence ring (either the main geofence or
706
+ * one of the holes) and traces it with hexagons and updates the search and
707
+ * found memory blocks. This is used for determining the initial hexagon set
708
+ * for the polyfill algorithm to execute on.
709
+ *
710
+ * @param geofence The geofence (or hole) to be traced
711
+ * @param numHexagons The maximum number of hexagons possible for the geofence
712
+ * (also the bounds of the search and found arrays)
713
+ * @param res The hexagon resolution (0-15)
714
+ * @param numSearchHexes The number of hexagons found so far to be searched
715
+ * @param search The block of memory containing the hexagons to search from
716
+ * @param found The block of memory containing the hexagons found from the
717
+ * search
718
+ *
719
+ * @return An error code if the hash function cannot insert a found hexagon
720
+ * into the found array.
721
+ */
722
+ int _getEdgeHexagons(const Geofence* geofence, int numHexagons, int res,
723
+ int* numSearchHexes, H3Index* search, H3Index* found) {
724
+ for (int i = 0; i < geofence->numVerts; i++) {
725
+ GeoCoord origin = geofence->verts[i];
726
+ GeoCoord destination = i == geofence->numVerts - 1
727
+ ? geofence->verts[0]
728
+ : geofence->verts[i + 1];
729
+ const int numHexesEstimate =
730
+ lineHexEstimate(&origin, &destination, res);
731
+ for (int j = 0; j < numHexesEstimate; j++) {
732
+ GeoCoord interpolate;
733
+ interpolate.lat =
734
+ (origin.lat * (numHexesEstimate - j) / numHexesEstimate) +
735
+ (destination.lat * j / numHexesEstimate);
736
+ interpolate.lon =
737
+ (origin.lon * (numHexesEstimate - j) / numHexesEstimate) +
738
+ (destination.lon * j / numHexesEstimate);
739
+ H3Index pointHex = H3_EXPORT(geoToH3)(&interpolate, res);
740
+ // A simple hash to store the hexagon, or move to another place if
741
+ // needed
742
+ int loc = (int)(pointHex % numHexagons);
743
+ int loopCount = 0;
744
+ while (found[loc] != 0) {
745
+ // If this conditional is reached, the `found` memory block is
746
+ // too small for the given polygon. This should not happen.
747
+ if (loopCount > numHexagons)
748
+ return HEX_HASH_OVERFLOW; // LCOV_EXCL_LINE
749
+ if (found[loc] == pointHex)
750
+ break; // At least two points of the geofence index to the
751
+ // same cell
752
+ loc = (loc + 1) % numHexagons;
753
+ loopCount++;
754
+ }
755
+ if (found[loc] == pointHex)
756
+ continue; // Skip this hex, already exists in the found hash
757
+ // Otherwise, set it in the found hash for now
758
+ found[loc] = pointHex;
759
+
760
+ search[*numSearchHexes] = pointHex;
761
+ (*numSearchHexes)++;
762
+ }
763
+ }
764
+ return 0;
765
+ }
766
+
767
+ /**
768
+ * _polyfillInternal traces the provided geoPolygon data structure with hexagons
769
+ * and then iteratively searches through these hexagons and their immediate
770
+ * neighbors to see if they are contained within the polygon or not. Those that
771
+ * are found are added to the out array as well as the found array. Once all
772
+ * hexagons to search are checked, the found hexagons become the new search
773
+ * array and the found array is wiped and the process repeats until no new
774
+ * hexagons can be found.
775
+ *
776
+ * @param geoPolygon The geofence and holes defining the relevant area
777
+ * @param res The Hexagon resolution (0-15)
778
+ * @param out The slab of zeroed memory to write to. Assumed to be big enough.
779
+ *
780
+ * @return An error code if any of the hash operations fails to insert a hexagon
781
+ * into an array of memory.
782
+ */
783
+ int _polyfillInternal(const GeoPolygon* geoPolygon, int res, H3Index* out) {
659
784
  // One of the goals of the polyfill algorithm is that two adjacent polygons
660
785
  // with zero overlap have zero overlapping hexagons. That the hexagons are
661
786
  // uniquely assigned. There are a few approaches to take here, such as
@@ -674,39 +799,142 @@ void H3_EXPORT(polyfill)(const GeoPolygon* geoPolygon, int res, H3Index* out) {
674
799
  // This first part is identical to the maxPolyfillSize above.
675
800
 
676
801
  // Get the bounding boxes for the polygon and any holes
677
- BBox* bboxes = malloc((geoPolygon->numHoles + 1) * sizeof(BBox));
802
+ BBox* bboxes = H3_MEMORY(malloc)((geoPolygon->numHoles + 1) * sizeof(BBox));
678
803
  assert(bboxes != NULL);
679
804
  bboxesFromGeoPolygon(geoPolygon, bboxes);
680
- int minK = bboxHexRadius(&bboxes[0], res);
681
- int numHexagons = H3_EXPORT(maxKringSize)(minK);
682
-
683
- // Get the center hex
684
- GeoCoord center;
685
- bboxCenter(&bboxes[0], &center);
686
- H3Index centerH3 = H3_EXPORT(geoToH3)(&center, res);
687
-
688
- // From here on it works differently, first we get all potential
689
- // hexagons inserted into the available memory
690
- H3_EXPORT(kRing)(centerH3, minK, out);
691
-
692
- // Next we iterate through each hexagon, and test its center point to see if
693
- // it's contained in the GeoJSON-like struct
694
- for (int i = 0; i < numHexagons; i++) {
695
- // Skip records that are already zeroed
696
- if (out[i] == 0) {
697
- continue;
805
+
806
+ // Get the estimated number of hexagons and allocate some temporary memory
807
+ // for the hexagons
808
+ int numHexagons = H3_EXPORT(maxPolyfillSize)(geoPolygon, res);
809
+ H3Index* search = H3_MEMORY(calloc)(numHexagons, sizeof(H3Index));
810
+ H3Index* found = H3_MEMORY(calloc)(numHexagons, sizeof(H3Index));
811
+
812
+ // Some metadata for tracking the state of the search and found memory
813
+ // blocks
814
+ int numSearchHexes = 0;
815
+ int numFoundHexes = 0;
816
+
817
+ // 1. Trace the hexagons along the polygon defining the outer geofence and
818
+ // add them to the search hash. The hexagon containing the geofence point
819
+ // may or may not be contained by the geofence (as the hexagon's center
820
+ // point may be outside of the boundary.)
821
+ const Geofence geofence = geoPolygon->geofence;
822
+ int failure = _getEdgeHexagons(&geofence, numHexagons, res, &numSearchHexes,
823
+ search, found);
824
+ // If this branch is reached, we have exceeded the maximum number of
825
+ // hexagons possible and need to clean up the allocated memory.
826
+ // LCOV_EXCL_START
827
+ if (failure) {
828
+ H3_MEMORY(free)(search);
829
+ H3_MEMORY(free)(found);
830
+ H3_MEMORY(free)(bboxes);
831
+ return failure;
832
+ }
833
+ // LCOV_EXCL_STOP
834
+
835
+ // 2. Iterate over all holes, trace the polygons defining the holes with
836
+ // hexagons and add to only the search hash. We're going to temporarily use
837
+ // the `found` hash to use for dedupe purposes and then re-zero it once
838
+ // we're done here, otherwise we'd have to scan the whole set on each insert
839
+ // to make sure there's no duplicates, which is very inefficient.
840
+ for (int i = 0; i < geoPolygon->numHoles; i++) {
841
+ Geofence* hole = &(geoPolygon->holes[i]);
842
+ failure = _getEdgeHexagons(hole, numHexagons, res, &numSearchHexes,
843
+ search, found);
844
+ // If this branch is reached, we have exceeded the maximum number of
845
+ // hexagons possible and need to clean up the allocated memory.
846
+ // LCOV_EXCL_START
847
+ if (failure) {
848
+ H3_MEMORY(free)(search);
849
+ H3_MEMORY(free)(found);
850
+ H3_MEMORY(free)(bboxes);
851
+ return failure;
698
852
  }
699
- // Check if hexagon is inside of polygon
700
- GeoCoord hexCenter;
701
- H3_EXPORT(h3ToGeo)(out[i], &hexCenter);
702
- hexCenter.lat = constrainLat(hexCenter.lat);
703
- hexCenter.lon = constrainLng(hexCenter.lon);
704
- // And remove from list if not
705
- if (!pointInsidePolygon(geoPolygon, bboxes, &hexCenter)) {
706
- out[i] = H3_INVALID_INDEX;
853
+ // LCOV_EXCL_STOP
854
+ }
855
+
856
+ // 3. Re-zero the found hash so it can be used in the main loop below
857
+ for (int i = 0; i < numHexagons; i++) found[i] = 0;
858
+
859
+ // 4. Begin main loop. While the search hash is not empty do the following
860
+ while (numSearchHexes > 0) {
861
+ // Iterate through all hexagons in the current search hash, then loop
862
+ // through all neighbors and test Point-in-Poly, if point-in-poly
863
+ // succeeds, add to out and found hashes if not already there.
864
+ int currentSearchNum = 0;
865
+ int i = 0;
866
+ while (currentSearchNum < numSearchHexes) {
867
+ H3Index ring[MAX_ONE_RING_SIZE] = {0};
868
+ H3Index searchHex = search[i];
869
+ H3_EXPORT(kRing)(searchHex, 1, ring);
870
+ for (int j = 0; j < MAX_ONE_RING_SIZE; j++) {
871
+ if (ring[j] == H3_NULL) {
872
+ continue; // Skip if this was a pentagon and only had 5
873
+ // neighbors
874
+ }
875
+
876
+ H3Index hex = ring[j];
877
+
878
+ // A simple hash to store the hexagon, or move to another place
879
+ // if needed. This MUST be done before the point-in-poly check
880
+ // since that's far more expensive
881
+ int loc = (int)(hex % numHexagons);
882
+ int loopCount = 0;
883
+ while (out[loc] != 0) {
884
+ // If this branch is reached, we have exceeded the maximum
885
+ // number of hexagons possible and need to clean up the
886
+ // allocated memory.
887
+ // LCOV_EXCL_START
888
+ if (loopCount > numHexagons) {
889
+ H3_MEMORY(free)(search);
890
+ H3_MEMORY(free)(found);
891
+ H3_MEMORY(free)(bboxes);
892
+ return -1;
893
+ }
894
+ // LCOV_EXCL_STOP
895
+ if (out[loc] == hex) break; // Skip duplicates found
896
+ loc = (loc + 1) % numHexagons;
897
+ loopCount++;
898
+ }
899
+ if (out[loc] == hex) {
900
+ continue; // Skip this hex, already exists in the out hash
901
+ }
902
+
903
+ // Check if the hexagon is in the polygon or not
904
+ GeoCoord hexCenter;
905
+ H3_EXPORT(h3ToGeo)(hex, &hexCenter);
906
+
907
+ // If not, skip
908
+ if (!pointInsidePolygon(geoPolygon, bboxes, &hexCenter)) {
909
+ continue;
910
+ }
911
+
912
+ // Otherwise set it in the output array
913
+ out[loc] = hex;
914
+
915
+ // Set the hexagon in the found hash
916
+ found[numFoundHexes] = hex;
917
+ numFoundHexes++;
918
+ }
919
+ currentSearchNum++;
920
+ i++;
707
921
  }
922
+
923
+ // Swap the search and found pointers, copy the found hex count to the
924
+ // search hex count, and zero everything related to the found memory.
925
+ H3Index* temp = search;
926
+ search = found;
927
+ found = temp;
928
+ for (int j = 0; j < numSearchHexes; j++) found[j] = 0;
929
+ numSearchHexes = numFoundHexes;
930
+ numFoundHexes = 0;
931
+ // Repeat until no new hexagons are found
708
932
  }
709
- free(bboxes);
933
+ // The out memory structure should be complete, end it here
934
+ H3_MEMORY(free)(bboxes);
935
+ H3_MEMORY(free)(search);
936
+ H3_MEMORY(free)(found);
937
+ return 0;
710
938
  }
711
939
 
712
940
  /**