Created
February 11, 2021 22:11
-
-
Save kopp/e2a434dafd567872d00969fe57354a8f to your computer and use it in GitHub Desktop.
Dash App with plotted map and some user interaction
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# %% | |
# Application that displays data on a map and allows to modify the data in a | |
# callback triggered by a button. | |
# | |
# Note: This application must not be executed using multiple workers, but only | |
# single-threaded (i.e. run with `python <name>`). | |
# See https://dash.plotly.com/sharing-data-between-callbacks for the reason. | |
import dash | |
import dash_core_components as dcc | |
import dash_html_components as html | |
from dash.dependencies import Input, Output, State | |
import plotly.graph_objects as go | |
import pandas as pd | |
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] | |
app = dash.Dash(__name__, external_stylesheets=external_stylesheets) | |
styles = { | |
'pre': { | |
'border': 'thin lightgrey solid', | |
'overflowX': 'scroll' | |
} | |
} | |
data = pd.DataFrame([(1, 45, -73, 0), (2, 44, -72, 0), (3, 45, -72, 1), (4, 44, -74, 1)], columns=["id", "lat", "lon", "chunk"]) | |
def make_figure_for_data(data): | |
fig = go.Figure() | |
fig.add_trace(go.Scattermapbox( | |
lat=data["lat"], | |
lon=data["lon"], | |
customdata=data["id"], | |
mode='markers', | |
marker=go.scattermapbox.Marker( | |
size=12 | |
), | |
)) | |
fig.update_layout( | |
hovermode='closest', | |
clickmode='event+select', | |
mapbox=dict( | |
style="open-street-map", # or "carto-positron", "carto-darkmatter", "stamen-terrain", "stamen-toner" or "stamen-watercolor" | |
bearing=0, | |
center=go.layout.mapbox.Center( | |
lat=45, | |
lon=-73 | |
), | |
pitch=0, | |
zoom=5 | |
) | |
) | |
fig.update_traces(marker_size=20) | |
return fig | |
# %% | |
# make_figure_for_data(data).show() | |
# %% UI Elements of the App | |
app.layout = html.Div([ | |
dcc.Graph( | |
id='figure', | |
figure=make_figure_for_data(data), | |
), | |
html.Div([ | |
dcc.Markdown(""" | |
**Selected Points:** | |
Choose the lasso or rectangle tool in the graph's menu | |
bar and then select points in the graph or hold down the shift | |
button while clicking. | |
""" | |
), | |
]), | |
html.Div([ | |
html.Ul(id='selected-data'), | |
], className='three columns'), | |
html.Div([ | |
html.Button("Use the Selection", id='use-selection'), | |
html.P(id='placeholder'), | |
]), | |
]) | |
def get_point_ids_from_selected_points(selectedData): | |
if selectedData is None: | |
return [] | |
selected_ids = [] | |
for point in selectedData.get("points", []): | |
selected_ids.append(point.get("customdata", -1)) | |
return selected_ids | |
@app.callback( | |
Output('selected-data', 'children'), | |
[Input('figure', 'selectedData')]) | |
def display_selected_data(selectedData: dict): | |
selected_ids = get_point_ids_from_selected_points(selectedData) | |
lis = list(map(lambda id: html.Li(f"point {id}"), selected_ids)) | |
return lis | |
@app.callback( | |
[Output('use-selection', 'n_clicks'), Output("figure", "figure")], | |
[Input('use-selection', "n_clicks")], | |
[State("figure", "selectedData"), State("figure", "figure")], | |
) | |
def process_selected_data(click_count, selected_data, figure): | |
# Since we have both selection and button as input, the callback | |
# is called when the selection changes (and button is not pressed). | |
# To distinguish those: Check the click count and re-set that to 0 | |
# at the end of the callback. | |
# Note: when using a State for the selection, this is not necessary any more. | |
button_was_clicked = click_count is not None and click_count > 0 | |
if button_was_clicked: | |
selected_ids = get_point_ids_from_selected_points(selected_data) | |
global data | |
new_common_chunk = data.loc[data["id"].isin(selected_ids), "chunk"].min() | |
for id in selected_ids: | |
data.loc[data["id"] == id, "chunk"] = new_common_chunk | |
# add some visible modifications | |
data.loc[data["id"] == id, "lat"] -= 0.5 | |
# update the figure's data | |
# keep the rest of the state as is | |
figure["data"][0].update( | |
lat=data["lat"], | |
lon=data["lon"], | |
customdata=data["id"], | |
) | |
return 0, figure | |
# %% | |
if __name__ == '__main__': | |
app.run_server(debug=True) | |
# %% |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment