Flickering animation, animation speed no longer controlled by frame duration and transition duration etc

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': "&#9654;", #play symbol
                       'method': 'animate',
                      },
                      {
                       'args': [[None], setFrameArgs(0)],
                       'label': "&#9724;", #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()

Hey @jimy, this is a lot of code to read- maybe the reason that nobody answered yet.

Maybe you could include some images/gif to explain whats happening. Also, you could think about including your data (or equivalent data) so that people could play with your code.

Hi @AIMPED, thank you for the response. Yes, I realize this is too much for others to read. I sort of resolve my problem by just separating the figure to two figures doing different things. I ran into another issue that the hover text don’t display when mixing one Scatter plot with a mapbox in two subplot panels. It works fine if I don’t draw the mapbox. I think someone else reported the same issue a while ago but no one responded. It is so strange this hover text issue did not show up when I had images, scatter plots and mapbox etc all in one figure.

Although the problems I have are no longer a big issue, it might be helpful to the community if I show the simplified programs with the data. So I will try to do that at some point after I am all done with my project.

Much appreciated.