@@ -886,15 +886,134 @@ def __getattr__(self, attr):
886886 )
887887
888888
889+ def create_flag_dict (da ):
890+ if not da .cf .is_flag_variable :
891+ raise ValueError (
892+ "Comparisons are only supported for DataArrays that represent CF flag variables."
893+ ".attrs must contain 'flag_values' and 'flag_meanings'"
894+ )
895+
896+ flag_meanings = da .attrs ["flag_meanings" ].split (" " )
897+ flag_values = da .attrs ["flag_values" ]
898+ # TODO: assert flag_values is iterable
899+ assert len (flag_values ) == len (flag_meanings )
900+ return dict (zip (flag_meanings , flag_values ))
901+
902+
889903class CFAccessor :
890904 """
891905 Common Dataset and DataArray accessor functionality.
892906 """
893907
894- def __init__ (self , da ):
895- self ._obj = da
908+ def __init__ (self , obj ):
909+ self ._obj = obj
896910 self ._all_cell_measures = None
897911
912+ def _assert_valid_other_comparison (self , other ):
913+ flag_dict = create_flag_dict (self ._obj )
914+ if other not in flag_dict :
915+ raise ValueError (
916+ f"Did not find flag value meaning [{ other } ] in known flag meanings: [{ flag_dict .keys ()!r} ]"
917+ )
918+ return flag_dict
919+
920+ def __eq__ (self , other ):
921+ """
922+ Compare flag values against `other`.
923+
924+ `other` must be in the 'flag_meanings' attribute.
925+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
926+ compared.
927+ """
928+ flag_dict = self ._assert_valid_other_comparison (other )
929+ return self ._obj == flag_dict [other ]
930+
931+ def __ne__ (self , other ):
932+ """
933+ Compare flag values against `other`.
934+
935+ `other` must be in the 'flag_meanings' attribute.
936+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
937+ compared.
938+ """
939+ flag_dict = self ._assert_valid_other_comparison (other )
940+ return self ._obj != flag_dict [other ]
941+
942+ def __lt__ (self , other ):
943+ """
944+ Compare flag values against `other`.
945+
946+ `other` must be in the 'flag_meanings' attribute.
947+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
948+ compared.
949+ """
950+ flag_dict = self ._assert_valid_other_comparison (other )
951+ return self ._obj < flag_dict [other ]
952+
953+ def __le__ (self , other ):
954+ """
955+ Compare flag values against `other`.
956+
957+ `other` must be in the 'flag_meanings' attribute.
958+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
959+ compared.
960+ """
961+ flag_dict = self ._assert_valid_other_comparison (other )
962+ return self ._obj <= flag_dict [other ]
963+
964+ def __gt__ (self , other ):
965+ """
966+ Compare flag values against `other`.
967+
968+ `other` must be in the 'flag_meanings' attribute.
969+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
970+ compared.
971+ """
972+ flag_dict = self ._assert_valid_other_comparison (other )
973+ return self ._obj > flag_dict [other ]
974+
975+ def __ge__ (self , other ):
976+ """
977+ Compare flag values against `other`.
978+
979+ `other` must be in the 'flag_meanings' attribute.
980+ `other` is mapped to the corresponding value in the 'flag_values' attribute, and then
981+ compared.
982+ """
983+ flag_dict = self ._assert_valid_other_comparison (other )
984+ return self ._obj >= flag_dict [other ]
985+
986+ def isin (self , test_elements ):
987+ """Test each value in the array for whether it is in test_elements.
988+
989+ Parameters
990+ ----------
991+ test_elements : array_like, 1D
992+ The values against which to test each value of `element`.
993+ These must be in "flag_meanings" attribute, and are mapped
994+ to the corresponding value in "flag_values" before passing
995+ that on to DataArray.isin.
996+
997+
998+ Returns
999+ -------
1000+ isin : DataArray
1001+ Has the same type and shape as this object, but with a bool dtype.
1002+ """
1003+ if not isinstance (self ._obj , DataArray ):
1004+ raise ValueError (
1005+ ".cf.isin is only supported on DataArrays that contain CF flag attributes."
1006+ )
1007+ flag_dict = create_flag_dict (self ._obj )
1008+ mapped_test_elements = []
1009+ for elem in test_elements :
1010+ if elem not in flag_dict :
1011+ raise ValueError (
1012+ f"Did not find flag value meaning [{ elem } ] in known flag meanings: [{ flag_dict .keys ()!r} ]"
1013+ )
1014+ mapped_test_elements .append (flag_dict [elem ])
1015+ return self ._obj .isin (mapped_test_elements )
1016+
8981017 def _get_all_cell_measures (self ):
8991018 """
9001019 Get all cell measures defined in the object, adding CF pre-defined measures.
@@ -1130,7 +1249,12 @@ def make_text_section(subtitle, attr, valid_values, default_keys=None):
11301249
11311250 return "\n " .join (rows ) + "\n "
11321251
1133- text = "Coordinates:"
1252+ if isinstance (self ._obj , DataArray ) and self ._obj .cf .is_flag_variable :
1253+ flag_dict = create_flag_dict (self ._obj )
1254+ text = f"CF Flag variable with mapping:\n \t { flag_dict !r} \n \n "
1255+ else :
1256+ text = ""
1257+ text += "Coordinates:"
11341258 text += make_text_section ("CF Axes" , "axes" , coords , _AXIS_NAMES )
11351259 text += make_text_section ("CF Coordinates" , "coordinates" , coords , _COORD_NAMES )
11361260 text += make_text_section (
@@ -2057,4 +2181,18 @@ def __getitem__(self, key: Union[str, List[str]]) -> DataArray:
20572181
20582182 return _getitem (self , key )
20592183
2060- pass
2184+ @property
2185+ def is_flag_variable (self ):
2186+ """
2187+ Returns True if the DataArray satisfies CF conventions for flag variables.
2188+
2189+ Flag masks are not supported yet.
2190+ """
2191+ if (
2192+ isinstance (self ._obj , DataArray )
2193+ and "flag_meanings" in self ._obj .attrs
2194+ and "flag_values" in self ._obj .attrs
2195+ ):
2196+ return True
2197+ else :
2198+ return False
0 commit comments