Dear Help,
I started from scratch and learned plotly to make an animation of my data by googling the posts in this community and by asking help here a few time. Thanks to those you who answered my questions in the past.
As you can see from code pasted below, the plot I am trying to make consists of two image subplots, one mapbox plot, one contour&scatter plot, and another bar&scatter plot. When I only have the two image traces and the mapbox trace added to the animation frame, the program works well and all the photos as well as the dots on the mapbox are played correctly frame by frame at a speed controlled by the frame and transition durations. However, as soon as I added another scatter plot (trace 9 in the code below) to the animation frame, the animation goes crazy and keeps flickering. Searching the previous posts here, I realized animating a contour plot can have this behavior. However, I am puzzled why this happens even though I completely left out the contour plot and other traces on the same contour plot from the animation frames.
I am running out of ideas to test this program. I suspect something isnβt setting correctly. Can anyone here please help? Although I would love to make everything animated, I can leave the contour heat map out of the animation if I must do that to have a reasonable animated plot. Much appreciated.
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from PIL import Image, ImageOps
import plotly.express as px
data = pd.read_csv('myData.csv')
maxT = int(data['HighT_F'].max() + 1.0)
minT = int(data['LowT_F'].min())
nrow = data.shape[0]
nT = maxT-minT+1
zArr = np.ones((maxT-minT+1, nrow))
zT = np.arange(minT, maxT+1).reshape(nT, 1)
zArr = np.multiply(zArr, zT)
fig = make_subplots(rows=4, cols=3,
horizontal_spacing=0.01,
vertical_spacing=0.02,
shared_xaxes=False,
column_widths=[0.33, 0.33, 0.34],
specs=[[{'type':'image', 'rowspan':2}, {'type':'image', 'rowspan':2},
{'type':'mapbox', 'rowspan':2}],
[{}, {}, {}],
[{'type':'contour', 'colspan':3}, {}, {}],
[{'type':'xy', 'colspan':3, 'secondary_y':True}, {}, {}]
]
)
imgPOTH=Image.open(data.loc[0, 'POTH'])
fig.add_trace(px.imshow(imgPOTH.resize((800,600) )).data[0], row=1, col=1)
cropSize = (0, 420, 785, 960)
newSize = (785, 540)
imgStrava= Image.open(data.loc[0, 'Strava']).crop(cropSize) #.resize(newSize)
fig.add_trace(px.imshow(imgStrava).data[0], row=1, col=2)
fig.add_trace(go.Scattermapbox(lat=[data.loc[0, 'LAT']],
lon=[data.loc[0, 'LON']],
mode='markers',
marker=dict(size=15, colorscale='bluered',
color=[data.loc[0, 'TR_CNT']],
cmin=1, cmax=6, opacity=0.75,
colorbar=dict(dtick=1, bordercolor='white',
borderwidth=2, x=0.98, y=0.755, len=0.5,
#yanchor='middle', x=0.33, ypad=150
),
),
text=data.loc[0, 'Trail'],
),
row=1, col=3
)
fig.add_trace(go.Contour(z=zArr,
x=np.arange(nrow),
y=list(np.arange(minT, maxT+1)),
colorscale='thermal',
contours_coloring='heatmap',
colorbar=dict(len=0.24, x=0.98, y=0.368, dtick=20),
line_width=0
),
row=3, col=1
)
fig.add_trace(go.Scatter(x=np.arange(nrow),
y=list(data['HighT_F']),
fill='none',
mode='lines',
marker=dict(color='lightblue'),
),
row=3, col=1
)
fig.add_trace(go.Scatter(x=np.arange(nrow),
y=[maxT]*nrow,
fill='tonexty',
fillcolor='lightblue',
mode='lines',
marker=dict(color='lightblue'),
),
row=3, col=1
)
fig.add_trace(go.Scatter(x=np.arange(nrow),
y=list(data['LowT_F']),
fill='tozeroy',
fillcolor='lightblue',
mode='lines',
marker=dict(color='lightblue'),
),
row=3, col=1
)
fig.add_trace(go.Scatter(x=np.arange(nrow),
y=list(data['MS_F']),
mode='markers+lines',
marker=dict(size=10, color='rgb(64, 256, 64)', opacity=0.80),
line=dict(width=3, color='rgb(64, 256, 64)'),
),
row=3, col=1
)
color_name=['sunny', 'cloudy', 'rain', 'snow']
color_vals=np.arange(4)
num_colors=4
colors=data['WCode']%num_colors
trBlue = 'rgba(0,0,255,0.3)'
fig.add_trace(go.Bar(x=np.arange(nrow),
y=list(data['Miles']),
marker=dict(color=colors, colorscale='YlOrRd_r',
colorbar=dict(len=0.24, x=0.98, y=0.12, dtick=4,
ticktext=color_name, tickvals=color_vals
)
),
width=0.6,
text=list(data['Hrs'].astype(str) + ' hrs'),
),
row=4, col=1
)
fig.update_traces(textangle=-90, selector=dict(type='bar'))
fig.add_trace(go.Scatter(x=[0],
y=[data.loc[0, 'NHikers']],
mode='markers+lines',
marker=dict(size=10, color=trBlue),
line=dict(width=3, color=trBlue),
),
secondary_y=True,
row=4, col=1
)
fig.add_trace(go.Scatter(x=[0,800], y=[600, 0], mode='markers', marker_size=0), row=1, col=1)
fig.add_trace(go.Scatter(x=[0,785], y=[540, 0], mode='markers', marker_size=0), row=1, col=2)
frames = []
for r in np.arange(nrow):
poth = Image.open(data.loc[r, 'POTH'])
w, h = poth.size
if r in [43, 51]:
poth = ImageOps.exif_transpose(poth)
#poth = poth.rotate(-90)
w, h = poth.size
if 'Image_NA' in data.loc[r, 'POTH']:
h0 = 600
w0 = 800
dw = w0-w
dh = h0-h
elif h/w <= 0.75:
c = 800.0/w
w = int(w*c)
h = int(h*c)
w0 = w
h0 = int(w*0.75)
dw = w0-w
dh = h0-h
elif h/w > 0.75:
c = 600.0/h
w = int(w*c)
h = int(h*c)
h0 = h
w0 = int(h/0.75)
dw = w0-w
dh = h0-h
poth = poth.resize((w,h))
pad = (dw//2, dh//2, dw-dw//2, dh-dh//2)
poth= ImageOps.expand(poth, pad, fill='lightblue')
strv = Image.open(data.loc[r, 'Strava'])
if 'Image_NA' in data.loc[r, 'Strava']:
w, h = strv.size
h0 = 540
w0 = 785
dw = w0 - w
dh = h0 - h
pad = (dw//2, dh//2, dw-dw//2, dh-dh//2)
strv = ImageOps.expand(strv, pad, fill='lightblue')
else:
cSize= tuple(data.loc[r, ['C0', 'C1', 'C2', 'C3']])
strv = strv.crop(cSize)
fData= [
px.imshow(poth).data[0],
px.imshow(strv).data[0],
go.Scattermapbox(lat=list(data.loc[0:r+1, 'LAT']),
lon=list(data.loc[0:r+1, 'LON']),
marker=dict(color=list(data.loc[0:r+1, 'TR_CNT'])),
text=data.loc[0:r, 'Trail'],
),
go.Scatter(x=np.arange(r+1), y=list(data.loc[0:r+1, 'NHikers'])),
]
fData[0].xaxis = 'x'
fData[0].yaxis = 'y'
fData[1].xaxis = 'x2'
fData[1].yaxis = 'y2'
fData[3].xaxis = 'x9'
fData[3].yaxis = 'y10'
frames.append(go.Frame(data=fData, name=f'fr{r}', traces=[0,1,2,9]))
fig.update(frames=frames)
fig.update_mapboxes(
style='open-street-map',
bearing=0,
center=dict(
lat=38.5,
lon=-111.2,
),
pitch=0,
zoom=8
)
fig.update_xaxes(range=[0,800], zeroline=False, showticklabels=False, showgrid=False, row=1, col=1)
fig.update_xaxes(range=[0,785], zeroline=False, showticklabels=False, showgrid=False, row=1, col=2)
fig.update_xaxes(tickfont=dict(family='Arial', size=100), range=[-1, nrow],
tickmode='auto', fixedrange=False, type='category', showgrid=False,
showticklabels=False, zeroline=True, showline=True, linewidth=2,
linecolor='lightblue', row=3, col=1
)
fig.update_xaxes(tickfont=dict(family='Arial', size=20), range=[-1, nrow],
tickmode='auto', fixedrange=False, type='category', showgrid=False,
tickangle=-45, showticklabels=False, zeroline=True, linewidth=2,
showline=True, linecolor='lightblue', row=4, col=1
)
fig.update_yaxes(range=[600,0], zeroline=False, showticklabels=False, showgrid=False, row=1, col=1)
fig.update_yaxes(range=[540,0], zeroline=False, showticklabels=False, showgrid=False, row=1, col=2)
fig.update_yaxes(tickfont=dict(family='Arial', size=20), title='Fahrenheit',
titlefont=dict(family='Arial', size=24), title_standoff=0,
showgrid=False, anchor='free', position=0, zeroline=True,
linecolor='lightblue', linewidth=2, showline=True, automargin='left',
ticks='outside', row=3, col=1
)
fig.update_yaxes(range=[0, 10], tickfont=dict(family='Arial', size=20), title='miles',
titlefont=dict(family='Arial', size=24), title_standoff=0, showgrid=False,
anchor='free', position=0, zeroline=True, linecolor='lightblue',
linewidth=2, showline=True, automargin='left', secondary_y=False,
ticks='outside', row=4, col=1
)
fig.update_yaxes(range=[0,25], tickfont=dict(family='Arial', size=20, color=trBlue),
title='hikers', titlefont=dict(family='Arial', size=24, color=trBlue),
showgrid=False, zeroline=True, linecolor='lightblue', linewidth=2,
showline=True, secondary_y=True, ticks='outside', row=4, col=1
)
def setFrameArgs(duration):
return {"frame": {"duration": duration},
"mode": "immediate",
"fromcurrent": True,
"transition": {"duration": duration, "easing": "linear"},
}
frDuration = 1000
slider = [
{
'pad': {"b": 10, "t": 50},
'len': 0.85,
'x': 0.045,
'y': 0.044,
'currentvalue': {
'font': {'size': 20, 'color': 'red'},
'prefix': '',
'suffix': '',
'visible': True,
'xanchor': 'center',
},
'steps': [
{
'args': [
[f.name],
setFrameArgs(frDuration),
{'title.text': data.loc[k,'Date'] + ', ' + data.loc[k,'Trail']},
],
'label': data.loc[k,'Date'],
'method': 'animate',
}
for k, f in enumerate(fig.frames)
],
}
]
menus = [
{
'buttons': [
{
'args': [None, setFrameArgs(frDuration)],
'label': "▶", #play symbol
'method': 'animate',
},
{
'args': [[None], setFrameArgs(0)],
'label': "◼", #pause symbol
'method': 'animate',
}
],
'direction': 'left',
'pad': {'r': 10, 't': 70},
'type': 'buttons',
'x': 0.04,
'y': 0.044,
}
]
fig.update_layout(plot_bgcolor="lightblue", showlegend=False,
updatemenus=menus, sliders=slider,
title={'text': data.loc[0,'Date'] + ', ' + data.loc[0,'Trail'],
'font': dict(size=32, color='red', family='Raleway Bold Italic'),
'x': 0.5,
'y': 0.95,
'xanchor': 'center',
'yanchor': 'top',
},
)
for k in np.arange(nrow):
fig.frames[k].layout.update(title_text=data.loc[k,'Date'] + ', ' + data.loc[k,'Trail'])
fig.write_html('test.html', full_html=True, include_plotlyjs=True)
fig.show()
exit()