06a: Using Matplotlib to Create Animation

This notebooks shows how to create an animation that will run and play inside a notebook. The animation can also be written to a video file.

[1]:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
from IPython.display import HTML

import cartopy
import cartopy.crs as ccrs
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 6
      2 import numpy as np
      3 import matplotlib.animation as animation
      4 from IPython.display import HTML
      5
----> 6 import cartopy
      7 import cartopy.crs as ccrs

ModuleNotFoundError: No module named 'cartopy'
[2]:
def get_y(x, a, b, c, d):
    y = a * np.sin(b * x + c) + d
    return y

fig, ax = plt.subplots()
x = np.linspace(0, 2 * np.pi, 100)

a = 1
b = 1
c = 0
d = 0
y0 = get_y(x, a, b, c, d)

line = ax.plot(x, y0, 'b-', clip_on=False)
line = line[0]

xd = np.pi
yd = get_y(xd, a, b, c, d)
dot = ax.plot(xd, yd, 'ro', ms=10, clip_on=False)
dot = dot[0]

ax.set_xlim(0, 2 * np.pi)
ax.set_ylim(-a, a)

frames = 50

def update(i):

    dt = 2 * np.pi / frames
    c = i * dt

    # for each frame, update the data stored on each artist.
    y = get_y(x, a, b, c, d)
    line.set_xdata(x)
    line.set_ydata(y)

    yd = get_y(xd, a, b, c, d)
    dot.set_ydata([yd])

    return line,

ani = animation.FuncAnimation(fig=fig, func=update, frames=frames)
plt.close()


HTML(ani.to_jshtml())
[2]:
[3]:
# the following can be used to save the animation to a file
ext = "mp4" # mac mp4
# ext = "avi" # windows avi
ani.save(f"sin_animation.{ext}")

a map example

Plotting data on maps

Creating some data that spans the whole globe.

[4]:
lon, lat = np.mgrid[-180:181, -90:91]

# build a fake dataset that shifts through n time steps
n = 45
data = np.array([2 * np.sin(3 * np.deg2rad(lon+dt)) + 3 * np.cos(4 * np.deg2rad(lat+dt))
                 for dt in np.arange(0,n)])
[5]:
# let's check the first time step
plt.pcolormesh(lon, lat, data[0])
plt.colorbar();
../../_images/notebooks_part0_python_intro_06b_matplotlib_animation_8_0.png

Now let’s animate it!

[6]:
fig, ax = plt.subplots()
mesh = ax.pcolormesh(lon, lat, data[0])
fig.colorbar(mesh, orientation='vertical')

def animate(i):
    h = data[i]
    mesh.set_array(h)
    title = ax.set_title(f'Year {i+1}')
    return mesh

ani = animation.FuncAnimation(
    fig,
    animate,
    frames=data.shape[0],
    # save_count=data.shape[0],
    # interval=500,
)
plt.close()

HTML(ani.to_jshtml())
[6]:

Geospatially

Plotting data on a Cartesian grid is equivalent to plotting data in the PlateCarree projection, where meridians and parallels are all straight lines with constant spacing. As a result of this simplicity, global datasets we use often begin in the PlateCarree projection.

We can plot these data values as a contour on a GeoAxes map. To do so, we must specify the transform keyword argument, which specifies the projection type used by our data. The transform keyword can be given to all matplotlib plotting methods. The projection type specified in pcolormesh will be transformed into the projection type specified in the subplot method.

[7]:
fig = plt.figure(figsize=(11, 8.5))
ax = plt.subplot(1, 1, 1, projection=ccrs.Orthographic(central_latitude=45))
ax.coastlines()
mesh = ax.pcolormesh(lon, lat, data[0], transform=ccrs.PlateCarree())
plt.colorbar(mesh, orientation='horizontal', pad=.05);
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[7], line 2
      1 fig = plt.figure(figsize=(11, 8.5))
----> 2 ax = plt.subplot(1, 1, 1, projection=ccrs.Orthographic(central_latitude=45))
      3 ax.coastlines()
      4 mesh = ax.pcolormesh(lon, lat, data[0], transform=ccrs.PlateCarree())
      5 plt.colorbar(mesh, orientation='horizontal', pad=.05);

NameError: name 'ccrs' is not defined
<Figure size 1100x850 with 0 Axes>

Class Activity

Animate the above GeoAxes in the way we had done with the simpler graph previously.

Bonus: How does it work if you switch over to using contourf instead?

[ ]:

[ ]:

[ ]: