h3_polyfill.c (496 lines of code) (raw):
/*
* 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 polygon A GeoJSON-like data structure indicating the poly to fill
* @param res Hexagon resolution (0-15)
* @param flags Bit mask of option flags
* @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;
}