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