Coverage Report

Created: 2026-09-01 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h3/src/h3lib/lib/algos.c
Line
Count
Source
1
/*
2
 * Copyright 2016-2021 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 algos.c
17
 * @brief   Hexagon grid algorithms
18
 */
19
20
#include "algos.h"
21
22
#include <assert.h>
23
#include <float.h>
24
#include <math.h>
25
#include <stdbool.h>
26
#include <stdlib.h>
27
#include <string.h>
28
29
#include "alloc.h"
30
#include "baseCells.h"
31
#include "bbox.h"
32
#include "cellsToMultiPoly.h"
33
#include "faceijk.h"
34
#include "h3Assert.h"
35
#include "h3Index.h"
36
#include "h3api.h"
37
#include "latLng.h"
38
#include "linkedGeo.h"
39
#include "mathExtensions.h"
40
#include "polygon.h"
41
42
/*
43
 * Return codes from gridDiskUnsafe and related functions.
44
 */
45
46
0
#define MAX_ONE_RING_SIZE 7
47
0
#define POLYGON_TO_CELLS_BUFFER 12
48
49
/**
50
 * Directions used for traversing a hexagonal ring counterclockwise around
51
 * {1, 0, 0}
52
 *
53
 * <pre>
54
 *      _
55
 *    _/ \\_
56
 *   / \\5/ \\
57
 *   \\0/ \\4/
58
 *   / \\_/ \\
59
 *   \\1/ \\3/
60
 *     \\2/
61
 * </pre>
62
 */
63
static const Direction DIRECTIONS[6] = {J_AXES_DIGIT, JK_AXES_DIGIT,
64
                                        K_AXES_DIGIT, IK_AXES_DIGIT,
65
                                        I_AXES_DIGIT, IJ_AXES_DIGIT};
66
67
/**
68
 * Direction used for traversing to the next outward hexagonal ring.
69
 */
70
static const Direction NEXT_RING_DIRECTION = I_AXES_DIGIT;
71
72
/**
73
 * New digit when traversing along class II grids.
74
 *
75
 * Current digit -> direction -> new digit.
76
 */
77
static const Direction NEW_DIGIT_II[7][7] = {
78
    {CENTER_DIGIT, K_AXES_DIGIT, J_AXES_DIGIT, JK_AXES_DIGIT, I_AXES_DIGIT,
79
     IK_AXES_DIGIT, IJ_AXES_DIGIT},
80
    {K_AXES_DIGIT, I_AXES_DIGIT, JK_AXES_DIGIT, IJ_AXES_DIGIT, IK_AXES_DIGIT,
81
     J_AXES_DIGIT, CENTER_DIGIT},
82
    {J_AXES_DIGIT, JK_AXES_DIGIT, K_AXES_DIGIT, I_AXES_DIGIT, IJ_AXES_DIGIT,
83
     CENTER_DIGIT, IK_AXES_DIGIT},
84
    {JK_AXES_DIGIT, IJ_AXES_DIGIT, I_AXES_DIGIT, IK_AXES_DIGIT, CENTER_DIGIT,
85
     K_AXES_DIGIT, J_AXES_DIGIT},
86
    {I_AXES_DIGIT, IK_AXES_DIGIT, IJ_AXES_DIGIT, CENTER_DIGIT, J_AXES_DIGIT,
87
     JK_AXES_DIGIT, K_AXES_DIGIT},
88
    {IK_AXES_DIGIT, J_AXES_DIGIT, CENTER_DIGIT, K_AXES_DIGIT, JK_AXES_DIGIT,
89
     IJ_AXES_DIGIT, I_AXES_DIGIT},
90
    {IJ_AXES_DIGIT, CENTER_DIGIT, IK_AXES_DIGIT, J_AXES_DIGIT, K_AXES_DIGIT,
91
     I_AXES_DIGIT, JK_AXES_DIGIT}};
92
93
/**
94
 * New traversal direction when traversing along class II grids.
95
 *
96
 * Current digit -> direction -> new ap7 move (at coarser level).
97
 */
98
static const Direction NEW_ADJUSTMENT_II[7][7] = {
99
    {CENTER_DIGIT, CENTER_DIGIT, CENTER_DIGIT, CENTER_DIGIT, CENTER_DIGIT,
100
     CENTER_DIGIT, CENTER_DIGIT},
101
    {CENTER_DIGIT, K_AXES_DIGIT, CENTER_DIGIT, K_AXES_DIGIT, CENTER_DIGIT,
102
     IK_AXES_DIGIT, CENTER_DIGIT},
103
    {CENTER_DIGIT, CENTER_DIGIT, J_AXES_DIGIT, JK_AXES_DIGIT, CENTER_DIGIT,
104
     CENTER_DIGIT, J_AXES_DIGIT},
105
    {CENTER_DIGIT, K_AXES_DIGIT, JK_AXES_DIGIT, JK_AXES_DIGIT, CENTER_DIGIT,
106
     CENTER_DIGIT, CENTER_DIGIT},
107
    {CENTER_DIGIT, CENTER_DIGIT, CENTER_DIGIT, CENTER_DIGIT, I_AXES_DIGIT,
108
     I_AXES_DIGIT, IJ_AXES_DIGIT},
109
    {CENTER_DIGIT, IK_AXES_DIGIT, CENTER_DIGIT, CENTER_DIGIT, I_AXES_DIGIT,
110
     IK_AXES_DIGIT, CENTER_DIGIT},
111
    {CENTER_DIGIT, CENTER_DIGIT, J_AXES_DIGIT, CENTER_DIGIT, IJ_AXES_DIGIT,
112
     CENTER_DIGIT, IJ_AXES_DIGIT}};
113
114
/**
115
 * New traversal direction when traversing along class III grids.
116
 *
117
 * Current digit -> direction -> new ap7 move (at coarser level).
118
 */
119
static const Direction NEW_DIGIT_III[7][7] = {
120
    {CENTER_DIGIT, K_AXES_DIGIT, J_AXES_DIGIT, JK_AXES_DIGIT, I_AXES_DIGIT,
121
     IK_AXES_DIGIT, IJ_AXES_DIGIT},
122
    {K_AXES_DIGIT, J_AXES_DIGIT, JK_AXES_DIGIT, I_AXES_DIGIT, IK_AXES_DIGIT,
123
     IJ_AXES_DIGIT, CENTER_DIGIT},
124
    {J_AXES_DIGIT, JK_AXES_DIGIT, I_AXES_DIGIT, IK_AXES_DIGIT, IJ_AXES_DIGIT,
125
     CENTER_DIGIT, K_AXES_DIGIT},
126
    {JK_AXES_DIGIT, I_AXES_DIGIT, IK_AXES_DIGIT, IJ_AXES_DIGIT, CENTER_DIGIT,
127
     K_AXES_DIGIT, J_AXES_DIGIT},
128
    {I_AXES_DIGIT, IK_AXES_DIGIT, IJ_AXES_DIGIT, CENTER_DIGIT, K_AXES_DIGIT,
129
     J_AXES_DIGIT, JK_AXES_DIGIT},
130
    {IK_AXES_DIGIT, IJ_AXES_DIGIT, CENTER_DIGIT, K_AXES_DIGIT, J_AXES_DIGIT,
131
     JK_AXES_DIGIT, I_AXES_DIGIT},
132
    {IJ_AXES_DIGIT, CENTER_DIGIT, K_AXES_DIGIT, J_AXES_DIGIT, JK_AXES_DIGIT,
133
     I_AXES_DIGIT, IK_AXES_DIGIT}};
134
135
/**
136
 * New traversal direction when traversing along class III grids.
137
 *
138
 * Current digit -> direction -> new ap7 move (at coarser level).
139
 */
140
static const Direction NEW_ADJUSTMENT_III[7][7] = {
141
    {CENTER_DIGIT, CENTER_DIGIT, CENTER_DIGIT, CENTER_DIGIT, CENTER_DIGIT,
142
     CENTER_DIGIT, CENTER_DIGIT},
143
    {CENTER_DIGIT, K_AXES_DIGIT, CENTER_DIGIT, JK_AXES_DIGIT, CENTER_DIGIT,
144
     K_AXES_DIGIT, CENTER_DIGIT},
145
    {CENTER_DIGIT, CENTER_DIGIT, J_AXES_DIGIT, J_AXES_DIGIT, CENTER_DIGIT,
146
     CENTER_DIGIT, IJ_AXES_DIGIT},
147
    {CENTER_DIGIT, JK_AXES_DIGIT, J_AXES_DIGIT, JK_AXES_DIGIT, CENTER_DIGIT,
148
     CENTER_DIGIT, CENTER_DIGIT},
149
    {CENTER_DIGIT, CENTER_DIGIT, CENTER_DIGIT, CENTER_DIGIT, I_AXES_DIGIT,
150
     IK_AXES_DIGIT, I_AXES_DIGIT},
151
    {CENTER_DIGIT, K_AXES_DIGIT, CENTER_DIGIT, CENTER_DIGIT, IK_AXES_DIGIT,
152
     IK_AXES_DIGIT, CENTER_DIGIT},
153
    {CENTER_DIGIT, CENTER_DIGIT, IJ_AXES_DIGIT, CENTER_DIGIT, I_AXES_DIGIT,
154
     CENTER_DIGIT, IJ_AXES_DIGIT}};
155
156
/**
157
 * k value which will encompass all cells at resolution 15.
158
 * This is the largest possible k in the H3 grid system.
159
 */
160
static const int K_ALL_CELLS_AT_RES_15 = 13780510;
161
162
/**
163
 * Maximum number of cells that result from the gridDisk algorithm with the
164
 * given k. Formula source and proof: https://oeis.org/A003215
165
 *
166
 * @param   k   k value, k >= 0.
167
 * @param out   size in indexes
168
 */
169
0
H3Error H3_EXPORT(maxGridDiskSize)(int k, int64_t *out) {
170
0
    if (k < 0) {
171
0
        return E_DOMAIN;
172
0
    }
173
0
    if (k >= K_ALL_CELLS_AT_RES_15) {
174
        // If a k value of this value or above is provided, this function will
175
        // estimate more cells than exist in the H3 grid at the finest
176
        // resolution. This is a problem since the function does signed integer
177
        // arithmetic on `k`, which could overflow. To prevent that, instead
178
        // substitute the maximum number of cells in the grid, as it should not
179
        // be possible for the gridDisk functions to exceed that. Note this is
180
        // not resolution specific. So, when resolution < 15, this function may
181
        // still estimate a size larger than the number of cells in the grid.
182
0
        return H3_EXPORT(getNumCells)(MAX_H3_RES, out);
183
0
    }
184
0
    *out = 3 * (int64_t)k * ((int64_t)k + 1) + 1;
185
0
    return E_SUCCESS;
186
0
}
187
188
/**
189
 * Produce cells within grid distance k of the origin cell.
190
 *
191
 * k-ring 0 is defined as the origin cell, k-ring 1 is defined as k-ring 0 and
192
 * all neighboring cells, and so on.
193
 *
194
 * Output is placed in the provided array in no particular order. Elements of
195
 * the output array may be left zero, as can happen when crossing a pentagon.
196
 *
197
 * @param  origin   origin cell
198
 * @param  k        k >= 0
199
 * @param  out      zero-filled array which must be of size maxGridDiskSize(k)
200
 */
201
0
H3Error H3_EXPORT(gridDisk)(H3Index origin, int k, H3Index *out) {
202
0
    return H3_EXPORT(gridDiskDistances)(origin, k, out, NULL);
203
0
}
204
205
/**
206
 * Produce cells and their distances from the given origin cell, up to
207
 * distance k.
208
 *
209
 * k-ring 0 is defined as the origin cell, k-ring 1 is defined as k-ring 0 and
210
 * all neighboring cells, and so on.
211
 *
212
 * Output is placed in the provided array in no particular order. Elements of
213
 * the output array may be left zero, as can happen when crossing a pentagon.
214
 *
215
 * @param  origin      origin cell
216
 * @param  k           k >= 0
217
 * @param  out         zero-filled array which must be of size
218
 * maxGridDiskSize(k)
219
 * @param  distances   NULL or a zero-filled array which must be of size
220
 *                     maxGridDiskSize(k)
221
 */
222
H3Error H3_EXPORT(gridDiskDistances)(H3Index origin, int k, H3Index *out,
223
0
                                     int *distances) {
224
    // Optimistically try the faster gridDiskUnsafe algorithm first
225
0
    const H3Error failed =
226
0
        H3_EXPORT(gridDiskDistancesUnsafe)(origin, k, out, distances);
227
0
    if (failed) {
228
0
        int64_t maxIdx;
229
0
        H3Error err = H3_EXPORT(maxGridDiskSize)(k, &maxIdx);
230
0
        if (err) {
231
0
            return err;
232
0
        }
233
        // Fast algo failed, fall back to slower, correct algo
234
        // and also wipe out array because contents untrustworthy
235
0
        memset(out, 0, maxIdx * sizeof(H3Index));
236
237
0
        if (distances == NULL) {
238
0
            distances = H3_MEMORY(calloc)(maxIdx, sizeof(int));
239
0
            if (!distances) {
240
0
                return E_MEMORY_ALLOC;
241
0
            }
242
0
            H3Error result = _gridDiskDistancesInternal(origin, k, out,
243
0
                                                        distances, maxIdx, 0);
244
0
            H3_MEMORY(free)(distances);
245
0
            return result;
246
0
        } else {
247
0
            memset(distances, 0, maxIdx * sizeof(int));
248
0
            return _gridDiskDistancesInternal(origin, k, out, distances, maxIdx,
249
0
                                              0);
250
0
        }
251
0
    } else {
252
0
        return E_SUCCESS;
253
0
    }
254
0
}
255
256
/**
257
 * Internal algorithm for the safe but slow version of gridDiskDistances
258
 *
259
 * Adds the origin cell to the output set (treating it as a hash set)
260
 * and recurses to its neighbors, if needed.
261
 *
262
 * @param  origin      Origin cell
263
 * @param  k           Maximum distance to move from the origin
264
 * @param  out         Array treated as a hash set, elements being either
265
 *                     H3Index or 0.
266
 * @param  distances   Scratch area, with elements paralleling the out array.
267
 *                     Elements indicate ijk distance from the origin cell to
268
 *                     the output cell
269
 * @param  maxIdx      Size of out and scratch arrays (must be
270
 * maxGridDiskSize(k))
271
 * @param  curK        Current distance from the origin
272
 */
273
H3Error _gridDiskDistancesInternal(H3Index origin, int k, H3Index *out,
274
0
                                   int *distances, int64_t maxIdx, int curK) {
275
    // Put origin in the output array. out is used as a hash set.
276
0
    int64_t off = origin % maxIdx;
277
0
    while (out[off] != 0 && out[off] != origin) {
278
0
        off = (off + 1) % maxIdx;
279
0
    }
280
281
    // We either got a free slot in the hash set or hit a duplicate
282
    // We might need to process the duplicate anyways because we got
283
    // here on a longer path before.
284
0
    if (out[off] == origin && distances[off] <= curK) return E_SUCCESS;
285
286
0
    out[off] = origin;
287
0
    distances[off] = curK;
288
289
    // Base case: reached an index k away from the origin.
290
0
    if (curK >= k) return E_SUCCESS;
291
292
    // Recurse to all neighbors in no particular order.
293
0
    for (int i = 0; i < 6; i++) {
294
0
        int rotations = 0;
295
0
        H3Index nextNeighbor;
296
0
        H3Error neighborResult = h3NeighborRotations(origin, DIRECTIONS[i],
297
0
                                                     &rotations, &nextNeighbor);
298
0
        if (neighborResult != E_PENTAGON) {
299
            // E_PENTAGON is an expected case when trying to traverse off of
300
            // pentagons.
301
0
            if (neighborResult != E_SUCCESS) {
302
0
                return neighborResult;
303
0
            }
304
0
            neighborResult = _gridDiskDistancesInternal(
305
0
                nextNeighbor, k, out, distances, maxIdx, curK + 1);
306
0
            if (neighborResult) {
307
0
                return neighborResult;
308
0
            }
309
0
        }
310
0
    }
311
0
    return E_SUCCESS;
312
0
}
313
314
/**
315
 * Safe but slow version of gridDiskDistances (also called by it when needed).
316
 *
317
 * Adds the origin cell to the output set (treating it as a hash set)
318
 * and recurses to its neighbors, if needed.
319
 *
320
 * @param  origin      Origin cell
321
 * @param  k           Maximum distance to move from the origin
322
 * @param  out         Array treated as a hash set, elements being either
323
 *                     H3Index or 0.
324
 * @param  distances   Scratch area, with elements paralleling the out array.
325
 *                     Elements indicate ijk distance from the origin cell to
326
 *                     the output cell
327
 */
328
H3Error H3_EXPORT(gridDiskDistancesSafe)(H3Index origin, int k, H3Index *out,
329
0
                                         int *distances) {
330
0
    int64_t maxIdx;
331
0
    H3Error err = H3_EXPORT(maxGridDiskSize)(k, &maxIdx);
332
0
    if (err) {
333
0
        return err;
334
0
    }
335
0
    return _gridDiskDistancesInternal(origin, k, out, distances, maxIdx, 0);
336
0
}
337
338
/**
339
 * Maximum number of cells that result from the gridRing algorithm with the
340
 * given k.
341
 *
342
 * @param   k   k value, k >= 0.
343
 * @param out   size in indexes
344
 */
345
0
H3Error H3_EXPORT(maxGridRingSize)(int k, int64_t *out) {
346
0
    if (k < 0) {
347
0
        return E_DOMAIN;
348
0
    }
349
0
    if (k == 0) {
350
0
        *out = 1;
351
0
        return E_SUCCESS;
352
0
    }
353
0
    *out = 6 * (int64_t)k;
354
0
    return E_SUCCESS;
355
0
}
356
357
/**
358
 * Returns the "hollow" ring of hexagons at exactly grid distance k from
359
 * the origin hexagon. In particular, k=0 returns just the origin hexagon.
360
 *
361
 * Elements of the output array may be left zero, as can happen when crossing a
362
 * pentagon.
363
 *
364
 * @param origin Origin location.
365
 * @param k k >= 0
366
 * @param out Array which must be of size 6 * k (or 1 if k == 0)
367
 * @return 0 if successful; nonzero otherwise.
368
 */
369
0
H3Error H3_EXPORT(gridRing)(H3Index origin, int k, H3Index *out) {
370
    // Optimistically try the faster gridDiskUnsafe algorithm first
371
0
    const H3Error failed = H3_EXPORT(gridRingUnsafe)(origin, k, out);
372
0
    if (!failed) {
373
0
        return E_SUCCESS;
374
0
    }
375
    // Fast algo failed, fall back to slower, correct algo
376
    // and also wipe out array because contents untrustworthy
377
0
    memset(out, 0, 6 * k * sizeof(H3Index));
378
0
    return _gridRingInternal(origin, k, out);
379
0
}
380
381
/**
382
 * Internal algorithm for the safe but slow version of gridRing
383
 *
384
 * This function uses _gridDiskDistancesInternal to get all cells up to distance
385
 * k, then filters those results to include only cells exactly at distance k.
386
 *
387
 * @param origin Origin cell.
388
 * @param k Exact distance to filter for.
389
 * @param out Array which must be of size 6 * k (or 1 if k == 0). This is where
390
 * the final filtered results will be placed.
391
 * @return H3Error indicating success or failure.
392
 */
393
0
H3Error _gridRingInternal(H3Index origin, int k, H3Index *out) {
394
    // Short-circuit on 'identity' ring
395
0
    if (k == 0) {
396
0
        out[0] = origin;
397
0
        return E_SUCCESS;
398
0
    }
399
400
0
    int64_t maxIdx;
401
0
    H3Error err = H3_EXPORT(maxGridDiskSize)(k, &maxIdx);
402
0
    if (err) {
403
0
        return err;
404
0
    }
405
406
0
    H3Index *disk_out = H3_MEMORY(calloc)(maxIdx, sizeof(H3Index));
407
0
    if (!disk_out) {
408
0
        return E_MEMORY_ALLOC;
409
0
    }
410
0
    int *disk_distances = H3_MEMORY(calloc)(maxIdx, sizeof(int));
411
0
    if (!disk_distances) {
412
0
        H3_MEMORY(free)(disk_out);
413
0
        return E_MEMORY_ALLOC;
414
0
    }
415
416
0
    err = _gridDiskDistancesInternal(origin, k, disk_out, disk_distances,
417
0
                                     maxIdx, 0);
418
0
    if (err) {
419
0
        H3_MEMORY(free)(disk_out);
420
0
        H3_MEMORY(free)(disk_distances);
421
0
        return err;
422
0
    }
423
424
0
    int current_idx = 0;
425
0
    for (int64_t i = 0; i < maxIdx; ++i) {
426
0
        if (disk_out[i] != 0 && disk_distances[i] == k) {
427
0
            out[current_idx++] = disk_out[i];
428
0
        }
429
0
    }
430
0
    H3_MEMORY(free)(disk_out);
431
0
    H3_MEMORY(free)(disk_distances);
432
0
    return E_SUCCESS;
433
0
}
434
435
/**
436
 * Returns the hexagon index neighboring the origin, in the direction dir.
437
 *
438
 * Implementation note: The only reachable case where this returns 0 is if the
439
 * origin is a pentagon and the translation is in the k direction. Thus,
440
 * 0 can only be returned if origin is a pentagon.
441
 *
442
 * @param origin Origin index
443
 * @param dir Direction to move in
444
 * @param rotations Number of ccw rotations to perform to reorient the
445
 *                  translation vector. Will be modified to the new number of
446
 *                  rotations to perform (such as when crossing a face edge.)
447
 * @param out H3Index of the specified neighbor if succesful
448
 * @return E_SUCCESS on success
449
 */
450
H3Error h3NeighborRotations(H3Index origin, Direction dir, int *rotations,
451
0
                            H3Index *out) {
452
0
    H3Index current = origin;
453
454
0
    if (dir < CENTER_DIGIT || dir >= INVALID_DIGIT) {
455
0
        return E_FAILED;
456
0
    }
457
    // Ensure that rotations is modulo'd by 6 before any possible addition,
458
    // to protect against signed integer overflow.
459
0
    *rotations = *rotations % 6;
460
0
    for (int i = 0; i < *rotations; i++) {
461
0
        dir = _rotate60ccw(dir);
462
0
    }
463
464
0
    int newRotations = 0;
465
0
    int oldBaseCell = H3_GET_BASE_CELL(current);
466
0
    if (NEVER(oldBaseCell < 0) || oldBaseCell >= NUM_BASE_CELLS) {
467
        // Base cells less than zero can not be represented in an index
468
0
        return E_CELL_INVALID;
469
0
    }
470
0
    Direction oldLeadingDigit = _h3LeadingNonZeroDigit(current);
471
472
    // Adjust the indexing digits and, if needed, the base cell.
473
0
    int r = H3_GET_RESOLUTION(current) - 1;
474
0
    while (true) {
475
0
        if (r == -1) {
476
0
            H3_SET_BASE_CELL(current, baseCellNeighbors[oldBaseCell][dir]);
477
0
            newRotations = baseCellNeighbor60CCWRots[oldBaseCell][dir];
478
479
0
            if (H3_GET_BASE_CELL(current) == INVALID_BASE_CELL) {
480
                // Adjust for the deleted k vertex at the base cell level.
481
                // This edge actually borders a different neighbor.
482
0
                H3_SET_BASE_CELL(current,
483
0
                                 baseCellNeighbors[oldBaseCell][IK_AXES_DIGIT]);
484
0
                newRotations =
485
0
                    baseCellNeighbor60CCWRots[oldBaseCell][IK_AXES_DIGIT];
486
487
                // perform the adjustment for the k-subsequence we're skipping
488
                // over.
489
0
                current = _h3Rotate60ccw(current);
490
0
                *rotations = *rotations + 1;
491
0
            }
492
493
0
            break;
494
0
        } else {
495
0
            Direction oldDigit = H3_GET_INDEX_DIGIT(current, r + 1);
496
0
            Direction nextDir;
497
0
            if (oldDigit == INVALID_DIGIT) {
498
                // Only possible on invalid input
499
0
                return E_CELL_INVALID;
500
0
            } else if (isResolutionClassIII(r + 1)) {
501
0
                H3_SET_INDEX_DIGIT(current, r + 1, NEW_DIGIT_II[oldDigit][dir]);
502
0
                nextDir = NEW_ADJUSTMENT_II[oldDigit][dir];
503
0
            } else {
504
0
                H3_SET_INDEX_DIGIT(current, r + 1,
505
0
                                   NEW_DIGIT_III[oldDigit][dir]);
506
0
                nextDir = NEW_ADJUSTMENT_III[oldDigit][dir];
507
0
            }
508
509
0
            if (nextDir != CENTER_DIGIT) {
510
0
                dir = nextDir;
511
0
                r--;
512
0
            } else {
513
                // No more adjustment to perform
514
0
                break;
515
0
            }
516
0
        }
517
0
    }
518
519
0
    int newBaseCell = H3_GET_BASE_CELL(current);
520
0
    if (_isBaseCellPentagon(newBaseCell)) {
521
0
        int alreadyAdjustedKSubsequence = 0;
522
523
        // force rotation out of missing k-axes sub-sequence
524
0
        if (_h3LeadingNonZeroDigit(current) == K_AXES_DIGIT) {
525
0
            if (oldBaseCell != newBaseCell) {
526
                // in this case, we traversed into the deleted
527
                // k subsequence of a pentagon base cell.
528
                // We need to rotate out of that case depending
529
                // on how we got here.
530
                // check for a cw/ccw offset face; default is ccw
531
532
0
                if (ALWAYS(_baseCellIsCwOffset(
533
0
                        newBaseCell,
534
0
                        baseCellData[oldBaseCell].homeFijk.face))) {
535
0
                    current = _h3Rotate60cw(current);
536
0
                } else {
537
                    // See cwOffsetPent in testGridDisk.c for why this is
538
                    // unreachable.
539
0
                    current = _h3Rotate60ccw(current);
540
0
                }
541
0
                alreadyAdjustedKSubsequence = 1;
542
0
            } else {
543
                // In this case, we traversed into the deleted
544
                // k subsequence from within the same pentagon
545
                // base cell.
546
0
                if (oldLeadingDigit == CENTER_DIGIT) {
547
                    // Undefined: the k direction is deleted from here
548
0
                    return E_PENTAGON;
549
0
                } else if (oldLeadingDigit == JK_AXES_DIGIT) {
550
                    // Rotate out of the deleted k subsequence
551
                    // We also need an additional change to the direction we're
552
                    // moving in
553
0
                    current = _h3Rotate60ccw(current);
554
0
                    *rotations = *rotations + 1;
555
0
                } else if (oldLeadingDigit == IK_AXES_DIGIT) {
556
                    // Rotate out of the deleted k subsequence
557
                    // We also need an additional change to the direction we're
558
                    // moving in
559
0
                    current = _h3Rotate60cw(current);
560
0
                    *rotations = *rotations + 5;
561
0
                } else {
562
                    // Could occur on invalid inputs that were already within
563
                    // the deleted k subsequence
564
0
                    return E_FAILED;
565
0
                }
566
0
            }
567
0
        }
568
569
0
        for (int i = 0; i < newRotations; i++)
570
0
            current = _h3RotatePent60ccw(current);
571
572
        // Account for differing orientation of the base cells (this edge
573
        // might not follow properties of some other edges.)
574
0
        if (oldBaseCell != newBaseCell) {
575
0
            if (_isBaseCellPolarPentagon(newBaseCell)) {
576
                // 'polar' base cells behave differently because they have all
577
                // i neighbors.
578
0
                if (oldBaseCell != 118 && oldBaseCell != 8 &&
579
0
                    _h3LeadingNonZeroDigit(current) != JK_AXES_DIGIT) {
580
0
                    *rotations = *rotations + 1;
581
0
                }
582
0
            } else if (_h3LeadingNonZeroDigit(current) == IK_AXES_DIGIT &&
583
0
                       !alreadyAdjustedKSubsequence) {
584
                // account for distortion introduced to the 5 neighbor by the
585
                // deleted k subsequence.
586
0
                *rotations = *rotations + 1;
587
0
            }
588
0
        }
589
0
    } else {
590
0
        for (int i = 0; i < newRotations; i++)
591
0
            current = _h3Rotate60ccw(current);
592
0
    }
593
594
0
    *rotations = (*rotations + newRotations) % 6;
595
0
    *out = current;
596
597
0
    return E_SUCCESS;
598
0
}
599
600
/**
601
 * Get the direction from the origin to a given neighbor. This is effectively
602
 * the reverse operation for h3NeighborRotations. Returns INVALID_DIGIT if the
603
 * cells are not neighbors.
604
 *
605
 * TODO: This is currently a brute-force algorithm, but as it's O(6) that's
606
 * probably acceptable.
607
 */
608
0
Direction directionForNeighbor(H3Index origin, H3Index destination) {
609
0
    bool isPent = H3_EXPORT(isPentagon)(origin);
610
    // Checks each neighbor, in order, to determine which direction the
611
    // destination neighbor is located. Skips CENTER_DIGIT since that
612
    // would be the origin; skips deleted K direction for pentagons.
613
0
    for (Direction direction = isPent ? J_AXES_DIGIT : K_AXES_DIGIT;
614
0
         direction < NUM_DIGITS; direction++) {
615
0
        H3Index neighbor;
616
0
        int rotations = 0;
617
0
        H3Error neighborError =
618
0
            h3NeighborRotations(origin, direction, &rotations, &neighbor);
619
0
        if (!neighborError && neighbor == destination) {
620
0
            return direction;
621
0
        }
622
0
    }
623
0
    return INVALID_DIGIT;
624
0
}
625
626
/**
627
 * gridDiskUnsafe produces indexes within k distance of the origin index.
628
 * Output behavior is undefined when one of the indexes returned by this
629
 * function is a pentagon or is in the pentagon distortion area.
630
 *
631
 * k-ring 0 is defined as the origin index, k-ring 1 is defined as k-ring 0 and
632
 * all neighboring indexes, and so on.
633
 *
634
 * Output is placed in the provided array in order of increasing distance from
635
 * the origin.
636
 *
637
 * @param origin Origin location.
638
 * @param k k >= 0
639
 * @param out Array which must be of size maxGridDiskSize(k).
640
 * @return 0 if no pentagon or pentagonal distortion area was encountered.
641
 */
642
0
H3Error H3_EXPORT(gridDiskUnsafe)(H3Index origin, int k, H3Index *out) {
643
0
    return H3_EXPORT(gridDiskDistancesUnsafe)(origin, k, out, NULL);
644
0
}
645
646
/**
647
 * gridDiskDistancesUnsafe produces indexes within k distance of the origin
648
 * index. Output behavior is undefined when one of the indexes returned by this
649
 * function is a pentagon or is in the pentagon distortion area.
650
 *
651
 * k-ring 0 is defined as the origin index, k-ring 1 is defined as k-ring 0 and
652
 * all neighboring indexes, and so on.
653
 *
654
 * Output is placed in the provided array in order of increasing distance from
655
 * the origin. The distances in hexagons is placed in the distances array at
656
 * the same offset.
657
 *
658
 * @param origin Origin location.
659
 * @param k k >= 0
660
 * @param out Array which must be of size maxGridDiskSize(k).
661
 * @param distances Null or array which must be of size maxGridDiskSize(k).
662
 * @return 0 if no pentagon or pentagonal distortion area was encountered.
663
 */
664
H3Error H3_EXPORT(gridDiskDistancesUnsafe)(H3Index origin, int k, H3Index *out,
665
0
                                           int *distances) {
666
    // Return codes:
667
    // 1 Pentagon was encountered
668
    // 2 Pentagon distortion (deleted k subsequence) was encountered
669
    // Pentagon being encountered is not itself a problem; really the deleted
670
    // k-subsequence is the problem, but for compatibility reasons we fail on
671
    // the pentagon.
672
0
    if (k < 0) {
673
0
        return E_DOMAIN;
674
0
    }
675
676
    // k must be >= 0, so origin is always needed
677
0
    int idx = 0;
678
0
    out[idx] = origin;
679
0
    if (distances) {
680
0
        distances[idx] = 0;
681
0
    }
682
0
    idx++;
683
684
0
    if (H3_EXPORT(isPentagon)(origin)) {
685
        // Pentagon was encountered; bail out as user doesn't want this.
686
0
        return E_PENTAGON;
687
0
    }
688
689
    // 0 < ring <= k, current ring
690
0
    int ring = 1;
691
    // 0 <= direction < 6, current side of the ring
692
0
    int direction = 0;
693
    // 0 <= i < ring, current position on the side of the ring
694
0
    int i = 0;
695
    // Number of 60 degree ccw rotations to perform on the direction (based on
696
    // which faces have been crossed.)
697
0
    int rotations = 0;
698
699
0
    while (ring <= k) {
700
0
        if (direction == 0 && i == 0) {
701
            // Not putting in the output set as it will be done later, at
702
            // the end of this ring.
703
0
            H3Error neighborResult = h3NeighborRotations(
704
0
                origin, NEXT_RING_DIRECTION, &rotations, &origin);
705
0
            if (neighborResult) {
706
                // Should not be possible because `origin` would have to be a
707
                // pentagon
708
0
                return neighborResult;
709
0
            }
710
711
0
            if (H3_EXPORT(isPentagon)(origin)) {
712
                // Pentagon was encountered; bail out as user doesn't want this.
713
0
                return E_PENTAGON;
714
0
            }
715
0
        }
716
717
0
        H3Error neighborResult = h3NeighborRotations(
718
0
            origin, DIRECTIONS[direction], &rotations, &origin);
719
0
        if (neighborResult) {
720
0
            return neighborResult;
721
0
        }
722
0
        out[idx] = origin;
723
0
        if (distances) {
724
0
            distances[idx] = ring;
725
0
        }
726
0
        idx++;
727
728
0
        i++;
729
        // Check if end of this side of the k-ring
730
0
        if (i == ring) {
731
0
            i = 0;
732
0
            direction++;
733
            // Check if end of this ring.
734
0
            if (direction == 6) {
735
0
                direction = 0;
736
0
                ring++;
737
0
            }
738
0
        }
739
740
0
        if (H3_EXPORT(isPentagon)(origin)) {
741
            // Pentagon was encountered; bail out as user doesn't want this.
742
0
            return E_PENTAGON;
743
0
        }
744
0
    }
745
0
    return E_SUCCESS;
746
0
}
747
748
/**
749
 * gridDisksUnsafe takes an array of input hex IDs and a max k-ring and returns
750
 * an array of hexagon IDs sorted first by the original hex IDs and then by the
751
 * k-ring (0 to max), with no guaranteed sorting within each k-ring group.
752
 *
753
 * @param h3Set A pointer to an array of H3Indexes
754
 * @param length The total number of H3Indexes in h3Set
755
 * @param k The number of rings to generate
756
 * @param out A pointer to the output memory to dump the new set of H3Indexes to
757
 *            The memory block should be equal to maxGridDiskSize(k) * length
758
 * @return 0 if no pentagon is encountered. Cannot trust output otherwise
759
 */
760
H3Error H3_EXPORT(gridDisksUnsafe)(H3Index *h3Set, int length, int k,
761
0
                                   H3Index *out) {
762
0
    H3Index *segment;
763
0
    int64_t segmentSize;
764
0
    H3Error err = H3_EXPORT(maxGridDiskSize)(k, &segmentSize);
765
0
    if (err) {
766
0
        return err;
767
0
    }
768
0
    for (int i = 0; i < length; i++) {
769
        // Determine the appropriate segment of the output array to operate on
770
0
        segment = out + i * segmentSize;
771
0
        H3Error failed = H3_EXPORT(gridDiskUnsafe)(h3Set[i], k, segment);
772
0
        if (failed) return failed;
773
0
    }
774
0
    return E_SUCCESS;
775
0
}
776
777
/**
778
 * Returns the "hollow" ring of hexagons at exactly grid distance k from
779
 * the origin hexagon. In particular, k=0 returns just the origin hexagon.
780
 *
781
 * A nonzero failure code may be returned in some cases, for example,
782
 * if a pentagon is encountered.
783
 * Failure cases may be fixed in future versions.
784
 *
785
 * @param origin Origin location.
786
 * @param k k >= 0
787
 * @param out Array which must be of size 6 * k (or 1 if k == 0)
788
 * @return 0 if successful; nonzero otherwise.
789
 */
790
0
H3Error H3_EXPORT(gridRingUnsafe)(H3Index origin, int k, H3Index *out) {
791
0
    if (k < 0) {
792
0
        return E_DOMAIN;
793
0
    }
794
    // Short-circuit on 'identity' ring
795
0
    if (k == 0) {
796
0
        out[0] = origin;
797
0
        return E_SUCCESS;
798
0
    }
799
0
    int idx = 0;
800
    // Number of 60 degree ccw rotations to perform on the direction (based on
801
    // which faces have been crossed.)
802
0
    int rotations = 0;
803
    // Scratch structure for checking for pentagons
804
0
    if (H3_EXPORT(isPentagon)(origin)) {
805
        // Pentagon was encountered; bail out as user doesn't want this.
806
0
        return E_PENTAGON;
807
0
    }
808
809
0
    for (int ring = 0; ring < k; ring++) {
810
0
        H3Error neighborResult = h3NeighborRotations(
811
0
            origin, NEXT_RING_DIRECTION, &rotations, &origin);
812
0
        if (neighborResult) {
813
            // Should not be possible because `origin` would have to be a
814
            // pentagon
815
0
            return neighborResult;
816
0
        }
817
818
0
        if (H3_EXPORT(isPentagon)(origin)) {
819
0
            return E_PENTAGON;
820
0
        }
821
0
    }
822
823
0
    H3Index lastIndex = origin;
824
825
0
    out[idx] = origin;
826
0
    idx++;
827
828
0
    for (int direction = 0; direction < 6; direction++) {
829
0
        for (int pos = 0; pos < k; pos++) {
830
0
            H3Error neighborResult = h3NeighborRotations(
831
0
                origin, DIRECTIONS[direction], &rotations, &origin);
832
0
            if (neighborResult) {
833
                // Should not be possible because `origin` would have to be a
834
                // pentagon
835
0
                return neighborResult;
836
0
            }
837
838
            // Skip the very last index, it was already added. We do
839
            // however need to traverse to it because of the pentagonal
840
            // distortion check, below.
841
0
            if (pos != k - 1 || direction != 5) {
842
0
                out[idx] = origin;
843
0
                idx++;
844
845
0
                if (H3_EXPORT(isPentagon)(origin)) {
846
0
                    return E_PENTAGON;
847
0
                }
848
0
            }
849
0
        }
850
0
    }
851
852
    // Check that this matches the expected lastIndex, if it doesn't,
853
    // it indicates pentagonal distortion occurred and we should report
854
    // failure.
855
0
    if (lastIndex != origin) {
856
0
        return E_PENTAGON;
857
0
    }
858
0
    return E_SUCCESS;
859
0
}
860
861
/**
862
 * maxPolygonToCellsSize returns the number of cells to allocate space for
863
 * when performing a polygonToCells on the given GeoJSON-like data structure.
864
 *
865
 * The size is the maximum of either the number of points in the geoloop or the
866
 * number of cells in the bounding box of the geoloop.
867
 *
868
 * @param geoPolygon A GeoJSON-like data structure indicating the poly to fill
869
 * @param res Hexagon resolution (0-15)
870
 * @param out number of cells to allocate for
871
 * @return 0 (E_SUCCESS) on success.
872
 */
873
H3Error H3_EXPORT(maxPolygonToCellsSize)(const GeoPolygon *geoPolygon, int res,
874
0
                                         uint32_t flags, int64_t *out) {
875
0
    H3Error flagErr = validatePolygonFlags(flags);
876
0
    if (flagErr) {
877
0
        return flagErr;
878
0
    }
879
    // Get the bounding box for the GeoJSON-like struct
880
0
    BBox bbox;
881
0
    const GeoLoop geoloop = geoPolygon->geoloop;
882
0
    bboxFromGeoLoop(&geoloop, &bbox);
883
0
    int64_t numHexagons;
884
0
    H3Error estimateErr = bboxHexEstimate(&bbox, res, &numHexagons);
885
0
    if (estimateErr) {
886
0
        return estimateErr;
887
0
    }
888
    // This algorithm assumes that the number of vertices is usually less than
889
    // the number of hexagons, but when it's wrong, this will keep it from
890
    // failing
891
0
    int64_t totalVerts = geoloop.numVerts;
892
0
    for (int i = 0; i < geoPolygon->numHoles; i++) {
893
0
        if (ADD_INT64S_OVERFLOWS(totalVerts, geoPolygon->holes[i].numVerts)) {
894
0
            return E_MEMORY_ALLOC;
895
0
        }
896
0
        totalVerts += geoPolygon->holes[i].numVerts;
897
0
    }
898
0
    if (numHexagons < totalVerts) numHexagons = totalVerts;
899
    // When the polygon is very small, near an icosahedron edge and is an odd
900
    // resolution, the line tracing needs an extra buffer than the estimator
901
    // function provides (but beefing that up to cover causes most situations to
902
    // overallocate memory)
903
0
    if (ADD_INT64S_OVERFLOWS(numHexagons, POLYGON_TO_CELLS_BUFFER)) {
904
0
        return E_MEMORY_ALLOC;
905
0
    }
906
0
    numHexagons += POLYGON_TO_CELLS_BUFFER;
907
0
    *out = numHexagons;
908
0
    return E_SUCCESS;
909
0
}
910
911
/**
912
 * _getEdgeHexagons takes a given geoloop ring (either the main geoloop or
913
 * one of the holes) and traces it with hexagons and updates the search and
914
 * found memory blocks. This is used for determining the initial hexagon set
915
 * for the polygonToCells algorithm to execute on.
916
 *
917
 * @param geoloop The geoloop (or hole) to be traced
918
 * @param numHexagons The maximum number of hexagons possible for the geoloop
919
 *                    (also the bounds of the search and found arrays)
920
 * @param res The hexagon resolution (0-15)
921
 * @param numSearchHexes The number of hexagons found so far to be searched
922
 * @param search The block of memory containing the hexagons to search from
923
 * @param found The block of memory containing the hexagons found from the
924
 * search
925
 *
926
 * @return An error code if the hash function cannot insert a found hexagon
927
 *         into the found array.
928
 */
929
H3Error _getEdgeHexagons(const GeoLoop *geoloop, int64_t numHexagons, int res,
930
                         int64_t *numSearchHexes, H3Index *search,
931
0
                         H3Index *found) {
932
0
    for (int i = 0; i < geoloop->numVerts; i++) {
933
0
        LatLng origin = geoloop->verts[i];
934
0
        LatLng destination = i == geoloop->numVerts - 1 ? geoloop->verts[0]
935
0
                                                        : geoloop->verts[i + 1];
936
0
        int64_t numHexesEstimate;
937
0
        H3Error estimateErr =
938
0
            lineHexEstimate(&origin, &destination, res, &numHexesEstimate);
939
0
        if (estimateErr) {
940
0
            return estimateErr;
941
0
        }
942
0
        for (int64_t j = 0; j < numHexesEstimate; j++) {
943
0
            LatLng interpolate;
944
0
            double invNumHexesEst = 1.0 / numHexesEstimate;
945
0
            interpolate.lat =
946
0
                (origin.lat * (numHexesEstimate - j) * invNumHexesEst) +
947
0
                (destination.lat * j * invNumHexesEst);
948
0
            interpolate.lng =
949
0
                (origin.lng * (numHexesEstimate - j) * invNumHexesEst) +
950
0
                (destination.lng * j * invNumHexesEst);
951
0
            H3Index pointHex;
952
0
            H3Error e = H3_EXPORT(latLngToCell)(&interpolate, res, &pointHex);
953
0
            if (e) {
954
0
                return e;
955
0
            }
956
            // A simple hash to store the hexagon, or move to another place if
957
            // needed
958
0
            int64_t loc = (int64_t)(pointHex % numHexagons);
959
0
            int64_t loopCount = 0;
960
0
            while (found[loc] != 0) {
961
                // If this conditional is reached, the `found` memory block is
962
                // too small for the given polygon. This should not happen.
963
                // TODO: Reachable via fuzzer
964
0
                if (loopCount > numHexagons) return E_FAILED;
965
0
                if (found[loc] == pointHex)
966
0
                    break;  // At least two points of the geoloop index to the
967
                            // same cell
968
0
                loc = (loc + 1) % numHexagons;
969
0
                loopCount++;
970
0
            }
971
0
            if (found[loc] == pointHex)
972
0
                continue;  // Skip this hex, already exists in the found hash
973
            // Otherwise, set it in the found hash for now
974
0
            found[loc] = pointHex;
975
976
0
            search[*numSearchHexes] = pointHex;
977
0
            (*numSearchHexes)++;
978
0
        }
979
0
    }
980
0
    return E_SUCCESS;
981
0
}
982
983
/**
984
 * polygonToCells takes a given GeoJSON-like data structure and preallocated,
985
 * zeroed memory, and fills it with the hexagons that are contained by
986
 * the GeoJSON-like data structure.
987
 *
988
 * This implementation traces the GeoJSON geoloop(s) in cartesian space with
989
 * hexagons, tests them and their neighbors to be contained by the geoloop(s),
990
 * and then any newly found hexagons are used to test again until no new
991
 * hexagons are found.
992
 *
993
 * @param geoPolygon The geoloop and holes defining the relevant area
994
 * @param res The Hexagon resolution (0-15)
995
 * @param out The slab of zeroed memory to write to. Assumed to be big enough.
996
 */
997
H3Error H3_EXPORT(polygonToCells)(const GeoPolygon *geoPolygon, int res,
998
0
                                  uint32_t flags, H3Index *out) {
999
0
    H3Error flagErr = validatePolygonFlags(flags);
1000
0
    if (flagErr) {
1001
0
        return flagErr;
1002
0
    }
1003
    // One of the goals of the polygonToCells algorithm is that two adjacent
1004
    // polygons with zero overlap have zero overlapping hexagons. That the
1005
    // hexagons are uniquely assigned. There are a few approaches to take here,
1006
    // such as deciding based on which polygon has the greatest overlapping area
1007
    // of the hexagon, or the most number of contained points on the hexagon
1008
    // (using the center point as a tiebreaker).
1009
    //
1010
    // But if the polygons are convex, both of these more complex algorithms can
1011
    // be reduced down to checking whether or not the center of the hexagon is
1012
    // contained in the polygon, and so this is the approach that this
1013
    // polygonToCells algorithm will follow, as it's simpler, faster, and the
1014
    // error for concave polygons is still minimal (only affecting concave
1015
    // shapes on the order of magnitude of the hexagon size or smaller, not
1016
    // impacting larger concave shapes)
1017
    //
1018
    // This first part is identical to the maxPolygonToCellsSize above.
1019
1020
    // Get the bounding boxes for the polygon and any holes
1021
0
    BBox *bboxes = H3_MEMORY(malloc)((geoPolygon->numHoles + 1) * sizeof(BBox));
1022
0
    if (!bboxes) {
1023
0
        return E_MEMORY_ALLOC;
1024
0
    }
1025
0
    bboxesFromGeoPolygon(geoPolygon, bboxes);
1026
1027
    // Get the estimated number of hexagons and allocate some temporary memory
1028
    // for the hexagons
1029
0
    int64_t numHexagons;
1030
0
    H3Error numHexagonsError =
1031
0
        H3_EXPORT(maxPolygonToCellsSize)(geoPolygon, res, flags, &numHexagons);
1032
0
    if (numHexagonsError) {
1033
0
        H3_MEMORY(free)(bboxes);
1034
0
        return numHexagonsError;
1035
0
    }
1036
0
    H3Index *search = H3_MEMORY(calloc)(numHexagons, sizeof(H3Index));
1037
0
    if (!search) {
1038
0
        H3_MEMORY(free)(bboxes);
1039
0
        return E_MEMORY_ALLOC;
1040
0
    }
1041
0
    H3Index *found = H3_MEMORY(calloc)(numHexagons, sizeof(H3Index));
1042
0
    if (!found) {
1043
0
        H3_MEMORY(free)(bboxes);
1044
0
        H3_MEMORY(free)(search);
1045
0
        return E_MEMORY_ALLOC;
1046
0
    }
1047
1048
    // Some metadata for tracking the state of the search and found memory
1049
    // blocks
1050
0
    int64_t numSearchHexes = 0;
1051
0
    int64_t numFoundHexes = 0;
1052
1053
    // 1. Trace the hexagons along the polygon defining the outer geoloop and
1054
    // add them to the search hash. The hexagon containing the geoloop point
1055
    // may or may not be contained by the geoloop (as the hexagon's center
1056
    // point may be outside of the boundary.)
1057
0
    const GeoLoop geoloop = geoPolygon->geoloop;
1058
0
    H3Error edgeHexError = _getEdgeHexagons(&geoloop, numHexagons, res,
1059
0
                                            &numSearchHexes, search, found);
1060
    // If this branch is reached, we have exceeded the maximum number of
1061
    // hexagons possible and need to clean up the allocated memory.
1062
0
    if (edgeHexError) {
1063
0
        H3_MEMORY(free)(search);
1064
0
        H3_MEMORY(free)(found);
1065
0
        H3_MEMORY(free)(bboxes);
1066
0
        return edgeHexError;
1067
0
    }
1068
1069
    // 2. Iterate over all holes, trace the polygons defining the holes with
1070
    // hexagons and add to only the search hash. We're going to temporarily use
1071
    // the `found` hash to use for dedupe purposes and then re-zero it once
1072
    // we're done here, otherwise we'd have to scan the whole set on each insert
1073
    // to make sure there's no duplicates, which is very inefficient.
1074
0
    for (int i = 0; i < geoPolygon->numHoles; i++) {
1075
0
        GeoLoop *hole = &(geoPolygon->holes[i]);
1076
0
        edgeHexError = _getEdgeHexagons(hole, numHexagons, res, &numSearchHexes,
1077
0
                                        search, found);
1078
        // If this branch is reached, we have exceeded the maximum number of
1079
        // hexagons possible and need to clean up the allocated memory.
1080
0
        if (edgeHexError) {
1081
0
            H3_MEMORY(free)(search);
1082
0
            H3_MEMORY(free)(found);
1083
0
            H3_MEMORY(free)(bboxes);
1084
0
            return edgeHexError;
1085
0
        }
1086
0
    }
1087
1088
    // 3. Re-zero the found hash so it can be used in the main loop below
1089
0
    for (int64_t i = 0; i < numHexagons; i++) found[i] = H3_NULL;
1090
1091
    // 4. Begin main loop. While the search hash is not empty do the following
1092
0
    while (numSearchHexes > 0) {
1093
        // Iterate through all hexagons in the current search hash, then loop
1094
        // through all neighbors and test Point-in-Poly, if point-in-poly
1095
        // succeeds, add to out and found hashes if not already there.
1096
0
        int64_t currentSearchNum = 0;
1097
0
        int64_t i = 0;
1098
0
        while (currentSearchNum < numSearchHexes) {
1099
0
            H3Index ring[MAX_ONE_RING_SIZE] = {0};
1100
0
            H3Index searchHex = search[i];
1101
0
            H3_EXPORT(gridDisk)(searchHex, 1, ring);
1102
0
            for (int j = 0; j < MAX_ONE_RING_SIZE; j++) {
1103
0
                if (ring[j] == H3_NULL) {
1104
0
                    continue;  // Skip if this was a pentagon and only had 5
1105
                               // neighbors
1106
0
                }
1107
1108
0
                H3Index hex = ring[j];
1109
1110
                // A simple hash to store the hexagon, or move to another place
1111
                // if needed. This MUST be done before the point-in-poly check
1112
                // since that's far more expensive
1113
0
                int64_t loc = (int64_t)(hex % numHexagons);
1114
0
                int64_t loopCount = 0;
1115
0
                while (out[loc] != 0) {
1116
                    // If this branch is reached, we have exceeded the maximum
1117
                    // number of hexagons possible and need to clean up the
1118
                    // allocated memory.
1119
                    // TODO: Reachable via fuzzer
1120
0
                    if (loopCount > numHexagons) {
1121
0
                        H3_MEMORY(free)(search);
1122
0
                        H3_MEMORY(free)(found);
1123
0
                        H3_MEMORY(free)(bboxes);
1124
0
                        return E_FAILED;
1125
0
                    }
1126
0
                    if (out[loc] == hex) break;  // Skip duplicates found
1127
0
                    loc = (loc + 1) % numHexagons;
1128
0
                    loopCount++;
1129
0
                }
1130
0
                if (out[loc] == hex) {
1131
0
                    continue;  // Skip this hex, already exists in the out hash
1132
0
                }
1133
1134
                // Check if the hexagon is in the polygon or not
1135
0
                LatLng hexCenter;
1136
0
                H3_EXPORT(cellToLatLng)(hex, &hexCenter);
1137
1138
                // If not, skip
1139
0
                if (!pointInsidePolygon(geoPolygon, bboxes, &hexCenter)) {
1140
0
                    continue;
1141
0
                }
1142
1143
                // Otherwise set it in the output array
1144
0
                out[loc] = hex;
1145
1146
                // Set the hexagon in the found hash
1147
0
                found[numFoundHexes] = hex;
1148
0
                numFoundHexes++;
1149
0
            }
1150
0
            currentSearchNum++;
1151
0
            i++;
1152
0
        }
1153
1154
        // Swap the search and found pointers, copy the found hex count to the
1155
        // search hex count, and zero everything related to the found memory.
1156
0
        H3Index *temp = search;
1157
0
        search = found;
1158
0
        found = temp;
1159
0
        for (int64_t j = 0; j < numSearchHexes; j++) found[j] = 0;
1160
0
        numSearchHexes = numFoundHexes;
1161
0
        numFoundHexes = 0;
1162
        // Repeat until no new hexagons are found
1163
0
    }
1164
    // The out memory structure should be complete, end it here
1165
0
    H3_MEMORY(free)(bboxes);
1166
0
    H3_MEMORY(free)(search);
1167
0
    H3_MEMORY(free)(found);
1168
0
    return E_SUCCESS;
1169
0
}
1170
1171
/**
1172
 * Create a LinkedGeoPolygon describing the outline(s) of a set of  hexagons.
1173
 * Polygon outlines will follow GeoJSON MultiPolygon order: Each polygon will
1174
 * have one outer loop, which is first in the list, followed by any holes.
1175
 *
1176
 * It is the responsibility of the caller to call destroyLinkedMultiPolygon on
1177
 * the populated linked geo structure, or the memory for that structure will not
1178
 * be freed.
1179
 *
1180
 * All cells in the set must be valid, have the same resolution, and contain
1181
 * no duplicates. Returns an error if these conditions are not met.
1182
 *
1183
 * @param h3Set    Set of hexagons
1184
 * @param numHexes Number of hexagons in set
1185
 * @param out      Output polygon
1186
 */
1187
H3Error H3_EXPORT(cellsToLinkedMultiPolygon)(const H3Index *h3Set,
1188
                                             const int numHexes,
1189
0
                                             LinkedGeoPolygon *out) {
1190
0
    GeoMultiPolygon mpoly;
1191
0
    H3Error err = H3_EXPORT(cellsToMultiPolygon)(h3Set, numHexes, &mpoly);
1192
0
    if (err) {
1193
0
        return err;
1194
0
    }
1195
0
    err = geoMultiPolygonToLinkedGeoPolygon(&mpoly, out);
1196
0
    H3_EXPORT(destroyGeoMultiPolygon)(&mpoly);
1197
0
    return err;
1198
0
}
1199
1200
/**
1201
 * Free all allocated memory for a GeoLoop. The caller is
1202
 * responsible for freeing memory allocated to input GeoLoop struct.
1203
 */
1204
0
void destroyGeoLoop(GeoLoop *loop) {
1205
0
    H3_MEMORY(free)(loop->verts);
1206
0
    loop->verts = NULL;
1207
0
    loop->numVerts = 0;
1208
0
}
1209
1210
/**
1211
 * Free all allocated memory for a GeoPolygon. The caller is
1212
 * responsible for freeing memory allocated to input GeoPolygon struct.
1213
 */
1214
0
void destroyGeoPolygon(GeoPolygon *poly) {
1215
0
    destroyGeoLoop(&poly->geoloop);
1216
0
    for (int i = 0; i < poly->numHoles; i++) {
1217
0
        destroyGeoLoop(&poly->holes[i]);
1218
0
    }
1219
0
    H3_MEMORY(free)(poly->holes);
1220
0
    poly->holes = NULL;
1221
0
    poly->numHoles = 0;
1222
0
}
1223
1224
/**
1225
 * Free all allocated memory for a GeoMultiPolygon. The caller is
1226
 * responsible for freeing memory allocated to input GeoMultiPolygon struct.
1227
 */
1228
0
void H3_EXPORT(destroyGeoMultiPolygon)(GeoMultiPolygon *mpoly) {
1229
0
    for (int i = 0; i < mpoly->numPolygons; i++) {
1230
0
        destroyGeoPolygon(&mpoly->polygons[i]);
1231
0
    }
1232
0
    H3_MEMORY(free)(mpoly->polygons);
1233
    mpoly->polygons = NULL;
1234
0
    mpoly->numPolygons = 0;
1235
0
}