H3 hexagon overlaid on maps with Bokeh


I’ve searched for a working example of overlaying a map with h3 hexagons using Bokeh. There are a lot of examples using other rendering libraries like Folium but not with Bokeh. If anyone has done this before and can share an example, I’d appreciate it.


What are “h3” hexagons?

H3 is Uber’s Hexagonal Hierarchical Geospatial Indexing System in Python:


For example, the hexagonal in which the following point belongs to is:

import h3

lat, lng = 37.769377, -122.388903
h3_resolution = 9
h3index = h3.geo_to_h3(lat, lng, h3_resolution)
polygon = h3.h3_to_geo_boundary(h3index

The polygon is a list of 6 pairs of long/lats. I want to decorate (overlay) several of these hexagons on a map.

If that’s the case, then I think you can:

  • convert the lat/lon coordinates to web mercator coordinates
  • pass the converted coordinates to patches or multi_polygons

Thanks for the tip. This is what I came up. It runs a bit slow for large dataframes so if anyone knows how to speed it up, please let me know.

import h3
import h3pandas
import pandas as pd
import geopandas as gpd
from bokeh.plotting import figure, show
from bokeh.tile_providers import get_provider
import xyzservices.providers as xyz

lat, lng = 32.733801, -117.193394
h3_resolution = 9
h3index = h3.geo_to_h3(lat, lng, h3_resolution)
h3index = list(h3.k_ring(h3index, 1))

df = pd.DataFrame(index=h3index)
df = df.h3.h3_to_geo_boundary()
df.rename(columns={'geometry': 'Polygon'}, inplace=True)
df['Polygon'].set_crs(crs='epsg:4324', allow_override=True, inplace=True)

def extract_xy(df):
    xx, yy = df['geometry'].exterior.coords.xy
    df['xx'] = xx.tolist()
    df['yy'] = yy.tolist()
    return df

df['geometry'] = df['Polygon'].to_crs('epsg:3857')
# line below is quite slow when dataframe size is large
df = df.apply(extract_xy, axis=1)

x_min = min(min(df['xx'].tolist(), key=min))
x_max = max(max(df['xx'].tolist(), key=max))
y_min = min(min(df['yy'].tolist(), key=min))
y_max = max(max(df['yy'].tolist(), key=max))

tile_provider = get_provider(xyz.OpenStreetMap.Mapnik)

TOOLS = "pan,wheel_zoom,box_zoom,reset,save,box_select,hover"
p = figure(width=800, height=800, 
           x_range = (x_min,x_max), y_range=(y_min,y_max),
                   x_axis_type="mercator", y_axis_type="mercator", 
                   tools = TOOLS)

p.patches(df['xx'].tolist(), df['yy'].tolist(), color=["firebrick"]*df.shape[0], alpha=[0.2]*df.shape[0], line_width=2)


It runs a bit slow for large dataframes

@arashm That’s pretty vague, you’ll have to elaborate, or ideally, provide a Minimal Reproducible Example to actually run and investigate for any possible performance tweeks.

Otherwise, there is a new involved WebGL maintainer, so there is some hope that WebGL Patches · Issue #5057 · bokeh/bokeh · GitHub may get implemented at some point in the not-distant future.