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
@@ -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
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2016-2018 Uber Technologies, Inc.
2
+ * Copyright 2016-2019 Uber Technologies, Inc.
3
3
  *
4
4
  * Licensed under the Apache License, Version 2.0 (the "License");
5
5
  * you may not use this file except in compliance with the License.
@@ -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) {
@@ -345,7 +363,7 @@ H3Index h3NeighborRotations(H3Index origin, Direction dir, int* rotations) {
345
363
  // base cell.
346
364
  if (oldLeadingDigit == CENTER_DIGIT) {
347
365
  // Undefined: the k direction is deleted from here
348
- return H3_INVALID_INDEX;
366
+ return H3_NULL;
349
367
  } else if (oldLeadingDigit == JK_AXES_DIGIT) {
350
368
  // Rotate out of the deleted k subsequence
351
369
  // We also need an additional change to the direction we're
@@ -360,7 +378,7 @@ H3Index h3NeighborRotations(H3Index origin, Direction dir, int* rotations) {
360
378
  *rotations = *rotations + 5;
361
379
  } else {
362
380
  // Should never occur
363
- return H3_INVALID_INDEX; // LCOV_EXCL_LINE
381
+ return H3_NULL; // LCOV_EXCL_LINE
364
382
  }
365
383
  }
366
384
  }
@@ -410,7 +428,7 @@ H3Index h3NeighborRotations(H3Index origin, Direction dir, int* rotations) {
410
428
  * @return 0 if no pentagon or pentagonal distortion area was encountered.
411
429
  */
412
430
  int H3_EXPORT(hexRange)(H3Index origin, int k, H3Index* out) {
413
- return H3_EXPORT(hexRangeDistances)(origin, k, out, 0);
431
+ return H3_EXPORT(hexRangeDistances)(origin, k, out, NULL);
414
432
  }
415
433
 
416
434
  /**
@@ -469,7 +487,7 @@ int H3_EXPORT(hexRangeDistances)(H3Index origin, int k, H3Index* out,
469
487
  // the end of this ring.
470
488
  origin =
471
489
  h3NeighborRotations(origin, NEXT_RING_DIRECTION, &rotations);
472
- if (origin == 0) {
490
+ if (origin == 0) { // LCOV_EXCL_BR_LINE
473
491
  // Should not be possible because `origin` would have to be a
474
492
  // pentagon
475
493
  return HEX_RANGE_K_SUBSEQUENCE; // LCOV_EXCL_LINE
@@ -482,7 +500,7 @@ int H3_EXPORT(hexRangeDistances)(H3Index origin, int k, H3Index* out,
482
500
  }
483
501
 
484
502
  origin = h3NeighborRotations(origin, DIRECTIONS[direction], &rotations);
485
- if (origin == 0) {
503
+ if (origin == 0) { // LCOV_EXCL_BR_LINE
486
504
  // Should not be possible because `origin` would have to be a
487
505
  // pentagon
488
506
  return HEX_RANGE_K_SUBSEQUENCE; // LCOV_EXCL_LINE
@@ -569,7 +587,7 @@ int H3_EXPORT(hexRing)(H3Index origin, int k, H3Index* out) {
569
587
 
570
588
  for (int ring = 0; ring < k; ring++) {
571
589
  origin = h3NeighborRotations(origin, NEXT_RING_DIRECTION, &rotations);
572
- if (origin == 0) {
590
+ if (origin == 0) { // LCOV_EXCL_BR_LINE
573
591
  // Should not be possible because `origin` would have to be a
574
592
  // pentagon
575
593
  return HEX_RANGE_K_SUBSEQUENCE; // LCOV_EXCL_LINE
@@ -589,7 +607,7 @@ int H3_EXPORT(hexRing)(H3Index origin, int k, H3Index* out) {
589
607
  for (int pos = 0; pos < k; pos++) {
590
608
  origin =
591
609
  h3NeighborRotations(origin, DIRECTIONS[direction], &rotations);
592
- if (origin == 0) {
610
+ if (origin == 0) { // LCOV_EXCL_BR_LINE
593
611
  // Should not be possible because `origin` would have to be a
594
612
  // pentagon
595
613
  return HEX_RANGE_K_SUBSEQUENCE; // LCOV_EXCL_LINE
@@ -623,9 +641,8 @@ int H3_EXPORT(hexRing)(H3Index origin, int k, H3Index* out) {
623
641
  * maxPolyfillSize returns the number of hexagons to allocate space for when
624
642
  * performing a polyfill on the given GeoJSON-like data structure.
625
643
  *
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? ;)
644
+ * The size is the maximum of either the number of points in the geofence or the
645
+ * number of hexagons in the bounding box of the geofence.
629
646
  *
630
647
  * @param geoPolygon A GeoJSON-like data structure indicating the poly to fill
631
648
  * @param res Hexagon resolution (0-15)
@@ -634,12 +651,23 @@ int H3_EXPORT(hexRing)(H3Index origin, int k, H3Index* out) {
634
651
  int H3_EXPORT(maxPolyfillSize)(const GeoPolygon* geoPolygon, int res) {
635
652
  // Get the bounding box for the GeoJSON-like struct
636
653
  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);
654
+ const Geofence geofence = geoPolygon->geofence;
655
+ bboxFromGeofence(&geofence, &bbox);
656
+ int numHexagons = bboxHexEstimate(&bbox, res);
657
+ // This algorithm assumes that the number of vertices is usually less than
658
+ // the number of hexagons, but when it's wrong, this will keep it from
659
+ // failing
660
+ int totalVerts = geofence.numVerts;
661
+ for (int i = 0; i < geoPolygon->numHoles; i++) {
662
+ totalVerts += geoPolygon->holes[i].numVerts;
663
+ }
664
+ if (numHexagons < totalVerts) numHexagons = totalVerts;
665
+ // When the polygon is very small, near an icosahedron edge and is an odd
666
+ // resolution, the line tracing needs an extra buffer than the estimator
667
+ // function provides (but beefing that up to cover causes most situations to
668
+ // overallocate memory)
669
+ numHexagons += POLYFILL_BUFFER;
670
+ return numHexagons;
643
671
  }
644
672
 
645
673
  /**
@@ -647,15 +675,109 @@ int H3_EXPORT(maxPolyfillSize)(const GeoPolygon* geoPolygon, int res) {
647
675
  * zeroed memory, and fills it with the hexagons that are contained by
648
676
  * the GeoJSON-like data structure.
649
677
  *
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.
678
+ * This implementation traces the GeoJSON geofence(s) in cartesian space with
679
+ * hexagons, tests them and their neighbors to be contained by the geofence(s),
680
+ * and then any newly found hexagons are used to test again until no new
681
+ * hexagons are found.
653
682
  *
654
683
  * @param geoPolygon The geofence and holes defining the relevant area
655
684
  * @param res The Hexagon resolution (0-15)
656
685
  * @param out The slab of zeroed memory to write to. Assumed to be big enough.
657
686
  */
658
687
  void H3_EXPORT(polyfill)(const GeoPolygon* geoPolygon, int res, H3Index* out) {
688
+ // TODO: Eliminate this wrapper with the H3 4.0.0 release
689
+ int failure = _polyfillInternal(geoPolygon, res, out);
690
+ // The polyfill algorithm can theoretically fail if the allocated memory is
691
+ // not large enough for the polygon, but this should be impossible given the
692
+ // conservative overestimation of the number of hexagons possible.
693
+ // LCOV_EXCL_START
694
+ if (failure) {
695
+ int numHexagons = H3_EXPORT(maxPolyfillSize)(geoPolygon, res);
696
+ for (int i = 0; i < numHexagons; i++) out[i] = H3_NULL;
697
+ }
698
+ // LCOV_EXCL_STOP
699
+ }
700
+
701
+ /**
702
+ * _getEdgeHexagons takes a given geofence ring (either the main geofence or
703
+ * one of the holes) and traces it with hexagons and updates the search and
704
+ * found memory blocks. This is used for determining the initial hexagon set
705
+ * for the polyfill algorithm to execute on.
706
+ *
707
+ * @param geofence The geofence (or hole) to be traced
708
+ * @param numHexagons The maximum number of hexagons possible for the geofence
709
+ * (also the bounds of the search and found arrays)
710
+ * @param res The hexagon resolution (0-15)
711
+ * @param numSearchHexes The number of hexagons found so far to be searched
712
+ * @param search The block of memory containing the hexagons to search from
713
+ * @param found The block of memory containing the hexagons found from the
714
+ * search
715
+ *
716
+ * @return An error code if the hash function cannot insert a found hexagon
717
+ * into the found array.
718
+ */
719
+ int _getEdgeHexagons(const Geofence* geofence, int numHexagons, int res,
720
+ int* numSearchHexes, H3Index* search, H3Index* found) {
721
+ for (int i = 0; i < geofence->numVerts; i++) {
722
+ GeoCoord origin = geofence->verts[i];
723
+ GeoCoord destination = i == geofence->numVerts - 1
724
+ ? geofence->verts[0]
725
+ : geofence->verts[i + 1];
726
+ const int numHexesEstimate =
727
+ lineHexEstimate(&origin, &destination, res);
728
+ for (int j = 0; j < numHexesEstimate; j++) {
729
+ GeoCoord interpolate;
730
+ interpolate.lat =
731
+ (origin.lat * (numHexesEstimate - j) / numHexesEstimate) +
732
+ (destination.lat * j / numHexesEstimate);
733
+ interpolate.lon =
734
+ (origin.lon * (numHexesEstimate - j) / numHexesEstimate) +
735
+ (destination.lon * j / numHexesEstimate);
736
+ H3Index pointHex = H3_EXPORT(geoToH3)(&interpolate, res);
737
+ // A simple hash to store the hexagon, or move to another place if
738
+ // needed
739
+ int loc = (int)(pointHex % numHexagons);
740
+ int loopCount = 0;
741
+ while (found[loc] != 0) {
742
+ // If this conditional is reached, the `found` memory block is
743
+ // too small for the given polygon. This should not happen.
744
+ if (loopCount > numHexagons)
745
+ return HEX_HASH_OVERFLOW; // LCOV_EXCL_LINE
746
+ if (found[loc] == pointHex)
747
+ break; // At least two points of the geofence index to the
748
+ // same cell
749
+ loc = (loc + 1) % numHexagons;
750
+ loopCount++;
751
+ }
752
+ if (found[loc] == pointHex)
753
+ continue; // Skip this hex, already exists in the found hash
754
+ // Otherwise, set it in the found hash for now
755
+ found[loc] = pointHex;
756
+
757
+ search[*numSearchHexes] = pointHex;
758
+ (*numSearchHexes)++;
759
+ }
760
+ }
761
+ return 0;
762
+ }
763
+
764
+ /**
765
+ * _polyfillInternal traces the provided geoPolygon data structure with hexagons
766
+ * and then iteratively searches through these hexagons and their immediate
767
+ * neighbors to see if they are contained within the polygon or not. Those that
768
+ * are found are added to the out array as well as the found array. Once all
769
+ * hexagons to search are checked, the found hexagons become the new search
770
+ * array and the found array is wiped and the process repeats until no new
771
+ * hexagons can be found.
772
+ *
773
+ * @param geoPolygon The geofence and holes defining the relevant area
774
+ * @param res The Hexagon resolution (0-15)
775
+ * @param out The slab of zeroed memory to write to. Assumed to be big enough.
776
+ *
777
+ * @return An error code if any of the hash operations fails to insert a hexagon
778
+ * into an array of memory.
779
+ */
780
+ int _polyfillInternal(const GeoPolygon* geoPolygon, int res, H3Index* out) {
659
781
  // One of the goals of the polyfill algorithm is that two adjacent polygons
660
782
  // with zero overlap have zero overlapping hexagons. That the hexagons are
661
783
  // uniquely assigned. There are a few approaches to take here, such as
@@ -674,39 +796,142 @@ void H3_EXPORT(polyfill)(const GeoPolygon* geoPolygon, int res, H3Index* out) {
674
796
  // This first part is identical to the maxPolyfillSize above.
675
797
 
676
798
  // Get the bounding boxes for the polygon and any holes
677
- BBox* bboxes = malloc((geoPolygon->numHoles + 1) * sizeof(BBox));
799
+ BBox* bboxes = H3_MEMORY(malloc)((geoPolygon->numHoles + 1) * sizeof(BBox));
678
800
  assert(bboxes != NULL);
679
801
  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;
802
+
803
+ // Get the estimated number of hexagons and allocate some temporary memory
804
+ // for the hexagons
805
+ int numHexagons = H3_EXPORT(maxPolyfillSize)(geoPolygon, res);
806
+ H3Index* search = H3_MEMORY(calloc)(numHexagons, sizeof(H3Index));
807
+ H3Index* found = H3_MEMORY(calloc)(numHexagons, sizeof(H3Index));
808
+
809
+ // Some metadata for tracking the state of the search and found memory
810
+ // blocks
811
+ int numSearchHexes = 0;
812
+ int numFoundHexes = 0;
813
+
814
+ // 1. Trace the hexagons along the polygon defining the outer geofence and
815
+ // add them to the search hash. The hexagon containing the geofence point
816
+ // may or may not be contained by the geofence (as the hexagon's center
817
+ // point may be outside of the boundary.)
818
+ const Geofence geofence = geoPolygon->geofence;
819
+ int failure = _getEdgeHexagons(&geofence, numHexagons, res, &numSearchHexes,
820
+ search, found);
821
+ // If this branch is reached, we have exceeded the maximum number of
822
+ // hexagons possible and need to clean up the allocated memory.
823
+ // LCOV_EXCL_START
824
+ if (failure) {
825
+ H3_MEMORY(free)(search);
826
+ H3_MEMORY(free)(found);
827
+ H3_MEMORY(free)(bboxes);
828
+ return failure;
829
+ }
830
+ // LCOV_EXCL_STOP
831
+
832
+ // 2. Iterate over all holes, trace the polygons defining the holes with
833
+ // hexagons and add to only the search hash. We're going to temporarily use
834
+ // the `found` hash to use for dedupe purposes and then re-zero it once
835
+ // we're done here, otherwise we'd have to scan the whole set on each insert
836
+ // to make sure there's no duplicates, which is very inefficient.
837
+ for (int i = 0; i < geoPolygon->numHoles; i++) {
838
+ Geofence* hole = &(geoPolygon->holes[i]);
839
+ failure = _getEdgeHexagons(hole, numHexagons, res, &numSearchHexes,
840
+ search, found);
841
+ // If this branch is reached, we have exceeded the maximum number of
842
+ // hexagons possible and need to clean up the allocated memory.
843
+ // LCOV_EXCL_START
844
+ if (failure) {
845
+ H3_MEMORY(free)(search);
846
+ H3_MEMORY(free)(found);
847
+ H3_MEMORY(free)(bboxes);
848
+ return failure;
698
849
  }
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;
850
+ // LCOV_EXCL_STOP
851
+ }
852
+
853
+ // 3. Re-zero the found hash so it can be used in the main loop below
854
+ for (int i = 0; i < numHexagons; i++) found[i] = 0;
855
+
856
+ // 4. Begin main loop. While the search hash is not empty do the following
857
+ while (numSearchHexes > 0) {
858
+ // Iterate through all hexagons in the current search hash, then loop
859
+ // through all neighbors and test Point-in-Poly, if point-in-poly
860
+ // succeeds, add to out and found hashes if not already there.
861
+ int currentSearchNum = 0;
862
+ int i = 0;
863
+ while (currentSearchNum < numSearchHexes) {
864
+ H3Index ring[MAX_ONE_RING_SIZE] = {0};
865
+ H3Index searchHex = search[i];
866
+ H3_EXPORT(kRing)(searchHex, 1, ring);
867
+ for (int j = 0; j < MAX_ONE_RING_SIZE; j++) {
868
+ if (ring[j] == H3_NULL) {
869
+ continue; // Skip if this was a pentagon and only had 5
870
+ // neighbors
871
+ }
872
+
873
+ H3Index hex = ring[j];
874
+
875
+ // A simple hash to store the hexagon, or move to another place
876
+ // if needed. This MUST be done before the point-in-poly check
877
+ // since that's far more expensive
878
+ int loc = (int)(hex % numHexagons);
879
+ int loopCount = 0;
880
+ while (out[loc] != 0) {
881
+ // If this branch is reached, we have exceeded the maximum
882
+ // number of hexagons possible and need to clean up the
883
+ // allocated memory.
884
+ // LCOV_EXCL_START
885
+ if (loopCount > numHexagons) {
886
+ H3_MEMORY(free)(search);
887
+ H3_MEMORY(free)(found);
888
+ H3_MEMORY(free)(bboxes);
889
+ return -1;
890
+ }
891
+ // LCOV_EXCL_STOP
892
+ if (out[loc] == hex) break; // Skip duplicates found
893
+ loc = (loc + 1) % numHexagons;
894
+ loopCount++;
895
+ }
896
+ if (out[loc] == hex) {
897
+ continue; // Skip this hex, already exists in the out hash
898
+ }
899
+
900
+ // Check if the hexagon is in the polygon or not
901
+ GeoCoord hexCenter;
902
+ H3_EXPORT(h3ToGeo)(hex, &hexCenter);
903
+
904
+ // If not, skip
905
+ if (!pointInsidePolygon(geoPolygon, bboxes, &hexCenter)) {
906
+ continue;
907
+ }
908
+
909
+ // Otherwise set it in the output array
910
+ out[loc] = hex;
911
+
912
+ // Set the hexagon in the found hash
913
+ found[numFoundHexes] = hex;
914
+ numFoundHexes++;
915
+ }
916
+ currentSearchNum++;
917
+ i++;
707
918
  }
919
+
920
+ // Swap the search and found pointers, copy the found hex count to the
921
+ // search hex count, and zero everything related to the found memory.
922
+ H3Index* temp = search;
923
+ search = found;
924
+ found = temp;
925
+ for (int j = 0; j < numSearchHexes; j++) found[j] = 0;
926
+ numSearchHexes = numFoundHexes;
927
+ numFoundHexes = 0;
928
+ // Repeat until no new hexagons are found
708
929
  }
709
- free(bboxes);
930
+ // The out memory structure should be complete, end it here
931
+ H3_MEMORY(free)(bboxes);
932
+ H3_MEMORY(free)(search);
933
+ H3_MEMORY(free)(found);
934
+ return 0;
710
935
  }
711
936
 
712
937
  /**