Skip to content

Commit 6646fa4

Browse files
authored
fix(CARTO): H3 tile bounding box includes edge children (#9693)
1 parent d57aa05 commit 6646fa4

File tree

2 files changed

+22
-9
lines changed

2 files changed

+22
-9
lines changed

modules/carto/src/layers/h3-tileset-2d.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,19 @@ export type H3TileIndex = {i: string};
1919

2020
const MAX_LATITUDE = 85.051128;
2121

22-
// `polygonToCells()` fills based on hexagon center, this function will
23-
// pad the bounds such that all cells that overlap the bounds will be included
22+
/**
23+
* `polygonToCells()` fills based on hexagon center, this function will
24+
* pad the bounds such that all cells that overlap the bounds will be included.
25+
*
26+
* @param bbox - The bounding box to pad.
27+
* @param resolution - The resolution of the hexagons.
28+
* @param scale - The scale of the buffer. 1 is to pad by the max edge length of the tile cell.
29+
* @returns The padded bounding box.
30+
*/
2431
function padBoundingBox(
2532
{west, north, east, south}: GeoBoundingBox,
26-
resolution: number
33+
resolution: number,
34+
scale: number = 1.0
2735
): GeoBoundingBox {
2836
const corners = [
2937
[north, east],
@@ -35,7 +43,7 @@ function padBoundingBox(
3543
const cornerEdgeLengths = cornerCells.map(
3644
c => (Math.max(...originToDirectedEdges(c).map(e => edgeLength(e, UNITS.rads))) * 180) / Math.PI
3745
);
38-
const bufferLat = Math.max(...cornerEdgeLengths);
46+
const bufferLat = Math.max(...cornerEdgeLengths) * scale;
3947
const bufferLon = Math.min(180, bufferLat / Math.cos((((north + south) / 2) * Math.PI) / 180));
4048

4149
return {
@@ -84,7 +92,12 @@ function tileToBoundingBox(index: string): GeoBoundingBox {
8492
const south = Math.min(...latitudes);
8593
const east = Math.max(...longitudes);
8694
const north = Math.max(...latitudes);
87-
return {west, south, east, north};
95+
const bbox = {west, south, east, north};
96+
97+
// H3 child cells extend beyond their parent's boundary forming a "snowflake"
98+
// fractal pattern. The required buffer is approximately 10% of the
99+
// edge length of the tile cell, add a bit more to be safe.
100+
return padBoundingBox(bbox, getResolution(index), 0.12);
88101
}
89102

90103
// Resolution conversion function. Takes a WebMercatorViewport and returns

test/modules/carto/layers/h3-tileset-2d.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ test('H3Tileset2D', async t => {
3636
t.equal(tileset.getTileId({i: '82754ffffffffff'}), '82754ffffffffff', 'tile id');
3737
const {bbox} = tileset.getTileMetadata({i: '82754ffffffffff'});
3838
const expectedBbox = {
39-
west: -0.8199508788179312,
40-
south: -1.855492618547643,
41-
east: 1.9211969045935835,
42-
north: 0.9361637645983679
39+
west: -1.0122479382442804,
40+
south: -2.0477834895958438,
41+
east: 2.113493964019933,
42+
north: 1.1284546356465657
4343
};
4444
t.ok(
4545
Object.keys(bbox).every(name => equals(bbox[name], expectedBbox[name])),

0 commit comments

Comments
 (0)