Black Lives Matter. Please consider donating to Black Girls Code today.
Dash HoloViews is now available! Check out the docs.

Reactive markers on scatter map box plot to other pathnames/URL

The code below attempts to react to the clicking the marker and invoking the specified Href in the dcc.link to move to another page. Unfortunately, I am unsure how to get the dcc.link to work through the callback function. The app is trying to move to the app1 page. I have tried passing through the href property directly into the dcc.link within the callback too but it doesnt work either.

import numpy as np
import pandas as pd
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output


import  plotly.plotly as py
import plotly.graph_objs as go

from app import app


mapbox_access_token = "pk.eyJ1IjoiamFja3AiLCJhIjoidGpzN0lXVSJ9.7YK6eRwUNFwd3ODZff6JvA"

df = pd.read_csv('lat_lon_counties.csv',encoding='cp1252')
#df_lat_lon['FIPS'] = df_lat_lon['FIPS'].apply(lambda x: str(x).zfill(5))

#app = dash.Dash()


# example measurement stations
lats = [41.434760, 38.436662]
lons = [-105.925030, -88.962141]
text = ['APAC', 'ME']

clickme = html.Div([
    html.Div(id='change_page'),
    dcc.Link(href='/apps/app1')
])

layout = html.Div([
    # map centered to USA
    html.Div([
        dcc.Graph(
            id = "mapbox",
            figure={
                "data": [
                    dict(
                        type = "scattermapbox",
                        lat = lats,
                        lon = lons,
                        mode = "markers",
                        marker = {'size':'8','color':'rgb(255, 0, 0)','opacity':'0.7'},
                        text = text

                    ),

                    dict(
                        type = "scattermapbox",
                        lat = lats,
                        lon = lons,
                        mode = "markers",
                        marker = {'size':'200','color':'rgb(242, 177, 172)','opacity':'0.3'},
                        text = text

                    )
                ],
                "layout": dict(
                    autosize = True,
                    hovermode = "closest",
                    margin = dict(l = 0, r = 0, t = 0, b = 0),
                    mapbox = dict(
                        accesstoken = mapbox_access_token,
                        bearing =0,
                        center = dict(lat = 38.30, lon = -90.68),
                        style = "light",
                        pitch = 0,
                        zoom = 1.5,
                        layers = []
                    )
                )
            },
            style = {"height": "100%"}
        )
    ], style = {"border-style": "solid", "height": "90vh"}),

])

app.callback(
        Output('change_page','children'),
        [Input('mapbox', 'clickData')])
def plot_basin(clickData):
    if clickData is None:
        return {}
    else:
        return app3.clickme #app3 is the name of the current file

if __name__ == "__main__":
    app.run_server(debug=True)

The plot with markers is as below.

hovering over the marker shows the text as well as coordinates but doesnt react to clicking as is expected.

How were you returning the dcc.Link in the callback? Something like that should work, where the link would be within the change_page div, and you can change the href in the link depending on what’s in the callback.

I have a index.py with a dcc.location to update the id=url

I’m still not totally understanding what your specific issue is from your repo, but hopefully a simple example might help clarify things for you

Perhaps the confusion is the layout you’re trying to use isn’t exactly like the standard multi-page layout. Without using the graph, a multi page layout has an index.py similar to what you have, and links that display a full page’s content. Index.py directs people to the different pages using ONLY dcc.Location.

In your case, you want the map to also redirect people. The dcc.Graph component therefore needs to be outside of the page-content div, just like dcc.Location is outside of the div, so your app structure is like this:

dcc.Location(id='url'),
dcc.Graph(id='graph'),
html.Div(id='page-content')

Check out this sample app which allows users to navigate between pages using a graph. You’ll notice there are a few callbacks. One sets the URL based on what a user clicked on, and one sets the page content based on the URL.

I also added a third callback for the graph style. Since the graph is now outside of page-content, it’s always there. To mimic the desired behavior, I simply set the style to hidden when the url isn’t '/'.

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go

fig = go.Figure(
    data=[
        go.Scatter(x=[1, 2, 3], y=[2, 1, 2])
    ],
    layout=go.Layout(title='Multi-Page App With Graph Navigation')
)

app = dash.Dash()

app.layout = html.Div([
    dcc.Location(id='url'),
    html.Div([
        dcc.Graph(id='graph', figure=fig)
    ], id='graph-div'),
    html.Div(id='page-content')
])


@app.callback(
    Output('graph-div', 'style'),
    [Input('url', 'pathname')]
)
def hide_graph(url):
    print({'display': 'block' if url == '/' else 'none'})
    return {'display': 'block' if url == '/' else 'none'}


@app.callback(
    Output('url', 'pathname'),
    [Input('graph', 'clickData')]
)
def change_url(clickData):
    if clickData:
        return '/' + str(clickData['points'][0]['x'])


@app.callback(
    Output('page-content', 'children'),
    [Input('url', 'pathname')]
)
def display_content(url):
    return html.H2('You are on page{}'.format(str(url)))


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

Thank you for your prompt reply.

Maybe if not directly but indirectly through your answer I believe I can draft the question a bit better. Also, sorry about the repo I did not update my master in awhile but it should be good to go now.

my app3 has a map with markers which produces clickData when you click it. now my dcc.location is in my index.py through which my pathnames are updated and we can change the url etc. The index.py is perfectly functional. Now, since my dcc.location is in a different file, we need the dcc.link to update the pathname through id=url between different files (correct me if im wrong). Now the typical example in the docs is that you have something like dcc.Link('Go to App 3', href='/apps/app3') where clicking go to app 3 will invoke changing the pathname through href. But in this particular scenario we have an clickData through mapbox, how can that initiate the dcc.link to ultimately change the URL in dcc.location within index.py.

I tried multiple approaches, once such approach I tried to update the href property of the dcc.link through the callback function. I gave the dcc.link an id=url1. but this didnt work and it would give me an error as

return self.callback_map[target_id]['callback'](*args)
KeyError: 'callback'

My callback looked like

app.callback(
         Output('url1','href'),
         [Input('mapbox', 'clickData')])

def change_page(clickData):
    if clickData:
        return '/apps/app1'