h3 3.6.0 → 3.7.2

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