5
5
from scipy .interpolate import interp1d
6
6
from numpy import concatenate , int32 , empty , maximum , where , array , \
7
7
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
9
9
from netCDF4 import Dataset
10
10
from scipy .ndimage import gaussian_filter , convolve
11
- from scipy .ndimage .filters import convolve1d
12
11
from scipy .interpolate import RectBivariateSpline
13
12
from scipy .spatial import cKDTree
14
- from matplotlib .figure import Figure
15
13
from matplotlib .path import Path as BasePath
16
14
from matplotlib .contour import QuadContourSet as BaseQuadContourSet
17
15
from pyproj import Proj
18
16
from ..tools import fit_circle_c , distance_vector
19
17
from ..observations import EddiesObservations
20
- from ..eddy_feature import Amplitude , get_uavg
18
+ from ..eddy_feature import Amplitude , get_uavg , Contours
21
19
22
20
23
21
def contour_iter (self , anticyclonic_search ):
@@ -35,6 +33,7 @@ def isvalid(self):
35
33
BasePath .isvalid = isvalid
36
34
37
35
36
+ @property
38
37
def mean_coordinates (self ):
39
38
return self .vertices .mean (axis = 0 )
40
39
@@ -83,19 +82,22 @@ def uniform_resample(x_val, y_val, num_fac=2, fixed_size=None):
83
82
def regular_coordinates (self ):
84
83
"""Give a standard/regular/double sample of contour
85
84
"""
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 ) )
88
87
return self ._regular_coordinates
89
88
90
89
90
+ BasePath .regular_coordinates = regular_coordinates
91
+
92
+
91
93
def fit_circle_path (self ):
92
94
if not hasattr (self , '_circle_params' ):
93
95
self ._fit_circle_path ()
94
96
return self ._circle_params
95
97
96
98
97
99
def _fit_circle_path (self ):
98
- lon_mean , lat_mean = self .mean_coordinates ()
100
+ lon_mean , lat_mean = self .mean_coordinates
99
101
# Prepare for shape test and get eddy_radius_e
100
102
# http://www.geo.hunter.cuny.edu/~jochen/gtech201/lectures/
101
103
# lec6concepts/map%20coordinate%20systems/
@@ -344,12 +346,7 @@ def bounds(self):
344
346
"""
345
347
return self .x_bounds .min (), self .x_bounds .max (), self .y_bounds .min (), self .y_bounds .max ()
346
348
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 ):
353
350
if pixel_limit is None :
354
351
pixel_limit = (8 , 1000 )
355
352
@@ -361,22 +358,15 @@ def eddy_identification(self, grid_height, step=0.005, shape_error=55, extra_var
361
358
levels = arange (z_min - z_min % step , z_max - z_max % step + 2 * step , step )
362
359
363
360
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 )
373
364
374
365
anticyclonic_search = True
375
366
iterator = 1 if anticyclonic_search else - 1
376
367
377
368
# 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 )):
380
370
corrected_coll_index = coll_ind
381
371
if iterator == - 1 :
382
372
corrected_coll_index = - coll_ind - 1
@@ -390,11 +380,13 @@ def eddy_identification(self, grid_height, step=0.005, shape_error=55, extra_var
390
380
corrected_coll_index , cvalues , nb_paths )
391
381
392
382
# 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
394
386
# Filter for closed contours
395
- if not cont .isvalid :
387
+ if not current_contour .isvalid :
396
388
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 ()
398
390
# Filter for shape
399
391
if aerr < 0 or aerr > shape_error :
400
392
continue
@@ -410,10 +402,10 @@ def eddy_identification(self, grid_height, step=0.005, shape_error=55, extra_var
410
402
if anticyclonic_search != acyc_not_cyc :
411
403
continue
412
404
413
- i_x_in , i_y_in = cont .pixels_in (self )
405
+ i_x_in , i_y_in = current_contour .pixels_in (self )
414
406
415
407
# 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 ]:
417
409
continue
418
410
419
411
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
426
418
if reset_centroid :
427
419
centi = reset_centroid [0 ]
428
420
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 ]
431
423
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 )
439
426
440
427
# 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 ))
444
429
# First, get position based on innermost
445
430
# contour
446
- c_x , c_y = proj (inner_contlon , inner_contlat )
431
+ c_x , c_y = proj (inner_contour . lon , inner_contour . lat )
447
432
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 )
450
434
# Second, get speed-based radius based on
451
435
# 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 )
457
437
_ , _ , eddy_radius_s , aerr_s = fit_circle_c (c_x , c_y )
458
438
459
439
# Instantiate new EddyObservation object
460
440
properties = EddiesObservations (
461
441
size = 1 ,
462
- track_extra_variables = extra_variables ,
442
+ track_extra_variables = [ 'shape_error_e' , 'shape_error_s' ] ,
463
443
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' ]
465
445
)
466
446
properties .obs ['amplitude' ] = amp .amplitude
467
447
properties .obs ['radius_s' ] = eddy_radius_s / 1000
468
- properties .obs ['speed_radius' ] = uavg
448
+ properties .obs ['speed_radius' ] = average_speed
469
449
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
486
452
properties .obs ['lon' ] = centlon_s
487
453
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
505
465
506
466
@staticmethod
507
467
def _gaussian_filter (data , sigma , mode = 'reflect' ):
@@ -562,15 +522,6 @@ def get_pixels_in(self, contour):
562
522
i_x , i_y = where (mask )
563
523
i_x += slice_x .start
564
524
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')
574
525
return i_x , i_y
575
526
576
527
def nearest_grd_indice (self , x , y ):
0 commit comments