Skip to content

Commit b357421

Browse files
authored
Loopers (AntSimi#118)
* Add short example about loopers and eddies
1 parent e56905c commit b357421

File tree

26 files changed

+432
-107
lines changed

26 files changed

+432
-107
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ Fixed
2121

2222
- Fix bug in convolution(filter), lowest rows was replace by zeros in convolution computation.
2323
Important impact for tiny kernel
24+
- Fix method of sampling before contour fitting
2425

2526
Added
2627
^^^^^
2728

2829
- Allow to replace mask by isnan method to manage nan data instead of masked data
30+
- Add drifter colocation example
2931

3032
[3.5.0] - 2021-06-22
3133
--------------------

examples/06_grid_manipulation/pet_advect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def _repr_html_(self, *args, **kwargs):
5050

5151
def save(self, *args, **kwargs):
5252
if args[0].endswith("gif"):
53-
# In this case gif is use to create thumbnail which are not use but consume same time than video
53+
# In this case gif is used to create thumbnail which is not used but consume same time than video
5454
# So we create an empty file, to save time
5555
with open(args[0], "w") as _:
5656
pass

examples/06_grid_manipulation/pet_lavd.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def _repr_html_(self, *args, **kwargs):
6565

6666
def save(self, *args, **kwargs):
6767
if args[0].endswith("gif"):
68-
# In this case gif is use to create thumbnail which are not use but consume same time than video
68+
# In this case gif is used to create thumbnail which is not used but consume same time than video
6969
# So we create an empty file, to save time
7070
with open(args[0], "w") as _:
7171
pass
@@ -159,11 +159,7 @@ def update(i_frame):
159159
# Format LAVD data
160160
lavd = RegularGridDataset.with_array(
161161
coordinates=("lon", "lat"),
162-
datas=dict(
163-
lavd=lavd.T,
164-
lon=x_g,
165-
lat=y_g,
166-
),
162+
datas=dict(lavd=lavd.T, lon=x_g, lat=y_g,),
167163
centered=True,
168164
)
169165

examples/07_cube_manipulation/pet_cube.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def _repr_html_(self, *args, **kwargs):
3131

3232
def save(self, *args, **kwargs):
3333
if args[0].endswith("gif"):
34-
# In this case gif is use to create thumbnail which are not use but consume same time than video
34+
# In this case gif is used to create thumbnail which is not used but consume same time than video
3535
# So we create an empty file, to save time
3636
with open(args[0], "w") as _:
3737
pass

examples/08_tracking_manipulation/pet_track_anim_matplotlib_animation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def _repr_html_(self, *args, **kwargs):
3030

3131
def save(self, *args, **kwargs):
3232
if args[0].endswith("gif"):
33-
# In this case gif is use to create thumbnail which are not use but consume same time than video
33+
# In this case gif is used to create thumbnail which is not used but consume same time than video
3434
# So we create an empty file, to save time
3535
with open(args[0], "w") as _:
3636
pass

examples/10_tracking_diagnostics/pet_normalised_lifetime.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ def eddy_norm_lifetime(self, name, nb, factor=1):
6565
# %%
6666
# Figure
6767
# ------
68-
fig, axs = plt.subplots(nrows=2, figsize=(8, 6))
68+
fig, (ax0, ax1) = plt.subplots(nrows=2, figsize=(8, 6))
6969

70-
axs[0].set_title("Normalised Mean Radius")
71-
axs[0].plot(*AC_radius), axs[0].plot(*CC_radius)
72-
axs[0].set_ylabel("Radius (km)"), axs[0].grid()
73-
axs[0].set_xlim(0, 1), axs[0].set_ylim(0, None)
70+
ax0.set_title("Normalised Mean Radius")
71+
ax0.plot(*AC_radius), ax0.plot(*CC_radius)
72+
ax0.set_ylabel("Radius (km)"), ax0.grid()
73+
ax0.set_xlim(0, 1), ax0.set_ylim(0, None)
7474

75-
axs[1].set_title("Normalised Mean Amplitude")
76-
axs[1].plot(*AC_amplitude, label="AC"), axs[1].plot(*CC_amplitude, label="CC")
77-
axs[1].set_ylabel("Amplitude (cm)"), axs[1].grid(), axs[1].legend()
78-
_ = axs[1].set_xlim(0, 1), axs[1].set_ylim(0, None)
75+
ax1.set_title("Normalised Mean Amplitude")
76+
ax1.plot(*AC_amplitude, label="AC"), ax1.plot(*CC_amplitude, label="CC")
77+
ax1.set_ylabel("Amplitude (cm)"), ax1.grid(), ax1.legend()
78+
_ = ax1.set_xlim(0, 1), ax1.set_ylim(0, None)
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
"""
2+
Colocate looper with eddy from altimetry
3+
========================================
4+
5+
All loopers data used in this example are a subset from the dataset described in this article
6+
[Lumpkin, R. : Global characteristics of coherent vortices from surface drifter trajectories](https://doi.org/10.1002/2015JC011435)
7+
"""
8+
9+
import re
10+
11+
import numpy as np
12+
import py_eddy_tracker_sample
13+
from matplotlib import pyplot as plt
14+
from matplotlib.animation import FuncAnimation
15+
16+
from py_eddy_tracker import data
17+
from py_eddy_tracker.appli.gui import Anim
18+
from py_eddy_tracker.observations.tracking import TrackEddiesObservations
19+
20+
21+
# %%
22+
class VideoAnimation(FuncAnimation):
23+
def _repr_html_(self, *args, **kwargs):
24+
"""To get video in html and have a player"""
25+
content = self.to_html5_video()
26+
return re.sub(
27+
r'width="[0-9]*"\sheight="[0-9]*"', 'width="100%" height="100%"', content
28+
)
29+
30+
def save(self, *args, **kwargs):
31+
if args[0].endswith("gif"):
32+
# In this case gif is used to create thumbnail which is not used but consume same time than video
33+
# So we create an empty file, to save time
34+
with open(args[0], "w") as _:
35+
pass
36+
return
37+
return super().save(*args, **kwargs)
38+
39+
40+
def start_axes(title):
41+
fig = plt.figure(figsize=(13, 5))
42+
ax = fig.add_axes([0.03, 0.03, 0.90, 0.94], aspect="equal")
43+
ax.set_xlim(-6, 36.5), ax.set_ylim(30, 46)
44+
ax.set_title(title, weight="bold")
45+
return ax
46+
47+
48+
def update_axes(ax, mappable=None):
49+
ax.grid()
50+
if mappable:
51+
plt.colorbar(mappable, cax=ax.figure.add_axes([0.94, 0.05, 0.01, 0.9]))
52+
53+
54+
# %%
55+
# Load eddies dataset
56+
cyclonic_eddies = TrackEddiesObservations.load_file(
57+
py_eddy_tracker_sample.get_demo_path("eddies_med_adt_allsat_dt2018/Cyclonic.zarr")
58+
)
59+
anticyclonic_eddies = TrackEddiesObservations.load_file(
60+
py_eddy_tracker_sample.get_demo_path(
61+
"eddies_med_adt_allsat_dt2018/Anticyclonic.zarr"
62+
)
63+
)
64+
65+
# %%
66+
# Load loopers dataset
67+
loopers_med = TrackEddiesObservations.load_file(
68+
data.get_demo_path("loopers_lumpkin_med.nc")
69+
)
70+
71+
# %%
72+
# Global view
73+
# ===========
74+
ax = start_axes("All drifters available in Med from Lumpkin dataset")
75+
loopers_med.plot(ax, lw=0.5, color="r", ref=-10)
76+
update_axes(ax)
77+
78+
# %%
79+
# One segment of drifter
80+
# ======================
81+
#
82+
# Get a drifter segment (the indexes used have no correspondance with the original dataset).
83+
looper = loopers_med.extract_ids((3588,))
84+
fig = plt.figure(figsize=(16, 6))
85+
ax = fig.add_subplot(111, aspect="equal")
86+
looper.plot(ax, lw=0.5, label="Original position of drifter")
87+
looper_filtered = looper.copy()
88+
looper_filtered.position_filter(1, 13)
89+
s = looper_filtered.scatter(
90+
ax,
91+
"time",
92+
cmap=plt.get_cmap("Spectral_r", 20),
93+
label="Filtered position of drifter",
94+
)
95+
plt.colorbar(s).set_label("time (days from 1/1/1950)")
96+
ax.legend()
97+
ax.grid()
98+
99+
# %%
100+
# Try to find a detected eddies with adt at same place. We used filtered track to simulate an eddy center
101+
match = looper_filtered.close_tracks(
102+
anticyclonic_eddies, method="close_center", delta=0.1, nb_obs_min=50
103+
)
104+
fig = plt.figure(figsize=(16, 6))
105+
ax = fig.add_subplot(111, aspect="equal")
106+
looper.plot(ax, lw=0.5, label="Original position of drifter")
107+
looper_filtered.plot(ax, lw=1.5, label="Filtered position of drifter")
108+
match.plot(ax, lw=1.5, label="Matched eddy")
109+
ax.legend()
110+
ax.grid()
111+
112+
# %%
113+
# Display radius of this 2 datasets.
114+
fig = plt.figure(figsize=(20, 8))
115+
ax = fig.add_subplot(111)
116+
ax.plot(looper.time, looper.radius_s / 1e3, label="loopers")
117+
looper_radius = looper.copy()
118+
looper_radius.median_filter(1, "time", "radius_s", inplace=True)
119+
looper_radius.loess_filter(13, "time", "radius_s", inplace=True)
120+
ax.plot(
121+
looper_radius.time,
122+
looper_radius.radius_s / 1e3,
123+
label="loopers (filtered half window 13 days)",
124+
)
125+
ax.plot(match.time, match.radius_s / 1e3, label="altimetry")
126+
match_radius = match.copy()
127+
match_radius.median_filter(1, "time", "radius_s", inplace=True)
128+
match_radius.loess_filter(13, "time", "radius_s", inplace=True)
129+
ax.plot(
130+
match_radius.time,
131+
match_radius.radius_s / 1e3,
132+
label="altimetry (filtered half window 13 days)",
133+
)
134+
ax.set_ylabel("radius(km)"), ax.set_ylim(0, 100)
135+
ax.legend()
136+
ax.set_title("Radius from loopers and altimeter")
137+
ax.grid()
138+
139+
140+
# %%
141+
# Animation of a drifter and its colocated eddy
142+
def update(frame):
143+
# We display last 5 days of loopers trajectory
144+
m = (looper.time < frame) * (looper.time > (frame - 5))
145+
anim.func_animation(frame)
146+
line.set_data(looper.lon[m], looper.lat[m])
147+
148+
149+
anim = Anim(match, intern=True, figsize=(8, 8), cmap="magma_r", nb_step=10, dpi=75)
150+
# mappable to show drifter in red
151+
line = anim.ax.plot([], [], "r", lw=4, zorder=100)[0]
152+
anim.fig.suptitle("")
153+
_ = VideoAnimation(anim.fig, update, frames=np.arange(*anim.period, 1), interval=125)

examples/16_network/pet_follow_particle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def _repr_html_(self, *args, **kwargs):
3131

3232
def save(self, *args, **kwargs):
3333
if args[0].endswith("gif"):
34-
# In this case gif is used to create thumbnail which are not used but consumes same time than video
34+
# In this case gif is used to create thumbnail which is not used but consume same time than video
3535
# So we create an empty file, to save time
3636
with open(args[0], "w") as _:
3737
pass

examples/16_network/pet_group_anim.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def _repr_html_(self, *args, **kwargs):
2929

3030
def save(self, *args, **kwargs):
3131
if args[0].endswith("gif"):
32-
# In this case gif is use to create thumbnail which are not use but consume same time than video
32+
# In this case gif is used to create thumbnail which is not used but consume same time than video
3333
# So we create an empty file, to save time
3434
with open(args[0], "w") as _:
3535
pass

examples/16_network/pet_ioannou_2017_case.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def _repr_html_(self, *args, **kwargs):
3636

3737
def save(self, *args, **kwargs):
3838
if args[0].endswith("gif"):
39-
# In this case gif is use to create thumbnail which are not use but consume same time than video
39+
# In this case gif is used to create thumbnail which is not used but consume same time than video
4040
# So we create an empty file, to save time
4141
with open(args[0], "w") as _:
4242
pass

0 commit comments

Comments
 (0)