5
5
from glob import glob
6
6
import logging
7
7
import time
8
-
8
+ from datetime import timedelta , datetime
9
+ import os
9
10
import netCDF4
10
11
from numba import njit , types as nb_types
11
12
from numba .typed import List
16
17
bincount ,
17
18
bool_ ,
18
19
concatenate ,
20
+
19
21
empty ,
20
22
nan ,
21
23
ones ,
@@ -120,13 +122,15 @@ def __repr__(self):
120
122
period = (self .period [1 ] - self .period [0 ]) / 365.25
121
123
nb_by_network = self .network_size ()
122
124
nb_trash = 0 if self .ref_index != 0 else nb_by_network [0 ]
125
+ lifetime = self .lifetime
123
126
big = 50_000
124
127
infos = [
125
128
f"Atlas with { self .nb_network } networks ({ self .nb_network / period :0.0f} networks/year),"
126
129
f" { self .nb_segment } segments ({ self .nb_segment / period :0.0f} segments/year), { len (self )} observations ({ len (self ) / period :0.0f} observations/year)" ,
127
130
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)" ,
128
131
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)" ,
129
132
f" { nb_trash } observations in trash" ,
133
+ f" { lifetime .max ()} days max of lifetime" ,
130
134
]
131
135
return "\n " .join (infos )
132
136
@@ -201,6 +205,13 @@ def ref_segment_track_index(self):
201
205
@property
202
206
def ref_index (self ):
203
207
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 )
204
215
205
216
def network_segment_size (self , id_networks = None ):
206
217
"""Get number of segment by network
@@ -226,12 +237,15 @@ def network_size(self, id_networks=None):
226
237
i = id_networks - self .index_network [2 ]
227
238
return self .index_network [1 ][i ] - self .index_network [0 ][i ]
228
239
240
+ @property
229
241
def networks_period (self ):
230
242
"""
231
243
Return period for each network
232
244
"""
233
245
return get_period_with_index (self .time , * self .index_network [:2 ])
234
246
247
+
248
+
235
249
def unique_segment_to_id (self , id_unique ):
236
250
"""Return id network and id segment for a unique id
237
251
@@ -281,7 +295,7 @@ def astype(self, cls):
281
295
new [k ][:] = self [k ][:]
282
296
new .sign_type = self .sign_type
283
297
return new
284
-
298
+
285
299
def longer_than (self , nb_day_min = - 1 , nb_day_max = - 1 ):
286
300
"""
287
301
Select network on time duration
@@ -1132,23 +1146,29 @@ def segment_track_array(self):
1132
1146
self ._segment_track_array = build_unique_array (self .segment , self .track )
1133
1147
return self ._segment_track_array
1134
1148
1135
- def birth_event (self ):
1149
+ def birth_event (self , only_index = False ):
1136
1150
"""Extract birth events."""
1137
1151
i_start , _ , _ = self .index_segment_track
1138
1152
indices = i_start [self .previous_obs [i_start ] == - 1 ]
1139
1153
if self .first_is_trash ():
1140
1154
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
+
1143
1160
generation_event = birth_event
1144
1161
1145
- def death_event (self ):
1162
+ def death_event (self , only_index = False ):
1146
1163
"""Extract death events."""
1147
1164
_ , i_stop , _ = self .index_segment_track
1148
1165
indices = i_stop [self .next_obs [i_stop - 1 ] == - 1 ] - 1
1149
1166
if self .first_is_trash ():
1150
1167
indices = indices [1 :]
1151
- return self .extract_event (indices )
1168
+ if only_index :
1169
+ return indices
1170
+ else :
1171
+ return self .extract_event (indices )
1152
1172
1153
1173
dissipation_event = death_event
1154
1174
@@ -1459,7 +1479,7 @@ def plot(self, ax, ref=None, color_cycle=None, **kwargs):
1459
1479
j += 1
1460
1480
return mappables
1461
1481
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 ):
1463
1483
"""
1464
1484
Remove short segments that don't connect several segments
1465
1485
@@ -1485,6 +1505,8 @@ def remove_dead_end(self, nobs=3, ndays=0, recursive=0, mask=None):
1485
1505
)
1486
1506
# get mask for selected obs
1487
1507
m = ~ self .segment_mask (segments_keep )
1508
+ if return_mask :
1509
+ return ~ m
1488
1510
self .track [m ] = 0
1489
1511
self .segment [m ] = 0
1490
1512
self .previous_obs [m ] = - 1
@@ -1502,6 +1524,8 @@ def remove_dead_end(self, nobs=3, ndays=0, recursive=0, mask=None):
1502
1524
self .sort ()
1503
1525
if recursive > 0 :
1504
1526
self .remove_dead_end (nobs , ndays , recursive - 1 )
1527
+
1528
+
1505
1529
1506
1530
def extract_segment (self , segments , absolute = False ):
1507
1531
"""Extract given segments
@@ -2042,6 +2066,29 @@ def group_observations(self, min_overlap=0.2, minimal_area=False, **kwargs):
2042
2066
results , nb_obs = list (), list ()
2043
2067
# To display print only in INFO
2044
2068
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
+
2045
2092
for i , filename in enumerate (self .filenames ):
2046
2093
if display_iteration :
2047
2094
print (f"{ filename } compared to { self .window } next" , end = "\r " )
0 commit comments