Source code for jdaviz.configs.specviz.plugins.unit_conversion.unit_conversion
import numpy as np
from astropy import units as u
from traitlets import List, Unicode, observe
from jdaviz.core.events import GlobalDisplayUnitChanged
from jdaviz.core.registries import tray_registry
from jdaviz.core.template_mixin import PluginTemplateMixin, UnitSelectPluginComponent, PluginUserApi
from jdaviz.core.validunits import (create_spectral_equivalencies_list,
create_flux_equivalencies_list)
__all__ = ['UnitConversion']
def _valid_glue_display_unit(unit_str, sv, axis='x'):
# need to make sure the unit string is formatted according to the list of valid choices
# that glue will accept (may not be the same as the defaults of the installed version of
# astropy)
if not unit_str:
return unit_str
unit_u = u.Unit(unit_str)
choices_str = getattr(sv.state.__class__, f'{axis}_display_unit').get_choices(sv.state)
choices_str = [choice for choice in choices_str if choice is not None]
choices_u = [u.Unit(choice) for choice in choices_str]
if unit_u not in choices_u:
raise ValueError(f"{unit_str} could not find match in valid {axis} display units {choices_str}") # noqa
ind = choices_u.index(unit_u)
return choices_str[ind]
[docs]
@tray_registry('g-unit-conversion', label="Unit Conversion",
viewer_requirements='spectrum')
class UnitConversion(PluginTemplateMixin):
"""
The Unit Conversion plugin handles global app-wide unit-conversion.
See the :ref:`Unit Conversion Plugin Documentation <unit-conversion>` for more details.
Only the following attributes and methods are available through the
:ref:`public plugin API <plugin-apis>`:
* :meth:`~jdaviz.core.template_mixin.PluginTemplateMixin.show`
* :meth:`~jdaviz.core.template_mixin.PluginTemplateMixin.open_in_tray`
* :meth:`~jdaviz.core.template_mixin.PluginTemplateMixin.close_in_tray`
* ``spectral_unit`` (:class:`~jdaviz.core.template_mixin.SelectPluginComponent`):
Global unit to use for all spectral axes.
* ``flux_unit`` (:class:`~jdaviz.core.template_mixin.SelectPluginComponent`):
Global unit to use for all flux axes.
"""
template_file = __file__, "unit_conversion.vue"
spectral_unit_items = List().tag(sync=True)
spectral_unit_selected = Unicode().tag(sync=True)
flux_unit_items = List().tag(sync=True)
flux_unit_selected = Unicode().tag(sync=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.config not in ['specviz', 'cubeviz']:
# TODO [specviz2d, mosviz] x_display_unit is not implemented in glue for image viewer
# used by spectrum-2d-viewer
# TODO [mosviz]: add to yaml file
# TODO [cubeviz, slice]: slice indicator broken after changing spectral_unit
# TODO: support for multiple viewers and handling of mixed state from glue (or does
# this force all to sync?)
self.disabled_msg = f'This plugin is temporarily disabled in {self.config}. Effort to improve it is being tracked at GitHub Issue 1972.' # noqa
# TODO [markers]: existing markers need converting
self.spectrum_viewer.state.add_callback('x_display_unit',
self._on_glue_x_display_unit_changed)
self.spectrum_viewer.state.add_callback('y_display_unit',
self._on_glue_y_display_unit_changed)
self.spectral_unit = UnitSelectPluginComponent(self,
items='spectral_unit_items',
selected='spectral_unit_selected')
self.flux_unit = UnitSelectPluginComponent(self,
items='flux_unit_items',
selected='flux_unit_selected')
@property
def user_api(self):
return PluginUserApi(self, expose=('spectral_unit', 'flux_unit'))
def _on_glue_x_display_unit_changed(self, x_unit):
if x_unit is None:
return
if x_unit == 'deg' and self.app.config == 'cubeviz':
# original unit during init can be deg (before axis is set correctly)
return
self.spectrum_viewer.set_plot_axes()
if x_unit != self.spectral_unit.selected:
x_unit = _valid_glue_display_unit(x_unit, self.spectrum_viewer, 'x')
x_u = u.Unit(x_unit)
choices = create_spectral_equivalencies_list(x_u)
# ensure that original entry is in the list of choices
if not np.any([x_u == u.Unit(choice) for choice in choices]):
choices = [x_unit] + choices
self.spectral_unit.choices = choices
# in addition to the jdaviz options, allow the user to set any glue-valid unit
# which would then be appended on to the list of choices going forward
self.spectral_unit._addl_unit_strings = self.spectrum_viewer.state.__class__.x_display_unit.get_choices(self.spectrum_viewer.state) # noqa
self.spectral_unit.selected = x_unit
if not len(self.flux_unit.choices):
# in case flux_unit was triggered first (but could not be set because there
# as no spectral_unit to determine valid equivalencies)
self._on_glue_y_display_unit_changed(self.spectrum_viewer.state.y_display_unit)
def _on_glue_y_display_unit_changed(self, y_unit):
if y_unit is None:
return
if self.spectral_unit.selected == "":
# no spectral unit set yet, cannot determine equivalencies
# setting the spectral unit will check len(flux_unit.choices) and call this manually
# in the case that that is triggered second.
return
self.spectrum_viewer.set_plot_axes()
if y_unit != self.flux_unit.selected:
x_u = u.Unit(self.spectral_unit.selected)
y_unit = _valid_glue_display_unit(y_unit, self.spectrum_viewer, 'y')
y_u = u.Unit(y_unit)
choices = create_flux_equivalencies_list(y_u, x_u)
# ensure that original entry is in the list of choices
if not np.any([y_u == u.Unit(choice) for choice in choices]):
choices = [y_unit] + choices
self.flux_unit.choices = choices
self.flux_unit.selected = y_unit
@observe('spectral_unit_selected')
def _on_spectral_unit_changed(self, *args):
xunit = _valid_glue_display_unit(self.spectral_unit.selected, self.spectrum_viewer, 'x')
if self.spectrum_viewer.state.x_display_unit != xunit:
self.spectrum_viewer.state.x_display_unit = xunit
self.hub.broadcast(GlobalDisplayUnitChanged('spectral',
self.spectral_unit.selected,
sender=self))
@observe('flux_unit_selected')
def _on_flux_unit_changed(self, *args):
yunit = _valid_glue_display_unit(self.flux_unit.selected, self.spectrum_viewer, 'y')
if self.spectrum_viewer.state.y_display_unit != yunit:
self.spectrum_viewer.state.y_display_unit = yunit
self.hub.broadcast(GlobalDisplayUnitChanged('flux',
self.flux_unit.selected,
sender=self))