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

Display units: line lists #2148

Merged
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 2 additions & 2 deletions jdaviz/configs/specviz/plugins/line_analysis/line_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -529,8 +529,8 @@ def _uncertainty(result):
def _compute_redshift_for_selected_line(self):
index = self.line_items.index(self.selected_line)
line_mark = self.line_marks[index]
rest_value = (line_mark.rest_value * line_mark._x_unit).to_value(u.AA,
equivalencies=u.spectral())
rest_value = (line_mark.rest_value * line_mark.xunit).to_value(u.AA,
equivalencies=u.spectral())
return (self.results_centroid - rest_value) / rest_value

@observe('sync_identify')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ def user_api(self):
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')
Expand Down
175 changes: 88 additions & 87 deletions jdaviz/core/marks.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,77 @@ def _update_counts(self, *args):
self.right.text = [f'{oob_right} \u25b6' if oob_right > 0 else '']


class BaseSpectrumVerticalLine(Lines, HubListener):
class PluginMark():
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wait, this doesn't inherit anything...does the base python Class init handle setting self.[whatever] for kwargs if you call super().__init__ like this?

Copy link
Member Author

Choose a reason for hiding this comment

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

turns out its required in order for the comm to be set correctly when other classes inherit from this and Lines

self.xunit = None
self.yunit = None
# whether to update existing marks when global display units are changed
self.auto_update_units = True
self.hub.subscribe(self, GlobalDisplayUnitChanged,
handler=self._on_global_display_unit_changed)

if not self.auto_update_units:
return
if self.xunit is None:
self.set_x_unit()
if self.yunit is None:
self.set_y_unit()
kecnry marked this conversation as resolved.
Show resolved Hide resolved

@property
def hub(self):
return self.viewer.hub

def update_xy(self, x, y):
self.x = np.asarray(x)
self.y = np.asarray(y)

def append_xy(self, x, y):
self.x = np.append(self.x, x)
self.y = np.append(self.y, y)

def set_x_unit(self, unit=None):
if unit is None:
if not hasattr(self.viewer.state, 'x_display_unit'):
return
unit = self.viewer.state.x_display_unit
unit = u.Unit(unit)
if self.xunit is not None:
x = (self.x * self.xunit).to_value(unit, u.spectral())
self.xunit = unit
self.x = x
self.xunit = unit

def set_y_unit(self, unit=None):
if unit is None:
if not hasattr(self.viewer.state, 'y_display_unit'):
return
unit = self.viewer.state.y_display_unit
unit = u.Unit(unit)
if self.yunit is not None:
self.y = (self.y * self.yunit).to_value(unit)
self.yunit = unit

def _on_global_display_unit_changed(self, msg):
if not self.auto_update_units:
return
if self.viewer.__class__.__name__ in ['SpecvizProfileView', 'CubevizProfileView']:
axis_map = {'spectral': 'x', 'flux': 'y'}
elif self.viewer.__class__.__name__ == 'MosvizProfile2DView':
axis_map = {'spectral': 'x'}
else:
return
axis = axis_map.get(msg.axis, None)
if axis is not None:
getattr(self, f'set_{axis}_unit')(msg.unit)

def clear(self):
self.update_xy([], [])


class BaseSpectrumVerticalLine(Lines, PluginMark, HubListener):
def __init__(self, viewer, x, **kwargs):
# we'll store the current units so that we can automatically update the
# positioning on a change to the x-units
self._x_unit = viewer.state.reference_data.get_object(cls=Spectrum1D).spectral_axis.unit
self.viewer = viewer

# the location of the marker will need to update automatically if the
# underlying data changes (through a unit conversion, for example)
Expand All @@ -84,14 +150,14 @@ def _update_reference_data(self, reference_data):

def _update_data(self, x_all):
# the x-units may have changed. We want to convert the internal self.x
# from self._x_unit to the new units (x_all.unit)
# from self.xunit to the new units (x_all.unit)
new_unit = x_all.unit
if new_unit == self._x_unit:
if new_unit == self.xunit:
return
old_quant = self.x[0]*self._x_unit
old_quant = self.x[0]*self.xunit
x = old_quant.to_value(x_all.unit, equivalencies=u.spectral())
self.x = [x, x]
self._x_unit = new_unit
self.xunit = new_unit


class SpectralLine(BaseSpectrumVerticalLine):
Expand All @@ -111,7 +177,7 @@ def __init__(self, viewer, rest_value, redshift=0, name=None, **kwargs):
# setting redshift will set self.x and enable the obs_value property,
# but to do that we need x_unit set first (would normally be assigned
# in the super init)
self._x_unit = viewer.state.reference_data.get_object(cls=Spectrum1D).spectral_axis.unit
self.xunit = u.Unit(viewer.state.x_display_unit)
self.redshift = redshift

viewer.session.hub.subscribe(self, LineIdentifyMessage,
Expand All @@ -132,23 +198,28 @@ def rest_value(self):
def obs_value(self):
return self.x[0]

def set_x_unit(self, unit=None):
prev_unit = self.xunit
super().set_x_unit(unit=unit)
self._rest_value = (self._rest_value * prev_unit).to_value(unit, u.spectral())

@property
def redshift(self):
return self._redshift

@redshift.setter
def redshift(self, redshift):
self._redshift = redshift
if str(self._x_unit.physical_type) == 'length':
if str(self.xunit.physical_type) == 'length':
obs_value = self._rest_value*(1+redshift)
elif str(self._x_unit.physical_type) == 'frequency':
elif str(self.xunit.physical_type) == 'frequency':
obs_value = self._rest_value/(1+redshift)
else:
# catch all for anything else (wavenumber, energy, etc)
rest_angstrom = (self._rest_value*self._x_unit).to_value(u.Angstrom,
equivalencies=u.spectral())
rest_angstrom = (self._rest_value*self.xunit).to_value(u.Angstrom,
equivalencies=u.spectral())
obs_angstrom = rest_angstrom*(1+redshift)
obs_value = (obs_angstrom*u.Angstrom).to_value(self._x_unit,
obs_value = (obs_angstrom*u.Angstrom).to_value(self.xunit,
equivalencies=u.spectral())
self.x = [obs_value, obs_value]

Expand All @@ -169,14 +240,14 @@ def _process_identify_change(self, msg):

def _update_data(self, x_all):
new_unit = x_all.unit
if new_unit == self._x_unit:
if new_unit == self.xunit:
return

old_quant = self._rest_value*self._x_unit
old_quant = self._rest_value*self.xunit
self._rest_value = old_quant.to_value(new_unit, equivalencies=u.spectral())
# re-compute self.x from current redshift (instead of converting that as well)
self.redshift = self._redshift
self._x_unit = new_unit
self.xunit = new_unit


class SliceIndicatorMarks(BaseSpectrumVerticalLine, HubListener):
Expand Down Expand Up @@ -485,76 +556,6 @@ def _on_shadowing_changed(self, change):
self._update_align()


class PluginMark():
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.xunit = None
self.yunit = None
# whether to update existing marks when global display units are changed
self.auto_update_units = True
self.hub.subscribe(self, GlobalDisplayUnitChanged,
handler=self._on_global_display_unit_changed)
self._update_units()

@property
def hub(self):
return self.viewer.hub

def update_xy(self, x, y):
self.x = np.asarray(x)
self.y = np.asarray(y)

def append_xy(self, x, y):
self.x = np.append(self.x, x)
self.y = np.append(self.y, y)

def _update_units(self):
if not self.auto_update_units:
return
if self.xunit is None:
self.set_x_unit()
if self.yunit is None:
self.set_y_unit()

def set_x_unit(self, unit=None):
if unit is None:
if not hasattr(self.viewer.state, 'x_display_unit'):
return
unit = self.viewer.state.x_display_unit
unit = u.Unit(unit)
if self.xunit is not None:
x = (self.x * self.xunit).to_value(unit, u.spectral())
self.xunit = unit
self.x = x
self.xunit = unit

def set_y_unit(self, unit=None):
if unit is None:
if not hasattr(self.viewer.state, 'y_display_unit'):
return
unit = self.viewer.state.y_display_unit
unit = u.Unit(unit)
if self.yunit is not None:
self.y = (self.y * self.yunit).to_value(unit)
self.yunit = unit

def _on_global_display_unit_changed(self, msg):
if not self.auto_update_units:
return
if self.viewer.__class__.__name__ in ['SpecvizProfileView', 'CubevizProfileView']:
axis_map = {'spectral': 'x', 'flux': 'y'}
elif self.viewer.__class__.__name__ == 'MosvizProfile2DView':
axis_map = {'spectral': 'x'}
else:
return
axis = axis_map.get(msg.axis, None)
if axis is not None:
getattr(self, f'set_{axis}_unit')(msg.unit)

def clear(self):
self.update_xy([], [])


class LinesAutoUnit(PluginMark, Lines, HubListener):
def __init__(self, viewer, *args, **kwargs):
self.viewer = viewer
Expand Down