55from numpy import concatenate , int32 , empty , maximum , where , array , \
66 sin , deg2rad , pi , ones , cos , ma , int8 , histogram2d , arange , float_ , \
77 linspace , errstate , int_ , column_stack , interp , meshgrid , nan , ceil , sinc , float64 , isnan , \
8- floor
8+ floor , percentile
99from datetime import datetime
1010from scipy .special import j1
1111from netCDF4 import Dataset
1212from scipy .ndimage import gaussian_filter , convolve
1313from scipy .interpolate import RectBivariateSpline , interp1d
1414from scipy .spatial import cKDTree
1515from scipy .signal import welch , convolve2d
16+ from cv2 import filter2D
1617from numba import jit
1718from matplotlib .path import Path as BasePath
1819from matplotlib .contour import QuadContourSet as BaseQuadContourSet
@@ -396,6 +397,16 @@ def eddy_identification(self, grid_height, uname, vname, date, step=0.005, shape
396397
397398 # Compute levels for ssh
398399 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+
399410 levels = arange (z_min - z_min % step , z_max - z_max % step + 2 * step , step )
400411
401412 # Get x and y values
@@ -532,6 +543,12 @@ def eddy_identification(self, grid_height, uname, vname, date, step=0.005, shape
532543 eddies_collection .sign_type = 1 if anticyclonic_search else - 1
533544 eddies_collection .obs ['time' ] = (date - datetime (1950 , 1 , 1 )).total_seconds () / 86400.
534545
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+
535552 a_and_c .append (eddies_collection )
536553 h_units = self .units (grid_height )
537554 units = UnitRegistry ()
@@ -733,12 +750,16 @@ class RegularGridDataset(GridDataset):
733750 '_speed_ev' ,
734751 '_is_circular' ,
735752 'x_size' ,
753+ '_x_step' ,
754+ '_y_step' ,
736755 )
737756
738757 def __init__ (self , * args , ** kwargs ):
739758 super (RegularGridDataset , self ).__init__ (* args , ** kwargs )
740759 self ._is_circular = None
741760 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 ()
742763
743764 def init_pos_interpolator (self ):
744765 """Create function to have a quick index interpolator
@@ -777,20 +798,20 @@ def normalize_x_indice(self, indices):
777798 return indices % self .x_size
778799
779800 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 )
782803
783804 @property
784805 def xstep (self ):
785806 """Only for regular grid with no step variation
786807 """
787- return self .x_c [ 1 ] - self . x_c [ 0 ]
808+ return self ._x_step
788809
789810 @property
790811 def ystep (self ):
791812 """Only for regular grid with no step variation
792813 """
793- return self .y_c [ 1 ] - self . y_c [ 0 ]
814+ return self ._y_step
794815
795816 def compute_pixel_path (self , x0 , y0 , x1 , y1 ):
796817 """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
949970 data_out = ma .zeros (data .shape )
950971 data_out .mask = ones (data_out .shape , dtype = bool )
951972 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 () :
953974 data_out .mask [:, i ] = True
954975 continue
955976 # Get kernel
@@ -975,9 +996,10 @@ def convolve_filter_with_dynamic_kernel(self, grid_name, kernel_func, lat_max=85
975996 # Convolution
976997 m = ~ tmp_matrix .mask
977998 tmp_matrix [~ m ] = 0
978- values_sum = convolve2d (tmp_matrix , kernel , mode = 'valid' )[:,0 ]
979999
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 ]
9811003 with errstate (invalid = 'ignore' ):
9821004 data_out [:, i ] = values_sum / kernel_sum
9831005 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
10181040 self .vars [grid_name ] -= data_out
10191041
10201042 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 ))
10211045 data_out = self .convolve_filter_with_dynamic_kernel (
10221046 grid_name , self .kernel_bessel , lat_max = lat_max , wave_length = wave_length , order = order )
1047+ logging .debug ('Filtering done' )
10231048 self .vars [grid_name ] -= data_out
10241049
10251050 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):
11551180 new z
11561181 """
11571182 z = empty (lons .shape , dtype = 'f4' ).reshape (- 1 )
1183+ g = self .grid (grid_name ).astype ('f4' )
11581184 interp_numba (
11591185 self .x_c .astype ('f4' ),
11601186 self .y_c .astype ('f4' ),
1161- self . grid ( grid_name ). astype ( 'f4' ) ,
1187+ g ,
11621188 lons .reshape (- 1 ).astype ('f4' ),
11631189 lats .reshape (- 1 ).astype ('f4' ),
11641190 z ,
1191+ g .fill_value
11651192 )
11661193 return z
11671194
11681195
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 ):
11711198 x_ref = x_g [0 ]
11721199 y_ref = y_g [0 ]
11731200 x_step = x_g [1 ] - x_ref
@@ -1181,5 +1208,18 @@ def interp_numba(x_g, y_g, z, x, y, dest_z):
11811208 j1 = j0 + 1
11821209 xd = (x_ - i0 )
11831210 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
11851225
0 commit comments