Animations In VCS

This Notebook demonstrates how to create animations in VCS

Animations are rendered via FFMPEG

The CDAT software was developed by LLNL. This tutorial was written by Charles Doutriaux. This work was performed under the auspices of the U.S. Department of Energy by Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344.

Download the Jupyter notebook

Preparing The Notebook

Back to top

We need some sample data

In [1]:
import requests
r = requests.get("https://uvcdat.llnl.gov/cdat/sample_data/clt.nc",stream=True)
with open("clt.nc","wb") as f:
    for chunk in r.iter_content(chunk_size=1024):
        if chunk:  # filter local_filename keep-alive new chunks
            f.write(chunk)

Example 1: Simple 2D data animation

In this example we will show a simple 2D animation

Back to top

Prepare data

Back to Top

In [2]:
import cdms2
f=cdms2.open("clt.nc")
clt = f("clt")

Create Frames

Back to top

In [3]:
# make directory for pngs if not present
import os
if not os.path.exists("pngs"):
    os.makedirs("pngs")
    
import vcs
x=vcs.init(bg=True)

for i in range(clt.shape[0]):
    x.clear()
    x.plot(clt[i])
    x.png("pngs/2d_%s.png" % str(i).zfill(6))

Create Animation

Back to top

In [4]:
import glob
name = "2d_animation.mp4"

# Create animation into file
x.ffmpeg(name,sorted(glob.glob("pngs/2d*png")))
Out[4]:

Example 2: 1D data moving behind a 1D target data

In this example we will show a fix 1D target dataset and a moving red dataset in the back

Back to top

Let's prepare some data

Back to Top

In [5]:
import cdutil
import numpy
# Open data file
f=cdms2.open("clt.nc")

# reads in data
s=f("clt")

# Computes time serie
ts=cdutil.averager(s,axis="xy")

# remove the mean
mean = ts.mean()
ts -= mean

# create some random data with some extra points to fake the move
Nextra = 120
ts2 = numpy.random.rand(len(ts)+Nextra)*4.-2.

# When plotting we will get data for 2 extra years than our target grid
# Prepare the "faxe time" axis
fake_time_axis = cdms2.createAxis(numpy.arange(-12,132))
fake_time_axis.designateTime()
fake_time_axis.id = "time"
fake_time_axis.units="months since 1979"

Prepare graphic methods and templates

Back to top

In [6]:
# Create a "blank" template
blank = vcs.createtemplate()
blank.blank()
blank.data.priority=1 # turn only data area

# Create the "black" 1D line
black = vcs.create1d()
black.linecolor ="black"
black.markercolor = [0,0,0,0]
black.linewidth = 4
black.datawc_y1= -3
black.datawc_y2 = 3
black.datawc_x1 = -12
black.datawc_x2 = 132

# And from it let's create the "red" moving one
red = vcs.create1d(source=black.name)
red.linecolor="red"
red.linewidth=2
red.linetype = "dot"

Create each frame/png

Back to top

In [7]:
x=vcs.init(bg=True)
for i in range(Nextra-24):
    # Get some subset of data
    tsub = cdms2.MV2.array(ts2[i:i+len(ts)+24]) # Two years worth of extra data
    # Aplly fake axis to make it look like it moves
    tsub.setAxis(0,fake_time_axis)
    # clear and plot
    x.clear()
    x.plot(ts,black)
    x.plot(tsub,red,blank)
    # save data to a png file
    x.png("pngs/1d_%s.png" % str(i).zfill(6))

Create animation

Back to top

In [8]:
name = "1d_animation.mp4"

# Create animation into file
x.ffmpeg(name,sorted(glob.glob("pngs/1d*png")))
Out[8]:

Slowing Down

Back to top

In [9]:
import glob
name = "1d_animation_slow.mp4"

# Create animation into file
x.ffmpeg(name,sorted(glob.glob("pngs/1d*png")),rate=2)
Out[9]:

Speeding up the animation

Back to top

In [10]:
import glob
name = "1d_animation_fast.mp4"

# Create animation into file
x.ffmpeg(name,sorted(glob.glob("pngs/1d*png")),rate=200)
Out[10]:

Example 3: Rotating Globe

In this example we will display a rotating earth Back to top

Preparing the data

Back to top

In [11]:
f=cdms2.open("clt.nc")
clt=f("clt",time=slice(0,1),longitude=(0,361),squeeze=1) # Read the time slice and squeeze it out

Preparing the vcs objects

Back to top

In [12]:
# create new canvas
x = vcs.init(bg=True)

# Create projection
polar = vcs.createprojection("rotate")
polar.type = "orthographic"
polar.centerlongitude=0.
polar.centerlatitude=45.

# Create isofill method
iso = vcs.createisofill()
iso.levels = [a for a in range(0,110,10)]
iso.fillareacolors = vcs.getcolors(iso.levels)

iso.projection = polar

Preparing the images

Back to top

In [13]:
for centerLongitude in range(0,366,5):
    polar.centerlongitude = centerLongitude
    x.clear()
    x.plot(clt,iso)
    x.png("pngs/rotate_%s.png" % str(centerLongitude).zfill(6))

Creating the animation

Back to top

In [14]:
name = "rot_animation.mp4"

# Create animation into file
print("files used",glob.glob("pngs/rotate_*png"))
x.ffmpeg(name,sorted(glob.glob("pngs/rotate_*png")), rate=15)
files used ['pngs/rotate_000215.png', 'pngs/rotate_000135.png', 'pngs/rotate_000350.png', 'pngs/rotate_000075.png', 'pngs/rotate_000065.png', 'pngs/rotate_000090.png', 'pngs/rotate_000290.png', 'pngs/rotate_000230.png', 'pngs/rotate_000260.png', 'pngs/rotate_000115.png', 'pngs/rotate_000050.png', 'pngs/rotate_000130.png', 'pngs/rotate_000040.png', 'pngs/rotate_000150.png', 'pngs/rotate_000035.png', 'pngs/rotate_000245.png', 'pngs/rotate_000330.png', 'pngs/rotate_000250.png', 'pngs/rotate_000185.png', 'pngs/rotate_000270.png', 'pngs/rotate_000010.png', 'pngs/rotate_000055.png', 'pngs/rotate_000285.png', 'pngs/rotate_000280.png', 'pngs/rotate_000180.png', 'pngs/rotate_000120.png', 'pngs/rotate_000200.png', 'pngs/rotate_000025.png', 'pngs/rotate_000085.png', 'pngs/rotate_000335.png', 'pngs/rotate_000320.png', 'pngs/rotate_000310.png', 'pngs/rotate_000015.png', 'pngs/rotate_000105.png', 'pngs/rotate_000220.png', 'pngs/rotate_000160.png', 'pngs/rotate_000295.png', 'pngs/rotate_000195.png', 'pngs/rotate_000210.png', 'pngs/rotate_000125.png', 'pngs/rotate_000300.png', 'pngs/rotate_000000.png', 'pngs/rotate_000255.png', 'pngs/rotate_000225.png', 'pngs/rotate_000170.png', 'pngs/rotate_000155.png', 'pngs/rotate_000175.png', 'pngs/rotate_000110.png', 'pngs/rotate_000165.png', 'pngs/rotate_000240.png', 'pngs/rotate_000100.png', 'pngs/rotate_000235.png', 'pngs/rotate_000145.png', 'pngs/rotate_000140.png', 'pngs/rotate_000325.png', 'pngs/rotate_000045.png', 'pngs/rotate_000205.png', 'pngs/rotate_000080.png', 'pngs/rotate_000005.png', 'pngs/rotate_000340.png', 'pngs/rotate_000190.png', 'pngs/rotate_000095.png', 'pngs/rotate_000070.png', 'pngs/rotate_000365.png', 'pngs/rotate_000345.png', 'pngs/rotate_000355.png', 'pngs/rotate_000265.png', 'pngs/rotate_000305.png', 'pngs/rotate_000030.png', 'pngs/rotate_000060.png', 'pngs/rotate_000275.png', 'pngs/rotate_000315.png', 'pngs/rotate_000360.png', 'pngs/rotate_000020.png']
Out[14]:

Controlling

Back to Top

But it is rotating THE WRONG WAY!!!*

Why?

FFMPEG uses the images passed by the list in the order passed.

Look above the order is with growing centerLongitude

Let's fix this by passing the image sequence in reversed order

In [15]:
name = "rot_animation_correct_order.mp4"

# Create animation into file
x.ffmpeg(name,sorted(glob.glob("pngs/rotate_*png"))[::-1], rate=15)
Out[15]:

Animating Globe and Data

Back to Top

In [16]:
# Create pngs
clt = f("clt",longitude=(0,361))
for i,centerLongitude in enumerate(range(0,361,4)):
    polar.centerlongitude = centerLongitude
    x.clear()
    x.plot(clt[i],iso)
    x.png("pngs/all_%s.png" % str(centerLongitude).zfill(6))
In [17]:
name = "rot_animation_all.mp4"

# Create animation into file
x.ffmpeg(name,sorted(glob.glob("pngs/all_*png"))[::-1], rate=10)
Out[17]: