Skip to content

Commit c90fb0d

Browse files
committed
Add function to subset atlas
1 parent 75fbcea commit c90fb0d

File tree

4 files changed

+152
-9
lines changed

4 files changed

+152
-9
lines changed

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
scripts=[
1717
'src/scripts/GridFiltering',
1818
'src/scripts/EddyId',
19+
'src/scripts/EddySubSetter',
1920
'src/scripts/EddyTracking',
2021
'src/scripts/EddyFinalTracking',
2122
'src/scripts/EddyMergeCorrespondances',

src/py_eddy_tracker/observations/observation.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,18 @@ def __init__(
174174
self.active = True
175175
self.sign_type = None
176176

177+
@property
178+
def time(self):
179+
return self.observations["time"]
180+
181+
@property
182+
def tracks(self):
183+
return self.observations["track"]
184+
185+
@property
186+
def observation_number(self):
187+
return self.observations["n"]
188+
177189
@property
178190
def sign_legend(self):
179191
return "Cyclonic" if self.sign_type == -1 else "Anticyclonic"

src/py_eddy_tracker/observations/tracking.py

Lines changed: 106 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,27 @@
2828
2929
"""
3030
from numpy import empty, arange, where, unique, \
31-
interp
31+
interp, ones, bool_, zeros
3232
from .. import VAR_DESCR_inv
3333
import logging
3434
from datetime import datetime, timedelta
3535
from .observation import EddiesObservations
36+
from numba import njit
3637

3738

3839
class TrackEddiesObservations(EddiesObservations):
3940
"""Class to practice Tracking on observations
4041
"""
41-
__slots__ = ()
42+
__slots__ = ('__obs_by_track', '__first_index_of_track')
4243

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
4652

4753
def filled_by_interpolation(self, mask):
4854
"""Filled selected values by interpolation
@@ -97,7 +103,7 @@ def extract_longer_eddies(self, nb_min, nb_obs, compress_id=True):
97103
def elements(self):
98104
elements = super(TrackEddiesObservations, self).elements
99105
elements.extend(['track', 'n', 'virtual'])
100-
return elements
106+
return list(set(elements))
101107

102108
def set_global_attr_netcdf(self, h_nc):
103109
"""Set global attr
@@ -126,19 +132,110 @@ def extract_with_period(self, period, **kwargs):
126132
Returns:
127133
same object with selected data
128134
"""
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)
130147
return self.__extract_with_mask(mask, **kwargs)
131148

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):
133182
"""
134183
Extract a subset of observations
135184
Args:
136185
mask: mask to select observations
137186
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
139189
140190
Returns:
141191
same object with selected observations
142192
"""
143193
if full_path and remove_incomplete:
144194
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

src/scripts/EddySubSetter

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
Subset eddy Dataset
5+
"""
6+
from py_eddy_tracker import EddyParser
7+
from py_eddy_tracker.observations.tracking import TrackEddiesObservations
8+
from os.path import basename, dirname
9+
10+
11+
def id_parser():
12+
parser = EddyParser('Eddy Identification')
13+
parser.add_argument('filename')
14+
parser.add_argument('filename_out')
15+
parser.add_argument('-p', '--period', nargs=2, type=int,
16+
help='Start day and end day, if it s negative value we will add to day min and add to day max, if 0 it s not use')
17+
parser.add_argument('-f', '--full_path', action='store_true',
18+
help='Extract path, if one obs or more are selected')
19+
parser.add_argument('-d', '--remove_incomplete', action='store_true',
20+
help='Extract path only if all obs are selected')
21+
return parser
22+
23+
24+
if __name__ == '__main__':
25+
args = id_parser().parse_args()
26+
27+
dataset = TrackEddiesObservations.load_from_netcdf(args.filename, raw_data=True)
28+
if args.period is not None:
29+
dataset = dataset.extract_with_period(args.period, full_path=args.full_path,
30+
remove_incomplete=args.remove_incomplete)
31+
32+
if len(dataset) != 0:
33+
dataset.write_netcdf(filename=args.filename_out)

0 commit comments

Comments
 (0)