55from glob import glob
66import logging
77import time
8-
8+ from datetime import timedelta , datetime
9+ import os
910import netCDF4
1011from numba import njit , types as nb_types
1112from numba .typed import List
1617 bincount ,
1718 bool_ ,
1819 concatenate ,
20+
1921 empty ,
2022 nan ,
2123 ones ,
@@ -120,13 +122,15 @@ def __repr__(self):
120122 period = (self .period [1 ] - self .period [0 ]) / 365.25
121123 nb_by_network = self .network_size ()
122124 nb_trash = 0 if self .ref_index != 0 else nb_by_network [0 ]
125+ lifetime = self .lifetime
123126 big = 50_000
124127 infos = [
125128 f"Atlas with { self .nb_network } networks ({ self .nb_network / period :0.0f} networks/year),"
126129 f" { self .nb_segment } segments ({ self .nb_segment / period :0.0f} segments/year), { len (self )} observations ({ len (self ) / period :0.0f} observations/year)" ,
127130 f" { m_event .size } merging ({ m_event .size / period :0.0f} merging/year), { s_event .size } splitting ({ s_event .size / period :0.0f} splitting/year)" ,
128131 f" with { (nb_by_network > big ).sum ()} network with more than { big } obs and the biggest have { nb_by_network .max ()} observations ({ nb_by_network [nb_by_network > big ].sum ()} observations cumulate)" ,
129132 f" { nb_trash } observations in trash" ,
133+ f" { lifetime .max ()} days max of lifetime" ,
130134 ]
131135 return "\n " .join (infos )
132136
@@ -201,6 +205,13 @@ def ref_segment_track_index(self):
201205 @property
202206 def ref_index (self ):
203207 return self .index_network [2 ]
208+
209+ @property
210+ def lifetime (self ):
211+ """Return lifetime for each observation"""
212+ lt = self .networks_period .astype ("int" )
213+ nb_by_network = self .network_size ()
214+ return lt .repeat (nb_by_network )
204215
205216 def network_segment_size (self , id_networks = None ):
206217 """Get number of segment by network
@@ -226,12 +237,15 @@ def network_size(self, id_networks=None):
226237 i = id_networks - self .index_network [2 ]
227238 return self .index_network [1 ][i ] - self .index_network [0 ][i ]
228239
240+ @property
229241 def networks_period (self ):
230242 """
231243 Return period for each network
232244 """
233245 return get_period_with_index (self .time , * self .index_network [:2 ])
234246
247+
248+
235249 def unique_segment_to_id (self , id_unique ):
236250 """Return id network and id segment for a unique id
237251
@@ -281,7 +295,7 @@ def astype(self, cls):
281295 new [k ][:] = self [k ][:]
282296 new .sign_type = self .sign_type
283297 return new
284-
298+
285299 def longer_than (self , nb_day_min = - 1 , nb_day_max = - 1 ):
286300 """
287301 Select network on time duration
@@ -1132,23 +1146,29 @@ def segment_track_array(self):
11321146 self ._segment_track_array = build_unique_array (self .segment , self .track )
11331147 return self ._segment_track_array
11341148
1135- def birth_event (self ):
1149+ def birth_event (self , only_index = False ):
11361150 """Extract birth events."""
11371151 i_start , _ , _ = self .index_segment_track
11381152 indices = i_start [self .previous_obs [i_start ] == - 1 ]
11391153 if self .first_is_trash ():
11401154 indices = indices [1 :]
1141- return self .extract_event (indices )
1142-
1155+ if only_index :
1156+ return indices
1157+ else :
1158+ return self .extract_event (indices )
1159+
11431160 generation_event = birth_event
11441161
1145- def death_event (self ):
1162+ def death_event (self , only_index = False ):
11461163 """Extract death events."""
11471164 _ , i_stop , _ = self .index_segment_track
11481165 indices = i_stop [self .next_obs [i_stop - 1 ] == - 1 ] - 1
11491166 if self .first_is_trash ():
11501167 indices = indices [1 :]
1151- return self .extract_event (indices )
1168+ if only_index :
1169+ return indices
1170+ else :
1171+ return self .extract_event (indices )
11521172
11531173 dissipation_event = death_event
11541174
@@ -1459,7 +1479,7 @@ def plot(self, ax, ref=None, color_cycle=None, **kwargs):
14591479 j += 1
14601480 return mappables
14611481
1462- def remove_dead_end (self , nobs = 3 , ndays = 0 , recursive = 0 , mask = None ):
1482+ def remove_dead_end (self , nobs = 3 , ndays = 0 , recursive = 0 , mask = None , return_mask = False ):
14631483 """
14641484 Remove short segments that don't connect several segments
14651485
@@ -1485,6 +1505,8 @@ def remove_dead_end(self, nobs=3, ndays=0, recursive=0, mask=None):
14851505 )
14861506 # get mask for selected obs
14871507 m = ~ self .segment_mask (segments_keep )
1508+ if return_mask :
1509+ return ~ m
14881510 self .track [m ] = 0
14891511 self .segment [m ] = 0
14901512 self .previous_obs [m ] = - 1
@@ -1502,6 +1524,8 @@ def remove_dead_end(self, nobs=3, ndays=0, recursive=0, mask=None):
15021524 self .sort ()
15031525 if recursive > 0 :
15041526 self .remove_dead_end (nobs , ndays , recursive - 1 )
1527+
1528+
15051529
15061530 def extract_segment (self , segments , absolute = False ):
15071531 """Extract given segments
@@ -2042,6 +2066,29 @@ def group_observations(self, min_overlap=0.2, minimal_area=False, **kwargs):
20422066 results , nb_obs = list (), list ()
20432067 # To display print only in INFO
20442068 display_iteration = logger .getEffectiveLevel () == logging .INFO
2069+
2070+
2071+ # Trier les fichiers par date
2072+ def extract_date (file ):
2073+ filename = os .path .basename (file )
2074+ date_str = filename .split ('_' )[- 1 ].split ('.' )[0 ] # Extraire la partie date (ex : "20180101")
2075+ return datetime .strptime (date_str , "%Y%m%d" ) # Convertir en objet datetime
2076+ self .filenames = sorted (self .filenames , key = extract_date )
2077+
2078+ # Detect missing date and print them to inform the user which files are missing
2079+ missing_dates = []
2080+ dates_list = [extract_date (self .filenames [i ]) for i in range (len (self .filenames ))]
2081+ for i in range (len (dates_list ) - 1 ):
2082+ expected_date = dates_list [i ] + timedelta (days = 1 )
2083+ while expected_date < dates_list [i + 1 ]:
2084+ missing_dates .append (expected_date )
2085+ expected_date += timedelta (days = 1 )
2086+ if missing_dates :
2087+ missing_str = ', ' .join (date .strftime ("%Y-%m-%d" ) for date in missing_dates )
2088+ raise Exception (f"Following files missing : { missing_str } " )
2089+ else :
2090+ print ("No missing files" )
2091+
20452092 for i , filename in enumerate (self .filenames ):
20462093 if display_iteration :
20472094 print (f"{ filename } compared to { self .window } next" , end = "\r " )
0 commit comments