Skip to content

Commit 94db015

Browse files
Merge branch 'master' into fixs
2 parents 290affd + 0e2bb64 commit 94db015

File tree

30 files changed

+441
-104
lines changed

30 files changed

+441
-104
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Fixed
2525

2626
- Fix bug in convolution(filter), lowest rows was replace by zeros in convolution computation.
2727
Important impact for tiny kernel
28+
- Fix method of sampling before contour fitting
2829
- Fix bug when loading dataset in zarr format, not all variables were correctly loaded
2930
- Fix bug when zarr dataset has same size for number of observations and contour size
3031
- Fix bug when tracking, previous_virtual_obs was not always loaded
@@ -33,6 +34,7 @@ Added
3334
^^^^^
3435

3536
- Allow to replace mask by isnan method to manage nan data instead of masked data
37+
- Add drifter colocation example
3638

3739
[3.5.0] - 2021-06-22
3840
--------------------

doc/environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ channels:
22
- conda-forge
33
- defaults
44
dependencies:
5-
- python=3.7
5+
- python=3.8
66
- ffmpeg
77
- pip:
88
- sphinx-gallery

environment.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ channels:
33
- conda-forge
44
- defaults
55
dependencies:
6-
- python=3.7
6+
- python=3.8
77
- ffmpeg
88
- pip:
99
- -r requirements.txt

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: 1 addition & 1 deletion
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

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.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
Track animation
33
===============
44
5-
Run in a terminal this script, which allow to watch eddy evolution
5+
Run in a terminal this script, which allow to watch eddy evolution.
6+
7+
You could use also *EddyAnim* script to display/save animation.
68
79
"""
810
import py_eddy_tracker_sample

examples/08_tracking_manipulation/pet_track_anim_matplotlib_animation.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
Track animation with standard matplotlib
33
========================================
44
5-
Run in a terminal this script, which allow to watch eddy evolution
5+
Run in a terminal this script, which allow to watch eddy evolution.
6+
7+
You could use also *EddyAnim* script to display/save animation.
68
79
"""
810
import re
@@ -28,7 +30,7 @@ def _repr_html_(self, *args, **kwargs):
2830

2931
def save(self, *args, **kwargs):
3032
if args[0].endswith("gif"):
31-
# 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
3234
# So we create an empty file, to save time
3335
with open(args[0], "w") as _:
3436
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)

0 commit comments

Comments
 (0)