From a796f415ecd397e0df82e60bf9be50dd785a20df Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Thu, 21 Mar 2024 11:14:21 -0400 Subject: [PATCH 01/16] adding bit decoding utils --- .../default/plugins/data_quality/dq_utils.py | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/jdaviz/configs/default/plugins/data_quality/dq_utils.py b/jdaviz/configs/default/plugins/data_quality/dq_utils.py index e1ea1619a0..019fa603a7 100644 --- a/jdaviz/configs/default/plugins/data_quality/dq_utils.py +++ b/jdaviz/configs/default/plugins/data_quality/dq_utils.py @@ -1,7 +1,11 @@ from importlib import resources from pathlib import Path + +import numpy as np +from matplotlib.colors import ListedColormap, rgb2hex from astropy.table import Table +# paths to CSV files with DQ flag mappings: dq_flag_map_paths = { 'jwst': Path('data', 'data_quality', 'jwst.csv'), 'roman': Path('data', 'data_quality', 'roman.csv'), @@ -71,3 +75,93 @@ def write_flag_map(flag_mapping, csv_path, **kwargs): table.add_row(row) table.write(csv_path, format='ascii.csv', **kwargs) + + +def generate_listed_colormap(n_flags, seed=42): + """ + Generate a list of random "light" colors of length ``n_flags``. + + Parameters + ---------- + n_flags : int + Number of colors in the listed colormap, should match the + number of unique DQ flags (before they're decomposed). + seed : int + Seed for the random number generator used to + draw random colors. + + Returns + ------- + cmap : `~matplotlib.pyplot.colors.ListedColormap` + Colormap constructed with ``n_flags`` colors. + rgba_colors : list of tuples + Random light colors of length ``n_flags``. + """ + rng = np.random.default_rng(seed) + default_alpha = 1 + + # Generate random colors that are generally "light", i.e. with + # RGB values in the upper half of the interval (0, 1): + rgba_colors = [ + tuple(rng.uniform(low=0.5, high=1, size=3).tolist() + [default_alpha]) + for _ in range(n_flags) + ] + + cmap = ListedColormap(rgba_colors) + + # setting `bad` alpha=0 will make NaNs transparent: + cmap.set_bad(alpha=0) + return cmap, rgba_colors + + +def decompose_bit(bit): + """ + For an integer ``bit``, return a list of the powers of + two that sum up to ``bit``. + + Parameters + ---------- + bit : int + Sum of powers of two. + + Returns + ------- + powers : list of integers + Powers of two which sum to ``bit``. + """ + bit = int(bit) + powers = [] + i = 1 + while i <= bit: + if i & bit: + powers.append(int(np.log2(i))) + i <<= 1 + return sorted(powers) + + +def decode_flags(flag_map, unique_flags, rgba_colors): + """ + For a list of unique bits in ``unique_flags``, return a list of + dictionaries of the decomposed bits with their names, definitions, and + colors defined in ``rgba_colors``. + + Parameters + ---------- + flag_map : dict + Flag mapping, such as the ones produced by ``load_flag_map``. + unique_flags : list or array + Sequence of unique flags which occur in a data quality array. + rgba_colors : list of tuples + RGBA color tuples, one per unique flag. + """ + decoded_flags = [] + + for i, (bit, color) in enumerate(zip(unique_flags, rgba_colors)): + decoded_bits = decompose_bit(bit) + decoded_flags.append({ + 'flag': int(bit), + 'decomposed': {bit: flag_map[bit] for bit in decoded_bits}, + 'color': rgb2hex(color), + }) + + return decoded_flags From 774ab901965596a29d18bbfb0fbf5a76c4791e3a Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Thu, 21 Mar 2024 12:45:44 -0400 Subject: [PATCH 02/16] working on initial DQ plugin UI --- jdaviz/configs/default/default.yaml | 2 +- jdaviz/configs/default/plugins/__init__.py | 1 + .../default/plugins/data_quality/__init__.py | 2 +- .../plugins/data_quality/data_quality.py | 197 ++++++++++++++++++ .../plugins/data_quality/data_quality.vue | 124 +++++++++++ .../default/plugins/data_quality/dq_utils.py | 8 +- .../plugins/plot_options/plot_options.py | 51 +++++ jdaviz/configs/imviz/imviz.yaml | 1 + jdaviz/core/template_mixin.py | 24 ++- 9 files changed, 402 insertions(+), 8 deletions(-) create mode 100644 jdaviz/configs/default/plugins/data_quality/data_quality.py create mode 100644 jdaviz/configs/default/plugins/data_quality/data_quality.vue diff --git a/jdaviz/configs/default/default.yaml b/jdaviz/configs/default/default.yaml index 4dda8cbb79..ebf9e8449f 100644 --- a/jdaviz/configs/default/default.yaml +++ b/jdaviz/configs/default/default.yaml @@ -15,4 +15,4 @@ toolbar: tray: - g-subset-plugin - g-gaussian-smooth - - export + - export \ No newline at end of file diff --git a/jdaviz/configs/default/plugins/__init__.py b/jdaviz/configs/default/plugins/__init__.py index e516c2dd82..a79315363a 100644 --- a/jdaviz/configs/default/plugins/__init__.py +++ b/jdaviz/configs/default/plugins/__init__.py @@ -11,3 +11,4 @@ from .export.export import * # noqa from .plot_options.plot_options import * # noqa from .markers.markers import * # noqa +from .data_quality.data_quality import * # noqa diff --git a/jdaviz/configs/default/plugins/data_quality/__init__.py b/jdaviz/configs/default/plugins/data_quality/__init__.py index 8e834ef147..9fd5bcb8ff 100644 --- a/jdaviz/configs/default/plugins/data_quality/__init__.py +++ b/jdaviz/configs/default/plugins/data_quality/__init__.py @@ -1 +1 @@ -from .dq_utils import * # noqa +from .data_quality import * # noqa diff --git a/jdaviz/configs/default/plugins/data_quality/data_quality.py b/jdaviz/configs/default/plugins/data_quality/data_quality.py new file mode 100644 index 0000000000..3523e97b09 --- /dev/null +++ b/jdaviz/configs/default/plugins/data_quality/data_quality.py @@ -0,0 +1,197 @@ +import os +from traitlets import Any, Dict, Bool, List, Unicode, observe + +import numpy as np +from glue_jupyter.common.toolbar_vuetify import read_icon +from echo import delay_callback +from matplotlib.colors import hex2color + +from jdaviz.core.registries import tray_registry +from jdaviz.core.template_mixin import ( + PluginTemplateMixin, ViewerSelect, LayerSelect +) +from jdaviz.core.tools import ICON_DIR +from jdaviz.configs.default.plugins.data_quality.dq_utils import ( + decode_flags, generate_listed_colormap, dq_flag_map_paths, load_flag_map +) + +__all__ = ['DataQuality'] + +telescope_names = { + "jwst": "JWST", + "roman": "Roman" +} + + +@tray_registry('g-data-quality', label="Data Quality", viewer_requirements="image") +class DataQuality(PluginTemplateMixin): + template_file = __file__, "data_quality.vue" + + viewer_multiselect = Bool(False).tag(sync=True) + viewer_items = List().tag(sync=True) + viewer_selected = Any().tag(sync=True) # Any needed for multiselect + viewer_limits = Dict().tag(sync=True) + + # `layer` is the science data layer + science_layer_multiselect = Bool(False).tag(sync=True) + science_layer_items = List().tag(sync=True) + science_layer_selected = Any().tag(sync=True) # Any needed for multiselect + + # `dq_layer` is teh data quality layer corresponding to the + # science data in `layer` + dq_layer_multiselect = Bool(False).tag(sync=True) + dq_layer_items = List().tag(sync=True) + dq_layer_selected = Any().tag(sync=True) # Any needed for multiselect + + flag_map_definitions = Dict().tag(sync=True) + flag_map_selected = Any().tag(sync=True) + flag_map_items = List().tag(sync=True) + viewer_selected = Any().tag(sync=True) # Any needed for multiselect + decoded_flags = List().tag(sync=True) + + icons = Dict().tag(sync=True) + icon_radialtocheck = Unicode(read_icon(os.path.join(ICON_DIR, 'radialtocheck.svg'), 'svg+xml')).tag(sync=True) # noqa + icon_checktoradial = Unicode(read_icon(os.path.join(ICON_DIR, 'checktoradial.svg'), 'svg+xml')).tag(sync=True) # noqa + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.icons = {k: v for k, v in self.app.state.icons.items()} + + self.viewer = ViewerSelect( + self, 'viewer_items', 'viewer_selected', 'viewer_multiselect' + ) + self.science_layer = LayerSelect( + self, 'science_layer_items', 'science_layer_selected', + 'viewer_selected', 'science_layer_multiselect', is_root=True + ) + + self.dq_layer = LayerSelect( + self, 'dq_layer_items', 'dq_layer_selected', + 'viewer_selected', 'dq_layer_multiselect', is_root=False, + is_child_of=self.science_layer.selected + ) + + self.load_default_flag_maps() + self.init_decoding() + + @observe('science_layer_selected') + def update_dq_layer(self, *args): + if not hasattr(self, 'dq_layer'): + return + + self.dq_layer.filter_is_child_of = self.science_layer_selected + self.dq_layer._update_layer_items() + + def load_default_flag_maps(self): + for name in dq_flag_map_paths: + self.flag_map_definitions[name] = load_flag_map(name) + self.flag_map_items = self.flag_map_items + [telescope_names[name]] + + @property + def multiselect(self): + logging.warning(f"DeprecationWarning: multiselect has been replaced by separate viewer_multiselect and layer_multiselect and will be removed in the future. This currently evaluates viewer_multiselect or layer_multiselect") # noqa + return self.viewer_multiselect or self.layer_multiselect + + @multiselect.setter + def multiselect(self, value): + logging.warning(f"DeprecationWarning: multiselect has been replaced by separate viewer_multiselect and layer_multiselect and will be removed in the future. This currently sets viewer_multiselect and layer_multiselect") # noqa + self.viewer_multiselect = value + self.layer_multiselect = value + + def vue_set_value(self, data): + attr_name = data.get('name') + value = data.get('value') + setattr(self, attr_name, value) + + @property + def unique_flags(self): + selected_dq = self.dq_layer.selected_obj + if not len(selected_dq): + return [] + + dq = selected_dq[0].get_image_data() + return np.unique(dq[~np.isnan(dq)]) + + @property + def flag_map_definitions_selected(self): + return self.flag_map_definitions[self.flag_map_selected.lower()] + + @property + def validate_flag_decode_possible(self): + return ( + self.flag_map_selected is not None and + len(self.dq_layer.selected_obj) > 0 + ) + + @observe('dq_layer_selected') + def init_decoding(self, event={}): + if not self.validate_flag_decode_possible: + return + + unique_flags = self.unique_flags + cmap, rgba_colors = generate_listed_colormap(n_flags=len(unique_flags)) + self.decoded_flags = decode_flags( + flag_map=self.flag_map_definitions_selected, + unique_flags=unique_flags, + rgba_colors=rgba_colors + ) + + viewer = self.viewer.selected_obj + [dq_layer] = [ + layer for layer in viewer.layers if + layer.layer.label == self.dq_layer_selected + ] + dq_layer.composite._allow_bad_alpha = True + + flag_bits = np.float32([flag['flag'] for flag in self.decoded_flags]) + + with delay_callback(dq_layer.state, 'alpha', 'cmap', 'v_min', 'v_max', 'stretch'): + dq_layer.state.cmap = cmap + + dq_layer.state.stretch = 'lookup' + stretch_object = dq_layer.state.stretch_object + stretch_object.flags = flag_bits + + dq_layer.state.v_min = min(flag_bits) + dq_layer.state.v_max = max(flag_bits) + dq_layer.state.alpha = 0.9 + + @observe('decoded_flags') + def update_cmap(self, event={}): + viewer = self.viewer.selected_obj + [dq_layer] = [ + layer for layer in viewer.layers if + layer.layer.label == self.dq_layer_selected + ] + flag_bits = np.float32([flag['flag'] for flag in self.decoded_flags]) + rgb_colors = [hex2color(flag['color']) for flag in self.decoded_flags] + + # update the colors of the listed colormap without + # reassigning the layer.state.cmap object + cmap = dq_layer.state.cmap + cmap.colors = rgb_colors + cmap._init() + + with delay_callback(dq_layer.state, 'v_min', 'v_max', 'alpha'): + # trigger updates to cmap in viewer: + dq_layer.update() + + # set correct stretch and limits: + dq_layer.state.stretch = 'lookup' + dq_layer.state.v_min = min(flag_bits) + dq_layer.state.v_max = max(flag_bits) + dq_layer.state.alpha = 0.9 + + @observe('science_layer_selected') + def mission_or_instrument_from_meta(self, event): + if not hasattr(self, 'science_layer'): + return + + layer = self.science_layer.selected_obj + if len(layer): + # this is defined for JWST and ROMAN, should be upper case: + telescope = layer[0].layer.meta.get('telescope', None) + + if telescope is not None: + self.flag_map_selected = telescope_names[telescope.lower()] diff --git a/jdaviz/configs/default/plugins/data_quality/data_quality.vue b/jdaviz/configs/default/plugins/data_quality/data_quality.vue new file mode 100644 index 0000000000..4cfa4064cf --- /dev/null +++ b/jdaviz/configs/default/plugins/data_quality/data_quality.vue @@ -0,0 +1,124 @@ + + + + + + diff --git a/jdaviz/configs/default/plugins/data_quality/dq_utils.py b/jdaviz/configs/default/plugins/data_quality/dq_utils.py index 019fa603a7..940071295a 100644 --- a/jdaviz/configs/default/plugins/data_quality/dq_utils.py +++ b/jdaviz/configs/default/plugins/data_quality/dq_utils.py @@ -45,7 +45,7 @@ def load_flag_map(mission_or_instrument=None, path=None): flag_mapping = {} for flag, name, desc in flag_table.iterrows(): - flag_mapping[flag] = dict(name=name, description=desc) + flag_mapping[int(flag)] = dict(name=name, description=desc) return flag_mapping @@ -77,7 +77,7 @@ def write_flag_map(flag_mapping, csv_path, **kwargs): table.write(csv_path, format='ascii.csv', **kwargs) -def generate_listed_colormap(n_flags, seed=42): +def generate_listed_colormap(n_flags=None, seed=3): """ Generate a list of random "light" colors of length ``n_flags``. @@ -103,14 +103,14 @@ def generate_listed_colormap(n_flags, seed=42): # Generate random colors that are generally "light", i.e. with # RGB values in the upper half of the interval (0, 1): rgba_colors = [ - tuple(rng.uniform(low=0.5, high=1, size=3).tolist() + [default_alpha]) + tuple(np.insert(rng.uniform(size=2), rng.integers(0, 3), 1).tolist() + [default_alpha]) for _ in range(n_flags) ] cmap = ListedColormap(rgba_colors) # setting `bad` alpha=0 will make NaNs transparent: - cmap.set_bad(alpha=0) + cmap.set_bad(color='k', alpha=0) return cmap, rgba_colors diff --git a/jdaviz/configs/default/plugins/plot_options/plot_options.py b/jdaviz/configs/default/plugins/plot_options/plot_options.py index 4dff3fbf69..841d5af664 100644 --- a/jdaviz/configs/default/plugins/plot_options/plot_options.py +++ b/jdaviz/configs/default/plugins/plot_options/plot_options.py @@ -115,10 +115,61 @@ def update_knots(self, x, y): self.spline = PchipInterpolator(self._x, self._y) +class LookupStretch: + """ + Stretch class specific to DQ arrays. + + Attributes + ---------- + flags : array-like + DQ flags. + """ + + def __init__(self, flags=None): + # Default x, y values(0-1) range chosen for a typical initial spline shape. + # Can be modified if required. + if flags is None: + flags = np.linspace(0, 1, 5) + self.flags = np.asarray(flags) + + def __call__(self, values, out=None, clip=False): + # For our uses, we can ignore `out` and `clip`, but those would need + # to be implemented before contributing this class upstream. + + # find closest index in `self.flags` for each value in `values`: + if hasattr(values, 'squeeze'): + values = values.squeeze() + + # renormalize the flags on range (0, 1): + scaled_flags = self.flags / np.max(self.flags) + + # `values` will have already been passed through + # astropy.visualization.ManualInterval and normalized on (0, 1) + # before they arrive here. Now find the index of the closest entry in + # `scaled_flags` for each of `values` using array broadcasting. + min_indices = np.argmin(np.abs( + np.nan_to_num(values, nan=-10).flatten()[None, :] - scaled_flags[:, None] + ), axis=0).reshape(values.shape) + + # normalize by the number of flags, onto interval (0, 1): + renormed = min_indices / (len(self.flags) - 1) + + # preserve nans in the result: + renormed = np.where( + np.isnan(values), + np.nan, + renormed + ) + return renormed + + # Add the spline stretch to the glue stretch registry if not registered if "spline" not in stretches: stretches.add("spline", SplineStretch, display="Spline") +if "lookup" not in stretches: + stretches.add("lookup", LookupStretch, display="DQ") + def _round_step(step): # round the step for a float input diff --git a/jdaviz/configs/imviz/imviz.yaml b/jdaviz/configs/imviz/imviz.yaml index 4e7de049bb..2934cebabf 100644 --- a/jdaviz/configs/imviz/imviz.yaml +++ b/jdaviz/configs/imviz/imviz.yaml @@ -24,6 +24,7 @@ tray: - g-plot-options - g-subset-plugin - g-markers + - g-data-quality - imviz-compass - imviz-line-profile-xy - imviz-aper-phot-simple diff --git a/jdaviz/core/template_mixin.py b/jdaviz/core/template_mixin.py index 8647b4c6b1..91bbe36cf3 100644 --- a/jdaviz/core/template_mixin.py +++ b/jdaviz/core/template_mixin.py @@ -1341,8 +1341,9 @@ def __init__(self, plugin, items, selected, viewer, multiselect=None, default_text=None, manual_options=[], default_mode='first', - filters=['not_child_layer'], - only_wcs_layers=False): + only_wcs_layers=False, + is_root=True, + is_child_of=None): """ Parameters ---------- @@ -1395,6 +1396,25 @@ def __init__(self, plugin, items, selected, viewer, self._update_layer_items() self.update_wcs_only_filter(only_wcs_layers) + self.filter_is_root = is_root + self.filter_is_child_of = is_child_of + + if self.filter_is_root: + # ignore layers that are children in associations: + def filter_is_root(data): + return self.app._get_assoc_data_parent(data.label) is None + + self.add_filter(filter_is_root) + + elif not self.filter_is_root and self.filter_is_child_of is not None: + # only offer layers that are children of the correct parent: + def has_correct_parent(data): + if self.filter_is_child_of == '': + return False + return self.app._get_assoc_data_parent(data.label) == self.filter_is_child_of + + self.add_filter(has_correct_parent) + def _get_viewer(self, viewer): # newer will likely be the viewer name in most cases, but viewer id in the case # of additional viewers in imviz. From 0b0776ae60be7eb5738244f23ca5364a164e2254 Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Mon, 25 Mar 2024 10:09:45 -0400 Subject: [PATCH 03/16] adding irrelevant message, commented out --- jdaviz/configs/default/plugins/data_quality/data_quality.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jdaviz/configs/default/plugins/data_quality/data_quality.py b/jdaviz/configs/default/plugins/data_quality/data_quality.py index 3523e97b09..04a8a6f4f6 100644 --- a/jdaviz/configs/default/plugins/data_quality/data_quality.py +++ b/jdaviz/configs/default/plugins/data_quality/data_quality.py @@ -27,6 +27,9 @@ class DataQuality(PluginTemplateMixin): template_file = __file__, "data_quality.vue" + # TODO: uncomment this line before merging into main: + # irrelevant_msg = Unicode("Data Quality plugin is in development.").tag(sync=True) + viewer_multiselect = Bool(False).tag(sync=True) viewer_items = List().tag(sync=True) viewer_selected = Any().tag(sync=True) # Any needed for multiselect From 8e608700f50522f94d922fb78e17f0b299a1f0e4 Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Tue, 26 Mar 2024 14:53:42 -0400 Subject: [PATCH 04/16] working single-bit masking --- .../plugins/data_quality/data_quality.py | 88 ++++++++++++----- .../plugins/data_quality/data_quality.vue | 77 ++++++++++++--- .../default/plugins/data_quality/dq_utils.py | 98 +++++++++++++++++-- .../plugins/plot_options/plot_options.py | 51 ---------- 4 files changed, 218 insertions(+), 96 deletions(-) diff --git a/jdaviz/configs/default/plugins/data_quality/data_quality.py b/jdaviz/configs/default/plugins/data_quality/data_quality.py index 04a8a6f4f6..f58052de7f 100644 --- a/jdaviz/configs/default/plugins/data_quality/data_quality.py +++ b/jdaviz/configs/default/plugins/data_quality/data_quality.py @@ -147,18 +147,19 @@ def init_decoding(self, event={}): ] dq_layer.composite._allow_bad_alpha = True - flag_bits = np.float32([flag['flag'] for flag in self.decoded_flags]) + flag_bits = np.array([flag['flag'] for flag in self.decoded_flags]) - with delay_callback(dq_layer.state, 'alpha', 'cmap', 'v_min', 'v_max', 'stretch'): - dq_layer.state.cmap = cmap + dq_layer.state.stretch = 'lookup' + stretch_object = dq_layer.state.stretch_object + stretch_object.flags = flag_bits - dq_layer.state.stretch = 'lookup' - stretch_object = dq_layer.state.stretch_object - stretch_object.flags = flag_bits + with delay_callback(dq_layer.state, 'alpha', 'cmap', 'v_min', 'v_max'): + if len(flag_bits): + dq_layer.state.v_min = min(flag_bits) + dq_layer.state.v_max = max(flag_bits) - dq_layer.state.v_min = min(flag_bits) - dq_layer.state.v_max = max(flag_bits) dq_layer.state.alpha = 0.9 + dq_layer.state.cmap = cmap @observe('decoded_flags') def update_cmap(self, event={}): @@ -167,34 +168,73 @@ def update_cmap(self, event={}): layer for layer in viewer.layers if layer.layer.label == self.dq_layer_selected ] - flag_bits = np.float32([flag['flag'] for flag in self.decoded_flags]) + flag_bits = np.array([flag['flag'] for flag in self.decoded_flags]) rgb_colors = [hex2color(flag['color']) for flag in self.decoded_flags] + hidden_flags = np.array([flag['flag'] for flag in self.decoded_flags if not flag['show']]) - # update the colors of the listed colormap without - # reassigning the layer.state.cmap object - cmap = dq_layer.state.cmap - cmap.colors = rgb_colors - cmap._init() + with delay_callback(dq_layer.state, 'v_min', 'v_max', 'alpha', 'stretch', 'cmap'): + # set correct stretch and limits: + # dq_layer.state.stretch = 'lookup' + stretch_object = dq_layer.state.stretch_object + stretch_object.flags = flag_bits + stretch_object.dq_array = dq_layer.get_image_data() + stretch_object.hidden_flags = hidden_flags + + # update the colors of the listed colormap without + # reassigning the layer.state.cmap object + cmap = dq_layer.state.cmap + cmap.colors = rgb_colors + cmap._init() - with delay_callback(dq_layer.state, 'v_min', 'v_max', 'alpha'): # trigger updates to cmap in viewer: dq_layer.update() - # set correct stretch and limits: - dq_layer.state.stretch = 'lookup' - dq_layer.state.v_min = min(flag_bits) - dq_layer.state.v_max = max(flag_bits) + if len(flag_bits): + dq_layer.state.v_min = min(flag_bits) + dq_layer.state.v_max = max(flag_bits) + dq_layer.state.alpha = 0.9 + def update_visibility(self, index): + self.decoded_flags[index]['show'] = not self.decoded_flags[index]['show'] + self.send_state('decoded_flags') + self.update_cmap() + + def vue_update_visibility(self, index): + self.update_visibility(index) + + def update_color(self, index, color): + self.decoded_flags[index]['color'] = color + self.send_state('decoded_flags') + self.update_cmap() + + def vue_update_color(self, args): + index, color = args + self.update_color(index, color) + @observe('science_layer_selected') def mission_or_instrument_from_meta(self, event): if not hasattr(self, 'science_layer'): return layer = self.science_layer.selected_obj - if len(layer): - # this is defined for JWST and ROMAN, should be upper case: - telescope = layer[0].layer.meta.get('telescope', None) + if not len(layer): + return + + # this is defined for JWST and ROMAN, should be upper case: + telescope = layer[0].layer.meta.get('telescope', None) + + if telescope is not None: + self.flag_map_selected = telescope_names[telescope.lower()] + + def vue_hide_all_flags(self, event): + for flag in self.decoded_flags: + flag['show'] = False + self.send_state('decoded_flags') + self.update_cmap() - if telescope is not None: - self.flag_map_selected = telescope_names[telescope.lower()] + def vue_show_all_flags(self, event): + for flag in self.decoded_flags: + flag['show'] = True + self.send_state('decoded_flags') + self.update_cmap() diff --git a/jdaviz/configs/default/plugins/data_quality/data_quality.vue b/jdaviz/configs/default/plugins/data_quality/data_quality.vue index 4cfa4064cf..3a0d9a0f6f 100644 --- a/jdaviz/configs/default/plugins/data_quality/data_quality.vue +++ b/jdaviz/configs/default/plugins/data_quality/data_quality.vue @@ -49,17 +49,50 @@ Quality Flags + + + + + mdi-eye + Show All + + + + + + + mdi-eye-off + Hide All + + + + + Color - - Flag - - - (Decomposed) + + Flag (Decomposed) + @@ -78,21 +111,30 @@
+ @update:color="throttledSetColor(index, $event.hexa)"> >
- -
{{item.flag}} ({{Object.keys(item.decomposed).join(', ')}})
+ +
{{item.flag}} ({{Object.keys(item.decomposed).join(', ')}})
- - {{item.name}} ({{key}}): {{item.description}} + + + + {{item.show ? "mdi-eye" : "mdi-eye-off"}} + + + + {{item.name}} ({{key}}): {{item.description}} + + + @@ -103,12 +145,19 @@