|
28 | 28 |
|
29 | 29 | """
|
30 | 30 | from numpy import empty, arange, where, unique, \
|
31 |
| - interp |
| 31 | + interp, ones, bool_, zeros |
32 | 32 | from .. import VAR_DESCR_inv
|
33 | 33 | import logging
|
34 | 34 | from datetime import datetime, timedelta
|
35 | 35 | from .observation import EddiesObservations
|
| 36 | +from numba import njit |
36 | 37 |
|
37 | 38 |
|
38 | 39 | class TrackEddiesObservations(EddiesObservations):
|
39 | 40 | """Class to practice Tracking on observations
|
40 | 41 | """
|
41 |
| - __slots__ = () |
| 42 | + __slots__ = ('__obs_by_track', '__first_index_of_track') |
42 | 43 |
|
43 |
| - ELEMENTS = ['lon', 'lat', 'radius_s', 'radius_e', 'amplitude', 'speed_radius', 'time', |
44 |
| - 'shape_error_e', 'shape_error_s', 'nb_contour_selected', |
45 |
| - 'height_max_speed_contour', 'height_external_contour', 'height_inner_contour', 'cost_association'] |
| 44 | + ELEMENTS = ['lon', 'lat', 'radius_s', 'radius_e', 'amplitude', 'speed_radius', 'time', 'shape_error_e', |
| 45 | + 'shape_error_s', 'nb_contour_selected', 'height_max_speed_contour', 'height_external_contour', |
| 46 | + 'height_inner_contour', 'cost_association'] |
| 47 | + |
| 48 | + def __init__(self, *args, **kwargs): |
| 49 | + super(TrackEddiesObservations, self).__init__(*args, **kwargs) |
| 50 | + self.__first_index_of_track = None |
| 51 | + self.__obs_by_track = None |
46 | 52 |
|
47 | 53 | def filled_by_interpolation(self, mask):
|
48 | 54 | """Filled selected values by interpolation
|
@@ -97,7 +103,7 @@ def extract_longer_eddies(self, nb_min, nb_obs, compress_id=True):
|
97 | 103 | def elements(self):
|
98 | 104 | elements = super(TrackEddiesObservations, self).elements
|
99 | 105 | elements.extend(['track', 'n', 'virtual'])
|
100 |
| - return elements |
| 106 | + return list(set(elements)) |
101 | 107 |
|
102 | 108 | def set_global_attr_netcdf(self, h_nc):
|
103 | 109 | """Set global attr
|
@@ -126,19 +132,110 @@ def extract_with_period(self, period, **kwargs):
|
126 | 132 | Returns:
|
127 | 133 | same object with selected data
|
128 | 134 | """
|
129 |
| - mask = (self.time > period[0]) * (self.time < period[1]) |
| 135 | + dataset_period = self.period |
| 136 | + p_min, p_max = period |
| 137 | + if p_min > 0: |
| 138 | + mask = self.time >= p_min |
| 139 | + elif p_min < 0: |
| 140 | + mask = self.time >= (dataset_period[0] - p_min) |
| 141 | + else: |
| 142 | + mask = ones(self.time.shape, dtype=bool_) |
| 143 | + if p_max > 0: |
| 144 | + mask *= self.time <= p_max |
| 145 | + elif p_max < 0: |
| 146 | + mask *= self.time <= (dataset_period[1] + p_max) |
130 | 147 | return self.__extract_with_mask(mask, **kwargs)
|
131 | 148 |
|
132 |
| - def __extract_with_mask(self, mask, full_path=False, remove_incomplete=False): |
| 149 | + @property |
| 150 | + def period(self): |
| 151 | + """ |
| 152 | + Give time coverage |
| 153 | + Returns: 2 date |
| 154 | + """ |
| 155 | + return self.time.min(), self.time.max() |
| 156 | + |
| 157 | + def get_mask_from_id(self, tracks): |
| 158 | + mask = zeros(self.tracks.shape, dtype=bool_) |
| 159 | + compute_mask_from_id(tracks, self.index_from_track, self.nb_obs_by_track, mask) |
| 160 | + return mask |
| 161 | + |
| 162 | + def compute_index(self): |
| 163 | + if self.__first_index_of_track is None: |
| 164 | + s = self.tracks.max() |
| 165 | + self.__first_index_of_track = -ones(s, self.tracks.dtype) |
| 166 | + self.__obs_by_track = zeros(s, self.observation_number.dtype) |
| 167 | + logging.debug('Start computing index ...') |
| 168 | + compute_index(self.tracks, self.__first_index_of_track, self.__obs_by_track) |
| 169 | + logging.debug('... OK') |
| 170 | + |
| 171 | + @property |
| 172 | + def index_from_track(self): |
| 173 | + self.compute_index() |
| 174 | + return self.__first_index_of_track |
| 175 | + |
| 176 | + @property |
| 177 | + def nb_obs_by_track(self): |
| 178 | + self.compute_index() |
| 179 | + return self.__obs_by_track |
| 180 | + |
| 181 | + def __extract_with_mask(self, mask, full_path=False, remove_incomplete=False, compress_id=False): |
133 | 182 | """
|
134 | 183 | Extract a subset of observations
|
135 | 184 | Args:
|
136 | 185 | mask: mask to select observations
|
137 | 186 | full_path: extract full path if only one part is selected
|
138 |
| - remove_incomplete: delete path which are not totatly selected |
| 187 | + remove_incomplete: delete path which are not fully selected |
| 188 | + compress_id: resample track number to use a little range |
139 | 189 |
|
140 | 190 | Returns:
|
141 | 191 | same object with selected observations
|
142 | 192 | """
|
143 | 193 | if full_path and remove_incomplete:
|
144 | 194 | logging.warning('Incompatible option, remove_incomplete option will be remove')
|
| 195 | + remove_incomplete = False |
| 196 | + |
| 197 | + if full_path: |
| 198 | + tracks = unique(self.tracks[mask]) |
| 199 | + mask = self.get_mask_from_id(tracks) |
| 200 | + elif remove_incomplete: |
| 201 | + raise Exception("") |
| 202 | + |
| 203 | + nb_obs = mask.sum() |
| 204 | + new = TrackEddiesObservations( |
| 205 | + size=nb_obs, |
| 206 | + track_extra_variables=self.track_extra_variables, |
| 207 | + track_array_variables=self.track_array_variables, |
| 208 | + array_variables=self.array_variables, |
| 209 | + raw_data=self.raw_data |
| 210 | + ) |
| 211 | + new.sign_type = self.sign_type |
| 212 | + if nb_obs == 0: |
| 213 | + logging.warning('Empty dataset will be created') |
| 214 | + else: |
| 215 | + for field in self.obs.dtype.descr: |
| 216 | + logging.debug('Copy of field %s ...', field) |
| 217 | + var = field[0] |
| 218 | + new.obs[var] = self.obs[var][mask] |
| 219 | + if compress_id: |
| 220 | + list_id = unique(new.obs['track']) |
| 221 | + list_id.sort() |
| 222 | + id_translate = arange(list_id.max() + 1) |
| 223 | + id_translate[list_id] = arange(len(list_id)) + 1 |
| 224 | + new.obs['track'] = id_translate[new.obs['track']] |
| 225 | + return new |
| 226 | + |
| 227 | + |
| 228 | +@njit(cache=True) |
| 229 | +def compute_index(tracks, index, number): |
| 230 | + previous_track = -1 |
| 231 | + for i, track in enumerate(tracks): |
| 232 | + if track != previous_track: |
| 233 | + index[track] = i |
| 234 | + number[track] += 1 |
| 235 | + previous_track = track |
| 236 | + |
| 237 | + |
| 238 | +@njit(cache=True) |
| 239 | +def compute_mask_from_id(tracks, first_index, number_of_obs, mask): |
| 240 | + for track in tracks: |
| 241 | + mask[first_index[track]:first_index[track] + number_of_obs[track]] = True |
0 commit comments