6
6
7
7
8
8
def 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" ,
10
13
) -> DataArray :
11
14
"""
12
15
Convert bounds variable to vertices. There 2 covered cases:
@@ -18,15 +21,18 @@ def bounds_to_vertices(
18
21
Parameters
19
22
----------
20
23
bounds : DataArray
21
- The bounds to convert. Must be of shape (N, 2) or (N, M, 4).
24
+ The bounds to convert.
22
25
bounds_dim : str
23
26
The name of the bounds dimension of `bounds` (the one of length 2 or 4).
24
27
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.
26
29
Order the bounds are given in, assuming that ax0-ax1-upward is a right handed
27
30
coordinate system, where ax0 and ax1 are the two first dimensions of `bounds`.
28
31
If None, the counterclockwise version is computed and then verified. If the
29
32
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).
30
36
31
37
Returns
32
38
-------
@@ -44,41 +50,60 @@ def bounds_to_vertices(
44
50
45
51
Please refer to the CF conventions document : http://cfconventions.org/Data/cf-conventions/cf-conventions-1.8/cf-conventions.html#cell-boundaries.
46
52
"""
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 ],
50
79
)
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 :
55
84
# Vertices case (2D lat/lon)
56
85
if order in ["counterclockwise" , None ]:
57
86
# 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 ]
62
91
vertex_vals = np .block ([[bot_left , bot_right ], [top_left , top_right ]])
63
92
if order is None : # We verify if the ccw version works.
64
93
calc_bnds = vertices_to_bounds (vertex_vals ).values
65
94
order = "counterclockwise" if np .all (calc_bnds == values ) else "clockwise"
66
95
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 ]
71
100
# Our asumption was wrong, axis 1 is rightward and axis 2 is upward
72
101
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 :
74
103
# 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 )
80
105
81
- return xr . DataArray ( vertex_vals , dims = new_dims )
106
+ return vertex_vals
82
107
83
108
84
109
def vertices_to_bounds (
0 commit comments