66
77
88def bounds_to_vertices (
9- bounds : DataArray , bounds_dim : str , order : Optional [str ] = "counterclockwise"
9+ bounds : DataArray ,
10+ bounds_dim : str ,
11+ core_dims = None ,
12+ order : Optional [str ] = "counterclockwise" ,
1013) -> DataArray :
1114 """
1215 Convert bounds variable to vertices. There 2 covered cases:
@@ -18,15 +21,18 @@ def bounds_to_vertices(
1821 Parameters
1922 ----------
2023 bounds : DataArray
21- The bounds to convert. Must be of shape (N, 2) or (N, M, 4).
24+ The bounds to convert.
2225 bounds_dim : str
2326 The name of the bounds dimension of `bounds` (the one of length 2 or 4).
2427 order : {'counterclockwise', 'clockwise', None}
25- Valid for 2D coordinates only (bounds of shape (N, M, 4), ignored otherwise.
28+ Valid for 2D coordinates only (i.e. bounds of shape (..., N, M, 4), ignored otherwise.
2629 Order the bounds are given in, assuming that ax0-ax1-upward is a right handed
2730 coordinate system, where ax0 and ax1 are the two first dimensions of `bounds`.
2831 If None, the counterclockwise version is computed and then verified. If the
2932 check fails the clockwise version is returned. See Notes for more details.
33+ core_dims : list, optional
34+ List of core dimensions for apply_ufunc. This must not include bounds_dims.
35+ The shape of (*core_dims, bounds_dim) must be (N, 2) or (N, M, 4).
3036
3137 Returns
3238 -------
@@ -44,41 +50,60 @@ def bounds_to_vertices(
4450
4551 Please refer to the CF conventions document : http://cfconventions.org/Data/cf-conventions/cf-conventions-1.8/cf-conventions.html#cell-boundaries.
4652 """
47- # Get old and new dimension names and retranspose array to have bounds dim at axis 0.
48- bnd_dim = (
49- bounds_dim if isinstance (bounds_dim , str ) else bounds .get_axis_num (bounds_dim )
53+
54+ if core_dims is None :
55+ core_dims = [dim for dim in bounds .dims if dim != bounds_dim ]
56+
57+ output_sizes = {f"{ dim } _vertices" : bounds .sizes [dim ] + 1 for dim in core_dims }
58+ output_core_dims = list (output_sizes .keys ())
59+
60+ n_core_dims = len (core_dims )
61+ nbounds = bounds [bounds_dim ].size
62+
63+ if not (n_core_dims == 2 and nbounds == 4 ) and not (
64+ n_core_dims == 1 and nbounds == 2
65+ ):
66+ raise ValueError (
67+ f"Bounds format not understood. Got { bounds .dims } with shape { bounds .shape } ."
68+ )
69+
70+ return xr .apply_ufunc (
71+ _bounds_helper ,
72+ bounds ,
73+ input_core_dims = [core_dims + [bounds_dim ]],
74+ dask = "parallelized" ,
75+ kwargs = {"n_core_dims" : n_core_dims , "nbounds" : nbounds , "order" : order },
76+ output_core_dims = [output_core_dims ],
77+ dask_gufunc_kwargs = dict (output_sizes = output_sizes ),
78+ output_dtypes = [bounds .dtype ],
5079 )
51- old_dims = [ dim for dim in bounds . dims if dim != bnd_dim ]
52- new_dims = [ f" { dim } _vertices" for dim in old_dims ]
53- values = bounds . transpose ( bnd_dim , * old_dims ). data
54- if len ( old_dims ) == 2 and bounds . ndim == 3 and bounds [ bnd_dim ]. size == 4 :
80+
81+
82+ def _bounds_helper ( values , n_core_dims , nbounds , order ):
83+ if n_core_dims == 2 and nbounds == 4 :
5584 # Vertices case (2D lat/lon)
5685 if order in ["counterclockwise" , None ]:
5786 # Names assume we are drawing axis 1 upward et axis 2 rightward.
58- bot_left = values [0 , :, :]
59- bot_right = values [1 , :, - 1 :]
60- top_right = values [2 , - 1 :, - 1 :]
61- top_left = values [3 , - 1 :, :]
87+ bot_left = values [... , :, :, 0 ]
88+ bot_right = values [... , :, - 1 :, 1 ]
89+ top_right = values [... , - 1 :, - 1 :, 2 ]
90+ top_left = values [... , - 1 :, :, 3 ]
6291 vertex_vals = np .block ([[bot_left , bot_right ], [top_left , top_right ]])
6392 if order is None : # We verify if the ccw version works.
6493 calc_bnds = vertices_to_bounds (vertex_vals ).values
6594 order = "counterclockwise" if np .all (calc_bnds == values ) else "clockwise"
6695 if order == "clockwise" :
67- bot_left = values [0 , :, :]
68- top_left = values [1 , - 1 :, :]
69- top_right = values [2 , - 1 :, - 1 :]
70- bot_right = values [3 , :, - 1 :]
96+ bot_left = values [... , :, :, 0 ]
97+ top_left = values [... , - 1 :, :, 1 ]
98+ top_right = values [... , - 1 :, - 1 :, 2 ]
99+ bot_right = values [... , :, - 1 :, 3 ]
71100 # Our asumption was wrong, axis 1 is rightward and axis 2 is upward
72101 vertex_vals = np .block ([[bot_left , bot_right ], [top_left , top_right ]])
73- elif len ( old_dims ) == 1 and bounds . ndim == 2 and bounds [ bnd_dim ]. size == 2 :
102+ elif n_core_dims == 1 and nbounds == 2 :
74103 # Middle points case (1D lat/lon)
75- vertex_vals = np .concatenate ((values [0 , :], values [1 , - 1 :]))
76- else :
77- raise ValueError (
78- f"Bounds format not understood. Got { bounds .dims } with shape { bounds .shape } ."
79- )
104+ vertex_vals = np .concatenate ((values [..., :, 0 ], values [..., - 1 :, 1 ]), axis = - 1 )
80105
81- return xr . DataArray ( vertex_vals , dims = new_dims )
106+ return vertex_vals
82107
83108
84109def vertices_to_bounds (
0 commit comments