From 0a758fd8e9285ae9954dfea94eb7d021219d6c56 Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Mon, 29 Apr 2024 14:39:55 -0400 Subject: [PATCH 1/4] backend compatibility with composite subsets as aperture --- .../spectral_extraction.py | 14 ++++ .../tests/test_spectral_extraction.py | 76 ++++++++++++++++++- jdaviz/core/template_mixin.py | 4 + 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py index 2cacd749b0..dde2ed6f38 100644 --- a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py +++ b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py @@ -288,6 +288,11 @@ def collapse_to_spectrum(self, add_data=True, **kwargs): # Boolean cube which is True outside of the aperture # (i.e., the numpy boolean mask convention) mask = np.isclose(shape_mask, 0) + + # composite subset masks are in `nddata.mask`: + if nddata.mask is not None and np.all(shape_mask == 0): + mask &= nddata.mask + else: nddata = spectral_cube.get_object(cls=NDDataArray) if uncert_cube: @@ -377,6 +382,15 @@ def get_aperture(self): statistic=None) display_unit = astropy.units.Unit(self.app._get_display_unit(self.slice_display_unit_name)) + # if subset is a composite subset, skip the other logic: + if self.aperture.is_composite: + [subset_group] = [ + subset_group for subset_group in self.app.data_collection.subset_groups + if subset_group.label == self.aperture_selected] + # mask_weights = np.logical_not(subset_group.subsets[0].to_mask()).astype(np.float32) + mask_weights = subset_group.subsets[0].to_mask().astype(np.float32) + return mask_weights + # Center is reverse coordinates center = (self.aperture.selected_spatial_region.center.y, self.aperture.selected_spatial_region.center.x) diff --git a/jdaviz/configs/cubeviz/plugins/spectral_extraction/tests/test_spectral_extraction.py b/jdaviz/configs/cubeviz/plugins/spectral_extraction/tests/test_spectral_extraction.py index 1e5f155d46..edccaa7106 100644 --- a/jdaviz/configs/cubeviz/plugins/spectral_extraction/tests/test_spectral_extraction.py +++ b/jdaviz/configs/cubeviz/plugins/spectral_extraction/tests/test_spectral_extraction.py @@ -6,8 +6,8 @@ from astropy import units as u from astropy.nddata import NDDataArray, StdDevUncertainty from astropy.utils.exceptions import AstropyUserWarning -from glue.core.roi import CircularROI -from glue.core.edit_subset_mode import ReplaceMode +from glue.core.roi import CircularROI, RectangularROI +from glue.core.edit_subset_mode import ReplaceMode, AndNotMode, NewMode from numpy.testing import assert_allclose, assert_array_equal from regions import (CirclePixelRegion, CircleAnnulusPixelRegion, EllipsePixelRegion, RectanglePixelRegion, PixCoord) @@ -447,3 +447,75 @@ def test_autoupdate_results(cubeviz_helper, spectrum1d_cube_largest): # cover to make sure the logic does not crash # new_med_flux = np.median(cubeviz_helper.get_data('extracted').flux) # assert new_med_flux > orig_med_flux + + +def test_aperture_composite_detection(cubeviz_helper, spectrum1d_cube): + cubeviz_helper.load_data(spectrum1d_cube) + flux_viewer = cubeviz_helper.app.get_viewer('flux-viewer') + subset_plugin = cubeviz_helper.plugins['Subset Tools']._obj + spec_extr_plugin = cubeviz_helper.plugins['Spectral Extraction']._obj + + # create a rectangular subset with all spaxels: + rectangle = RectangularROI(-0.5, 1.5, -0.5, 3.5) + flux_viewer.toolbar.active_tool = flux_viewer.toolbar.tools['bqplot:rectangle'] + flux_viewer.apply_roi(rectangle) + + # select subset 1, ensure it's not a composite subset: + spec_extr_plugin.aperture_selected = 'Subset 1' + assert not spec_extr_plugin.aperture.is_composite + + # now remove from this subset a circular region in the center: + flux_viewer.toolbar.active_tool = flux_viewer.toolbar.tools['bqplot:truecircle'] + subset_plugin.subset_selected = 'Subset 1' + cubeviz_helper.app.session.edit_subset_mode.mode = AndNotMode + + circle = CircularROI(0.5, 1.5, 1) + flux_viewer.apply_roi(circle) + + # now the subset is composite: + assert spec_extr_plugin.aperture.is_composite + + +def test_extraction_composite_subset(cubeviz_helper, spectrum1d_cube): + cubeviz_helper.load_data(spectrum1d_cube) + + flux_viewer = cubeviz_helper.app.get_viewer('flux-viewer') + subset_plugin = cubeviz_helper.plugins['Subset Tools']._obj + spec_extr_plugin = cubeviz_helper.plugins['Spectral Extraction']._obj + + lower_aperture = RectangularROI(-0.5, 1.5, -0.5, 0.5) + upper_aperture = RectangularROI(-0.5, 1.5, 2.5, 3.5) + + flux_viewer.toolbar.active_tool = flux_viewer.toolbar.tools['bqplot:rectangle'] + flux_viewer.apply_roi(lower_aperture) + + cubeviz_helper.app.session.edit_subset_mode.mode = NewMode + flux_viewer.apply_roi(upper_aperture) + + spec_extr_plugin.aperture_selected = 'Subset 1' + spectrum_1 = spec_extr_plugin.collapse_to_spectrum() + + spec_extr_plugin.aperture_selected = 'Subset 2' + spectrum_2 = spec_extr_plugin.collapse_to_spectrum() + + subset_plugin.subset_selected = 'Create New' + rectangle = RectangularROI(-0.5, 1.5, -0.5, 3.5) + flux_viewer.toolbar.active_tool = flux_viewer.toolbar.tools['bqplot:rectangle'] + flux_viewer.apply_roi(rectangle) + + flux_viewer.toolbar.active_tool = flux_viewer.toolbar.tools['bqplot:truecircle'] + subset_plugin.subset_selected = 'Subset 3' + cubeviz_helper.app.session.edit_subset_mode.mode = AndNotMode + circle = CircularROI(0.5, 1.5, 1.1) + flux_viewer.apply_roi(circle) + + spec_extr_plugin.aperture_selected = 'Subset 3' + + assert spec_extr_plugin.aperture.is_composite + + spectrum_3 = spec_extr_plugin.collapse_to_spectrum() + + np.testing.assert_allclose( + (spectrum_1 + spectrum_2).flux.value, + (spectrum_3).flux.value + ) diff --git a/jdaviz/core/template_mixin.py b/jdaviz/core/template_mixin.py index 7d5e826d32..749110ef81 100644 --- a/jdaviz/core/template_mixin.py +++ b/jdaviz/core/template_mixin.py @@ -2257,6 +2257,10 @@ def _set_mark_visiblities(self, visible): def _plugin_active_changed(self, *args): self._set_mark_visiblities(self.plugin.is_active) + @property + def is_composite(self): + return hasattr(self.selected_obj, '__len__') and len(self.selected_obj) > 1 + @property def image_viewers(self): return [viewer for viewer in self.app._viewer_store.values() From 288077e118608cc8c1db879d24a8d8507d9ffea7 Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Mon, 29 Apr 2024 14:45:52 -0400 Subject: [PATCH 2/4] explain removed options due to composite subset in the UI --- .../plugins/spectral_extraction/spectral_extraction.py | 1 - jdaviz/core/template_mixin.py | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py index dde2ed6f38..1851c4853d 100644 --- a/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py +++ b/jdaviz/configs/cubeviz/plugins/spectral_extraction/spectral_extraction.py @@ -387,7 +387,6 @@ def get_aperture(self): [subset_group] = [ subset_group for subset_group in self.app.data_collection.subset_groups if subset_group.label == self.aperture_selected] - # mask_weights = np.logical_not(subset_group.subsets[0].to_mask()).astype(np.float32) mask_weights = subset_group.subsets[0].to_mask().astype(np.float32) return mask_weights diff --git a/jdaviz/core/template_mixin.py b/jdaviz/core/template_mixin.py index 749110ef81..90a2284d95 100644 --- a/jdaviz/core/template_mixin.py +++ b/jdaviz/core/template_mixin.py @@ -2319,7 +2319,9 @@ def _get_mark_coords_and_validate(self, viewer=None, selected=None): # (or selected_spatial_region) will fail. if np.any([len(obj) > 1 for obj in objs]): validity = {'is_aperture': False, - 'aperture_message': 'composite subsets are not supported', + 'aperture_message': 'composite subsets only support extraction ' + 'with aperture extraction method "center," and ' + 'without wavelength dependence', 'is_composite': True} return [], [], validity From e2f690bfaeacc10146944d89657c62c5205b3b67 Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Mon, 29 Apr 2024 14:56:45 -0400 Subject: [PATCH 3/4] add changelog --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 85d68ba70d..0097e9b144 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,8 @@ Cubeviz - Enable spectral unit conversion in cubeviz. [#2758, #2803] +- Enable spectral extraction for composite subsets. [#2837] + Imviz ^^^^^ From fbbdbd62d7ff3257cb260ba9219a7db6950a1eb0 Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Wed, 1 May 2024 13:59:37 -0400 Subject: [PATCH 4/4] Revert to usual aperture_message --- jdaviz/core/template_mixin.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/jdaviz/core/template_mixin.py b/jdaviz/core/template_mixin.py index 90a2284d95..749110ef81 100644 --- a/jdaviz/core/template_mixin.py +++ b/jdaviz/core/template_mixin.py @@ -2319,9 +2319,7 @@ def _get_mark_coords_and_validate(self, viewer=None, selected=None): # (or selected_spatial_region) will fail. if np.any([len(obj) > 1 for obj in objs]): validity = {'is_aperture': False, - 'aperture_message': 'composite subsets only support extraction ' - 'with aperture extraction method "center," and ' - 'without wavelength dependence', + 'aperture_message': 'composite subsets are not supported', 'is_composite': True} return [], [], validity