Skip to content
Advertisement

Dash+Plotly Synchronize zoom and pan between two plots using imshow

I try to synchronize zoom and pan between two graphs in a dashboard (dash + plotly). I obtain strange behavior when I zoom on a graph, the second graph does not update. I need to zoom on the second graph to make both graphs update but not with the same zoom nor the same location on the graphs. Furthermore the shapes of the two graphs change.

Below is the code I am in. I do not see I am doing wrong.

import os

from dash import Dash, html, dcc, Input, Output, State
import plotly.express as px
import numpy as np
import rasterio as rio

app2 = Dash(__name__)

data_folder = r'.data'
store = {}

for filename in os.listdir(data_folder):
    if os.path.isfile(os.path.join(data_folder, filename)):
        band_name = filename.replace('.', '_').split(sep='_')[-2]
        with rio.open(os.path.join(data_folder, filename)) as dataset:
            nb_band = dataset.count
            if nb_band == 1:
                data = dataset.read(1)
            else:
                data = dataset.read(tuple(range(1, nb_band + 1)))

            if band_name == 'triband':
                data = np.swapaxes(data, 2, 0)
                data = np.swapaxes(data, 0, 1)
                store[band_name] = data.astype(float)
            else:
                store[f'B{band_name}'] = data.astype(float)

fig1 = px.imshow(store['triband'])
fig1.update_xaxes(showticklabels=False, showgrid=False, zeroline=False)
fig1.update_yaxes(showticklabels=False, showgrid=False, zeroline=False)
fig1.update_layout(
    margin=dict(l=0, r=0, t=0, b=0),
    plot_bgcolor='rgba(0, 0, 0, 0)',
    paper_bgcolor='rgba(0, 0, 0, 0)',
)


# Application structure and content
app2.layout = html.Div(className='main', children=[
    html.H1(children='Hello Dash', style={'padding': 10}),

    html.Div(children=[

        html.Div(children=[
            dcc.Graph(
                id='graph1',
                figure=fig1,
                responsive=True
            )
        ], style={'padding': 5, 'flex': 1}),

        html.Div(children=[
            dcc.Graph(
                id='graph2',
                figure=fig1,
                responsive=True
            )
        ], style={'padding': 5, 'flex': 1})

    ], style={'display': 'flex', 'flex-direction': 'row'}),
])


@app2.callback(Output('graph2', 'figure'),
               Input('graph1', 'relayoutData'),
               State('graph2', 'figure'))
def graph_event1(select_data, fig):
    if select_data is not None:
        try:
            fig['layout']['xaxis']['range'] = [select_data['xaxis.range[0]'], select_data['xaxis.range[1]']],
            fig['layout']['yaxis']['range'] = [select_data['yaxis.range[0]'], select_data['yaxis.range[1]']]
        except KeyError:
            pass
    return fig


@app2.callback(Output('graph1', 'figure'),
               Input('graph2', 'relayoutData'),
               State('graph1', 'figure'))
def graph_event2(select_data,  fig):
    if select_data is not None:
        try:
            fig['layout']['xaxis']['range'] = [select_data['xaxis.range[0]'], select_data['xaxis.range[1]']],
            fig['layout']['yaxis']['range'] = [select_data['yaxis.range[0]'], select_data['yaxis.range[1]']]
        except KeyError:
            pass
    return fig


if __name__ == '__main__':
    app2.run_server(debug=True)

Advertisement

Answer

I found a solution : rather than creating two graphs, I created a graph with several subplots and force zoom and pan between subplots.

fig = make_subplots(rows=1, cols=3, shared_xaxes=True, shared_yaxes=True)
fig.add_trace(
    px.imshow(store['triband']).data[0],
    row=1, col=1
)

fig.add_trace(
    px.imshow(index_store['NDVI']).data[0],
    row=1, col=2
)

fig.add_trace(
    px.imshow(np.where(index_store['NDVI'] >= np.median(index_store['NDVI']),
                       0.8 * np.max(index_store['NDVI']),
                       0.8 * np.min(index_store['NDVI']))
              ).data[0],
    row=1, col=3
)

fig.update_xaxes(matches='x', showticklabels=False, showgrid=False, zeroline=False)
fig.update_yaxes(matches='y', showticklabels=False, showgrid=False, zeroline=False)
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement