Show and Tell - Dash Leaflet

Were you able to ever find a solution to this? I’m having the exact same issue. The map keeps autopanning to my popups and immediately closing them. autoPan and autoClose were available when I was using dl.Popup and dl.MarkerClusterGroup but I’m having a very hard time trying to mimic those behaviors withdl.GeoJSON since it doesn’t support them natively.

Hi, All! Does the dash leaflet support a custom CRS for dl.Map like a proj4leaflet?

The supported CRS are,

    "EPSG3395",
    "EPSG3857",
    "EPSG4326",
    "Earth",
    "Simple",
    "Base"

Does that answer your question?

Yes! Thank You for response!

Hello!

Dash Leaflet is awesome tool! Thanks. But can i get a vector tiles, such as an MVT with dash leaflet? I try to get it with TileLayer. Dash Leaflet send a request to my MVT server. It works, and server returns an MVT tile, that I can see in my browser cache, download and open in QGIS. But this tiles can not render my dash leaflet map

Thanks! No, vector tiles are currently not supported :blush:. But they may be at some point.

1 Like

Thank You!

Created a quick Legend (that I’ve used quite a bit) for Leaflet using Dash Mantine Components. The code is a bit imbedded, so hard to pull out a MRE, but figured I’d share and happy to walk anyone through how to recreate it.

If you didn’t know this (I didn’t until a week or so ago), you can have an Html.Div as one of your leaflet layers and just use an absolute position. The US geojson tutorial has an example of this in the Dash Leaflet docs.

If you want just a basic legend I did this:

myGroup =[dmc.Group([DashIconify(icon="bi:square-fill",color=color_config[key]),html.P(key,style={'color':'black'})]) for key in color_config if key in statLst]

Where color_config is some dict and statLst is my list of entries. I then add it to the map like this:

    filterLayer = html.Div([
        html.Div(dmc.SimpleGrid(children=myGroup,cols=1),style={'display':'block'}),
    ],style={"position": "absolute", "top": "10px", "left": "10px", "z-index": "1000"})

If you want the interactivity like in the attached GIF… just create two legends, and use a callback to swap the styles from ‘block’ to ‘none’.

So the full code for that would be:

    filterLayer = html.Div([
        dmc.SegmentedControl(
            id='myID',
            value='Option1',
            data = ['Option1','Option2'],
        ),
        html.Div(dmc.SimpleGrid(children=myGroup,cols=1),id='stat-drill',style={'display':'block'}),
        html.Div(dmc.SimpleGrid(children=myGroup2,cols=1),id='form-drill',style={'display':'none'})
    ],style={"position": "absolute", "top": "10px", "left": "10px", "z-index": "1000"})

Finally, if you want to tie that back to the GeoJSON, you can use a clientside callback and set the style with whatever control you are using. In my case I was using the SegmentedControl. Obviously, you are going to have to pivot a bit if your legend is pretty long, but you can always set the dmc.SimpleGrid to have more columns (can do that dynamically). I’ve seen (and asked) a bit about legends in Dash Leaflet, so figured I’d share an example.

gif link: ezgif.com video to gif - Gifyu

Hey - my hat’s off for making this component, very useful!

One question: in a multi-page app, how to persist the map such that navigating between pages does not reset the map? There’s no persistence prop on the component. Would adding them as explained in this doc solve the problem? Or there are easier ways than forking the component?

Thanks! Yes, I guess that would do the trick. I’ll consider it for the new implementation that I am working on :slight_smile:

I took a stab at it and added the required properties, but I didn’t get it right. If you have any tips, I’d be grateful! I was hoping that with e.g. center and zoom I could at least preserve the location when user navigates back, but the map still gets a full refresh.

    persistence: PropTypes.oneOfType([
        PropTypes.bool,
        PropTypes.string,
        PropTypes.number,
    ]),

    /**
     * Properties whose user interactions will persist after refreshing the
     * component or the page.
     */
    persisted_props: PropTypes.arrayOf(
        PropTypes.oneOf(['location_lat_lon_acc', 'center', 'zoom'])
    ),

    /**
     * Where persisted user changes will be stored:
     * memory: only kept in memory, reset on page refresh.
     * local: window.localStorage, data is kept after the browser quit.
     * session: window.sessionStorage, data is cleared once the browser quit.
     */
    persistence_type: PropTypes.oneOf(['local', 'session', 'memory']),

Very nice work on Dash Leaflet @Emil !
I am getting a little stuck trying to work out a few things and was hoping for a bit of guidance. Using the GeoJSON Choropleth Map example ( Dash (dash-leaflet.herokuapp.com)), how would I add an on click event to change the style of the clicked feature, and then when clicked off the style of the feature would revert back to its previous style? I have tried a callback using the following but seem to be getting nowhere with it:

Output('geojson', 'style_feature'),
Input('geojson', 'click_feature'),

Any help on this would be greatly appreciated!

Thanks! I believe the most efficient approach would be to store the state (i.e. if the feature is selected or not) in the hideout prop and then include the conditional styling in the style function (written in JS). In Dash you would then create a callback with the click event as input, and the hideout prop as output.

Thanks for the quick reply! I am somewhat of a newbie to this and so working with the hideout has been a little challenging. Any chance of a small example to help point me in the right direction please?

Its hard to get much more into detail without providing an actual implementation. So here we go :smiley:

import dash_leaflet as dl
from dash import Dash, html, Output, Input, State
from dash_extensions.javascript import assign

url = "https://raw.githubusercontent.com/PublicaMundi/MappingAPI/master/data/geojson/us-states.json"
# Color selected state(s) red.
style_handle = assign("""function(feature, context){
    const {selected} = context.props.hideout;
    if(selected.includes(feature.properties.name)){
        return {fillColor: 'red'}
    }
}""")
# Create geojson.
geojson = dl.GeoJSON(url=url,  # url to geojson file
                     hideout=dict(selected=[]),  # no initial selections
                     options=dict(style=style_handle),  # how to style each polygon
                     zoomToBounds=True,  # when true, zooms to bounds when data changes (e.g. on load)
                     id="geojson")
# Create app.
app = Dash(prevent_initial_callbacks=True)
app.layout = html.Div([dl.Map(children=[dl.TileLayer(), geojson])],
                      style={'width': '100%', 'height': '50vh', 'margin': "auto", "display": "block"}, id="map")

@app.callback(Output("geojson", "hideout"), Input("geojson", "n_clicks"), 
              State("geojson", "click_feature"), State("geojson", "hideout"))
def toggle_selected(_, feature, hideout):
    name, selected = feature["properties"]["name"], hideout["selected"]
    if name not in selected:
        selected.append(name)
    else:
        selected.remove(name)
    return hideout

if __name__ == '__main__':
    app.run_server()

Thanks @Emil , that worked perfectly for my situation with a few minor tweaks.

1 Like

Hi @Emil , do you know if its possible to return an ‘on click’ event from the LayerControl component such that I can trigger a callback if a given layer is ‘true’? This would then allow me to load my geojson data as the user requires it and not all up front but still have the Overlay names pre-populated in the LayerControl component.

It is not possible in the current version, but it is an option that i am planning to enable in the next major release :slight_smile:

2 Likes

I have just released a new major version of Dash Leaflet, which is the result of a complete rewrite of the library in TypeSciprt using React Leaflet v4. The new version comes with updated dependencies, fixed vulnerabilities, improved unit tests, a number of bugfixes, and a new event system for improved flexibility.

I have also created a new documentation page, which is available on https://www.dash-leaflet.com/. However, the old documentation page on Heroko, i.e. http://dash-leaflet.herokuapp.com/ is still online - but it is no longer tied to my Heroku account, so I can’t remove it/update it. If anyone here knows who owns it, please contact me :slight_smile:

As always, you can install the latest release via pip,

pip install dash-leaflet==1.0.7

As a lot of code has been touched, there may be some rough edges here and there. I have added some migration notes where (some of) the breaking changes are outlined. If you experience any (other) issues, please let me know, and I’ll try to address them and/or update the migration guidelines.

@troynh - It believe it should now be possible to do what you want registering an eventhandler on the map for the appropriate event(s)

5 Likes

Great work @Emil , thanks!

One question concerning the GeoJSON examples in the docs. In the Interactivity via the hideout prop you create the data from a dictionary, and hence have dd_defaults and dd_options readily available. What if I am loading the data from a PBF (or straight GeoJSON) and would like to show name property that’s part of the feature as a tooltip?

One way appears to be renaming first the name property to tooltip, but that’s limiting and surely there’s a better way. I presume an arrow function is a way to go, but so far I didn’t get it right. Could you give an example?