Skip to content

Commit ff8e241

Browse files
author
adelepoulle
committed
merge
2 parents 4a85891 + 62f80cd commit ff8e241

15 files changed

+193
-217
lines changed

.hgignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
syntax: glob
2+
*.pyc
3+
*.so
4+
*.c
5+
*.egg-*
6+
*.eclipse.*
7+
syntax: regexp
8+
^build$
9+
syntax: regexp
10+
^dist$

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ To avoid problems with installation, use of the virtualenv Python virtual enviro
66

77
Then use pip to install all dependencies (numpy, scipy, matplotlib, netCDF4, cython, pyproj, Shapely, ...), e.g.:
88

9-
* ** pip install cython **
9+
* ** pip install cython numpy matplotlib scipy netCDF4 shapely pyproj**
1010

1111
Then run the following to install the eddy tracker:
1212

setup.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,16 @@
2323
# 'src/scripts/EddyTrackingFull'
2424
],
2525
zip_safe=False,
26-
cmdclass={
27-
'build_ext': cython_build_ext,
28-
},
26+
cmdclass=dict(
27+
build_ext=cython_build_ext,
28+
),
29+
entry_points=dict(console_scripts=[
30+
'EddyUpdate = py_eddy_tracker.update.__init__:eddy_update',
31+
]),
2932
ext_modules=[Extension("py_eddy_tracker.tools",
3033
["src/py_eddy_tracker/tools.pyx"],
3134
include_dirs=[numpy.get_include()])],
35+
package_data={'py_eddy_tracker.featured_tracking': ['*.nc', ]},
3236
setup_requires=[
3337
'numpy>=1.8'],
3438
install_requires=[

share/tracking.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ DIAGNOSTIC_TYPE: 'SLA'
44

55
PATHS:
66
# Files produces with EddyIdentification
7-
FILES_PATTERN: /data/adelepoulle/Test/Test_eddy/20160330_brasilia_comparaison/brasil/Anticyclonic_*.nc
87
FILES_PATTERN: /home/emason/toto/Anticyclonic_*.nc
98
# Path and filename of Chelton et al (1998) Rossby radius data
109
# Obtain file from:

src/py_eddy_tracker/featured_tracking/old_tracker_reference.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ def mask_function(self, other):
2020
# Compute Parameter of ellips
2121
y = self.basic_formula_ellips_major_axis(
2222
self.obs['lat'],
23-
degrees=True)
23+
degrees=True,
24+
c0=1.05,
25+
cmin=1.05,
26+
cmax=1.5,
27+
lat1=23,
28+
lat2=5,
29+
)
2430
# mask from ellips
2531
mask = self.shifted_ellipsoid_degrees_mask(
2632
other,

src/py_eddy_tracker/grid/__init__.py

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from pyproj import Proj
3434
from numpy import unique, array, unravel_index, r_, floor, interp, arange, \
3535
sin, cos, deg2rad, arctan2, sqrt, pi, zeros, reciprocal, ma, empty, \
36-
concatenate
36+
concatenate, bytes_
3737
import logging
3838
from ..tracking_objects import nearest
3939
from re import compile as re_compile
@@ -48,16 +48,35 @@ def browse_dataset_in(data_dir, files_model, date_regexp, date_model,
4848
full_path = join_path(data_dir, files_model)
4949
logging.info('Search files : %s', full_path)
5050

51-
dataset_list = array(glob(full_path),
51+
filenames = bytes_(glob(full_path))
52+
dataset_list = empty(len(filenames),
5253
dtype=[('filename', 'S256'),
5354
('date', 'datetime64[D]'),
5455
])
56+
dataset_list['filename'] = bytes_(glob(full_path))
5557

5658
logging.info('%s grids available', dataset_list.shape[0])
59+
mode_attrs = False
60+
if '(' not in date_regexp:
61+
logging.debug('Attrs date : %s', date_regexp)
62+
mode_attrs = date_regexp.strip().split(':')
63+
else:
64+
logging.debug('Pattern date : %s', date_regexp)
65+
5766
for item in dataset_list:
58-
result = pattern_regexp.match(item['filename'])
59-
if result:
60-
str_date = result.groups()[0]
67+
str_date = None
68+
if mode_attrs:
69+
with Dataset(item['filename'].decode("utf-8")) as h:
70+
if len(mode_attrs) == 1:
71+
str_date = getattr(h, mode_attrs[0])
72+
else:
73+
str_date = getattr(h.variables[mode_attrs[0]], mode_attrs[1])
74+
else:
75+
result = pattern_regexp.match(str(item['filename']))
76+
if result:
77+
str_date = result.groups()[0]
78+
79+
if str_date is not None:
6180
item['date'] = datetime.strptime(str_date, date_model).date()
6281

6382
dataset_list.sort(order=['date', 'filename'])
@@ -71,6 +90,9 @@ def browse_dataset_in(data_dir, files_model, date_regexp, date_model,
7190
dataset_list = dataset_list[::sub_sampling_step]
7291

7392
if start_date is not None or end_date is not None:
93+
logging.info('Available grid from %s to %s',
94+
dataset_list[0]['date'],
95+
dataset_list[-1]['date'])
7496
logging.info('Filtering grid by time %s, %s', start_date, end_date)
7597
mask = (dataset_list['date'] >= start_date) * (
7698
dataset_list['date'] <= end_date)
@@ -176,14 +198,9 @@ def read_nc(self, varname, indices=slice(None)):
176198
varname : variable ('temp', 'mask_rho', etc) to read
177199
indices : slice
178200
"""
179-
with Dataset(self.grid_filename) as h_nc:
201+
with Dataset(self.grid_filename.decode("utf-8")) as h_nc:
180202
return h_nc.variables[varname][indices]
181203

182-
@property
183-
def nc_variables(self):
184-
with Dataset(self.grid_filename) as h_nc:
185-
return h_nc.variables.keys()
186-
187204
@property
188205
def view(self):
189206
return (self.slice_j,
@@ -207,7 +224,7 @@ def read_nc_att(self, varname, att):
207224
varname : variable ('temp', 'mask_rho', etc) to read
208225
att : string of attribute, eg. 'valid_range'
209226
"""
210-
with Dataset(self.grid_filename) as h_nc:
227+
with Dataset(self.grid_filename.decode("utf-8")) as h_nc:
211228
return getattr(h_nc.variables[varname], att)
212229

213230
@property
@@ -416,27 +433,14 @@ def vv2vr(vv_in, m_p, l_p):
416433
mshp, lshp = vv_in.shape
417434
return vv2vr(vv_in, mshp + 1, lshp)
418435

419-
def rho2u_2d(self, rho_in):
420-
"""
421-
Convert a 2D field at rho points to a field at u_val points
422-
"""
423-
assert rho_in.ndim == 2, 'rho_in must be 2d'
424-
return ((rho_in[:, :-1] + rho_in[:, 1:]) / 2).squeeze()
425-
426-
def rho2v_2d(self, rho_in):
427-
"""
428-
Convert a 2D field at rho points to a field at v_val points
429-
"""
430-
assert rho_in.ndim == 2, 'rho_in must be 2d'
431-
return ((rho_in[:-1] + rho_in[1:]) / 2).squeeze()
432-
433436
def uvmask(self):
434437
"""
435438
Get mask at U and V points
436439
"""
437440
logging.info('--- Computing umask and vmask for padded grid')
438-
self._umask = self.mask[:, :-1] * self.mask[:, 1:]
439-
self._vmask = self.mask[:-1] * self.mask[1:]
441+
if getattr(self, 'mask', None) is not None:
442+
self._umask = self.mask[:, :-1] * self.mask[:, 1:]
443+
self._vmask = self.mask[:-1] * self.mask[1:]
440444

441445
def set_basemap(self, with_pad=True):
442446
"""
@@ -505,10 +509,11 @@ def uspd(self):
505509
"""Get scalar speed
506510
"""
507511
uspd = (self.u_val ** 2 + self.v_val ** 2) ** .5
508-
if hasattr(uspd, 'mask'):
509-
uspd.mask += self.mask[self.view_unpad]
510-
else:
511-
uspd = ma.array(uspd, mask=self.mask[self.view_unpad])
512+
if self.mask is not None:
513+
if hasattr(uspd, 'mask'):
514+
uspd.mask += self.mask[self.view_unpad]
515+
else:
516+
uspd = ma.array(uspd, mask=self.mask[self.view_unpad])
512517
return uspd
513518

514519
def set_interp_coeffs(self, sla, uspd):

src/py_eddy_tracker/grid/aviso.py

Lines changed: 38 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
from scipy import spatial
3232
from dateutil import parser
3333
from numpy import meshgrid, zeros, array, where, ma, argmin, vstack, ones, \
34-
newaxis, sqrt, diff, r_
34+
newaxis, sqrt, diff, r_, arange
35+
from scipy.interpolate import interp1d
3536
import logging
3637
from netCDF4 import Dataset
3738

@@ -60,6 +61,8 @@ class AvisoGrid(BaseData):
6061
'fillval',
6162
'_angle',
6263
'sla_coeffs',
64+
'xinterp',
65+
'yinterp',
6366
'uspd_coeffs',
6467
'__lon',
6568
'__lat',
@@ -77,22 +80,37 @@ def __init__(self, aviso_file, the_domain,
7780
super(AvisoGrid, self).__init__()
7881
logging.info('Initialising the *AVISO_grid*')
7982
self.grid_filename = aviso_file
80-
self.domain = the_domain
81-
self.lonmin = float(lonmin)
82-
self.lonmax = float(lonmax)
83-
self.latmin = float(latmin)
84-
self.latmax = float(latmax)
85-
self.grid_filename = aviso_file
86-
83+
8784
self.lon_name = lon_name
8885
self.lat_name = lat_name
8986
self.grid_name = grid_name
9087

9188
self._lon = self.read_nc(self.lon_name)
9289
self._lat = self.read_nc(self.lat_name)
90+
if the_domain is None:
91+
self.domain = 'Automatic Domain'
92+
dlon = abs(self._lon[1] - self._lon[0])
93+
dlat = abs(self._lat[1] - self._lat[0])
94+
self.lonmin = float(self._lon.min()) + dlon * 2
95+
self.lonmax = float(self._lon.max()) - dlon * 2
96+
self.latmin = float(self._lat.min()) + dlat * 2
97+
self.latmax = float(self._lat.max()) - dlat * 2
98+
if ((self._lon[-1] + dlon) % 360) == self._lon[0]:
99+
self.domain = 'Global'
100+
self.lonmin = -100.
101+
self.lonmax = 290.
102+
self.latmin = -80.
103+
self.latmax = 80.
104+
else:
105+
self.domain = the_domain
106+
self.lonmin = float(lonmin)
107+
self.lonmax = float(lonmax)
108+
self.latmin = float(latmin)
109+
self.latmax = float(latmax)
110+
93111
self.fillval = self.read_nc_att(self.grid_name, '_FillValue')
94112

95-
if lonmin < 0 and lonmax <= 0:
113+
if self.lonmin < 0 and self.lonmax <= 0:
96114
self._lon -= 360.
97115
self._lon, self._lat = meshgrid(self._lon, self._lat)
98116
self._angle = zeros(self._lon.shape)
@@ -102,7 +120,7 @@ def __init__(self, aviso_file, the_domain,
102120

103121
# zero_crossing, used for handling a longitude range that
104122
# crosses zero degree meridian
105-
if lonmin < 0 and lonmax >= 0 and 'MedSea' not in self.domain:
123+
if self.lonmin < 0 and self.lonmax >= 0 and 'MedSea' not in self.domain:
106124
if ((self.lonmax < self._lon.max()) and (self.lonmax > self._lon.min()) and (self.lonmin < self._lon.max()) and (self.lonmin > self._lon.min())):
107125
pass
108126
else:
@@ -119,9 +137,15 @@ def __init__(self, aviso_file, the_domain,
119137
self.get_aviso_f_pm_pn()
120138
self.set_u_v_eke()
121139
self.shape = self.lon.shape
122-
# pad2 = 2 * self.pad
123-
# self.shape = (self.f_coriolis.shape[0] - pad2,
124-
# self.f_coriolis.shape[1] - pad2)
140+
141+
# self.init_pos_interpolator()
142+
143+
def init_pos_interpolator(self):
144+
self.xinterp = interp1d(self.lon[0].copy(), arange(self.lon.shape[1]), assume_sorted=True, copy=False, fill_value=(0, -1), bounds_error=False, kind='nearest')
145+
self.yinterp = interp1d(self.lat[:, 0].copy(), arange(self.lon.shape[0]), assume_sorted=True, copy=False, fill_value=(0, -1), bounds_error=False, kind='nearest')
146+
147+
def nearest_indice(self, lon, lat):
148+
return self.xinterp(lon), self.yinterp(lat)
125149

126150
def set_filename(self, file_name):
127151
self.grid_filename = file_name
@@ -137,7 +161,7 @@ def get_aviso_data(self, aviso_file, dimensions=None):
137161
if units not in self.KNOWN_UNITS:
138162
raise Exception('Unknown units : %s' % units)
139163

140-
with Dataset(self.grid_filename) as h_nc:
164+
with Dataset(self.grid_filename.decode('utf-8')) as h_nc:
141165
grid_dims = array(h_nc.variables[self.grid_name].dimensions)
142166
lat_dim = h_nc.variables[self.lat_name].dimensions[0]
143167
lon_dim = h_nc.variables[self.lon_name].dimensions[0]
@@ -186,44 +210,6 @@ def set_mask(self, sla):
186210
sea_label = self.labels[plus9, plus200]
187211
self.mask += self.labels != sea_label
188212

189-
def fillmask(self, data, mask):
190-
"""
191-
Fill missing values in an array with an average of nearest
192-
neighbours
193-
From http://permalink.gmane.org/gmane.comp.python.scientific.user/19610
194-
"""
195-
raise Exception('Use convolution to fill data')
196-
assert data.ndim == 2, 'data must be a 2D array.'
197-
fill_value = 9999.99
198-
data[mask == 0] = fill_value
199-
200-
# Create (i, j) point arrays for good and bad data.
201-
# Bad data are marked by the fill_value, good data elsewhere.
202-
igood = vstack(where(data != fill_value)).T
203-
ibad = vstack(where(data == fill_value)).T
204-
205-
# Create a tree for the bad points, the points to be filled
206-
tree = spatial.cKDTree(igood)
207-
208-
# Get the four closest points to the bad points
209-
# here, distance is squared
210-
dist, iquery = tree.query(ibad, k=4, p=2)
211-
212-
# Create a normalised weight, the nearest points are weighted as 1.
213-
# Points greater than one are then set to zero
214-
weight = dist / (dist.min(axis=1)[:, newaxis])
215-
weight *= ones(dist.shape)
216-
weight[weight > 1.] = 0.
217-
218-
# Multiply the queried good points by the weight, selecting only the
219-
# nearest points. Divide by the number of nearest points to get average
220-
xfill = weight * data[igood[:, 0][iquery], igood[:, 1][iquery]]
221-
xfill = (xfill / weight.sum(axis=1)[:, newaxis]).sum(axis=1)
222-
223-
# Place average of nearest good points, xfill, into bad point locations
224-
data[ibad[:, 0], ibad[:, 1]] = xfill
225-
return data
226-
227213
@property
228214
def lon(self):
229215
if self.__lon is None:

0 commit comments

Comments
 (0)