Skip to content

Commit d59406c

Browse files
Merge branch 'master' into modification_dissociate
2 parents e08d16a + 98b19f7 commit d59406c

File tree

6 files changed

+205
-20
lines changed

6 files changed

+205
-20
lines changed

examples/06_grid_manipulation/pet_lavd.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
Method are described here:
99
1010
- Abernathey, Ryan, and George Haller. "Transport by Lagrangian Vortices in the Eastern Pacific",
11-
Journal of Physical Oceanography 48, 3 (2018): 667-685, accessed Feb 16, 2021,
12-
https://doi.org/10.1175/JPO-D-17-0102.1
11+
Journal of Physical Oceanography 48, 3 (2018): 667-685, accessed Feb 16, 2021,
12+
https://doi.org/10.1175/JPO-D-17-0102.1
1313
- `Transport by Coherent Lagrangian Vortices`_,
14-
R. Abernathey, Sinha A., Tarshish N., Liu T., Zhang C., Haller G., 2019,
15-
Talk a t the Sources and Sinks of Ocean Mesoscale Eddy Energy CLIVAR Workshop
14+
R. Abernathey, Sinha A., Tarshish N., Liu T., Zhang C., Haller G., 2019,
15+
Talk a t the Sources and Sinks of Ocean Mesoscale Eddy Energy CLIVAR Workshop
1616
1717
.. _Transport by Coherent Lagrangian Vortices:
1818
https://usclivar.org/sites/default/files/meetings/2019/presentations/Aberernathey_CLIVAR.pdf
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"""
2+
Network group process
3+
=====================
4+
"""
5+
6+
import re
7+
from datetime import datetime
8+
9+
from matplotlib import pyplot as plt
10+
from matplotlib.animation import FuncAnimation
11+
from matplotlib.colors import ListedColormap
12+
from numba import njit
13+
from numpy import arange, array, empty, ones
14+
15+
from py_eddy_tracker import data
16+
from py_eddy_tracker.generic import flatten_line_matrix
17+
from py_eddy_tracker.observations.network import Network
18+
from py_eddy_tracker.observations.observation import EddiesObservations
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+
'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 use to create thumbnail which are not use 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+
# %%
41+
NETWORK_GROUPS = list()
42+
43+
44+
@njit(cache=True)
45+
def apply_replace(x, x0, x1):
46+
nb = x.shape[0]
47+
for i in range(nb):
48+
if x[i] == x0:
49+
x[i] = x1
50+
51+
52+
# %%
53+
# Modified class to catch group process at each step in order to illustrate processing
54+
class MyNetwork(Network):
55+
def get_group_array(self, results, nb_obs):
56+
"""With a loop on all pair of index, we will label each obs with a group
57+
number
58+
"""
59+
nb_obs = array(nb_obs, dtype="u4")
60+
day_start = nb_obs.cumsum() - nb_obs
61+
gr = empty(nb_obs.sum(), dtype="u4")
62+
gr[:] = self.NOGROUP
63+
64+
id_free = 1
65+
for i, j, ii, ij in results:
66+
gr_i = gr[slice(day_start[i], day_start[i] + nb_obs[i])]
67+
gr_j = gr[slice(day_start[j], day_start[j] + nb_obs[j])]
68+
# obs with no groups
69+
m = (gr_i[ii] == self.NOGROUP) * (gr_j[ij] == self.NOGROUP)
70+
nb_new = m.sum()
71+
gr_i[ii[m]] = gr_j[ij[m]] = arange(id_free, id_free + nb_new)
72+
id_free += nb_new
73+
# associate obs with no group with obs with group
74+
m = (gr_i[ii] != self.NOGROUP) * (gr_j[ij] == self.NOGROUP)
75+
gr_j[ij[m]] = gr_i[ii[m]]
76+
m = (gr_i[ii] == self.NOGROUP) * (gr_j[ij] != self.NOGROUP)
77+
gr_i[ii[m]] = gr_j[ij[m]]
78+
# case where 2 obs have a different group
79+
m = gr_i[ii] != gr_j[ij]
80+
if m.any():
81+
# Merge of group, ref over etu
82+
for i_, j_ in zip(ii[m], ij[m]):
83+
g0, g1 = gr_i[i_], gr_j[j_]
84+
apply_replace(gr, g0, g1)
85+
NETWORK_GROUPS.append((i, j, gr.copy()))
86+
return gr
87+
88+
89+
# %%
90+
# Movie period
91+
t0 = (datetime(2005, 5, 1) - datetime(1950, 1, 1)).days
92+
t1 = (datetime(2005, 6, 1) - datetime(1950, 1, 1)).days
93+
94+
# %%
95+
# Get data from period and area
96+
e = EddiesObservations.load_file(data.get_path("Anticyclonic_seg.nc"))
97+
e = e.extract_with_mask((e.time >= t0) * (e.time < t1)).extract_with_area(
98+
dict(llcrnrlon=25, urcrnrlon=35, llcrnrlat=31, urcrnrlat=37.5)
99+
)
100+
# %%
101+
# Reproduce individual daily identification(for demonstration)
102+
EDDIES_BY_DAYS = list()
103+
for i, b0, b1 in e.iter_on("time"):
104+
EDDIES_BY_DAYS.append(e.index(i))
105+
# need for display
106+
e = EddiesObservations.concatenate(EDDIES_BY_DAYS)
107+
108+
# %%
109+
# Run network building group to intercept every step
110+
n = MyNetwork.from_eddiesobservations(EDDIES_BY_DAYS, window=7)
111+
_ = n.group_observations(minimal_area=True)
112+
113+
114+
# %%
115+
def update(frame):
116+
i_current, i_match, gr = NETWORK_GROUPS[frame]
117+
current = EDDIES_BY_DAYS[i_current]
118+
x = flatten_line_matrix(current.contour_lon_e)
119+
y = flatten_line_matrix(current.contour_lat_e)
120+
current_contour.set_data(x, y)
121+
match = EDDIES_BY_DAYS[i_match]
122+
x = flatten_line_matrix(match.contour_lon_e)
123+
y = flatten_line_matrix(match.contour_lat_e)
124+
matched_contour.set_data(x, y)
125+
groups.set_array(gr)
126+
txt.set_text(f"Day {i_current} match with day {i_match}")
127+
s = 80 * ones(gr.shape)
128+
s[gr == 0] = 4
129+
groups.set_sizes(s)
130+
131+
132+
# %%
133+
# Anim
134+
# ----
135+
fig = plt.figure(figsize=(16, 9), dpi=50)
136+
ax = fig.add_axes([0, 0, 1, 1])
137+
ax.set_aspect("equal"), ax.grid(), ax.set_xlim(26, 34), ax.set_ylim(32, 36.5)
138+
groups = ax.scatter(
139+
e.lon,
140+
e.lat,
141+
c=NETWORK_GROUPS[0][2],
142+
cmap=ListedColormap(["gray", *e.COLORS[:-1]], name="from_list", N=30),
143+
vmin=0,
144+
vmax=30,
145+
)
146+
current_contour = ax.plot([], [], "k", lw=2, label="Current contour")[0]
147+
matched_contour = ax.plot([], [], "r", lw=1, ls="--", label="Candidate contour")[0]
148+
txt = ax.text(31.5, 35.5, "", fontsize=25)
149+
ax.legend(fontsize=25)
150+
ani = VideoAnimation(fig, update, frames=len(NETWORK_GROUPS), interval=220)

examples/16_network/pet_replay_segmentation.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@
1111
from datetime import datetime, timedelta
1212

1313
import numpy as np
14-
from matplotlib import colors
1514
from matplotlib import pyplot as plt
1615
from matplotlib.animation import FuncAnimation
1716
from matplotlib.ticker import FuncFormatter
1817

1918
import py_eddy_tracker.gui
20-
from py_eddy_tracker.appli.gui import Anim
2119
from py_eddy_tracker.data import get_path
2220
from py_eddy_tracker.observations.network import NetworkObservations
2321
from py_eddy_tracker.observations.tracking import TrackEddiesObservations
@@ -34,7 +32,7 @@ def save(self, *args, **kwargs):
3432
if args[0].endswith("gif"):
3533
# In this case gif is use to create thumbnail which are not use but consume same time than video
3634
# So we create an empty file, to save time
37-
with open(args[0], "w") as h:
35+
with open(args[0], "w") as _:
3836
pass
3937
return
4038
return super().save(*args, **kwargs)

examples/16_network/pet_segmentation_anim.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,4 @@ def update(i_frame):
9797
mappable_CONTOUR = ax.plot(
9898
e.contour_lon_e[INDICES[0]], e.contour_lat_e[INDICES[0]], color=cmap.colors[0]
9999
)[0]
100-
ani = VideoAnimation(
101-
fig, update, frames=range(1, len(TRACKS), 4), interval=125, blit=True
102-
)
100+
ani = VideoAnimation(fig, update, frames=range(1, len(TRACKS), 4), interval=125)

src/py_eddy_tracker/observations/network.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ def __init__(self, buffersize, intern=False, memory=False):
4343
self.memory = memory
4444

4545
def load_contour(self, filename):
46+
if isinstance(filename, EddiesObservations):
47+
return filename[self.xname], filename[self.yname]
4648
if filename not in self.DATA:
4749
if len(self.FLIST) > self.buffersize:
4850
self.DATA.pop(self.FLIST.pop(0))
@@ -837,10 +839,18 @@ def __init__(self, input_regex, window=5, intern=False, memory=False):
837839
"""
838840
self.window = window
839841
self.buffer = Buffer(window, intern, memory)
842+
self.memory = memory
843+
840844
self.filenames = glob(input_regex)
841845
self.filenames.sort()
842846
self.nb_input = len(self.filenames)
843-
self.memory = memory
847+
848+
@classmethod
849+
def from_eddiesobservations(cls, observations, *args, **kwargs):
850+
new = cls("", *args, **kwargs)
851+
new.filenames = observations
852+
new.nb_input = len(new.filenames)
853+
return new
844854

845855
def get_group_array(self, results, nb_obs):
846856
"""With a loop on all pair of index, we will label each obs with a group
@@ -871,10 +881,13 @@ def get_group_array(self, results, nb_obs):
871881
if m.any():
872882
# Merge of group, ref over etu
873883
for i_, j_ in zip(ii[m], ij[m]):
874-
merge_id.append((gr_i[i_], gr_j[j_]))
884+
g0, g1 = gr_i[i_], gr_j[j_]
885+
if g0 > g1:
886+
g0, g1 = g1, g0
887+
merge_id.append((g0, g1))
875888

876889
gr_transfer = arange(id_free, dtype="u4")
877-
for i, j in merge_id:
890+
for i, j in set(merge_id):
878891
gr_i, gr_j = gr_transfer[i], gr_transfer[j]
879892
if gr_i != gr_j:
880893
apply_replace(gr_transfer, gr_i, gr_j)

src/py_eddy_tracker/observations/observation.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,8 @@ def iter_on(self, xname, bins=None):
490490
d = x[1:] - x[:-1]
491491
if bins is None:
492492
bins = arange(x.min(), x.max() + 2)
493+
elif not isinstance(bins, ndarray):
494+
bins = array(bins)
493495
nb_bins = len(bins) - 1
494496
i = numba_digitize(x, bins) - 1
495497
# Not monotonous
@@ -1609,7 +1611,9 @@ def extract_with_area(self, area, **kwargs):
16091611
16101612
.. minigallery:: py_eddy_tracker.EddiesObservations.extract_with_area
16111613
"""
1612-
mask = (self.latitude > area["llcrnrlat"]) * (self.latitude < area["urcrnrlat"])
1614+
lat0 = area.get("llcrnrlat", -90)
1615+
lat1 = area.get("urcrnrlat", 90)
1616+
mask = (self.latitude > lat0) * (self.latitude < lat1)
16131617
lon0 = area["llcrnrlon"]
16141618
lon = (self.longitude - lon0) % 360 + lon0
16151619
mask *= (lon > lon0) * (lon < area["urcrnrlon"])
@@ -2194,6 +2198,26 @@ def grid_count_pixel_in(
21942198
grid_count_(grid, i, j)
21952199

21962200

2201+
@njit(cache=True)
2202+
def reduce_size(x, y):
2203+
"""
2204+
Reduce array size if last position is repeated, in order to save compute time
2205+
2206+
:param array x: longitude
2207+
:param array y: latitude
2208+
2209+
:return: reduce arrays x,y
2210+
:rtype: ndarray,ndarray
2211+
"""
2212+
i = x.shape[0]
2213+
x0, y0 = x[0], y[0]
2214+
while True:
2215+
i -= 1
2216+
if x[i] != x0 or y[i] != y0:
2217+
i += 1
2218+
return x[:i], y[:i]
2219+
2220+
21972221
@njit(cache=True)
21982222
def poly_indexs(x_p, y_p, x_c, y_c):
21992223
"""
@@ -2208,9 +2232,10 @@ def poly_indexs(x_p, y_p, x_c, y_c):
22082232
nb_c = x_c.shape[0]
22092233
indexs = -ones(nb_p, dtype=numba_types.int32)
22102234
for i in range(nb_c):
2211-
x_c_min, y_c_min = x_c[i].min(), y_c[i].min()
2212-
x_c_max, y_c_max = x_c[i].max(), y_c[i].max()
2213-
v = create_vertice(x_c[i], y_c[i])
2235+
x_, y_ = reduce_size(x_c[i], y_c[i])
2236+
x_c_min, y_c_min = x_.min(), y_.min()
2237+
x_c_max, y_c_max = x_.max(), y_.max()
2238+
v = create_vertice(x_, y_)
22142239
for j in range(nb_p):
22152240
if indexs[j] != -1:
22162241
continue
@@ -2235,9 +2260,10 @@ def insidepoly(x_p, y_p, x_c, y_c):
22352260
nb_c = x_c.shape[0]
22362261
flag = zeros(nb_p, dtype=numba_types.bool_)
22372262
for i in range(nb_c):
2238-
x_c_min, y_c_min = x_c[i].min(), y_c[i].min()
2239-
x_c_max, y_c_max = x_c[i].max(), y_c[i].max()
2240-
v = create_vertice(x_c[i], y_c[i])
2263+
x_, y_ = reduce_size(x_c[i], y_c[i])
2264+
x_c_min, y_c_min = x_.min(), y_.min()
2265+
x_c_max, y_c_max = x_.max(), y_.max()
2266+
v = create_vertice(x_, y_)
22412267
for j in range(nb_p):
22422268
if flag[j]:
22432269
continue

0 commit comments

Comments
 (0)