Using Dash Callback to create an interactive dashboard

Hi everyone, I am very new to plotly, Dash and to be honest, Python as well. I am trying to create an interactive dashboard that shows a marine vessel’s lat/long along with several other factors. I have been using Plotly’s mapbox functions to do this. However, I need to be able to create an interactive version of those figures. I have shared the code below. Everything seems to be running fine but the figure that is supposed to show is not showing. There is no figure at all. Also, the dropdown options are appearing but I can only select one of them and they do not update the figure (since there is no figure).

I hope I was able to explain this well, any help would be appreciated!

BTW

  • “av” is the dataframe being used
  • from “fig_sng” to “return figure” is the figure being drawn
  • “ship” is the value being selected in the dropdown which is used to filter the dataframe
  • “ship” corresponds to a vessel code (i.e. an identifying code for the vessel)
  • “df” is a copy of the dataframe according to the “ship” input
#creating the Dash App for online use

app = dash.Dash()

app.layout = html.Div([
    html.H1(children = 'Voyage Tracker Function',
            style = {
                'textAlign' : 'center'
            }),
    html.Div(children = 'Using Dash to create an online display',
            style = {
                'textAlign' : 'center'
            }),
    html.Div(children = '    ',
            style = {
                'textAlign' : 'center'
            }),        
    html.Div(
        children=[
            dcc.Dropdown(id='my-dropdown', multi=True,
            options=[{'label': x, 'value': 'x'} for x in sorted (av.vessel_code.unique())],
            value=["MT1", "MT2", "MT5", "ST"]),

            html.Button(id='my-button', n_clicks=0, children="Show Chosen Data"),
            dcc.Graph(id='graph-output', figure={}),
        ])
])

@app.callback(
    Output(component_id='graph-ouput', component_property='figure'),
    [Input(component_id='my-dropdown', component_property='value')],
    # Input(component_id='vl', component_property='value'),
    # Input(component_id='vr', component_property='value')],
    prevent_initial_call=False
)

def update_graph(ship):  
    if len(ship) > 0:
        print(n)
        print(f"Viewing Vessel: {ship}")
        print(type(ship))

        df = av[av['vessel_code'].isin([ship])]

        fig_sng = px.line_mapbox(fg_map, 
            lon=fg_map['long_sng'],
            lat=fg_map['lat_sng'],
            center=dict(lat=21, lon=90))
            #middle ground trace
        fig_mg = px.line_mapbox(fg_map, 
            lon=fg_map['long_mg'],
            lat=fg_map['lat_mg'],
            center=dict(lat=21, lon=90))
            #south patch trace
        fig_sp = px.line_mapbox(fg_map, 
            lon=fg_map['long_sp'],
            lat=fg_map['lat_sp'],
            center=dict(lat=21, lon=90))
            #south patch trace
        fig_ssp = px.line_mapbox(fg_map, 
            lon=fg_map['long_ssp'],
            lat=fg_map['lat_ssp'],
            center=dict(lat=21, lon=90))
        fig = px.scatter_mapbox(df, 
            lon=df['long'], 
            lat=df['lat'], 
            color=df['voyage_no'],
            size=df['catch'],
            center=dict(lat=21, lon=90))

        fig.update_layout(mapbox_style="open-street-map")

            #fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0}, 
            #              mapbox=dict(
            #                  pitch=60,
            #                  bearing=30
            #              ))

        fig.update_geos(lataxis_showgrid=True, 
                    lonaxis_showgrid=True,
                    showrivers=True, rivercolor="Blue",
                    showcountries=True)

        figure = go.Figure(data=fig_sng.data + fig_mg.data + fig_sp.data + fig_ssp.data + fig.data, layout = fig.layout)        
        
        return figure

    elif len(ship) == 0:
        raise dash.exception.PreventUpdate

if __name__ == '__main__':
    app.run_server(port=6050)

. . .

Hello @fishmonger,

If this is a direct copy and paste of your code, it looks like your options are not quite setup properly.

It seems that you are setting the values for all options to be ‘x’, this will cause it to not have any information for you to display. :slight_smile:

Hi @jinnyzor,

thank you for replying so quickly. What can I do to fix this issue?

You can try this:

#creating the Dash App for online use

app = dash.Dash()

app.layout = html.Div([
    html.H1(children = 'Voyage Tracker Function',
            style = {
                'textAlign' : 'center'
            }),
    html.Div(children = 'Using Dash to create an online display',
            style = {
                'textAlign' : 'center'
            }),
    html.Div(children = '    ',
            style = {
                'textAlign' : 'center'
            }),        
    html.Div(
        children=[
            dcc.Dropdown(id='my-dropdown', multi=True,
            options=[{'label': x, 'value': x} for x in sorted (av.vessel_code.unique())],
            value=["MT1", "MT2", "MT5", "ST"]
            ),

            html.Button(id='my-button', n_clicks=0, children="Show Chosen Data"),
            dcc.Graph(id='graph-output', figure={}),
        ])
])

@app.callback(
    Output(component_id='graph-ouput', component_property='figure'),
    [Input(component_id='my-dropdown', component_property='value')],
    # Input(component_id='vl', component_property='value'),
    # Input(component_id='vr', component_property='value')],
    prevent_initial_call=False
)

def update_graph(ship):  
    if len(ship) > 0:
        print(n)
        print(f"Viewing Vessel: {ship}")
        print(type(ship))

        df = av[av['vessel_code'].isin([ship])]

        fig_sng = px.line_mapbox(fg_map, 
            lon=fg_map['long_sng'],
            lat=fg_map['lat_sng'],
            center=dict(lat=21, lon=90))
            #middle ground trace
        fig_mg = px.line_mapbox(fg_map, 
            lon=fg_map['long_mg'],
            lat=fg_map['lat_mg'],
            center=dict(lat=21, lon=90))
            #south patch trace
        fig_sp = px.line_mapbox(fg_map, 
            lon=fg_map['long_sp'],
            lat=fg_map['lat_sp'],
            center=dict(lat=21, lon=90))
            #south patch trace
        fig_ssp = px.line_mapbox(fg_map, 
            lon=fg_map['long_ssp'],
            lat=fg_map['lat_ssp'],
            center=dict(lat=21, lon=90))
        fig = px.scatter_mapbox(df, 
            lon=df['long'], 
            lat=df['lat'], 
            color=df['voyage_no'],
            size=df['catch'],
            center=dict(lat=21, lon=90))

        fig.update_layout(mapbox_style="open-street-map")

            #fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0}, 
            #              mapbox=dict(
            #                  pitch=60,
            #                  bearing=30
            #              ))

        fig.update_geos(lataxis_showgrid=True, 
                    lonaxis_showgrid=True,
                    showrivers=True, rivercolor="Blue",
                    showcountries=True)

        figure = go.Figure(data=fig_sng.data + fig_mg.data + fig_sp.data + fig_ssp.data + fig.data, layout = fig.layout)        
        
        return figure

    elif len(ship) == 0:
        raise dash.exception.PreventUpdate

if __name__ == '__main__':
    app.run_server(port=6050)


I’d like to see what you’ve made, I had stuff on the water as well. :wink:

I’d love to show you after I can get it working! In the meantime, could you point out where you’ve made a change here? It seems to have fixed the issue of not being able to select more than one option in the dropdown, but my figure still isn’t showing. :frowning:

All I did was adjust this line.

I’d recommend stepping through and printing the dfs along the way and make sure that it is building the way that you imagine it. (Since we dont have any data to go by)

Also, note, px can use a dataframe and the column names, go is where you have to give it the series. That should adjust some of your errors. You also have fg_map used instead of df this might be an issue too. :slight_smile:

Ok I understand, fg_map is actually a separate dataframe, that I have not shown here. Basically, I had five different datasets. Four of them were used to draw a figure to map out fishing ground boundaries (i.e. purely for reference and aesthetic). And the last one contains all the actual location data of the vessels I’m trying to track. I used go.Figure() to join them all together in one map so that I could see the vessels as well as the fishing grounds.

I’ve tried replacing the px. with go. and it doesn’t seem to work. Forgive me for being clueless btw, I am very new to this. :sweat_smile:

I am attaching a picture of the output. In the place of the cartesian plane, there should be a geomap (using Mapbox) with the coordinates given by my df.

Instead of trying to do it that way, what you could do is build like this:

fig = go.Figure()

fig.add_trace(first layer)
fig.add_trace(second layer)

return fig

Ahh I see. I didn’t think of it this way. Goes to show that I am doing it very inefficiently. Thank you for the tip!

I did want to know though; is my callback setup ok? I want to at least make sure that I can pinpoint the problem.

Yes, it looks like it should be fine, as long as the data is there.

Cant say for 100% obviously. :slight_smile:

Heads up, this is what the output is supposed to look like. The code I’ve put in to create the figure seems to be working fine outside of the callback function.

1 Like

Turn that into a function, and then use the function as you pass variables to it.

Yes, I actually started off by creating a function, but couldn’t understand the syntax of Dash so I simplified it into a figure taking in only the input from the callback. I’ll try connecting the original function I used, using some more inputs. It just gets a bit difficult to keep track of :sweat_smile:

I also couldn’t figure out the way to use line_mapbox and scattermapbox using .add_traces. So I’m not sure if I’ll be able to do it that way. Although I’m not sure if that is where the issue arises in the first place

I still feel like there’s something either super obvious or some minor error that is stopping the figure from showing in the output. I just can’t figure out what. Everything else is working completely fine.

You can call other functions from inside the callback, instead of trying to do it all in the callback. :slight_smile:

Ohhh that makes a lot of sense. I didn’t really understand that until now. How do you connect the output though?

You just need to return from the function:

def createChart(values=None):
     if values:
         ### build fig
         return fig
     return go.figure()

##callback
def updateChart(inputs):
    return chartChart(inputs)