Animated polar plot with oceanographic data

Original Notebook: https://matplotlib.org/matplotblog/posts/animated-polar-plot/

[1]:
import numpy as np
import pandas as pd

from argopy import DataFetcher as ArgoDataFetcher

argo_loader = ArgoDataFetcher(cache=True)
[2]:
# Query surface and 1000m temp in Med sea with argopy

df1 = argo_loader.region(
    [-1.2, 29.0, 28.0, 46.0, 0, 10.0, "2009-12", "2020-01"]
).to_xarray()
df2 = argo_loader.region(
    [-1.2, 29.0, 28.0, 46.0, 975.0, 1025.0, "2009-12", "2020-01"]
).to_xarray()
[3]:
start_date = "2010-01-04"
end_date = "2020-01-07"
[4]:
# Parameters
start_date = "2016-01-01"
end_date = "2020-01-03"

[5]:
# Weekly date array

daterange = np.arange(start_date, end_date, dtype="datetime64[7D]")
dayoftheyear = pd.DatetimeIndex(
    np.array(daterange, dtype="datetime64[D]") + 3
).dayofyear  # middle of the week

activeyear = pd.DatetimeIndex(
    np.array(daterange, dtype="datetime64[D]") + 3
).year  # extract year
[6]:
# Init final arrays

tsurf = np.zeros(len(daterange))
t1000 = np.zeros(len(daterange))
[7]:
# Filling arrays

for i in range(len(daterange)):
    i1 = (df1["TIME"] >= daterange[i]) & (df1["TIME"] < daterange[i] + 7)
    i2 = (df2["TIME"] >= daterange[i]) & (df2["TIME"] < daterange[i] + 7)
    tsurf[i] = df1.where(i1, drop=True)["TEMP"].mean().values
    t1000[i] = df2.where(i2, drop=True)["TEMP"].mean().values
[8]:
# Creating dataframe

d = {
    "date": np.array(daterange, dtype="datetime64[D]"),
    "tsurf": tsurf,
    "t1000": t1000,
}
ndf = pd.DataFrame(data=d)
ndf.head()
[8]:
date tsurf t1000
0 2015-12-31 16.008982 13.473844
1 2016-01-07 15.808208 13.474958
2 2016-01-14 15.696154 13.482945
3 2016-01-21 15.573696 13.489998
4 2016-01-28 15.446599 13.481531
[9]:
from IPython.display import HTML

import matplotlib
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.lines import Line2D

plt.rcParams["xtick.major.pad"] = "17"
plt.rcParams["axes.axisbelow"] = False
matplotlib.rc("axes", edgecolor="w")

big_angle = 360 / 12  # How we split our polar space

date_angle = (
    ((360 / 365) * dayoftheyear) * np.pi / 180
)  # For a day, a corresponding angle

# inner and outer ring limit values

inner = 10
outer = 30
# setting our color values

ocean_color = ["#ff7f50", "#004752"]
[10]:
def dress_axes(ax):
    ax.set_facecolor("w")
    ax.set_theta_zero_location("N")
    ax.set_theta_direction(-1)
    # Here is how we position the months labels

    middles = np.arange(big_angle / 2, 360, big_angle) * np.pi / 180
    ax.set_xticks(middles)
    ax.set_xticklabels(
        [
            "January",
            "February",
            "March",
            "April",
            "May",
            "June",
            "July",
            "August",
            "September",
            "October",
            "November",
            "December",
        ]
    )
    ax.set_yticks([15, 20, 25])
    ax.set_yticklabels(["15°C", "20°C", "25°C"])
    # Changing radial ticks angle

    ax.set_rlabel_position(359)
    ax.tick_params(axis="both", color="w")
    plt.grid(None, axis="x")
    plt.grid(axis="y", color="w", linestyle=":", linewidth=1)
    # Here is the bar plot that we use as background

    bars = ax.bar(
        middles,
        outer,
        width=big_angle * np.pi / 180,
        bottom=inner,
        color="lightgray",
        edgecolor="w",
        zorder=0,
    )
    plt.ylim([2, outer])
    # Custom legend

    legend_elements = [
        Line2D(
            [0],
            [0],
            marker="o",
            color="w",
            label="Surface",
            markerfacecolor=ocean_color[0],
            markersize=15,
        ),
        Line2D(
            [0],
            [0],
            marker="o",
            color="w",
            label="1000m",
            markerfacecolor=ocean_color[1],
            markersize=15,
        ),
    ]
    ax.legend(handles=legend_elements, loc="center", fontsize=13, frameon=False)
    # Main title for the figure

    plt.suptitle(
        "Mediterranean temperature from Argo profiles",
        fontsize=16,
        horizontalalignment="center",
    )
[11]:
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, polar=True)
dress_axes(ax)
plt.show()
../../_images/notebooks_output_animated-polar-plot_start_date_2016-01-01_end_date_2020-01-03_11_0.png
[12]:
def draw_data(i):
    # Clear

    ax.cla()
    # Redressing axes

    dress_axes(ax)
    # Limit between thin lines and thick line, this is current date minus 51 weeks basically.

    # why 51 and not 52 ? That create a small gap before the current date, which is prettier

    i0 = np.max([i - 51, 0])

    ax.plot(
        date_angle[i0 : i + 1],
        ndf["tsurf"][i0 : i + 1],
        "-",
        color=ocean_color[0],
        alpha=1.0,
        linewidth=5,
    )
    ax.plot(
        date_angle[0 : i + 1],
        ndf["tsurf"][0 : i + 1],
        "-",
        color=ocean_color[0],
        linewidth=0.7,
    )

    ax.plot(
        date_angle[i0 : i + 1],
        ndf["t1000"][i0 : i + 1],
        "-",
        color=ocean_color[1],
        alpha=1.0,
        linewidth=5,
    )
    ax.plot(
        date_angle[0 : i + 1],
        ndf["t1000"][0 : i + 1],
        "-",
        color=ocean_color[1],
        linewidth=0.7,
    )

    # Plotting a line to spot the current date easily

    ax.plot([date_angle[i], date_angle[i]], [inner, outer], "k-", linewidth=0.5)
    # Display the current year as a title, just beneath the suptitle

    plt.title(str(activeyear[i]), fontsize=16, horizontalalignment="center")
[13]:
anim = FuncAnimation(
    fig, draw_data, interval=40, frames=len(daterange) - 1, repeat=False
)

HTML(anim.to_html5_video())
[13]:
../../_images/notebooks_output_animated-polar-plot_start_date_2016-01-01_end_date_2020-01-03_13_1.png