Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

DQ support for Cubeviz #2817

Merged
merged 13 commits into from
Apr 26, 2024
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ New Features

- The filename entry in the export plugin is now automatically populated based on the selection. [#2824]

- Adding Data Quality plugin for Imviz and Cubeviz. [#2767, #2817]

Cubeviz
^^^^^^^

Expand Down
13 changes: 13 additions & 0 deletions docs/cubeviz/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ To show axes on image viewers, toggle on the "Show axes" option at the bottom of
:ref:`Spectral Plot Options <specviz-plot-settings>`
Documentation on Specviz display settings in the Jdaviz viewers.

.. _cubeviz-data-quality:

Data Quality
============

Visualize data quality arrays for spectral cubes from JWST.

.. seealso::

:ref:`Data Quality Plugin <imviz-data-quality>`
Follow the link to the rest of the Data Quality plugin documentation.


.. _cubeviz-subset-plugin:

Subset Tools
Expand Down
26 changes: 26 additions & 0 deletions docs/imviz/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,32 @@ This plugin gives access to per-viewer and per-layer plotting options.
:ref:`Display Settings <imviz-display-settings>`
Documentation on various display settings in the Jdaviz viewers.

.. _imviz-data-quality:

Data Quality
============

Visualize data quality arrays for science data. The currently supported
data quality flag mappings include JWST (all instruments) and Roman/WFI.

Each science data layer can have one associated data quality layer.
The visibility of the data quality layer can be toggled from the data
dropdown menu, and toggling the science data visibility will do the
same for the data quality layer. The mapping between bits and data quality
flags is defined differently for each mission or instrument, and the
plugin will infer the correct flag mapping from the file metadata.
The opacity of the data quality layer can be changed relative to the
opacity of the science data layer using the slider.

The Quality Flag section contains a dropdown for applying a filter to the
visualized bits. Select bits from the dropdown to visualize only flags
containing those bits. The list of data quality flags beneath shows
every flag in the data quality layer in bold, followed by the
decomposed bits in that flag in parentheses. Clicking on a row
will expand to flag to reveal the flag's short name and description,
as well as a visibility toggle for that flag. Click on the color swatch
to select a color for any flag.

.. _imviz-subset-plugin:

Subset Tools
Expand Down
51 changes: 41 additions & 10 deletions jdaviz/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2091,10 +2091,12 @@ def set_data_visibility(self, viewer_reference, data_label, visible=True, replac
sender=self)
self.hub.broadcast(add_data_message)

assoc_children = self._get_assoc_data_children(data_label)

# set visibility state of all applicable layers
for layer in viewer.layers:
layer_is_wcs_only = getattr(layer.layer, 'meta', {}).get(_wcs_only_label, False)
if layer.layer.data.label == data_label:
if layer.layer.data.label in [data_label] + assoc_children:
if layer_is_wcs_only:
layer.visible = False
layer.update()
Expand All @@ -2112,7 +2114,22 @@ def set_data_visibility(self, viewer_reference, data_label, visible=True, replac
layer.visible = False

# if Data has children, update their visibilities to match Data:
assoc_children = self._get_assoc_data_children(data_label)
available_plugins = [tray_item['name'] for tray_item in self.state.tray_items]
for child in assoc_children:
if child not in viewer.data_labels_loaded:
self.add_data_to_viewer(viewer.reference, child, visible=visible)

if 'g-data-quality' in available_plugins and visible:
# if we're adding a DQ layer to a viewer, make sure that
# the layer is appropriately colormapped as DQ:
data_quality_plugin = self.get_tray_item_from_name('g-data-quality')
old_viewer = data_quality_plugin.viewer_selected
data_quality_plugin.viewer_selected = viewer.reference
data_quality_plugin.science_layer_selected = data_label
data_quality_plugin.dq_layer_selected = child
data_quality_plugin.init_decoding(viewers=[viewer])
data_quality_plugin.viewer_selected = old_viewer

for layer in viewer.layers:
if layer.layer.data.label in assoc_children:
if visible and not layer.visible:
Expand All @@ -2121,14 +2138,19 @@ def set_data_visibility(self, viewer_reference, data_label, visible=True, replac
else:
layer.visible = visible

# update data menu - selected_data_items should be READ ONLY, not modified by the user/UI
# update data menu - selected_data_items should be READ ONLY, not modified by the user/UI.
# must update the visibility of `data_label` and its children:
selected_items = viewer_item['selected_data_items']
data_id = self._data_id_from_label(data_label)
selected_items[data_id] = 'visible' if visible else 'hidden'
if replace:
for id in selected_items:
if id != data_id:
selected_items[id] = 'hidden'
update_data_labels = [data_label] + assoc_children
for update_data_label in update_data_labels:
data_id = self._data_id_from_label(update_data_label)

if replace and update_data_label == data_label:
for id in selected_items:
if id != data_id:
selected_items[id] = 'hidden'

selected_items[data_id] = 'visible' if visible else 'hidden'

# remove WCS-only data from selected items, add to wcs_only_layers:
for layer in viewer.layers:
Expand Down Expand Up @@ -2316,7 +2338,9 @@ def _expose_meta(key):
'has_wcs': data_has_valid_wcs(data),
'is_astrowidgets_markers_table': (self.config == "imviz") and layer_is_table_data(data),
'meta': {k: v for k, v in data.meta.items() if _expose_meta(k)},
'children': []}
'children': [],
'parent': None,
}

@staticmethod
def _create_stack_item(container='gl-stack', children=None, viewers=None):
Expand Down Expand Up @@ -2712,6 +2736,13 @@ def _add_assoc_data_as_parent(self, data_label):
def _set_assoc_data_as_child(self, data_label, new_parent_label):
# Data has a new parent:
self._data_associations[data_label]['parent'] = new_parent_label

# update the data item so vue can see the change:
for data_item in self.state.data_items:
if data_item['name'] == data_label:
data_item['parent'] = new_parent_label
break

# parent has a new child:
self._data_associations[new_parent_label]['children'].append(data_label)

Expand Down
8 changes: 6 additions & 2 deletions jdaviz/components/viewer_data_select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
:icon="layer_icons[item.name]"
:icons="icons"
:viewer="viewer"
:multi_select="multi_select"
:multi_select="multi_select || isChild(item)"
:is_wcs_only="false"
:n_data_entries="nDataEntries"
@data-item-visibility="$emit('data-item-visibility', $event)"
Expand Down Expand Up @@ -273,6 +273,10 @@ module.exports = {
isRefData() {
return this.$props.item.viewer.reference_data_label === this.$props.item.name
},
isChild(item) {
// only override multi_select choice when data entry is a child:
return item.parent !== null
},
selectRefData() {
this.$emit('change-reference-data', {
id: this.$props.viewer.id,
Expand Down Expand Up @@ -302,7 +306,7 @@ module.exports = {
return this.$props.data_items.filter((item) => this.itemIsVisible(item, false))
},
extraDataItems() {
return this.$props.data_items.filter((item) => this.itemIsVisible(item, true))
return this.$props.data_items.filter((item) => this.itemIsVisible(item, true) && !this.isChild(item))
},
nDataEntries() {
// return number of data entries in the entire plugin that were NOT created by a plugin
Expand Down
6 changes: 5 additions & 1 deletion jdaviz/components/viewer_data_select_item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,11 @@ module.exports = {
} else {
return 'gray'
}
}
},
isChild(item) {
// only override multi_select choice when data entry is a child:
return item.parent !== null
},
}
};
</script>
Expand Down
1 change: 1 addition & 0 deletions jdaviz/configs/cubeviz/cubeviz.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ toolbar:
tray:
- g-metadata-viewer
- g-plot-options
- g-data-quality
- g-subset-plugin
- g-markers
- cubeviz-slice
Expand Down
32 changes: 28 additions & 4 deletions jdaviz/configs/cubeviz/plugins/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from astropy.wcs import WCS
from specutils import Spectrum1D

from jdaviz.configs.imviz.plugins.parsers import prep_data_layer_as_dq
from jdaviz.core.registries import data_parser_registry
from jdaviz.utils import standardize_metadata, PRIHDR_KEY

Expand All @@ -22,7 +23,7 @@


@data_parser_registry("cubeviz-data-parser")
def parse_data(app, file_obj, data_type=None, data_label=None):
def parse_data(app, file_obj, data_type=None, data_label=None, parent=None):
"""
Attempts to parse a data file and auto-populate available viewers in
cubeviz.
Expand Down Expand Up @@ -84,10 +85,24 @@
('ERR', uncert_viewer_reference_name),
('DQ', None)):
data_label = app.return_data_label(file_name, ext)

if ext == 'SCI':
sci_ext = data_label

# TODO: generalize/centralize this for use in other configs too

if parent is not None:
parent_data_label = parent

Check warning on line 95 in jdaviz/configs/cubeviz/plugins/parsers.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/parsers.py#L95

Added line #L95 was not covered by tests
elif ext == 'DQ':
parent_data_label = sci_ext
else:
parent_data_label = None

_parse_jwst_s3d(
app, hdulist, data_label, ext=ext, viewer_name=viewer_name,
flux_viewer_reference_name=flux_viewer_reference_name,
spectrum_viewer_reference_name=spectrum_viewer_reference_name
spectrum_viewer_reference_name=spectrum_viewer_reference_name,
parent=parent_data_label
)
elif telescop == 'jwst' and filetype == 'r3d' and system == 'esa-pipeline':
for ext, viewer_name in (('DATA', flux_viewer_reference_name),
Expand Down Expand Up @@ -270,7 +285,7 @@

def _parse_jwst_s3d(app, hdulist, data_label, ext='SCI',
viewer_name=None, flux_viewer_reference_name=None,
spectrum_viewer_reference_name=None):
spectrum_viewer_reference_name=None, parent=None):
hdu = hdulist[ext]
data_type = _get_data_type_by_hdu(hdu)

Expand Down Expand Up @@ -305,7 +320,13 @@
metadata[PRIHDR_KEY] = standardize_metadata(hdulist['PRIMARY'].header)

data = _return_spectrum_with_correct_units(flux, wcs, metadata, data_type, hdulist=hdulist)
app.add_data(data, data_label)
app.add_data(data, data_label, parent=parent)

# get glue data and update if DQ:
if ext == 'DQ':
data = app.data_collection[-1]
prep_data_layer_as_dq(data, component_id='flux')

if data_type == 'flux': # Forced wave unit conversion made it lose stuff, so re-add
app.data_collection[-1].get_component("flux").units = flux.unit

Expand All @@ -315,6 +336,9 @@
if viewer_name == flux_viewer_reference_name:
app.add_data_to_viewer(spectrum_viewer_reference_name, data_label)

if ext == 'DQ':
app.add_data_to_viewer(flux_viewer_reference_name, data_label, visible=False)

if data_type == 'flux':
app._jdaviz_helper._loaded_flux_cube = app.data_collection[data_label]
elif data_type == 'uncert':
Expand Down
Loading