Updating data on mapbox without updating the whole map

Hi,

My idea is to load a map, and then with checkboxes you should be able to click on information you want showed on the map.

So the question is, after loading the map - is it then possible to update the map with the information the user wants.
Or would I have to reload the whole map that includes the new chosen data?

If I have to reload the whole map, is it then possible to keep the map at the exact same view before and after reloading?

I haven’t tried with the default Plotly map tools, but you can do it with Dash Leaflet. You can attach callback to the properties of any element of the map and update it that way. In example 1 here,

the tile layers are changed by radio buttons.

Hi Emil,

I’m trying to run your examples; usage_gallery.py… But I do get errors;

Traceback (most recent call last):
  File "C:/Users/Desktop/dash-leaflet/usage_gallery.py", line 5, in <module>
    import dash_leaflet as dl
  File "C:\Users\Desktop\dash-leaflet\dash_leaflet\__init__.py", line 10, in <module>
    from ._imports_ import *
  ModuleNotFoundError: No module named 'dash_leaflet._imports_'

@keend
You need to install dash_leaflet. The error is telling you that it is not installed.

It is installed, version 0.0.3?

It might have to do with how you are running the file. Here is a step-by-step guide, which works on my PC.

  • Create a folder for the project, copy the “usage_gallery.py” file into the folder and put in your own mapbox API key.
  • Create a new virtual environment in the folder and activate it. On Linux the commands would be
python3 -m venv venv
source venv/bin/activate
  • Install the proper packages

pip install dash dash_leaflet

  • Run the code

python3 usage_gallery.py

Ok, I just cloned your git - installed packages and tried to run…

Doing it the way you explain, gives me an error;

ModuleNotFoundError: No module named ‘settings’

What module is that?

@keend That because i put my API key in a file called settings.py. Så by the step “put in you own mapbox API key”, i mean that you should create a file called “settings.py” with the content

MAPBOX_TOKEN = [PUT YOUR TOKEN HERE]

and place it in your project directory (next to “usage_gallery.py”). Alternatively, you can put the key directly in the example file. In this case you should delete the settings import, i.e. the line

import settings

and replace the line

mapbox_token = settings.MAPBOX_TOKEN

with

mapbox_token = [PUT YOUR TOKEN HERE]

Got it! Thank you :slight_smile:

Good to hear that it works :slight_smile:

Emil, thanks for the hints in this thread. Would you by any chance have an alternative for the dead github link?
I’m having trouble displaying a new layer (or rather scratching an old one and replacing it with new data, including new geometries). This is my current process:

  • control buttons filter and aggregate a global dataframe that is outputted to a dcc.Store
  • an other callback updates graphs based on that Store input

This works fine for regular dcc.Graph components, but the dl.GeoJSON child doesn’t seem to refresh. I was thinking there could be a way to delete the element prior to refreshing, or maybe render dynamic ids with Pattern-Matching. Any help would be very welcome. Here’s a simplified version of the culprite callback:

@app.callback(Output('road_layer', 'children'),
    [Input('road_store', 'data')])
def road_display(roads):
    return dl.GeoJSON(
        id='geojson',
        data=roads
    ),

I guess at the moment, it would work if you changed the id every time, i.e. set it to a new GUID, current time or something like that. However, i am working on a major overhaul of the GeoJSON component, which will allow you (among other things) to target the data property directly, which will be simpler/more intuitive.

Great news for the GeoJSON upgrade.
By the way, dynamic ids worked : id=f'geojson-{str(uuid.uuid4())[:8]}'
However it disables the hover callback that was based on that id. I’ll give pattern-matching callbacks a try.

That should work, yes. But it’s not so pretty :stuck_out_tongue:

EDIT: I plan to release the new GeoJSON component this week.

1 Like

The new GeoJSON has now been released. If you want to try it out, you’ll need to install a few packages,

pip install dash-leaflet==0.1.0
pip install dash-transcrypt==0.0.6
pip install geobuf

With the new component, you should be able to update the data property directly. I added an example to the docs that demonstrates it. Be aware that i have made some breaking changes though.

@Emil, since this link is not working, can you please help to understand how the code works?

I have similar issue. I have a base map (I create it with scatter_mapbox()) which returns a map with the positions of addresses. I also have dropdown menu which filters only certain countries, partners, etc. Each time I use drop-down menu, the map goes back to its initial center point. I’m wondering how I can avoid the update of the whole map.

Here’s the base map:

def main_map(df):
    map_fig = px.scatter_mapbox(df, lat="latitude", lon="longitude", hover_data=["age", "income"],
                        color_discrete_sequence=["fuchsia"], zoom=12, height=780, center={'lat':57.67, 'lon':12.00})
    map_fig.update_layout(mapbox_style="open-street-map")
    map_fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})

    return map_fig

And callback:

@app.callback(
    Output('map', 'figure'),
    Input('country-filter', 'value'),
    Input('partner-filter', 'value'),
    Input('product-filter', 'value'))
def update_main_map(country,partner,product):

    input_list = []

    if country is not None:
        a = "`country` == " + "'" + country + "'"
        input_list.append(a)
    if partner is not None:
        b = "`partner` == " + "'" + partner + "'"
        input_list.append(b)
    if product is not None:
        c = "`product` == " + "'" + product + "'"
        input_list.append(c)
    
    
    delimeter = " and "

    if len(input_list)>0:
        query_string = delimeter.join(input_list)  
        dff = df.query(query_string)
    else:
        dff=df.copy()

    map_fig = px.scatter_mapbox(dff, lat="latitude", lon="longitude", hover_data=["age", "income"],
                        color_discrete_sequence=["fuchsia"], zoom=6, height=780, center={'lat':57.67, 'lon':12.00})
    map_fig.update_layout(mapbox_style="open-street-map")
    map_fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})

    return map_fig

The inputs to the callback are country, partner, product filters.

The docs have moved to Render :slight_smile:

https://dash-leaflet-docs.onrender.com/

Thanks! :slight_smile: I will have a look and write back if something is not clear.

UPDATE.

I found out that, there is a built-in setting to do this in plotly maps. In map layout, you should set uirevision to True uirevision=True. To understand how syntax works, one can check this app:

https://github.com/plotly/dash-sample-apps/blob/main/apps/dash-spatial-clustering/app.py

And this is the link to the app on Dash app gallery: https://dash.gallery/dash-spatial-clustering/

1 Like