3

I have been using dash leaflet for creating my own dashboard with maps, and it has been great to be able to visualize things with an interactive map. However, there is one thing that I have been stumped on a while for how to deal with a click happening on a polygon or marker. To explain this, I have created a simple example below

import geopandas as gpd
import dash_leaflet as dl
import dash_leaflet.express as dlx
from dash import Dash, html, Output, Input
import json

location = gpd.GeoDataFrame(geometry=gpd.points_from_xy([-74.0060], [40.7128]))
app = Dash()
app.layout = html.Div(
                    children=[
                        dl.Map(
                            center=[39, -98], 
                            zoom=4, 
                            children=[
                                dl.TileLayer(),
                                dl.GeoJSON(data=dlx.geojson_to_geobuf(json.loads(location.to_json())), format='geobuf', id='locations', zoomToBoundsOnClick=True)], 
                            style={'width': '100%', 'height': '80vh'}, id="map"),
                        html.Div(id='counter')])
counter = 0
@app.callback(Output('counter', 'children'), Input('locations', 'click_feature'))
def update_counter(feature):
    global counter
    counter += 1
    return str(counter)

if __name__ == "__main__":
    app.run_server(debug=True)

And here is what the dashboard looks like when you first load it enter image description here

Below the map there is a div that contains the number of times the geojson has been clicked (and I realize that when initializing that the function gets called, but that isn't the focus of this problem). When you click on the marker the first time, the div gets updated and the number increases. However, if you were to try and click on the marker again, the div does not update and there is no increase in the number. What I have figured out is that an event will only be fired if you click on a different marker (you can add a coordinate to the geojson and click between the two markers to see for yourself). In the example, this is a counter, but I am trying to filter my data to the location clicked on every time. So my question is what do you have to do to have your data filter every time the geojson is clicked on?

Another small thing that I have noticed is that even though I set zoomToBoundsOnClick to true, I do not have the map zoom in on the marker that I clicked on. This isn't a big deal, but it would be a nice to have. So if someone know how to get that work, that would also be appreciated.

1 Answers1

0

In Dash, a callback is only invoked when a property changes. If you click the same feature twice, the click_feature property doesn't change, and the callback is thus not invoked. If you want to invoke the callback on every click, you can target the n_clicks property - it is incremented on (every) click, and the callback will thus fire on every click.

emher
  • 5,634
  • 1
  • 21
  • 32
  • After reading through my question, I think I may have missed an important component to the question. I am using the click to filter data, so the problem with n_clicks is that it does not tell you which polygon (or in this case marker) was clicked. I want the data to be filtered every time the geo_json is clicked. I am sorry that I wasn't more explicit with that. I will edit my question to include that. – Robert Allan Jul 19 '22 at 13:24
  • That should be possible by configuring the callback with 'n_clicks' as 'Input' (to ensure the callback is triggered on every click) and 'click_feature' as 'State' (to get the data that you need passed to the callback). – emher Jul 19 '22 at 15:18
  • 2
    I just tried that and it worked! Just so that other people can see this, it might be good to include that in your answer. Essentially the code would be `@app.callback(Output('table', 'data'), [Input('locations', 'feature_click'), Input('locations', 'n_clicks')]` where below this you would write `def geo_filter(feature, n_clicks):`. In the function you would write `return df[(df['lat'] == feature['properties']['lat']) & (df['lon'] == feature['properties']['lon'])] if feature else None`. You can have the function be something besides a data table, but this is the general idea. Thanks! – Robert Allan Jul 20 '22 at 13:12