Hi,
I have an application with a map that shows the locations of some platforms, these same platforms are in a drop down menu. I would like the user to be able to select the platforms of interest using either the map or the dropdown menu and thus I’d like to keep them in sync.
I can get the map selections and update the menu value accordingly, but I’m having trouble figuring out how to do the reverse - change the map selections based on the changes in the menu. I can edit the “selectedData” object and return it, but it does not affect the selections show on the map. I haven’t tried constructing the “selectedData” object from whole cloth when the first menu item is selected since it has no effect.
A self-contained example is included below.
Thanks,
Roland
import dash
import dash_core_components as dcc
import dash_html_components as html
import numpy as np
import pandas as pd
import json
import plotly.express as px
from dash.dependencies import Output, Input, State
app = dash.Dash(__name__)
locations = "\"{\\\"site_code\\\":{\\\"0\\\":\\\"0n110w\\\",\\\"1\\\":\\\"0n140w\\\",\\\"2\\\":\\\"0n165e\\\",\\\"3\\\":\\\"0n170w\\\",\\\"4\\\":\\\"0n23w\\\",\\\"5\\\":\\\"0n80.5e\\\",\\\"6\\\":\\\"0n95w\\\",\\\"7\\\":\\\"10n95w\\\",\\\"8\\\":\\\"10s10w\\\",\\\"9\\\":\\\"12n23w\\\",\\\"10\\\":\\\"12n95w\\\",\\\"11\\\":\\\"15n38w\\\",\\\"12\\\":\\\"15n65e\\\",\\\"13\\\":\\\"15n90e\\\",\\\"14\\\":\\\"19s34w\\\",\\\"15\\\":\\\"20n38w\\\",\\\"16\\\":\\\"2n95w\\\",\\\"17\\\":\\\"2s95w\\\",\\\"18\\\":\\\"3.5n95w\\\",\\\"19\\\":\\\"5n95w\\\",\\\"20\\\":\\\"5s95w\\\",\\\"21\\\":\\\"6s8e\\\",\\\"22\\\":\\\"8n95w\\\",\\\"23\\\":\\\"8s67e\\\",\\\"24\\\":\\\"8s95w\\\"},\\\"wmo_platform_code\\\":{\\\"0\\\":\\\"32323\\\",\\\"1\\\":\\\"51311\\\",\\\"2\\\":\\\"52321\\\",\\\"3\\\":\\\"51010\\\",\\\"4\\\":\\\"31007\\\",\\\"5\\\":\\\"23001\\\",\\\"6\\\":\\\"32321\\\",\\\"7\\\":\\\"43008\\\",\\\"8\\\":\\\"15001\\\",\\\"9\\\":\\\"13001\\\",\\\"10\\\":\\\"43011\\\",\\\"11\\\":\\\"13008\\\",\\\"12\\\":\\\"23011\\\",\\\"13\\\":\\\"23009\\\",\\\"14\\\":\\\"31005\\\",\\\"15\\\":\\\"41139\\\",\\\"16\\\":\\\"32320\\\",\\\"17\\\":\\\"32322\\\",\\\"18\\\":\\\"32011\\\",\\\"19\\\":\\\"32303\\\",\\\"20\\\":\\\"32304\\\",\\\"21\\\":\\\"15007\\\",\\\"22\\\":\\\"43301\\\",\\\"23\\\":\\\"14040\\\",\\\"24\\\":\\\"32305\\\"},\\\"latitude\\\":{\\\"0\\\":0.0,\\\"1\\\":0.0,\\\"2\\\":0.0,\\\"3\\\":0.0,\\\"4\\\":0.0,\\\"5\\\":0.0,\\\"6\\\":0.0,\\\"7\\\":10.0,\\\"8\\\":-10.0,\\\"9\\\":12.0,\\\"10\\\":12.0,\\\"11\\\":15.0,\\\"12\\\":15.0,\\\"13\\\":15.0,\\\"14\\\":-19.0,\\\"15\\\":20.0,\\\"16\\\":2.0,\\\"17\\\":-2.0,\\\"18\\\":3.5,\\\"19\\\":5.0,\\\"20\\\":-5.0,\\\"21\\\":-6.0,\\\"22\\\":8.0,\\\"23\\\":-8.0,\\\"24\\\":-8.0},\\\"longitude\\\":{\\\"0\\\":-110.0,\\\"1\\\":-140.0,\\\"2\\\":165.0,\\\"3\\\":-170.0,\\\"4\\\":-23.0,\\\"5\\\":80.5,\\\"6\\\":-95.0,\\\"7\\\":-95.0,\\\"8\\\":-10.0,\\\"9\\\":-23.0,\\\"10\\\":-95.0,\\\"11\\\":-38.0,\\\"12\\\":65.0,\\\"13\\\":90.0,\\\"14\\\":-34.0,\\\"15\\\":-38.0,\\\"16\\\":-95.0,\\\"17\\\":-95.0,\\\"18\\\":-95.0,\\\"19\\\":-95.0,\\\"20\\\":-95.0,\\\"21\\\":8.0,\\\"22\\\":-95.0,\\\"23\\\":67.0,\\\"24\\\":-95.0}}\""
locs = json.loads(locations)
df = pd.read_json(locs, dtype={'wmo_platform_code': str, 'latitude': np.float64, 'longitude': np.float64})
menu_options = [{'label': platform, 'value': platform} for platform in sorted(df['wmo_platform_code'].to_list())]
app.layout = html.Div(children=[
dcc.Graph(id='location-map'),
dcc.Dropdown(id='platforms-dd', options=menu_options, multi=True),
html.Div(id='hidden-div', style={'display': 'none'})
]
)
@app.callback(
Output("location-map", "figure"),
Input('hidden-div', 'children')
)
def show_map(adiv):
location_map = px.scatter_geo(df,
lat='latitude', lon='longitude',
color='wmo_platform_code',
custom_data=['wmo_platform_code'],
labels={'title': 'Platform'},
category_orders={'wmo_platform_code': sorted(df['wmo_platform_code'].to_list())},
)
location_map.update_layout(clickmode='select+event')
location_map.update_traces(marker_size=10,
unselected=dict(marker=dict(size=10)),
selected=dict(marker=dict(size=14))
)
return location_map
@app.callback(
Output('platforms-dd', 'value'),
Output('location-map', 'selectedData'),
Input('platforms-dd', 'value'),
Input('location-map', 'selectedData'),
Input('location-map', 'clickData'),
prevent_initial_call=True
)
def show_map(menu_values, selections, clicks):
ctx = dash.callback_context
oid = None
if ctx.triggered:
oid = ctx.triggered[0]['prop_id'].split('.')[0]
else:
print('call back with no trigger')
if oid == 'location-map':
# Map click, set the menu according to the map
menu_values = []
if clicks is not None:
if selections is not None:
if 'points' in selections:
for p in selections['points']:
plat = p['customdata'][0]
if plat not in menu_values:
menu_values.append(plat)
print(str(menu_values))
else: # The menu changed
remove = []
if selections is not None:
if 'points' in selections:
for a_point in selections['points']:
plat_selected = a_point['customdata'][0]
if plat_selected not in menu_values:
remove.append(a_point)
for r in remove:
if r in selections['points']:
selections['points'].remove(r)
print(str(selections))
return menu_values, selections
if __name__ == '__main__':
app.run_server(debug=True)