Search code examples
pythonplotly

How to modify points drawn on map using a dropdown menu?


Here's how I create the map and add a dropdown menu with option1 and option2.

import pandas as pd
import plotly.express as px

us_cities = pd.read_csv(
    'https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv'
)


fig = px.scatter_mapbox(
    us_cities,
    lat='lat',
    lon='lon',
    hover_name='City',
    hover_data=['State', 'Population'],
    color_discrete_sequence=['fuchsia'],
    zoom=3,
)
fig.update_layout(mapbox_style='open-street-map')
fig.update_layout(margin={'r': 0, 't': 0, 'l': 0, 'b': 0})
fig.update_layout(
    updatemenus=[
        dict(
            buttons=list(
                [
                    {
                        'args': ['type', 'option1'],
                        'label': 'Option 1',
                        'method': 'restyle',
                    },
                    {
                        'args': ['type', 'option2'],
                        'label': 'Option 2',
                        'method': 'restyle',
                    },
                ]
            ),
            direction='down',
            x=0.075,
            xanchor='right',
            yanchor='bottom',
        ),
    ]
)
fig.show()

I want to have all states listed in the dropdown menu, with an option to plot all data points of all states when set to all or option1 or whatever the name is, or alternatively, to select a state and only the data points belonging to the state to be shown and the rest discarded.


Solution

  • Since express did not support drop-downs, I switched to a graph object. go does not automatically set hover data, so I introduced custom data and added city names and population. In order to switch the map by drop-down, it is necessary to have a configuration with the necessary map and data for each button, so we will prepare an empty list (for the map and for the button) for each and add the data extracted for each state. At the same time, the display and non-display of the map will be tied to the buttons. Note that the dropdowns do not scroll, so the graph height and dropdown font size must be adjusted to display all states.

    import pandas as pd
    import plotly.express as px
    import plotly.graph_objects as go
    
    us_cities = pd.read_csv(
        'https://raw.githubusercontent.com/plotly/datasets/master/us-cities-top-1k.csv'
    )
    
    traces = []
    buttons = []
    state_list = np.append('All_state', us_cities['State'].unique())
    visible = state_list
    for s in state_list:
        #print(s)
        if s == 'All_state':
            filtered_df = us_cities.copy()
        else:
            filtered_df = us_cities[us_cities['State'] == s]
    
        traces.append(go.Scattermapbox(
            lat=filtered_df['lat'],
            lon=filtered_df['lon'],
            mode='markers',
            visible=True if s == state_list[0] else False,
            customdata=filtered_df,
            hovertemplate='City: %{customdata[0]}<br>Population: %{customdata[2]}<extra></extra>',
            marker=go.scattermapbox.Marker(
                size=9,
                color='fuchsia'
            )
        ))
        
        buttons.append(
            dict(
                method='update',
                label=s,
                args=[{'visible':list(visible==s)}],)
        )
    
    fig = go.Figure(data=traces)
    fig.update_layout(
        mapbox=dict(
            style='open-street-map',
            #accesstoken=mapbox_access_token,
            bearing=0,
            center=go.layout.mapbox.Center(
                lat=us_cities['lat'].mean(),
                lon=us_cities['lon'].mean(),
            ),
            zoom=3
        ),
        margin={'r':10, 't': 0, 'l': 0, 'b': 0}
    )
    
    fig.update_layout(
        # autosize=False,
        height=1000,
        showlegend=False,
        updatemenus=[
            dict(
                buttons=buttons,
                direction='down',
                x=0.05,
                y=1.0,
                xanchor='right',
                yanchor='bottom',
                font=dict(size=8)
            ),
        ]
    )
    
    fig.show()
    

    enter image description here