How to create discrete color mapping with thresholds via Plotly Hexbin

I have dataframe of latitude, longitude and Power. I am doing the binning with Plotly hexbin and getting the outcome like on the photo.

latitude longitude Power
26.321 50.623 -100
26.319 50.622 -80
26.321 50.627 -45

What i want is that, instead of continuous color mapping via “color_continuous_scale” of hexbins (like on the photo), i want to create discrete specific colors for specific power ranges. For instance from -140 to -100 red color, from -100 to -80 blue and from -80 to -40 green. I was trying “color_discrete_map” parameter but getting error while defining thresholds.

Here is the initial code:

import plotly.figure_factory as ff
import plotly.express as px

fig = ff.create_hexbin_mapbox(
    data_frame=df, lat="latitude", lon="longitude",
    nx_hexagon=200, opacity=0.7, labels={"color": "Power"},
    color="Power", agg_func=np.mean, color_continuous_scale="HSV", range_color=[-140,-40],
    zoom = 9

)

fig.update_layout(mapbox_style="open-street-map")
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
fig.show()
1 Like

@DANORM
There are more answers to this question I gave here, mainly for heatmaps. I collected them and created this example for
hexbin mapbox (but I wasn’t inspired on choosing nice colors :frowning: ):

import plotly.figure_factory as ff
import plotly.express as px
import numpy as np

def discrete_colorscale(bvals, colors):
    """
    bvals - list of values bounding intervals/ranges of interest
    colors - list of rgb or hex color codes for values in [bvals[k], bvals[k+1]],0<=k < len(bvals)-1
    returns a nonuniform   discrete colorscale
    """
    if len(bvals) != len(colors)+1:
        raise ValueError('len(boundary values) should be equal to  len(colors)+1')
    bvals = sorted(bvals)     
    nvals = [(v-bvals[0])/(bvals[-1]-bvals[0]) for v in bvals]  #normalized values
    
    dcolorscale = [] #discrete colorscale
    for k in range(len(colors)):
        dcolorscale.extend([[nvals[k], colors[k]], [nvals[k+1], colors[k]]])
    return dcolorscale  

bvals= [1, 2, 5, 8, 10]
colors=["#19d3f3", "#e763fa", "#cbeb00", '#0010ff']
discrete_nonuniform= discrete_colorscale(bvals, colors)

#the tickvals and ticktext for colorbar
bvals = np.array(bvals)
tickvals = [np.mean(bvals[k:k+2]) for k in range(len(bvals)-1)] #position with respect to bvals where ticktext is displayed
ticktext =  [f'{bvals[k]}-{bvals[k+1]}' for k in range(0, len(bvals)-1)]

px.set_mapbox_access_token(open(".mapbox_token").read())
df = px.data.carshare()

fig = ff.create_hexbin_mapbox(
    data_frame=df, lat="centroid_lat", lon="centroid_lon", color_continuous_scale=discrete_nonuniform,
    nx_hexagon=18, opacity=0.8, labels={"color": "n_cars"},
    min_count=1,  original_data_marker=dict(marker_size=0, marker_line_width=0)
)
fig.update_traces(marker_line_width=0.5)
fig.update_layout(coloraxis =dict(colorbar_thickness=25, colorbar_ticktext=ticktext, colorbar_tickvals=tickvals))
fig.update_mapboxes(style='stamen-terrain')

2 Likes