1313from  scipy .spatial  import  cKDTree 
1414from  matplotlib .figure  import  Figure 
1515from  matplotlib .path  import  Path  as  BasePath 
16+ from  matplotlib .contour  import  QuadContourSet  as  BaseQuadContourSet 
1617from  pyproj  import  Proj 
1718from  ..tools  import  fit_circle_c , distance_vector 
1819from  ..observations  import  EddiesObservations 
19- from  ..eddy_feature  import  Amplitude 
20+ from  ..eddy_feature  import  Amplitude ,  get_uavg 
2021
2122
23+ def  contour_iter (self , anticyclonic_search ):
24+     for  coll  in  self .collections [::1  if  anticyclonic_search  else  - 1 ]:
25+         yield  coll 
26+ 
27+ BaseQuadContourSet .iter_  =  contour_iter 
28+ 
2229@property  
2330def  isvalid (self ):
2431    return  False  not  in self .vertices [0 ] ==  self .vertices [- 1 ]
@@ -51,12 +58,34 @@ def lat(self):
5158BasePath .lat  =  lat 
5259
5360
54- # def nearest_grd_indice(self, lon_value, lat_value, grid): 
55- # if not hasattr(self, '_grid_indices'): 
56- # self._grid_indices = nearest(lon_value, lat_value, 
57- # grid.lon[0], grid.lat[:, 0]) 
58- # return self._grid_indices 
59- # BasePath.nearest_grd_indice = nearest_grd_indice 
61+ def  uniform_resample (x_val , y_val , num_fac = 2 , fixed_size = None ):
62+     """ 
63+     Resample contours to have (nearly) equal spacing 
64+        x_val, y_val    : input contour coordinates 
65+        num_fac : factor to increase lengths of output coordinates 
66+     """ 
67+     # Get distances 
68+     dist  =  empty (x_val .shape )
69+     dist [0 ] =  0 
70+     distance_vector (
71+         x_val [:- 1 ], y_val [:- 1 ], x_val [1 :], y_val [1 :], dist [1 :])
72+     dist .cumsum (out = dist )
73+     # Get uniform distances 
74+     if  fixed_size  is  None :
75+         fixed_size  =  dist .size  *  num_fac 
76+     d_uniform  =  linspace (0 , dist [- 1 ], num = fixed_size )
77+     x_new  =  interp (d_uniform , dist , x_val )
78+     y_new  =  interp (d_uniform , dist , y_val )
79+     return  x_new , y_new 
80+ 
81+ 
82+ @property  
83+ def  regular_coordinates (self ):
84+     """Give a standard/regular/double sample of contour 
85+     """ 
86+     if  hasattr (self , '_regular_coordinates' ):
87+         self ._regular_coordinates  =  uniform_resample (self .lon , self .lat )
88+     return  self ._regular_coordinates 
6089
6190
6291def  fit_circle_path (self ):
@@ -93,25 +122,21 @@ def _fit_circle_path(self):
93122BasePath ._fit_circle_path  =  _fit_circle_path 
94123
95124
96- def  uniform_resample (x_val , y_val , num_fac = 2 , fixed_size = None ):
97-     """ 
98-     Resample contours to have (nearly) equal spacing 
99-        x_val, y_val    : input contour coordinates 
100-        num_fac : factor to increase lengths of output coordinates 
101-     """ 
102-     # Get distances 
103-     dist  =  empty (x_val .shape )
104-     dist [0 ] =  0 
105-     distance_vector (
106-         x_val [:- 1 ], y_val [:- 1 ], x_val [1 :], y_val [1 :], dist [1 :])
107-     dist .cumsum (out = dist )
108-     # Get uniform distances 
109-     if  fixed_size  is  None :
110-         fixed_size  =  dist .size  *  num_fac 
111-     d_uniform  =  linspace (0 , dist [- 1 ], num = fixed_size )
112-     x_new  =  interp (d_uniform , dist , x_val )
113-     y_new  =  interp (d_uniform , dist , y_val )
114-     return  x_new , y_new 
125+ def  pixels_in (self , grid ):
126+     if  not  hasattr (self , '_pixels_in' ):
127+         self ._pixels_in  =  grid .get_pixels_in (self )
128+     return  self ._pixels_in 
129+ 
130+ 
131+ @property  
132+ def  nb_pixel (self ):
133+     if  not  hasattr (self , '_pixels_in' ):
134+         raise  Exception ('No pixels_in call before!' )
135+     return  self ._pixels_in [0 ].shape [0 ]
136+ 
137+     
138+ BasePath .pixels_in  =  pixels_in 
139+ BasePath .nb_pixel  =  nb_pixel 
115140
116141
117142class  GridDataset (object ):
@@ -138,6 +163,7 @@ class GridDataset(object):
138163        'global_attrs' ,
139164        'vars' ,
140165        'interpolators' ,
166+         'speed_coef' ,
141167    )
142168
143169    GRAVITY  =  9.807 
@@ -179,6 +205,7 @@ def is_centered(self):
179205    def  load_general_features (self ):
180206        """Load attrs 
181207        """ 
208+         logging .debug ('Load general feature from %(filename)s' , dict (filename = self .filename ))
182209        with  Dataset (self .filename ) as  h :
183210            # Load generals 
184211            self .dimensions  =  {i : len (v ) for  i , v  in  h .dimensions .items ()}
@@ -286,6 +313,7 @@ def grid(self, varname):
286313        if  varname  not  in self .vars :
287314            coordinates_dims  =  list (self .x_dim )
288315            coordinates_dims .extend (list (self .y_dim ))
316+             logging .debug ('Load %(varname)s from %(filename)s' , dict (varname = varname , filename = self .filename ))
289317            with  Dataset (self .filename ) as  h :
290318                dims  =  h .variables [varname ].dimensions 
291319                sl  =  [slice (None ) if  dim  in  coordinates_dims  else  0  for  dim  in  dims ]
@@ -316,17 +344,18 @@ def bounds(self):
316344        """ 
317345        return  self .x_bounds .min (), self .x_bounds .max (), self .y_bounds .min (), self .y_bounds .max ()
318346
319-     def  eddy_identification (self , grid_name , step = 0.005 , shape_error = 55 , extra_variables = None ,
347+     def  eddy_identification (self , grid_height , step = 0.005 , shape_error = 55 , extra_variables = None ,
320348                            extra_array_variables = None , array_sampling = 50 , pixel_limit = None ):
321349        if  extra_variables  is  None :
322350            extra_variables  =  list ()
323351        if  extra_array_variables  is  None :
324352            extra_array_variables  =  list ()
325353        if  pixel_limit  is  None :
326354            pixel_limit  =  (8 , 1000 )
327-         fig  =  Figure ()
328-         ax  =  fig .add_subplot (111 )
329-         data  =  self .grid (grid_name )
355+ 
356+         self .init_speed_coef ()
357+ 
358+         data  =  self .grid (grid_height )
330359        z_min , z_max  =  data .min (), data .max ()
331360
332361        levels  =  arange (z_min  -  z_min  %  step , z_max  -  z_max  %  step  +  2  *  step , step )
@@ -336,7 +365,11 @@ def eddy_identification(self, grid_name, step=0.005, shape_error=55, extra_varia
336365        if  len (x .shape ) ==  1 :
337366            data  =  data .T 
338367        ##  
368+         logging .debug ('Start computing iso lines' )
369+         fig  =  Figure ()
370+         ax  =  fig .add_subplot (111 )
339371        contours  =  ax .contour (x , y , data , levels )
372+         logging .debug ('Finish computing iso lines' )
340373
341374        anticyclonic_search  =  True 
342375        iterator  =  1  if  anticyclonic_search  else  - 1 
@@ -353,7 +386,7 @@ def eddy_identification(self, grid_name, step=0.005, shape_error=55, extra_varia
353386            if  nb_paths  ==  0 :
354387                continue 
355388            cvalues  =  contours .cvalues [corrected_coll_index ]
356-             logging .debug ('doing collection %s, contour value %s , %d paths' ,
389+             logging .debug ('doing collection %s, contour value %.4f , %d paths' ,
357390                          corrected_coll_index , cvalues , nb_paths )
358391
359392            # Loop over individual c_s contours (i.e., every eddy in field) 
@@ -377,57 +410,36 @@ def eddy_identification(self, grid_name, step=0.005, shape_error=55, extra_varia
377410                if  anticyclonic_search  !=  acyc_not_cyc :
378411                    continue 
379412
380-                 i_x_in , i_y_in  =  self .get_pixels_in (cont )
381-                 nb_pixel  =  i_x_in .shape [0 ]
413+                 i_x_in , i_y_in  =  cont .pixels_in (self )
382414
383415                # Maybe limit max must be replace with a maximum of surface 
384-                 if  nb_pixel  <  pixel_limit [0 ] or  nb_pixel  >  pixel_limit [1 ]:
416+                 if  cont . nb_pixel  <  pixel_limit [0 ] or  cont . nb_pixel  >  pixel_limit [1 ]:
385417                    continue 
386418
387-                 # Resample the contour points for a more even 
388-                 # circumferential distribution 
389-                 contlon_e , contlat_e  =  uniform_resample (cont .lon , cont .lat )
390- 
391-                 amp  =  self .get_amplitude (contlon_e , contlat_e , i_x_in , i_y_in , cvalues , data ,
392-                                          anticyclonic_search = anticyclonic_search )
419+                 reset_centroid , amp  =  self .get_amplitude (i_x_in , i_y_in , cvalues , data ,
420+                     anticyclonic_search = anticyclonic_search , level = contours .levels [corrected_coll_index ], step = step )
393421
394-                 print ( amp . within_amplitude_limits (),  amp . amplitude ) 
422+                 # If we have a valid  amplitude
395423                if  (not  amp .within_amplitude_limits ()) or  (amp .amplitude  ==  0 ):
396424                    continue 
397425
398-                 eddy .reshape_mask_eff (grd )
399-                 # Instantiate Amplitude object 
400-                 amp  =  Amplitude (contlon_e , contlat_e , eddy , grd )
401- 
402-                 if  anticyclonic_search :
403-                     reset_centroid  =  amp .all_pixels_above_h0 (
404-                         contours .levels [corrected_coll_index ])
405- 
406-                 else :
407-                     reset_centroid  =  amp .all_pixels_below_h0 (
408-                         contours .levels [corrected_coll_index ])
409- 
410-                 if  not  amp .within_amplitude_limits () or  amp .amplitude :
411-                     continue 
412- 
413426                if  reset_centroid :
414427                    centi  =  reset_centroid [0 ]
415428                    centj  =  reset_centroid [1 ]
416-                     centlon_e  =  grd . lon [centj , centi ]
417-                     centlat_e  =  grd . lat [centj , centi ]
429+                     centlon_e  =  x [centj , centi ]
430+                     centlat_e  =  y [centj , centi ]
418431
419432                # Get sum of eke within Ceff 
420-                 teke  =  grd .eke [eddy .slice_j , eddy .slice_i ][eddy .mask_eff ].sum ()
433+                 #~  teke = grd.eke[eddy.slice_j, eddy.slice_i][eddy.mask_eff].sum()
421434
422-                 (uavg , contlon_s , contlat_s , inner_contlon , inner_contlat ,
423-                  any_inner_contours 
424-                  )  =   get_uavg ( eddy ,  contours ,  centlon_e ,  centlat_e ,  cont ,  grd , 
425-                                anticyclonic_search )
435+                 #~  (uavg, contlon_s, contlat_s, inner_contlon, inner_contlat, any_inner_contours 
436+                      #~ ) = get_uavg(eddy, contours, centlon_e, centlat_e, cont, grd, anticyclonic_search) 
437+                 ( uavg ,  contlon_s ,  contlat_s ,  inner_contlon ,  inner_contlat ,  any_inner_contours 
438+                     )  =   get_uavg ( self ,  contours ,  centlon_e ,  centlat_e ,  cont ,  anticyclonic_search )
426439
427440                # Use azimuth equal projection for radius 
428441                proj  =  Proj ('+proj=aeqd +ellps=WGS84 +lat_0=%s +lon_0=%s' 
429-                             %  (inner_contlat .mean (),
430-                                inner_contlon .mean ()))
442+                             %  (inner_contlat .mean (), inner_contlon .mean ()))
431443
432444                # First, get position based on innermost 
433445                # contour 
@@ -455,7 +467,7 @@ def eddy_identification(self, grid_name, step=0.005, shape_error=55, extra_varia
455467                properties .obs ['radius_s' ] =  eddy_radius_s  /  1000 
456468                properties .obs ['speed_radius' ] =  uavg 
457469                properties .obs ['radius_e' ] =  eddy_radius_e  /  1000 
458-                 properties .obs ['eke' ] =  teke 
470+                 #~  properties.obs['eke'] = teke
459471                if  'shape_error_e'  in  eddy .track_extra_variables :
460472                    properties .obs ['shape_error_e' ] =  aerr 
461473                if  'shape_error_s'  in  eddy .track_extra_variables :
@@ -505,33 +517,36 @@ def _gaussian_filter(data, sigma, mode='reflect'):
505517            return  ma .array (v  /  w , mask = w  ==  0 )
506518
507519    @staticmethod  
508-     def  get_amplitude (cont_x ,  cont_y ,  i_x_in , i_y_in , contour_height , data , anticyclonic_search = True ):
520+     def  get_amplitude (i_x_in , i_y_in , contour_height , data , anticyclonic_search = True ,  level = None ,  step = None ):
509521        # Instantiate Amplitude object 
510522        amp  =  Amplitude (
511-             contour_x = cont_x ,
512-             contour_y = cont_y ,
523+             # Indices of all pixels in contour 
513524            i_contour_x = i_x_in ,
514525            i_contour_y = i_y_in ,
526+             # Height of level 
515527            contour_height = contour_height ,
516-             data = data )
528+             # All grid 
529+             data = data ,
530+             # Step by level 
531+             interval = step )
517532
518533        if  anticyclonic_search :
519-             reset_centroid  =  amp .all_pixels_above_h0 ()
534+             reset_centroid  =  amp .all_pixels_above_h0 (level )
520535
521536        else :
522-             reset_centroid  =  amp .all_pixels_below_h0 (
523-                 contours .levels [corrected_coll_index ])
537+             reset_centroid  =  amp .all_pixels_below_h0 (level )
524538
525-         return  amp 
526-         # if not amp.within_amplitude_limits() or amp.amplitude: 
527-         # continue 
539+         return  reset_centroid , amp 
528540
529541
530542class  UnRegularGridDataset (GridDataset ):
531543    """Class which manage unregular grid 
532544    """ 
533545
534-     __slots__  =  'index_interp' 
546+     __slots__  =  (
547+         'index_interp' ,
548+         '_speed_norm' ,
549+         )
535550
536551    def  bbox_indice (self , vertices ):
537552        dist , idx  =  self .index_interp .query (vertices , k = 1 )
@@ -568,11 +583,13 @@ def compute_pixel_path(self, x0, y0, x1, y1):
568583        pass 
569584
570585    def  init_pos_interpolator (self ):
586+         logging .debug ('Create a KdTree could be long ...' )
571587        self .index_interp  =  cKDTree (
572588            column_stack ((
573589                self .x_c .reshape (- 1 ),
574590                self .y_c .reshape (- 1 )
575591            )))
592+         logging .debug ('... OK' )
576593
577594    def  _low_filter (self , grid_name , x_cut , y_cut , factor = 40. ):
578595        data  =  self .grid (grid_name )
@@ -614,6 +631,16 @@ def _low_filter(self, grid_name, x_cut, y_cut, factor=40.):
614631        z_interp  =  RectBivariateSpline (x_center , y_center , z_filtered , ** opts_interpolation ).ev (x , y )
615632        return  ma .array (z_interp , mask = m_interp .ev (x , y ) >  0.00001 )
616633
634+     def  speed_coef (self , contour ):
635+         dist , idx  =  self .index_interp .query (contour .regular_coordinates [1 :], k = 4 )
636+         i_y  =  idx  %  self .x_c .shape [1 ]
637+         i_x  =  int_ ((idx  -  i_y ) /  self .x_c .shape [1 ])
638+         # A simplified solution to be change by a weight mean 
639+         return  self ._speed_norm [i_x , i_y ].mean (axis = 1 )
640+ 
641+     def  init_speed_coef (self , uname = 'u' , vname = 'v' ):
642+         self ._speed_norm  =  (self .grid (uname ) **  2  +  self .grid (vname ) **  2 ) **  .5 
643+ 
617644
618645class  RegularGridDataset (GridDataset ):
619646    """Class only for regular grid 
@@ -782,3 +809,9 @@ def add_uv(self, grid_height):
782809        d_x_degrees  =  (d_x_degrees  +  180 ) %  360  -  180 
783810        d_x  =  self .EARTH_RADIUS  *  2  *  pi  /  360  *  d_x_degrees  *  cos (deg2rad (self .y_c ))
784811        self .vars ['v' ] =  d_hx  /  d_x  *  gof 
812+ 
813+     def  init_speed_coef (self , uname = 'u' , vname = 'v' ):
814+         """Draft 
815+         """ 
816+         uspd  =  (self .grid (uname ) **  2  +  self .grid (vname ) **  2 ) **  .5 
817+         self .speed_coef  =  RectBivariateSpline (self .xc , self .yc , uspd , kx = 1 , ky = 1 ).ev 
0 commit comments