Skip to content

Commit 40ef190

Browse files
committed
# Conflicts: # src/py_eddy_tracker/observations/tracking.py
2 parents ac9c142 + ee5de9e commit 40ef190

File tree

3 files changed

+56
-42
lines changed

3 files changed

+56
-42
lines changed

src/py_eddy_tracker/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def parse_args(self, *args, **kwargs):
197197
nc_attr=dict(
198198
units="degrees_east",
199199
axis="X",
200-
comment="Longitude center of the fitted circle",
200+
comment="Longitude center of the fit circle",
201201
long_name="Eddy Center Longitude",
202202
standard_name="longitude",
203203
),
@@ -214,7 +214,7 @@ def parse_args(self, *args, **kwargs):
214214
axis="Y",
215215
long_name="Eddy Center Latitude",
216216
standard_name="latitude",
217-
comment="Latitude center of the fitted circle",
217+
comment="Latitude center of the fit circle",
218218
),
219219
),
220220
lon_max=dict(

src/py_eddy_tracker/observations/tracking.py

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
"""
3-
Class to manage observations gathered in track
3+
Class to manage observations gathered in trajectories
44
"""
55
import logging
66
from datetime import datetime, timedelta
@@ -123,7 +123,7 @@ def __repr__(self):
123123
return content
124124

125125
def add_distance(self):
126-
"""Add a field of distance (m) between to consecutive observation, 0 for the last observation of each track"""
126+
"""Add a field of distance (m) between two consecutive observations, 0 for the last observation of each track"""
127127
if "distance_next" in self.observations.dtype.descr:
128128
return self
129129
new = self.add_fields(("distance_next",))
@@ -181,7 +181,7 @@ def filled_by_interpolation(self, mask):
181181
)
182182

183183
def extract_longer_eddies(self, nb_min, nb_obs, compress_id=True):
184-
"""Select eddies which are longer than nb_min"""
184+
"""Select the trajectories longer than nb_min"""
185185
mask = nb_obs >= nb_min
186186
nb_obs_select = mask.sum()
187187
logger.info("Selection of %d observations", nb_obs_select)
@@ -226,9 +226,9 @@ def set_global_attr_netcdf(self, h_nc):
226226

227227
def extract_with_period(self, period, **kwargs):
228228
"""
229-
Extract with a period
229+
Extract within a time period
230230
231-
:param (int,int) period: two date to define period, must be specify from 1/1/1950
231+
:param (int,int) period: two dates to define the period, must be specify from 1/1/1950
232232
:param dict kwargs: look at :py:meth:`extract_with_mask`
233233
:return: Return all eddy tracks which are in bounds
234234
:rtype: TrackEddiesObservations
@@ -251,9 +251,9 @@ def extract_with_period(self, period, **kwargs):
251251

252252
def get_azimuth(self, equatorward=False):
253253
"""
254-
Return azimuth for each tracks.
254+
Return azimuth for each track.
255255
256-
Azimuth is compute with first and last observation
256+
Azimuth is computed with first and last observation
257257
258258
:param bool equatorward: If True, Poleward are positive and equatorward negative
259259
:rtype: array
@@ -285,7 +285,7 @@ def compute_index(self):
285285
"""
286286
if self.__first_index_of_track is None:
287287
s = self.tracks.max() + 1
288-
# Doesn't work => core dump with numba, maybe he wait i8 instead of u4
288+
# Doesn't work => core dump with numba, maybe he wants i8 instead of u4
289289
# self.__first_index_of_track = -ones(s, self.tracks.dtype)
290290
# self.__obs_by_track = zeros(s, self.observation_number.dtype)
291291
self.__first_index_of_track = -ones(s, "i8")
@@ -333,12 +333,12 @@ def nb_obs_by_track(self):
333333

334334
@property
335335
def lifetime(self):
336-
"""Return for each observation lifetime"""
336+
"""Return lifetime for each observation"""
337337
return self.nb_obs_by_track.repeat(self.nb_obs_by_track)
338338

339339
@property
340340
def age(self):
341-
"""Return for each observation age in %, will be [0:100]"""
341+
"""Return age in % for each observation, will be [0:100]"""
342342
return self.n.astype("f4") / (self.lifetime - 1) * 100.0
343343

344344
def extract_ids(self, tracks):
@@ -347,10 +347,10 @@ def extract_ids(self, tracks):
347347

348348
def extract_toward_direction(self, west=True, delta_lon=None):
349349
"""
350-
Get eddy which go in same direction
350+
Get trajectories going in the same direction
351351
352-
:param bool west: Only eastward eddy if True return westward
353-
:param None,float delta_lon: Only eddy with more than delta_lon span in longitude
352+
:param bool west: Only eastward eddies if True return westward
353+
:param None,float delta_lon: Only eddies with more than delta_lon span in longitude
354354
:return: Only eastern eddy
355355
:rtype: __class__
356356
@@ -397,10 +397,10 @@ def extract_in_direction(self, direction, value=0):
397397

398398
def extract_with_length(self, bounds):
399399
"""
400-
Return all observations in [b0:b1]
400+
Return the observations within trajectories lasting between [b0:b1]
401401
402-
:param (int,int) bounds: length min and max of selected eddies, if use of -1 this bound is not used
403-
:return: Return all eddy tracks which have length between bounds
402+
:param (int,int) bounds: length min and max of the desired trajectories, if -1 this bound is not used
403+
:return: Return all trajectories having length between bounds
404404
:rtype: TrackEddiesObservations
405405
406406
.. minigallery:: py_eddy_tracker.TrackEddiesObservations.extract_with_length
@@ -460,11 +460,11 @@ def extract_with_mask(
460460
Extract a subset of observations
461461
462462
:param array(bool) mask: mask to select observations
463-
:param bool full_path: extract full path if only one part is selected
464-
:param bool remove_incomplete: delete path which are not fully selected
465-
:param bool compress_id: resample track number to use a little range
466-
:param bool reject_virtual: if track are only virtual in selection we remove track
467-
:return: same object with selected observations
463+
:param bool full_path: extract the full trajectory if only one part is selected
464+
:param bool remove_incomplete: delete trajectory if not fully selected
465+
:param bool compress_id: resample trajectory number to use a smaller range
466+
:param bool reject_virtual: if only virtual are selected, the trajectory is removed
467+
:return: same object with the selected observations
468468
:rtype: self.__class__
469469
"""
470470
if full_path and remove_incomplete:
@@ -509,7 +509,9 @@ def re_reference_index(index, ref):
509509

510510
def shape_polygon(self, intern=False):
511511
"""
512-
Get polygons which enclosed each track
512+
Get the polygon enclosing each trajectory.
513+
514+
The polygon merges the non-overlapping bounds of the specified contours
513515
514516
:param bool intern: If True use speed contour instead of effective contour
515517
:rtype: list(array, array)
@@ -519,9 +521,9 @@ def shape_polygon(self, intern=False):
519521

520522
def display_shape(self, ax, ref=None, intern=False, **kwargs):
521523
"""
522-
This function will draw shape of each track
524+
This function will draw the shape of each trajectory
523525
524-
:param matplotlib.axes.Axes ax: ax where drawed
526+
:param matplotlib.axes.Axes ax: ax to draw
525527
:param float,int ref: if defined all coordinates will be wrapped with ref like west boundary
526528
:param bool intern: If True use speed contour instead of effective contour
527529
:param dict kwargs: keyword arguments for Axes.plot
@@ -546,10 +548,10 @@ def display_shape(self, ax, ref=None, intern=False, **kwargs):
546548

547549
def close_tracks(self, other, nb_obs_min=10, **kwargs):
548550
"""
549-
Get close from another atlas.
551+
Get close trajectories from another atlas.
550552
551553
:param self other: Atlas to compare
552-
:param int nb_obs_min: Minimal number of overlap for one track
554+
:param int nb_obs_min: Minimal number of overlap for one trajectory
553555
:param dict kwargs: keyword arguments for match function
554556
:return: return other atlas reduce to common track with self
555557
@@ -576,10 +578,10 @@ def format_label(self, label):
576578

577579
def plot(self, ax, ref=None, **kwargs):
578580
"""
579-
This function will draw path of each track
581+
This function will draw path of each trajectory
580582
581-
:param matplotlib.axes.Axes ax: ax where drawed
582-
:param float,int ref: if defined all coordinates will be wrapped with ref like west boundary
583+
:param matplotlib.axes.Axes ax: ax to draw
584+
:param float,int ref: if defined, all coordinates will be wrapped with ref like west boundary
583585
:param dict kwargs: keyword arguments for Axes.plot
584586
:return: matplotlib mappable
585587
"""
@@ -594,7 +596,7 @@ def plot(self, ax, ref=None, **kwargs):
594596
return ax.plot(x, y, **kwargs)
595597

596598
def split_network(self, intern=True, **kwargs):
597-
"""Divide each group in track"""
599+
"""Return each group (network) divided in segments"""
598600
track_s, track_e, track_ref = build_index(self.tracks)
599601
ids = empty(
600602
len(self),
@@ -609,17 +611,23 @@ def split_network(self, intern=True, **kwargs):
609611
],
610612
)
611613
ids["group"], ids["time"] = self.tracks, self.time
612-
# To store id track
614+
# Initialisation
615+
# To store the id of the segments, the backward and forward cost associations
613616
ids["track"], ids["previous_cost"], ids["next_cost"] = 0, 0, 0
617+
# To store the indexes of the backward and forward observations associated
614618
ids["previous_obs"], ids["next_obs"] = -1, -1
619+
# At the end, ids["previous_obs"] == -1 means the start of a non-split segment
620+
# and ids["next_obs"] == -1 means the end of a non-merged segment
615621

616622
xname, yname = self.intern(intern)
617623
for i_s, i_e in zip(track_s, track_e):
618624
if i_s == i_e or self.tracks[i_s] == self.NOGROUP:
619625
continue
620626
sl = slice(i_s, i_e)
621627
local_ids = ids[sl]
628+
# built segments with local indices
622629
self.set_tracks(self[xname][sl], self[yname][sl], local_ids, **kwargs)
630+
# shift the local indices to the total indexation for the used observations
623631
m = local_ids["previous_obs"] != -1
624632
local_ids["previous_obs"][m] += i_s
625633
m = local_ids["next_obs"] != -1
@@ -628,7 +636,7 @@ def split_network(self, intern=True, **kwargs):
628636

629637
def set_tracks(self, x, y, ids, window, **kwargs):
630638
"""
631-
Will split one group in tracks
639+
Will split one group (network) in segments
632640
633641
:param array x: coordinates of group
634642
:param array y: coordinates of group
@@ -640,18 +648,21 @@ def set_tracks(self, x, y, ids, window, **kwargs):
640648
nb = x.shape[0]
641649
used = zeros(nb, dtype="bool")
642650
track_id = 1
643-
# build all polygon (need to check if wrap is needed)
651+
# build all polygons (need to check if wrap is needed)
644652
for i in range(nb):
645-
# If observation already in one track, we go to the next one
653+
# If the observation is already in one track, we go to the next one
646654
if used[i]:
647655
continue
656+
# Search a possible continuation (forward)
648657
self.follow_obs(i, track_id, used, ids, x, y, *time_index, window, **kwargs)
649658
track_id += 1
650-
# Search a possible ancestor
659+
# Search a possible ancestor (backward)
651660
self.previous_obs(i, ids, x, y, *time_index, window, **kwargs)
652661

653662
@classmethod
654663
def follow_obs(cls, i_next, track_id, used, ids, *args, **kwargs):
664+
"""Associate the observations to the segments"""
665+
655666
while i_next != -1:
656667
# Flag
657668
used[i_next] = True
@@ -675,14 +686,16 @@ def follow_obs(cls, i_next, track_id, used, ids, *args, **kwargs):
675686

676687
@staticmethod
677688
def previous_obs(i_current, ids, x, y, time_s, time_e, time_ref, window, **kwargs):
689+
"""Backward association of observations to the segments"""
690+
678691
time_cur = ids["time"][i_current]
679692
t0, t1 = time_cur - 1 - time_ref, max(time_cur - window - time_ref, 0)
680693
for t_step in range(t0, t1 - 1, -1):
681694
i0, i1 = time_s[t_step], time_e[t_step]
682695
# No observation at the time step
683696
if i0 == i1:
684697
continue
685-
# Intersection / union, to be able to separte in case of multiple inside
698+
# Search for overlaps
686699
xi, yi, xj, yj = x[[i_current]], y[[i_current]], x[i0:i1], y[i0:i1]
687700
ii, ij = bbox_intersection(xi, yi, xj, yj)
688701
if len(ii) == 0:
@@ -703,6 +716,7 @@ def previous_obs(i_current, ids, x, y, time_s, time_e, time_ref, window, **kwarg
703716

704717
@staticmethod
705718
def next_obs(i_current, ids, x, y, time_s, time_e, time_ref, window, **kwargs):
719+
"""Forward association of observations to the segments"""
706720
time_max = time_e.shape[0] - 1
707721
time_cur = ids["time"][i_current]
708722
t0, t1 = time_cur + 1 - time_ref, min(time_cur + window - time_ref, time_max)
@@ -713,7 +727,7 @@ def next_obs(i_current, ids, x, y, time_s, time_e, time_ref, window, **kwargs):
713727
# No observation at the time step
714728
if i0 == i1:
715729
continue
716-
# Intersection / union, to be able to separte in case of multiple inside
730+
# Search for overlaps
717731
xi, yi, xj, yj = x[[i_current]], y[[i_current]], x[i0:i1], y[i0:i1]
718732
ii, ij = bbox_intersection(xi, yi, xj, yj)
719733
if len(ii) == 0:

src/py_eddy_tracker/poly.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ def polygon_overlap(p0, p1, minimal_area=False):
452452
453453
:param list(Polygon) p0: List of polygon to compare with p1 list
454454
:param list(Polygon) p1: List of polygon to compare with p0 list
455-
:param bool minimal_area: If True, function will compute intersection/little polygon, else intersection/union
455+
:param bool minimal_area: If True, function will compute intersection/smaller polygon, else intersection/union
456456
:return: Result of cost function
457457
:rtype: array
458458
"""
@@ -462,10 +462,10 @@ def polygon_overlap(p0, p1, minimal_area=False):
462462
p_ = p1[i]
463463
# Area of intersection
464464
intersection = (p0 & p_).area()
465-
# we divide intersection with the little one result from 0 to 1
465+
# we divide the intersection by the smaller area, result from 0 to 1
466466
if minimal_area:
467467
cost[i] = intersection / min(p0.area(), p_.area())
468-
# we divide intersection with polygon merging result from 0 to 1
468+
# we divide the intersection by the merged polygons area, result from 0 to 1
469469
else:
470470
cost[i] = intersection / (p0 + p_).area()
471471
return cost

0 commit comments

Comments
 (0)