mikkokotila
mikkokotila

Reputation: 1431

How to get zoom level in time series data as callback input in Dash

Given a line graph with time series data in Dash (Plotly) dashboard, when I zoom in and the resolution of the x-axis (timestamp) change, how can I capture that as an input to another callback?

What I want to achieve, is plot-B displaying data based on the zoom in plot-A. When plot-A zooms in, plot-B automatically follows.

Note that plot-A and plot-B are using different dataset, with matching timestamps.

Upvotes: 5

Views: 8310

Answers (4)

Martin
Martin

Reputation: 648

Posting little bit different solution for multiple figures than @asmaier proposed


for p in ['graph1', 'graph2', 'add-your-graphs']:
    @callback(
        Output(p, 'figure'),
        [
            Input('graph', 'relayoutData'),
            State(p, 'figure')
        ]
    )
    def update_zoom(relayout_data, figure, component=p):
        if relayout_data is None:
            raise PreventUpdate

        try:
            figure['layout']["xaxis"]["range"] = [relayout_data['xaxis.range[0]'], relayout_data['xaxis.range[1]']]
            figure['layout']["xaxis"]["autorange"] = False
        except KeyError:
            raise PreventUpdate
        return figure

Upvotes: 0

asmaier
asmaier

Reputation: 11756

Here is an improved version of the callback based on the the answer from @Sachin Dev Sharma:

@app.callback([Output('graph2', 'figure')],
         [Input('graph', 'relayoutData')], # this triggers the event
         [State('graph2', 'figure')])
def zoom_event(relayout_data, *figures):
    outputs = []
    for fig in figures:
        try:
            fig['layout']["xaxis"]["range"] = [relayout_data['xaxis.range[0]'], relayout_data['xaxis.range[1]']]
            fig['layout']["xaxis"]["autorange"] = False
        except (KeyError, TypeError):
            fig['layout']["xaxis"]["autorange"] = True

        outputs.append(fig)

    return outputs

The advantage here is, that we don't overwrite the complete layout of the figure in the callback. We only overwrite the xaxis.range and xaxis.autorange property. Also this solution allows to set the zoom on multiple output figures by accepting multiple figure arguments (see *figures).

Upvotes: 5

Sachin Dev Sharma
Sachin Dev Sharma

Reputation: 219

Sample code for synchronizing the zoom level in plotly dash.

app=dash.Dash()

app.layout = html.Div([
                dcc.Graph(id='graph',figure=fig),
                html.Pre(id='relayout-data', style=styles['pre']),
                dcc.Graph(id='graph2', figure=fig)])

@app.callback(Output('relayout-data', 'children'),
              [Input('graph', 'relayoutData')])
def display_relayout_data(relayoutData):
    return json.dumps(relayoutData, indent=2)


@app.callback(Output('graph2', 'figure'),
             [Input('graph', 'relayoutData')], # this triggers the event
             [State('graph2', 'figure')])
def graph_event(select_data,  fig):
    try:
       fig['layout'] = {'xaxis':{'range':[select_data['xaxis.range[0]'],select_data['xaxis.range[1]']]}}
    except KeyError:
       fig['layout'] = {'xaxis':{'range':[zoomed out value]}}
return fig

app.run_server()

Upvotes: 11

coralvanda
coralvanda

Reputation: 6616

The Dash docs have an answer for this here.

relayoutData

Data from latest relayout event which occurs when the user zooms or pans on the plot or other layout-level edits. Has the form {: } describing the changes made. Read-only.

Unfortunately, as a read-only prop, you will not be able to update the second graph to follow the zoom on the first one.

Upvotes: 0

Related Questions