@@ -143,74 +143,97 @@ def __new__(cls, name_or_coord, minimum, maximum,
143143 'groupby_point, groupby_slice' )
144144
145145
146- def _discontiguity_in_2d_bounds (bds , abs_tol = 1e-4 ):
146+ def _discontiguity_in_2d_bounds (bds , atol = 1e-4 ):
147147 """
148- Check bounds of a 2-dimensional coordinate are contiguous
148+ Checks that the bounds of a 2-dimensional coordinate are contiguous.
149+
149150 Args:
150- bds: Array of bounds of shape (X,Y,4)
151- abs_tol: tolerance
151+ * bds: (array)
152+ Bounds array of shape (X,Y,4).
153+ * atol: (float)
154+ Absolute tolerance that is used when checking contiguity. Defaults to
155+ 1e-4.
152156
153157 Returns:
154- Bool, if there are no discontinuities
155- absolute difference along the x axis
156- absolute difference along the y axis
158+ * all_equal: (boolean)
159+ True if there are no discontiguities.
160+ * diffs_along_x: (array or None)
161+ Either an array of the absolute differences along the x-axis, of the
162+ shape (X,Y-1) or None if the input bounds has the shape (1,Y,4).
163+ * diffs_along_y: (array or None)
164+ Either an array of the absolute differences along the y-axis, of the
165+ shape (X-1,Y) or None if the input bounds has the shape (X,1,4).
157166
158167 """
159168 # Check bds has the shape (ny, nx, 4)
160- if not bds .ndim == 3 and bds .shape [2 ] == 4 :
161- raise ValueError ('2D coordinates must have 4 bounds per point '
162- 'for 2D coordinate plotting ' )
169+ if not ( bds .ndim == 3 and bds .shape [- 1 ] == 4 ) :
170+ raise ValueError ('Bounds for 2D coordinates must be 3-dimensional and '
171+ 'have 4 bounds per point. ' )
163172
164173 # Check ordering:
165174 # i i+1
166175 # j @0 @1
167176 # j+1 @3 @2
168- def mod360_diff (x1 , x2 ):
169- diff = x1 - x2
177+ def mod360_diff (upper_bounds , lower_bounds ):
178+ diff = upper_bounds - lower_bounds
170179 diff = (diff + 360.0 + 180.0 ) % 360.0 - 180.0
171180 return diff
172181
173- # Compare cell with the cell next to it (i+1)
174- diffs_along_x = mod360_diff (bds [:, :- 1 , 1 ], bds [:, 1 :, 0 ])
175- # Compare cell with the cell above it (j+1)
176- diffs_along_y = mod360_diff (bds [:- 1 , :, 3 ], bds [1 :, :, 0 ])
182+ def diffs_below_tolerance (diffs_along_axis ):
183+ return np .all (np .abs (diffs_along_axis ) < atol )
177184
178- def eq_diffs (x1 ):
179- return np .all (np .abs (x1 ) < abs_tol )
185+ # Compare cell with the cell next to it (i+1)
186+ if bds .shape [0 ] > 1 :
187+ diffs_along_x = np .abs (mod360_diff (bds [:, :- 1 , 1 ], bds [:, 1 :, 0 ]))
188+ match_y0_x1 = diffs_below_tolerance (diffs_along_x )
189+ else :
190+ diffs_along_x = None
191+ match_y0_x1 = True
180192
181- match_y0_x1 = eq_diffs (diffs_along_x )
182- match_y1_x0 = eq_diffs (diffs_along_y )
193+ # Compare cell with the cell above it (j+1)
194+ if bds .shape [1 ] > 1 :
195+ diffs_along_y = np .abs (mod360_diff (bds [:- 1 , :, 3 ], bds [1 :, :, 0 ]))
196+ match_y1_x0 = diffs_below_tolerance (diffs_along_y )
197+ else :
198+ diffs_along_y = None
199+ match_y1_x0 = True
183200
184201 all_eq = match_y0_x1 and match_y1_x0
185202
186- return all_eq , np . abs ( diffs_along_x ), np . abs ( diffs_along_y )
203+ return all_eq , diffs_along_x , diffs_along_y
187204
188205
189206def _get_2d_coord_bound_grid (bds ):
190207 """
191- Function used that takes a bounds array for a 2-D coordinate variable with
192- 4 sides and returns the bounds grid.
208+ Creates a grid using the bounds of a 2D coordinate with 4 sided cells.
193209
194- Cf standards requires the four vertices of the cell to be traversed
195- anti-clockwise if the coordinates are defined in a right handed coordinate
196- system.
210+ Assumes that the four vertices of the cells are in an anti-clockwise order
211+ (bottom-left, bottom-right, top-right, top-left).
197212
198- selects the zeroth vertex of each cell and then adds the column the first
199- vertex at the end. For the top row it uses the thirs vertex, with the
200- second added on to the end.
213+ Selects the zeroth vertex of each cell. A final column is added, which
214+ contains the first vertex of the cells in the final column. A final row
215+ is added, which contains the third vertex of all the cells in the final
216+ row, except for in the final column where it uses the second vertex.
201217 e.g.
202218 # 0-0-0-0-1
203219 # 0-0-0-0-1
204220 # 3-3-3-3-2
205221
206222
207223 Args:
208- bounds: array of shape (X,Y,4)
224+ * bounds: (array)
225+ Coordinate bounds array of shape (X,Y,4)
209226
210227 Returns:
211- array of shape (X+1, Y+1)
228+ * grid: (array)
229+ Grid of shape (X+1, Y+1)
212230
213231 """
232+ # Check bds has the shape (ny, nx, 4)
233+ if not (bds .ndim == 3 and bds .shape [- 1 ] == 4 ):
234+ raise ValueError ('Bounds for 2D coordinates must be 3-dimensional and '
235+ 'have 4 bounds per point.' )
236+
214237 bds_shape = bds .shape
215238 result = np .zeros ((bds_shape [0 ] + 1 , bds_shape [1 ] + 1 ))
216239
@@ -1029,7 +1052,7 @@ def cells(self):
10291052 """
10301053 return _CellIterator (self )
10311054
1032- def _sanity_check_contiguous (self ):
1055+ def _sanity_check_bounds (self ):
10331056 if self .ndim == 1 :
10341057 if self .nbounds != 2 :
10351058 raise ValueError ('Invalid operation for {!r}, with {} bounds. '
@@ -1043,9 +1066,9 @@ def _sanity_check_contiguous(self):
10431066 'coordinates with 4 bounds.' .format
10441067 (self .name (), self .nbounds ))
10451068 else :
1046- raise ValueError ('Invalid operation for {!r}. Contiguous bounds '
1047- 'are not defined for coordinates with more than '
1048- '2 dimensions.' . format (self .name ()))
1069+ raise ValueError ('Invalid operation for {!r}. Not supported for '
1070+ 'bounds with more than 2 dimensions.' . format
1071+ (self .name ()))
10491072
10501073 def is_contiguous (self , rtol = 1e-05 , atol = 1e-08 ):
10511074 """
@@ -1064,28 +1087,25 @@ def is_contiguous(self, rtol=1e-05, atol=1e-08):
10641087
10651088 """
10661089 if self .has_bounds ():
1067- self ._sanity_check_contiguous ()
1090+ self ._sanity_check_bounds ()
10681091 if self .ndim == 1 :
10691092 contiguous = np .allclose (self .bounds [1 :, 0 ],
10701093 self .bounds [:- 1 , 1 ],
10711094 rtol = rtol , atol = atol )
10721095 elif self .ndim == 2 :
1073- contiguous , _ , _ = _discontiguity_in_2d_bounds (self .bounds ,
1074- abs_tol = atol )
1096+
1097+ contiguous , _ , _ = _discontiguity_in_2d_bounds (
1098+ self .bounds , atol = atol )
10751099 else :
10761100 contiguous = False
10771101 return contiguous
10781102
10791103 def contiguous_bounds (self ):
10801104 """
10811105 Returns the N+1 bound values for a contiguous bounded 1D coordinate
1082- of length N.
1083-
1084- Returns the (N+1, M+1) bound values for a contiguous bounded 2D
1106+ of length N, or the (N+1, M+1) bound values for a contiguous bounded 2D
10851107 coordinate of shape (N, M).
10861108
1087- Assumes input is contiguous.
1088-
10891109 .. note::
10901110
10911111 If the coordinate does not have bounds, this method will
@@ -1097,7 +1117,10 @@ def contiguous_bounds(self):
10971117 'contiguous bounds.' .format (self .name ()))
10981118 bounds = self ._guess_bounds ()
10991119 else :
1100- self ._sanity_check_contiguous ()
1120+ if not self .is_contiguous ():
1121+ raise ValueError ('Invalid operation. Bounds of coord {!r} '
1122+ 'are not contiguous.' .format (self .name ()))
1123+
11011124 bounds = self .bounds
11021125
11031126 if self .ndim == 1 :
0 commit comments