Skip to content

Commit 5d7c1c1

Browse files
authored
Alternate polygonToCells algorithm (#785)
This PR implements an alternate algorithm for `polygonToCells`, which provides `polygonToCompactCells` as a byproduct. ### Algorithm Overview ``` let cell = base cell 0 while cell: res = getResolution(cell) if res == target res: if cell is in polygon: set output to cell and return if res < target res: let bbox = boundingBox(cell) // where bbox is padded to contain all descendants if bbox intersects polygon: if bbox is contained by polygon: set output to cell and return cell = first child of cell continue cell = next cell // where "next" is the next sibling, or next sibling of parent, etc when cell is null we're done ``` ### Results - This PR includes an iterator-based algo with full test parity to the current algorithm. In its iterator form, it takes very little memory and that memory does not scale with the size of the output - so we can stream cells out of a very large polyfill process without worrying about memory issues. - The new algo gives compact (or nearly compact) output by default, so we get `polygonToCellsCompact` effectively for free. - The performance isn't exactly better or worse than the current algorithm - it just has different characteristics. In benchmarks, it's faster than the current algorithm in roughly 80% of cases
1 parent 5aeff3d commit 5d7c1c1

25 files changed

+2555
-58
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ The public API of this library consists of the functions declared in file
77

88
## [Unreleased]
99
### Changed
10+
- Replace internal algorithm for `polygonToCells` with a new version that is more memory-efficient (#785)
1011
- Reorganize tests into public / internal. (#762)
1112
- Performance enhancement for aarch64, should not affect other platforms (#790, #792)
1213

CMakeLists.txt

+8
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ set(LIB_SOURCE_FILES
125125
src/h3lib/include/bbox.h
126126
src/h3lib/include/polygon.h
127127
src/h3lib/include/polygonAlgos.h
128+
src/h3lib/include/polyfill.h
128129
src/h3lib/include/h3Index.h
129130
src/h3lib/include/directedEdge.h
130131
src/h3lib/include/latLng.h
@@ -146,6 +147,7 @@ set(LIB_SOURCE_FILES
146147
src/h3lib/lib/coordijk.c
147148
src/h3lib/lib/bbox.c
148149
src/h3lib/lib/polygon.c
150+
src/h3lib/lib/polyfill.c
149151
src/h3lib/lib/h3Index.c
150152
src/h3lib/lib/vec2d.c
151153
src/h3lib/lib/vec3d.c
@@ -192,7 +194,9 @@ set(OTHER_SOURCE_FILES
192194
src/apps/testapps/testVertexGraphInternal.c
193195
src/apps/testapps/testCompactCells.c
194196
src/apps/testapps/testPolygonToCells.c
197+
src/apps/testapps/testPolygonToCellsExperimental.c
195198
src/apps/testapps/testPolygonToCellsReported.c
199+
src/apps/testapps/testPolygonToCellsReportedExperimental.c
196200
src/apps/testapps/testPentagonIndexes.c
197201
src/apps/testapps/testGridDisk.c
198202
src/apps/testapps/testGridDiskInternal.c
@@ -209,6 +213,7 @@ set(OTHER_SOURCE_FILES
209213
src/apps/testapps/testCellToLatLng.c
210214
src/apps/testapps/testCellToCenterChild.c
211215
src/apps/testapps/testCellToChildren.c
216+
src/apps/testapps/testCellToBBoxExhaustive.c
212217
src/apps/testapps/testCellToChildPos.c
213218
src/apps/testapps/testGetIcosahedronFaces.c
214219
src/apps/testapps/testLatLng.c
@@ -220,6 +225,7 @@ set(OTHER_SOURCE_FILES
220225
src/apps/testapps/testVertexInternal.c
221226
src/apps/testapps/testVertexExhaustive.c
222227
src/apps/testapps/testPolygonInternal.c
228+
src/apps/testapps/testPolyfillInternal.c
223229
src/apps/testapps/testVec2dInternal.c
224230
src/apps/testapps/testVec3dInternal.c
225231
src/apps/testapps/testDirectedEdge.c
@@ -270,6 +276,7 @@ set(OTHER_SOURCE_FILES
270276
src/apps/fuzzers/fuzzerInternalAlgos.c
271277
src/apps/fuzzers/fuzzerInternalCoordIjk.c
272278
src/apps/benchmarks/benchmarkPolygonToCells.c
279+
src/apps/benchmarks/benchmarkPolygonToCellsExperimental.c
273280
src/apps/benchmarks/benchmarkPolygon.c
274281
src/apps/benchmarks/benchmarkCellsToLinkedMultiPolygon.c
275282
src/apps/benchmarks/benchmarkCellToChildren.c
@@ -566,6 +573,7 @@ if(BUILD_BENCHMARKS)
566573
add_h3_benchmark(benchmarkCellsToLinkedMultiPolygon src/apps/benchmarks/benchmarkCellsToLinkedMultiPolygon.c)
567574
add_h3_benchmark(benchmarkCellToChildren src/apps/benchmarks/benchmarkCellToChildren.c)
568575
add_h3_benchmark(benchmarkPolygonToCells src/apps/benchmarks/benchmarkPolygonToCells.c)
576+
add_h3_benchmark(benchmarkPolygonToCellsExperimental src/apps/benchmarks/benchmarkPolygonToCellsExperimental.c)
569577
if(ENABLE_REQUIRES_ALL_SYMBOLS)
570578
add_h3_benchmark(benchmarkPolygon src/apps/benchmarks/benchmarkPolygon.c)
571579
endif()

CMakeTests.cmake

+4
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,9 @@ add_h3_test(testCellsToLinkedMultiPolygon src/apps/testapps/testCellsToLinkedMul
186186
add_h3_test(testH3SetToVertexGraphInternal src/apps/testapps/testH3SetToVertexGraphInternal.c)
187187
add_h3_test(testLinkedGeoInternal src/apps/testapps/testLinkedGeoInternal.c)
188188
add_h3_test(testPolygonToCells src/apps/testapps/testPolygonToCells.c)
189+
add_h3_test(testPolygonToCellsExperimental src/apps/testapps/testPolygonToCellsExperimental.c)
189190
add_h3_test(testPolygonToCellsReported src/apps/testapps/testPolygonToCellsReported.c)
191+
add_h3_test(testPolygonToCellsReportedExperimental src/apps/testapps/testPolygonToCellsReportedExperimental.c)
190192
add_h3_test(testVertexGraphInternal src/apps/testapps/testVertexGraphInternal.c)
191193
add_h3_test(testDirectedEdge src/apps/testapps/testDirectedEdge.c)
192194
add_h3_test(testLatLng src/apps/testapps/testLatLng.c)
@@ -195,6 +197,7 @@ add_h3_test(testBBoxInternal src/apps/testapps/testBBoxInternal.c)
195197
add_h3_test(testVertex src/apps/testapps/testVertex.c)
196198
add_h3_test(testVertexInternal src/apps/testapps/testVertexInternal.c)
197199
add_h3_test(testPolygonInternal src/apps/testapps/testPolygonInternal.c)
200+
add_h3_test(testPolyfillInternal src/apps/testapps/testPolyfillInternal.c)
198201
add_h3_test(testVec2dInternal src/apps/testapps/testVec2dInternal.c)
199202
add_h3_test(testVec3dInternal src/apps/testapps/testVec3dInternal.c)
200203
add_h3_test(testCellToLocalIj src/apps/testapps/testCellToLocalIj.c)
@@ -223,6 +226,7 @@ add_h3_test(testCellToLocalIjExhaustive src/apps/testapps/testCellToLocalIjExhau
223226
add_h3_test(testGridPathCellsExhaustive src/apps/testapps/testGridPathCellsExhaustive.c)
224227
add_h3_test(testGridDistanceExhaustive src/apps/testapps/testGridDistanceExhaustive.c)
225228
add_h3_test(testH3CellAreaExhaustive src/apps/testapps/testH3CellAreaExhaustive.c)
229+
add_h3_test(testCellToBBoxExhaustive src/apps/testapps/testCellToBBoxExhaustive.c)
226230

227231
add_h3_cli_test(testCliCellToLatLng "cellToLatLng -c 8928342e20fffff" "37.5012466151, -122.5003039349")
228232
add_h3_cli_test(testCliLatLngToCell "latLngToCell --lat 20 --lng 123 -r 2" "824b9ffffffffff")

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,13 @@ You can run a faster test suite that excludes the most expensive tests with `mak
112112

113113
#### Coverage
114114

115-
You can generate a code coverage report if `lcov` is installed, and if the project was built with the `CMAKE_BUILD_TYPE=Debug` option.
115+
You can generate a code coverage report if `lcov` is installed, and if the project was built with the `CMAKE_BUILD_TYPE=Debug` and `ENABLE_COVERAGE=ON` options.
116116
For example, from a clean repository, you could run:
117117

118118
```
119119
mkdir build
120120
cd build
121-
cmake -DCMAKE_BUILD_TYPE=Debug ..
121+
cmake -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON ..
122122
make
123123
make coverage
124124
```

src/apps/applib/include/utility.h

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <stdio.h>
2424

25+
#include "bbox.h"
2526
#include "coordijk.h"
2627
#include "h3api.h"
2728

@@ -46,6 +47,8 @@ void geoPrintNoFmt(const LatLng *p);
4647
void geoPrintlnNoFmt(const LatLng *p);
4748
void cellBoundaryPrint(const CellBoundary *b);
4849
void cellBoundaryPrintln(const CellBoundary *b);
50+
void bboxPrint(const BBox *bbox);
51+
void bboxPrintln(const BBox *bbox);
4952

5053
void randomGeo(LatLng *p);
5154

src/apps/applib/lib/utility.c

+12
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,18 @@ void cellBoundaryPrintln(const CellBoundary *b) {
118118
printf("}\n");
119119
}
120120

121+
void bboxPrint(const BBox *bbox) {
122+
printf(
123+
"bbox {%.9lf, %.9lf, %.9lf, %.9lf}", H3_EXPORT(radsToDegs)(bbox->north),
124+
H3_EXPORT(radsToDegs)(bbox->south), H3_EXPORT(radsToDegs)(bbox->east),
125+
H3_EXPORT(radsToDegs)(bbox->west));
126+
}
127+
128+
void bboxPrintln(const BBox *bbox) {
129+
bboxPrint(bbox);
130+
printf("\n");
131+
}
132+
121133
/**
122134
* Apply callback for every unidirectional edge at the given resolution.
123135
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright 2017, 2020-2021 Uber Technologies, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#include "algos.h"
17+
#include "benchmark.h"
18+
#include "h3api.h"
19+
#include "polyfill.h"
20+
21+
// Fixtures
22+
LatLng sfVerts[] = {
23+
{0.659966917655, -2.1364398519396}, {0.6595011102219, -2.1359434279405},
24+
{0.6583348114025, -2.1354884206045}, {0.6581220034068, -2.1382437718946},
25+
{0.6594479998527, -2.1384597563896}, {0.6599990002976, -2.1376771158464}};
26+
GeoLoop sfGeoLoop;
27+
GeoPolygon sfGeoPolygon;
28+
29+
LatLng alamedaVerts[] = {{0.6597959342671712, -2.133241848488897},
30+
{0.6597959348850178, -2.133241848495878},
31+
{0.6598352639563587, -2.1331688423977755},
32+
{0.6601346536539207, -2.13270417124178},
33+
{0.6601594763880223, -2.1326680320633344},
34+
{0.6601512007732382, -2.1326594176574534},
35+
{0.6598535076212304, -2.1323049630593562},
36+
{0.6596565748646488, -2.132069889917591},
37+
{0.6594645035394391, -2.131843148468039},
38+
{0.6593438094209757, -2.1316994860539844},
39+
{0.6591174422311021, -2.131429776816562},
40+
{0.658849344286881, -2.1311111485483867},
41+
{0.6588348862079956, -2.1310988536794455},
42+
{0.6586273138317915, -2.131668420800747},
43+
{0.6583729538174264, -2.132370426573979},
44+
{0.6582479206289285, -2.132718691911663},
45+
{0.6582322393220743, -2.1327614200082317},
46+
{0.6583003647098981, -2.132837478687196},
47+
{0.6584457274847966, -2.132827956758973},
48+
{0.6585526679060995, -2.1330231566043203},
49+
{0.6587379099516777, -2.1331602726234538},
50+
{0.6587273684736642, -2.1332676321559063},
51+
{0.6584638025857692, -2.133305719954319},
52+
{0.6583545950288919, -2.1334323622944993},
53+
{0.6584427148370682, -2.1335885223323947},
54+
{0.6584715236640714, -2.133649780409862},
55+
{0.6584715242505019, -2.133649780481421},
56+
{0.658474662092443, -2.1336459234695804},
57+
{0.6591666596433436, -2.1348354004882926},
58+
{0.6591809355063646, -2.1348424115474565},
59+
{0.6593477498700266, -2.1351460576998926},
60+
{0.6597155087395117, -2.1351049454274},
61+
{0.6597337410387994, -2.135113899444683},
62+
{0.6598277083823935, -2.1351065432309517},
63+
{0.659837290351688, -2.1350919904836627},
64+
{0.6598391300107502, -2.1350911731005957},
65+
{0.6598335712627461, -2.1350732321630828},
66+
{0.6597162034032434, -2.134664026354221},
67+
{0.6596785831942451, -2.134651647657116},
68+
{0.6596627824684727, -2.13458880305965},
69+
{0.6596785832500957, -2.134530719130462},
70+
{0.6596093592822273, -2.13428052987356},
71+
{0.6596116166352313, -2.134221493755564},
72+
{0.6595973199434513, -2.134146270344056},
73+
{0.6595536764042369, -2.1340805688066653},
74+
{0.6594611172376618, -2.133753252031165},
75+
{0.6594829406269346, -2.1337342082305697},
76+
{0.6594897134102581, -2.1337104032834757},
77+
{0.6597920983773051, -2.1332343063312775},
78+
{0.6597959342671712, -2.133241848488897}};
79+
GeoLoop alamedaGeoLoop;
80+
GeoPolygon alamedaGeoPolygon;
81+
82+
LatLng southernVerts[] = {{0.6367481147484843, -2.1290865397798906},
83+
{0.6367481152301953, -2.129086539469222},
84+
{0.6367550754426818, -2.128887436716856},
85+
{0.6367816002113981, -2.1273204058681094},
86+
{0.6380814125349741, -2.127201274803692},
87+
{0.6388614350074809, -2.12552061082428},
88+
{0.6393520289210095, -2.124274316938293},
89+
{0.639524834205869, -2.122168447308359},
90+
{0.6405714857447717, -2.122083222593005},
91+
{0.640769478635285, -2.120979885974894},
92+
{0.6418936996869471, -2.1147667448862255},
93+
{0.6419094141707652, -2.1146521242709584},
94+
{0.6269997808948107, -2.1038647304637257},
95+
{0.6252080524974937, -2.1195521728170457},
96+
{0.626379700264057, -2.1203708632511162},
97+
{0.6282200029232767, -2.1210412050690723},
98+
{0.6283657301211779, -2.1219496416754393},
99+
{0.6305651783819565, -2.123628532238016},
100+
{0.6308259852882764, -2.124225549648211},
101+
{0.6317049665784865, -2.124887756638367},
102+
{0.6323403882676475, -2.1266205835454053},
103+
{0.6334397909415498, -2.1277211741619553},
104+
{0.6367481147484843, -2.1290865397798906}};
105+
GeoLoop southernGeoLoop;
106+
GeoPolygon southernGeoPolygon;
107+
108+
BEGIN_BENCHMARKS();
109+
110+
sfGeoLoop.numVerts = 6;
111+
sfGeoLoop.verts = sfVerts;
112+
sfGeoPolygon.geoloop = sfGeoLoop;
113+
114+
alamedaGeoLoop.numVerts = 50;
115+
alamedaGeoLoop.verts = alamedaVerts;
116+
alamedaGeoPolygon.geoloop = alamedaGeoLoop;
117+
118+
southernGeoLoop.numVerts = 23;
119+
southernGeoLoop.verts = southernVerts;
120+
southernGeoPolygon.geoloop = southernGeoLoop;
121+
122+
int64_t numHexagons;
123+
H3Index *hexagons;
124+
125+
BENCHMARK(polygonToCellsSF, 500, {
126+
H3_EXPORT(maxPolygonToCellsSize)(&sfGeoPolygon, 9, 0, &numHexagons);
127+
hexagons = calloc(numHexagons, sizeof(H3Index));
128+
H3_EXPORT(polygonToCellsExperimental)(&sfGeoPolygon, 9, 0, hexagons);
129+
free(hexagons);
130+
});
131+
132+
BENCHMARK(polygonToCellsAlameda, 500, {
133+
H3_EXPORT(maxPolygonToCellsSize)(&alamedaGeoPolygon, 9, 0, &numHexagons);
134+
hexagons = calloc(numHexagons, sizeof(H3Index));
135+
H3_EXPORT(polygonToCellsExperimental)(&alamedaGeoPolygon, 9, 0, hexagons);
136+
free(hexagons);
137+
});
138+
139+
BENCHMARK(polygonToCellsSouthernExpansion, 10, {
140+
H3_EXPORT(maxPolygonToCellsSize)(&southernGeoPolygon, 9, 0, &numHexagons);
141+
hexagons = calloc(numHexagons, sizeof(H3Index));
142+
H3_EXPORT(polygonToCellsExperimental)(&southernGeoPolygon, 9, 0, hexagons);
143+
free(hexagons);
144+
});
145+
146+
END_BENCHMARKS();

0 commit comments

Comments
 (0)