Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

PoC: Cubeviz parser for Roman L1 (ramp) products #2376

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,8 @@ Cubeviz
- Add Spectral Extraction plugin for Cubeviz, which converts spectral cubes
to 1D spectra with propagated uncertainties [#2039]

- Support for loading Roman WFI Level 1 (ramp) files [#2376]

Imviz
^^^^^

Expand Down
10 changes: 8 additions & 2 deletions jdaviz/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,13 @@
# should return the converted values. Note that original_units
# gives the units of the values array, which might not be the same
# as the original native units of the component in the data.
if cid.label == "flux":
if cid.label == 'Pixel Axis 0 [z]' and target_units == '':
# handle ramps loaded into Cubeviz by avoiding conversion
# of the groups axis:
return values

Check warning on line 110 in jdaviz/app.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/app.py#L110

Added line #L110 was not covered by tests
elif cid.label == "flux":
try:
spec = data.get_object(cls=Spectrum1D)

except RuntimeError:
data = data.get_object(cls=NDDataArray)
spec = Spectrum1D(flux=data.data * u.Unit(original_units))
Expand Down Expand Up @@ -1255,6 +1258,9 @@
elif axis == 'flux':
sv = self.get_viewer(self._jdaviz_helper._default_spectrum_viewer_reference_name)
return sv.data()[0].flux.unit
elif axis == 'data':
sv = self.get_viewer(self._jdaviz_helper._default_spectrum_viewer_reference_name)
return sv.data()[0].unit

Check warning on line 1263 in jdaviz/app.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/app.py#L1261-L1263

Added lines #L1261 - L1263 were not covered by tests
else:
raise ValueError(f"could not find units for axis='{axis}'")
try:
Expand Down
125 changes: 123 additions & 2 deletions jdaviz/configs/cubeviz/plugins/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,21 @@
from jdaviz.utils import standardize_metadata, PRIHDR_KEY, download_uri_to_path


try:
from roman_datamodels import datamodels as rdd
except ImportError:
HAS_ROMAN_DATAMODELS = False
else:
HAS_ROMAN_DATAMODELS = True

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L23 was not covered by tests

__all__ = ['parse_data']

EXT_TYPES = dict(flux=['flux', 'sci', 'data'],
uncert=['ivar', 'err', 'error', 'var', 'uncert'],
mask=['mask', 'dq', 'quality', 'data_quality'])

cubeviz_ramp_meta_flag = '_roman_ramp'


@data_parser_registry("cubeviz-data-parser")
def parse_data(app, file_obj, data_type=None, data_label=None,
Expand Down Expand Up @@ -79,6 +88,19 @@
_parse_gif(app, file_obj, data_label,
flux_viewer_reference_name=flux_viewer_reference_name)
return
elif file_obj.lower().endswith('.asdf'):
if not HAS_ROMAN_DATAMODELS:
raise ImportError(

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

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/parsers.py#L92-L93

Added lines #L92 - L93 were not covered by tests
"ASDF detected but roman-datamodels is not installed."
)
with rdd.open(file_obj) as pf:
_roman_3d_to_glue_data(

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

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/parsers.py#L96-L97

Added lines #L96 - L97 were not covered by tests
app, pf, data_label,
flux_viewer_reference_name=flux_viewer_reference_name,
spectrum_viewer_reference_name=spectrum_viewer_reference_name,
uncert_viewer_reference_name=uncert_viewer_reference_name
)
return

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L103 was not covered by tests

# try parsing file_obj as a URI/URL:
file_obj = download_uri_to_path(
Expand Down Expand Up @@ -158,7 +180,18 @@
_parse_ndarray(app, file_obj, data_label=data_label, data_type=data_type,
flux_viewer_reference_name=flux_viewer_reference_name,
uncert_viewer_reference_name=uncert_viewer_reference_name)

app.get_tray_item_from_name("Spectral Extraction").disabled_msg = ""

elif HAS_ROMAN_DATAMODELS and isinstance(file_obj, rdd.DataModel):
with rdd.open(file_obj) as pf:
_roman_3d_to_glue_data(

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

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/parsers.py#L187-L188

Added lines #L187 - L188 were not covered by tests
app, pf, data_label,
flux_viewer_reference_name=flux_viewer_reference_name,
spectrum_viewer_reference_name=spectrum_viewer_reference_name,
uncert_viewer_reference_name=uncert_viewer_reference_name
)

else:
raise NotImplementedError(f'Unsupported data format: {file_obj}')

Expand Down Expand Up @@ -472,7 +505,8 @@

def _parse_ndarray(app, file_obj, data_label=None, data_type=None,
flux_viewer_reference_name=None,
uncert_viewer_reference_name=None):
uncert_viewer_reference_name=None,
meta={}):
if data_label is None:
data_label = app.return_data_label(file_obj)

Expand All @@ -486,7 +520,9 @@
if not hasattr(flux, 'unit'):
flux = flux << u.count

meta = standardize_metadata({'_orig_spatial_wcs': None})
meta.update({'_orig_spatial_wcs': None})
meta = standardize_metadata(meta)

s3d = Spectrum1D(flux=flux, meta=meta)
app.add_data(s3d, data_label)

Expand Down Expand Up @@ -531,3 +567,88 @@
else:
data_type = ''
return data_type


def _roman_3d_to_glue_data(
app, file_obj, data_label,
flux_viewer_reference_name=None,
spectrum_viewer_reference_name=None,
uncert_viewer_reference_name=None,
):
"""
Parse a Roman 3D ramp cube file (Level 1),
usually with suffix '_uncal.asdf'.
"""
def _swap_axes(x):

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L582 was not covered by tests
# swap axes per the conventions of Roman cubes
# (group axis comes first) and the default in
# Cubeviz (wavelength axis expected last)
return np.swapaxes(x, 0, -1)

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L586 was not covered by tests

# update viewer reference names for Roman ramp cubes:
# app._update_viewer_reference_name()

data = file_obj.data

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L591 was not covered by tests

if data_label is None:
data_label = app.return_data_label(file_obj)

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

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/parsers.py#L593-L594

Added lines #L593 - L594 were not covered by tests

# last axis is the group axis, first two are spatial axes:
diff_data = np.vstack([

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L597 was not covered by tests
# begin with a group of zeros, so
# that `diff_data.ndim == data.ndim`
np.zeros((1, *data[0].shape)),
np.diff(data, axis=0)
])

meta = {cubeviz_ramp_meta_flag: True}

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L604 was not covered by tests

# load the `data` cube into what's usually the "flux-viewer"
_parse_ndarray(

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L607 was not covered by tests
app,
file_obj=_swap_axes(data),
data_label=f"{data_label}[DATA]",
data_type="flux",
flux_viewer_reference_name=flux_viewer_reference_name,
meta=meta
)

# load the diff of the data cube
# into what's usually the "uncert-viewer"
_parse_ndarray(

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L618 was not covered by tests
app,
file_obj=_swap_axes(diff_data),
data_type="uncert",
data_label=f"{data_label}[DIFF]",
uncert_viewer_reference_name=uncert_viewer_reference_name,
meta=meta
)

# If the default Cubeviz viewers are still their defaults, rename them to
# names that are appropriate for the Roman ramp files that we just parsed:
if 'flux-viewer' in app.get_viewer_reference_names():
app._update_viewer_reference_name('flux-viewer', 'group-viewer')
app._update_viewer_reference_name('uncert-viewer', 'group-diff-viewer')
app._update_viewer_reference_name('spectrum-viewer', 'integration-viewer')

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

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/parsers.py#L629-L632

Added lines #L629 - L632 were not covered by tests

# the default collapse function in the profile viewer is "sum",
# but for ramp files, "median" is more useful:
viewer = app.get_viewer('integration-viewer')
viewer.state.function = 'median'

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

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/parsers.py#L636-L637

Added lines #L636 - L637 were not covered by tests

# some Cubeviz plugins aren't relevant for ramps, so remove them:
remove_tray_items = [

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L640 was not covered by tests
'g-line-list',
'specviz-line-analysis',
'cubeviz-moment-maps',
'g-gaussian-smooth'
]

for item_name in remove_tray_items:
item_names = [

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

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/parsers.py#L647-L648

Added lines #L647 - L648 were not covered by tests
tray_item['name'] for tray_item in app.state.tray_items
]

app.state.tray_items.pop(

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

View check run for this annotation

Codecov / codecov/patch

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

Added line #L652 was not covered by tests
item_names.index(item_name)
)
71 changes: 71 additions & 0 deletions jdaviz/configs/cubeviz/plugins/slice/slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
from astropy import units as u
from astropy.units import UnitsWarning
from traitlets import Bool, Int, Unicode, observe
from specutils import Spectrum1D
from glue_jupyter.bqplot.image import BqplotImageView
from glue_jupyter.bqplot.profile import BqplotProfileView

from jdaviz.configs.cubeviz.plugins.viewers import (WithSliceIndicator, WithSliceSelection,
CubevizImageView)
Expand All @@ -19,6 +22,7 @@
from jdaviz.core.registries import tray_registry
from jdaviz.core.template_mixin import PluginTemplateMixin
from jdaviz.core.user_api import PluginUserApi
from jdaviz.configs.cubeviz.plugins.parsers import cubeviz_ramp_meta_flag


__all__ = ['Slice']
Expand Down Expand Up @@ -147,6 +151,73 @@
def slice_selection_viewers(self):
return [v for v in self.app._viewer_store.values() if isinstance(v, WithSliceSelection)]

def slice_indicator(self):
return self.spectrum_viewer.slice_indicator

Check warning on line 155 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L155

Added line #L155 was not covered by tests

def _watch_viewer(self, viewer, watch=True):
if isinstance(viewer, BqplotImageView):
if watch and viewer not in self._watched_viewers:
self._watched_viewers.append(viewer)
viewer.state.add_callback('slices',

Check warning on line 161 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L158-L161

Added lines #L158 - L161 were not covered by tests
self._viewer_slices_changed)
elif not watch and viewer in self._watched_viewers:
viewer.state.remove_callback('slices',

Check warning on line 164 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L163-L164

Added lines #L163 - L164 were not covered by tests
self._viewer_slices_changed)
self._watched_viewers.remove(viewer)
elif isinstance(viewer, BqplotProfileView) and watch:
viewer_data = viewer.data()
if self._x_all is None and len(viewer.data()):
if hasattr(viewer_data, 'spectral_axis'):

Check warning on line 170 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L166-L170

Added lines #L166 - L170 were not covered by tests
# cache wavelengths so that wavelength <> slice
# conversion can be done efficiently
self._update_data(viewer_data[0].spectral_axis)

Check warning on line 173 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L173

Added line #L173 was not covered by tests
else:
sample_index = np.arange(viewer_data[0].shape[-1]) * u.one
self._update_data(sample_index)

Check warning on line 176 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L175-L176

Added lines #L175 - L176 were not covered by tests

if viewer not in self._indicator_viewers:
self._indicator_viewers.append(viewer)

Check warning on line 179 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L178-L179

Added lines #L178 - L179 were not covered by tests
# if the units (or data) change, we need to update internally
viewer.state.add_callback("reference_data",

Check warning on line 181 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L181

Added line #L181 was not covered by tests
self._update_reference_data)

def _on_data_added(self, msg):
if isinstance(msg.viewer, BqplotImageView):
if len(msg.data.shape) == 3:
self.max_value = msg.data.shape[-1] - 1 # Same as i_end in Export Plot plugin
self._watch_viewer(msg.viewer, True)
msg.viewer.state.slices = (0, 0, int(self.slice))

Check warning on line 189 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L185-L189

Added lines #L185 - L189 were not covered by tests

elif isinstance(msg.viewer, BqplotProfileView):
self._watch_viewer(msg.viewer, True)

Check warning on line 192 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L191-L192

Added lines #L191 - L192 were not covered by tests

def _update_reference_data(self, reference_data):
if reference_data is None:

Check warning on line 195 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L195

Added line #L195 was not covered by tests
return # pragma: no cover

if reference_data.meta.get(cubeviz_ramp_meta_flag, False):
sample_index = np.arange(reference_data['flux'].shape[-1]) * u.one
self._update_data(sample_index)

Check warning on line 200 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L198-L200

Added lines #L198 - L200 were not covered by tests
else:
self._update_data(reference_data.get_object(cls=Spectrum1D).spectral_axis)

Check warning on line 202 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L202

Added line #L202 was not covered by tests

def _update_data(self, x_all):
self._x_all = x_all.value

Check warning on line 205 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L205

Added line #L205 was not covered by tests

if self.wavelength == -1:
if len(x_all):

Check warning on line 208 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L207-L208

Added lines #L207 - L208 were not covered by tests
# initialize at middle of cube
self.slice = int(len(x_all)/2)

Check warning on line 210 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L210

Added line #L210 was not covered by tests
else:
# leave in the pre-init state and don't update the wavelength/slice
return

Check warning on line 213 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L213

Added line #L213 was not covered by tests

# Also update unit when data is updated
self.wavelength_unit = x_all.unit.to_string()

Check warning on line 216 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L216

Added line #L216 was not covered by tests

# force wavelength to update from the current slider value
self._on_slider_updated({'new': self.slice})

Check warning on line 219 in jdaviz/configs/cubeviz/plugins/slice/slice.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/cubeviz/plugins/slice/slice.py#L219

Added line #L219 was not covered by tests

@property
def slice_indicator_viewers(self):
return [v for v in self.app._viewer_store.values() if isinstance(v, WithSliceIndicator)]
Expand Down
18 changes: 17 additions & 1 deletion jdaviz/configs/default/plugins/model_fitting/model_fitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
from specutils.utils import QuantityModel
from traitlets import Bool, List, Unicode, observe

from jdaviz.core.events import SnackbarMessage, GlobalDisplayUnitChanged
from jdaviz.core.events import (
SnackbarMessage, GlobalDisplayUnitChanged, ViewerRenamedMessage
)
from jdaviz.core.registries import tray_registry
from jdaviz.core.template_mixin import (PluginTemplateMixin,
SelectPluginComponent,
Expand All @@ -25,6 +27,7 @@
from jdaviz.configs.default.plugins.model_fitting.initializers import (MODELS,
initialize,
get_model_parameters)
from jdaviz.configs.cubeviz.plugins.parsers import cubeviz_ramp_meta_flag

__all__ = ['ModelFitting']

Expand Down Expand Up @@ -158,6 +161,9 @@
self.hub.subscribe(self, GlobalDisplayUnitChanged,
handler=self._on_global_display_unit_changed)

self.hub.subscribe(self, ViewerRenamedMessage,
handler=self._on_viewer_renamed)

@property
def _default_spectrum_viewer_reference_name(self):
return getattr(
Expand Down Expand Up @@ -328,7 +334,12 @@
# during initial init, this can trigger before the component is initialized
return

if self.dataset.selected_obj:
if self.dataset.selected_obj.meta.get(cubeviz_ramp_meta_flag, False):
return

Check warning on line 339 in jdaviz/configs/default/plugins/model_fitting/model_fitting.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/default/plugins/model_fitting/model_fitting.py#L339

Added line #L339 was not covered by tests

selected_spec = self.dataset.selected_spectrum

if selected_spec is None:
return

Expand Down Expand Up @@ -980,3 +991,8 @@

spectrum.mask = subset_mask
return spectrum

def _on_viewer_renamed(self, msg):
if self.dataset._viewers:
if msg.old_viewer_ref in self.dataset._viewers:
self.dataset._viewers = [self._default_spectrum_viewer_reference_name]

Check warning on line 998 in jdaviz/configs/default/plugins/model_fitting/model_fitting.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/default/plugins/model_fitting/model_fitting.py#L997-L998

Added lines #L997 - L998 were not covered by tests
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,9 @@
self.disabled_msg = 'Line Analysis unavailable without spectral data'
return

if viewer_data.spectral_axis.unit == u.pix:
if not hasattr(viewer_data, 'spectral_axis'):
self.disabled_msg = 'Line Analysis plugin unavailable when viewing ramps'

Check warning on line 171 in jdaviz/configs/specviz/plugins/line_analysis/line_analysis.py

View check run for this annotation

Codecov / codecov/patch

jdaviz/configs/specviz/plugins/line_analysis/line_analysis.py#L171

Added line #L171 was not covered by tests
elif viewer_data.spectral_axis.unit == u.pix:
# disable the plugin until we can address this properly (either using the wavelength
# solution to support pixels in line-lists, or properly displaying the extracted
# 1d spectrum in wavelength-space)
Expand Down
Loading
Loading