Search code examples
pythoncartopymatplotlib-animation

Spinning globe GIF with cartopy


I'm failing to find the most efficient way to generate a simple animation of a spinning globe with a filled contour using cartopy. The following code yields a static gif, probably because the figure is not redrawing itself? Is there a way for the animation function to just change the geographic projection, without calling a contourf() again (which is computationally expensive)?

from pylab import *
import cartopy.crs as ccrs
from matplotlib.animation import FuncAnimation

lon, lat = meshgrid(
    (linspace(-180,180,361)+0.5)[::4],
    (linspace(-90,90,181)+0.5)[::4],
    )   

h = (abs(lon)<20).astype(int) * (abs(lat)<10).astype(int)

fig = figure(figsize=(3,3))
ax = fig.add_subplot(1, 1, 1, projection = ccrs.Orthographic())
ax.contourf(lon, lat, h, transform=ccrs.PlateCarree())

def update_fig(t):
    ax.projection = ccrs.Orthographic(t)

ani = FuncAnimation(
    fig,
    update_fig,
    frames = linspace(0,360,13)[:-1],
    interval = 100,
    blit = False,
    )

ani.save('mwe.gif')

Solution

  • It's worth noting that your code does not actually produce a static gif. If you add borders to your axis (with ax.coastlines()) then you'll see that the globe is being updated each frame, your data is just not updating to match the new projection.

    This problem can be solved if you're using ax.pcolormesh() as follows:

    from pylab import *
    import cartopy.crs as ccrs
    from matplotlib.animation import FuncAnimation
    
    lon, lat = meshgrid(
        (linspace(-180,180,361)+0.5)[::4],
        (linspace(-90,90,181)+0.5)[::4],
        )   
    
    h = (abs(lon)<20).astype(int) * (abs(lat)<10).astype(int)
    
    fig = figure(figsize=(3,3))
    ax = fig.add_subplot(1, 1, 1, projection = ccrs.Orthographic())
    ax.coastlines()
    mesh = ax.pcolormesh(lon, lat, h, transform=ccrs.PlateCarree())
    
    def update_fig(t):
        ax.projection = ccrs.Orthographic(t)
        mesh.set(transform=ccrs.PlateCarree())
    
    ani = FuncAnimation(
        fig,
        update_fig,
        frames = linspace(0,360,13)[:-1],
        interval = 200)
    
    

    Unfortunately, the object produced by ax.contourf doesn't seem to work in this way. The closest I could find was mesh.set_array(), which is used in the cartopy documentation for pcolormesh but also works for contourf. There is no .set_transform() or .set() for the object produced by contourf.