diff --git a/CHANGES.rst b/CHANGES.rst index 676130399a..a6c24df5de 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -12,6 +12,8 @@ Cubeviz Imviz ^^^^^ +* Orientation plugin API now exposes create_north_up_east_left and create_north_up_east_right methods. [#3308] + Mosviz ^^^^^^ diff --git a/jdaviz/configs/imviz/plugins/orientation/orientation.py b/jdaviz/configs/imviz/plugins/orientation/orientation.py index 13b66c5152..7d32a20519 100644 --- a/jdaviz/configs/imviz/plugins/orientation/orientation.py +++ b/jdaviz/configs/imviz/plugins/orientation/orientation.py @@ -52,15 +52,21 @@ class Orientation(PluginTemplateMixin, ViewerSelectMixin): * :meth:`~jdaviz.core.template_mixin.PluginTemplateMixin.close_in_tray` * ``align_by`` (`~jdaviz.core.template_mixin.SelectPluginComponent`) * ``wcs_fast_approximation`` - * ``delete_subsets`` + * :meth:`delete_subsets` * ``viewer`` * ``orientation`` * ``rotation_angle`` * ``east_left`` - * ``add_orientation`` + * :meth:`add_orientation` + * :meth:`set_north_up_east_left` + * :meth:`set_north_up_east_right` """ template_file = __file__, "orientation.vue" + # defined as traitlet in addition to global variable above to + # allow access from UI - leave fixed + base_wcs_layer_label = Unicode(base_wcs_layer_label).tag(sync=True) + align_by_items = List().tag(sync=True) align_by_selected = Unicode().tag(sync=True) wcs_use_fallback = Bool(True).tag(sync=True) @@ -146,7 +152,8 @@ def user_api(self): expose=( 'align_by', 'link_type', 'wcs_fast_approximation', 'wcs_use_affine', 'delete_subsets', 'viewer', 'orientation', - 'rotation_angle', 'east_left', 'add_orientation' + 'rotation_angle', 'east_left', 'add_orientation', + 'set_north_up_east_left', 'set_north_up_east_right', ) ) @@ -283,6 +290,9 @@ def _on_subset_change(self, msg): self.need_clear_subsets = len(self.app.data_collection.subset_groups) > 0 def delete_subsets(self): + """ + Delete all subsets app-wide. Required before changing ``align_by``. + """ # subsets will be deleted on changing link type: for subset_group in self.app.data_collection.subset_groups: self.app.data_collection.remove_subset_group(subset_group) @@ -526,39 +536,59 @@ def _on_viewer_change(self, msg={}): if ref_data.label in self.orientation.choices: self.orientation.selected = ref_data.label - def create_north_up_east_left(self, label="North-up, East-left", set_on_create=False, - from_ui=False): - """ - Set the rotation angle and flip to achieve North up and East left - according to the reference image WCS. - """ + def _set_north_up_east_left(self, label="North-up, East-left", set_as_orientation=False, + from_ui=False): if label not in self.orientation.choices: degn = self._get_wcs_angles()[-3] self._add_orientation(rotation_angle=degn, east_left=True, - label=label, set_on_create=set_on_create, + label=label, set_on_create=set_as_orientation, from_ui=from_ui) - elif set_on_create: + elif set_as_orientation: self.orientation.selected = label - def create_north_up_east_right(self, label="North-up, East-right", set_on_create=False, - from_ui=False): + def set_north_up_east_left(self, label="North-up, East-left"): """ - Set the rotation angle and flip to achieve North up and East right - according to the reference image WCS. + Set (and create if necessary) the rotation angle and flip to achieve North up + and East left according to the reference image WCS. + + Parameters + ---------- + label : str + Data label for the orientation layer. If already exists, will be set as the + current orientation layer according to ``set_as_orientation``. Otherwise, + a new layer will be created with this label. """ + self._set_north_up_east_left(label=label, set_as_orientation=True) + + def _set_north_up_east_right(self, label="North-up, East-right", set_as_orientation=False, + from_ui=False): if label not in self.orientation.choices: degn = self._get_wcs_angles()[-3] self._add_orientation(rotation_angle=180 - degn, east_left=False, - label=label, set_on_create=set_on_create, + label=label, set_on_create=set_as_orientation, from_ui=from_ui) - elif set_on_create: + elif set_as_orientation: self.orientation.selected = label + def set_north_up_east_right(self, label="North-up, East-right"): + """ + Set (and create, if necessary) the rotation angle and flip to achieve North up + and East right according to the reference image WCS. + + Parameters + ---------- + label : str + Data label for the orientation layer. If already exists, will be set as the + current orientation layer according to ``set_as_orientation``. Otherwise, + a new layer will be created with this label. + """ + self._set_north_up_east_right(label=label, set_as_orientation=True) + def vue_select_north_up_east_left(self, *args, **kwargs): - self.create_north_up_east_left(set_on_create=True, from_ui=True) + self._set_north_up_east_left(set_as_orientation=True, from_ui=True) def vue_select_north_up_east_right(self, *args, **kwargs): - self.create_north_up_east_right(set_on_create=True, from_ui=True) + self._set_north_up_east_right(set_as_orientation=True, from_ui=True) def vue_select_default_orientation(self, *args, **kwargs): self.orientation.selected = base_wcs_layer_label diff --git a/jdaviz/configs/imviz/plugins/orientation/orientation.vue b/jdaviz/configs/imviz/plugins/orientation/orientation.vue index d272d0d9fb..4cb514d58e 100644 --- a/jdaviz/configs/imviz/plugins/orientation/orientation.vue +++ b/jdaviz/configs/imviz/plugins/orientation/orientation.vue @@ -128,18 +128,45 @@ Presets: - + mdi-image-outline + {{ api_hints_enabled ? + 'plg.orientation = \''+base_wcs_layer_label+'\'' + : + null + }} - + + {{ api_hints_enabled ? + 'plg.set_north_up_east_left()' + : + null + }} - + + {{ api_hints_enabled ? + 'plg.set_north_up_east_right()' + : + null + }} diff --git a/jdaviz/configs/imviz/tests/test_delete_data.py b/jdaviz/configs/imviz/tests/test_delete_data.py index e5f03ab9a5..11f855c9e6 100644 --- a/jdaviz/configs/imviz/tests/test_delete_data.py +++ b/jdaviz/configs/imviz/tests/test_delete_data.py @@ -87,7 +87,7 @@ def test_delete_wcs_layer_with_subset(self): lc_plugin.align_by = 'WCS' # Should automatically be applied as reference to first viewer. - lc_plugin._obj.create_north_up_east_left(set_on_create=True) + lc_plugin.set_north_up_east_left() # Create a rotated ellipse. reg = EllipsePixelRegion( diff --git a/jdaviz/configs/imviz/tests/test_footprints.py b/jdaviz/configs/imviz/tests/test_footprints.py index 29a09d92fe..e5fe4c8f47 100644 --- a/jdaviz/configs/imviz/tests/test_footprints.py +++ b/jdaviz/configs/imviz/tests/test_footprints.py @@ -240,8 +240,8 @@ def test_footprint_updates_on_rotation(imviz_helper): assert np.concatenate([marks[0].y, marks[1].y]).min() < -3 # now rotate to north-up east-left: - orientation = imviz_helper.plugins['Orientation']._obj - orientation.create_north_up_east_left(set_on_create=True) + orientation = imviz_helper.plugins['Orientation'] + orientation.set_north_up_east_left() # If all footprint orientations have been updated, the lowest # mark should still be centered low. If the footprint diff --git a/jdaviz/configs/imviz/tests/test_orientation.py b/jdaviz/configs/imviz/tests/test_orientation.py index 9049840207..6f6e9f6429 100644 --- a/jdaviz/configs/imviz/tests/test_orientation.py +++ b/jdaviz/configs/imviz/tests/test_orientation.py @@ -125,14 +125,14 @@ def test_N_up_multi_viewer(self): lc_plugin.align_by = 'WCS' # Should automatically be applied as reference to first viewer. - lc_plugin._obj.create_north_up_east_left(set_on_create=True) + lc_plugin.set_north_up_east_left() # This would set a different reference to second viewer. viewer_2 = self.imviz.create_image_viewer() self.imviz.app.add_data_to_viewer("imviz-1", "has_wcs_1[SCI,1]") lc_plugin.viewer = "imviz-1" - lc_plugin._obj.create_north_up_east_right(set_on_create=True) + lc_plugin.set_north_up_east_right() assert self.viewer.state.reference_data.label == "North-up, East-left" assert viewer_2.state.reference_data.label == "North-up, East-right" @@ -157,8 +157,8 @@ def test_custom_orientation(self): lc_plugin.viewer = "imviz-0" lc_plugin.rotation_angle = 42 # Triggers auto-label - lc_plugin._obj.add_orientation(rotation_angle=None, east_left=True, label=None, - set_on_create=True, wrt_data=None) + lc_plugin.add_orientation(rotation_angle=None, east_left=True, label=None, + set_on_create=True, wrt_data=None) assert self.viewer.state.reference_data.label == "CCW 42.00 deg (E-left)" @@ -169,7 +169,7 @@ def test_delete_orientation_multi_viewer(self): lc_plugin.align_by = 'WCS' # Should automatically be applied as reference to first viewer. - lc_plugin._obj.create_north_up_east_left(set_on_create=True) + lc_plugin.set_north_up_east_left() # This would set a different reference to second viewer. viewer_2 = self.imviz.create_image_viewer() @@ -192,7 +192,7 @@ def test_delete_orientation_with_subset(self, klass, angle, sbst_theta): lc_plugin.align_by = 'WCS' # Should automatically be applied as reference to first viewer. - lc_plugin._obj.create_north_up_east_left(set_on_create=True) + lc_plugin.set_north_up_east_left() # Create rotated shape reg = klass(center=SkyCoord(ra=337.51931488, dec=-20.83187472, unit="deg"), @@ -200,7 +200,7 @@ def test_delete_orientation_with_subset(self, klass, angle, sbst_theta): self.imviz.plugins['Subsets'].import_region(reg) # Switch to N-up E-right - lc_plugin._obj.create_north_up_east_right(set_on_create=True) + lc_plugin.set_north_up_east_right() self.imviz.app.vue_data_item_remove({"item_name": "North-up, East-left"}) @@ -234,13 +234,13 @@ def test_create_no_data(self): lc_plugin.viewer = "imviz-1" with pytest.raises(ValueError, match="Viewer must have data loaded"): - lc_plugin._obj.create_north_up_east_left(set_on_create=True) + lc_plugin.set_north_up_east_left() def test_select_no_data(self): lc_plugin = self.imviz.plugins['Orientation'] lc_plugin.align_by = 'WCS' - lc_plugin._obj.create_north_up_east_left(set_on_create=True) + lc_plugin.set_north_up_east_left() self.imviz.create_image_viewer() lc_plugin.viewer = "imviz-1"