Skip to content

Commit d81604d

Browse files
committed
- add ellipse fit
- minor english modifs
1 parent 64efe1b commit d81604d

File tree

4 files changed

+72
-21
lines changed

4 files changed

+72
-21
lines changed

examples/16_network/pet_ioannou_2017_case.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,3 +188,47 @@ def update_axes(ax, mappable=None):
188188
m = close_to_i3.scatter_timeline(ax, "shape_error_e", vmin=14, vmax=70, **kw)
189189
cb = update_axes(ax, m["scatter"])
190190
cb.set_label("Effective shape error")
191+
192+
# %%
193+
# Rotation angle
194+
# --------------
195+
from py_eddy_tracker.generic import coordinates_to_local, local_to_coordinates
196+
from py_eddy_tracker.poly import fit_ellips
197+
theta_ = list()
198+
a_ = list()
199+
b_ = list()
200+
for obs in close_to_i3:
201+
x, y = obs['contour_lon_s'], obs['contour_lat_s']
202+
x0_, y0_ = x.mean(), y.mean()
203+
x_, y_ = coordinates_to_local(x, y, x0_, y0_)
204+
x0, y0, a, b, theta = fit_ellips(x_, y_)
205+
theta_.append(theta)
206+
a_.append(a)
207+
b_.append(b)
208+
a_=array(a_)
209+
b_=array(b_)
210+
211+
# %%
212+
# Theta
213+
ax = timeline_axes()
214+
m = close_to_i3.scatter_timeline(ax, theta_, vmin=-pi/2, vmax=pi/2, cmap='hsv')
215+
cb = update_axes(ax, m["scatter"])
216+
217+
# %%
218+
# A
219+
ax = timeline_axes()
220+
m = close_to_i3.scatter_timeline(ax, a_ * 1e-3, vmin=0, vmax=80, cmap='Spectral_r')
221+
cb = update_axes(ax, m["scatter"])
222+
223+
# %%
224+
# B
225+
ax = timeline_axes()
226+
m = close_to_i3.scatter_timeline(ax, b_ * 1e-3, vmin=0, vmax=80, cmap='Spectral_r')
227+
cb = update_axes(ax, m["scatter"])
228+
229+
# %%
230+
# A/B
231+
ax = timeline_axes()
232+
m = close_to_i3.scatter_timeline(ax, a_/b_, vmin=1, vmax=2, cmap='Spectral_r')
233+
cb = update_axes(ax, m["scatter"])
234+

src/py_eddy_tracker/dataset/grid.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -609,17 +609,17 @@ def eddy_identification(
609609
:param str vname: Grid name of v speed component
610610
:param datetime.datetime date: Date which will be stored in object to date data
611611
:param float,int step: Height between two layers in m
612-
:param float,int shape_error: Maximal error allowed for outter contour in %
612+
:param float,int shape_error: Maximal error allowed for outermost contour in %
613613
:param int sampling: Number of points to store contours and speed profile
614614
:param str sampling_method: Method to resample 'uniform' or 'visvalingam'
615615
:param (int,int),None pixel_limit:
616-
Min and max number of pixels inside the inner and the outer contour to be considered as an eddy
616+
Min and max number of pixels inside the inner and the outermost contour to be considered as an eddy
617617
:param float,None precision: Truncate values at the defined precision in m
618618
:param str force_height_unit: Unit used for height unit
619619
:param str force_speed_unit: Unit used for speed unit
620620
:param dict kwargs: Argument given to amplitude
621621
622-
:return: Return a list of 2 elements: Anticyclone and Cyclone
622+
:return: Return a list of 2 elements: Anticyclones and Cyclones
623623
:rtype: py_eddy_tracker.observations.observation.EddiesObservations
624624
625625
.. minigallery:: py_eddy_tracker.GridDataset.eddy_identification
@@ -729,7 +729,8 @@ def eddy_identification(
729729
for contour in contour_paths:
730730
if contour.used:
731731
continue
732-
# FIXME : center could be not in contour and fit on raw sampling
732+
# FIXME : center could be outside the contour due to the fit
733+
# FIXME : warning : the fit is made on raw sampling
733734
_, _, _, aerr = contour.fit_circle()
734735

735736
# Filter for shape
@@ -752,6 +753,7 @@ def eddy_identification(
752753
):
753754
continue
754755

756+
# Test the number of pixels within the outermost contour
755757
# FIXME : Maybe limit max must be replace with a maximum of surface
756758
if (
757759
contour.nb_pixel < pixel_limit[0]
@@ -760,6 +762,9 @@ def eddy_identification(
760762
contour.reject = 3
761763
continue
762764

765+
# Here the considered contour passed shape_error test, masked_pixels test,
766+
# values strictly above (AEs) or below (CEs) the contour, number_pixels test)
767+
763768
# Compute amplitude
764769
reset_centroid, amp = self.get_amplitude(
765770
contour,

src/py_eddy_tracker/eddy_feature.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
class Amplitude(object):
3232
"""
3333
Class to calculate *amplitude* and counts of *local maxima/minima*
34-
within a closed region of a sea level anomaly field.
34+
within a closed region of a sea surface height field.
3535
"""
3636

3737
EPSILON = 1e-8
@@ -66,8 +66,8 @@ def __init__(
6666
:param array data:
6767
:param float interval:
6868
:param int mle: maximum number of local maxima in contour
69-
:param int nb_step_min: number of interval to consider like an eddy
70-
:param int nb_step_to_be_mle: number of interval to be consider like another maxima
69+
:param int nb_step_min: number of intervals to consider an eddy
70+
:param int nb_step_to_be_mle: number of intervals to be considered as an another maxima
7171
"""
7272

7373
# Height of the contour
@@ -102,8 +102,9 @@ def __init__(
102102
self.nb_pixel = i_x.shape[0]
103103

104104
# Only pixel in contour
105+
# FIXME : change sla by ssh as the grid can be adt?
105106
self.sla = data[contour.pixels_index]
106-
# Amplitude which will be provide
107+
# Amplitude which will be provided
107108
self.amplitude = 0
108109
# Maximum local extrema accepted
109110
self.mle = mle
@@ -115,9 +116,10 @@ def within_amplitude_limits(self):
115116
def all_pixels_below_h0(self, level):
116117
"""
117118
Check CSS11 criterion 1: The SSH values of all of the pixels
118-
are below a given SSH threshold for cyclonic eddies.
119+
are below (above) a given SSH threshold for cyclonic (anticyclonic)
120+
eddies.
119121
"""
120-
# In some case pixel value must be very near of contour bounds
122+
# In some cases pixel value may be very close to the contour bounds
121123
if self.sla.mask.any() or ((self.sla.data - self.h_0) > self.EPSILON).any():
122124
return False
123125
else:
@@ -293,10 +295,10 @@ class Contours(object):
293295
294296
Attributes:
295297
contour:
296-
A matplotlib contour object of high-pass filtered SLA
298+
A matplotlib contour object of high-pass filtered SSH
297299
298300
eddy:
299-
A tracklist object holding the SLA data
301+
A tracklist object holding the SSH data
300302
301303
grd:
302304
A grid object
@@ -406,7 +408,7 @@ def __init__(self, x, y, z, levels, wrap_x=False, keep_unclose=False):
406408
fig = Figure()
407409
ax = fig.add_subplot(111)
408410
if wrap_x:
409-
logger.debug("wrapping activate to compute contour")
411+
logger.debug("wrapping activated to compute contour")
410412
x = concatenate((x, x[:1] + 360))
411413
z = ma.concatenate((z, z[:1]))
412414
logger.debug("X shape : %s", x.shape)
@@ -602,8 +604,8 @@ def display(
602604
Must be 'shape_error', 'x', 'y' or 'radius'.
603605
If define display_criterion is not use.
604606
bins argument must be define
605-
:param array bins: bins use to colorize contour
606-
:param str cmap: Name of cmap to use for field display
607+
:param array bins: bins used to colorize contour
608+
:param str cmap: Name of cmap for field display
607609
:param dict kwargs: look at :py:meth:`matplotlib.collections.LineCollection`
608610
609611
.. minigallery:: py_eddy_tracker.Contours.display
@@ -688,7 +690,7 @@ def display(
688690
ax.autoscale_view()
689691

690692
def label_contour_unused_which_contain_eddies(self, eddies):
691-
"""Select contour which contain several eddies"""
693+
"""Select contour containing several eddies"""
692694
if eddies.sign_type == 1:
693695
# anticyclonic
694696
sl = slice(None, -1)

src/py_eddy_tracker/poly.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -474,13 +474,13 @@ def polygon_overlap(p0, p1, minimal_area=False):
474474
return cost
475475

476476

477-
# FIXME: only one function are needed
477+
# FIXME: only one function is needed
478478
@njit(cache=True)
479479
def fit_circle(x, y):
480480
"""
481481
From a polygon, function will fit a circle.
482482
483-
Must be call with local coordinates (in m, to get a radius in m).
483+
Must be called with local coordinates (in m, to get a radius in m).
484484
485485
:param array x: x of polygon
486486
:param array y: y of polygon
@@ -510,11 +510,11 @@ def fit_circle(x, y):
510510
radius **= 0.5
511511
x0 *= scale
512512
y0 *= scale
513-
# radius of fitted circle
513+
# radius of fit circle
514514
radius *= scale
515-
# center X-position of fitted circle
515+
# center X-position of fit circle
516516
x0 += x_mean
517-
# center Y-position of fitted circle
517+
# center Y-position of fit circle
518518
y0 += y_mean
519519

520520
err = shape_error(x, y, x0, y0, radius)

0 commit comments

Comments
 (0)