l’Hexagone in hexagons: geospatial data with Uber H3 and Folium
I don’t think I need to sell you the concept of hexagons. They are just great, and always have been. Among the reasons: they tile the plane, can be any size you need, have consistent area (I’m looking at you, zipcodes) and have 6 neighbours the same distance from each other. No sqrt(2)-because-we-diagonals-are-so-special nonsense.
Be aware: this article was written for H3 v3.7.7, before the breaking API changes of h3-py v4.x. Use pip install h3==3.7.7 or modify the API calls accordingly!
In a pinch, just truncating latitude and longitude to a certain number of decimals can work, but you will soon start kicking yourself and create workarounds for that irregular-sized grid headache you just created.
However, working out how hexagons should be placed to cover Earth is not something you want to do over and over. Fortunately, Uber’s H3 library comes to the rescue. It provides you with utility functions to map from <latitude, longitude> to <hex ID>, where hex ID is a compact 64-bit number which encodes both the location and the size of the cell.
Abdullah Kurkcu’s blog post about H3 finally got me going with trying it out, but there were a couple of problems (perhaps older library versions?) in the code snippets that prevented it from working out for me. Still, this was a very good start.
For a little example, I reached for the convenient dataset provided by Arthur Charpentier covering French communes and their population numbers.
Visualizing your geospatial data using hexagons takes just a few simple steps:
- Add a hex ID wherever you have latitudes and longitudes.
- Aggregate your data based on your hex IDs.
- Plot your hexagons in a map view, transforming your hex IDs into their actual geometric shapes as needed.
Step 1 is as simple as it sounds (here with a pandas dataset):
Step 2 is also simple:
And then there is step 3. I’ll be honest with you: I lied about all steps being simple. This step is pretty much the reason I even made this blog post. I started out with the helper functions from Abdullah’s blog post, fixed the issues I found and made some changes to make it more convenient (at least for me) to use. Still, this block is just a few helper functions and you don’t need to repeat them for every chart. Alternatively, you can extract the goodies you need for your own versions. (Complete version with all required imports available at the end of the page.)
And that’s it!
If this can be accomplished in an easier way, without those helper functions, please do let me know. I am not tied to the Folium maps, if that helps.
Your mileage may vary, and if it does — and by “vary”, I mean that your cells are pretty much all the same colors — my advice to you is to transform your value range. Above, I have used population percentiles, which of course ensures full use of the colour range, because the actual raw data (below) has outliers in the Paris region with a population that is a lot higher than any other part of France; hexagons or not.
In case you wonder why we need all of those little hexagons when most of France could be covered in much larger ones, the next one is for you. H3 provides the ability of mapping to larger hexagons (“compacting”) in a reversible way, by ignoring that hexagons do not have perfect child containment (one of their few flaws). This results in some gaps in an aggregated view but it looks pretty cool anyway.
Full Jupyter notebook for the charts above available here.
Be aware: this article was written for H3 v3.7.7, before the breaking API changes of h3-py v4.x. Use pip install h3==3.7.7 or modify the API calls accordingly!
I will resist the urge to link to the excellent Hexagons are the Bestagons (oh no, I failed!) and instead end with a favourite video among those covering H3 from Uber Engineering themselves:
If you have suggestions on how to improve or simplify the above, please let me know. One thing I am missing is a colour legend, but it seems tricky to add that to Folium maps (and the ways I have tried did not work). Also, it would be great if I could get tooltip-like numeric info for individual hex cells.