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

Image rotation UI/UX updates #23

Merged
merged 11 commits into from
Nov 27, 2023
Merged
4 changes: 2 additions & 2 deletions docs/imviz/displayimages.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Pan/Zoom and Linked Pan/Zoom
Linked Pan/Zoom is an Imviz-specific feature enabled only when there are multiple viewers that
allows the user to pan and zoom images in multiple different viewers simultaneously. This works by matching images
based on the way they are linked together. Images are linked by pixels on load time,
but you can re-link them via WCS using :ref:`imviz-link-control`.
but you can re-link them via WCS using :ref:`imviz-orientation`.

Single-viewer Pan/Zoom is also available and is used in a similar way as in
other Jdaviz tools. To access this option when there are multiple viewers, right-click on the
Expand Down Expand Up @@ -216,7 +216,7 @@ section above in the same way you would with the other subset selection
tools.

When you have multiple images loaded and linked by WCS
(see :ref:`imviz-link-control`), the region defined is with respect to
(see :ref:`imviz-orientation`), the region defined is with respect to
the reference image, which might not be the image you are viewing.

.. warning::
Expand Down
12 changes: 8 additions & 4 deletions docs/imviz/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,16 @@ To export the table into the notebook via the API, call
:meth:`~jdaviz.core.template_mixin.TableMixin.export_table`
(see :ref:`plugin-apis`).

.. _imviz-link-control:
.. _imviz-orientation:

Link Control
============
Orientation
===========

.. note::

This plugin was previous called "Links Control"

This plugin is used to re-link images by pixels or WCS using
This plugin is used to align image layers by pixels or sky (WCS) using
:func:`~jdaviz.configs.imviz.helper.link_image_data`.
All images are automatically linked by pixels on load but you can use
it to re-link by pixels or WCS as needed.
Expand Down
51 changes: 30 additions & 21 deletions jdaviz/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,9 @@ class ApplicationState(State):

icons = DictCallbackProperty({
'radialtocheck': read_icon(os.path.join(ICON_DIR, 'radialtocheck.svg'), 'svg+xml'),
'checktoradial': read_icon(os.path.join(ICON_DIR, 'checktoradial.svg'), 'svg+xml')
'checktoradial': read_icon(os.path.join(ICON_DIR, 'checktoradial.svg'), 'svg+xml'),
'nuer': read_icon(os.path.join(ICON_DIR, 'right-east.svg'), 'svg+xml'),
'nuel': read_icon(os.path.join(ICON_DIR, 'left-east.svg'), 'svg+xml')
}, docstring="Custom application icons")

viewer_icons = DictCallbackProperty({}, docstring="Indexed icons (numbers) for viewers across the app") # noqa
Expand Down Expand Up @@ -476,19 +478,22 @@ def _on_layers_changed(self, msg):
else:
raise NotImplementedError(f"cannot recognize new layer from {msg}")

wcs_only_refdata_icon = 'mdi-rotate-left'
n_wcs_layers = (
len([icon.startswith('mdi-rotate-left') for icon in self.state.layer_icons])
if is_wcs_only else 0
)
wcs_only_refdata_icon = '' # blank - might be replaced with custom icon in the future
# any changes here should also be manually reflected in orientation.vue
orientation_icons = {'Default orientation': 'mdi-image-outline',
'North-up, East-left': 'nuel',
'North-up, East-right': 'nuer'}

if layer_name not in self.state.layer_icons:
if is_wcs_only:
self.state.layer_icons = {**self.state.layer_icons,
layer_name: wcs_only_refdata_icon}
layer_name: orientation_icons.get(layer_name,
wcs_only_refdata_icon)}
else:
self.state.layer_icons = {
**self.state.layer_icons,
layer_name: alpha_index(len(self.state.layer_icons) - n_wcs_layers)
layer_name: alpha_index(len([ln for ln, ic in self.state.layer_icons.items()
if not ic.startswith('mdi-')]))
}

def _change_reference_data(self, new_refdata_label, viewer_id=None):
Expand All @@ -507,16 +512,11 @@ def _change_reference_data(self, new_refdata_label, viewer_id=None):

old_refdata = viewer.state.reference_data

if (new_refdata_label == old_refdata.label) or (old_refdata.coords is None):
if old_refdata is not None and ((new_refdata_label == old_refdata.label)
or (old_refdata.coords is None)):
# if there's no refdata change nor WCS, don't do anything:
return

# locate the central coordinate of old refdata in this viewer:
sky_cen = viewer._get_center_skycoord(old_refdata)

# estimate FOV in the viewer with old reference data:
fov_sky_init = viewer._get_fov(old_refdata)

new_refdata = self.data_collection[new_refdata_label]

# make sure new refdata can be selected:
Expand All @@ -533,13 +533,22 @@ def _change_reference_data(self, new_refdata_label, viewer_id=None):
viewer_item = self._get_viewer_item(viewer_ref)
viewer_item['reference_data_label'] = new_refdata.label

if old_refdata is None:
return

self.hub.broadcast(ChangeRefDataMessage(
new_refdata,
viewer,
viewer_id=viewer.reference,
old=old_refdata,
sender=self))

# locate the central coordinate of old refdata in this viewer:
sky_cen = viewer._get_center_skycoord(old_refdata)

# estimate FOV in the viewer with old reference data:
fov_sky_init = viewer._get_fov(old_refdata)

if (
all('_WCS_ONLY' in refdata.meta for refdata in [old_refdata, new_refdata]) and
viewer.shape is not None
Expand Down Expand Up @@ -2031,7 +2040,7 @@ def _reparent_subsets(self, old_parent, new_parent=None):

# Translate bounds through WCS if needed
if (self.config == "imviz" and
self._jdaviz_helper.plugins["Links Control"].link_type == "WCS"):
self._jdaviz_helper.plugins["Orientation"].link_type == "WCS"):
# Get the correct link to use for translation
roi = subset_state.roi
if type(roi) in (CircularROI, CircularAnnulusROI,
Expand Down Expand Up @@ -2250,7 +2259,7 @@ def vue_data_item_remove(self, event):
# the reference data (which would leave 0 external_links).
if len(self.data_collection) > 1 and len(self.data_collection.external_links) == 0:
if self.config == "imviz" and imviz_refdata:
link_type = self._jdaviz_helper.plugins["Links Control"].link_type.selected.lower()
link_type = self._jdaviz_helper.plugins["Orientation"].link_type.selected.lower()
self._jdaviz_helper.link_data(link_type=link_type, error_on_fail=True)
# Hack to restore responsiveness to imviz layers
for viewer_ref in self.get_viewer_reference_names():
Expand Down Expand Up @@ -2521,12 +2530,12 @@ def _on_new_viewer(self, msg, vid=None, name=None, add_layers_to_viewer=False):
viewer.figure_widget.layout.height = '100%'

if hasattr(viewer.state, 'linked_by_wcs'):
links_control_plugin = self._jdaviz_helper.plugins.get('Links Control', None)
if links_control_plugin is not None:
viewer.state.linked_by_wcs = links_control_plugin.link_type.selected == 'WCS'
orientation_plugin = self._jdaviz_helper.plugins.get('Orientation', None)
if orientation_plugin is not None:
viewer.state.linked_by_wcs = orientation_plugin.link_type.selected == 'WCS'
elif len(self._viewer_store):
# The plugin would only not exist for instances of Imviz where the user has
# intentionally removed the Links Control plugin, but in that case we will
# intentionally removed the Orientation plugin, but in that case we will
# adopt "linked_by_wcs" from the first (assuming all are the same)
# NOTE: deleting the default viewer is forbidden both by API and UI, but if
# for some reason that was the case here, linked_by_wcs will default to False
Expand Down
18 changes: 16 additions & 2 deletions jdaviz/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@
<div v-if="trayItemVisible(trayItem, state.tray_items_filter)">
<v-expansion-panel-header >
<j-tooltip :tipid="trayItem.name">
{{ trayItem.label }}
{{ trayItem.label == 'Orientation' ? 'Orientation (prev. Links Control)' : trayItem.label }}
</j-tooltip>
</v-expansion-panel-header>
<v-expansion-panel-content style="margin-left: -12px; margin-right: -12px;">
<jupyter-widget :widget="trayItem.widget" v-if="state.tray_items_open.includes(index)"></jupyter-widget>
<jupyter-widget v-if="state.tray_items_open.includes(index)" :widget="trayItem.widget"></jupyter-widget>
</v-expansion-panel-content>
</div>
</v-expansion-panel>
Expand Down Expand Up @@ -351,6 +351,20 @@ div.output_wrapper {
padding-right: 12px !important;
}

.tray-plugin .v-alert {
padding: 8px;
line-height: normal;
font-size: small;
}

.v-alert.warning {
color: black !important;
}

.v-alert.warning .v-icon {
color: black;
}

a:link {
text-decoration: none;
}
Expand Down
5 changes: 4 additions & 1 deletion jdaviz/components/layer_viewer_icon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
<v-icon v-if="String(icon).startsWith('mdi-')" :size="icon_size || 16">
{{icon}}
</v-icon>
<span v-else-if="icons && Object.keys(icons).indexOf(icon) !== -1">
<img :src="icons[icon]" :width="icon_size || 16"/>
</span>
<span v-else :class="prevent_invert_if_dark ? 'layer-viewer-icon' : 'invert-if-dark layer-viewer-icon'" :style="span_style+'; color: '+color+'; '+borderStyle">
{{String(icon).toUpperCase()}}
</span>
Expand All @@ -11,7 +14,7 @@

<script>
module.exports = {
props: ['span_style', 'color', 'icon', 'icon_size', 'linewidth', 'linestyle', 'prevent_invert_if_dark'],
props: ['span_style', 'color', 'icon', 'icons', 'icon_size', 'linewidth', 'linestyle', 'prevent_invert_if_dark'],
computed: {
borderStyle() {
if (this.$props.linewidth > 0) {
Expand Down
2 changes: 1 addition & 1 deletion jdaviz/components/plugin_editable_select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<v-alert
v-else-if="mode=='remove'"
type="warning"
style="width: 100%"
style="width: 100%; padding-top: 16px; padding-bottom: 16px"
>
<span>remove '{{selected}}' {{label.toLowerCase()}}?</span>
<template v-slot:append>
Expand Down
8 changes: 4 additions & 4 deletions jdaviz/components/plugin_layer_select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
<div class="single-line" style="width: 100%">
<v-chip v-if="multiselect" style="width: calc(100% - 10px)">
<span>
<j-layer-viewer-icon :icon="data.item.icon" :prevent_invert_if_dark="true"></j-layer-viewer-icon>
<j-layer-viewer-icon :icon="data.item.icon" :icons="icons" :prevent_invert_if_dark="true"></j-layer-viewer-icon>
{{ data.item.label }}
</span>
</v-chip>
<span v-else>
<j-layer-viewer-icon span_style="margin-right: 4px" :icon="data.item.icon" :prevent_invert_if_dark="true"></j-layer-viewer-icon>
<j-layer-viewer-icon span_style="margin-right: 4px" :icon="data.item.icon" :icons="icons" :prevent_invert_if_dark="true"></j-layer-viewer-icon>
{{ data.item.label }}
</span>
</div>
Expand All @@ -52,7 +52,7 @@
<template slot="item" slot-scope="data">
<div class="single-line">
<span>
<j-layer-viewer-icon span_style="margin-right: 4px" :icon="data.item.icon" :prevent_invert_if_dark="true"></j-layer-viewer-icon>
<j-layer-viewer-icon span_style="margin-right: 4px" :icon="data.item.icon" :icons="icons" :prevent_invert_if_dark="true"></j-layer-viewer-icon>
{{ data.item.label }}
</span>
</div>
Expand All @@ -64,7 +64,7 @@

<script>
module.exports = {
props: ['items', 'selected', 'label', 'hint', 'rules', 'show_if_single_entry', 'multiselect']
props: ['items', 'selected', 'label', 'hint', 'rules', 'icons', 'show_if_single_entry', 'multiselect']
};
</script>

Expand Down
3 changes: 3 additions & 0 deletions jdaviz/components/viewer_data_select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<j-viewer-data-select-item
:item="item"
:icon="layer_icons[item.name]"
:icons="icons"
:viewer="viewer"
:multi_select="multi_select"
:is_wcs_only="false"
Expand All @@ -63,6 +64,7 @@
<j-viewer-data-select-item
:item="item"
:icon="layer_icons[item.name]"
:icons="icons"
:viewer="viewer"
:multi_select="multi_select"
:is_wcs_only="true"
Expand Down Expand Up @@ -93,6 +95,7 @@
<j-viewer-data-select-item
:item="item"
:icon="layer_icons[item.name]"
:icons="icons"
:viewer="viewer"
:multi_select="multi_select"
:is_wcs_only="false"
Expand Down
21 changes: 8 additions & 13 deletions jdaviz/components/viewer_data_select_item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@
:tooltipcontent="isRefData() ? 'Current viewer orientation' : 'Set as viewer orientation'"
span_style="width: 36px"
>
<span @click="selectRefData">
<v-badge
dot
style="margin-right: 0px; margin-left: 8px; margin-top: 6px"
color="accent"
:value="isRefData()"
>
<j-layer-viewer-icon :icon="icon" icon_size="20" color="#000000DE"></j-layer-viewer-icon>
</v-badge>
</span>
<v-btn
icon
:color="isRefData() ? 'accent' : 'default'"
@click="selectRefData">
<v-icon>{{isRefData() ? "mdi-radiobox-marked" : "mdi-radiobox-blank"}}</v-icon>
</v-btn>
</j-tooltip>
</div>
<div v-else-if="isSelected">
Expand Down Expand Up @@ -50,8 +46,7 @@
</div>

<j-tooltip :tooltipcontent="is_wcs_only ? '' : 'data label: '+item.name" span_style="font-size: 12pt; padding-top: 6px; padding-left: 4px; padding-right: 16px; width: calc(100% - 80px); white-space: nowrap; cursor: default;">
<j-layer-viewer-icon v-if="!is_wcs_only" span_style="margin-left: 4px;" :icon="icon" color="#000000DE"></j-layer-viewer-icon>
<span v-else style="padding-right: 24px"></span>
<j-layer-viewer-icon span_style="margin-left: 4px;" :icon="icon" :icons="icons" color="#000000DE"></j-layer-viewer-icon>

<div class="text-ellipsis-middle" style="font-weight: 500;">
<span>
Expand Down Expand Up @@ -89,7 +84,7 @@
<script>

module.exports = {
props: ['item', 'icon', 'multi_select', 'viewer', 'n_data_entries', 'is_wcs_only'],
props: ['item', 'icon', 'icons', 'multi_select', 'viewer', 'n_data_entries', 'is_wcs_only'],
methods: {
selectClicked() {
prevVisibleState = this.visibleState
Expand Down
2 changes: 1 addition & 1 deletion jdaviz/configs/default/plugins/markers/markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def _recompute_mark_positions(self, viewer):
orig_world_x = np.asarray(self.table._qtable['world'][:, 0][in_viewer])
orig_world_y = np.asarray(self.table._qtable['world'][:, 1][in_viewer])

if self.app._link_type == 'wcs':
if self.app._link_type.lower() == 'wcs':
# convert from the sky coordinates in the table to pixels via the WCS of the current
# reference data
new_wcs = viewer.state.reference_data.coords
Expand Down
Loading