Skip to content
Advertisement

Synchronous zoom of four graphs in Dash. How to implement?

Good afternoon everyone! 🙂

What is the problem. I have 4 plots and the challenge is to keep them scaling in sync (i.e. if you zoom in on 1 plot, then the rest of the plots are zoomed in as well, etc.).

Now all I have done is that I can zoom in on one particular graph (‘graph1’) and the rest are zoomed in with it. This is what it looks like.

@app.callback(
              Output('graph1', 'figure'), Output('graph2', 'figure'), Output('graph3', 'figure'),Output('graph4', 'figure'),
              Input('graph1', 'relayoutData'),
              State('graph1', 'figure'), State('graph2', 'figure'), State('graph3', 'figure'), State('graph4', '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) as e:
            fig['layout']["xaxis"]["autorange"] = True

        outputs.append(fig)
    return outputs

If you try to do something that looks like synchronizing charts, you will get countless errors. I tried to make 2 figures with subplots, and even one figure with 4 subplots, but all in vain.

One of the latter was that outputs accept more inputs than there are outputs. But I haven’t figured it out specifically in this error. I tried to do so.

@app.callback(
Output('graph1', 'figure'), Output('graph2', 'figure'), Output('graph3', 'figure'), Output('graph4', 'figure'),
Input('graph1', 'relayoutData'), Input('graph2', 'relayoutData'), Input('graph3', 'relayoutData'), Input('graph4', 'relayoutData'), 
State('graph1', 'figure'), State('graph2', 'figure'), State('graph3', 'figure'), State('graph4', 'figure')
)

Thank you if you dare to help.

For searches, I recommend going here Dygraphs – synchronous zooming

Here the participant solved a rather similar problem (but it is written in javascript, I will be glad to hear suggestions on how this can be combined)

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

Advertisement

Answer

I fought with this for a while and this was the solution I eventually came up with.

In my use case I’m dynamically adding graphs by

dcc.Graph({'type': 'graph', 'index': index_counter})

Linking the zoom behavior

@app.callback(
    Output({'type': 'graph', 'index': ALL}, 'relayoutData'),
    Output({'type': 'graph', 'index': ALL}, 'figure'),
    Input({'type': 'graph', 'index': ALL}, 'relayoutData'),
    State({'type': 'graph', 'index': ALL}, 'figure'))
def LinkedZoom(relayout_data, figure_states):
    unique_data = None
    for data in relayout_data:
      if relayout_data.count(data) == 1:
        unique_data = data
    if unique_data:
      for figure_state in figure_states:
        if unique_data.get('xaxis.autorange'):
          figure_state['layout']['xaxis']['autorange'] = True
          figure_state['layout']['yaxis']['autorange'] = True
        else:
          figure_state['layout']['xaxis']['range'] = [
              unique_data['xaxis.range[0]'], unique_data['xaxis.range[1]']]
          figure_state['layout']['xaxis']['autorange'] = False
          figure_state['layout']['yaxis']['range'] = [
              unique_data['yaxis.range[0]'], unique_data['yaxis.range[1]']]
          figure_state['layout']['yaxis']['autorange'] = False
      return [unique_data] * len(relayout_data), figure_states
    return relayout_data, figure_states

Reference commit: https://github.com/djhedges/exit_speed/commit/863efbf00250c79663dd460f6ab2bd54e051b9d7

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement