Skip to content

Commit

Permalink
exclude WCS-only layer subsets from plot options (#2699)
Browse files Browse the repository at this point in the history
* exclude WCS-only layer subsets from plot options
* add to image rotation changelog entry
  • Loading branch information
kecnry authored Feb 14, 2024
1 parent c8c45db commit bf46c9b
Show file tree
Hide file tree
Showing 10 changed files with 58 additions and 39 deletions.
2 changes: 1 addition & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Imviz
^^^^^

- There is now option for image rotation in Orientation (was Links Control) plugin.
This feature requires WCS linking. [#2179, #2673]
This feature requires WCS linking. [#2179, #2673, #2699]

Mosviz
^^^^^^
Expand Down
11 changes: 5 additions & 6 deletions jdaviz/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
data_parser_registry)
from jdaviz.core.tools import ICON_DIR
from jdaviz.utils import (SnackbarQueue, alpha_index, data_has_valid_wcs, layer_is_table_data,
MultiMaskSubsetState)
MultiMaskSubsetState, _wcs_only_label)

__all__ = ['Application', 'ALL_JDAVIZ_CONFIGS']

Expand Down Expand Up @@ -306,7 +306,6 @@ def __init__(self, configuration=None, *args, **kwargs):
self.auto_link = kwargs.pop('auto_link', True)

# Imviz linking
self._wcs_only_label = "_WCS_ONLY"
self._link_type = 'pixels'
if self.config == "imviz":
self._wcs_use_affine = None
Expand Down Expand Up @@ -470,7 +469,7 @@ def _color_to_level(color):
def _on_layers_changed(self, msg):
if hasattr(msg, 'data'):
layer_name = msg.data.label
is_wcs_only = msg.data.meta.get(self._wcs_only_label, False)
is_wcs_only = msg.data.meta.get(_wcs_only_label, False)
elif hasattr(msg, 'subset'):
layer_name = msg.subset.label
is_wcs_only = False
Expand Down Expand Up @@ -1947,7 +1946,7 @@ def set_data_visibility(self, viewer_reference, data_label, visible=True, replac

# set visibility state of all applicable layers
for layer in viewer.layers:
layer_is_wcs_only = getattr(layer.layer, 'meta', {}).get(self._wcs_only_label, False)
layer_is_wcs_only = getattr(layer.layer, 'meta', {}).get(_wcs_only_label, False)
if layer.layer.data.label == data_label:
if layer_is_wcs_only:
layer.visible = False
Expand Down Expand Up @@ -1976,7 +1975,7 @@ def set_data_visibility(self, viewer_reference, data_label, visible=True, replac

# remove WCS-only data from selected items, add to wcs_only_layers:
for layer in viewer.layers:
layer_is_wcs_only = getattr(layer.layer, 'meta', {}).get(self._wcs_only_label, False)
layer_is_wcs_only = getattr(layer.layer, 'meta', {}).get(_wcs_only_label, False)
if layer.layer.data.label == data_label and layer_is_wcs_only:
layer.visible = False
if data_label not in viewer.state.wcs_only_layers:
Expand Down Expand Up @@ -2100,7 +2099,7 @@ def _on_data_deleted(self, msg):
def _create_data_item(self, data):
ndims = len(data.shape)
wcsaxes = data.meta.get('WCSAXES', None)
wcs_only = data.meta.get(self._wcs_only_label, False)
wcs_only = data.meta.get(_wcs_only_label, False)
if wcsaxes is None:
# then we'll need to determine type another way, we want to avoid
# this when we can though since its not as cheap
Expand Down
8 changes: 3 additions & 5 deletions jdaviz/configs/default/plugins/plot_options/plot_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
from jdaviz.core.user_api import PluginUserApi
from jdaviz.core.tools import ICON_DIR
from jdaviz.core.custom_traitlets import IntHandleEmpty
from jdaviz.utils import is_not_wcs_only


from scipy.interpolate import PchipInterpolator

Expand Down Expand Up @@ -388,10 +390,6 @@ def __init__(self, *args, **kwargs):
self.layer = LayerSelect(self, 'layer_items', 'layer_selected',
'viewer_selected', 'layer_multiselect')

def is_not_wcs_only(layer):
# exclude WCS-only layers from the layer choices:
return not getattr(layer.layer, 'meta', {}).get(self.app._wcs_only_label, False)

self.layer.filters += [is_not_wcs_only]

self.swatches_palette = [
Expand Down Expand Up @@ -427,7 +425,7 @@ def not_image_or_spatial_subset(state):
return not is_image(state) and not is_spatial_subset(state)

def is_spatial_subset(state):
return isinstance(state, ImageSubsetLayerState)
return isinstance(state, ImageSubsetLayerState) and is_not_wcs_only(state.layer)

def is_not_subset(state):
return not is_spatial_subset(state)
Expand Down
4 changes: 2 additions & 2 deletions jdaviz/configs/default/plugins/viewers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from jdaviz.core.astrowidgets_api import AstrowidgetsImageViewerMixin
from jdaviz.core.registries import viewer_registry
from jdaviz.core.user_api import ViewerUserApi
from jdaviz.utils import ColorCycler, get_subset_type
from jdaviz.utils import ColorCycler, get_subset_type, _wcs_only_label

__all__ = ['JdavizViewerMixin']

Expand Down Expand Up @@ -215,7 +215,7 @@ def _get_layer_info(layer):
for layer in self.state.layers[::-1]:
layer_is_wcs_only = (
hasattr(layer.layer, 'meta') and
layer.layer.meta.get(self.jdaviz_app._wcs_only_label, False)
layer.layer.meta.get(_wcs_only_label, False)
)
if layer.visible and not layer_is_wcs_only:
prefix_icon, suffix = _get_layer_info(layer)
Expand Down
12 changes: 5 additions & 7 deletions jdaviz/configs/imviz/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from jdaviz.configs.imviz.wcs_utils import (
_get_rotated_nddata_from_label, get_compass_info
)
from jdaviz.utils import data_has_valid_wcs
from jdaviz.utils import data_has_valid_wcs, _wcs_only_label

__all__ = ['Imviz']

Expand Down Expand Up @@ -191,7 +191,7 @@ def load_data(self, data, data_label=None, show_in_viewer=True, **kwargs):
if label not in prev_data_labels:
applied_labels.append(label)
applied_visible.append(True)
layer_is_wcs_only.append(data.meta.get(self.app._wcs_only_label, False))
layer_is_wcs_only.append(data.meta.get(_wcs_only_label, False))
layer_has_wcs.append(data_has_valid_wcs(data))

if show_in_viewer is True:
Expand Down Expand Up @@ -404,14 +404,12 @@ def layer_is_2d(layer):
return isinstance(layer, BaseData) and layer.ndim == 2


# NOTE: Sync with app._wcs_only_label as needed.
def layer_is_image_data(layer):
return layer_is_2d(layer) and not layer.meta.get("_WCS_ONLY", False)
return layer_is_2d(layer) and not layer.meta.get(_wcs_only_label, False)


# NOTE: Sync with app._wcs_only_label as needed.
def layer_is_wcs_only(layer):
return layer_is_2d(layer) and layer.meta.get("_WCS_ONLY", False)
return layer_is_2d(layer) and layer.meta.get(_wcs_only_label, False)


def get_wcs_only_layer_labels(app):
Expand Down Expand Up @@ -549,7 +547,7 @@ def link_image_data(app, link_type='pixels', wcs_fallback_scheme=None, wcs_use_a
data_already_linked = []
if link_type == app._link_type and wcs_use_affine == app._wcs_use_affine:
for link in app.data_collection.external_links:
if link.data1.label != app._wcs_only_label:
if link.data1.label != _wcs_only_label:
data_already_linked.append(link.data2)
else:
for viewer in app._viewer_store.values():
Expand Down
7 changes: 4 additions & 3 deletions jdaviz/configs/imviz/plugins/orientation/orientation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
PluginTemplateMixin, SelectPluginComponent, LayerSelect, ViewerSelectMixin, AutoTextField
)
from jdaviz.core.user_api import PluginUserApi
from jdaviz.utils import _wcs_only_label

__all__ = ['Orientation']

Expand Down Expand Up @@ -230,7 +231,7 @@ def _update_link(self, msg={}):
data_in_viewer = self.app.get_viewer(viewer_selected.reference).data()

for data in self.app.data_collection:
is_wcs_only = data.meta.get(self.app._wcs_only_label, False)
is_wcs_only = data.meta.get(_wcs_only_label, False)
has_wcs = hasattr(data.coords, 'pixel_to_world')
if not is_wcs_only:
if data in data_in_viewer and wcs_linked and not has_wcs:
Expand Down Expand Up @@ -395,11 +396,11 @@ def _send_wcs_layers_to_all_viewers(self, *args, **kwargs):

def _on_data_add_to_viewer(self, msg):
all_wcs_only_layers = all(
layer.layer.meta.get(self.app._wcs_only_label)
layer.layer.meta.get(_wcs_only_label)
for layer in self.viewer.selected_obj.layers
if hasattr(layer.layer, "meta")
)
if all_wcs_only_layers and msg.data.meta.get(self.app._wcs_only_label, False):
if all_wcs_only_layers and msg.data.meta.get(_wcs_only_label, False):
# on adding first data layer, reset the limits:
self.viewer.selected_obj.state.reset_limits()

Expand Down
6 changes: 3 additions & 3 deletions jdaviz/configs/imviz/plugins/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from jdaviz.core.registries import data_parser_registry
from jdaviz.core.events import SnackbarMessage
from jdaviz.utils import standardize_metadata, PRIHDR_KEY
from jdaviz.utils import standardize_metadata, PRIHDR_KEY, _wcs_only_label

try:
from roman_datamodels import datamodels as rdd
Expand Down Expand Up @@ -146,7 +146,7 @@ def get_image_data_iterator(app, file_obj, data_label, ext=None):
data_iter = _hdu_to_glue_data(file_obj, data_label)

elif isinstance(file_obj, NDData):
if file_obj.meta.get(app._wcs_only_label, False):
if file_obj.meta.get(_wcs_only_label, False):
data_iter = _wcsonly_to_glue_data(file_obj, data_label)
else:
data_iter = _nddata_to_glue_data(file_obj, data_label)
Expand Down Expand Up @@ -184,7 +184,7 @@ def _parse_image(app, file_obj, data_label, ext=None):
# for outside_*_bounding_box should also be updated.
data.coords._orig_bounding_box = data.coords.bounding_box
data.coords.bounding_box = None
if not data.meta.get(app._wcs_only_label, False):
if not data.meta.get(_wcs_only_label, False):
data_label = app.return_data_label(data_label, alt_name="image_data")
app.add_data(data, data_label)

Expand Down
3 changes: 2 additions & 1 deletion jdaviz/configs/imviz/wcs_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from gwcs.wcs import WCS as GWCS

from matplotlib.patches import Polygon
from jdaviz.utils import _wcs_only_label

__all__ = ['get_compass_info', 'draw_compass_mpl']

Expand Down Expand Up @@ -530,7 +531,7 @@ def _get_rotated_nddata_from_label(
data.coords,
rotation_angle,
refdata_shape,
wcs_only_key=app._wcs_only_label,
wcs_only_key=_wcs_only_label,
data=data,
cdelt_signs=cdelt_signs
)
Expand Down
24 changes: 13 additions & 11 deletions jdaviz/core/template_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
from jdaviz.core.region_translators import regions2roi, _get_region_from_spatial_subset
from jdaviz.core.user_api import UserApiWrapper, PluginUserApi
from jdaviz.style_registry import PopoutStyleWrapper
from jdaviz.utils import get_subset_type
from jdaviz.utils import get_subset_type, is_wcs_only, is_not_wcs_only, _wcs_only_label


__all__ = ['show_widget', 'TemplateMixin', 'PluginTemplateMixin',
Expand Down Expand Up @@ -1343,7 +1343,7 @@ def _layer_to_dict(self, layer_label):
visibilities = []
for viewer in self.viewer_objs:
for layer in viewer.layers:
if layer.layer.label == layer_label:
if layer.layer.label == layer_label and is_not_wcs_only(layer.layer):
if is_subset is None:
is_subset = ((hasattr(layer, 'state') and hasattr(layer.state, 'subset_state')) or # noqa
(hasattr(layer, 'layer') and hasattr(layer.layer, 'subset_state'))) # noqa
Expand Down Expand Up @@ -1384,6 +1384,8 @@ def _on_viewer_selected_changed(self, msg=None):
# we call _update_layer_items in the PlotOptionsSyncState for color_mode
# old_viewer.state.remove_callback('color_mode', self._update_layer_items)
for layer in old_viewer.state.layers:
if is_wcs_only(layer.layer):
continue
layer.remove_callback('color', self._update_layer_items)
if hasattr(layer, 'cmap'):
layer.remove_callback('cmap', self._update_layer_items)
Expand All @@ -1400,6 +1402,8 @@ def _on_viewer_selected_changed(self, msg=None):
# we call _update_layer_items in the PlotOptionsSyncState for color_mode
# new_viewer.state.add_callback('color_mode', self._update_layer_items)
for layer in new_viewer.state.layers:
if is_wcs_only(layer.layer):
continue
layer.add_callback('color', self._update_layer_items)
if hasattr(layer, 'cmap'):
layer.add_callback('cmap', self._update_layer_items)
Expand All @@ -1413,11 +1417,9 @@ def _on_subset_created(self, msg=None):
viewer = self.viewer if isinstance(self.viewer, list) else [self.viewer]
for current_viewer in viewer:
for layer in self._get_viewer(current_viewer).state.layers:
if layer.layer.label == new_subset_label:
# Is it ok if only one subset layer has this callback?
if layer.layer.label == new_subset_label and is_not_wcs_only(layer.layer):
layer.add_callback('color', self._update_layer_items)
layer.add_callback('visible', self._update_layer_items)
break
# TODO: Add ability to add new item to self.items instead of recompiling
self._update_layer_items({'source': 'subset_added'})

Expand All @@ -1429,6 +1431,8 @@ def _on_data_added(self, msg=None):
for current_viewer in viewer:
for layer in self._get_viewer(current_viewer).state.layers:
if layer.layer.label == new_data_label and not hasattr(layer.layer, 'subset_state'):
if is_wcs_only(layer.layer):
continue
# Add a callback to the layer's color attribute to call
# _on_layers_changed whenever the color changes
# TODO: find out if this conflicts with another color change event
Expand All @@ -1454,7 +1458,7 @@ def _update_layer_items(self, msg={}):
all_layers = [
layer for viewer in self.viewer_objs
for layer in getattr(viewer, 'layers', [])
if self._is_valid_item(layer)
if self._is_valid_item(layer.layer)
]

# remove duplicates - we'll loop back through all selected viewers to get a list of colors
Expand Down Expand Up @@ -1491,8 +1495,6 @@ def update_wcs_only_filter(self, wcs_only):
`True` will filter only the WCS-only layers, `False` will
give the non-WCS-only layers.
"""
def is_wcs_only(layer):
return getattr(layer.layer, 'meta', {}).get(self.app._wcs_only_label, False)

filter_names = [getattr(filt, '__name__', '') for filt in self.filters]

Expand Down Expand Up @@ -1520,7 +1522,7 @@ def selected_obj(self):
viewers = [self._get_viewer(viewer_name) for viewer_name in viewer_names]

layers = [[layer for layer in viewer.layers
if layer.layer.label in selected]
if layer.layer.label in selected and self._is_valid_item(layer.layer)]
for viewer in viewers]

if not self.is_multiselect and len(layers) == 1:
Expand Down Expand Up @@ -2961,7 +2963,7 @@ def is_cube(data):
return len(data.shape) == 3

def is_not_wcs_only(data):
return not data.meta.get(self.app._wcs_only_label, False)
return not data.meta.get(_wcs_only_label, False)

return super()._is_valid_item(data, locals())

Expand Down Expand Up @@ -3774,7 +3776,7 @@ def _on_glue_value_changed(self, value):
value = value.name
elif self._glue_name in GLUE_STATES_WITH_HELPERS:
value = str(value)
elif isinstance(self.value, (int, float)) and self._glue_name != 'percentile':
elif self._glue_name != 'percentile' and isinstance(self.value, (int, float)):
# glue might pass us ints for float or vice versa, but our traitlets care
# so let's cast to the type expected by the traitlet to avoid having to
# use Any traitlets for all of these. We skip percentile as that needs
Expand Down
20 changes: 20 additions & 0 deletions jdaviz/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,26 @@ def layer_is_table_data(layer):
return isinstance(layer, BaseData) and layer.ndim == 1


_wcs_only_label = "_WCS_ONLY"


def is_wcs_only(layer):
# exclude WCS-only layers from the layer choices:
if hasattr(layer, 'layer'):
state = layer.layer
elif hasattr(layer, 'data'):
state = layer.data
elif hasattr(layer, 'meta'):
state = layer
else:
raise NotImplementedError
return getattr(state, 'meta', {}).get(_wcs_only_label, False)


def is_not_wcs_only(layer):
return not is_wcs_only(layer)


def standardize_metadata(metadata):
"""Standardize given metadata so it can be viewed in
Metadata Viewer plugin. The input can be plain
Expand Down

0 comments on commit bf46c9b

Please sign in to comment.