@@ -255,6 +255,31 @@ def _get_measure(obj: Union[DataArray, Dataset], key: str) -> List[str]:
255
255
return list (results )
256
256
257
257
258
+ def _get_bounds (obj : Union [DataArray , Dataset ], key : str ) -> List [str ]:
259
+ """
260
+ Translate from key (either CF key or variable name) to its bounds' variable names.
261
+ This function interprets the ``bounds`` attribute on DataArrays.
262
+
263
+ Parameters
264
+ ----------
265
+ obj : DataArray, Dataset
266
+ DataArray belonging to the coordinate to be checked
267
+ key : str
268
+ key to check for.
269
+
270
+ Returns
271
+ -------
272
+ List[str], Variable name(s) in parent xarray object that are bounds of `key`
273
+ """
274
+
275
+ results = set ()
276
+ for var in apply_mapper (_get_all , obj , key , error = False , default = [key ]):
277
+ if "bounds" in obj [var ].attrs :
278
+ results |= {obj [var ].attrs ["bounds" ]}
279
+
280
+ return list (results )
281
+
282
+
258
283
def _get_with_standard_name (
259
284
obj : Union [DataArray , Dataset ], name : Union [str , List [str ]]
260
285
) -> List [str ]:
@@ -436,6 +461,14 @@ def _getattr(
436
461
try :
437
462
attribute : Union [Mapping , Callable ] = getattr (obj , attr )
438
463
except AttributeError :
464
+ if getattr (
465
+ CFDatasetAccessor if isinstance (obj , DataArray ) else CFDataArrayAccessor ,
466
+ attr ,
467
+ None ,
468
+ ):
469
+ raise AttributeError (
470
+ f"{ obj .__class__ .__name__ + '.cf' !r} object has no attribute { attr !r} "
471
+ )
439
472
raise AttributeError (
440
473
f"{ attr !r} is not a valid attribute on the underlying xarray object."
441
474
)
@@ -976,7 +1009,9 @@ def __repr__(self):
976
1009
coords = self ._obj .coords
977
1010
dims = self ._obj .dims
978
1011
979
- def make_text_section (subtitle , vardict , valid_values , default_keys = None ):
1012
+ def make_text_section (subtitle , attr , valid_values , default_keys = None ):
1013
+
1014
+ vardict = getattr (self , attr , {})
980
1015
981
1016
star = " * "
982
1017
tab = len (star ) * " "
@@ -1019,21 +1054,21 @@ def make_text_section(subtitle, vardict, valid_values, default_keys=None):
1019
1054
return "\n " .join (rows ) + "\n "
1020
1055
1021
1056
text = "Coordinates:"
1022
- text += make_text_section ("CF Axes" , self .axes , coords , _AXIS_NAMES )
1057
+ text += make_text_section ("CF Axes" , "axes" , coords , _AXIS_NAMES )
1058
+ text += make_text_section ("CF Coordinates" , "coordinates" , coords , _COORD_NAMES )
1023
1059
text += make_text_section (
1024
- "CF Coordinates " , self . coordinates , coords , _COORD_NAMES
1060
+ "Cell Measures " , "cell_measures" , coords , _CELL_MEASURES
1025
1061
)
1026
- text += make_text_section (
1027
- "Cell Measures" , self .cell_measures , coords , _CELL_MEASURES
1028
- )
1029
- text += make_text_section ("Standard Names" , self .standard_names , coords )
1062
+ text += make_text_section ("Standard Names" , "standard_names" , coords )
1063
+ text += make_text_section ("Bounds" , "bounds" , coords )
1030
1064
if isinstance (self ._obj , Dataset ):
1031
1065
data_vars = self ._obj .data_vars
1032
1066
text += "\n Data Variables:"
1033
1067
text += make_text_section (
1034
- "Cell Measures" , self . cell_measures , data_vars , _CELL_MEASURES
1068
+ "Cell Measures" , " cell_measures" , data_vars , _CELL_MEASURES
1035
1069
)
1036
- text += make_text_section ("Standard Names" , self .standard_names , data_vars )
1070
+ text += make_text_section ("Standard Names" , "standard_names" , data_vars )
1071
+ text += make_text_section ("Bounds" , "bounds" , data_vars )
1037
1072
1038
1073
return text
1039
1074
@@ -1144,7 +1179,7 @@ def get_standard_names(self) -> List[str]:
1144
1179
@property
1145
1180
def standard_names (self ) -> Dict [str , List [str ]]:
1146
1181
"""
1147
- Returns a sorted list of standard names in Dataset .
1182
+ Returns a dictionary mapping standard names to variable names .
1148
1183
1149
1184
Parameters
1150
1185
----------
@@ -1153,7 +1188,7 @@ def standard_names(self) -> Dict[str, List[str]]:
1153
1188
1154
1189
Returns
1155
1190
-------
1156
- Dictionary of standard names in dataset
1191
+ Dictionary mapping standard names to variable names.
1157
1192
"""
1158
1193
if isinstance (self ._obj , Dataset ):
1159
1194
variables = self ._obj .variables
@@ -1480,6 +1515,26 @@ def __getitem__(self, key: Union[str, List[str]]) -> Union[DataArray, Dataset]:
1480
1515
"""
1481
1516
return _getitem (self , key )
1482
1517
1518
+ @property
1519
+ def bounds (self ) -> Dict [str , List [str ]]:
1520
+ """
1521
+ Property that returns a dictionary mapping valid keys
1522
+ to the variable names of their bounds.
1523
+
1524
+ Returns
1525
+ -------
1526
+ Dictionary mapping valid keys to the variable names of their bounds.
1527
+ """
1528
+
1529
+ obj = self ._obj
1530
+ keys = self .keys () | set (obj .variables )
1531
+
1532
+ vardict = {
1533
+ key : apply_mapper (_get_bounds , obj , key , error = False ) for key in keys
1534
+ }
1535
+
1536
+ return {k : sorted (v ) for k , v in vardict .items () if v }
1537
+
1483
1538
def get_bounds (self , key : str ) -> DataArray :
1484
1539
"""
1485
1540
Get bounds variable corresponding to key.
@@ -1493,12 +1548,8 @@ def get_bounds(self, key: str) -> DataArray:
1493
1548
-------
1494
1549
DataArray
1495
1550
"""
1496
- name = apply_mapper (
1497
- _single (_get_all ), self ._obj , key , error = False , default = [key ]
1498
- )[0 ]
1499
- bounds = self ._obj [name ].attrs ["bounds" ]
1500
- obj = self ._maybe_to_dataset ()
1501
- return obj [bounds ]
1551
+
1552
+ return apply_mapper (_variables (_single (_get_bounds )), self ._obj , key )[0 ]
1502
1553
1503
1554
def get_bounds_dim_name (self , key : str ) -> str :
1504
1555
"""
0 commit comments