Create dynamic color bar in dash leaflet map

I’m trying to make a colored map based on some indicator value. The problem is the indicators values are different in their range of values so a unified color bar will make each indicator look the same, look at the next two photos:



This happens due to that the color bar range is fixed, and I can’t think of method to make it flexible based on the indicator value range (max would be top color, and min would be bottom color).
Here is my full code:

import dash_leaflet as dl
import dash_leaflet.express as dlx
from dash import Dash, html, Output, Input, dcc
from dash_extensions.javascript import arrow_function, assign
import geopandas as gpd
import json


hover_style= dict(weight=4, fillColor='grey')
gdf_gov = gpd.read_file('transformed_data.geojson')
gdf_gov.fillna(0, inplace=True)


# Rename the 'REGIONCODE' column to 'region_id'
region_names = gdf_gov['REGIONNAME_EN'].unique()

names = gdf_gov['indicator_name'].unique()
classes = [0, 10, 20, 50, 100, 200, 500, 1000]
colorscale = ['#FFEDA0', '#FED976', '#FEB24C', '#FD8D3C', '#FC4E2A', '#E31A1C', '#BD0026', '#800026']
style = dict(weight=2, opacity=1, color='white', dashArray='3', fillOpacity=0.7)
# Create colorbar.
ctg = ["{}+".format(cls, classes[i + 1]) for i, cls in enumerate(classes[:-1])] + ["{}+".format(classes[-1])]
colorbar = dlx.categorical_colorbar(categories=ctg, colorscale=colorscale, width=300, height=30, position="bottomleft")
# Geojson rendering logic, must be JavaScript as it is executed in clientside.
style_handle = assign("""function(feature, context){
    const {classes, colorscale, style, colorProp} = context.props.hideout;  // get props from hideout
    const value = feature.properties[colorProp];  // get value the determines the color
    for (let i = 0; i < classes.length; ++i) {
        if (value > classes[i]) {
            style.fillColor = colorscale[i];  // set the fill color according to the class
        }
    }
    return style;
}""")
# Create geojson.
geojson = dl.GeoJSON(data=json.loads(gdf_gov.to_json()),  # url to geojson file
                     options=dict(style=style_handle),  # how to style each polygon
                     zoomToBounds=True,  # when true, zooms to bounds when data changes (e.g. on load)
                     hoverStyle=arrow_function(dict(weight=5, color='#666', dashArray='')),  # style applied on hover
                     hideout=dict(colorscale=colorscale, classes=classes, style=style, colorProp="indicator_value"),
                     id="geojson")

app = Dash()
app.layout = html.Div([
    

    dl.Map(children=[dl.TileLayer(), geojson, colorbar],center=[24, 47.5], zoom =4, id="nothing"),
                       dcc.Dropdown(
                             id="region-dropdown",
                            options=[{'label': region, 'value': region} for region in region_names],
                            value=region_names[2],
                            clearable=False,
                            style={"color": "black"},
                            
                        ),
                        dcc.Dropdown(
                            id="name-dropdown",
                            options=[{'label': name, 'value': name} for name in names],
                            value=names[0],
                            clearable=False,
                            style={"color": "black"},
                        )],
                      style={'width': '100%', 'height': '50vh', 'margin': "auto", "display": "block"}, id="map")

@app.callback([Output('geojson', 'data')],
              [Input('region-dropdown', 'value'),
               Input('name-dropdown', 'value')])
def update_map(region, name):
    
    filtered_gdf_gov = gdf_gov[gdf_gov['REGIONNAME_EN'] == region]
    filtered_gdf_gov = filtered_gdf_gov[filtered_gdf_gov['indicator_name'] == name]
    layer2_data = json.loads(filtered_gdf_gov.to_json())
    return layer2_data,

if __name__ == '__main__':
    app.run_server(debug=True, port=8052)```

Hi @mo_taweel, you could specify ranges for your value and assign a color to these ranges.

An example from @empet:

And a less elegant example (mine):

1 Like

Thanks, I’ll try to use this and see if it works for me.