diff --git a/CHANGES.rst b/CHANGES.rst index 9ae34fee26..c1b31fe284 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -29,7 +29,8 @@ API Changes ----------- - Removed API access to plugins that have passed the deprecation period: Links Control, Canvas Rotation, Export Plot. [#3270] -- Subset Tools plugin now exposes the ``subset``, ``combination_mode``, ``get_center``, and ``set_center`` in the user API. [#3293, #3304] +- Subset Tools plugin now exposes the ``subset``, ``combination_mode``, ``recenter_dataset``, + ``recenter``, ``get_center``, and ``set_center`` in the user API. [#3293, #3304, #3325] - Metadata plugin: ``metadata_plugin.metadata`` API has been deprecated; use ``metadata_plugin.meta`` instead, which will return a Python dictionary instead of diff --git a/jdaviz/configs/default/plugins/subset_tools/subset_tools.py b/jdaviz/configs/default/plugins/subset_tools/subset_tools.py index d536d1a5cf..081f7ffd77 100644 --- a/jdaviz/configs/default/plugins/subset_tools/subset_tools.py +++ b/jdaviz/configs/default/plugins/subset_tools/subset_tools.py @@ -28,7 +28,7 @@ from jdaviz.core.region_translators import regions2roi, aperture2regions from jdaviz.core.events import SnackbarMessage, GlobalDisplayUnitChanged, LinkUpdatedMessage from jdaviz.core.registries import tray_registry -from jdaviz.core.template_mixin import (PluginTemplateMixin, DatasetSelectMixin, +from jdaviz.core.template_mixin import (PluginTemplateMixin, DatasetSelect, SubsetSelect, SelectPluginComponent) from jdaviz.core.tools import ICON_DIR from jdaviz.core.user_api import PluginUserApi @@ -63,7 +63,7 @@ @tray_registry('g-subset-tools', label="Subset Tools") -class SubsetTools(PluginTemplateMixin, DatasetSelectMixin): +class SubsetTools(PluginTemplateMixin): """ See the :ref:`Subset Tools ` for more details. @@ -77,6 +77,9 @@ class SubsetTools(PluginTemplateMixin, DatasetSelectMixin): Manages subset selection and creation * ``combination_mode`` (:class:`~jdaviz.core.template_mixin.SelectPluginComponent`): Allows selection of combination modes for subsets + * ``recenter_dataset`` (:class:`~jdaviz.core.template_mixin.DatasetSelect`): + Data used for recentering. + * :meth:`recenter` * :meth:`get_center` * :meth:`set_center` * :meth:`import_region` @@ -93,6 +96,9 @@ class SubsetTools(PluginTemplateMixin, DatasetSelectMixin): glue_state_types = List([]).tag(sync=True) has_subset_details = Bool(False).tag(sync=True) + recenter_dataset_items = List().tag(sync=True) + recenter_dataset_selected = Unicode().tag(sync=True) + subplugins_opened = Any().tag(sync=True) multiselect = Bool(False).tag(sync=True) # multiselect only for subset @@ -148,6 +154,10 @@ def __init__(self, *args, **kwargs): align_by = getattr(self.app, '_align_by', None) self.display_sky_coordinates = (align_by == 'wcs' and not self.multiselect) + self.recenter_dataset = DatasetSelect(self, 'recenter_dataset_items', + 'recenter_dataset_selected', + multiselect=None) + self.combination_mode = SelectPluginComponent(self, items='combination_items', selected='combination_selected', @@ -155,7 +165,10 @@ def __init__(self, *args, **kwargs): @property def user_api(self): - expose = ['subset', 'combination_mode', 'get_center', 'set_center', 'import_region'] + expose = ['subset', 'combination_mode', + 'recenter_dataset', 'recenter', + 'get_center', 'set_center', + 'import_region'] return PluginUserApi(self, expose) def _on_link_update(self, *args): @@ -556,7 +569,11 @@ def _check_input(self): return status, reason - def vue_recenter_subset(self, *args): + def recenter(self): + """ + Recenter the selected subset on the centroid of the region of the current subset + in the selected data layer. + """ # Composite region cannot be centered. This only works for Imviz. if not self.is_centerable or self.config != 'imviz': # no-op raise NotImplementedError( @@ -570,7 +587,7 @@ def _do_recentering(subset, subset_state): try: reg = _get_region_from_spatial_subset(self, subset_state) aperture = regions2aperture(reg) - data = self.dataset.selected_dc_item + data = self.recenter_dataset.selected_dc_item comp = data.get_component(data.main_components[0]) comp_data = comp.data phot_aperstats = ApertureStats(comp_data, aperture, wcs=data.coords) @@ -613,6 +630,9 @@ def _do_recentering(subset, subset_state): f"composite subset {sub}", color='error', sender=self)) + def vue_recenter_subset(self, *args): + self.recenter() + def _get_subset_state(self, subset_name=None): if self.multiselect and not subset_name: raise ValueError("Please include subset_name in when in multiselect mode") diff --git a/jdaviz/configs/default/plugins/subset_tools/subset_tools.vue b/jdaviz/configs/default/plugins/subset_tools/subset_tools.vue index 00c70a694c..7055a0b45a 100644 --- a/jdaviz/configs/default/plugins/subset_tools/subset_tools.vue +++ b/jdaviz/configs/default/plugins/subset_tools/subset_tools.vue @@ -1,10 +1,18 @@ + + diff --git a/jdaviz/configs/imviz/tests/test_subset_centroid.py b/jdaviz/configs/imviz/tests/test_subset_centroid.py index 7221986520..4cbd5e9f0e 100644 --- a/jdaviz/configs/imviz/tests/test_subset_centroid.py +++ b/jdaviz/configs/imviz/tests/test_subset_centroid.py @@ -9,26 +9,26 @@ class TestImvizSpatialSubsetCentroidPixelLinked(BaseImviz_WCS_GWCS): def test_centroiding_pixel(self): reg = CirclePixelRegion(PixCoord(2, 2), 3) - plg = self.imviz.plugins['Subset Tools']._obj + plg = self.imviz.plugins['Subset Tools'] plg.import_region(reg) - plg.subset_selected = 'Subset 1' + plg.subset = 'Subset 1' # Since they are linked by pixels, the bright corner pixel aligns # and nothing should change. for data_label in ('fits_wcs[DATA]', 'gwcs[DATA]'): - plg.dataset_selected = data_label + plg.recenter_dataset = data_label plg.set_center((2, 2), update=True) # Move the Subset back first. - plg.vue_recenter_subset() + plg.recenter() # Calculate and move to centroid. for key in ("X Center (pixels)", "Y Center (pixels)"): - assert plg._get_value_from_subset_definition(0, key, "value") == -1 - assert plg._get_value_from_subset_definition(0, key, "orig") == -1 + assert plg._obj._get_value_from_subset_definition(0, key, "value") == -1 + assert plg._obj._get_value_from_subset_definition(0, key, "orig") == -1 # Radius will not be touched. for key in ("value", "orig"): - assert plg._get_value_from_subset_definition(0, "Radius (pixels)", key) == 3 + assert plg._obj._get_value_from_subset_definition(0, "Radius (pixels)", key) == 3 assert plg.get_center() == (-1, -1) @@ -40,34 +40,34 @@ def test_centroiding_wcs(self): self.imviz.link_data(align_by='wcs') reg = CirclePixelRegion(PixCoord(2, 2), 3).to_sky(self.wcs_1) - plg = self.imviz.plugins['Subset Tools']._obj + plg = self.imviz.plugins['Subset Tools'] plg.import_region(reg) - plg.subset_selected = 'Subset 1' - plg.dataset_selected = 'fits_wcs[DATA]' - plg.vue_recenter_subset() + plg.subset = 'Subset 1' + plg.recenter_dataset = 'fits_wcs[DATA]' + plg.recenter() # Pixel value is now w.r.t. fake WCS layer, not the selected data. for key in ("value", "orig"): # subset definition is now in sky coordinates. get RA and Dec and convert back to pixel # to compare with expected recentered position. - ra = plg._get_value_from_subset_definition(0, "RA Center (degrees)", key) * u.deg - dec = plg._get_value_from_subset_definition(0, "Dec Center (degrees)", key) * u.deg + ra = plg._obj._get_value_from_subset_definition(0, "RA Center (degrees)", key) * u.deg + dec = plg._obj._get_value_from_subset_definition(0, "Dec Center (degrees)", key) * u.deg x, y = SkyCoord(ra, dec).to_pixel(self.wcs_1) assert_allclose((x, y), -1) # GWCS does not extrapolate and this Subset is out of bounds, # so will get NaNs and enter the exception handling logic. - plg.dataset_selected = 'gwcs[DATA]' + plg.recenter_dataset = 'gwcs[DATA]' plg.set_center((2.6836, 1.6332), update=True) # Move the Subset back first. - plg.vue_recenter_subset() + plg.recenter() subsets = self.imviz.app.get_subsets(include_sky_region=True) subsets_sky = subsets['Subset 1'][0]['sky_region'] subsets_pix = subsets['Subset 1'][0]['region'] assert_allclose((subsets_pix.center.x, subsets_pix.center.y), (2.6836, 1.6332)) for key in ("value", "orig"): - ra = plg._get_value_from_subset_definition(0, "RA Center (degrees)", key) - dec = plg._get_value_from_subset_definition(0, "Dec Center (degrees)", key) + ra = plg._obj._get_value_from_subset_definition(0, "RA Center (degrees)", key) + dec = plg._obj._get_value_from_subset_definition(0, "Dec Center (degrees)", key) # make sure what is in subset_definitions matches what is returned by get_subsets assert_allclose((ra, dec), (subsets_sky.center.ra.deg, subsets_sky.center.dec.deg)) @@ -77,8 +77,8 @@ def test_centroiding_wcs(self): # vue_update_subset is called. plg.set_center((2, 2), update=False) for key in ("value", "orig"): - ra = plg._get_value_from_subset_definition(0, "RA Center (degrees)", key) - dec = plg._get_value_from_subset_definition(0, "Dec Center (degrees)", key) + ra = plg._obj._get_value_from_subset_definition(0, "RA Center (degrees)", key) + dec = plg._obj._get_value_from_subset_definition(0, "Dec Center (degrees)", key) # here 'ra' and 'dec' should remain unchanged from when they were defined, since # vue_update_subset hasn't run assert_allclose((ra, dec), (subsets_sky.center.ra.deg, subsets_sky.center.dec.deg)) diff --git a/jdaviz/tests/test_subsets.py b/jdaviz/tests/test_subsets.py index d0afb98f46..b00dd5f6be 100644 --- a/jdaviz/tests/test_subsets.py +++ b/jdaviz/tests/test_subsets.py @@ -36,7 +36,7 @@ def test_region_from_subset_2d(cubeviz_helper): assert_allclose(reg.height, 6.6) assert_allclose(reg.angle.value, 0) - assert subset_plugin._obj.subset_selected == "Subset 1" + assert subset_plugin.subset == "Subset 1" assert subset_plugin._obj.subset_types == ["EllipticalROI"] assert subset_plugin._obj.is_centerable for key in ("orig", "value"): @@ -52,7 +52,7 @@ def test_region_from_subset_2d(cubeviz_helper): # Recenter GUI should not be exposed, but API call would raise exception. with pytest.raises(NotImplementedError, match='Cannot recenter'): - subset_plugin._obj.vue_recenter_subset() + subset_plugin.recenter() def test_region_from_subset_3d(cubeviz_helper): @@ -430,12 +430,12 @@ def test_recenter_linked_by_wcs(imviz_helper): RectanglePixelRegion(center=PixCoord(x=229, y=152), width=17, height=7).to_sky(w)) subset_plugin = imviz_helper.plugins['Subset Tools'] - subset_plugin._obj.subset_selected = "Subset 1" - subset_plugin._obj.dataset_selected = "gauss100_fits_wcs_block_reduced[PRIMARY,1]" + subset_plugin.subset = "Subset 1" + subset_plugin.recenter_dataset = "gauss100_fits_wcs_block_reduced[PRIMARY,1]" # Do it a few times to converge. for _ in range(5): - subset_plugin._obj.vue_recenter_subset() + subset_plugin.recenter() # If handled correctly, it won't change much. # But if not, it move down by 7 pix or so (229.05, 145.92) and fails the test. @@ -453,7 +453,7 @@ def test_recenter_linked_by_wcs(imviz_helper): # Do it a few times to converge. for _ in range(5): - subset_plugin._obj.vue_recenter_subset() + subset_plugin._obj.recenter() xy = imviz_helper.default_viewer._obj._get_real_xy( imviz_helper.app.data_collection[0], *subset_plugin.get_center("Subset 2"))[:2]