h3-js
h3-js copied to clipboard
Somthing is funky at the borders of the cells
Uncaught Error: Resolution argument was outside of acceptable range (code: 4, value: -1)
c h3.js:1
s h3.js:1
y h3.js:1
polygonToCells h3.js:1
updateMapDisplay h3viewer.js:115
fire Events.js:195
_moveEnd Map.js:1248
_onZoomTransitionEnd Map.js:1735
setTimeout handler*_animateZoom Map.js:1714
_tryAnimatedZoom Map.js:1679
x Util.js:231
_tryAnimatedZoom Map.js:1676
setView Map.js:195
setZoomAround Map.js:249
_performZoom Map.ScrollWheelZoom.js:83
setTimeout handler*_onWheelScroll Map.ScrollWheelZoom.js:57
o DomEvent.js:108
ke DomEvent.js:123
S DomEvent.js:34
addHooks Map.ScrollWheelZoom.js:33
enable Handler.js:23
addHandler Map.js:733
i Class.js:114
callInitHooks Class.js:81
initialize Map.js:156
e Class.js:24
map Map.js:1750
<anonymous> logic.js:204
[h3.js:1:157510](/h3.umd.js)
this also happens when you zoom out so multiple earths are in frame. . it would be nice if the polygons supported Mercator clipping or something. not sure what the most sain implementation is
Anyway though id point it out if curious this is how im adding this to leaflet map
h3viewer.js
// let map, hexLayer, p;
const GeoUtils = {
EARTH_RADIUS_METERS: 6371000,
radiansToDegrees: (r) => r * 180 / Math.PI,
degreesToRadians: (d) => d * Math.PI / 180,
getDistanceOnEarthInMeters: (lat1, lon1, lat2, lon2) => {
const lat1Rad = GeoUtils.degreesToRadians(lat1);
const lat2Rad = GeoUtils.degreesToRadians(lat2);
const lonDelta = GeoUtils.degreesToRadians(lon2 - lon1);
const x = Math.sin(lat1Rad) * Math.sin(lat2Rad) +
Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.cos(lonDelta);
return GeoUtils.EARTH_RADIUS_METERS * Math.acos(Math.max(Math.min(x, 1), -1));
}
};
const ZOOM_TO_H3_RES_CORRESPONDENCE = {
5: 1,
6: 2,
7: 3,
8: 3,
9: 4,
10: 5,
11: 6,
12: 6,
13: 7,
14: 8,
15: 9,
16: 9,
17: 10,
18: 10,
19: 11,
20: 11,
21: 12,
22: 13,
23: 14,
24: 15,
};
const H3_RES_TO_ZOOM_CORRESPONDENCE = {};
for (const [zoom, res] of Object.entries(ZOOM_TO_H3_RES_CORRESPONDENCE)) {
H3_RES_TO_ZOOM_CORRESPONDENCE[res] = zoom;
}
const getH3ResForMapZoom = (mapZoom) => {
return ZOOM_TO_H3_RES_CORRESPONDENCE[mapZoom] ?? Math.floor((mapZoom - 1) * 0.7);
};
const h3BoundsToPolygon = (lngLatH3Bounds) => {
lngLatH3Bounds.push(lngLatH3Bounds[0]); // "close" the polygon
return lngLatH3Bounds;
};
/**
* Parse the current Query String and return its components as an object.
*/
const parseQueryString = () => {
const queryString = window.location.search;
const query = {};
const pairs = (queryString[0] === '?' ? queryString.substr(1) : queryString).split('&');
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i].split('=');
query[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1] || '');
}
return query;
};
const queryParams = parseQueryString();
const copyToClipboard = (text) => {
const dummy = document.createElement("textarea");
document.body.appendChild(dummy);
dummy.value = text;
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
};
function initH3LayerOnMap(h3code) {
let self = {
computeAverageEdgeLengthInMeters: function (vertexLocations) {
let totalLength = 0;
let edgeCount = 0;
for (let i = 1; i < vertexLocations.length; i++) {
const [fromLat, fromLng] = vertexLocations[i - 1];
const [toLat, toLng] = vertexLocations[i];
const edgeDistance = GeoUtils.getDistanceOnEarthInMeters(fromLat, fromLng, toLat, toLng);
totalLength += edgeDistance;
edgeCount++;
}
return totalLength / edgeCount;
},
updateMapDisplay: function () {
if (hexLayer) {
hexLayer.remove();
}
hexLayer = L.layerGroup().addTo(map);
const zoom = map.getZoom();
self.currentH3Res = getH3ResForMapZoom(zoom);
const {_southWest: sw, _northEast: ne} = map.getBounds();
const boundsPolygon = [
[sw.lat, sw.lng],
[ne.lat, sw.lng],
[ne.lat, ne.lng],
[sw.lat, ne.lng],
[sw.lat, sw.lng],
];
const h3s = h3.polygonToCells(boundsPolygon, self.currentH3Res);
for (const h3id of h3s) {
const polygonLayer = L.layerGroup()
.addTo(hexLayer);
const isSelected = h3id === self.searchH3Id;
const style = isSelected ? {fillColor: "orange"} : {};
const h3Bounds = h3.cellToBoundary(h3id);
const averageEdgeLength = self.computeAverageEdgeLengthInMeters(h3Bounds);
const cellArea = h3.cellArea(h3id, "m2");
const tooltipText = `
Cell ID: <b>${h3id}</b>
<br />
Average edge length (m): <b>${averageEdgeLength.toLocaleString()}</b>
<br />
Cell area (m^2): <b>${cellArea.toLocaleString()}</b>
`;
const h3Polygon = L.polygon(h3BoundsToPolygon(h3Bounds), style)
.on('click', () => copyToClipboard(h3id))
.bindTooltip(tooltipText)
.addTo(polygonLayer);
// less SVG, otherwise perf is bad
if (Math.random() > 0.8 || isSelected) {
var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg");
svgElement.setAttribute('viewBox', "0 0 200 200");
svgElement.innerHTML = `<text x="20" y="70" class="h3Text">${h3id}</text>`;
var svgElementBounds = h3Polygon.getBounds();
L.svgOverlay(svgElement, svgElementBounds).addTo(polygonLayer);
}
}
},
gotoLocation: function () {
const [lat, lon] = (self.gotoLatLon || "").split(",").map(Number);
if (Number.isFinite(lat) && Number.isFinite(lon)
&& lat <= 90 && lat >= -90 && lon <= 180 && lon >= -180) {
map.setView([lat, lon], 16);
}
},
findH3: function () {
if (!h3.isValidCell(self.searchH3Id)) {
return;
}
const h3Boundary = h3.cellToBoundary(self.searchH3Id);
let bounds = undefined;
for ([lat, lng] of h3Boundary) {
if (bounds === undefined) {
bounds = new L.LatLngBounds([lat, lng], [lat, lng]);
} else {
bounds.extend([lat, lng]);
}
}
map.fitBounds(bounds);
const newZoom = H3_RES_TO_ZOOM_CORRESPONDENCE[h3.getResolution(self.searchH3Id)];
map.setZoom(newZoom);
}
}
function mounted() {
map.on("zoomend", self.updateMapDisplay);
map.on("moveend", self.updateMapDisplay);
const h3 = h3code || p.h3viewer;
console.log(h3)
// if (h3) {
// self.searchH3Id = h3;
// window.setTimeout(() => self.findH3(), 50);
// }
self.updateMapDisplay();
return self;
}
return mounted()
}
var hexLayer
function getParamH3Viewer() {
let z = map.getZoom()
let c = map.getCenter()
let a = [z, c.lat, c.lng]
return "mapinfo=" + encodeURIComponent(JSON.stringify(a))
}
if (p && p.h3viewer) {
try {
let a = p.h3viewer
// hmm coulf be hexcode
console.log("yo dog")
window.h3viewer = initH3LayerOnMap(false)
} catch (e) {
console.error("Invaled mapinfo doing nothing [zoom, lat, lon]")
}
}
See this answer: https://github.com/uber/h3-js/issues/158#issuecomment-1244087555
The problem in the image is a rendering issue, not an H3-js issue. H3 always provides coordinates with normalized longitudes (in the range -180 to 180) but this doesn't always render well on web maps.
The error you pasted to is a different issue - at some point you're sending resolution -1 to the polygonToCells function, and this throws an error for bad input.