Skip to content

Commit 57f7e3e

Browse files
committed
Add animation example
1 parent a3ba91e commit 57f7e3e

File tree

6 files changed

+152
-11
lines changed

6 files changed

+152
-11
lines changed

doc/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
},
6464
"line_numbers": False,
6565
"filename_pattern": "/pet",
66+
"matplotlib_animations": True,
6667
"binder": {
6768
# Required keys
6869
"org": "AntSimi",

examples/08_tracking_manipulation/pet_track_anim.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
a = TrackEddiesObservations.load_file(
1515
py_eddy_tracker_sample.get_path("eddies_med_adt_allsat_dt2018/Anticyclonic.zarr")
1616
)
17-
eddy = a.extract_ids([9672])
17+
# We get only 300 first step to save time of documentation builder
18+
eddy = a.extract_ids([9672]).index(slice(0, 300))
1819

1920
# %%
2021
# Run animation
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
"""
2+
Track animation
3+
===============
4+
5+
Run in a terminal this script, which allow to watch eddy evolution
6+
7+
"""
8+
from py_eddy_tracker.observations.tracking import TrackEddiesObservations
9+
from py_eddy_tracker.appli.gui import Anim
10+
import py_eddy_tracker_sample
11+
from matplotlib.animation import FuncAnimation
12+
from numpy import arange
13+
14+
# %%
15+
# Load experimental atlas, and we select one eddy
16+
a = TrackEddiesObservations.load_file(
17+
py_eddy_tracker_sample.get_path("eddies_med_adt_allsat_dt2018/Anticyclonic.zarr")
18+
)
19+
eddy = a.extract_ids([9672])
20+
21+
# %%
22+
# Run animation
23+
a = Anim(eddy, intern=True, figsize=(8, 3.5), cmap="magma_r", nb_step=6)
24+
a.txt.set_position((17, 34.6))
25+
a.ax.set_xlim(16.5, 23)
26+
a.ax.set_ylim(34.5, 37)
27+
28+
# arguments to get full animation
29+
# kwargs = dict(frames=arange(*a.period), interval=50)
30+
# arguments to reduce compute cost for doucmentation, we display only every 10 days
31+
kwargs = dict(frames=arange(*a.period)[200:800:10], save_count=60, interval=200)
32+
33+
ani = FuncAnimation(a.fig, a.func_animation, **kwargs)

notebooks/python_module/08_tracking_manipulation/pet_track_anim.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
},
4545
"outputs": [],
4646
"source": [
47-
"a = TrackEddiesObservations.load_file(\n py_eddy_tracker_sample.get_path(\"eddies_med_adt_allsat_dt2018/Anticyclonic.zarr\")\n)\neddy = a.extract_ids([9672])"
47+
"a = TrackEddiesObservations.load_file(\n py_eddy_tracker_sample.get_path(\"eddies_med_adt_allsat_dt2018/Anticyclonic.zarr\")\n)\n# We get only 300 first step to save time of documentation builder\neddy = a.extract_ids([9672]).index(slice(0, 300))"
4848
]
4949
},
5050
{
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {
7+
"collapsed": false
8+
},
9+
"outputs": [],
10+
"source": [
11+
"%matplotlib inline"
12+
]
13+
},
14+
{
15+
"cell_type": "markdown",
16+
"metadata": {},
17+
"source": [
18+
"\n# Track animation\n\nRun in a terminal this script, which allow to watch eddy evolution\n"
19+
]
20+
},
21+
{
22+
"cell_type": "code",
23+
"execution_count": null,
24+
"metadata": {
25+
"collapsed": false
26+
},
27+
"outputs": [],
28+
"source": [
29+
"from py_eddy_tracker.observations.tracking import TrackEddiesObservations\nfrom py_eddy_tracker.appli.gui import Anim\nimport py_eddy_tracker_sample\nfrom matplotlib.animation import FuncAnimation\nfrom numpy import arange"
30+
]
31+
},
32+
{
33+
"cell_type": "markdown",
34+
"metadata": {},
35+
"source": [
36+
"Load experimental atlas, and we select one eddy\n\n"
37+
]
38+
},
39+
{
40+
"cell_type": "code",
41+
"execution_count": null,
42+
"metadata": {
43+
"collapsed": false
44+
},
45+
"outputs": [],
46+
"source": [
47+
"a = TrackEddiesObservations.load_file(\n py_eddy_tracker_sample.get_path(\"eddies_med_adt_allsat_dt2018/Anticyclonic.zarr\")\n)\neddy = a.extract_ids([9672])"
48+
]
49+
},
50+
{
51+
"cell_type": "markdown",
52+
"metadata": {},
53+
"source": [
54+
"Run animation\n\n"
55+
]
56+
},
57+
{
58+
"cell_type": "code",
59+
"execution_count": null,
60+
"metadata": {
61+
"collapsed": false
62+
},
63+
"outputs": [],
64+
"source": [
65+
"a = Anim(eddy, intern=True, figsize=(8, 3.5), cmap=\"magma_r\", nb_step=6)\na.txt.set_position((17, 34.6))\na.ax.set_xlim(16.5, 23)\na.ax.set_ylim(34.5, 37)\n\n# arguments to get full animation\nkwargs = dict(frames=arange(*a.period), interval=50)\n# arguments to reduce compute cost for doucmentation, we display only every 10 days\nkwargs = dict(frames=arange(*a.period)[200:800:10], save_count=60, interval=200)\n\nani = FuncAnimation(a.fig, a.func_animation, **kwargs)"
66+
]
67+
}
68+
],
69+
"metadata": {
70+
"kernelspec": {
71+
"display_name": "Python 3",
72+
"language": "python",
73+
"name": "python3"
74+
},
75+
"language_info": {
76+
"codemirror_mode": {
77+
"name": "ipython",
78+
"version": 3
79+
},
80+
"file_extension": ".py",
81+
"mimetype": "text/x-python",
82+
"name": "python",
83+
"nbconvert_exporter": "python",
84+
"pygments_lexer": "ipython3",
85+
"version": "3.7.7"
86+
}
87+
},
88+
"nbformat": 4,
89+
"nbformat_minor": 0
90+
}

src/py_eddy_tracker/appli/gui.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,19 @@
3333

3434

3535
class Anim:
36-
def __init__(self, eddy, intern=False, sleep_event=0.1, **kwargs):
36+
def __init__(self, eddy, intern=False, sleep_event=0.1, graphic_information=False, **kwargs):
3737
self.eddy = eddy
3838
x_name, y_name = eddy.intern(intern)
3939
self.t, self.x, self.y = eddy.time, eddy[x_name], eddy[y_name]
4040
self.x_core, self.y_core, self.track = eddy["lon"], eddy["lat"], eddy["track"]
41+
self.graphic_informations = graphic_information
4142
self.pause = False
4243
self.period = self.eddy.period
4344
self.sleep_event = sleep_event
4445
self.mappables = list()
4546
self.setup(**kwargs)
4647

47-
def setup(self, cmap="jet", nb_step=25, figsize=(8, 6)):
48+
def setup(self, cmap="jet", nb_step=25, figsize=(8, 6), **kwargs):
4849
cmap = pyplot.get_cmap(cmap)
4950
self.colors = cmap(arange(nb_step + 1) / nb_step)
5051
self.nb_step = nb_step
@@ -54,7 +55,7 @@ def setup(self, cmap="jet", nb_step=25, figsize=(8, 6)):
5455
y_min, y_max = self.y_core.min() - 2, self.y_core.max() + 2
5556
d_y = y_max - y_min
5657
# plot
57-
self.fig = pyplot.figure(figsize=figsize)
58+
self.fig = pyplot.figure(figsize=figsize, **kwargs)
5859
t0, t1 = self.period
5960
self.fig.suptitle(f"{t0} -> {t1}")
6061
self.ax = self.fig.add_axes((0.05, 0.05, 0.9, 0.9))
@@ -63,6 +64,7 @@ def setup(self, cmap="jet", nb_step=25, figsize=(8, 6)):
6364
self.ax.grid()
6465
# init mappable
6566
self.txt = self.ax.text(x_min + 0.05 * d_x, y_min + 0.05 * d_y, "", zorder=10)
67+
self.segs = list()
6668
self.contour = LineCollection([], zorder=1)
6769
self.ax.add_collection(self.contour)
6870

@@ -90,7 +92,6 @@ def show(self, infinity_loop=False):
9092
loop = True
9193
t0, t1 = self.period
9294
while loop:
93-
self.segs = list()
9495
self.now = t0
9596
while True:
9697
dt = self.sleep_event
@@ -119,13 +120,17 @@ def prev(self):
119120
self.now -= 1
120121
return self.draw_contour()
121122

122-
def draw_contour(self):
123-
# t0, t1 = self.period
124-
# select contour for this time step
123+
def func_animation(self, frame):
125124
while self.mappables:
126125
self.mappables.pop().remove()
126+
self.now = frame
127+
self.update()
128+
artists = [self.contour, self.txt]
129+
artists.extend(self.mappables)
130+
return artists
131+
132+
def update(self):
127133
m = self.t == self.now
128-
self.ax.figure.canvas.restore_region(self.bg_cache)
129134
if m.sum():
130135
self.segs.append(
131136
create_vertice(
@@ -137,7 +142,10 @@ def draw_contour(self):
137142
self.contour.set_paths(self.segs)
138143
self.contour.set_color(self.colors[-len(self.segs) :])
139144
self.contour.set_lw(arange(len(self.segs)) / len(self.segs) * 2.5)
140-
self.txt.set_text(f"{self.now} - {1/self.sleep_event:.0f} frame/s")
145+
txt = f"{self.now}"
146+
if self.graphic_informations:
147+
txt += f"- {1/self.sleep_event:.0f} frame/s"
148+
self.txt.set_text(txt)
141149
for i in where(m)[0]:
142150
mappable = self.ax.text(
143151
self.x_core[i], self.y_core[i], self.track[i], fontsize=8
@@ -149,6 +157,14 @@ def draw_contour(self):
149157
# Remove first segment to keep only T contour
150158
if len(self.segs) > self.nb_step:
151159
self.segs.pop(0)
160+
161+
def draw_contour(self):
162+
# t0, t1 = self.period
163+
# select contour for this time step
164+
while self.mappables:
165+
self.mappables.pop().remove()
166+
self.ax.figure.canvas.restore_region(self.bg_cache)
167+
self.update()
152168
# paint updated artist
153169
self.ax.figure.canvas.blit(self.ax.bbox)
154170

0 commit comments

Comments
 (0)