Visualize and Publish with Python

Overview

Teaching: 0 min
Exercises: 0 min
Questions
  • How to create animation plots and publish them on the web?

Objectives
  • Learn how to create simple animations with python

  • Learn to publish your python notebook on the web (gist and nbviewer)

Save your animations in mp4

We are taking one of our first example where we plot the ECMWF ERA-Interim Vorticity over a pre-defined geographical area.

To avoid downloading large datasets on your laptop, we use one frame only and randomly “perturb” the Vorticity field to demonstrate how to create and save your animations in python:

%matplotlib inline
def drawmap(ax,map,x,y,VO, cmap, bounds, norm, title):
    
    ax.set_title(title, fontsize=14)

    map.drawcoastlines()
    map.fillcontinents(color='#ffe2ab')
# draw parallels and meridians.
    map.drawparallels(np.arange(-90.,91.,20.))
    map.drawmeridians(np.arange(-180.,181.,10.))
    map.drawparallels(np.arange(-90.,120.,30.),labels=[1,0,0,0])
    cs = map.contourf(x,y,VO, cmap=cmap, norm=norm, levels=bounds,shading='interp', zorder=1, ax=ax)

    return cs
    
def myanimate(i, ax, map, x,y,VO, cmap, bounds, norm):
    ax.clear()
    # change VO (randomly...)
    VO += 0.1 * np.random.randn()
    new_contour = drawmap(ax,map,x,y,VO, cmap, bounds, norm, 'ECMWF ERA-Interim VO at 850 hPa: Frame %03d'%(i) ) 
    return new_contour
	
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib import colors as c
import matplotlib.animation as animation
%matplotlib inline
from mpl_toolkits.basemap import Basemap, shiftgrid
import numpy as np
import netCDF4


FFMpegWriter = animation.writers['ffmpeg']
metadata = dict(title='ECMWF ERA-Interim VO at 850 hPa from 2001-06-01 00:00', artist='Carpentry@UIO',
                comment='Movie for ECMWF ERA-Interim VO at 850 hPa from 2001-06-01 00:00')
writer = FFMpegWriter(fps=20, metadata=metadata)


f = netCDF4.Dataset('EI_VO_850hPa_Summer2001.nc', 'r')
lats = f.variables['lat'][:]
lons = f.variables['lon'][:]
VO = f.variables['VO'][0,0,:,:]*100000  # read first time and unique level
fig = plt.figure(figsize=[12,15])  # a new figure window
ax = fig.add_subplot(1, 1, 1)  # specify (nrows, ncols, axnum)


map = Basemap(projection='merc',llcrnrlat=38,urcrnrlat=76,\
            llcrnrlon=-65,urcrnrlon=30, resolution='c', ax=ax)
    
map.drawmeridians(np.arange(-180.,180.,60.),labels=[0,0,0,1])
# make a color map of fixed colors
cmap = c.ListedColormap(['#00004c','#000080','#0000b3','#0000e6','#0026ff','#004cff',
                         '#0073ff','#0099ff','#00c0ff','#00d900','#33f3ff','#73ffff','#c0ffff', 
                         (0,0,0,0),
                         '#ffff00','#ffe600','#ffcc00','#ffb300','#ff9900','#ff8000','#ff6600',
                         '#ff4c00','#ff2600','#e60000','#b30000','#800000','#4c0000'])
bounds=[-200,-100,-75,-50,-30,-25,-20,-15,-13,-11,-9,-7,-5,-3,3,5,7,9,11,13,15,20,25,30,50,75,100,200]
norm = c.BoundaryNorm(bounds, ncolors=cmap.N) # cmap.N gives the number of colors of your palette
    

# shift data so lons go from -180 to 180 instead of 0 to 360.
VO,lons = shiftgrid(180.,VO,lons,start=False)
llons, llats = np.meshgrid(lons, lats)
x,y = map(llons,llats)

first_contour = drawmap(ax,map,x,y,VO,cmap, bounds, norm, 'ECMWF ERA-Interim VO at 850 hPa 2001-06-01 00:00' ) 

## make a color bar
fig.colorbar(first_contour, cmap=cmap, norm=norm, boundaries=bounds, ticks=bounds, ax=ax, orientation='horizontal')

ani = animation.FuncAnimation(fig, myanimate, frames=np.arange(50), 
    fargs=(ax, map, x,y,VO, cmap, bounds, norm), interval=50)
ani.save("writer_ECMWF_EI_VO_850hPa_2001060100.mp4")

f.close()

Embedded animations within your jupyter notebook

The main goal here is to create animations embedded within your jupyter notebook. This is fairly simple to plot your animation within your jupyter notebook.

Let’s continue our previous example, and add the following:

from IPython.display import HTML

HTML(ani.to_html5_video())

Make your jupyter notebook interactive with Jupyter Widgets

Instead of creating a movie, you can allow users to select themselves which plots to show:

%matplotlib inline
def drawmap(llons,llats,VO, title):
    
    
    fig = plt.figure(figsize=[12,15])  # a new figure window
    ax = fig.add_subplot(1, 1, 1)  # specify (nrows, ncols, axnum)


    map = Basemap(projection='merc',llcrnrlat=38,urcrnrlat=76,\
            llcrnrlon=-65,urcrnrlon=30, resolution='c', ax=ax)
    
    ax.set_title(title, fontsize=14)

    map.drawcoastlines()
    map.fillcontinents(color='#ffe2ab')
# draw parallels and meridians.
    map.drawparallels(np.arange(-90.,91.,20.))
    map.drawmeridians(np.arange(-180.,181.,10.))
    map.drawparallels(np.arange(-90.,120.,30.),labels=[1,0,0,0])
# make a color map of fixed colors
    cmap = c.ListedColormap(['#00004c','#000080','#0000b3','#0000e6','#0026ff','#004cff',
                         '#0073ff','#0099ff','#00c0ff','#00d900','#33f3ff','#73ffff','#c0ffff', 
                         (0,0,0,0),
                         '#ffff00','#ffe600','#ffcc00','#ffb300','#ff9900','#ff8000','#ff6600',
                         '#ff4c00','#ff2600','#e60000','#b30000','#800000','#4c0000'])
    bounds=[-200,-100,-75,-50,-30,-25,-20,-15,-13,-11,-9,-7,-5,-3,3,5,7,9,11,13,15,20,25,30,50,75,100,200]
    norm = c.BoundaryNorm(bounds, ncolors=cmap.N) # cmap.N gives the number of colors of your palette
    
    x,y = map(llons,llats)

    cs = map.contourf(x,y,VO, cmap=cmap, norm=norm, levels=bounds,shading='interp', zorder=2, ax=ax)

    return cs
    
def myanimate(i, llons,llats,VO):
    #ax.clear()
    print(VO.min(),VO.max())
    # change VO (randomly...)
    VO += 0.1 * np.random.randn()
    new_contour = drawmap(llons,llats,VO, 'ECMWF ERA-Interim VO at 850 hPa: Frame %03d'%(i) ) 
    
	
    
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import colors as c
%matplotlib inline
from mpl_toolkits.basemap import Basemap, shiftgrid
import numpy as np
import netCDF4
from ipywidgets import interact



f = netCDF4.Dataset('EI_VO_850hPa_Summer2001.nc', 'r')
lats = f.variables['lat'][:]
lons = f.variables['lon'][:]
VO = f.variables['VO'][0,0,:,:]*100000  # read first time and unique level

# shift data so lons go from -180 to 180 instead of 0 to 360.
VO,lons = shiftgrid(180.,VO,lons,start=False)
llons, llats = np.meshgrid(lons, lats)

f.close()

@interact(time=(0,50))
def finteract(time):
     ca = myanimate(time, llons,llats,VO)

Share your jupyter notebooks (nbviewer)

To be able to share your jupyter notebook:

For instance, the jupyter notebook generated has been shared and can be viewed here.

Rendering jupyter notebook on github

You can also store your jupyter notebook on github (and you are strongly encouraged to use a version control to keep your programs…) and according there is no interactive features or any javascript embedded, github will automatically show your jupyter notebook.

GEOJSON

A very simple way to visualize and explore GeoJSON files is to store them on github because gitHub supports rendering geoJSON and topoJSON map files within GitHub repositories.

Once available in your github repository, you can use your browser to visualize and share your GEOJSON plot. The final url depends on your github username:

<script src="https://embed.github.com/view/geojson/<username>/<repo>/<ref>/<path_to_file>"></script>

For instance, the file no-all-all.geojson has been stored in the lesson repository at https://github.com/annefou/metos_python/blob/gh-pages/data/no-all-all.geojson.

Then to visualize it, use:

<script src="https://embed.github.com/view/geojson/annefou/metos_python/gh-pages/data/no-all-all.geojson>"></script>

However, there is a number of limitations as described in the documentation.

Other interesting python packages

Packages that are worth to mention for analyzing spatio-temporal data:

  • “matplotlib, geopandas, pynio & pyngl, pyqgis, plotly, bokeh, gmaps, folium, cartopy, iris”
  • “nodebox-opengl - For playing around with animations”
  • “pandas, pytables, fiona, descartes, pyproj”

Key Points

  • Get an overview of nbviewer