H3 levels (oversampling)
by felixp
HTML
<!DOCTYPE html>
<html>
<head>
<title>H3 levels</title>
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<script src="https://unpkg.com/[email protected]"></script>
<script src="https://unpkg.com/[email protected]/dist.min.js"></script>
<script src="https://unpkg.com/@deck.gl/[email protected]/dist.min.js"></script>
<script src="https://libs.cartocdn.com/mapbox-gl/v1.13.0/mapbox-gl.js"></script>
<link href="https://libs.cartocdn.com/mapbox-gl/v1.13.0/mapbox-gl.css" rel="stylesheet" />
<!-- jsFiddle will insert css and js -->
</head>
<body>
<div id="map"></div>
<div id="textfield">Hexagons</div>
</body>
</html>
CSS
/* Always set the map height explicitly to define the size of the div element that contains the map. */
#map {
height: 100%;
}
#textfield {
position: absolute;
left: 10px;
top: 10px;
background: snow;
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3);
padding: 10px;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
JavaScript
// Relative scale factor (0 = no biasing, 2 = a few hexagons cover view)
const bias = 1;
// Resolution conversion function. Takes a WebMercatorViewport and returns
// a H3 resolution such that the screen space size of the hexagons is
// similar
function getHexagonResolution(viewport) {
const hexagonScaleFactor = (2 / 3) * viewport.zoom;
const latitudeScaleFactor = Math.log(1 / Math.cos(Math.PI * viewport.latitude / 180));
// Clip and bias
const r = hexagonScaleFactor + latitudeScaleFactor - bias;
return Math.max(0, Math.min(15, r));
}
// Style
const GREEN = [128, 189, 164];
const PURPLE = [45, 41, 95];
// Visualization
function createHexagonLayers(viewState) {
const resolution = getHexagonResolution(viewState);
const r0 = Math.floor(resolution);
const r1 = r0 + 1;
const rRef = Math.round(resolution);
const hexagons = getHexagonsInView(viewState, rRef);
// Stats display
const totalHex = 122 * Math.pow(7, rRef); // estimate
const el = document.getElementById('textfield');
el.innerHTML = `Hexagons: ${hexagons.length}/${totalHex}, resolution: ${rRef}`;
return [r0, r1].map(r => {
const alpha = Math.pow(1 - Math.abs(r - resolution), 3);
const color = Math.floor(r) % 2 ? GREEN : PURPLE;
return new deck.H3HexagonLayer({
id: `hex-${r}`,
data: getHexagonsInView(viewState, r),
getHexagon: d => d,
getLineColor: [...color, 255 * alpha],
getFillColor: [0, 0, 0, 100],
extruded: false,
filled: true,
stroked: true,
getLineWidth: 3,
lineWidthUnits: 'pixels',
});
});
}
const initialViewState = {
latitude: 40.4948,
longitude: -3.3746,
zoom: 5,
maxZoom: 30
}
const d = new deck.DeckGL({
container: 'map',
// mapStyle: deck.carto.BASEMAP.DARK_MATTER,
mapStyle: deck.carto.BASEMAP.POSITRON,
controller: true,
initialViewState,
layers: [createHexagonLayers(initialViewState)],
onViewStateChange: ({
viewState
}) => {
d.setProps({
...