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.
JavaScript
x
94
94
1
import os
2
3
from dash import Dash, html, dcc, Input, Output, State
4
import plotly.express as px
5
import numpy as np
6
import rasterio as rio
7
8
app2 = Dash(__name__)
9
10
data_folder = r'.data'
11
store = {}
12
13
for filename in os.listdir(data_folder):
14
if os.path.isfile(os.path.join(data_folder, filename)):
15
band_name = filename.replace('.', '_').split(sep='_')[-2]
16
with rio.open(os.path.join(data_folder, filename)) as dataset:
17
nb_band = dataset.count
18
if nb_band == 1:
19
data = dataset.read(1)
20
else:
21
data = dataset.read(tuple(range(1, nb_band + 1)))
22
23
if band_name == 'triband':
24
data = np.swapaxes(data, 2, 0)
25
data = np.swapaxes(data, 0, 1)
26
store[band_name] = data.astype(float)
27
else:
28
store[f'B{band_name}'] = data.astype(float)
29
30
fig1 = px.imshow(store['triband'])
31
fig1.update_xaxes(showticklabels=False, showgrid=False, zeroline=False)
32
fig1.update_yaxes(showticklabels=False, showgrid=False, zeroline=False)
33
fig1.update_layout(
34
margin=dict(l=0, r=0, t=0, b=0),
35
plot_bgcolor='rgba(0, 0, 0, 0)',
36
paper_bgcolor='rgba(0, 0, 0, 0)',
37
)
38
39
40
# Application structure and content
41
app2.layout = html.Div(className='main', children=[
42
html.H1(children='Hello Dash', style={'padding': 10}),
43
44
html.Div(children=[
45
46
html.Div(children=[
47
dcc.Graph(
48
id='graph1',
49
figure=fig1,
50
responsive=True
51
)
52
], style={'padding': 5, 'flex': 1}),
53
54
html.Div(children=[
55
dcc.Graph(
56
id='graph2',
57
figure=fig1,
58
responsive=True
59
)
60
], style={'padding': 5, 'flex': 1})
61
62
], style={'display': 'flex', 'flex-direction': 'row'}),
63
])
64
65
66
@app2.callback(Output('graph2', 'figure'),
67
Input('graph1', 'relayoutData'),
68
State('graph2', 'figure'))
69
def graph_event1(select_data, fig):
70
if select_data is not None:
71
try:
72
fig['layout']['xaxis']['range'] = [select_data['xaxis.range[0]'], select_data['xaxis.range[1]']],
73
fig['layout']['yaxis']['range'] = [select_data['yaxis.range[0]'], select_data['yaxis.range[1]']]
74
except KeyError:
75
pass
76
return fig
77
78
79
@app2.callback(Output('graph1', 'figure'),
80
Input('graph2', 'relayoutData'),
81
State('graph1', 'figure'))
82
def graph_event2(select_data, fig):
83
if select_data is not None:
84
try:
85
fig['layout']['xaxis']['range'] = [select_data['xaxis.range[0]'], select_data['xaxis.range[1]']],
86
fig['layout']['yaxis']['range'] = [select_data['yaxis.range[0]'], select_data['yaxis.range[1]']]
87
except KeyError:
88
pass
89
return fig
90
91
92
if __name__ == '__main__':
93
app2.run_server(debug=True)
94
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.
JavaScript
1
22
22
1
fig = make_subplots(rows=1, cols=3, shared_xaxes=True, shared_yaxes=True)
2
fig.add_trace(
3
px.imshow(store['triband']).data[0],
4
row=1, col=1
5
)
6
7
fig.add_trace(
8
px.imshow(index_store['NDVI']).data[0],
9
row=1, col=2
10
)
11
12
fig.add_trace(
13
px.imshow(np.where(index_store['NDVI'] >= np.median(index_store['NDVI']),
14
0.8 * np.max(index_store['NDVI']),
15
0.8 * np.min(index_store['NDVI']))
16
).data[0],
17
row=1, col=3
18
)
19
20
fig.update_xaxes(matches='x', showticklabels=False, showgrid=False, zeroline=False)
21
fig.update_yaxes(matches='y', showticklabels=False, showgrid=False, zeroline=False)
22