Skip to content

Instantly share code, notes, and snippets.

@barronh
Created July 10, 2026 12:45
Show Gist options
  • Select an option

  • Save barronh/1cd8a6b955c980fddcf043ffa8f46682 to your computer and use it in GitHub Desktop.

Select an option

Save barronh/1cd8a6b955c980fddcf043ffa8f46682 to your computer and use it in GitHub Desktop.
Animate AirFuse
__doc__ = """
Requires pycno, pandas, matplotlib, xarray and the airnowcompare.py gist.
pip install pycno pyproj pandas matplotlib xarray
wget -N https://gist.githubusercontent.com/barronh/945df1b465905b0f5454258b317cee1b/raw/airnowcompare.py
"""
import airnowcompare
import pycno
import pandas as pd
import matplotlib.animation as ani
import matplotlib.colors as mc
import matplotlib.pyplot as plt
# Change dates for your case
dates = pd.date_range('2026-06-29', '2026-07-03', freq='1h')
# Based on latest AirNowTech cutpoints
colors = ['#c8ffc8', '#00e400', '#007d00', '#ffffc8', '#ffff00', '#c8c800', '#ffbe78', '#ff7e00', '#c86400', '#ff6464', '#ff0000', '#c80000', '#c896c8', '#8f3f97', '#643264', '#7e0023', '#500019', '#32000f', '#000000']
edges = [3.0, 6.0, 9.1, 15.0, 25.0, 35.5, 40.0, 50.0, 55.5, 75.0, 100.0, 125.5, 150.0, 200.0, 225.5, 325.0, 500.0, 750.0]
cmap, norm = mc.from_levels_and_colors(edges, colors[1:-1])
cmap.set_under(colors[0])
cmap.set_over(colors[-1])
# Create a placeholder figure
fig, ax = plt.subplots(figsize=(6, 4), gridspec_kw=dict(left=.025, right=0.95))
# Define the animation function
def animate(i):
date = dates[i]
print(date)
f = airnowcompare.open_airfuse(dates[i], 'pm25', aqi=False).sel(x=slice(-800, 2000), y=slice(250, 2300))
title = 'EPA AirFuse: PM$_{2.5}$ Concentration' + f'\nDate: {date:%Y-%m-%d Time: %H:%M UTC}'
if len(fig.axes) == 1:
qm = ax.pcolormesh(f.x, f.y, f.pm25[0], norm=norm, cmap=cmap)
fig.colorbar(qm, extend='both', label=r'Concentration [$\mu$g/m3]')
pycno.cno(f.crs_proj4).drawstates(ax=ax, linewidth=1)
ax.set(xticks=[], yticks=[])
fig.text(.025, .98, title, verticalalignment='top')
ax.collections[0].set_array(f['pm25'][0].to_numpy())
fig.texts[0].set_text(title)
# Create animation object
anim = ani.FuncAnimation(fig, animate, frames=range(len(dates)), blit=False, interval=1000)
# Save as mp4
anim.save("AirFuse.mp4", writer="ffmpeg", fps=2)
@barronh

barronh commented Jul 10, 2026

Copy link
Copy Markdown
Author

This was originally developed to include a special label for particular dates. The modifications to add a label are shown below in case they are useful for the future.

     fig.text(.025, .98, title, verticalalignment='top')
+    fig.text(.95, .98, tag, verticalalignment='top', horizontalalignment='right', size=20, fontweight="bold")

   ax.collections[0].set_array(f['pm25'][0].to_numpy())
   fig.texts[0].set_text(title)
+  if date > pd.to_datetime('2026-06-30T14') and date < pd.to_datetime('2026-07-01T21'):
+     fig.texts[1].set_text('Label These Dates')
+   else:
+     fig.texts[1].set_text('')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment