I want to query for data based on the H3 hexagons that are visible on the viewport (and for new data on each viewport change). Is there anyway to achieve this with react-map-gl and deck.gl?
To get the hexagons inside the viewport, you need to get the bounding box of the current viewport. If you have the current viewport as {latitude, longitude, zoom, width, height}
(which you probably have in your component state if you're using react-map-gl
), you can get the viewport using viewport-mercator-project
:
import WebMercatorViewport from 'viewport-mercator-project';
function bboxFromViewport(viewport) {
const {width, height} = viewport;
const projection = new WebMercatorViewport(viewport);
const [west, north] = projection.unproject([0, 0]);
const [east, south] = projection.unproject([width, height]);
return {north, south, east, west};
}
Then you can use the bounding box with h3.polyfill
to get the list of contained hexagons at a given resolution:
const nw = [north, west];
const ne = [north, east];
const sw = [south, west];
const se = [south, east];
const hexes = h3.polyfill([nw, ne, se, sw], resolution);
Depending on your use case, you might want to expand the bounding box before calling polyfill
, to get additional data outside the immediate viewport.
You also probably want to bound this on the viewport extent somehow, or you could end up with millions of hexagons on zoom out. One cheap hack I've used for this is to take a very rough estimate of the number of hexagons we'll get, and avoid calling polyfill
if it's too high:
// Inexact, but it doesn't matter for our purposes
const KM_PER_DEGREE_LAT = 111.2;
function estimateHexagonsInBBox(bbox, width, height, res) {
// This is an extremely rough estimate, but we're just trying
// to get a reasonable order of magnitude
const aspect = width / height;
const latKm = (bbox.north - bbox.south) * KM_PER_DEGREE_LAT;
const lonKm = latKm * aspect;
return (latKm * lonKm) / h3.hexArea(res, h3.UNITS.km2);
}