55from  scipy .interpolate  import  interp1d 
66from  numpy  import  concatenate , int32 , empty , maximum , where , array , \
77    sin , deg2rad , pi , ones , cos , ma , int8 , histogram2d , arange , float_ , \
8-     linspace , meshgrid ,  sinc ,  errstate , round_ , int_ , column_stack , interp 
8+     linspace , errstate , round_ , int_ , column_stack , interp 
99from  netCDF4  import  Dataset 
1010from  scipy .ndimage  import  gaussian_filter , convolve 
11- from  scipy .ndimage .filters  import  convolve1d 
1211from  scipy .interpolate  import  RectBivariateSpline 
1312from  scipy .spatial  import  cKDTree 
14- from  matplotlib .figure  import  Figure 
1513from  matplotlib .path  import  Path  as  BasePath 
1614from  matplotlib .contour  import  QuadContourSet  as  BaseQuadContourSet 
1715from  pyproj  import  Proj 
1816from  ..tools  import  fit_circle_c , distance_vector 
1917from  ..observations  import  EddiesObservations 
20- from  ..eddy_feature  import  Amplitude , get_uavg 
18+ from  ..eddy_feature  import  Amplitude , get_uavg ,  Contours 
2119
2220
2321def  contour_iter (self , anticyclonic_search ):
@@ -35,6 +33,7 @@ def isvalid(self):
3533BasePath .isvalid  =  isvalid 
3634
3735
36+ @property  
3837def  mean_coordinates (self ):
3938    return  self .vertices .mean (axis = 0 )
4039
@@ -83,19 +82,22 @@ def uniform_resample(x_val, y_val, num_fac=2, fixed_size=None):
8382def  regular_coordinates (self ):
8483    """Give a standard/regular/double sample of contour 
8584    """ 
86-     if  hasattr (self , '_regular_coordinates' ):
87-         self ._regular_coordinates  =  uniform_resample (self .lon , self .lat )
85+     if  not   hasattr (self , '_regular_coordinates' ):
86+         self ._regular_coordinates  =  column_stack ( uniform_resample (self .lon , self .lat ) )
8887    return  self ._regular_coordinates 
8988
9089
90+ BasePath .regular_coordinates  =  regular_coordinates 
91+ 
92+ 
9193def  fit_circle_path (self ):
9294    if  not  hasattr (self , '_circle_params' ):
9395        self ._fit_circle_path ()
9496    return  self ._circle_params 
9597
9698
9799def  _fit_circle_path (self ):
98-     lon_mean , lat_mean  =  self .mean_coordinates () 
100+     lon_mean , lat_mean  =  self .mean_coordinates 
99101    # Prepare for shape test and get eddy_radius_e 
100102    # http://www.geo.hunter.cuny.edu/~jochen/gtech201/lectures/ 
101103    # lec6concepts/map%20coordinate%20systems/ 
@@ -344,12 +346,7 @@ def bounds(self):
344346        """ 
345347        return  self .x_bounds .min (), self .x_bounds .max (), self .y_bounds .min (), self .y_bounds .max ()
346348
347-     def  eddy_identification (self , grid_height , step = 0.005 , shape_error = 55 , extra_variables = None ,
348-                             extra_array_variables = None , array_sampling = 50 , pixel_limit = None ):
349-         if  extra_variables  is  None :
350-             extra_variables  =  list ()
351-         if  extra_array_variables  is  None :
352-             extra_array_variables  =  list ()
349+     def  eddy_identification (self , grid_height , step = 0.005 , shape_error = 55 , array_sampling = 50 , pixel_limit = None ):
353350        if  pixel_limit  is  None :
354351            pixel_limit  =  (8 , 1000 )
355352
@@ -361,22 +358,15 @@ def eddy_identification(self, grid_height, step=0.005, shape_error=55, extra_var
361358        levels  =  arange (z_min  -  z_min  %  step , z_max  -  z_max  %  step  +  2  *  step , step )
362359
363360        x , y  =  self .vars [self .coordinates [0 ]], self .vars [self .coordinates [1 ]]
364-         ## To re write 
365-         if  len (x .shape ) ==  1 :
366-             data  =  data .T 
367-         ##  
368-         logging .debug ('Start computing iso lines' )
369-         fig  =  Figure ()
370-         ax  =  fig .add_subplot (111 )
371-         contours  =  ax .contour (x , y , data , levels )
372-         logging .debug ('Finish computing iso lines' )
361+ 
362+         eddies  =  list ()
363+         contours  =  Contours (x , y , data , levels )
373364
374365        anticyclonic_search  =  True 
375366        iterator  =  1  if  anticyclonic_search  else  - 1 
376367
377368        # Loop over each collection 
378-         for  coll_ind , coll  in  enumerate (contours .collections [::iterator ]):
379- 
369+         for  coll_ind , coll  in  enumerate (contours .iter (step = iterator )):
380370            corrected_coll_index  =  coll_ind 
381371            if  iterator  ==  - 1 :
382372                corrected_coll_index  =  -  coll_ind  -  1 
@@ -390,11 +380,13 @@ def eddy_identification(self, grid_height, step=0.005, shape_error=55, extra_var
390380                          corrected_coll_index , cvalues , nb_paths )
391381
392382            # Loop over individual c_s contours (i.e., every eddy in field) 
393-             for  cont  in  contour_paths :
383+             for  current_contour  in  contour_paths :
384+                 if  current_contour .used :
385+                     continue 
394386                # Filter for closed contours 
395-                 if  not  cont .isvalid :
387+                 if  not  current_contour .isvalid :
396388                    continue 
397-                 centlon_e , centlat_e , eddy_radius_e , aerr  =  cont .fit_circle ()
389+                 centlon_e , centlat_e , eddy_radius_e , aerr  =  current_contour .fit_circle ()
398390                # Filter for shape 
399391                if  aerr  <  0  or  aerr  >  shape_error :
400392                    continue 
@@ -410,10 +402,10 @@ def eddy_identification(self, grid_height, step=0.005, shape_error=55, extra_var
410402                if  anticyclonic_search  !=  acyc_not_cyc :
411403                    continue 
412404
413-                 i_x_in , i_y_in  =  cont .pixels_in (self )
405+                 i_x_in , i_y_in  =  current_contour .pixels_in (self )
414406
415407                # Maybe limit max must be replace with a maximum of surface 
416-                 if  cont .nb_pixel  <  pixel_limit [0 ] or  cont .nb_pixel  >  pixel_limit [1 ]:
408+                 if  current_contour .nb_pixel  <  pixel_limit [0 ] or  current_contour .nb_pixel  >  pixel_limit [1 ]:
417409                    continue 
418410
419411                reset_centroid , amp  =  self .get_amplitude (i_x_in , i_y_in , cvalues , data ,
@@ -426,82 +418,50 @@ def eddy_identification(self, grid_height, step=0.005, shape_error=55, extra_var
426418                if  reset_centroid :
427419                    centi  =  reset_centroid [0 ]
428420                    centj  =  reset_centroid [1 ]
429-                     centlon_e  =  x [centj ,  centi ]
430-                     centlat_e  =  y [centj ,  centi ]
421+                     centlon_e  =  x [centi ,  centj ]
422+                     centlat_e  =  y [centi ,  centj ]
431423
432-                 # Get sum of eke within Ceff 
433-                 #~ teke = grd.eke[eddy.slice_j, eddy.slice_i][eddy.mask_eff].sum() 
434- 
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 )
424+                 average_speed , speed_contour , inner_contour , speed_array  =  \
425+                     get_uavg (self , contours , centlon_e , centlat_e , current_contour , anticyclonic_search , corrected_coll_index )
439426
440427                # Use azimuth equal projection for radius 
441-                 proj  =  Proj ('+proj=aeqd +ellps=WGS84 +lat_0=%s +lon_0=%s' 
442-                             %  (inner_contlat .mean (), inner_contlon .mean ()))
443- 
428+                 proj  =  Proj ('+proj=aeqd +ellps=WGS84 +lat_0={1} +lon_0={0}' .format (* inner_contour .mean_coordinates ))
444429                # First, get position based on innermost 
445430                # contour 
446-                 c_x , c_y  =  proj (inner_contlon ,  inner_contlat )
431+                 c_x , c_y  =  proj (inner_contour . lon ,  inner_contour . lat )
447432                centx_s , centy_s , _ , _  =  fit_circle_c (c_x , c_y )
448-                 centlon_s , centlat_s  =  proj (centx_s , centy_s ,
449-                                             inverse = True )
433+                 centlon_s , centlat_s  =  proj (centx_s , centy_s , inverse = True )
450434                # Second, get speed-based radius based on 
451435                # contour of max uavg 
452-                 # (perhaps we should make a new proj here 
453-                 # based on contlon_s, contlat_s but I'm not 
454-                 # sure it's that important ... Antoine?) 
455-                 # A. : I dont think, the difference is tiny 
456-                 c_x , c_y  =  proj (contlon_s , contlat_s )
436+                 c_x , c_y  =  proj (speed_contour .lon , speed_contour .lat )
457437                _ , _ , eddy_radius_s , aerr_s  =  fit_circle_c (c_x , c_y )
458438
459439                # Instantiate new EddyObservation object 
460440                properties  =  EddiesObservations (
461441                    size = 1 ,
462-                     track_extra_variables = extra_variables ,
442+                     track_extra_variables = [ 'shape_error_e' ,  'shape_error_s' ] ,
463443                    track_array_variables = array_sampling ,
464-                     array_variables = extra_array_variables 
444+                     array_variables = [ 'contour_lon_e' ,  'contour_lat_e' ,  'contour_lon_s' ,  'contour_lat_s' ] 
465445                )
466446                properties .obs ['amplitude' ] =  amp .amplitude 
467447                properties .obs ['radius_s' ] =  eddy_radius_s  /  1000 
468-                 properties .obs ['speed_radius' ] =  uavg 
448+                 properties .obs ['speed_radius' ] =  average_speed 
469449                properties .obs ['radius_e' ] =  eddy_radius_e  /  1000 
470-                 #~ properties.obs['eke'] = teke 
471-                 if  'shape_error_e'  in  eddy .track_extra_variables :
472-                     properties .obs ['shape_error_e' ] =  aerr 
473-                 if  'shape_error_s'  in  eddy .track_extra_variables :
474-                     properties .obs ['shape_error_s' ] =  aerr_s 
475- 
476-                 if  aerr  >  99.9  or  aerr_s  >  99.9 :
477-                     logging .warning (
478-                         'Strange shape at this step! shape_error : %f, %f' ,
479-                         aerr ,
480-                         aerr_s )
481-                     continue 
482- 
483-                 # Update SLA eddy properties 
484- 
485-                 # See CSS11 section B4 
450+                 properties .obs ['shape_error_e' ] =  aerr 
451+                 properties .obs ['shape_error_s' ] =  aerr_s 
486452                properties .obs ['lon' ] =  centlon_s 
487453                properties .obs ['lat' ] =  centlat_s 
488-                 if  'contour_lon_e'  in  extra_array_variables :
489-                     (properties .obs ['contour_lon_e' ],
490-                      properties .obs ['contour_lat_e' ]) =  uniform_resample (
491-                         cont .lon , cont .lat ,
492-                         fixed_size = array_sampling )
493-                 if  'contour_lon_s'  in  extra_array_variables :
494-                     (properties .obs ['contour_lon_s' ],
495-                      properties .obs ['contour_lat_s' ]) =  uniform_resample (
496-                         contlon_s , contlat_s ,
497-                         fixed_size = array_sampling )
498- 
499-                 # for AVISO 
500-                 eddy .update_eddy_properties (properties )
501- 
502-                 # Mask out already found eddies 
503-                 eddy .sla [eddy .slice_j , eddy .slice_i ][
504-                     eddy .mask_eff ] =  eddy .fillval 
454+                 properties .obs ['contour_lon_e' ], properties .obs ['contour_lat_e' ] =  uniform_resample (
455+                     current_contour .lon , current_contour .lat , fixed_size = array_sampling )
456+                 properties .obs ['contour_lon_s' ], properties .obs ['contour_lat_s' ] =  uniform_resample (
457+                         speed_contour .lon , speed_contour .lat , fixed_size = array_sampling )
458+                 if  aerr  >  99.9  or  aerr_s  >  99.9 :
459+                     logging .warning ('Strange shape at this step! shape_error : %f, %f' , aerr , aerr_s )
460+ 
461+                 eddies .append (properties )
462+                 # To reserve definitively the area 
463+                 data .mask [i_x_in , i_y_in ] =  True 
464+         return  eddies 
505465
506466    @staticmethod  
507467    def  _gaussian_filter (data , sigma , mode = 'reflect' ):
@@ -562,15 +522,6 @@ def get_pixels_in(self, contour):
562522        i_x , i_y  =  where (mask )
563523        i_x  +=  slice_x .start 
564524        i_y  +=  slice_y .start 
565- 
566-         # import matplotlib 
567-         # matplotlib.use('AGG') 
568-         # import matplotlib.pyplot as plt 
569-         # plt.figure()     
570-         # plt.plot(self.x_c[i_x, i_y], self.y_c[i_x, i_y], 'g.', zorder=-10, markersize=20) 
571-         # plt.plot(self.x_c[slice_x, slice_y], self.y_c[slice_x, slice_y], 'r.', zorder=+10) 
572-         # plt.plot(contour.lon, contour.lat, 'b', zorder=20) 
573-         # plt.savefig('/tmp/toto.png') 
574525        return  i_x , i_y 
575526
576527    def  nearest_grd_indice (self , x , y ):
0 commit comments