11# -*- coding: utf-8 -*-
22"""
3- Class to manage observations gathered in track
3+ Class to manage observations gathered in trajectories
44"""
55import logging
66from 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,26 +611,32 @@ 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 )
623- m = local_ids ["previous_obs" ] == - 1
630+ # shift the local indices to the total indexation for the used observations
631+ m = local_ids ["previous_obs" ] != - 1
624632 local_ids ["previous_obs" ][m ] += i_s
625- m = local_ids ["next_obs" ] = = - 1
633+ m = local_ids ["next_obs" ] ! = - 1
626634 local_ids ["next_obs" ][m ] += i_s
627635 return ids
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 :
0 commit comments