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

Cubeviz spectral regions fixes #1046

Merged
merged 10 commits into from
Jan 31, 2022
7 changes: 7 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ API Changes
- Viewers now can access the calling Jdaviz application using
``viewer.session.jdaviz_app``. [#1051]

Cubeviz
^^^^^^^

- Subsets from the spectrum viewer are now returned as SpectralRegion objects. [#1046]

Bug Fixes
---------

Expand All @@ -34,6 +39,8 @@ Bug Fixes
Cubeviz
^^^^^^^

- Spectral region retrieval now properly handles the case of multiple subregions. [#1046]

Imviz
^^^^^

Expand Down
21 changes: 17 additions & 4 deletions jdaviz/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from glue.core.subset import Subset, RangeSubsetState, RoiSubsetState
from glue_jupyter.app import JupyterApplication
from glue_jupyter.state_traitlets_helpers import GlueState
from glue_jupyter.bqplot.profile import BqplotProfileView
from ipyvuetify import VuetifyTemplate

from jdaviz.core.config import read_configuration, get_configuration
Expand Down Expand Up @@ -617,10 +618,12 @@ def _get_all_subregions(mask, spec_axis_data):
this_type = type(value.subset_state)

# Skip spatial or spectral subsets if only the other is wanted
if subset_type == "spectral" and this_type == RoiSubsetState:
continue
elif subset_type == "spatial" and this_type == RangeSubsetState:
continue
if subset_type == "spectral" or isinstance(viewer, BqplotProfileView):
if this_type == RoiSubsetState:
continue
elif subset_type == "spatial" or not isinstance(viewer, BqplotProfileView):
if this_type == RangeSubsetState:
continue

# Range selection on a profile is currently not supported in
# the glue translation machinery for astropy regions, so we
Expand Down Expand Up @@ -648,6 +651,16 @@ def _get_all_subregions(mask, spec_axis_data):
regions[key] = subregions_in_subset
continue

temp_data = self.get_data_from_viewer(viewer_reference, value.label)
if isinstance(temp_data, Spectrum1D):
# Note that we look for mask == False here, rather than True above,
# because specutils masks are the reverse of Glue (of course)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, specutils at least follows Numpy masked array convention (where mask==True means the pixel is masked). 🤷

subregions_in_subset = _get_all_subregions(
np.where(~temp_data.mask)[0], # noqa
temp_data.spectral_axis)
regions[key] = subregions_in_subset
continue

# Get the pixel coordinate [z] of the 3D data, repeating the
# wavelength axis. This doesn't seem strictly necessary as it
# returns the same data if the pixel axis is [y] or [x]
Expand Down
28 changes: 8 additions & 20 deletions jdaviz/configs/cubeviz/plugins/moment_maps/moment_maps.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
from glue.core.message import (DataCollectionAddMessage,
DataCollectionDeleteMessage)
from traitlets import List, Unicode, Any, Bool, observe
from specutils import Spectrum1D, manipulation, SpectralRegion, analysis
from regions import RectanglePixelRegion
from specutils import Spectrum1D, manipulation, analysis

from jdaviz.core.events import SnackbarMessage
from jdaviz.core.registries import tray_registry
Expand All @@ -33,8 +32,8 @@ class MomentMap(TemplateMixin):
spectral_min = Any().tag(sync=True)
spectral_max = Any().tag(sync=True)
spectral_unit = Unicode().tag(sync=True)
spectral_subset_items = List(["None"]).tag(sync=True)
selected_subset = Unicode("None").tag(sync=True)
spectral_subset_items = List(["Entire Spectrum"]).tag(sync=True)
selected_subset = Unicode("Entire Spectrum").tag(sync=True)

# NOTE: this is currently cubeviz-specific so will need to be updated
# to be config-specific if using within other viewer configurations.
Expand Down Expand Up @@ -95,34 +94,23 @@ def _on_data_selected(self, event):
def _on_subset_selected(self, event):
# If "None" selected, reset based on bounds of selected data
self._selected_subset = self.selected_subset
if self._selected_subset == "None":
if self._selected_subset == "Entire Spectrum":
cube = self._selected_data.get_object(cls=Spectrum1D, statistic=None)
self.spectral_min = cube.spectral_axis[0].value
self.spectral_max = cube.spectral_axis[-1].value
else:
spec_sub = self._spectral_subsets[self._selected_subset]
unit = u.Unit(self.spectral_unit)
spec_reg = SpectralRegion.from_center(spec_sub.center.x * unit,
spec_sub.width * unit)
self.spectral_min = spec_reg.lower.value
self.spectral_max = spec_reg.upper.value
self.spectral_min = spec_sub.lower.value
self.spectral_max = spec_sub.upper.value

@observe("filename")
def _on_filename_changed(self, event):
self._filename = self.filename

def vue_list_subsets(self, event):
"""Populate the spectral subset selection dropdown"""
temp_subsets = self.app.get_subsets_from_viewer("spectrum-viewer")
temp_list = ["None"]
temp_dict = {}
# Attempt to filter out spatial subsets
for key, region in temp_subsets.items():
if type(region) == RectanglePixelRegion:
temp_dict[key] = region
temp_list.append(key)
self._spectral_subsets = temp_dict
self.spectral_subset_items = temp_list
self._spectral_subsets = self.app.get_subsets_from_viewer("spectrum-viewer")
self.spectral_subset_items = ["Entire Spectrum"] + sorted(self._spectral_subsets.keys())

def vue_calculate_moment(self, *args):
# Retrieve the data cube and slice out desired region, if specified
Expand Down
53 changes: 41 additions & 12 deletions jdaviz/tests/test_subsets.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import astropy.units as u
from astropy.tests.helper import assert_quantity_allclose
import numpy as np
import pytest
from glue.core import Data
from glue.core.roi import RectangularROI, XRangeROI
from glue.core.edit_subset_mode import OrMode
from numpy.testing import assert_allclose
from specutils import SpectralRegion
from regions import RectanglePixelRegion

from jdaviz.app import Application
Expand Down Expand Up @@ -61,18 +65,18 @@ def test_region_from_subset_profile(jdaviz_app, spectral_cube_wcs):

jdaviz_app.add_data_to_viewer('spectrum-viewer', 'Test 1D Flux')

jdaviz_app.get_viewer("spectrum-viewer").apply_roi(XRangeROI(1, 3.5))
jdaviz_app.get_viewer("spectrum-viewer").apply_roi(XRangeROI(5, 15.5))

subsets = jdaviz_app.get_subsets_from_viewer('spectrum-viewer', subset_type='spectral')
reg = subsets.get('Subset 1')

print(reg)

assert len(subsets) == 1
assert isinstance(reg, RectanglePixelRegion)
assert isinstance(reg, SpectralRegion)

assert_allclose(reg.center.x, 2.25)
assert_allclose(reg.center.y, 128)
assert_allclose(reg.width, 2.5)
assert_allclose(reg.height, 256)
assert_quantity_allclose(reg.lower, 5.0 * u.Hz)
assert_quantity_allclose(reg.upper, 14.0 * u.Hz)


def test_region_spectral_spatial(jdaviz_app, spectral_cube_wcs):
Expand All @@ -82,7 +86,7 @@ def test_region_spectral_spatial(jdaviz_app, spectral_cube_wcs):
jdaviz_app.add_data_to_viewer('spectrum-viewer', 'Test Flux')
jdaviz_app.add_data_to_viewer('flux-viewer', 'Test Flux')

jdaviz_app.get_viewer("spectrum-viewer").apply_roi(XRangeROI(1, 3.5))
jdaviz_app.get_viewer("spectrum-viewer").apply_roi(XRangeROI(5, 15.5))

flux_viewer = jdaviz_app.get_viewer("flux-viewer")
# We set the active tool here to trigger a reset of the Subset state to "Create new"
Expand All @@ -92,13 +96,13 @@ def test_region_spectral_spatial(jdaviz_app, spectral_cube_wcs):
subsets = jdaviz_app.get_subsets_from_viewer('spectrum-viewer', subset_type='spectral')
reg = subsets.get('Subset 1')

print(reg)

assert len(subsets) == 1
assert isinstance(reg, RectanglePixelRegion)
assert isinstance(reg, SpectralRegion)

assert_allclose(reg.center.x, 2.25)
assert_allclose(reg.center.y, 128)
assert_allclose(reg.width, 2.5)
assert_allclose(reg.height, 256)
assert_quantity_allclose(reg.lower, 5.0 * u.Hz)
assert_quantity_allclose(reg.upper, 14 * u.Hz)

subsets = jdaviz_app.get_subsets_from_viewer('flux-viewer', subset_type='spatial')
reg = subsets.get('Subset 2')
Expand All @@ -110,3 +114,28 @@ def test_region_spectral_spatial(jdaviz_app, spectral_cube_wcs):
assert_allclose(reg.center.y, 1.55)
assert_allclose(reg.width, 2.5)
assert_allclose(reg.height, 3.5)


def test_disjoint_spectral_subset(jdaviz_app, spectral_cube_wcs):
data = Data(flux=np.ones((256, 128, 128)), label='Test Flux', coords=spectral_cube_wcs)
jdaviz_app.data_collection.append(data)

jdaviz_app.add_data_to_viewer('spectrum-viewer', 'Test Flux')
jdaviz_app.add_data_to_viewer('flux-viewer', 'Test Flux')

spec_viewer = jdaviz_app.get_viewer("spectrum-viewer")
spec_viewer.apply_roi(XRangeROI(5, 15.5))

# Add second region to Subset 1
jdaviz_app.session.edit_subset_mode.mode = OrMode
spec_viewer.apply_roi(XRangeROI(30, 35))

subsets = jdaviz_app.get_subsets_from_viewer('spectrum-viewer')
reg = subsets.get('Subset 1')

assert len(reg) == 2
assert isinstance(reg, SpectralRegion)
assert_quantity_allclose(reg[0].lower, 5.0*u.Hz)
assert_quantity_allclose(reg[0].upper, 15.0*u.Hz)
assert_quantity_allclose(reg[1].lower, 30.0*u.Hz)
assert_quantity_allclose(reg[1].upper, 34.0*u.Hz)
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ install_requires =
ipygoldenlayout>=0.3.0
voila>=0.2.4
pyyaml>=5.4.1
specutils>=1.5.0
specutils>=1.6.0
glue-astronomy>=0.3.2
click>=7.1.2
spectral-cube>=0.5
Expand Down