Coverage Report

Created: 2025-11-24 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/h3/src/h3lib/lib/polyfill.c
Line
Count
Source
1
/*
2
 * Copyright 2023 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 polyfill.c
17
 * @brief   Functions relating to the cell-to-polygon algorithm
18
 */
19
20
#include "polyfill.h"
21
22
#include <math.h>
23
#include <string.h>
24
25
#include "alloc.h"
26
#include "baseCells.h"
27
#include "coordijk.h"
28
#include "h3Assert.h"
29
#include "h3Index.h"
30
#include "polygon.h"
31
32
// Factor by which to scale the cell bounding box to include all cells.
33
// This was determined empirically by finding the smallest factor that
34
// passed exhaustive tests.
35
16.8M
#define CELL_SCALE_FACTOR 1.1
36
37
// Factor by which to scale the cell bounding box to include all children.
38
// This was determined empirically by finding the smallest factor that
39
// passed exhaustive tests.
40
16.9M
#define CHILD_SCALE_FACTOR 1.4
41
42
/**
43
 * Max cell edge length, in radians, for each resolution. This was computed
44
 * by taking the max exact edge length for cells at the center of each base
45
 * cell at that resolution.
46
 */
47
static double MAX_EDGE_LENGTH_RADS[MAX_H3_RES + 1] = {
48
    0.21577206265130, 0.08308767068495, 0.03148970436439, 0.01190662871439,
49
    0.00450053330908, 0.00170105523619, 0.00064293917678, 0.00024300820659,
50
    0.00009184847087, 0.00003471545901, 0.00001312121017, 0.00000495935129,
51
    0.00000187445860, 0.00000070847876, 0.00000026777980, 0.00000010121125};
52
53
/** All cells that contain the north pole, by res */
54
static H3Index NORTH_POLE_CELLS[MAX_H3_RES + 1] = {
55
    0x8001fffffffffff, 0x81033ffffffffff, 0x820327fffffffff, 0x830326fffffffff,
56
    0x8403263ffffffff, 0x85032623fffffff, 0x860326237ffffff, 0x870326233ffffff,
57
    0x880326233bfffff, 0x890326233abffff, 0x8a0326233ab7fff, 0x8b0326233ab0fff,
58
    0x8c0326233ab03ff, 0x8d0326233ab03bf, 0x8e0326233ab039f, 0x8f0326233ab0399};
59
60
/** All cells that contain the south pole, by res */
61
static H3Index SOUTH_POLE_CELLS[MAX_H3_RES + 1] = {
62
    0x80f3fffffffffff, 0x81f2bffffffffff, 0x82f297fffffffff, 0x83f293fffffffff,
63
    0x84f2939ffffffff, 0x85f29383fffffff, 0x86f29380fffffff, 0x87f29380effffff,
64
    0x88f29380e1fffff, 0x89f29380e0fffff, 0x8af29380e0d7fff, 0x8bf29380e0d0fff,
65
    0x8cf29380e0d0dff, 0x8df29380e0d0cff, 0x8ef29380e0d0cc7, 0x8ff29380e0d0cc4};
66
67
/** Pre-calculated bounding boxes for all res 0 cells */
68
static BBox RES0_BBOXES[NUM_BASE_CELLS] = {
69
    {1.52480158339146, 1.20305471830087, -0.60664883654036, 0.00568297271999},
70
    {1.52480158339146, 1.17872424267511, -0.60664883654036, 2.54046980298264},
71
    {1.52480158339146, 1.09069387298096, -2.85286053297673, 1.64310689027893},
72
    {1.41845302535151, 1.01285145697208, 0.00568297271999, -1.16770379632602},
73
    {1.27950477868453, 0.97226652536306, 0.55556064983494, -0.18229924845326},
74
    {1.32929586572429, 0.91898920750071, 2.05622344943192, 1.08813154278274},
75
    {1.32899086063916, 0.94271815376360, -2.29875289606378, 3.01700008041993},
76
    {1.26020983864103, 0.84291228415618, -0.89971867664861, -1.75967359310997},
77
    {1.21114673854945, 0.86170600921069, 1.19129757609455, 0.43777608996454},
78
    {1.21075831414294, 0.83795331049498, -1.72022875779891, -2.43793861727138},
79
    {1.15546530929588, 0.78982455384253, 2.53659412229266, 1.85709133451243},
80
    {1.15528445067052, 0.76641428724335, -3.06738507202411, 2.53646110244042},
81
    {1.10121643537669, 0.71330093663066, 0.09640581900154, -0.52154514518248},
82
    {1.07042472765165, 0.67603948819406, -0.47984202840088, -1.10306159603090},
83
    {1.03270228748960, 0.72356358827215, -2.24990138725146, -2.74510220919157},
84
    {1.01929924623886, 0.65491232835426, 0.63035574240731, 0.03537030096470},
85
    {1.01786037568858, 0.58827636737638, 1.53192721817065, 0.93672682511233},
86
    {0.98081434136020, 0.61076063532947, -2.67100636598529, 3.06516463008733},
87
    {0.98106023192774, 0.58679836571570, 2.02829766214461, 1.51334374970280},
88
    {0.96374551790056, 0.55186491737474, -1.42976721313659, -1.96852202530104},
89
    {0.87536136210723, 0.50008952762292, -1.92435613571430, -2.41641343219793},
90
    {0.88611243445554, 0.52742963716774, -0.95781946324194, -1.47628966305930},
91
    {0.86881343251986, 0.50770567021439, 1.03236795495839, 0.50347284027426},
92
    {0.89235638181782, 0.48781264892508, 2.76430302119150, 2.29989716697031},
93
    {0.82570569254601, 0.52173101741059, 2.30921681461428, 1.93198541828980},
94
    {0.80599330438546, 0.40150819579319, -3.06417559403240, 2.70079300784409},
95
    {0.81612079704781, 0.38396800633226, -0.21614378891839, -0.70420149722178},
96
    {0.75822779851431, 0.39943555383751, -2.34059978084699, -2.82127373822444},
97
    {0.78861390967531, 0.38742018303868, 0.23115687731652, -0.22599491086066},
98
    {0.71515840341957, 0.33012478438475, -0.64847976163163, -1.08249728121219},
99
    {0.70359051048414, 0.29148673180722, 1.71441081857246, 1.28443348381696},
100
    {0.69190629544818, 0.28808313184381, 0.64863909244647, 0.16372369282557},
101
    {0.64863235654749, 0.26290420067147, 2.10318098268379, 1.69556122548344},
102
    {0.65722892279906, 0.28222653310929, 1.30918693285466, 0.87594416271685},
103
    {0.64750997738584, 0.24149865709850, -1.30272192474556, -1.68708570163242},
104
    {0.62380174028378, 0.25522080363509, -2.72428423026826, 3.10401473237630},
105
    {0.64228460410023, 0.21206753429148, -1.67639240992071, -2.11772366767341},
106
    {0.59919175361146, 0.21620460836570, 2.48592868387690, 2.07350353893591},
107
    {0.55637406851384, 0.25276557437230, -0.99885388505694, -1.32642489358939},
108
    {0.55648013300665, 0.15187401321019, 2.87032088421324, 2.44642320475367},
109
    {0.54603687970450, 0.15589091511369, -2.06789866067060, -2.49091419631961},
110
    {0.51206347752697, 0.15522020377124, 0.95446767315996, 0.54443262110414},
111
    {0.49767951537101, 0.10944898890579, -0.04335162263358, -0.42900268178569},
112
    {0.46538045483671, 0.06029968637720, -0.41240613713421, -0.80603623808166},
113
    {0.44686891066946, 0.06926857458503, 0.32053284794952, -0.07005748900849},
114
    {0.43208958202064, 0.07796440938140, -3.06232453079660, 2.80602499990282},
115
    {0.43103892586713, 0.02927431919853, -2.41589238618422, -2.85735809951951},
116
    {0.38073727558986, -0.00297016159959, -0.77039553861218, -1.14788248745028},
117
    {0.39113816687141, -0.01518764903038, 1.49130246958290, 1.14714731736311},
118
    {0.33421063142418, 0.02526613430348, 1.15141032578749, 0.85000706261644},
119
    {0.38915669778582, -0.04371359825454, 1.88046353933242, 1.48230231380717},
120
    {0.33787520825987, -0.04835090128296, -1.12274014380603, -1.49454408844749},
121
    {0.33601418932337, -0.06675068178541, 2.23792354204464, 1.85723423013211},
122
    {0.31838318078049, -0.05821955623722, 0.66058854060373, 0.25452572938783},
123
    {0.33630761471457, -0.07589541031521, -1.47957331741818, -1.85981735718264},
124
    {0.28924817322870, -0.09150638064667, -1.83561930288569, -2.21855897384292},
125
    {0.26678632252475, -0.10058088990867, -2.76808651991421, 3.12792953247061},
126
    {0.29285254112587, -0.13483165093783, 2.61406468380434, 2.20466422911705},
127
    {0.20150342788824, -0.10279852729762, 0.06881896344365, -0.23925229432978},
128
    {0.21283813275258, -0.18626835417891, 2.93800440256577, 2.57470747655623},
129
    {0.19587614179884, -0.17237030304155, -2.16941795427335, -2.55405165906601},
130
    {0.17237030304155, -0.19587614179884, 0.97217469931645, 0.58754099452378},
131
    {0.18626835417891, -0.21283813275258, -0.20358825102402, -0.56688517703356},
132
    {0.10279852729762, -0.20150342788824, -3.07277369014614, 2.90234035926002},
133
    {0.13483165093783, -0.29285254112587, -0.52752796978545, -0.93692842447275},
134
    {0.10058088990867, -0.26678632252475, 0.37350613367558, -0.01366312111919},
135
    {0.09150638064667, -0.28924817322870, 1.30597335070410, 0.92303367974687},
136
    {0.07589541031521, -0.33630761471457, 1.66201933617161, 1.28177529640715},
137
    {0.05821955623722, -0.31838318078049, -2.48100411298606, -2.88706692420196},
138
    {0.06675068178541, -0.33601418932337, -0.90366911154516, -1.28435842345769},
139
    {0.04835090128296, -0.33787520825987, 2.01885250978376, 1.64704856514230},
140
    {0.04371359825454, -0.38915669778582, -1.26112911425737, -1.65929033978262},
141
    {-0.02526613430348, -0.33421063142418, -1.99018232780231,
142
     -2.29158559097336},
143
    {0.01518764903038, -0.39113816687140, -1.65029018400690, -1.99444533622668},
144
    {0.00297016159959, -0.38073727558986, 2.37119711497761, 1.99371016613951},
145
    {-0.02927431919853, -0.43103892586713, 0.72570026740558, 0.28423455407029},
146
    {-0.07796440938140, -0.43208958202064, 0.07926812279319, -0.33556765368697},
147
    {-0.06926857458503, -0.44686891066946, -2.82105980564027, 3.07153516458131},
148
    {-0.06029968637720, -0.46538045483671, 2.72918651645558, 2.33555641550814},
149
    {-0.10944898890579, -0.49767951537101, 3.09824103095621, 2.71258997180410},
150
    {-0.15522020377124, -0.51206347752697, -2.18712498042983,
151
     -2.59716003248565},
152
    {-0.15589091511369, -0.54603687970450, 1.07369399291919, 0.65067845727018},
153
    {-0.15187401321019, -0.55648013300665, -0.27127176937655,
154
     -0.69516944883612},
155
    {-0.25276557437230, -0.55637406851385, 2.14273876853285, 1.81516776000041},
156
    {-0.21620460836570, -0.59919175361146, -0.65566396971290,
157
     -1.06808911465388},
158
    {-0.21206753429148, -0.64228460410023, 1.46520024366909, 1.02386898591638},
159
    {-0.25522080363509, -0.62380174028378, 0.41730842332153, -0.03757792121350},
160
    {-0.24149865709850, -0.64750997738584, 1.83887072884423, 1.45450695195737},
161
    {-0.28222653310929, -0.65722892279906, -1.83240572073513,
162
     -2.26564849087294},
163
    {-0.26290420067147, -0.64863235654749, -1.03841167090601,
164
     -1.44603142810635},
165
    {-0.28808313184381, -0.69190629544818, -2.49295356114332,
166
     -2.97786896076422},
167
    {-0.29148673180722, -0.70359051048414, -1.42718183501734,
168
     -1.85715916977284},
169
    {-0.33012478438475, -0.71515840341957, 2.49311289195816, 2.05909537237761},
170
    {-0.38742018303868, -0.78861390967531, -2.91043577627328, 2.91559774272914},
171
    {-0.39943555383751, -0.75822779851431, 0.80099287274280, 0.32031891536535},
172
    {-0.38396800633226, -0.81612079704781, 2.92544886467140, 2.43739115636801},
173
    {-0.40150819579319, -0.80599330438546, 0.07741705955739, -0.44079964574570},
174
    {-0.52173101741059, -0.82570569254601, -0.83237583897551,
175
     -1.20960723529999},
176
    {-0.48781264892508, -0.89235638181782, -0.37728963239830,
177
     -0.84169548661948},
178
    {-0.50770567021439, -0.86881343251986, -2.10922469863141,
179
     -2.63811981331554},
180
    {-0.52742963716774, -0.88611243445554, 2.18377319034785, 1.66530299053050},
181
    {-0.50008952762292, -0.87536136210723, 1.21723651787549, 0.72517922139186},
182
    {-0.55186491737474, -0.96374551790056, 1.71182544045320, 1.17307062828876},
183
    {-0.58679836571570, -0.98106023192774, -1.11329499144518,
184
     -1.62824890388699},
185
    {-0.61076063532947, -0.98081434136020, 0.47058628760450, -0.07642802350246},
186
    {-0.58827636737638, -1.01786037568858, -1.60966543541914,
187
     -2.20486582847747},
188
    {-0.65491232835426, -1.01929924623886, -2.51123691118248,
189
     -3.10622235262510},
190
    {-0.72356358827215, -1.03270228748960, 0.89169126633833, 0.39649044439822},
191
    {-0.67603948819406, -1.07042472765165, 2.66175062518892, 2.03853105755889},
192
    {-0.71330093663066, -1.10121643537669, -3.04518683458825, 2.62004750840731},
193
    {-0.76641428724335, -1.15528445067052, 0.07420758156568, -0.60513155114938},
194
    {-0.78982455384253, -1.15546530929588, -0.60499853129713,
195
     -1.28450131907736},
196
    {-0.83795331049498, -1.21075831414294, 1.42136389579088, 0.70365403631841},
197
    {-0.86170600921069, -1.21114673854945, -1.95029507749525,
198
     -2.70381656362525},
199
    {-0.84291228415618, -1.26020983864103, 2.24187397694118, 1.38191906047983},
200
    {-0.94271815376360, -1.32899086063916, 0.84283975752601, -0.12459257316986},
201
    {-0.91898920750071, -1.32929586572429, -1.08536920415787,
202
     -2.05346111080706},
203
    {-0.97226652536306, -1.27950477868453, -2.58603200375485, 2.95929340513654},
204
    {-1.01285145697208, -1.41845302535151, -3.13590968086981, 1.97388885726377},
205
    {-1.09069387298096, -1.52480158339146, 0.28873212061306, -1.49848576331087},
206
    {-1.17872424267511, -1.52480158339146, 2.53494381704943, -0.60112285060716},
207
    {-1.20305471830087, -1.52480158339146, -0.60112285060716,
208
     2.53494381704943}};
209
210
static BBox VALID_RANGE_BBOX = {M_PI_2, -M_PI_2, M_PI, -M_PI};
211
212
/**
213
 * For a given cell, return its bounding box. If coverChildren is true, the bbox
214
 * will be guaranteed to contain its children at any finer resolution. Note that
215
 * no guarantee is provided as to the level of accuracy, and the bounding box
216
 * may have a significant margin of error.
217
 * @param cell Cell to calculate bbox for
218
 * @param out  BBox to hold output
219
 * @param coverChildren Whether the bounding box should cover all children
220
 */
221
33.7M
H3Error cellToBBox(H3Index cell, BBox *out, bool coverChildren) {
222
    // Adjust the BBox to handle poles, if needed
223
33.7M
    int res = H3_GET_RESOLUTION(cell);
224
225
33.7M
    if (res == 0) {
226
1.00M
        int baseCell = H3_GET_BASE_CELL(cell);
227
1.00M
        if (NEVER(baseCell < 0) || baseCell >= NUM_BASE_CELLS) {
228
0
            return E_CELL_INVALID;
229
0
        }
230
1.00M
        *out = RES0_BBOXES[baseCell];
231
32.7M
    } else {
232
32.7M
        LatLng center;
233
32.7M
        H3Error centerErr = H3_EXPORT(cellToLatLng)(cell, &center);
234
32.7M
        if (centerErr != E_SUCCESS) {
235
0
            return centerErr;
236
0
        }
237
32.7M
        double lngRatio = 1 / cos(center.lat);
238
32.7M
        out->north = center.lat + MAX_EDGE_LENGTH_RADS[res];
239
32.7M
        out->south = center.lat - MAX_EDGE_LENGTH_RADS[res];
240
32.7M
        out->east = center.lng + MAX_EDGE_LENGTH_RADS[res] * lngRatio;
241
32.7M
        out->west = center.lng - MAX_EDGE_LENGTH_RADS[res] * lngRatio;
242
32.7M
    }
243
244
    // Buffer the bounding box to cover children. Call this even if no buffering
245
    // is required in order to normalize the bbox to lat/lng bounds
246
33.7M
    scaleBBox(out, coverChildren ? CHILD_SCALE_FACTOR : CELL_SCALE_FACTOR);
247
248
    // Cell that contains the north pole
249
33.7M
    if (cell == NORTH_POLE_CELLS[res]) {
250
13.1k
        out->north = M_PI_2;
251
13.1k
    }
252
253
    // Cell that contains the south pole
254
33.7M
    if (cell == SOUTH_POLE_CELLS[res]) {
255
13.9k
        out->south = -M_PI_2;
256
13.9k
    }
257
258
    // If we contain a pole, expand the longitude to include the full domain,
259
    // effectively making the bbox a circle around the pole.
260
33.7M
    if (out->north == M_PI_2 || out->south == -M_PI_2) {
261
69.7k
        out->east = M_PI;
262
69.7k
        out->west = -M_PI;
263
69.7k
    }
264
265
33.7M
    return E_SUCCESS;
266
33.7M
}
267
268
/**
269
 * Get a base cell by number, or H3_NULL if out of bounds
270
 */
271
1.01M
H3Index baseCellNumToCell(int baseCellNum) {
272
1.01M
    if (baseCellNum < 0 || baseCellNum >= NUM_BASE_CELLS) {
273
8.11k
        return H3_NULL;
274
8.11k
    }
275
1.00M
    H3Index baseCell;
276
1.00M
    setH3Index(&baseCell, 0, baseCellNum, 0);
277
1.00M
    return baseCell;
278
1.01M
}
279
280
static void iterErrorPolygonCompact(IterCellsPolygonCompact *iter,
281
48
                                    H3Error error) {
282
48
    iterDestroyPolygonCompact(iter);
283
48
    iter->error = error;
284
48
}
285
286
/**
287
 * Given a cell, find the next cell in the sequence of all cells
288
 * to check in the iteration.
289
 */
290
29.3M
static H3Index nextCell(H3Index cell) {
291
29.3M
    int res = H3_GET_RESOLUTION(cell);
292
34.0M
    while (true) {
293
        // If this is a base cell, set to next base cell (or H3_NULL if done)
294
34.0M
        if (res == 0) {
295
1.00M
            return baseCellNumToCell(H3_GET_BASE_CELL(cell) + 1);
296
1.00M
        }
297
298
        // Faster cellToParent when we know the resolution is valid
299
        // and we're only moving up one level
300
33.0M
        H3Index parent = cell;
301
33.0M
        H3_SET_RESOLUTION(parent, res - 1);
302
33.0M
        H3_SET_INDEX_DIGIT(parent, res, H3_DIGIT_MASK);
303
304
        // If not the last sibling of parent, return next sibling
305
33.0M
        Direction digit = H3_GET_INDEX_DIGIT(cell, res);
306
33.0M
        if (digit < INVALID_DIGIT - 1) {
307
28.3M
            H3_SET_INDEX_DIGIT(cell, res,
308
28.3M
                               digit + ((H3_EXPORT(isPentagon)(parent) &&
309
28.3M
                                         digit == CENTER_DIGIT)
310
28.3M
                                            ? 2  // Skip missing pentagon child
311
28.3M
                                            : 1));
312
28.3M
            return cell;
313
28.3M
        }
314
        // Move up to the parent for the next loop iteration
315
4.72M
        res--;
316
4.72M
        cell = parent;
317
4.72M
    }
318
29.3M
}
319
320
/**
321
 * Internal function - initialize the iterator without stepping to the first
322
 * value
323
 */
324
static IterCellsPolygonCompact _iterInitPolygonCompact(
325
8.46k
    const GeoPolygon *polygon, int res, uint32_t flags) {
326
8.46k
    IterCellsPolygonCompact iter = {// Initialize output properties. The first
327
                                    // valid cell will be set in iterStep
328
8.46k
                                    .cell = baseCellNumToCell(0),
329
8.46k
                                    .error = E_SUCCESS,
330
                                    // Save input arguments
331
8.46k
                                    ._polygon = polygon,
332
8.46k
                                    ._res = res,
333
8.46k
                                    ._flags = flags,
334
8.46k
                                    ._bboxes = NULL,
335
8.46k
                                    ._started = false};
336
337
8.46k
    if (res < 0 || res > MAX_H3_RES) {
338
48
        iterErrorPolygonCompact(&iter, E_RES_DOMAIN);
339
48
        return iter;
340
48
    }
341
342
8.42k
    H3Error flagErr = validatePolygonFlags(flags);
343
8.42k
    if (flagErr) {
344
0
        iterErrorPolygonCompact(&iter, flagErr);
345
0
        return iter;
346
0
    }
347
348
    // Initialize bounding boxes for polygon and any holes. Memory allocated
349
    // here must be released through iterDestroyPolygonCompact
350
8.42k
    iter._bboxes = H3_MEMORY(calloc)((polygon->numHoles + 1), sizeof(BBox));
351
8.42k
    if (!iter._bboxes) {
352
0
        iterErrorPolygonCompact(&iter, E_MEMORY_ALLOC);
353
0
        return iter;
354
0
    }
355
8.42k
    bboxesFromGeoPolygon(polygon, iter._bboxes);
356
357
8.42k
    return iter;
358
8.42k
}
359
360
/**
361
 * Initialize a IterCellsPolygonCompact struct representing the sequence of
362
 * compact cells within the target polygon. The test for including edge cells is
363
 * defined by the polyfill mode passed in the `flags` argument.
364
 *
365
 * Initialization of this object may fail, in which case the `error` property
366
 * will be set and all iteration will return H3_NULL. It is the responsibility
367
 * of the caller to check the error property after initialization.
368
 *
369
 * At any point in the iteration, starting once the struct is initialized, the
370
 * output value can be accessed through the `cell` property.
371
 *
372
 * Note that initializing the iterator allocates memory. If an iterator is
373
 * exhausted or returns an error that memory is released; otherwise it must be
374
 * released manually with iterDestroyPolygonCompact.
375
 *
376
 * @param  polygon Polygon to fill with compact cells
377
 * @param  res     Finest resolution for output cells
378
 * @param  flags   Bit mask of option flags
379
 * @return         Initialized iterator, with the first value available
380
 */
381
IterCellsPolygonCompact iterInitPolygonCompact(const GeoPolygon *polygon,
382
4.14k
                                               int res, uint32_t flags) {
383
4.14k
    IterCellsPolygonCompact iter = _iterInitPolygonCompact(polygon, res, flags);
384
385
    // Start the iterator by taking the first step.
386
    // This is necessary to have a valid value after initialization.
387
4.14k
    iterStepPolygonCompact(&iter);
388
389
4.14k
    return iter;
390
4.14k
}
391
392
/**
393
 * Increment the polyfill iterator, running the polygon to cells algorithm.
394
 *
395
 * Briefly, the algorithm checks every cell in the global grid hierarchically,
396
 * starting with the base cells. Cells coarser than the target resolution are
397
 * checked for complete child inclusion using a bounding box guaranteed to
398
 * contain all children.
399
 * - If the bounding box is contained by the polygon, output is set to the cell
400
 * - If the bounding box intersects, recurse into the first child
401
 * - Otherwise, continue with the next cell in sequence
402
 *
403
 * For cells at the target resolution, a finer-grained check is used according
404
 * to the inclusion criteria set in flags.
405
 *
406
 * @param  iter Iterator to increment
407
 */
408
7.13M
void iterStepPolygonCompact(IterCellsPolygonCompact *iter) {
409
7.13M
    H3Index cell = iter->cell;
410
411
    // once the cell is H3_NULL, the iterator returns an infinite sequence of
412
    // H3_NULL
413
7.13M
    if (cell == H3_NULL) return;
414
415
    // For the first step, we need to evaluate the current cell; after that, we
416
    // should start with the next cell.
417
7.13M
    if (iter->_started) {
418
7.12M
        cell = nextCell(cell);
419
7.12M
    } else {
420
8.42k
        iter->_started = true;
421
8.42k
    }
422
423
    // Short-circuit iteration for 0-vert polygon
424
7.13M
    if (iter->_polygon->geoloop.numVerts == 0) {
425
80
        iterDestroyPolygonCompact(iter);
426
80
        return;
427
80
    }
428
429
7.13M
    ContainmentMode mode = FLAG_GET_CONTAINMENT_MODE(iter->_flags);
430
431
34.0M
    while (cell) {
432
34.0M
        int cellRes = H3_GET_RESOLUTION(cell);
433
434
        // Target res: Do a fine-grained check
435
34.0M
        if (cellRes == iter->_res) {
436
24.5M
            if (mode == CONTAINMENT_CENTER || mode == CONTAINMENT_OVERLAPPING ||
437
19.8M
                mode == CONTAINMENT_OVERLAPPING_BBOX) {
438
                // Check if the cell center is inside the polygon
439
19.8M
                LatLng center;
440
19.8M
                H3Error centerErr = H3_EXPORT(cellToLatLng)(cell, &center);
441
19.8M
                if (centerErr != E_SUCCESS) {
442
0
                    iterErrorPolygonCompact(iter, centerErr);
443
0
                    return;
444
0
                }
445
19.8M
                if (pointInsidePolygon(iter->_polygon, iter->_bboxes,
446
19.8M
                                       &center)) {
447
                    // Set to next output
448
3.88M
                    iter->cell = cell;
449
3.88M
                    return;
450
3.88M
                }
451
19.8M
            }
452
20.6M
            if (mode == CONTAINMENT_OVERLAPPING ||
453
16.8M
                mode == CONTAINMENT_OVERLAPPING_BBOX) {
454
                // For overlapping, we need to do a quick check to determine
455
                // whether the polygon is wholly contained by the cell. We
456
                // check the first polygon vertex, which if it is contained
457
                // could also mean we simply intersect.
458
459
                // Deferencing verts[0] is safe because we check numVerts above
460
12.0M
                LatLng firstVertex = iter->_polygon->geoloop.verts[0];
461
462
                // We have to check whether the point is in the expected range
463
                // first, because out-of-bounds values will yield false
464
                // positives with latLngToCell
465
12.0M
                if (bboxContains(&VALID_RANGE_BBOX, &firstVertex)) {
466
10.6M
                    H3Index polygonCell;
467
10.6M
                    H3Error polygonCellErr = H3_EXPORT(latLngToCell)(
468
10.6M
                        &(iter->_polygon->geoloop.verts[0]), cellRes,
469
10.6M
                        &polygonCell);
470
10.6M
                    if (NEVER(polygonCellErr != E_SUCCESS)) {
471
                        // This should be unreachable with the bbox check
472
0
                        iterErrorPolygonCompact(iter, polygonCellErr);
473
0
                        return;
474
0
                    }
475
10.6M
                    if (polygonCell == cell) {
476
                        // Set to next output
477
3.78k
                        iter->cell = cell;
478
3.78k
                        return;
479
3.78k
                    }
480
10.6M
                }
481
12.0M
            }
482
20.6M
            if (mode == CONTAINMENT_FULL || mode == CONTAINMENT_OVERLAPPING ||
483
16.8M
                mode == CONTAINMENT_OVERLAPPING_BBOX) {
484
16.8M
                CellBoundary boundary;
485
16.8M
                H3Error boundaryErr =
486
16.8M
                    H3_EXPORT(cellToBoundary)(cell, &boundary);
487
16.8M
                if (boundaryErr != E_SUCCESS) {
488
0
                    iterErrorPolygonCompact(iter, boundaryErr);
489
0
                    return;
490
0
                }
491
16.8M
                BBox bbox;
492
16.8M
                H3Error bboxErr = cellToBBox(cell, &bbox, false);
493
16.8M
                if (NEVER(bboxErr != E_SUCCESS)) {
494
                    // Should be unreachable - invalid cells would be caught in
495
                    // the previous boundaryErr
496
0
                    iterErrorPolygonCompact(iter, bboxErr);
497
0
                    return;
498
0
                }
499
                // Check if the cell is fully contained by the polygon
500
16.8M
                if ((mode == CONTAINMENT_FULL ||
501
12.0M
                     mode == CONTAINMENT_OVERLAPPING_BBOX) &&
502
12.9M
                    cellBoundaryInsidePolygon(iter->_polygon, iter->_bboxes,
503
12.9M
                                              &boundary, &bbox)) {
504
                    // Set to next output
505
549k
                    iter->cell = cell;
506
549k
                    return;
507
549k
                }
508
                // For overlap, we've already checked for center point inclusion
509
                // above; if that failed, we only need to check for line
510
                // intersection
511
16.2M
                else if ((mode == CONTAINMENT_OVERLAPPING ||
512
12.4M
                          mode == CONTAINMENT_OVERLAPPING_BBOX) &&
513
12.0M
                         cellBoundaryCrossesPolygon(
514
12.0M
                             iter->_polygon, iter->_bboxes, &boundary, &bbox)) {
515
                    // Set to next output
516
1.21M
                    iter->cell = cell;
517
1.21M
                    return;
518
1.21M
                }
519
16.8M
            }
520
18.8M
            if (mode == CONTAINMENT_OVERLAPPING_BBOX) {
521
                // Get a bounding box containing all the cell's children, so
522
                // this can work for the max size calculation
523
7.39M
                BBox bbox;
524
7.39M
                H3Error bboxErr = cellToBBox(cell, &bbox, true);
525
7.39M
                if (bboxErr) {
526
0
                    iterErrorPolygonCompact(iter, bboxErr);
527
0
                    return;
528
0
                }
529
7.39M
                if (bboxOverlapsBBox(&iter->_bboxes[0], &bbox)) {
530
4.86M
                    CellBoundary bboxBoundary = bboxToCellBoundary(&bbox);
531
4.86M
                    if (
532
                        // cell bbox contains the polygon
533
4.86M
                        bboxContainsBBox(&bbox, &iter->_bboxes[0]) ||
534
                        // polygon contains cell bbox
535
4.86M
                        pointInsidePolygon(iter->_polygon, iter->_bboxes,
536
4.86M
                                           &bboxBoundary.verts[0]) ||
537
                        // polygon crosses cell bbox
538
4.80M
                        cellBoundaryCrossesPolygon(iter->_polygon,
539
4.80M
                                                   iter->_bboxes, &bboxBoundary,
540
4.80M
                                                   &bbox)) {
541
350k
                        iter->cell = cell;
542
350k
                        return;
543
350k
                    }
544
4.86M
                }
545
7.39M
            }
546
18.8M
        }
547
548
        // Coarser cell: Check the bounding box
549
28.0M
        if (cellRes < iter->_res) {
550
            // Get a bounding box for all of the cell's children
551
9.51M
            BBox bbox;
552
9.51M
            H3Error bboxErr = cellToBBox(cell, &bbox, true);
553
9.51M
            if (bboxErr) {
554
0
                iterErrorPolygonCompact(iter, bboxErr);
555
0
                return;
556
0
            }
557
9.51M
            if (bboxOverlapsBBox(&iter->_bboxes[0], &bbox)) {
558
                // Quick check for possible containment
559
5.85M
                if (bboxContainsBBox(&iter->_bboxes[0], &bbox)) {
560
3.49M
                    CellBoundary bboxBoundary = bboxToCellBoundary(&bbox);
561
                    // Do a fine-grained, more expensive check on the polygon
562
3.49M
                    if (cellBoundaryInsidePolygon(iter->_polygon, iter->_bboxes,
563
3.49M
                                                  &bboxBoundary, &bbox)) {
564
                        // Bounding box is fully contained, so all children are
565
                        // included. Set to next output.
566
1.12M
                        iter->cell = cell;
567
1.12M
                        return;
568
1.12M
                    }
569
3.49M
                }
570
                // Otherwise, the intersecting bbox means we need to test all
571
                // children, starting with the first child
572
4.72M
                H3Index child;
573
4.72M
                H3Error childErr =
574
4.72M
                    H3_EXPORT(cellToCenterChild)(cell, cellRes + 1, &child);
575
4.72M
                if (childErr) {
576
0
                    iterErrorPolygonCompact(iter, childErr);
577
0
                    return;
578
0
                }
579
                // Restart the loop with the child cell
580
4.72M
                cell = child;
581
4.72M
                continue;
582
4.72M
            }
583
9.51M
        }
584
585
        // Find the next cell in the sequence of all cells and continue
586
22.2M
        cell = nextCell(cell);
587
22.2M
    }
588
    // If we make it out of the loop, we're done
589
8.11k
    iterDestroyPolygonCompact(iter);
590
8.11k
}
591
592
/**
593
 * Destroy an iterator, releasing any allocated memory. Iterators destroyed in
594
 * this manner are safe to use but will always return H3_NULL.
595
 * @param  iter Iterator to destroy
596
 */
597
8.46k
void iterDestroyPolygonCompact(IterCellsPolygonCompact *iter) {
598
8.46k
    if (iter->_bboxes) {
599
8.42k
        H3_MEMORY(free)(iter->_bboxes);
600
8.42k
    }
601
8.46k
    iter->cell = H3_NULL;
602
8.46k
    iter->error = E_SUCCESS;
603
8.46k
    iter->_polygon = NULL;
604
8.46k
    iter->_res = -1;
605
8.46k
    iter->_flags = 0;
606
8.46k
    iter->_bboxes = NULL;
607
8.46k
}
608
609
/**
610
 * Initialize a IterCellsPolygon struct representing the sequence of
611
 * cells within the target polygon. The test for including edge cells is defined
612
 * by the polyfill mode passed in the `flags` argument.
613
 *
614
 * Initialization of this object may fail, in which case the `error` property
615
 * will be set and all iteration will return H3_NULL. It is the responsibility
616
 * of the caller to check the error property after initialization.
617
 *
618
 * At any point in the iteration, starting once the struct is initialized, the
619
 * output value can be accessed through the `cell` property.
620
 *
621
 * Note that initializing the iterator allocates memory. If an iterator is
622
 * exhausted or returns an error that memory is released; otherwise it must be
623
 * released manually with iterDestroyPolygon.
624
 *
625
 * @param  polygon Polygon to fill with cells
626
 * @param  res     Resolution for output cells
627
 * @param  flags   Bit mask of option flags
628
 * @return         Initialized iterator, with the first value available
629
 */
630
IterCellsPolygon iterInitPolygon(const GeoPolygon *polygon, int res,
631
4.14k
                                 uint32_t flags) {
632
    // Create the sub-iterator for compact cells
633
4.14k
    IterCellsPolygonCompact cellIter =
634
4.14k
        iterInitPolygonCompact(polygon, res, flags);
635
    // Create the sub-iterator for children
636
4.14k
    IterCellsChildren childIter = iterInitParent(cellIter.cell, res);
637
638
4.14k
    IterCellsPolygon iter = {.cell = childIter.h,
639
4.14k
                             .error = cellIter.error,
640
4.14k
                             ._cellIter = cellIter,
641
4.14k
                             ._childIter = childIter};
642
4.14k
    return iter;
643
4.14k
}
644
645
/**
646
 * Increment the polyfill iterator, outputting the latest cell at the
647
 * desired resolution.
648
 *
649
 * @param  iter Iterator to increment
650
 */
651
71.6M
void iterStepPolygon(IterCellsPolygon *iter) {
652
71.6M
    if (iter->cell == H3_NULL) return;
653
654
    // See if there are more children to output
655
71.6M
    iterStepChild(&(iter->_childIter));
656
71.6M
    if (iter->_childIter.h) {
657
66.8M
        iter->cell = iter->_childIter.h;
658
66.8M
        return;
659
66.8M
    }
660
661
    // Otherwise, increment the polyfill iterator
662
4.72M
    iterStepPolygonCompact(&(iter->_cellIter));
663
4.72M
    if (iter->_cellIter.cell) {
664
4.71M
        _iterInitParent(iter->_cellIter.cell, iter->_cellIter._res,
665
4.71M
                        &(iter->_childIter));
666
4.71M
        iter->cell = iter->_childIter.h;
667
4.71M
        return;
668
4.71M
    }
669
670
    // All done, set to null and report errors if any
671
3.18k
    iter->cell = H3_NULL;
672
3.18k
    iter->error = iter->_cellIter.error;
673
3.18k
}
674
675
/**
676
 * Destroy an iterator, releasing any allocated memory. Iterators destroyed in
677
 * this manner are safe to use but will always return H3_NULL.
678
 * @param  iter Iterator to destroy
679
 */
680
228
void iterDestroyPolygon(IterCellsPolygon *iter) {
681
228
    iterDestroyPolygonCompact(&(iter->_cellIter));
682
    // null out the child iterator by passing H3_NULL
683
228
    _iterInitParent(H3_NULL, 0, &(iter->_childIter));
684
228
    iter->cell = H3_NULL;
685
228
    iter->error = E_SUCCESS;
686
228
}
687
688
/**
689
 * polygonToCells takes a given GeoJSON-like data structure and preallocated,
690
 * zeroed memory, and fills it with the hexagons that are contained by
691
 * the GeoJSON-like data structure. Polygons are considered in Cartesian space.
692
 *
693
 * @param polygon The geoloop and holes defining the relevant area
694
 * @param res The Hexagon resolution (0-15)
695
 * @param flags Algorithm flags such as containment mode
696
 * @param size Maximum number of indexes to write to `out`.
697
 * @param out The slab of zeroed memory to write to. Must be at least of size
698
 * `size`.
699
 */
700
H3Error H3_EXPORT(polygonToCellsExperimental)(const GeoPolygon *polygon,
701
                                              int res, uint32_t flags,
702
4.14k
                                              int64_t size, H3Index *out) {
703
4.14k
    IterCellsPolygon iter = iterInitPolygon(polygon, res, flags);
704
4.14k
    int64_t i = 0;
705
71.6M
    for (; iter.cell; iterStepPolygon(&iter)) {
706
71.6M
        if (i >= size) {
707
228
            iterDestroyPolygon(&iter);
708
228
            return E_MEMORY_BOUNDS;
709
228
        }
710
71.6M
        out[i++] = iter.cell;
711
71.6M
    }
712
3.92k
    return iter.error;
713
4.14k
}
714
715
static int MAX_SIZE_CELL_THRESHOLD = 10;
716
717
9.73k
static double getAverageCellArea(int res) {
718
9.73k
    double hexAreaKm2;
719
9.73k
    H3_EXPORT(getHexagonAreaAvgKm2)(res, &hexAreaKm2);
720
9.73k
    return hexAreaKm2;
721
9.73k
}
722
723
/**
724
 * maxPolygonToCellsSize returns the number of cells to allocate space for
725
 * when performing a polygonToCells on the given GeoJSON-like data structure.
726
 * @param polygon A GeoJSON-like data structure indicating the poly to fill
727
 * @param res Hexagon resolution (0-15)
728
 * @param flags Bit mask of option flags
729
 * @param out number of cells to allocate for
730
 * @return 0 (E_SUCCESS) on success.
731
 */
732
H3Error H3_EXPORT(maxPolygonToCellsSizeExperimental)(const GeoPolygon *polygon,
733
                                                     int res, uint32_t flags,
734
4.43k
                                                     int64_t *out) {
735
    // Special case: 0-vertex polygon
736
4.43k
    if (polygon->geoloop.numVerts == 0) {
737
112
        *out = 0;
738
112
        return E_SUCCESS;
739
112
    }
740
741
    // Initialize the iterator without stepping, so we can adjust the res and
742
    // flags (after they are validated by the initialization) before we start
743
4.32k
    IterCellsPolygonCompact iter = _iterInitPolygonCompact(polygon, res, flags);
744
745
4.32k
    if (iter.error) {
746
16
        return iter.error;
747
16
    }
748
749
    // Ignore the requested flags and use the faster overlapping-bbox mode
750
4.30k
    iter._flags = CONTAINMENT_OVERLAPPING_BBOX;
751
752
    // Get a (very) rough area of the polygon bounding box
753
4.30k
    BBox *polygonBBox = &iter._bboxes[0];
754
4.30k
    double polygonBBoxAreaKm2 =
755
4.30k
        bboxHeightRads(polygonBBox) * bboxWidthRads(polygonBBox) /
756
4.30k
        cos(fmin(fabs(polygonBBox->north), fabs(polygonBBox->south))) *
757
4.30k
        EARTH_RADIUS_KM * EARTH_RADIUS_KM;
758
759
    // Determine the res for the size estimate, based on a (very) rough estimate
760
    // of the number of cells at various resolutions that would fit in the
761
    // polygon. All we need here is a general order of magnitude.
762
12.4k
    while (iter._res > 0 &&
763
9.73k
           polygonBBoxAreaKm2 / getAverageCellArea(iter._res - 1) >
764
9.73k
               MAX_SIZE_CELL_THRESHOLD) {
765
8.09k
        iter._res--;
766
8.09k
    }
767
768
    // Now run the polyfill, counting the output in the target res.
769
    // We have to take the first step outside the loop, to get the first
770
    // valid output cell
771
4.30k
    iterStepPolygonCompact(&iter);
772
773
4.30k
    *out = 0;
774
4.30k
    int64_t childrenSize;
775
2.41M
    for (; iter.cell; iterStepPolygonCompact(&iter)) {
776
2.40M
        H3_EXPORT(cellToChildrenSize)(iter.cell, res, &childrenSize);
777
2.40M
        *out += childrenSize;
778
2.40M
    }
779
780
4.30k
    return iter.error;
781
4.32k
}