Search code examples
pythonmatplotlibcartopymatplotlib-animation

Dynamic Visualisation of Global Plots


I have produced 17 global plots that show the decadal averages in maximum surface ozone from 1850-2015. Rather than plotting them individually, I wish to create an animation that cycles through them (almost like a gif), i.e. have the same coastlines, axes and colour bar throughout but change what is being plotted as the contour.

Any help on how to adapt my code to do this would be greatly appreciated - thank you in advance!!

import numpy as np
import netCDF4 as n4
import matplotlib.pyplot as plt
from matplotlib import colorbar, colors
import matplotlib.cm as cm

import cartopy as cart
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import cartopy.feature as cfeature

nc = n4.Dataset('datafile.nc','r')

# daily maximum O3 VMR (units: mol mol-1)
sfo3max = nc.variables['sfo3max']
lon = nc.variables['lon'] # longitude
lat = nc.variables['lat'] # latitude

# (I manipulate the data to produce 17 arrays containing the decadal average O3 VMR which are
#  listed below in sfo3max_avg)

sfo3max_avg = [sfo3max_1850_1860_avg, sfo3max_1860_1870_avg, sfo3max_1870_1880_avg,
               sfo3max_1880_1890_avg, sfo3max_1890_1900_avg, sfo3max_1900_1910_avg,
               sfo3max_1910_1920_avg, sfo3max_1920_1930_avg, sfo3max_1930_1940_avg,
               sfo3max_1940_1950_avg, sfo3max_1950_1960_avg, sfo3max_1960_1970_avg,
               sfo3max_1970_1980_avg, sfo3max_1980_1990_avg, sfo3max_1990_2000_avg,
               sfo3max_2000_2010_avg, sfo3max_2010_2015_avg]

# find overall min & max values for colour bar in plots
min_sfo3max_avg = np.array([])
for i in sfo3max_avg:
    sfo3max_avg_min = np.amin(i)
    min_sfo3max_avg = np.append(min_sfo3max_avg, sfo3max_avg_min)
overall_min_sfo3max_avg = np.amin(min_sfo3max_avg)

max_sfo3max_avg = np.array([])
for i in sfo3max_avg:
    sfo3max_avg_max = np.amax(i)
    max_sfo3max_avg = np.append(max_sfo3max_avg, sfo3max_avg_max)
overall_max_sfo3max_avg = np.amax(max_sfo3max_avg)

# finally plot the 17 global plots of sfo3max_avg
for k in sfo3max_avg:
    fig = plt.figure()
    ax = plt.axes(projection=ccrs.PlateCarree())
    ax.coastlines() # Adding coastlines
    cs = ax.contourf(lon[:], lat[:], k[:], cmap='magma')
    ax.set_title('Decadal Average of Maximum O3 Volume Mixing Ratio')

    m = plt.cm.ScalarMappable(cmap=cm.magma)
    m.set_array(i[:])
    m.set_clim(overall_min_sfo3max_avg, overall_max_sfo3max_avg)

    # Additional necessary information
    cbar = plt.colorbar(m, boundaries=np.arange(overall_min_sfo3max_avg, overall_max_sfo3max_avg
                        + 0.5e-08, 0.5e-08))
    cbar.set_label('mol mol-1')

    # Adding axis labels - latitude & longitude
    gridl = ax.gridlines(color="black", linestyle="dotted", draw_labels=True) 
    gridl.xformatter=LONGITUDE_FORMATTER
    gridl.yformatter=LATITUDE_FORMATTER
    gridl.xlabels_top = False
    gridl.ylabels_right = False

    fig.set_size_inches(w=20,h=10)
    plt.show() # show global plot


Solution

  • Several elements in your plotting can be kept out of the loop because they only need to be set up once. After you set up the plot elements you can update the plot and animate by looping over the list. This can be achieved by making use of matplotlib's interactive mode as shown in the code below:

    import numpy as np
    import netCDF4 as n4
    import matplotlib
    matplotlib.use("nbagg")
    
    import matplotlib.pyplot as plt
    from matplotlib import colorbar, colors
    import matplotlib.cm as cm
    
    import cartopy as cart
    import cartopy.crs as ccrs
    from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
    import cartopy.feature as cfeature
    
    nc = n4.Dataset('datafile.nc','r')
    
    # daily maximum O3 VMR (units: mol mol-1)
    sfo3max = nc.variables['sfo3max']
    lon = nc.variables['lon'] # longitude
    lat = nc.variables['lat'] # latitude
    
    # (I manipulate the data to produce 17 arrays containing the decadal average O3 VMR which are
    #  listed below in sfo3max_avg)
    
    sfo3max_avg = [sfo3max_1850_1860_avg, sfo3max_1860_1870_avg, sfo3max_1870_1880_avg,
                   sfo3max_1880_1890_avg, sfo3max_1890_1900_avg, sfo3max_1900_1910_avg,
                   sfo3max_1910_1920_avg, sfo3max_1920_1930_avg, sfo3max_1930_1940_avg,
                   sfo3max_1940_1950_avg, sfo3max_1950_1960_avg, sfo3max_1960_1970_avg,
                   sfo3max_1970_1980_avg, sfo3max_1980_1990_avg, sfo3max_1990_2000_avg,
                   sfo3max_2000_2010_avg, sfo3max_2010_2015_avg]
    
    # find overall min & max values for colour bar in plots
    min_sfo3max_avg = np.array([])
    for i in sfo3max_avg:
        sfo3max_avg_min = np.amin(i)
        min_sfo3max_avg = np.append(min_sfo3max_avg, sfo3max_avg_min)
    overall_min_sfo3max_avg = np.amin(min_sfo3max_avg)
    
    max_sfo3max_avg = np.array([])
    for i in sfo3max_avg:
        sfo3max_avg_max = np.amax(i)
        max_sfo3max_avg = np.append(max_sfo3max_avg, sfo3max_avg_max)
    overall_max_sfo3max_avg = np.amax(max_sfo3max_avg)
    
    #setup the plot elements
    fig = plt.figure()
    fig.set_size_inches(w=20,h=10)
    ax = plt.axes(projection=ccrs.PlateCarree())
    ax.coastlines() # Adding coastlines
    ax.set_title('Decadal Average of Maximum O3 Volume Mixing Ratio')
    
    m = plt.cm.ScalarMappable(cmap=cm.magma)
    m.set_array(i[:])
    m.set_clim(overall_min_sfo3max_avg, overall_max_sfo3max_avg)
    
    
    # Additional necessary information
    cbar = plt.colorbar(m, boundaries=np.arange(overall_min_sfo3max_avg, overall_max_sfo3max_avg
                        + 0.5e-08, 0.5e-08))
    cbar.set_label('mol mol-1')
    
    # plot here only the 1st item in your sfo3max_avg list.
    cs = ax.contourf(lon[:], lat[:], sfo3max_avg[0][:], cmap='magma')
    
    # Adding axis labels - latitude & longitude
    gridl = ax.gridlines(color="black", linestyle="dotted", draw_labels=True) 
    gridl.xformatter=LONGITUDE_FORMATTER
    gridl.yformatter=LATITUDE_FORMATTER
    gridl.xlabels_top = False
    gridl.ylabels_right = False
    
    
    plt.ion()   # set interactive mode
    plt.show()
    
    # finally plot the 17 global plots of sfo3max_avg
    for k in sfo3max_avg:
        cs = ax.contourf(lon[:], lat[:], k[:], cmap='magma')
        plt.gcf().canvas.draw()
        plt.pause(1) #control the interval between successive displays, currently set to 1 sec.