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