Matplotlib animations from ECMWF data
<article class="article" style="box-sizing: border-box; display: block; color: rgb(33, 37, 41); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">
<header style="box-sizing: border-box; display: block;">
</header>
Loading data
In our previous post, we saw how to retrieve data from the ECMWF. Today we're going to work on a NetCDF dataset from ERA Interim providing the 2 metre temperature in kelvins. We retrieved this file using this Python code:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 14px; margin-top: 1em; margin-bottom: 1rem; overflow: auto; display: block; color: rgb(33, 37, 41); background: rgb(248, 248, 248); border: 1px solid rgba(0, 0, 0, 0.1); padding: 1rem;">from ecmwfapi import ECMWFDataServer
server = ECMWFDataServer()
server.retrieve({
"class": "ei",
"dataset": "interim",
"date": "2017-01-01/to/2017-12-31",
"expver": "1",
"grid": "0.75/0.75",
"levtype": "sfc",
"param": "167.128", # 2 metre temperature
"step": "0",
"stream": "oper",
"time": "00:00:00/06:00:00/12:00:00/18:00:00",
"type": "an",
"format": "netcdf",
"target": "temperature_ei_an_2017.nc",
})
</pre>
Once we have this data, it's fairly easy to read it with xarray and plot it at a given time using Matplotlib and its Cartopy extension. First we need a few import statements to be able to use these libraries.
In [1]:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 14px; margin: 0px; overflow: auto; display: block; color: rgb(33, 37, 41); background: transparent; border: none; padding: 0px;">import xarray as xr
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeat
</pre>
Now we can open the NetCDF file using xarray.
In [2]:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 14px; margin: 0px; overflow: auto; display: block; color: rgb(33, 37, 41); background: transparent; border: none; padding: 0px;">ds = xr.open_mfdataset("data/temperature_ei_an_2017.nc")
</pre>
Plotting data on a map
Once we have this xarray dataset object, we can use it to plot data on a map. First we define a function that will create the base map on which we're going to plot our data.
In [3]:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 14px; margin: 0px; overflow: auto; display: block; color: rgb(33, 37, 41); background: transparent; border: none; padding: 0px;">def make_figure():
fig = plt.figure(figsize=(8, 3))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
# generate a basemap with country borders, oceans and coastlines
ax.add_feature(cfeat.LAND)
ax.add_feature(cfeat.OCEAN)
ax.add_feature(cfeat.COASTLINE)
ax.add_feature(cfeat.BORDERS, linestyle='dotted')
return fig, ax
make_figure();
</pre>
For this tutorial, we restrict the domain to the carribean area because generating animations on a large domain can be quite ressource intensive. Let's retrieve the 2 metre temperature for this area.
In [4]:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 14px; margin: 0px; overflow: auto; display: block; color: rgb(33, 37, 41); background: transparent; border: none; padding: 0px;">area = ds.t2m.sel(longitude=slice(270, 310), latitude=slice(30, 4))
</pre>
Then we plot the temperature for this domain on the 1st of January 2017 at 12:00.
In [5]:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 14px; margin: 0px; overflow: auto; display: block; color: rgb(33, 37, 41); background: transparent; border: none; padding: 0px;">_, ax = make_figure()
plot the temperature field
grid = area.sel(time='2017-01-01T12:00:00')
grid.plot(ax=ax, transform=ccrs.PlateCarree());
</pre>
Generating an animated map
As you can see, Cartopy makes it very easy to add a simple basemap that will put your data in context. Now, using Matplotlib's animation module, we can visualize how the temperature changes over time. Note how we draw the color bar only for the initial frame (add_colorbar=True
), setting its scale based on the data.
In [10]:
<pre style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-size: 14px; margin: 0px; overflow: auto; display: block; color: rgb(33, 37, 41); background: transparent; border: none; padding: 0px;">import matplotlib.animation as animation
fig, ax = make_figure()
frames = area.time.size # Number of frames
min_value = area.values.min() # Lowest value
max_value = area.values.max() # Highest value
def draw(frame, add_colorbar):
grid = area[frame]
contour = grid.plot(ax=ax, transform=ccrs.PlateCarree(),
add_colorbar=add_colorbar, vmin=min_value, vmax=max_value)
title = u"%s — %s" % (ds.t2m.long_name, str(area.time[frame].values)[:19])
ax.set_title(title)
return contour
def init():
return draw(0, add_colorbar=True)
def animate(frame):
return draw(frame, add_colorbar=False)
ani = animation.FuncAnimation(fig, animate, frames, interval=0.01, blit=False,
init_func=init, repeat=False)
ani.save('images/temperature_ei_an_2017.mp4', writer=animation.FFMpegWriter(fps=8))
plt.close(fig)
</pre>
The animation has now been saved to a file and can be embedded in webpages or presentations like this:
I hope you can see now how you can generate an animated map with few lines of codes using Python and its fantastic ecosystem of libraries.
Feel free to contact contact us to send your feedback about this post or if you need help to bring weather data into your own projects.
</article>