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

Hover spectrum with extra axis #2661

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Cubeviz

- Moment map plugin now supports linear per-spaxel continuum subtraction. [#2587]

- Single-pixel subset tool now shows spectrum-at-spaxel on hover. [#2647]

Imviz
^^^^^

Expand Down
10 changes: 8 additions & 2 deletions docs/cubeviz/displaycubes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,10 @@ the bottom of the UI.
Spectrum At Spaxel
==================

This tool allows the user to create a one spaxel subset in an image viewer. This subset will then be
This tool allows the user to create a single-spaxel subset in an image viewer. This subset will then be
visualized in the spectrum viewer by showing the spectrum at that spaxel.
Activate this tool and then left-click to create the new region.
While this tool is active, hovering over a pixel in the image viewer will show a preview of the spectrum
at that spaxel in the spectrum viewer, and left-clicking will create a new subset at that spaxel.
Click again to move the region to a new location under the cursor. Holding down the
alt key (Alt key on Windows, Option key on Mac) while clicking on a spaxel creates a new subset at
that point instead of moving the previously created region.
Expand All @@ -110,6 +111,11 @@ You can also use the subset modes that are explained in the
:ref:`Spatial Regions <imviz_defining_spatial_regions>`
section above in the same way you would with the other subset selection tools.

Note that moving the cursor outside of the image viewer or deactivating the spectrum-at-spaxel tool
will revert the spectrum viewer zoom limits from the zoomed-in preview view to the limits set prior
to using the tool. Thus it may be necessary to reset the zoom to see any single-spaxel subset spectra
created using the tool.

.. _cubeviz-display-settings:

Display Settings
Expand Down
11 changes: 11 additions & 0 deletions jdaviz/configs/cubeviz/plugins/tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ def test_spectrum_at_spaxel(cubeviz_helper, spectrum1d_cube):
assert len(flux_viewer.native_marks) == 2
assert len(spectrum_viewer.data()) == 1

# Move to spaxel location
flux_viewer.toolbar.active_tool.on_mouse_move(
{'event': 'mousemove', 'domain': {'x': x, 'y': y}, 'altKey': False})
assert flux_viewer.toolbar.active_tool._mark in spectrum_viewer.figure.marks
assert flux_viewer.toolbar.active_tool._mark.visible is True

# Click on spaxel location
flux_viewer.toolbar.active_tool.on_mouse_event(
{'event': 'click', 'domain': {'x': x, 'y': y}, 'altKey': False})
Expand All @@ -30,6 +36,11 @@ def test_spectrum_at_spaxel(cubeviz_helper, spectrum1d_cube):
assert len(subsets) == 1
assert isinstance(reg, RectanglePixelRegion)

# Mouse leave event
flux_viewer.toolbar.active_tool.on_mouse_move(
{'event': 'mouseleave', 'domain': {'x': x, 'y': y}, 'altKey': False})
assert flux_viewer.toolbar.active_tool._mark.visible is False

# Deselect tool
flux_viewer.toolbar.active_tool = None
assert len(flux_viewer.native_marks) == 3
Expand Down
97 changes: 97 additions & 0 deletions jdaviz/configs/cubeviz/plugins/tools.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import time
import os

import bqplot
from glue.config import viewer_tool
from glue_jupyter.bqplot.image import BqplotImageView
from glue.viewers.common.tool import CheckableTool
import numpy as np

from jdaviz.configs.imviz.plugins.tools import _MatchedZoomMixin
from jdaviz.core.events import SliceToolStateMessage
from jdaviz.core.tools import PanZoom, BoxZoom, SinglePixelRegion
from jdaviz.core.marks import PluginLine

__all__ = []

Expand Down Expand Up @@ -83,3 +86,97 @@ class SpectrumPerSpaxel(SinglePixelRegion):
tool_id = 'jdaviz:spectrumperspaxel'
action_text = 'See spectrum at a single spaxel'
tool_tip = 'Click on the viewer and see the spectrum at that spaxel in the spectrum viewer'

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._spectrum_viewer = None
self._extra_axis = bqplot.Axis(grid_lines='none', label="", num_ticks=8,
orientation='vertical', scale=bqplot.LinearScale(),
side='right', tick_format='0.1e',
tick_style={'font-size': 15, 'font-weight': 600},
color="#c75d2c", grid_color="#c75d2c")
self._previous_bounds = None
self._mark = None
self._data = None

def _reset_spectrum_viewer_bounds(self):
sv_state = self._spectrum_viewer.state
sv_state.x_min = self._previous_bounds[0]
sv_state.x_max = self._previous_bounds[1]
sv_state.y_min = self._previous_bounds[2]
sv_state.y_max = self._previous_bounds[3]

def activate(self):
# Add callbacks (click is handled by super())
self.viewer.add_event_callback(self.on_mouse_move, events=['mousemove', 'mouseleave', 'mouseenter'])
# Get the spectrum viewer if activating for first time
if self._spectrum_viewer is None:
self._spectrum_viewer = self.viewer.jdaviz_helper.app.get_viewer('spectrum-viewer')
# Add extra y-axis to show on right hand side of spectrum viewer
if self._extra_axis not in self._spectrum_viewer.figure.axes:
self._spectrum_viewer.figure.axes.append(self._extra_axis)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self._spectrum_viewer.figure.axes.append(self._extra_axis)
self._spectrum_viewer.figure.axes = [*self._spectrum_viewer.figure.axes, self._extra_axis]

mutations are not detected

# Create the mark for the preview spectrum
if self._mark is None:
scales = {}
scales['x'] = self._spectrum_viewer.native_marks[0].scales['x']
scales['y'] = bqplot.LinearScale()
self._mark = PluginLine(self._spectrum_viewer, visible=False, scales=scales)
self._spectrum_viewer.figure.marks = self._spectrum_viewer.figure.marks + [self._mark]
# Store these so we can revert to previous user-set zoom after preview view
sv_state = self._spectrum_viewer.state
self._previous_bounds = [sv_state.x_min, sv_state.x_max, sv_state.y_min, sv_state.y_max]
super().activate()

def deactivate(self):
self.viewer.remove_event_callback(self.on_mouse_move)
#self._reset_spectrum_viewer_bounds()
# Fully remove the extra axis rather than just setting to invisible
if self._extra_axis in self._spectrum_viewer.figure.axes:
self._spectrum_viewer.figure.axes.remove(self._extra_axis)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self._spectrum_viewer.figure.axes.remove(self._extra_axis)
axes = self._spectrum_viewer.figure.axes.copy()
axes.remove(self._extra_axis)
self._spectrum_viewer.figure.axes = axes

mutations are not detected

self._spectrum_viewer.figure.fig_margin['right'] = 10
self._spectrum_viewer.figure.send_state()
super().deactivate()

def on_mouse_move(self, data):
# Set the mark and extra axis to be invisible
if data['event'] == 'mouseleave':
self._mark.visible = False
self._extra_axis.visible=False
self._extra_axis.send_state("visible")
self._spectrum_viewer.figure.fig_margin['right'] = 10
self._spectrum_viewer.figure.send_state("fig_margin")
#self._reset_spectrum_viewer_bounds()
return

elif data['event'] == 'mouseenter':
# Make room for the extra axis
self._spectrum_viewer.figure.fig_margin['right'] = 80
self._extra_axis.visible = True

x = int(np.round(data['domain']['x']))
y = int(np.round(data['domain']['y']))

# Use first visible layer for now
cube_data = [layer.layer for layer in self.viewer.layers if layer.state.visible][0]
spectrum = cube_data.get_object(statistic=None)

# Nothing to show if we're out of bounds of the data
if x >= spectrum.flux.shape[0] or x < 0 or y >= spectrum.flux.shape[1] or y < 0:
self._mark.visible = False
self._extra_axis.visible=False
else:
# Make the mark visible and update its y values
self._mark.visible = True
y_values = spectrum.flux[x, y, :]
if np.all(np.isnan(y_values)):
self._mark.visible = False
Copy link
Collaborator

@maartenbreddels maartenbreddels Jan 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While trying to reproduce the bug in bqplot I noticed this resulted in my axis not being visible most of the time, maybe the test in the line above is faulty?

return
self._mark.update_xy(spectrum.spectral_axis.value, y_values)
# Also update the extra axis to show the correct values
float_y_min = float(np.nanmin(y_values.value))
float_y_max = float(np.nanmax(y_values.value))
self._extra_axis.scale.min = float_y_min
self._extra_axis.scale.max = float_y_max

self._extra_axis.send_state(["scale", "visible"])
self._spectrum_viewer.figure.send_state(["fig_margin", "axes"])
Comment on lines +181 to +182
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These shouldn't be needed, maybe axes because you mutate them.

Loading