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