Skip to content

Commit 8dfa98b

Browse files
authored
Merge branch 'master' into visvalingam
2 parents 5ffc05a + 4bf8147 commit 8dfa98b

File tree

16 files changed

+441
-281
lines changed

16 files changed

+441
-281
lines changed

examples/01_general_things/pet_storage.py

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
"""
1717

1818
import py_eddy_tracker_sample
19+
from matplotlib import pyplot as plt
20+
from numpy import arange, outer
1921

20-
from py_eddy_tracker.data import get_demo_path, get_remote_demo_sample
22+
from py_eddy_tracker.data import get_demo_path
2123
from py_eddy_tracker.observations.network import NetworkObservations
2224
from py_eddy_tracker.observations.observation import EddiesObservations, Table
2325
from py_eddy_tracker.observations.tracking import TrackEddiesObservations
@@ -32,7 +34,7 @@
3234
# array field like contour/profile are 2D column.
3335

3436
# %%
35-
# Eddies files (zarr or netcdf) could be loaded with ```load_file``` method:
37+
# Eddies files (zarr or netcdf) can be loaded with ```load_file``` method:
3638
eddies_collections = EddiesObservations.load_file(get_demo_path("Cyclonic_20160515.nc"))
3739
eddies_collections.field_table()
3840
# offset and scale_factor are used only when data is stored in zarr or netCDF4
@@ -58,6 +60,53 @@
5860
# ---------------
5961
# All contours are stored on the same number of points, and are resampled if needed with an algorithm to be stored as objects
6062

63+
# %%
64+
# Speed profile storage
65+
# ---------------------
66+
# Speed profile is an interpolation of speed mean along each contour.
67+
# For each contour included in eddy, we compute mean of speed along the contour,
68+
# and after we interpolate speed mean array on a fixed size array.
69+
#
70+
# Several field are available to understand "uavg_profile" :
71+
# 0. - num_contours : Number of contour in eddies, must be equal to amplitude divide by isoline step
72+
# 1. - height_inner_contour : height of inner contour used
73+
# 2. - height_max_speed_contour : height of max speed contour used
74+
# 3. - height_external_contour : height of outter contour used
75+
#
76+
# Last value of "uavg_profile" is for inner contour and first value for outter contour.
77+
78+
# Observations selection of "uavg_profile" with high number of contour(Eddy with high amplitude)
79+
e = eddies_collections.extract_with_mask(eddies_collections.num_contours > 15)
80+
81+
# %%
82+
83+
# Raw display of profiles with more than 15 contours
84+
ax = plt.subplot(111)
85+
_ = ax.plot(e.uavg_profile.T, lw=0.5)
86+
87+
# %%
88+
89+
# Profile from inner to outter
90+
ax = plt.subplot(111)
91+
ax.plot(e.uavg_profile[:, ::-1].T, lw=0.5)
92+
_ = ax.set_xlabel("From inner to outter contour"), ax.set_ylabel("Speed (m/s)")
93+
94+
# %%
95+
96+
# If we normalize indice of contour to set speed contour to 1 and inner contour to 0
97+
ax = plt.subplot(111)
98+
h_in = e.height_inner_contour
99+
h_s = e.height_max_speed_contour
100+
h_e = e.height_external_contour
101+
r = (h_e - h_in) / (h_s - h_in)
102+
nb_pt = e.uavg_profile.shape[1]
103+
# Create an x array for each profile
104+
x = outer(arange(nb_pt) / nb_pt, r)
105+
106+
ax.plot(x, e.uavg_profile[:, ::-1].T, lw=0.5)
107+
_ = ax.set_xlabel("From inner to outter contour"), ax.set_ylabel("Speed (m/s)")
108+
109+
61110
# %%
62111
# Trajectories
63112
# ------------
@@ -86,11 +135,7 @@
86135
# - next_obs : Index of the next observation in the full dataset, if -1 there are no next observation (the segment ends)
87136
# - previous_cost : Result of the cost function (1 is a good association, 0 is bad) with previous observation
88137
# - next_cost : Result of the cost function (1 is a good association, 0 is bad) with next observation
89-
eddies_network = NetworkObservations.load_file(
90-
get_remote_demo_sample(
91-
"eddies_med_adt_allsat_dt2018_err70_filt500_order1/Anticyclonic_network.nc"
92-
)
93-
)
138+
eddies_network = NetworkObservations.load_file(get_demo_path("network_med.nc"))
94139
eddies_network.field_table()
95140

96141
# %%

examples/14_generic_tools/pet_fit_contour.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,30 @@
1515
from py_eddy_tracker import data
1616
from py_eddy_tracker.generic import coordinates_to_local, local_to_coordinates
1717
from py_eddy_tracker.observations.observation import EddiesObservations
18-
from py_eddy_tracker.poly import fit_circle_, fit_ellips
18+
from py_eddy_tracker.poly import fit_circle_, fit_ellipse
1919

2020
# %%
2121
# Load example identification file
2222
a = EddiesObservations.load_file(data.get_demo_path("Anticyclonic_20190223.nc"))
2323

2424

2525
# %%
26-
# Function to draw circle or ellips from parameter
26+
# Function to draw circle or ellipse from parameter
2727
def build_circle(x0, y0, r):
2828
angle = radians(linspace(0, 360, 50))
2929
x_norm, y_norm = cos(angle), sin(angle)
3030
return local_to_coordinates(x_norm * r, y_norm * r, x0, y0)
3131

3232

33-
def build_ellips(x0, y0, a, b, theta):
33+
def build_ellipse(x0, y0, a, b, theta):
3434
angle = radians(linspace(0, 360, 50))
3535
x = a * cos(theta) * cos(angle) - b * sin(theta) * sin(angle)
3636
y = a * sin(theta) * cos(angle) + b * cos(theta) * sin(angle)
3737
return local_to_coordinates(x, y, x0, y0)
3838

3939

4040
# %%
41-
# Plot fitted circle or ellips on stored contour
41+
# Plot fitted circle or ellipse on stored contour
4242
xs, ys = a.contour_lon_s, a.contour_lat_s
4343

4444
fig = plt.figure(figsize=(15, 15))
@@ -51,9 +51,9 @@ def build_ellips(x0, y0, a, b, theta):
5151
ax = fig.add_subplot(4, 4, j)
5252
ax.grid(), ax.set_aspect("equal")
5353
ax.plot(x, y, label="store", color="black")
54-
x0, y0, a, b, theta = fit_ellips(x_, y_)
54+
x0, y0, a, b, theta = fit_ellipse(x_, y_)
5555
x0, y0 = local_to_coordinates(x0, y0, x0_, y0_)
56-
ax.plot(*build_ellips(x0, y0, a, b, theta), label="ellips", color="green")
56+
ax.plot(*build_ellipse(x0, y0, a, b, theta), label="ellipse", color="green")
5757
x0, y0, radius, shape_error = fit_circle_(x_, y_)
5858
x0, y0 = local_to_coordinates(x0, y0, x0_, y0_)
5959
ax.plot(*build_circle(x0, y0, radius), label="circle", color="red", lw=0.5)

examples/16_network/pet_follow_particle.py

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

3434
def save(self, *args, **kwargs):
3535
if args[0].endswith("gif"):
36-
# In this case gif is use to create thumbnail which are not use but consume same time than video
36+
# In this case gif is used to create thumbnail which are not used but consumes same time than video
3737
# So we create an empty file, to save time
3838
with open(args[0], "w") as _:
3939
pass
@@ -147,7 +147,7 @@ def particle_candidate(x, y, c, eddies, t_start, i_target, pct, **kwargs):
147147
e = eddies.extract_with_mask(m_start)
148148
# to be able to get global index
149149
translate_start = where(m_start)[0]
150-
# Identify particle in eddies(only in core)
150+
# Identify particle in eddies (only in core)
151151
i_start = e.contains(x, y, intern=True)
152152
m = i_start != -1
153153
x, y, i_start = x[m], y[m], i_start[m]
@@ -158,9 +158,9 @@ def particle_candidate(x, y, c, eddies, t_start, i_target, pct, **kwargs):
158158
e_end = eddies.extract_with_mask(m_end)
159159
# to be able to get global index
160160
translate_end = where(m_end)[0]
161-
# Id eddies for each alive particle(in core and extern)
161+
# Id eddies for each alive particle (in core and extern)
162162
i_end = e_end.contains(x, y)
163-
# compute matrix and filled target array
163+
# compute matrix and fill target array
164164
get_matrix(i_start, i_end, translate_start, translate_end, i_target, pct)
165165

166166

@@ -169,7 +169,7 @@ def get_matrix(i_start, i_end, translate_start, translate_end, i_target, pct):
169169
nb_start, nb_end = translate_start.size, translate_end.size
170170
# Matrix which will store count for every couple
171171
count = zeros((nb_start, nb_end), dtype=nb_types.int32)
172-
# Number of particle in each origin observation
172+
# Number of particles in each origin observation
173173
ref = zeros(nb_start, dtype=nb_types.int32)
174174
# For each particle
175175
for i in range(i_start.size):
@@ -181,7 +181,7 @@ def get_matrix(i_start, i_end, translate_start, translate_end, i_target, pct):
181181
for i in range(nb_start):
182182
for j in range(nb_end):
183183
pct_ = count[i, j]
184-
# If there are particle from i to j
184+
# If there are particles from i to j
185185
if pct_ != 0:
186186
# Get percent
187187
pct_ = pct_ / ref[i] * 100.0

examples/16_network/pet_ioannou_2017_case.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@
1414
from matplotlib import pyplot as plt
1515
from matplotlib.animation import FuncAnimation
1616
from matplotlib.ticker import FuncFormatter
17-
from numpy import arange, where
17+
from numpy import arange, where, array, pi
1818

1919
from py_eddy_tracker.appli.gui import Anim
2020
from py_eddy_tracker.data import get_demo_path
2121
from py_eddy_tracker.gui import GUI_AXES
2222
from py_eddy_tracker.observations.network import NetworkObservations
2323

24+
from py_eddy_tracker.generic import coordinates_to_local, local_to_coordinates
25+
from py_eddy_tracker.poly import fit_ellipse
2426

2527
# %%
28+
29+
2630
class VideoAnimation(FuncAnimation):
2731
def _repr_html_(self, *args, **kwargs):
2832
"""To get video in html and have a player"""
@@ -188,3 +192,47 @@ def update_axes(ax, mappable=None):
188192
m = close_to_i3.scatter_timeline(ax, "shape_error_e", vmin=14, vmax=70, **kw)
189193
cb = update_axes(ax, m["scatter"])
190194
cb.set_label("Effective shape error")
195+
196+
# %%
197+
# Rotation angle
198+
# --------------
199+
# For each obs, fit an ellipse to the contour, with theta the angle from the x-axis,
200+
# a the semi ax in x direction and b the semi ax in y dimension
201+
202+
theta_ = list()
203+
a_ = list()
204+
b_ = list()
205+
for obs in close_to_i3:
206+
x, y = obs["contour_lon_s"], obs["contour_lat_s"]
207+
x0_, y0_ = x.mean(), y.mean()
208+
x_, y_ = coordinates_to_local(x, y, x0_, y0_)
209+
x0, y0, a, b, theta = fit_ellipse(x_, y_)
210+
theta_.append(theta)
211+
a_.append(a)
212+
b_.append(b)
213+
a_ = array(a_)
214+
b_ = array(b_)
215+
216+
# %%
217+
# Theta
218+
ax = timeline_axes()
219+
m = close_to_i3.scatter_timeline(ax, theta_, vmin=-pi / 2, vmax=pi / 2, cmap="hsv")
220+
cb = update_axes(ax, m["scatter"])
221+
222+
# %%
223+
# a
224+
ax = timeline_axes()
225+
m = close_to_i3.scatter_timeline(ax, a_ * 1e-3, vmin=0, vmax=80, cmap="Spectral_r")
226+
cb = update_axes(ax, m["scatter"])
227+
228+
# %%
229+
# b
230+
ax = timeline_axes()
231+
m = close_to_i3.scatter_timeline(ax, b_ * 1e-3, vmin=0, vmax=80, cmap="Spectral_r")
232+
cb = update_axes(ax, m["scatter"])
233+
234+
# %%
235+
# a/b
236+
ax = timeline_axes()
237+
m = close_to_i3.scatter_timeline(ax, a_ / b_, vmin=1, vmax=2, cmap="Spectral_r")
238+
cb = update_axes(ax, m["scatter"])

notebooks/python_module/01_general_things/pet_storage.ipynb

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
},
2727
"outputs": [],
2828
"source": [
29-
"import py_eddy_tracker_sample\n\nfrom py_eddy_tracker.data import get_demo_path, get_remote_demo_sample\nfrom py_eddy_tracker.observations.network import NetworkObservations\nfrom py_eddy_tracker.observations.observation import EddiesObservations, Table\nfrom py_eddy_tracker.observations.tracking import TrackEddiesObservations"
29+
"import py_eddy_tracker_sample\nfrom matplotlib import pyplot as plt\nfrom numpy import arange, outer\n\nfrom py_eddy_tracker.data import get_demo_path\nfrom py_eddy_tracker.observations.network import NetworkObservations\nfrom py_eddy_tracker.observations.observation import EddiesObservations, Table\nfrom py_eddy_tracker.observations.tracking import TrackEddiesObservations"
3030
]
3131
},
3232
{
@@ -108,6 +108,57 @@
108108
"## Contour storage\nAll contours are stored on the same number of points, and are resampled if needed with an algorithm to be stored as objects\n\n"
109109
]
110110
},
111+
{
112+
"cell_type": "markdown",
113+
"metadata": {},
114+
"source": [
115+
"## Speed profile storage\nSpeed profile is an interpolation of speed mean along each contour.\nFor each contour included in eddy, we compute mean of speed along the contour,\nand after we interpolate speed mean array on a fixed size array.\n\nSeveral field are available to understand \"uavg_profile\" :\n 0. - num_contours : Number of contour in eddies, must be equal to amplitude divide by isoline step\n 1. - height_inner_contour : height of inner contour used\n 2. - height_max_speed_contour : height of max speed contour used\n 3. - height_external_contour : height of outter contour used\n\nLast value of \"uavg_profile\" is for inner contour and first value for outter contour.\n\n"
116+
]
117+
},
118+
{
119+
"cell_type": "code",
120+
"execution_count": null,
121+
"metadata": {
122+
"collapsed": false
123+
},
124+
"outputs": [],
125+
"source": [
126+
"# Observations selection of \"uavg_profile\" with high number of contour(Eddy with high amplitude)\ne = eddies_collections.extract_with_mask(eddies_collections.num_contours > 15)"
127+
]
128+
},
129+
{
130+
"cell_type": "code",
131+
"execution_count": null,
132+
"metadata": {
133+
"collapsed": false
134+
},
135+
"outputs": [],
136+
"source": [
137+
"# Raw display of profiles with more than 15 contours\nax = plt.subplot(111)\n_ = ax.plot(e.uavg_profile.T, lw=0.5)"
138+
]
139+
},
140+
{
141+
"cell_type": "code",
142+
"execution_count": null,
143+
"metadata": {
144+
"collapsed": false
145+
},
146+
"outputs": [],
147+
"source": [
148+
"# Profile from inner to outter\nax = plt.subplot(111)\nax.plot(e.uavg_profile[:, ::-1].T, lw=0.5)\n_ = ax.set_xlabel(\"From inner to outter contour\"), ax.set_ylabel(\"Speed (m/s)\")"
149+
]
150+
},
151+
{
152+
"cell_type": "code",
153+
"execution_count": null,
154+
"metadata": {
155+
"collapsed": false
156+
},
157+
"outputs": [],
158+
"source": [
159+
"# If we normalize indice of contour to set speed contour to 1 and inner contour to 0\nax = plt.subplot(111)\nh_in = e.height_inner_contour\nh_s = e.height_max_speed_contour\nh_e = e.height_external_contour\nr = (h_e - h_in) / (h_s - h_in)\nnb_pt = e.uavg_profile.shape[1]\n# Create an x array for each profile\nx = outer(arange(nb_pt) / nb_pt, r)\n\nax.plot(x, e.uavg_profile[:, ::-1].T, lw=0.5)\n_ = ax.set_xlabel(\"From inner to outter contour\"), ax.set_ylabel(\"Speed (m/s)\")"
160+
]
161+
},
111162
{
112163
"cell_type": "markdown",
113164
"metadata": {},
@@ -141,7 +192,7 @@
141192
},
142193
"outputs": [],
143194
"source": [
144-
"eddies_network = NetworkObservations.load_file(\n get_remote_demo_sample(\n \"eddies_med_adt_allsat_dt2018_err70_filt500_order1/Anticyclonic_network.nc\"\n )\n)\neddies_network.field_table()"
195+
"eddies_network = NetworkObservations.load_file(get_demo_path(\"network_med.nc\"))\neddies_network.field_table()"
145196
]
146197
},
147198
{
@@ -179,7 +230,7 @@
179230
"name": "python",
180231
"nbconvert_exporter": "python",
181232
"pygments_lexer": "ipython3",
182-
"version": "3.7.7"
233+
"version": "3.9.2"
183234
}
184235
},
185236
"nbformat": 4,

src/py_eddy_tracker/appli/network.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def build_network():
3737

3838

3939
def divide_network():
40-
parser = EddyParser("Separate path for a same group(network)")
40+
parser = EddyParser("Separate path for a same group (network)")
4141
parser.add_argument("input", help="input network file")
4242
parser.add_argument("out", help="output file")
4343
parser.contour_intern_arg()
@@ -66,7 +66,7 @@ def subset_network():
6666
"--length",
6767
nargs=2,
6868
type=int,
69-
help="Nb of day which must be cover by network, first minimum number of day and last maximum number of day,"
69+
help="Nb of days that must be covered by the network, first minimum number of day and last maximum number of day,"
7070
"if value is negative, this bound won't be used",
7171
)
7272
parser.add_argument(
@@ -85,8 +85,8 @@ def subset_network():
8585
"--period",
8686
nargs=2,
8787
type=int,
88-
help="Start day and end day, if it's negative value we will add to day min and add to day max,"
89-
"if 0 it s not use",
88+
help="Start day and end day, if it's a negative value we will add to day min and add to day max,"
89+
"if 0 it is not used",
9090
)
9191
args = parser.parse_args()
9292
n = NetworkObservations.load_file(args.input, raw_data=True)

0 commit comments

Comments
 (0)