Published
Edited
Jun 25, 2021
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
map.on("load", () => {
// Add initial data for hexagons & points
console.log("load");
let resolution = 8;
const [hexFeatures, parrentFeatures, pointFeatures] = getFeatures(map.getBounds(), resolution);
map.addSource("geojson", {
type: "geojson",
data: hexFeatures
});
map.addSource("parrentgeojson", {
type: "geojson",
data: parrentFeatures
});
map.addSource("label", {
type: "geojson",
data: pointFeatures
});

// Add hexagon layer (outline of h3 hexagon)
map.addLayer({
"id": "geojsonLayer",
"type": "line",
"source": "geojson",
"layout": {},
"paint": {
"line-width": 1,
"line-color": "#000",
"line-opacity": 0.2
}
});
// Add parrent hexagon layer (outline of h3 hexagon)
map.addLayer({
"id": "parrentGeojsonLayer",
"type": "line",
"source": "parrentgeojson",
"layout": {},
"paint": {
"line-width": 1,
"line-color": "#000",
"line-opacity": 0.35
}
});

// Add text label layer (text of h3 index)
map.addLayer({
"id": "points",
"type": "symbol",
"source": "label",
"layout": {
"text-field": "{hex}",
"text-size": 12,
"text-anchor": "center"
},
"paint": {
"text-color": "#000"
}
});
// Setup our event listeners to update our map.
(() => {
map.on("moveend", () => updateLayer(resolution));
d3.select("#resolution-slider").on("change", () => {
resolution = parseInt(d3.select("#resolution-slider").property("value"));
updateLayer(resolution);
});
})()

});
Insert cell
Insert cell
// Update Hexagon & Text layer for the current view
function updateLayer(resolution) {
console.log(`Update layer (${resolution})`)
const [hexFeatures, parrentFeatures, pointFeatures] = getFeatures(map.getBounds(), resolution);
map.getSource("geojson").setData(hexFeatures);
map.getSource("parrentgeojson").setData(parrentFeatures);
map.getSource("label").setData(pointFeatures);
}
Insert cell
Insert cell
function h3IndexToSplitLong(inputText) {
const h3Index = inputText.trim();
if (!h3.h3IsValid(h3Index) && !h3.h3UnidirectionalEdgeIsValid(h3Index)) {
throw new Error('Please enter a valid H3 index');
}
const upper = parseInt(h3Index.substring(0, h3Index.length - 8), 16);
const lower = parseInt(h3Index.substring(h3Index.length - 8), 16);
return [lower, upper];
}
Insert cell
function getIndexDigit(h3Index, res) {
const [lower, upper] = h3IndexToSplitLong(h3Index);
const H3_PER_DIGIT_OFFSET = 3;
const H3_DIGIT_MASK = 7;
const MAX_H3_RES = 15;
const UPPER_RES_OFFSET = 11;
const UPPER_SPLIT_RES = 1;
const LOWER_SPLIT_RES = H3_PER_DIGIT_OFFSET - UPPER_SPLIT_RES;
// res < 5 is in the upper bits, with a one-bit offset
if (res < 5) {
return (upper >> UPPER_SPLIT_RES + (
(MAX_H3_RES - UPPER_RES_OFFSET - res) * H3_PER_DIGIT_OFFSET
)) & H3_DIGIT_MASK;
}
// res > 5 is in the lower bits
if (res > 5) {
return (lower >> ((MAX_H3_RES - res) * H3_PER_DIGIT_OFFSET)) & H3_DIGIT_MASK;
}
// res 5 is annoyingly split across upper and lower
return ((upper & 1) << 2) + (lower >>> 30);
}
Insert cell
function getFeatures(bbox, resolution) {
console.log(`Getting features (${resolution})...`);
// bbox is only _ne and _sw, add this for convenience
bbox._nw = { lat: bbox._ne.lat, lng: bbox._sw.lng };
bbox._se = { lat: bbox._sw.lat, lng: bbox._ne.lng };
const fuzzLng = (bbox._se.lng - bbox._sw.lng) * 0.1
const fuzzLat = (bbox._se.lat - bbox._ne.lat) * 0.1
const bboxFeature = ({
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [[
[bbox._sw.lng - fuzzLng, bbox._sw.lat + fuzzLat],
[bbox._nw.lng - fuzzLng, bbox._nw.lat - fuzzLat],
[bbox._ne.lng + fuzzLng, bbox._ne.lat - fuzzLat],
[bbox._se.lng + fuzzLng, bbox._se.lat + fuzzLat],
[bbox._sw.lng - fuzzLng, bbox._sw.lat + fuzzLat]
]]
}
});

const hexagons = geojson2h3.featureToH3Set(bboxFeature, resolution);
const parrentHexagons = geojson2h3.featureToH3Set(bboxFeature, resolution-1);

const hexPolygon = geojson2h3.h3SetToMultiPolygonFeature(hexagons);
const parrentHexPolygon = geojson2h3.h3SetToMultiPolygonFeature(parrentHexagons);
const points = {
"type": "FeatureCollection",
"features": hexagons.map(hex => (
{
"type": "Feature",
"properties": {
"hex": getIndexDigit(hex, resolution-1) + " - " + getIndexDigit(hex, resolution)
},
"geometry": {
"type": "Point",
"coordinates": h3.h3ToGeo(hex).reverse()
}
}
))
}
console.log("Done Getting features.");
return [hexPolygon, parrentHexPolygon, points];
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more