5
5
from numpy import concatenate , int32 , empty , maximum , where , array , \
6
6
sin , deg2rad , pi , ones , cos , ma , int8 , histogram2d , arange , float_ , \
7
7
linspace , errstate , int_ , column_stack , interp , meshgrid , nan , ceil , sinc , float64 , isnan , \
8
- floor
8
+ floor , percentile
9
9
from datetime import datetime
10
10
from scipy .special import j1
11
11
from netCDF4 import Dataset
12
12
from scipy .ndimage import gaussian_filter , convolve
13
13
from scipy .interpolate import RectBivariateSpline , interp1d
14
14
from scipy .spatial import cKDTree
15
15
from scipy .signal import welch , convolve2d
16
+ from cv2 import filter2D
16
17
from numba import jit
17
18
from matplotlib .path import Path as BasePath
18
19
from matplotlib .contour import QuadContourSet as BaseQuadContourSet
@@ -396,6 +397,16 @@ def eddy_identification(self, grid_height, uname, vname, date, step=0.005, shape
396
397
397
398
# Compute levels for ssh
398
399
z_min , z_max = data .min (), data .max ()
400
+ d_z = z_max - z_min
401
+ data_tmp = data [~ data .mask ]
402
+ epsilon = 0.001 # in %
403
+ z_min_p , z_max_p = percentile (data_tmp , epsilon ), percentile (data_tmp ,100 - epsilon )
404
+ d_zp = z_max_p - z_min_p
405
+ if d_z / d_zp > 2 :
406
+ logging .warning ('Maybe some extrema are present zmin %f (m) and zmax %f (m) will be replace by %f and %f' ,
407
+ z_min , z_max , z_min_p , z_max_p )
408
+ z_min , z_max = z_min_p , z_max_p
409
+
399
410
levels = arange (z_min - z_min % step , z_max - z_max % step + 2 * step , step )
400
411
401
412
# Get x and y values
@@ -532,6 +543,12 @@ def eddy_identification(self, grid_height, uname, vname, date, step=0.005, shape
532
543
eddies_collection .sign_type = 1 if anticyclonic_search else - 1
533
544
eddies_collection .obs ['time' ] = (date - datetime (1950 , 1 , 1 )).total_seconds () / 86400.
534
545
546
+ # normalization longitude between 0 - 360, because storage have an offset on 180
547
+ eddies_collection .obs ['lon' ] %= 360
548
+ ref = eddies_collection .obs ['lon' ] - 180
549
+ eddies_collection .obs ['contour_lon_e' ] = ((eddies_collection .obs ['contour_lon_e' ].T - ref ) % 360 + ref ).T
550
+ eddies_collection .obs ['contour_lon_s' ] = ((eddies_collection .obs ['contour_lon_s' ].T - ref ) % 360 + ref ).T
551
+
535
552
a_and_c .append (eddies_collection )
536
553
h_units = self .units (grid_height )
537
554
units = UnitRegistry ()
@@ -733,12 +750,16 @@ class RegularGridDataset(GridDataset):
733
750
'_speed_ev' ,
734
751
'_is_circular' ,
735
752
'x_size' ,
753
+ '_x_step' ,
754
+ '_y_step' ,
736
755
)
737
756
738
757
def __init__ (self , * args , ** kwargs ):
739
758
super (RegularGridDataset , self ).__init__ (* args , ** kwargs )
740
759
self ._is_circular = None
741
760
self .x_size = self .x_c .shape [0 ]
761
+ self ._x_step = (self .x_c [1 :] - self .x_c [:- 1 ]).mean ()
762
+ self ._y_step = (self .y_c [1 :] - self .y_c [:- 1 ]).mean ()
742
763
743
764
def init_pos_interpolator (self ):
744
765
"""Create function to have a quick index interpolator
@@ -777,20 +798,20 @@ def normalize_x_indice(self, indices):
777
798
return indices % self .x_size
778
799
779
800
def nearest_grd_indice (self , x , y ):
780
- return int32 (floor ((( x - self .x_c [0 ] + self . xstep ) % 360 ) // self .xstep ) ), \
781
- int32 (floor ((( y - self .y_c [0 ] + self . ystep ) % 360 ) // self .ystep ) )
801
+ return int32 ((( x - self .x_bounds [0 ]) % 360 ) // self .xstep ), \
802
+ int32 ((( y - self .y_bounds [0 ]) % 360 ) // self .ystep )
782
803
783
804
@property
784
805
def xstep (self ):
785
806
"""Only for regular grid with no step variation
786
807
"""
787
- return self .x_c [ 1 ] - self . x_c [ 0 ]
808
+ return self ._x_step
788
809
789
810
@property
790
811
def ystep (self ):
791
812
"""Only for regular grid with no step variation
792
813
"""
793
- return self .y_c [ 1 ] - self . y_c [ 0 ]
814
+ return self ._y_step
794
815
795
816
def compute_pixel_path (self , x0 , y0 , x1 , y1 ):
796
817
"""Give a series of index which describe the path between to position
@@ -949,7 +970,7 @@ def convolve_filter_with_dynamic_kernel(self, grid_name, kernel_func, lat_max=85
949
970
data_out = ma .zeros (data .shape )
950
971
data_out .mask = ones (data_out .shape , dtype = bool )
951
972
for i , lat in enumerate (self .y_c ):
952
- if abs (lat ) > lat_max :
973
+ if abs (lat ) > lat_max or data [:, i ]. mask . all () :
953
974
data_out .mask [:, i ] = True
954
975
continue
955
976
# Get kernel
@@ -975,9 +996,10 @@ def convolve_filter_with_dynamic_kernel(self, grid_name, kernel_func, lat_max=85
975
996
# Convolution
976
997
m = ~ tmp_matrix .mask
977
998
tmp_matrix [~ m ] = 0
978
- values_sum = convolve2d (tmp_matrix , kernel , mode = 'valid' )[:,0 ]
979
999
980
- kernel_sum = convolve2d ((m ).astype (float ), kernel , mode = 'valid' )[:,0 ]
1000
+ demi_x , demi_y = k_shape [0 ] // 2 , k_shape [1 ] // 2
1001
+ values_sum = filter2D (tmp_matrix , - 1 , kernel )[demi_x :- demi_x , demi_y ]
1002
+ kernel_sum = filter2D (m .astype (float ), - 1 , kernel )[demi_x :- demi_x , demi_y ]
981
1003
with errstate (invalid = 'ignore' ):
982
1004
data_out [:, i ] = values_sum / kernel_sum
983
1005
data_out = ma .array (data_out , mask = data .mask + data_out .mask )
@@ -1018,8 +1040,11 @@ def bessel_band_filter(self, grid_name, wave_length_inf, wave_length_sup, **kwar
1018
1040
self .vars [grid_name ] -= data_out
1019
1041
1020
1042
def bessel_high_filter (self , grid_name , wave_length , order = 1 , lat_max = 85 ):
1043
+ logging .debug ('Run filtering with wave of %(wave_length)s km and order of %(order)s ...' ,
1044
+ dict (wave_length = wave_length , order = order ))
1021
1045
data_out = self .convolve_filter_with_dynamic_kernel (
1022
1046
grid_name , self .kernel_bessel , lat_max = lat_max , wave_length = wave_length , order = order )
1047
+ logging .debug ('Filtering done' )
1023
1048
self .vars [grid_name ] -= data_out
1024
1049
1025
1050
def bessel_low_filter (self , grid_name , wave_length , order = 1 , lat_max = 85 ):
@@ -1155,19 +1180,21 @@ def interp(self, grid_name, lons, lats):
1155
1180
new z
1156
1181
"""
1157
1182
z = empty (lons .shape , dtype = 'f4' ).reshape (- 1 )
1183
+ g = self .grid (grid_name ).astype ('f4' )
1158
1184
interp_numba (
1159
1185
self .x_c .astype ('f4' ),
1160
1186
self .y_c .astype ('f4' ),
1161
- self . grid ( grid_name ). astype ( 'f4' ) ,
1187
+ g ,
1162
1188
lons .reshape (- 1 ).astype ('f4' ),
1163
1189
lats .reshape (- 1 ).astype ('f4' ),
1164
1190
z ,
1191
+ g .fill_value
1165
1192
)
1166
1193
return z
1167
1194
1168
1195
1169
- @jit ("void(f4[:], f4[:], f4[:,:], f4[:], f4[:], f4[:])" , nopython = True )
1170
- def interp_numba (x_g , y_g , z , x , y , dest_z ):
1196
+ @jit ("void(f4[:], f4[:], f4[:,:], f4[:], f4[:], f4[:], f4 )" , nopython = True )
1197
+ def interp_numba (x_g , y_g , z , x , y , dest_z , fill_value ):
1171
1198
x_ref = x_g [0 ]
1172
1199
y_ref = y_g [0 ]
1173
1200
x_step = x_g [1 ] - x_ref
@@ -1181,5 +1208,18 @@ def interp_numba(x_g, y_g, z, x, y, dest_z):
1181
1208
j1 = j0 + 1
1182
1209
xd = (x_ - i0 )
1183
1210
yd = (y_ - j0 )
1184
- dest_z [i ] = (z [i0 , j0 ] * (1 - xd ) + (z [i1 , j0 ] * xd )) * (1 - yd ) + (z [i0 , j1 ] * (1 - xd ) + z [i1 , j1 ] * xd ) * yd
1211
+ z00 = z [i0 , j0 ]
1212
+ z01 = z [i0 , j1 ]
1213
+ z10 = z [i1 , j0 ]
1214
+ z11 = z [i1 , j1 ]
1215
+ if z00 == fill_value :
1216
+ dest_z [i ] = nan
1217
+ elif z01 == fill_value :
1218
+ dest_z [i ] = nan
1219
+ elif z10 == fill_value :
1220
+ dest_z [i ] = nan
1221
+ elif z11 == fill_value :
1222
+ dest_z [i ] = nan
1223
+ else :
1224
+ dest_z [i ] = (z00 * (1 - xd ) + (z10 * xd )) * (1 - yd ) + (z01 * (1 - xd ) + z11 * xd ) * yd
1185
1225
0 commit comments