/src/h3/src/h3lib/lib/h3Index.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2016-2021, 2024 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 h3Index.c |
17 | | * @brief H3Index utility functions |
18 | | * (see h3api.h for the main library entry functions) |
19 | | */ |
20 | | #include "h3Index.h" |
21 | | |
22 | | #include <inttypes.h> |
23 | | #include <math.h> |
24 | | #include <stdlib.h> |
25 | | #include <string.h> |
26 | | |
27 | | #include "alloc.h" |
28 | | #include "baseCells.h" |
29 | | #include "faceijk.h" |
30 | | #include "h3Assert.h" |
31 | | #include "iterators.h" |
32 | | #include "mathExtensions.h" |
33 | | |
34 | | /** @var H3ErrorDescriptions |
35 | | * @brief An array of strings describing each of the H3ErrorCodes enum values |
36 | | */ |
37 | | static char *H3ErrorDescriptions[] = { |
38 | | /* E_SUCCESS */ "Success", |
39 | | /* E_FAILED */ |
40 | | "The operation failed but a more specific error is not available", |
41 | | /* E_DOMAIN */ "Argument was outside of acceptable range", |
42 | | /* E_LATLNG_DOMAIN */ |
43 | | "Latitude or longitude arguments were outside of acceptable range", |
44 | | /* E_RES_DOMAIN */ "Resolution argument was outside of acceptable range", |
45 | | /* E_CELL_INVALID */ "Cell argument was not valid", |
46 | | /* E_DIR_EDGE_INVALID */ "Directed edge argument was not valid", |
47 | | /* E_UNDIR_EDGE_INVALID */ "Undirected edge argument was not valid", |
48 | | /* E_VERTEX_INVALID */ "Vertex argument was not valid", |
49 | | /* E_PENTAGON */ "Pentagon distortion was encountered", |
50 | | /* E_DUPLICATE_INPUT */ "Duplicate input", |
51 | | /* E_NOT_NEIGHBORS */ "Cell arguments were not neighbors", |
52 | | /* E_RES_MISMATCH */ "Cell arguments had incompatible resolutions", |
53 | | /* E_MEMORY_ALLOC */ "Memory allocation failed", |
54 | | /* E_MEMORY_BOUNDS */ "Bounds of provided memory were insufficient", |
55 | | /* E_OPTION_INVALID */ "Mode or flags argument was not valid"}; |
56 | | |
57 | | /** |
58 | | * Returns the string describing the H3Error. This string is internally |
59 | | * allocated and should not be `free`d. |
60 | | * @param err The H3 error. |
61 | | * @return The string describing the H3Error |
62 | | */ |
63 | 0 | const char *H3_EXPORT(describeH3Error)(H3Error err) { |
64 | | // err is always non-negative because it is an unsigned integer |
65 | | // TODO: Better way to bounds check here? |
66 | 0 | if (err <= 15) { |
67 | 0 | return H3ErrorDescriptions[err]; |
68 | 0 | } else { |
69 | 0 | return "Invalid error code"; |
70 | 0 | } |
71 | 0 | } |
72 | | |
73 | | /** |
74 | | * Returns the H3 resolution of an H3 index. |
75 | | * @param h The H3 index. |
76 | | * @return The resolution of the H3 index argument. |
77 | | */ |
78 | 0 | int H3_EXPORT(getResolution)(H3Index h) { return H3_GET_RESOLUTION(h); } |
79 | | |
80 | | /** |
81 | | * Returns the H3 base cell "number" of an H3 cell (hexagon or pentagon). |
82 | | * |
83 | | * Note: Technically works on H3 edges, but will return base cell of the |
84 | | * origin cell. |
85 | | * |
86 | | * @param h The H3 cell. |
87 | | * @return The base cell "number" of the H3 cell argument. |
88 | | */ |
89 | 0 | int H3_EXPORT(getBaseCellNumber)(H3Index h) { return H3_GET_BASE_CELL(h); } |
90 | | |
91 | | /** |
92 | | * Converts a string representation of an H3 index into an H3 index. |
93 | | * @param str The string representation of an H3 index. |
94 | | * @param out Output: The H3 index corresponding to the string argument |
95 | | */ |
96 | 0 | H3Error H3_EXPORT(stringToH3)(const char *str, H3Index *out) { |
97 | 0 | H3Index h = H3_NULL; |
98 | | // If failed, h will be unmodified and we should return H3_NULL anyways. |
99 | 0 | int read = sscanf(str, "%" PRIx64, &h); |
100 | 0 | if (read != 1) { |
101 | 0 | return E_FAILED; |
102 | 0 | } |
103 | 0 | *out = h; |
104 | 0 | return E_SUCCESS; |
105 | 0 | } |
106 | | |
107 | | /** |
108 | | * Converts an H3 index into a string representation. |
109 | | * @param h The H3 index to convert. |
110 | | * @param str The string representation of the H3 index. |
111 | | * @param sz Size of the buffer `str` |
112 | | */ |
113 | 0 | H3Error H3_EXPORT(h3ToString)(H3Index h, char *str, size_t sz) { |
114 | | // An unsigned 64 bit integer will be expressed in at most |
115 | | // 16 digits plus 1 for the null terminator. |
116 | 0 | if (sz < 17) { |
117 | | // Buffer is potentially not large enough. |
118 | 0 | return E_MEMORY_BOUNDS; |
119 | 0 | } |
120 | 0 | sprintf(str, "%" PRIx64, h); |
121 | 0 | return E_SUCCESS; |
122 | 0 | } |
123 | | |
124 | | /* |
125 | | The top 8 bits of any cell should be a specific constant: |
126 | | |
127 | | - The 1 high bit should be `0` |
128 | | - The 4 mode bits should be `0001` (H3_CELL_MODE) |
129 | | - The 3 reserved bits should be `000` |
130 | | |
131 | | In total, the top 8 bits should be `0_0001_000` |
132 | | */ |
133 | 0 | static inline bool _hasGoodTopBits(H3Index h) { |
134 | 0 | h >>= (64 - 8); |
135 | 0 | return h == 0b00001000; |
136 | 0 | } |
137 | | |
138 | | /* Check that no digit from 1 to `res` is 7 (INVALID_DIGIT). |
139 | | |
140 | | MHI = 0b100100100100100100100100100100100100100100100; |
141 | | MLO = MHI >> 2; |
142 | | |
143 | | | d | d & MHI | ~d | ~d - MLO | d & MHI & (~d - MLO) | result | |
144 | | |-----|---------|-----|----------|----------------------|---------| |
145 | | | 000 | 000 | | | 000 | OK | |
146 | | | 001 | 000 | | | 000 | OK | |
147 | | | 010 | 000 | | | 000 | OK | |
148 | | | 011 | 000 | | | 000 | OK | |
149 | | | 100 | 100 | 011 | 010 | 000 | OK | |
150 | | | 101 | 100 | 010 | 001 | 000 | OK | |
151 | | | 110 | 100 | 001 | 000 | 000 | OK | |
152 | | | 111 | 100 | 000 | 111* | 100 | invalid | |
153 | | |
154 | | *: carry happened |
155 | | |
156 | | |
157 | | Note: only care about identifying the *lowest* 7. |
158 | | |
159 | | Examples with multiple digits: |
160 | | |
161 | | | d | d & MHI | ~d | ~d - MLO | d & MHI & (~d - MLO) | result | |
162 | | |---------|---------|---------|----------|----------------------|---------| |
163 | | | 111.111 | 100.100 | 000.000 | 110.111* | 100.100 | invalid | |
164 | | | 110.111 | 100.100 | 001.000 | 111.111* | 100.100 | invalid | |
165 | | | 110.110 | 100.100 | 001.001 | 000.000 | 000.000 | OK | |
166 | | |
167 | | *: carry happened |
168 | | |
169 | | In the second example with 110.111, we "misidentify" the 110 as a 7, due |
170 | | to a carry from the lower bits. But this is OK because we correctly |
171 | | identify the lowest (only, in this example) 7 just before it. |
172 | | |
173 | | We only have to worry about carries affecting higher bits in the case of |
174 | | a 7; all other digits (0--6) don't cause a carry when computing ~d - MLO. |
175 | | So even though a 7 can affect the results of higher bits, this is OK |
176 | | because we will always correctly identify the lowest 7. |
177 | | |
178 | | For further notes, see the discussion here: |
179 | | https://github.com/uber/h3/pull/496#discussion_r795851046 |
180 | | */ |
181 | 0 | static inline bool _hasAny7UptoRes(H3Index h, int res) { |
182 | 0 | const uint64_t MHI = 0b100100100100100100100100100100100100100100100; |
183 | 0 | const uint64_t MLO = MHI >> 2; |
184 | |
|
185 | 0 | int shift = 3 * (15 - res); |
186 | 0 | h >>= shift; |
187 | 0 | h <<= shift; |
188 | 0 | h = (h & MHI & (~h - MLO)); |
189 | |
|
190 | 0 | return h != 0; |
191 | 0 | } |
192 | | |
193 | | /* Check that all unused digits after `res` are set to 7 (INVALID_DIGIT). |
194 | | |
195 | | Bit shift to avoid looping through digits. |
196 | | */ |
197 | 0 | static inline bool _hasAll7AfterRes(H3Index h, int res) { |
198 | | // NOTE: res check is needed because we can't shift by 64 |
199 | 0 | if (res < 15) { |
200 | 0 | int shift = 19 + 3 * res; |
201 | |
|
202 | 0 | h = ~h; |
203 | 0 | h <<= shift; |
204 | 0 | h >>= shift; |
205 | |
|
206 | 0 | return h == 0; |
207 | 0 | } |
208 | 0 | return true; |
209 | 0 | } |
210 | | |
211 | | /* |
212 | | Get index of first nonzero bit of an H3Index. |
213 | | |
214 | | When available, use compiler intrinsics, which should be fast. |
215 | | If not available, fall back to a loop. |
216 | | */ |
217 | 0 | static inline int _firstOneIndex(H3Index h) { |
218 | 0 | #if defined(__GNUC__) || defined(__clang__) |
219 | 0 | return 63 - __builtin_clzll(h); |
220 | | #elif defined(_MSC_VER) && defined(_M_X64) // doesn't work on win32 |
221 | | unsigned long index; |
222 | | _BitScanReverse64(&index, h); |
223 | | return (int)index; |
224 | | #else |
225 | | // Portable fallback |
226 | | int pos = 63 - 19; |
227 | | H3Index m = 1; |
228 | | while ((h & (m << pos)) == 0) pos--; |
229 | | return pos; |
230 | | #endif |
231 | 0 | } |
232 | | |
233 | | /* |
234 | | One final validation just for cells whose base cell (res 0) |
235 | | is a pentagon. |
236 | | |
237 | | Pentagon cells start with a sequence of 0's (CENTER_DIGIT's). |
238 | | The first nonzero digit can't be a 1 (i.e., "deleted subsequence", |
239 | | PENTAGON_SKIPPED_DIGIT, or K_AXES_DIGIT). |
240 | | |
241 | | We can check that (in the lower 45 = 15*3 bits) the position of the |
242 | | first 1 bit isn't divisible by 3. |
243 | | */ |
244 | 0 | static inline bool _hasDeletedSubsequence(H3Index h, int base_cell) { |
245 | | // TODO: https://github.com/uber/h3/issues/984 |
246 | 0 | static const bool isBaseCellPentagonArr[128] = { |
247 | 0 | [4] = 1, [14] = 1, [24] = 1, [38] = 1, [49] = 1, [58] = 1, |
248 | 0 | [63] = 1, [72] = 1, [83] = 1, [97] = 1, [107] = 1, [117] = 1}; |
249 | |
|
250 | 0 | if (isBaseCellPentagonArr[base_cell]) { |
251 | 0 | h <<= 19; |
252 | 0 | h >>= 19; |
253 | |
|
254 | 0 | if (h == 0) return false; // all zeros: res 15 pentagon |
255 | 0 | return _firstOneIndex(h) % 3 == 0; |
256 | 0 | } |
257 | 0 | return false; |
258 | 0 | } |
259 | | |
260 | | /** |
261 | | * Returns whether or not an H3 index is a valid cell (hexagon or pentagon). |
262 | | * @param h The H3 index to validate. |
263 | | * @return 1 if the H3 index if valid, and 0 if it is not. |
264 | | */ |
265 | 0 | int H3_EXPORT(isValidCell)(H3Index h) { |
266 | | /* |
267 | | Look for bit patterns that would disqualify an H3Index from |
268 | | being valid. If identified, exit early. |
269 | | |
270 | | For reference the H3 index bit layout: |
271 | | |
272 | | | Region | # bits | |
273 | | |------------|--------| |
274 | | | High | 1 | |
275 | | | Mode | 4 | |
276 | | | Reserved | 3 | |
277 | | | Resolution | 4 | |
278 | | | Base Cell | 7 | |
279 | | | Digit 1 | 3 | |
280 | | | Digit 2 | 3 | |
281 | | | ... | ... | |
282 | | | Digit 15 | 3 | |
283 | | |
284 | | Speed benefits come from using bit manipulation instead of loops, |
285 | | whenever possible. |
286 | | */ |
287 | 0 | if (!_hasGoodTopBits(h)) return false; |
288 | | |
289 | | // No need to check resolution; any 4 bits give a valid resolution. |
290 | 0 | const int res = H3_GET_RESOLUTION(h); |
291 | | |
292 | | // Get base cell number and check that it is valid. |
293 | 0 | const int bc = H3_GET_BASE_CELL(h); |
294 | 0 | if (bc >= NUM_BASE_CELLS) return false; |
295 | | |
296 | 0 | if (_hasAny7UptoRes(h, res)) return false; |
297 | 0 | if (!_hasAll7AfterRes(h, res)) return false; |
298 | 0 | if (_hasDeletedSubsequence(h, bc)) return false; |
299 | | |
300 | | // If no disqualifications were identified, the index is a valid H3 cell. |
301 | 0 | return true; |
302 | 0 | } |
303 | | |
304 | | /** |
305 | | * Initializes an H3 index. |
306 | | * @param hp The H3 index to initialize. |
307 | | * @param res The H3 resolution to initialize the index to. |
308 | | * @param baseCell The H3 base cell to initialize the index to. |
309 | | * @param initDigit The H3 digit (0-7) to initialize all of the index digits to. |
310 | | */ |
311 | 0 | void setH3Index(H3Index *hp, int res, int baseCell, Direction initDigit) { |
312 | 0 | H3Index h = H3_INIT; |
313 | 0 | H3_SET_MODE(h, H3_CELL_MODE); |
314 | 0 | H3_SET_RESOLUTION(h, res); |
315 | 0 | H3_SET_BASE_CELL(h, baseCell); |
316 | 0 | for (int r = 1; r <= res; r++) H3_SET_INDEX_DIGIT(h, r, initDigit); |
317 | 0 | *hp = h; |
318 | 0 | } |
319 | | |
320 | | /** |
321 | | * cellToParent produces the parent index for a given H3 index |
322 | | * |
323 | | * @param h H3Index to find parent of |
324 | | * @param parentRes The resolution to switch to (parent, grandparent, etc) |
325 | | * @param out Output: H3Index of the parent |
326 | | */ |
327 | 0 | H3Error H3_EXPORT(cellToParent)(H3Index h, int parentRes, H3Index *out) { |
328 | 0 | int childRes = H3_GET_RESOLUTION(h); |
329 | 0 | if (parentRes < 0 || parentRes > MAX_H3_RES) { |
330 | 0 | return E_RES_DOMAIN; |
331 | 0 | } else if (parentRes > childRes) { |
332 | 0 | return E_RES_MISMATCH; |
333 | 0 | } else if (parentRes == childRes) { |
334 | 0 | *out = h; |
335 | 0 | return E_SUCCESS; |
336 | 0 | } |
337 | 0 | H3Index parentH = H3_SET_RESOLUTION(h, parentRes); |
338 | 0 | for (int i = parentRes + 1; i <= childRes; i++) { |
339 | 0 | H3_SET_INDEX_DIGIT(parentH, i, H3_DIGIT_MASK); |
340 | 0 | } |
341 | 0 | *out = parentH; |
342 | 0 | return E_SUCCESS; |
343 | 0 | } |
344 | | |
345 | | /** |
346 | | * Determines whether one resolution is a valid child resolution for a cell. |
347 | | * Each resolution is considered a valid child resolution of itself. |
348 | | * |
349 | | * @param h h3Index parent cell |
350 | | * @param childRes int resolution of the child |
351 | | * |
352 | | * @return The validity of the child resolution |
353 | | */ |
354 | 0 | static bool _hasChildAtRes(H3Index h, int childRes) { |
355 | 0 | int parentRes = H3_GET_RESOLUTION(h); |
356 | 0 | if (childRes < parentRes || childRes > MAX_H3_RES) { |
357 | 0 | return false; |
358 | 0 | } |
359 | 0 | return true; |
360 | 0 | } |
361 | | |
362 | | /** |
363 | | * cellToChildrenSize returns the exact number of children for a cell at a |
364 | | * given child resolution. |
365 | | * |
366 | | * @param h H3Index to find the number of children of |
367 | | * @param childRes The child resolution you're interested in |
368 | | * @param out Output: exact number of children (handles hexagons and |
369 | | * pentagons correctly) |
370 | | */ |
371 | 0 | H3Error H3_EXPORT(cellToChildrenSize)(H3Index h, int childRes, int64_t *out) { |
372 | 0 | if (!_hasChildAtRes(h, childRes)) return E_RES_DOMAIN; |
373 | | |
374 | 0 | int n = childRes - H3_GET_RESOLUTION(h); |
375 | |
|
376 | 0 | if (H3_EXPORT(isPentagon)(h)) { |
377 | 0 | *out = 1 + 5 * (_ipow(7, n) - 1) / 6; |
378 | 0 | } else { |
379 | 0 | *out = _ipow(7, n); |
380 | 0 | } |
381 | 0 | return E_SUCCESS; |
382 | 0 | } |
383 | | |
384 | | /** |
385 | | * makeDirectChild takes an index and immediately returns the immediate child |
386 | | * index based on the specified cell number. Bit operations only, could generate |
387 | | * invalid indexes if not careful (deleted cell under a pentagon). |
388 | | * |
389 | | * @param h H3Index to find the direct child of |
390 | | * @param cellNumber int id of the direct child (0-6) |
391 | | * |
392 | | * @return The new H3Index for the child |
393 | | */ |
394 | 0 | H3Index makeDirectChild(H3Index h, int cellNumber) { |
395 | 0 | int childRes = H3_GET_RESOLUTION(h) + 1; |
396 | 0 | H3Index childH = H3_SET_RESOLUTION(h, childRes); |
397 | 0 | H3_SET_INDEX_DIGIT(childH, childRes, cellNumber); |
398 | 0 | return childH; |
399 | 0 | } |
400 | | |
401 | | /** |
402 | | * cellToChildren takes the given hexagon id and generates all of the children |
403 | | * at the specified resolution storing them into the provided memory pointer. |
404 | | * It's assumed that cellToChildrenSize was used to determine the allocation. |
405 | | * |
406 | | * @param h H3Index to find the children of |
407 | | * @param childRes int the child level to produce |
408 | | * @param children H3Index* the memory to store the resulting addresses in |
409 | | */ |
410 | 0 | H3Error H3_EXPORT(cellToChildren)(H3Index h, int childRes, H3Index *children) { |
411 | 0 | int64_t i = 0; |
412 | 0 | for (IterCellsChildren iter = iterInitParent(h, childRes); iter.h; |
413 | 0 | iterStepChild(&iter)) { |
414 | 0 | children[i] = iter.h; |
415 | 0 | i++; |
416 | 0 | } |
417 | 0 | return E_SUCCESS; |
418 | 0 | } |
419 | | |
420 | | /** |
421 | | * Zero out index digits from start to end, inclusive. |
422 | | * No-op if start > end. |
423 | | */ |
424 | 0 | H3Index _zeroIndexDigits(H3Index h, int start, int end) { |
425 | 0 | if (start > end) return h; |
426 | | |
427 | 0 | H3Index m = 0; |
428 | |
|
429 | 0 | m = ~m; |
430 | 0 | m <<= H3_PER_DIGIT_OFFSET * (end - start + 1); |
431 | 0 | m = ~m; |
432 | 0 | m <<= H3_PER_DIGIT_OFFSET * (MAX_H3_RES - end); |
433 | 0 | m = ~m; |
434 | |
|
435 | 0 | return h & m; |
436 | 0 | } |
437 | | |
438 | | /** |
439 | | * cellToCenterChild produces the center child index for a given H3 index at |
440 | | * the specified resolution |
441 | | * |
442 | | * @param h H3Index to find center child of |
443 | | * @param childRes The resolution to switch to |
444 | | * @param child H3Index of the center child |
445 | | * @return 0 (E_SUCCESS) on success |
446 | | */ |
447 | 0 | H3Error H3_EXPORT(cellToCenterChild)(H3Index h, int childRes, H3Index *child) { |
448 | 0 | if (!_hasChildAtRes(h, childRes)) return E_RES_DOMAIN; |
449 | | |
450 | 0 | h = _zeroIndexDigits(h, H3_GET_RESOLUTION(h) + 1, childRes); |
451 | 0 | H3_SET_RESOLUTION(h, childRes); |
452 | 0 | *child = h; |
453 | 0 | return E_SUCCESS; |
454 | 0 | } |
455 | | |
456 | | /** |
457 | | * compactCells takes a set of hexagons all at the same resolution and |
458 | | * compresses them by pruning full child branches to the parent level. This is |
459 | | * also done for all parents recursively to get the minimum number of hex |
460 | | * addresses that perfectly cover the defined space. |
461 | | * @param h3Set Set of hexagons |
462 | | * @param compactedSet The output array of compressed hexagons (preallocated) |
463 | | * @param numHexes The size of the input and output arrays (possible that no |
464 | | * contiguous regions exist in the set at all and no compression possible) |
465 | | * @return an error code on bad input data |
466 | | */ |
467 | | H3Error H3_EXPORT(compactCells)(const H3Index *h3Set, H3Index *compactedSet, |
468 | 0 | const int64_t numHexes) { |
469 | 0 | if (numHexes == 0) { |
470 | 0 | return E_SUCCESS; |
471 | 0 | } |
472 | 0 | int res = H3_GET_RESOLUTION(h3Set[0]); |
473 | 0 | if (res == 0) { |
474 | | // No compaction possible, just copy the set to output |
475 | 0 | for (int64_t i = 0; i < numHexes; i++) { |
476 | 0 | compactedSet[i] = h3Set[i]; |
477 | 0 | } |
478 | 0 | return E_SUCCESS; |
479 | 0 | } |
480 | 0 | H3Index *remainingHexes = H3_MEMORY(malloc)(numHexes * sizeof(H3Index)); |
481 | 0 | if (!remainingHexes) { |
482 | 0 | return E_MEMORY_ALLOC; |
483 | 0 | } |
484 | 0 | memcpy(remainingHexes, h3Set, numHexes * sizeof(H3Index)); |
485 | 0 | H3Index *hashSetArray = H3_MEMORY(calloc)(numHexes, sizeof(H3Index)); |
486 | 0 | if (!hashSetArray) { |
487 | 0 | H3_MEMORY(free)(remainingHexes); |
488 | 0 | return E_MEMORY_ALLOC; |
489 | 0 | } |
490 | 0 | H3Index *compactedSetOffset = compactedSet; |
491 | 0 | int64_t numRemainingHexes = numHexes; |
492 | 0 | while (numRemainingHexes) { |
493 | 0 | res = H3_GET_RESOLUTION(remainingHexes[0]); |
494 | 0 | int parentRes = res - 1; |
495 | | |
496 | | // If parentRes is less than zero, we've compacted all the way up to the |
497 | | // base cells. Time to process the remaining cells. |
498 | 0 | if (parentRes >= 0) { |
499 | | // Put the parents of the hexagons into the temp array |
500 | | // via a hashing mechanism, and use the reserved bits |
501 | | // to track how many times a parent is duplicated |
502 | 0 | for (int64_t i = 0; i < numRemainingHexes; i++) { |
503 | 0 | H3Index currIndex = remainingHexes[i]; |
504 | | // TODO: This case is coverable (reachable by fuzzer) |
505 | 0 | if (currIndex != 0) { |
506 | | // If the reserved bits were set by the caller, the |
507 | | // algorithm below may encounter undefined behavior |
508 | | // because it expects to have set the reserved bits |
509 | | // itself. |
510 | 0 | if (H3_GET_RESERVED_BITS(currIndex) != 0) { |
511 | 0 | H3_MEMORY(free)(remainingHexes); |
512 | 0 | H3_MEMORY(free)(hashSetArray); |
513 | 0 | return E_CELL_INVALID; |
514 | 0 | } |
515 | | |
516 | 0 | H3Index parent; |
517 | 0 | H3Error parentError = |
518 | 0 | H3_EXPORT(cellToParent)(currIndex, parentRes, &parent); |
519 | | // Should never be reachable as a result of the compact |
520 | | // algorithm. Can happen if cellToParent errors e.g. |
521 | | // because of incompatible resolutions. |
522 | 0 | if (parentError) { |
523 | 0 | H3_MEMORY(free)(remainingHexes); |
524 | 0 | H3_MEMORY(free)(hashSetArray); |
525 | 0 | return parentError; |
526 | 0 | } |
527 | | // Modulus hash the parent into the temp array |
528 | 0 | int64_t loc = (int64_t)(parent % numRemainingHexes); |
529 | 0 | int64_t loopCount = 0; |
530 | 0 | while (hashSetArray[loc] != 0) { |
531 | 0 | if (NEVER(loopCount > numRemainingHexes)) { |
532 | | // This case should not be possible because at |
533 | | // most one index is placed into hashSetArray |
534 | | // per numRemainingHexes. |
535 | 0 | H3_MEMORY(free)(remainingHexes); |
536 | 0 | H3_MEMORY(free)(hashSetArray); |
537 | 0 | return E_FAILED; |
538 | 0 | } |
539 | 0 | H3Index tempIndex = |
540 | 0 | hashSetArray[loc] & H3_RESERVED_MASK_NEGATIVE; |
541 | 0 | if (tempIndex == parent) { |
542 | 0 | int count = |
543 | 0 | H3_GET_RESERVED_BITS(hashSetArray[loc]) + 1; |
544 | 0 | int limitCount = 7; |
545 | 0 | if (H3_EXPORT(isPentagon)( |
546 | 0 | tempIndex & H3_RESERVED_MASK_NEGATIVE)) { |
547 | 0 | limitCount--; |
548 | 0 | } |
549 | | // One is added to count for this check to match |
550 | | // one being added to count later in this |
551 | | // function when checking for all children being |
552 | | // present. |
553 | 0 | if (count + 1 > limitCount) { |
554 | | // Only possible on duplicate input |
555 | 0 | H3_MEMORY(free)(remainingHexes); |
556 | 0 | H3_MEMORY(free)(hashSetArray); |
557 | 0 | return E_DUPLICATE_INPUT; |
558 | 0 | } |
559 | 0 | H3_SET_RESERVED_BITS(parent, count); |
560 | 0 | hashSetArray[loc] = H3_NULL; |
561 | 0 | } else { |
562 | 0 | loc = (loc + 1) % numRemainingHexes; |
563 | 0 | } |
564 | 0 | loopCount++; |
565 | 0 | } |
566 | 0 | hashSetArray[loc] = parent; |
567 | 0 | } |
568 | 0 | } |
569 | 0 | } |
570 | | |
571 | | // Determine which parent hexagons have a complete set |
572 | | // of children and put them in the compactableHexes array |
573 | 0 | int64_t compactableCount = 0; |
574 | 0 | int64_t maxCompactableCount = |
575 | 0 | numRemainingHexes / 6; // Somehow all pentagons; conservative |
576 | 0 | if (maxCompactableCount == 0) { |
577 | 0 | memcpy(compactedSetOffset, remainingHexes, |
578 | 0 | numRemainingHexes * sizeof(remainingHexes[0])); |
579 | 0 | break; |
580 | 0 | } |
581 | 0 | H3Index *compactableHexes = |
582 | 0 | H3_MEMORY(calloc)(maxCompactableCount, sizeof(H3Index)); |
583 | 0 | if (!compactableHexes) { |
584 | 0 | H3_MEMORY(free)(remainingHexes); |
585 | 0 | H3_MEMORY(free)(hashSetArray); |
586 | 0 | return E_MEMORY_ALLOC; |
587 | 0 | } |
588 | 0 | for (int64_t i = 0; i < numRemainingHexes; i++) { |
589 | 0 | if (hashSetArray[i] == 0) continue; |
590 | 0 | int count = H3_GET_RESERVED_BITS(hashSetArray[i]) + 1; |
591 | | // Include the deleted direction for pentagons as implicitly "there" |
592 | 0 | if (H3_EXPORT(isPentagon)(hashSetArray[i] & |
593 | 0 | H3_RESERVED_MASK_NEGATIVE)) { |
594 | | // We need this later on, no need to recalculate |
595 | 0 | H3_SET_RESERVED_BITS(hashSetArray[i], count); |
596 | | // Increment count after setting the reserved bits, |
597 | | // since count is already incremented above, so it |
598 | | // will be the expected value for a complete hexagon. |
599 | 0 | count++; |
600 | 0 | } |
601 | 0 | if (count == 7) { |
602 | | // Bingo! Full set! |
603 | 0 | compactableHexes[compactableCount] = |
604 | 0 | hashSetArray[i] & H3_RESERVED_MASK_NEGATIVE; |
605 | 0 | compactableCount++; |
606 | 0 | } |
607 | 0 | } |
608 | | // Uncompactable hexes are immediately copied into the |
609 | | // output compactedSetOffset |
610 | 0 | int64_t uncompactableCount = 0; |
611 | 0 | for (int64_t i = 0; i < numRemainingHexes; i++) { |
612 | 0 | H3Index currIndex = remainingHexes[i]; |
613 | | // TODO: This case is coverable (reachable by fuzzer) |
614 | 0 | if (currIndex != H3_NULL) { |
615 | 0 | bool isUncompactable = true; |
616 | | // Resolution 0 cells always uncompactable, and trying to take |
617 | | // the res -1 parent of a cell is invalid. |
618 | 0 | if (parentRes >= 0) { |
619 | 0 | H3Index parent; |
620 | 0 | H3Error parentError = |
621 | 0 | H3_EXPORT(cellToParent)(currIndex, parentRes, &parent); |
622 | 0 | if (NEVER(parentError)) { |
623 | 0 | H3_MEMORY(free)(compactableHexes); |
624 | 0 | H3_MEMORY(free)(remainingHexes); |
625 | 0 | H3_MEMORY(free)(hashSetArray); |
626 | 0 | return parentError; |
627 | 0 | } |
628 | | // Modulus hash the parent into the temp array |
629 | | // to determine if this index was included in |
630 | | // the compactableHexes array |
631 | 0 | int64_t loc = (int64_t)(parent % numRemainingHexes); |
632 | 0 | int64_t loopCount = 0; |
633 | 0 | do { |
634 | 0 | if (NEVER(loopCount > numRemainingHexes)) { |
635 | | // This case should not be possible because at most |
636 | | // one index is placed into hashSetArray per input |
637 | | // hexagon. |
638 | 0 | H3_MEMORY(free)(compactableHexes); |
639 | 0 | H3_MEMORY(free)(remainingHexes); |
640 | 0 | H3_MEMORY(free)(hashSetArray); |
641 | 0 | return E_FAILED; |
642 | 0 | } |
643 | 0 | H3Index tempIndex = |
644 | 0 | hashSetArray[loc] & H3_RESERVED_MASK_NEGATIVE; |
645 | 0 | if (tempIndex == parent) { |
646 | 0 | int count = |
647 | 0 | H3_GET_RESERVED_BITS(hashSetArray[loc]) + 1; |
648 | 0 | if (count == 7) { |
649 | 0 | isUncompactable = false; |
650 | 0 | } |
651 | 0 | break; |
652 | 0 | } else { |
653 | 0 | loc = (loc + 1) % numRemainingHexes; |
654 | 0 | } |
655 | 0 | loopCount++; |
656 | 0 | } while (hashSetArray[loc] != parent); |
657 | 0 | } |
658 | 0 | if (isUncompactable) { |
659 | 0 | compactedSetOffset[uncompactableCount] = remainingHexes[i]; |
660 | 0 | uncompactableCount++; |
661 | 0 | } |
662 | 0 | } |
663 | 0 | } |
664 | | // Set up for the next loop |
665 | 0 | memset(hashSetArray, 0, numHexes * sizeof(H3Index)); |
666 | 0 | compactedSetOffset += uncompactableCount; |
667 | 0 | memcpy(remainingHexes, compactableHexes, |
668 | 0 | compactableCount * sizeof(H3Index)); |
669 | 0 | numRemainingHexes = compactableCount; |
670 | 0 | H3_MEMORY(free)(compactableHexes); |
671 | 0 | } |
672 | 0 | H3_MEMORY(free)(remainingHexes); |
673 | 0 | H3_MEMORY(free)(hashSetArray); |
674 | 0 | return E_SUCCESS; |
675 | 0 | } |
676 | | |
677 | | /** |
678 | | * uncompactCells takes a compressed set of cells and expands back to the |
679 | | * original set of cells. |
680 | | * |
681 | | * Skips elements that are H3_NULL (i.e., 0). |
682 | | * |
683 | | * @param compactedSet Set of compacted cells |
684 | | * @param numCompacted The number of cells in the input compacted set |
685 | | * @param outSet Output array for decompressed cells (preallocated) |
686 | | * @param numOut The size of the output array to bound check against |
687 | | * @param res The H3 resolution to decompress to |
688 | | * @return An error code if output array is too small or any cell |
689 | | * is smaller than the output resolution. |
690 | | */ |
691 | | H3Error H3_EXPORT(uncompactCells)(const H3Index *compactedSet, |
692 | | const int64_t numCompacted, H3Index *outSet, |
693 | 0 | const int64_t numOut, const int res) { |
694 | 0 | int64_t i = 0; |
695 | |
|
696 | 0 | for (int64_t j = 0; j < numCompacted; j++) { |
697 | 0 | if (!_hasChildAtRes(compactedSet[j], res)) return E_RES_MISMATCH; |
698 | | |
699 | 0 | for (IterCellsChildren iter = iterInitParent(compactedSet[j], res); |
700 | 0 | iter.h; i++, iterStepChild(&iter)) { |
701 | 0 | if (i >= numOut) return E_MEMORY_BOUNDS; // went too far; abort! |
702 | 0 | outSet[i] = iter.h; |
703 | 0 | } |
704 | 0 | } |
705 | 0 | return E_SUCCESS; |
706 | 0 | } |
707 | | |
708 | | /** |
709 | | * uncompactCellsSize takes a compacted set of hexagons and provides |
710 | | * the exact size of the uncompacted set of hexagons. |
711 | | * |
712 | | * @param compactedSet Set of hexagons |
713 | | * @param numCompacted The number of hexes in the input set |
714 | | * @param res The hexagon resolution to decompress to |
715 | | * @param out The number of hexagons to allocate memory for |
716 | | * @returns E_SUCCESS on success, or another value on error |
717 | | */ |
718 | | H3Error H3_EXPORT(uncompactCellsSize)(const H3Index *compactedSet, |
719 | | const int64_t numCompacted, const int res, |
720 | 0 | int64_t *out) { |
721 | 0 | int64_t numOut = 0; |
722 | 0 | for (int64_t i = 0; i < numCompacted; i++) { |
723 | 0 | if (compactedSet[i] == H3_NULL) continue; |
724 | | |
725 | 0 | int64_t childrenSize; |
726 | 0 | H3Error childrenError = |
727 | 0 | H3_EXPORT(cellToChildrenSize)(compactedSet[i], res, &childrenSize); |
728 | 0 | if (childrenError) { |
729 | | // The parent res does not contain `res`. |
730 | 0 | return E_RES_MISMATCH; |
731 | 0 | } |
732 | 0 | numOut += childrenSize; |
733 | 0 | } |
734 | 0 | *out = numOut; |
735 | 0 | return E_SUCCESS; |
736 | 0 | } |
737 | | |
738 | | /** |
739 | | * isResClassIII takes a hexagon ID and determines if it is in a |
740 | | * Class III resolution (rotated versus the icosahedron and subject |
741 | | * to shape distortion adding extra points on icosahedron edges, making |
742 | | * them not true hexagons). |
743 | | * @param h The H3Index to check. |
744 | | * @return Returns 1 if the hexagon is class III, otherwise 0. |
745 | | */ |
746 | 0 | int H3_EXPORT(isResClassIII)(H3Index h) { return H3_GET_RESOLUTION(h) % 2; } |
747 | | |
748 | | /** |
749 | | * isPentagon takes an H3Index and determines if it is actually a pentagon. |
750 | | * @param h The H3Index to check. |
751 | | * @return Returns 1 if it is a pentagon, otherwise 0. |
752 | | */ |
753 | 0 | int H3_EXPORT(isPentagon)(H3Index h) { |
754 | 0 | return _isBaseCellPentagon(H3_GET_BASE_CELL(h)) && |
755 | 0 | !_h3LeadingNonZeroDigit(h); |
756 | 0 | } |
757 | | |
758 | | /** |
759 | | * Returns the highest resolution non-zero digit in an H3Index. |
760 | | * @param h The H3Index. |
761 | | * @return The highest resolution non-zero digit in the H3Index. |
762 | | */ |
763 | 0 | Direction _h3LeadingNonZeroDigit(H3Index h) { |
764 | 0 | for (int r = 1; r <= H3_GET_RESOLUTION(h); r++) |
765 | 0 | if (H3_GET_INDEX_DIGIT(h, r)) return H3_GET_INDEX_DIGIT(h, r); |
766 | | |
767 | | // if we're here it's all 0's |
768 | 0 | return CENTER_DIGIT; |
769 | 0 | } |
770 | | |
771 | | /** |
772 | | * Rotate an H3Index 60 degrees counter-clockwise about a pentagonal center. |
773 | | * @param h The H3Index. |
774 | | */ |
775 | 0 | H3Index _h3RotatePent60ccw(H3Index h) { |
776 | | // rotate in place; skips any leading 1 digits (k-axis) |
777 | |
|
778 | 0 | int foundFirstNonZeroDigit = 0; |
779 | 0 | for (int r = 1, res = H3_GET_RESOLUTION(h); r <= res; r++) { |
780 | | // rotate this digit |
781 | 0 | H3_SET_INDEX_DIGIT(h, r, _rotate60ccw(H3_GET_INDEX_DIGIT(h, r))); |
782 | | |
783 | | // look for the first non-zero digit so we |
784 | | // can adjust for deleted k-axes sequence |
785 | | // if necessary |
786 | 0 | if (!foundFirstNonZeroDigit && H3_GET_INDEX_DIGIT(h, r) != 0) { |
787 | 0 | foundFirstNonZeroDigit = 1; |
788 | | |
789 | | // adjust for deleted k-axes sequence |
790 | 0 | if (_h3LeadingNonZeroDigit(h) == K_AXES_DIGIT) |
791 | 0 | h = _h3Rotate60ccw(h); |
792 | 0 | } |
793 | 0 | } |
794 | 0 | return h; |
795 | 0 | } |
796 | | |
797 | | /** |
798 | | * Rotate an H3Index 60 degrees clockwise about a pentagonal center. |
799 | | * @param h The H3Index. |
800 | | */ |
801 | 0 | H3Index _h3RotatePent60cw(H3Index h) { |
802 | | // rotate in place; skips any leading 1 digits (k-axis) |
803 | |
|
804 | 0 | int foundFirstNonZeroDigit = 0; |
805 | 0 | for (int r = 1, res = H3_GET_RESOLUTION(h); r <= res; r++) { |
806 | | // rotate this digit |
807 | 0 | H3_SET_INDEX_DIGIT(h, r, _rotate60cw(H3_GET_INDEX_DIGIT(h, r))); |
808 | | |
809 | | // look for the first non-zero digit so we |
810 | | // can adjust for deleted k-axes sequence |
811 | | // if necessary |
812 | 0 | if (!foundFirstNonZeroDigit && H3_GET_INDEX_DIGIT(h, r) != 0) { |
813 | 0 | foundFirstNonZeroDigit = 1; |
814 | | |
815 | | // adjust for deleted k-axes sequence |
816 | 0 | if (_h3LeadingNonZeroDigit(h) == K_AXES_DIGIT) h = _h3Rotate60cw(h); |
817 | 0 | } |
818 | 0 | } |
819 | 0 | return h; |
820 | 0 | } |
821 | | |
822 | | /** |
823 | | * Rotate an H3Index 60 degrees counter-clockwise. |
824 | | * @param h The H3Index. |
825 | | */ |
826 | 0 | H3Index _h3Rotate60ccw(H3Index h) { |
827 | 0 | for (int r = 1, res = H3_GET_RESOLUTION(h); r <= res; r++) { |
828 | 0 | Direction oldDigit = H3_GET_INDEX_DIGIT(h, r); |
829 | 0 | H3_SET_INDEX_DIGIT(h, r, _rotate60ccw(oldDigit)); |
830 | 0 | } |
831 | |
|
832 | 0 | return h; |
833 | 0 | } |
834 | | |
835 | | /** |
836 | | * Rotate an H3Index 60 degrees clockwise. |
837 | | * @param h The H3Index. |
838 | | */ |
839 | 0 | H3Index _h3Rotate60cw(H3Index h) { |
840 | 0 | for (int r = 1, res = H3_GET_RESOLUTION(h); r <= res; r++) { |
841 | 0 | H3_SET_INDEX_DIGIT(h, r, _rotate60cw(H3_GET_INDEX_DIGIT(h, r))); |
842 | 0 | } |
843 | |
|
844 | 0 | return h; |
845 | 0 | } |
846 | | |
847 | | /** |
848 | | * Convert an FaceIJK address to the corresponding H3Index. |
849 | | * @param fijk The FaceIJK address. |
850 | | * @param res The cell resolution. |
851 | | * @return The encoded H3Index (or H3_NULL on failure). |
852 | | */ |
853 | 0 | H3Index _faceIjkToH3(const FaceIJK *fijk, int res) { |
854 | | // initialize the index |
855 | 0 | H3Index h = H3_INIT; |
856 | 0 | H3_SET_MODE(h, H3_CELL_MODE); |
857 | 0 | H3_SET_RESOLUTION(h, res); |
858 | | |
859 | | // check for res 0/base cell |
860 | 0 | if (res == 0) { |
861 | 0 | if (fijk->coord.i > MAX_FACE_COORD || fijk->coord.j > MAX_FACE_COORD || |
862 | 0 | fijk->coord.k > MAX_FACE_COORD) { |
863 | | // out of range input |
864 | 0 | return H3_NULL; |
865 | 0 | } |
866 | | |
867 | 0 | H3_SET_BASE_CELL(h, _faceIjkToBaseCell(fijk)); |
868 | 0 | return h; |
869 | 0 | } |
870 | | |
871 | | // we need to find the correct base cell FaceIJK for this H3 index; |
872 | | // start with the passed in face and resolution res ijk coordinates |
873 | | // in that face's coordinate system |
874 | 0 | FaceIJK fijkBC = *fijk; |
875 | | |
876 | | // build the H3Index from finest res up |
877 | | // adjust r for the fact that the res 0 base cell offsets the indexing |
878 | | // digits |
879 | 0 | CoordIJK *ijk = &fijkBC.coord; |
880 | 0 | for (int r = res - 1; r >= 0; r--) { |
881 | 0 | CoordIJK lastIJK = *ijk; |
882 | 0 | CoordIJK lastCenter; |
883 | 0 | if (isResolutionClassIII(r + 1)) { |
884 | | // rotate ccw |
885 | 0 | _upAp7(ijk); |
886 | 0 | lastCenter = *ijk; |
887 | 0 | _downAp7(&lastCenter); |
888 | 0 | } else { |
889 | | // rotate cw |
890 | 0 | _upAp7r(ijk); |
891 | 0 | lastCenter = *ijk; |
892 | 0 | _downAp7r(&lastCenter); |
893 | 0 | } |
894 | |
|
895 | 0 | CoordIJK diff; |
896 | 0 | _ijkSub(&lastIJK, &lastCenter, &diff); |
897 | 0 | _ijkNormalize(&diff); |
898 | |
|
899 | 0 | H3_SET_INDEX_DIGIT(h, r + 1, _unitIjkToDigit(&diff)); |
900 | 0 | } |
901 | | |
902 | | // fijkBC should now hold the IJK of the base cell in the |
903 | | // coordinate system of the current face |
904 | |
|
905 | 0 | if (fijkBC.coord.i > MAX_FACE_COORD || fijkBC.coord.j > MAX_FACE_COORD || |
906 | 0 | fijkBC.coord.k > MAX_FACE_COORD) { |
907 | | // out of range input |
908 | 0 | return H3_NULL; |
909 | 0 | } |
910 | | |
911 | | // lookup the correct base cell |
912 | 0 | int baseCell = _faceIjkToBaseCell(&fijkBC); |
913 | 0 | H3_SET_BASE_CELL(h, baseCell); |
914 | | |
915 | | // rotate if necessary to get canonical base cell orientation |
916 | | // for this base cell |
917 | 0 | int numRots = _faceIjkToBaseCellCCWrot60(&fijkBC); |
918 | 0 | if (_isBaseCellPentagon(baseCell)) { |
919 | | // force rotation out of missing k-axes sub-sequence |
920 | 0 | if (_h3LeadingNonZeroDigit(h) == K_AXES_DIGIT) { |
921 | | // check for a cw/ccw offset face; default is ccw |
922 | 0 | if (_baseCellIsCwOffset(baseCell, fijkBC.face)) { |
923 | 0 | h = _h3Rotate60cw(h); |
924 | 0 | } else { |
925 | 0 | h = _h3Rotate60ccw(h); |
926 | 0 | } |
927 | 0 | } |
928 | |
|
929 | 0 | for (int i = 0; i < numRots; i++) h = _h3RotatePent60ccw(h); |
930 | 0 | } else { |
931 | 0 | for (int i = 0; i < numRots; i++) { |
932 | 0 | h = _h3Rotate60ccw(h); |
933 | 0 | } |
934 | 0 | } |
935 | |
|
936 | 0 | return h; |
937 | 0 | } |
938 | | |
939 | | /** |
940 | | * Encodes a coordinate on the sphere to the H3 index of the containing cell at |
941 | | * the specified resolution. |
942 | | * |
943 | | * Returns 0 on invalid input. |
944 | | * |
945 | | * @param g The spherical coordinates to encode. |
946 | | * @param res The desired H3 resolution for the encoding. |
947 | | * @param out The encoded H3Index. |
948 | | * @returns E_SUCCESS (0) on success, another value otherwise |
949 | | */ |
950 | 0 | H3Error H3_EXPORT(latLngToCell)(const LatLng *g, int res, H3Index *out) { |
951 | 0 | if (res < 0 || res > MAX_H3_RES) { |
952 | 0 | return E_RES_DOMAIN; |
953 | 0 | } |
954 | 0 | if (!isfinite(g->lat) || !isfinite(g->lng)) { |
955 | 0 | return E_LATLNG_DOMAIN; |
956 | 0 | } |
957 | | |
958 | 0 | FaceIJK fijk; |
959 | 0 | _geoToFaceIjk(g, res, &fijk); |
960 | 0 | *out = _faceIjkToH3(&fijk, res); |
961 | 0 | if (ALWAYS(*out)) { |
962 | 0 | return E_SUCCESS; |
963 | 0 | } else { |
964 | 0 | return E_FAILED; |
965 | 0 | } |
966 | 0 | } |
967 | | |
968 | | /** |
969 | | * Convert an H3Index to the FaceIJK address on a specified icosahedral face. |
970 | | * @param h The H3Index. |
971 | | * @param fijk The FaceIJK address, initialized with the desired face |
972 | | * and normalized base cell coordinates. |
973 | | * @return Returns 1 if the possibility of overage exists, otherwise 0. |
974 | | */ |
975 | 0 | int _h3ToFaceIjkWithInitializedFijk(H3Index h, FaceIJK *fijk) { |
976 | 0 | CoordIJK *ijk = &fijk->coord; |
977 | 0 | int res = H3_GET_RESOLUTION(h); |
978 | | |
979 | | // center base cell hierarchy is entirely on this face |
980 | 0 | int possibleOverage = 1; |
981 | 0 | if (!_isBaseCellPentagon(H3_GET_BASE_CELL(h)) && |
982 | 0 | (res == 0 || |
983 | 0 | (fijk->coord.i == 0 && fijk->coord.j == 0 && fijk->coord.k == 0))) |
984 | 0 | possibleOverage = 0; |
985 | |
|
986 | 0 | for (int r = 1; r <= res; r++) { |
987 | 0 | if (isResolutionClassIII(r)) { |
988 | | // Class III == rotate ccw |
989 | 0 | _downAp7(ijk); |
990 | 0 | } else { |
991 | | // Class II == rotate cw |
992 | 0 | _downAp7r(ijk); |
993 | 0 | } |
994 | |
|
995 | 0 | _neighbor(ijk, H3_GET_INDEX_DIGIT(h, r)); |
996 | 0 | } |
997 | |
|
998 | 0 | return possibleOverage; |
999 | 0 | } |
1000 | | |
1001 | | /** |
1002 | | * Convert an H3Index to a FaceIJK address. |
1003 | | * @param h The H3Index. |
1004 | | * @param fijk The corresponding FaceIJK address. |
1005 | | */ |
1006 | 0 | H3Error _h3ToFaceIjk(H3Index h, FaceIJK *fijk) { |
1007 | 0 | int baseCell = H3_GET_BASE_CELL(h); |
1008 | 0 | if (NEVER(baseCell < 0) || baseCell >= NUM_BASE_CELLS) { |
1009 | | // Base cells less than zero can not be represented in an index |
1010 | | // To prevent reading uninitialized memory, we zero the output. |
1011 | 0 | fijk->face = 0; |
1012 | 0 | fijk->coord.i = fijk->coord.j = fijk->coord.k = 0; |
1013 | 0 | return E_CELL_INVALID; |
1014 | 0 | } |
1015 | | // adjust for the pentagonal missing sequence; all of sub-sequence 5 needs |
1016 | | // to be adjusted (and some of sub-sequence 4 below) |
1017 | 0 | if (_isBaseCellPentagon(baseCell) && _h3LeadingNonZeroDigit(h) == 5) |
1018 | 0 | h = _h3Rotate60cw(h); |
1019 | | |
1020 | | // start with the "home" face and ijk+ coordinates for the base cell of c |
1021 | 0 | *fijk = baseCellData[baseCell].homeFijk; |
1022 | 0 | if (!_h3ToFaceIjkWithInitializedFijk(h, fijk)) |
1023 | 0 | return E_SUCCESS; // no overage is possible; h lies on this face |
1024 | | |
1025 | | // if we're here we have the potential for an "overage"; i.e., it is |
1026 | | // possible that c lies on an adjacent face |
1027 | | |
1028 | 0 | CoordIJK origIJK = fijk->coord; |
1029 | | |
1030 | | // if we're in Class III, drop into the next finer Class II grid |
1031 | 0 | int res = H3_GET_RESOLUTION(h); |
1032 | 0 | if (isResolutionClassIII(res)) { |
1033 | | // Class III |
1034 | 0 | _downAp7r(&fijk->coord); |
1035 | 0 | res++; |
1036 | 0 | } |
1037 | | |
1038 | | // adjust for overage if needed |
1039 | | // a pentagon base cell with a leading 4 digit requires special handling |
1040 | 0 | int pentLeading4 = |
1041 | 0 | (_isBaseCellPentagon(baseCell) && _h3LeadingNonZeroDigit(h) == 4); |
1042 | 0 | if (_adjustOverageClassII(fijk, res, pentLeading4, 0) != NO_OVERAGE) { |
1043 | | // if the base cell is a pentagon we have the potential for secondary |
1044 | | // overages |
1045 | 0 | if (_isBaseCellPentagon(baseCell)) { |
1046 | 0 | while (_adjustOverageClassII(fijk, res, 0, 0) != NO_OVERAGE) |
1047 | 0 | continue; |
1048 | 0 | } |
1049 | |
|
1050 | 0 | if (res != H3_GET_RESOLUTION(h)) _upAp7r(&fijk->coord); |
1051 | 0 | } else if (res != H3_GET_RESOLUTION(h)) { |
1052 | 0 | fijk->coord = origIJK; |
1053 | 0 | } |
1054 | 0 | return E_SUCCESS; |
1055 | 0 | } |
1056 | | |
1057 | | /** |
1058 | | * Determines the spherical coordinates of the center point of an H3 index. |
1059 | | * |
1060 | | * @param h3 The H3 index. |
1061 | | * @param g The spherical coordinates of the H3 cell center. |
1062 | | */ |
1063 | 0 | H3Error H3_EXPORT(cellToLatLng)(H3Index h3, LatLng *g) { |
1064 | 0 | FaceIJK fijk; |
1065 | 0 | H3Error e = _h3ToFaceIjk(h3, &fijk); |
1066 | 0 | if (e) { |
1067 | 0 | return e; |
1068 | 0 | } |
1069 | 0 | _faceIjkToGeo(&fijk, H3_GET_RESOLUTION(h3), g); |
1070 | 0 | return E_SUCCESS; |
1071 | 0 | } |
1072 | | |
1073 | | /** |
1074 | | * Determines the cell boundary in spherical coordinates for an H3 index. |
1075 | | * |
1076 | | * @param h3 The H3 index. |
1077 | | * @param cb The boundary of the H3 cell in spherical coordinates. |
1078 | | */ |
1079 | 0 | H3Error H3_EXPORT(cellToBoundary)(H3Index h3, CellBoundary *cb) { |
1080 | 0 | FaceIJK fijk; |
1081 | 0 | H3Error e = _h3ToFaceIjk(h3, &fijk); |
1082 | 0 | if (e) { |
1083 | 0 | return e; |
1084 | 0 | } |
1085 | 0 | if (H3_EXPORT(isPentagon)(h3)) { |
1086 | 0 | _faceIjkPentToCellBoundary(&fijk, H3_GET_RESOLUTION(h3), 0, |
1087 | 0 | NUM_PENT_VERTS, cb); |
1088 | 0 | } else { |
1089 | 0 | _faceIjkToCellBoundary(&fijk, H3_GET_RESOLUTION(h3), 0, NUM_HEX_VERTS, |
1090 | 0 | cb); |
1091 | 0 | } |
1092 | 0 | return E_SUCCESS; |
1093 | 0 | } |
1094 | | |
1095 | | /** |
1096 | | * Returns the max number of possible icosahedron faces an H3 index |
1097 | | * may intersect. |
1098 | | * |
1099 | | * @return int count of faces |
1100 | | */ |
1101 | 0 | H3Error H3_EXPORT(maxFaceCount)(H3Index h3, int *out) { |
1102 | | // a pentagon always intersects 5 faces, a hexagon never intersects more |
1103 | | // than 2 (but may only intersect 1) |
1104 | 0 | *out = H3_EXPORT(isPentagon)(h3) ? 5 : 2; |
1105 | 0 | return E_SUCCESS; |
1106 | 0 | } |
1107 | | |
1108 | | /** |
1109 | | * Find all icosahedron faces intersected by a given H3 index, represented |
1110 | | * as integers from 0-19. The array is sparse; since 0 is a valid value, |
1111 | | * invalid array values are represented as -1. It is the responsibility of |
1112 | | * the caller to filter out invalid values. |
1113 | | * |
1114 | | * @param h3 The H3 index |
1115 | | * @param out Output array. Must be of size maxFaceCount(h3). |
1116 | | */ |
1117 | 0 | H3Error H3_EXPORT(getIcosahedronFaces)(H3Index h3, int *out) { |
1118 | 0 | int res = H3_GET_RESOLUTION(h3); |
1119 | 0 | int isPent = H3_EXPORT(isPentagon)(h3); |
1120 | | |
1121 | | // We can't use the vertex-based approach here for class II pentagons, |
1122 | | // because all their vertices are on the icosahedron edges. Their |
1123 | | // direct child pentagons cross the same faces, so use those instead. |
1124 | 0 | if (isPent && !isResolutionClassIII(res)) { |
1125 | | // Note that this would not work for res 15, but this is only run on |
1126 | | // Class II pentagons, it should never be invoked for a res 15 index. |
1127 | 0 | H3Index childPentagon = makeDirectChild(h3, 0); |
1128 | 0 | return H3_EXPORT(getIcosahedronFaces)(childPentagon, out); |
1129 | 0 | } |
1130 | | |
1131 | | // convert to FaceIJK |
1132 | 0 | FaceIJK fijk; |
1133 | 0 | H3Error err = _h3ToFaceIjk(h3, &fijk); |
1134 | 0 | if (err) { |
1135 | 0 | return err; |
1136 | 0 | } |
1137 | | |
1138 | | // Get all vertices as FaceIJK addresses. For simplicity, always |
1139 | | // initialize the array with 6 verts, ignoring the last one for pentagons |
1140 | 0 | FaceIJK fijkVerts[NUM_HEX_VERTS]; |
1141 | 0 | int vertexCount; |
1142 | |
|
1143 | 0 | if (isPent) { |
1144 | 0 | vertexCount = NUM_PENT_VERTS; |
1145 | 0 | _faceIjkPentToVerts(&fijk, &res, fijkVerts); |
1146 | 0 | } else { |
1147 | 0 | vertexCount = NUM_HEX_VERTS; |
1148 | 0 | _faceIjkToVerts(&fijk, &res, fijkVerts); |
1149 | 0 | } |
1150 | | |
1151 | | // We may not use all of the slots in the output array, |
1152 | | // so fill with invalid values to indicate unused slots |
1153 | 0 | int faceCount; |
1154 | 0 | H3Error maxFaceCountError = H3_EXPORT(maxFaceCount)(h3, &faceCount); |
1155 | 0 | if (NEVER(maxFaceCountError != E_SUCCESS)) { |
1156 | 0 | return maxFaceCountError; |
1157 | 0 | } |
1158 | 0 | for (int i = 0; i < faceCount; i++) { |
1159 | 0 | out[i] = INVALID_FACE; |
1160 | 0 | } |
1161 | | |
1162 | | // add each vertex face, using the output array as a hash set |
1163 | 0 | for (int i = 0; i < vertexCount; i++) { |
1164 | 0 | FaceIJK *vert = &fijkVerts[i]; |
1165 | | |
1166 | | // Adjust overage, determining whether this vertex is |
1167 | | // on another face |
1168 | 0 | if (isPent) { |
1169 | 0 | _adjustPentVertOverage(vert, res); |
1170 | 0 | } else { |
1171 | 0 | _adjustOverageClassII(vert, res, 0, 1); |
1172 | 0 | } |
1173 | | |
1174 | | // Save the face to the output array |
1175 | 0 | int face = vert->face; |
1176 | 0 | int pos = 0; |
1177 | | // Find the first empty output position, or the first position |
1178 | | // matching the current face |
1179 | 0 | while (out[pos] != INVALID_FACE && out[pos] != face) { |
1180 | 0 | pos++; |
1181 | 0 | if (pos >= faceCount) { |
1182 | | // Mismatch between the heuristic used in maxFaceCount and |
1183 | | // calculation here - indicates an invalid index. |
1184 | 0 | return E_FAILED; |
1185 | 0 | } |
1186 | 0 | } |
1187 | 0 | out[pos] = face; |
1188 | 0 | } |
1189 | 0 | return E_SUCCESS; |
1190 | 0 | } |
1191 | | |
1192 | | /** |
1193 | | * pentagonCount returns the number of pentagons (same at any resolution) |
1194 | | * |
1195 | | * @return int count of pentagon indexes |
1196 | | */ |
1197 | 0 | int H3_EXPORT(pentagonCount)(void) { return NUM_PENTAGONS; } |
1198 | | |
1199 | | /** |
1200 | | * Generates all pentagons at the specified resolution |
1201 | | * |
1202 | | * @param res The resolution to produce pentagons at. |
1203 | | * @param out Output array. Must be of size pentagonCount(). |
1204 | | */ |
1205 | 0 | H3Error H3_EXPORT(getPentagons)(int res, H3Index *out) { |
1206 | 0 | if (res < 0 || res > MAX_H3_RES) { |
1207 | 0 | return E_RES_DOMAIN; |
1208 | 0 | } |
1209 | 0 | int i = 0; |
1210 | 0 | for (int bc = 0; bc < NUM_BASE_CELLS; bc++) { |
1211 | 0 | if (_isBaseCellPentagon(bc)) { |
1212 | 0 | H3Index pentagon; |
1213 | 0 | setH3Index(&pentagon, res, bc, 0); |
1214 | 0 | out[i++] = pentagon; |
1215 | 0 | } |
1216 | 0 | } |
1217 | 0 | return E_SUCCESS; |
1218 | 0 | } |
1219 | | |
1220 | | /** |
1221 | | * Returns whether or not a resolution is a Class III grid. Note that odd |
1222 | | * resolutions are Class III and even resolutions are Class II. |
1223 | | * @param res The H3 resolution. |
1224 | | * @return 1 if the resolution is a Class III grid, and 0 if the resolution is |
1225 | | * a Class II grid. |
1226 | | */ |
1227 | 0 | int isResolutionClassIII(int res) { return res % 2; } |
1228 | | |
1229 | | /** |
1230 | | * Validate a child position in the context of a given parent, returning |
1231 | | * an error if validation fails. |
1232 | | */ |
1233 | | static H3Error validateChildPos(int64_t childPos, H3Index parent, |
1234 | 0 | int childRes) { |
1235 | 0 | int64_t maxChildCount; |
1236 | 0 | H3Error sizeError = |
1237 | 0 | H3_EXPORT(cellToChildrenSize)(parent, childRes, &maxChildCount); |
1238 | 0 | if (NEVER(sizeError)) { |
1239 | 0 | return sizeError; |
1240 | 0 | } |
1241 | 0 | if (childPos < 0 || childPos >= maxChildCount) { |
1242 | 0 | return E_DOMAIN; |
1243 | 0 | } |
1244 | 0 | return E_SUCCESS; |
1245 | 0 | } |
1246 | | |
1247 | | /** |
1248 | | * Returns the position of the cell within an ordered list of all children of |
1249 | | * the cell's parent at the specified resolution |
1250 | | * @param child Child cell index |
1251 | | * @param parentRes Resolution of the parent cell to find the position within |
1252 | | * @param out Output: The position of the child cell within its parents cell |
1253 | | * list of children |
1254 | | */ |
1255 | 0 | H3Error H3_EXPORT(cellToChildPos)(H3Index child, int parentRes, int64_t *out) { |
1256 | 0 | int childRes = H3_GET_RESOLUTION(child); |
1257 | | // Get the parent at res. This will catch any resolution errors |
1258 | 0 | H3Index originalParent; |
1259 | 0 | H3Error parentError = |
1260 | 0 | H3_EXPORT(cellToParent(child, parentRes, &originalParent)); |
1261 | 0 | if (parentError) { |
1262 | 0 | return parentError; |
1263 | 0 | } |
1264 | | |
1265 | | // Define the initial parent. Note that these variables are reassigned |
1266 | | // within the loop. |
1267 | 0 | H3Index parent = originalParent; |
1268 | 0 | int parentIsPentagon = H3_EXPORT(isPentagon)(parent); |
1269 | | |
1270 | | // Walk up the resolution digits, incrementing the index |
1271 | 0 | *out = 0; |
1272 | 0 | if (parentIsPentagon) { |
1273 | | // Pentagon logic. Pentagon parents skip the 1 digit, so the offsets are |
1274 | | // different from hexagons |
1275 | 0 | for (int res = childRes; res > parentRes; res--) { |
1276 | 0 | H3Error parentError = |
1277 | 0 | H3_EXPORT(cellToParent(child, res - 1, &parent)); |
1278 | 0 | if (NEVER(parentError)) { |
1279 | 0 | return parentError; |
1280 | 0 | } |
1281 | | |
1282 | 0 | parentIsPentagon = H3_EXPORT(isPentagon)(parent); |
1283 | 0 | int rawDigit = H3_GET_INDEX_DIGIT(child, res); |
1284 | | // Validate the digit before proceeding |
1285 | 0 | if (rawDigit == INVALID_DIGIT || |
1286 | 0 | (parentIsPentagon && rawDigit == K_AXES_DIGIT)) { |
1287 | 0 | return E_CELL_INVALID; |
1288 | 0 | } |
1289 | 0 | int digit = |
1290 | 0 | parentIsPentagon && rawDigit > 0 ? rawDigit - 1 : rawDigit; |
1291 | 0 | if (digit != CENTER_DIGIT) { |
1292 | 0 | int64_t hexChildCount = _ipow(7, childRes - res); |
1293 | | // The offset for the 0-digit slot depends on whether the |
1294 | | // current index is the child of a pentagon. If so, the offset |
1295 | | // is based on the count of pentagon children, otherwise, |
1296 | | // hexagon children. |
1297 | 0 | *out += (parentIsPentagon |
1298 | 0 | ? // pentagon children. See the explanation |
1299 | | // for getNumCells in h3api.h.in |
1300 | 0 | 1 + (5 * (hexChildCount - 1)) / 6 |
1301 | 0 | : // one hexagon's children |
1302 | 0 | hexChildCount) + |
1303 | | // the other hexagon children |
1304 | 0 | (digit - 1) * hexChildCount; |
1305 | 0 | } |
1306 | 0 | } |
1307 | 0 | } else { |
1308 | | // Hexagon logic. Offsets are simple powers of 7 |
1309 | 0 | for (int res = childRes; res > parentRes; res--) { |
1310 | 0 | int digit = H3_GET_INDEX_DIGIT(child, res); |
1311 | 0 | if (digit == INVALID_DIGIT) { |
1312 | 0 | return E_CELL_INVALID; |
1313 | 0 | } |
1314 | 0 | *out += digit * _ipow(7, childRes - res); |
1315 | 0 | } |
1316 | 0 | } |
1317 | | |
1318 | 0 | if (NEVER(validateChildPos(*out, originalParent, childRes))) { |
1319 | | // This is the result of an internal error, so return E_FAILED |
1320 | | // instead of the validation error |
1321 | 0 | return E_FAILED; |
1322 | 0 | } |
1323 | | |
1324 | 0 | return E_SUCCESS; |
1325 | 0 | } |
1326 | | |
1327 | | /** |
1328 | | * Returns the child cell at a given position within an ordered list of all |
1329 | | * children at the specified resolution |
1330 | | * @param childPos Position within the ordered list |
1331 | | * @param parent Parent cell of the cell index to find |
1332 | | * @param childRes Resolution of the child cell index |
1333 | | * @param child Output: child cell index |
1334 | | */ |
1335 | | H3Error H3_EXPORT(childPosToCell)(int64_t childPos, H3Index parent, |
1336 | 0 | int childRes, H3Index *child) { |
1337 | | // Validate resolution |
1338 | 0 | if (childRes < 0 || childRes > MAX_H3_RES) { |
1339 | 0 | return E_RES_DOMAIN; |
1340 | 0 | } |
1341 | | // Validate parent resolution |
1342 | 0 | int parentRes = H3_GET_RESOLUTION(parent); |
1343 | 0 | if (childRes < parentRes) { |
1344 | 0 | return E_RES_MISMATCH; |
1345 | 0 | } |
1346 | | // Validate child pos |
1347 | 0 | H3Error childPosErr = validateChildPos(childPos, parent, childRes); |
1348 | 0 | if (childPosErr) { |
1349 | 0 | return childPosErr; |
1350 | 0 | } |
1351 | | |
1352 | 0 | int resOffset = childRes - parentRes; |
1353 | |
|
1354 | 0 | *child = parent; |
1355 | 0 | int64_t idx = childPos; |
1356 | |
|
1357 | 0 | H3_SET_RESOLUTION(*child, childRes); |
1358 | |
|
1359 | 0 | if (H3_EXPORT(isPentagon)(parent)) { |
1360 | | // Pentagon tile logic. Pentagon tiles skip the 1 digit, so the offsets |
1361 | | // are different |
1362 | 0 | bool inPent = true; |
1363 | 0 | for (int res = 1; res <= resOffset; res++) { |
1364 | 0 | int64_t resWidth = _ipow(7, resOffset - res); |
1365 | 0 | if (inPent) { |
1366 | | // While we are inside a parent pentagon, we need to check if |
1367 | | // this cell is a pentagon, and if not, we need to offset its |
1368 | | // digit to account for the skipped direction |
1369 | 0 | int64_t pentWidth = 1 + (5 * (resWidth - 1)) / 6; |
1370 | 0 | if (idx < pentWidth) { |
1371 | 0 | H3_SET_INDEX_DIGIT(*child, parentRes + res, 0); |
1372 | 0 | } else { |
1373 | 0 | idx -= pentWidth; |
1374 | 0 | inPent = false; |
1375 | 0 | H3_SET_INDEX_DIGIT(*child, parentRes + res, |
1376 | 0 | (idx / resWidth) + 2); |
1377 | 0 | idx %= resWidth; |
1378 | 0 | } |
1379 | 0 | } else { |
1380 | | // We're no longer inside a pentagon, continue as for hex |
1381 | 0 | H3_SET_INDEX_DIGIT(*child, parentRes + res, idx / resWidth); |
1382 | 0 | idx %= resWidth; |
1383 | 0 | } |
1384 | 0 | } |
1385 | 0 | } else { |
1386 | | // Hexagon tile logic. Offsets are simple powers of 7 |
1387 | 0 | for (int res = 1; res <= resOffset; res++) { |
1388 | 0 | int64_t resWidth = _ipow(7, resOffset - res); |
1389 | 0 | H3_SET_INDEX_DIGIT(*child, parentRes + res, idx / resWidth); |
1390 | 0 | idx %= resWidth; |
1391 | 0 | } |
1392 | 0 | } |
1393 | |
|
1394 | 0 | return E_SUCCESS; |
1395 | 0 | } |