Satellite Animations

Unidata Logo

Making a Satellite Animation

Unidata Python Workshop


Questions

  1. How are animations made?

This is just a quick taste of producing an animation using matplotlib. The animation support in matplotlib is robust, but sometimes installation of the underlying tool (ffmpeg) can be a little tricky. In order to make sure we get don't get bogged down, this is really more of a demo than something expected to work out of the box.

Conda-forge has packages, so it may be as easy as:

In [1]:
#!conda install -y -n unidata-workshop -c conda-forge ffmpeg

First, we'll import the animation support from matplotlib. We also tell it that we want it to render the animations to HTML using the HTML5 video tag:

In [2]:
import os.path
import sys

from datetime import datetime

import cartopy.feature as cfeature
import matplotlib as mpl
import matplotlib.pyplot as plt
import metpy

from IPython.display import HTML
from matplotlib.animation import ArtistAnimation
from metpy.plots import add_timestamp, colortables
from siphon.catalog import TDSCatalog

We create the base figure, then we loop over a bunch of the datasets to create an animation. For each one we pull out the data and plot both the timestamp and the image. The ArtistAnimation class takes the Figure instance and a list as required arguments. The contents of this list are a collection of matplotlib artists for each frame of the animation. In the loop below, we populate this list with the Text instance created when adding the timestamp as well as the image that results from plotting the data.

In [3]:
mpl.rcParams['animation.embed_limit'] = 50

# List used to store the contents of all frames. Each item in the list is a tuple of
# (image, text)
artists = []

case_date = datetime(2017, 9, 9)
channel = 8

# Get the IRMA case study catalog
cat = TDSCatalog('http://thredds.ucar.edu/thredds/catalog/casestudies/irma'
                 f'/goes16/Mesoscale-1/Channel{channel:02d}/{case_date:%Y%m%d}/'
                 'catalog.xml')
    
datasets = cat.datasets.filter_time_range(datetime(2017, 9, 9), datetime(2017, 9, 9, 6))

# Grab the first dataset and make the figure using its projection information
ds = datasets[0]
ds = ds.remote_access(use_xarray=True)
dat = ds.metpy.parse_cf('Sectorized_CMI')
proj = dat.metpy.cartopy_crs

fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(1, 1, 1, projection=proj)
plt.subplots_adjust(left=0.005, bottom=0.005, right=0.995, top=0.995, wspace=0, hspace=0)
ax.add_feature(cfeature.COASTLINE.with_scale('50m'), linewidth=2)
ax.add_feature(cfeature.BORDERS.with_scale('50m'), linewidth=2)

wv_norm, wv_cmap = colortables.get_with_range('WVCIMSS_r', 195, 265)

# Loop over the datasets and make the animation
for ds in datasets[::-6]:

    # Open the data
    ds = ds.remote_access(use_xarray=True)
    dat = ds.metpy.parse_cf('Sectorized_CMI')
    
    # Pull out the image data, x and y coordinates, and the time. Also go ahead and
    # convert the time to a python datetime
    x = dat['x']
    y = dat['y']
    timestamp = datetime.strptime(ds.start_date_time, '%Y%j%H%M%S')
    img_data = ds['Sectorized_CMI']

    # Plot the image and the timestamp. We save the results of these plotting functions
    # so that we can tell the animation that these two things should be drawn as one
    # frame in the animation
    im = ax.imshow(dat, extent=(x.min(), x.max(), y.min(), y.max()), origin='upper',
                   cmap=wv_cmap, norm=wv_norm)

    text_time = add_timestamp(ax, timestamp, pretext=f'GOES-16 Ch.{channel} ',
                              high_contrast=True, fontsize=16, y=0.01)
    
    # Stuff them in a tuple and add to the list of things to animate
    artists.append((im, text_time))

# Create the animation--in addition to the required args, we also state that each
# frame should last 200 milliseconds
anim = ArtistAnimation(fig, artists, interval=200., blit=False)
anim.save('GOES_Animation.mp4')
HTML(anim.to_jshtml())
Out[3]: