A basic question about Map in Dash-Leaflet

I am quite new to dash and therefore excuse me if this sounds too basic. I created map of India with markers using some of the example codes that i found in the official document of dash-leaflet. I wanted to create a map of India where i state boundaries would highlight when i hovered over them and a pop-up with some relevant statistic would open. Also i wanted size of the markers to be smaller but i think we can’t do that currently.
If i get a basic idea or example code as to how to fill state polygons with colors as well as hover popups with info, it will be really helpful.Thanks.
Code -
gdf = gpd.read_file(‘Indian_states.geojson’)
json.loads(gdf.to_json())

layer=dl.GeoJSON(data=json.loads(gdf.to_json()))

app=Dash()

geojson = dlx.dicts_to_geojson([{**c, **dict(tooltip=c[‘name’])} for c in indian_states])

geojson_filter = assign(“function(feature, context){{return feature.properties.name;}}”)

html.Div([
dl.Map(children=[
layer,
dl.GeoJSON(data=geojson, filter=geojson_filter)
], style={‘height’: ‘70vh’,‘width’:‘90vh’}, center=[20,80], zoom=4),
])

Hey Arpit,

Welcome to plotly here are some useful references for what you’ve described in this post.

For setting up boundaries you could use this to get a geojson of just india that would work within dash leaflet:

Then you can refrence this code to get an idea of how to set the layer, create a custom icon, change the size of said icon allow on click with map to display icon and showcase the icon only if its used within the layered borders you’ve setup:

# Open and load the GeoJSON file
with open('assets/region_borders/dev.geo.json', 'r',  encoding='utf-8') as file:
    dev_borders = json.load(file)

# check if point is within geojson area
def is_point_in(lat, lon, geojson=dev_borders):
    if not isinstance(geojson, dict) or 'features' not in geojson:
        raise ValueError("GeoJSON must be a dictionary with a 'features' key.")

    # Correct the order of lat and lon when creating the Point
    point = Point(lon, lat)  # Use (longitude, latitude)

    # Create a GeoDataFrame from the GeoJSON features
    gdf = gpd.GeoDataFrame.from_features(geojson['features'])

    # Check if the point is within any of the geometries in the GeoDataFrame
    is_within = gdf.contains(point).any()

    return is_within

map_context = [
            dl.TileLayer(id='map_style_tileLayer'), dl.LayerGroup(id="layer"),
            dl.GeoJSON(
                data=dev_borders,  # Use the loaded GeoJSON data
                id="dev_layer",
                style={"weight": 2, "dashArray": "3", "fillOpacity": 0.3},
                # Default style for the paths
                hoverStyle={"weight": 5, "color": "orange", "dashArray": ""}  # Style for hovered countries
            ),
            dl.LocateControl(
                locateOptions={'enableHighAccuracy': True, 'drawMarker': True, 'flyTo': True, 'showCompass': True}),
            dl.EasyButton(icon="fa-globe", title="Map Switch", id="site_management_satellite_button")
        ]

new_center=[38.75593, -3.93264]

layout = [dl.Map(center=new_center, zoom=2, children=map_context,
               id='map',
               style={'width': '100vw', 'height': '100vh'})]

@dash.callback(Output("layer", "children"), [Input("map", "clickData")], prevent_initial_call=True)
def map_click(click_lat_lng):
    if click_lat_lng is not None:
        coordinates = click_lat_lng['latlng']
        lat, lon = coordinates.values()

        if point_in:
            # Return a list of new elements to add to the 'layer' component
            return dl.Marker(
                    position=[lat, lon],
                    children=dl.Tooltip("Chosen 🗺 Location: ({:.5f}, {:.5f})".format(lat, lon)),
                    icon={'iconUrl': 'assets/site_icon.png',
                          'iconRetinaUrl': 'assets/site_icon.png',
                          'iconSize': [40, 40], 'iconAnchor': [12, 12], 'popupAnchor': [0, -12],
                          'shadowUrl': None, 'shadowSize': None, 'shadowAnchor': None}
                )
        return dash.no_update

Needs a little adjusting but its a solid outline to help you understand how to go about setting this up within your own project.

hope this helps.

@PipInstallPython
Thanks for the solution. Solved a lot of my problems. Still stuck on the hovering pop-up or hovering text part. Would be great if you could help me with that as well.

Thanks.

i have mine wrapped in a callback but thats not necessary, basically just setup a marker with a tooltip and tooltips can take images as children. Kinda looks like:

icon = {
    "iconUrl": f"{value}",
    "iconSize": [45, 45]  # size of the icon
}
pop_up = slides[0]['src']

return dl.Marker(position=[lat, lon],
                 children=dl.Tooltip(children=html.Img(src=pop_up, style={'width': '10vw'})),
                 icon=icon, id='initial_marker')

Can we do it without using a marker?

Yeah you can reference this instead:

https://www.dash-leaflet.com/components/ui_layers/popup

Is there a way to fill the state polygons with different colours depending on a value?

I tried to do it using a way listed in the documents but it doesnot work.All the states are coloured with the same colour.

Code :

classes = [60,65,70,75,80,85,90,95]
colorscale = [‘#FFEDA0’, ‘#FED976’, ‘#FEB24C’, ‘#FD8D3C’, ‘#FC4E2A’, ‘#E31A1C’, ‘#BD0026’, ‘#800026’]
style = dict(weight=2, opacity=1, color=‘white’, dashArray=‘3’, fillOpacity=0.7)
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”)
style_handle = assign(“”“function(feature, context){
const {classes, colorscale, style, colorProp} = context.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
}
}
console.log(value)
return style;
}”“”)

map_context = [
dl.LayerGroup(id=“layer”),
dl.GeoJSON(
data=dev_borders, # Use the loaded GeoJSON data
id=“dev_layer”,
style=style_handle,
hideout=dict(colorscale=colorscale, classes=classes, style=style, colorProp=“density”),
hoverStyle={“weight”: 5, “color”: “orange”, “dashArray”: “”}, # Style for hovered countries
),
dl.LocateControl(
locateOptions={‘enableHighAccuracy’: True, ‘drawMarker’: True, ‘flyTo’: True, ‘showCompass’: True}),
dl.EasyButton(icon=“fa-globe”, title=“Map Switch”, id=“site_management_satellite_button”)
]

whoah, your making this too complicated :joy:

Look the idea is to update the map context, break up the map context with as may dl.GeoJSON layers as you need, for example if I wanted usa and mexico I would do:

map_context = [
dl.LayerGroup(id=“layer”),
dl.GeoJSON(
data=usa_borders, # Use the loaded GeoJSON USA
id=“usa_layer”,
style={"weight": 2, "dashArray": "3", "fillOpacity": 0.3, 'color':'red'},
hoverStyle={“weight”: 5, “color”: “orange”, “dashArray”: “”}, # Style for hovered countries
),
dl.GeoJSON(
data=mx_borders, # Use the loaded GeoJSON Mexico
id=“mx_layer”,
style={"weight": 2, "dashArray": "3", "fillOpacity": 0.3, 'color':'green'},
hoverStyle={“weight”: 5, “color”: “orange”, “dashArray”: “”}, # Style for hovered countries
),
dl.LocateControl(
locateOptions={‘enableHighAccuracy’: True, ‘drawMarker’: True, ‘flyTo’: True, ‘showCompass’: True}),
dl.EasyButton(icon=“fa-globe”, title=“Map Switch”, id=“site_management_satellite_button”)
]

I wanted the pop-up or text to showup only if we are hovering over a polygon or MultiPolygon. With the suggested method, it will be open by-default.

Thanks for all the help though.
Much appreciated.
:slight_smile:

I’d try something like:

dl.Polygon(positions=[[57, 10], [57, 11], [56, 11]], children=[dl.Popup(content="This is <b>html<b/>!")])

It would still be open by default and not open up only on hovering and then again close once the cursor moves over it.

I would look at this function and see if you can setup the functionality you are looking for based off this example, not sure though, haven’t attempted anything like you are describing with popups on pollygon or MultiPolygon objects