plotlyplotly-dashdash-bootstrap-components

Toggle between different density maps using HTML


I've got two separate figures that use callbacks to filter spatial coordinates. The figures contain a scatter plot and a heat map. Is it possible to incorporate both within a single figure, but use either a Dropdown or RadioItems to select one or the other?

Eventually, the single figure would contain multiple maps to visualise spatial coordinates (scatter, heat map, hex bin, etc.).

import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objs as go
import pandas as pd
import numpy as np

data = pd.DataFrame({
       'Cat': ['t','y','y','y','f','f','j','k','k','k','s','s','s','s'],
       'LAT': [5,6,4,5,4,7,8,9,5,6,18,17,15,16],
       'LON': [10,11,9,11,10,8,8,5,8,7,18,16,16,17],
       })

N = 30
data = pd.concat([data] * N, ignore_index=True)

data['Color'] = data['Cat'].map(dict(zip(data['Cat'].unique(), px.colors.qualitative.Plotly[:len(data['Cat'].unique())])))

Color = data['Color'].unique()

Type_Category = data['Cat'].unique()
Type_cats = dict(zip(Type_Category, Color))


external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]

app = dash.Dash(__name__, external_stylesheets = external_stylesheets)

filtering = html.Div(children=[
    html.Div(children=[
        html.Label('Cats', style = {'paddingTop': '2rem', 'display': 'inline-block'}),
        dcc.Checklist(
            id = 'Cats',
            options = [
                {'label': 't', 'value': 't'},
                {'label': 'y', 'value': 'y'},
                {'label': 'f', 'value': 'f'},
                {'label': 'j', 'value': 'j'},
                {'label': 'k', 'value': 'k'},
                {'label': 's', 'value': 's'},
            ],
            value = ['t', 'y', 'f', 'j', 'k', 's'],
        ),
    ], className = "two columns",
    )
])

app.layout = dbc.Container([

    dbc.Row([
        dbc.Col(html.Div(filtering)),
        dbc.Col([
            dbc.Row([
                dbc.Col(dcc.Graph(id = 'scatter-chart')),
            ]),
        ]),
                dbc.Col(dcc.Graph(id = 'heatmap-chart')),
    ])
], fluid = True)


df = data

@app.callback(
    Output('scatter-chart', 'figure'),
    [Input("Cats", "value")])

def scatter_chart(cats):

    dff = df[df['Cat'].isin(cats)]

    data = px.scatter_mapbox(data_frame = dff,
                                   lat = 'LAT',
                                   lon = 'LON',
                                   color = 'Cat',
                                   color_discrete_map = Type_cats,
                                   zoom = 3,
                                   mapbox_style = 'carto-positron',
                                   )

    fig = go.Figure(data = data)

    return fig


@app.callback(
    Output('heatmap-chart', 'figure'),
    [Input("Cats", "value")])

def heatmap_chart(cats):

    dff = df[df['Cat'].isin(cats)]

    # Creating 2-D grid of features
    [X, Y] = np.meshgrid(dff['LAT'], dff['LON'])

    Z = np.cos(X / 2) + np.sin(Y / 4)

    fig = go.Figure(data =
           go.Densitymapbox(lat = dff['LON'],
                            lon = dff['LAT'],
                            z = Z,
                            )
                    )

    fig.update_layout(mapbox_style = "carto-positron")

    return fig


if __name__ == '__main__':
    app.run_server(debug=True, port = 8051)

Solution

  • As I understand, you can use html.Div and return dcc.Graph inside it. Something as below:

    import dash
    from dash import dcc
    from dash import html
    from dash.dependencies import Input, Output
    import dash_bootstrap_components as dbc
    import plotly.express as px
    import plotly.graph_objs as go
    import pandas as pd
    import numpy as np
    
    data = pd.DataFrame({
           'Cat': ['t','y','y','y','f','f','j','k','k','k','s','s','s','s'],
           'LAT': [5,6,4,5,4,7,8,9,5,6,18,17,15,16],
           'LON': [10,11,9,11,10,8,8,5,8,7,18,16,16,17],
           })
    
    N = 30
    data = pd.concat([data] * N, ignore_index=True)
    
    data['Color'] = data['Cat'].map(dict(zip(data['Cat'].unique(), px.colors.qualitative.Plotly[:len(data['Cat'].unique())])))
    
    Color = data['Color'].unique()
    
    Type_Category = data['Cat'].unique()
    Type_cats = dict(zip(Type_Category, Color))
    
    
    external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
    
    app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
    
    filtering = html.Div(children=[
        html.Div(children=[
            html.Label('Cats', style = {'paddingTop': '2rem', 'display': 'inline-block'}),
            dcc.Checklist(
                id = 'Cats',
                options = [
                    {'label': 't', 'value': 't'},
                    {'label': 'y', 'value': 'y'},
                    {'label': 'f', 'value': 'f'},
                    {'label': 'j', 'value': 'j'},
                    {'label': 'k', 'value': 'k'},
                    {'label': 's', 'value': 's'},
                ],
                value = ['t', 'y', 'f', 'j', 'k', 's'],
            ),
        ],
        )
    ])
    
    app.layout = dbc.Container([
        dbc.Row([
            dbc.Col([
                html.Div(filtering),
                html.Label('Type', style = {'paddingTop': '2rem', 'display': 'inline-block'}),
                dcc.RadioItems(['Scatter', 'Heatmaps'], 'Scatter', inline=True,id='maps'),
            ],width=2),
            dbc.Col([
                html.Div(id='chart')],width=10)
        ])
    ], fluid = True)
    
    df = data
    
    @app.callback(
        Output('chart', 'children'),
        [Input("Cats", "value"),
         Input("maps", "value")])
    
    def scatter_chart(cats,maps):
        if maps == 'Scatter':
            dff = df[df['Cat'].isin(cats)]
            data = px.scatter_mapbox(data_frame = dff,
                                     lat = 'LAT',
                                     lon = 'LON',
                                     color = 'Cat',
                                     color_discrete_map = Type_cats,
                                     zoom = 3,
                                     mapbox_style = 'carto-positron',
                                    )
    
            fig = go.Figure(data = data)
    
        else:
            dff = df[df['Cat'].isin(cats)]
            # Creating 2-D grid of features
            [X, Y] = np.meshgrid(dff['LAT'], dff['LON'])
    
            Z = np.cos(X / 2) + np.sin(Y / 4)
    
            fig = go.Figure(data =
               go.Densitymapbox(lat = dff['LON'],
                                lon = dff['LAT'],
                                z = Z,
                                )
                        )
    
            fig.update_layout(mapbox_style = "carto-positron")
        return dcc.Graph(figure=fig)
    
    if __name__ == '__main__':
        app.run_server(debug=False, port = 8051)
    

    Enter image description here

    Enter image description here