Black Lives Matter. Please consider donating to Black Girls Code today.
Dash Enterprise delivers an incredible 21x cost savings 💸Download the e-book!

Animated Choroplethmapbox?

I’m wondering if it is possible to animate a Choroplethmapbox heat map? If tried to stitch together code based on animating scatter plots but substituting Choroplethmapbox in place of Scatter elements.

Here’s what I have written. It never finishes execution. I’m using plotly v4.6.

Thanks for any help!

-Chris

from urllib.request import urlopen
import json
with urlopen('https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json') as response:
    counties = json.load(response)

state = "New York"
us_county_df = df[(df["Country/Region"] == "United States") & \
                  (df["Province/State"] == state) & \
                  (df.Date > pd.datetime(2020, 3, 21))]
days = np.sort(us_county_df.Date.unique())

fig_frames = []
for day in days:
    day_df = us_county_df[us_county_df.Date == days[0]]
    fig_frames.append([go.Choroplethmapbox(geojson=counties, locations=day_df.FIPS,
                                           z=day_df.Confirmed, colorscale="Viridis",
                                           marker_opacity=0.5, marker_line_width=0)])


day_df = us_county_df[us_county_df.Date == days[0]]
fig = go.Figure(
    data=[go.Choroplethmapbox(geojson=counties, locations=day_df.FIPS,
                              z=day_df.Confirmed, colorscale="Viridis",
                              marker_opacity=0.5, marker_line_width=0)],
    layout=go.Layout(
        mapbox_style="carto-positron",
        mapbox_zoom=3,
        mapbox_center = {"lat": 37.0902, "lon": -95.7129},
        title="Start Title",
        updatemenus=[dict(type="buttons",
                          buttons=[dict(label="Play",
                                        method="animate",
                                        args=[None])])]
    ),
    frames=fig_frames
)
1 Like

For posterity’s sake I’ll share my finished code.

The primary source which helped me was a forum post on a similar issue which I stumbled across while looking for answers to another problem: Animation with slider not moving when pressing 'play'

I also refactored my code to follow the Slider and Buttons example here: https://plotly.com/python/animations/

I’ll note that the reference documentation is pretty slim for how to get sliders and buttons to work together. There’s no explanation for why the example code I mentioned actually works and the Python reference documentation for updatemenus, slider and frames leaves a lot unsaid. After much trial and error, here’s my final code.

import pandas as pd
import numpy as np
import plotly, plotly.graph_objects as go
import datetime as dt
from urllib.request import urlopen
import json

# read US county geojson file
with open(data_path +"us_county_geo.json") as f:
  counties_json = json.load(f)

def numpy_dt64_to_str(dt64):
    day_timestamp_dt = (dt64 - np.datetime64('1970-01-01T00:00:00Z')) / np.timedelta64(1, 's')
    day_dt = dt.datetime.utcfromtimestamp(day_timestamp_dt)
    return day_dt.strftime("%b %d")

plot_var = "ConfirmedPerDate"
us_df = df[(df["Country/Region"] == "United States") & \
           (df.Date > pd.datetime(2020, 3, 21))]
days = np.sort(us_df.Date.unique())
plot_df = us_df[us_df.Date == days[-1]]

fig_data =go.Choroplethmapbox(geojson=counties_json, locations=plot_df.FIPS, 
                              z=np.log10(plot_df[plot_var]),
                              zmin=0,
                              zmax=np.log10(us_df[plot_var].max()),
                              customdata=plot_df[plot_var],
                              name="",
                              text=plot_df.County.astype(str) + ", " + \
                                   plot_df["Province/State"].astype(str),
                              hovertemplate="%{text}<br>Cases: %{customdata}",
                              colorbar=dict(outlinewidth=1,
                                            outlinecolor="#333333",
                                            len=0.9,
                                            lenmode="fraction",
                                            xpad=30,
                                            xanchor="right",
                                            bgcolor=None,
                                            title=dict(text="Cases",
                                                       font=dict(size=14)),
                                            tickvals=[0,1,2,3,4,5,6],
                                            ticktext=["1", "10", "100", "1K", "10K", "100K", "1M"],
                                            tickcolor="#333333",
                                            tickwidth=2,
                                            tickfont=dict(color="#333333",
                                                          size=12)),
                              colorscale="ylorrd", #ylgn
                              #reversescale=True,
                              marker_opacity=0.7,
                              marker_line_width=0)

token = open(data_path + ".mapbox_token").read()
fig_layout = go.Layout(mapbox_style="light",
                       mapbox_zoom=3,
                       mapbox_accesstoken=token,
                       mapbox_center={"lat": 37.0902, "lon": -95.7129},
                       margin={"r":0,"t":0,"l":0,"b":0},
                       plot_bgcolor=None)

fig_layout["updatemenus"] = [dict(type="buttons",
                                  buttons=[dict(label="Play",
                                                method="animate",
                                                args=[None,
                                                      dict(frame=dict(duration=1000,
                                                                      redraw=True),
                                                           fromcurrent=True)]),
                                           dict(label="Pause",
                                                method="animate",
                                                args=[[None],
                                                      dict(frame=dict(duration=0,
                                                                      redraw=True),
                                                           mode="immediate")])],
                                  direction="left",
                                  pad={"r": 10, "t": 35},
                                  showactive=False,
                                  x=0.1,
                                  xanchor="right",
                                  y=0,
                                  yanchor="top")]

sliders_dict = dict(active=len(days) - 1,
                    visible=True,
                    yanchor="top",
                    xanchor="left",
                    currentvalue=dict(font=dict(size=20),
                                      prefix="Date: ",
                                      visible=True,
                                      xanchor="right"),
                    pad=dict(b=10,
                             t=10),
                    len=0.875,
                    x=0.125,
                    y=0,
                    steps=[])

fig_frames = []
for day in days:
    plot_df = us_df[us_df.Date == day]
    frame = go.Frame(data=[go.Choroplethmapbox(locations=plot_df.FIPS,
                                               z=np.log10(plot_df[plot_var]),
                                               customdata=plot_df[plot_var],
                                               name="",
                                               text=plot_df.County.astype(str) + ", " + \
                                                    plot_df["Province/State"].astype(str),
                                               hovertemplate="%{text}<br>%{customdata}")],
                     name=numpy_dt64_to_str(day))
    fig_frames.append(frame)

    slider_step = dict(args=[[numpy_dt64_to_str(day)],
                             dict(mode="immediate",
                                  frame=dict(duration=300,
                                             redraw=True))],
                       method="animate",
                       label=numpy_dt64_to_str(day))
    sliders_dict["steps"].append(slider_step)

fig_layout.update(sliders=[sliders_dict])

# Plot the figure 
fig=go.Figure(data=fig_data, layout=fig_layout, frames=fig_frames)
fig.show(renderer="browser")
2 Likes

awesome, but can you share the datasets too?

I created this account just to say thank you! I was struggling so much with this!