From 9b797c80d35a68e21c5445197cad356bb0cb2a75 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Thu, 28 Dec 2023 14:23:11 -0500 Subject: [PATCH 01/10] basic plugin to toggle actively selected flux column per-dataset --- docs/plugins.rst | 9 +++ lcviz/helper.py | 24 +++--- lcviz/parsers.py | 11 ++- lcviz/plugins/__init__.py | 1 + lcviz/plugins/flux_origin/__init__.py | 1 + lcviz/plugins/flux_origin/flux_origin.py | 91 +++++++++++++++++++++++ lcviz/plugins/flux_origin/flux_origin.vue | 31 ++++++++ 7 files changed, 157 insertions(+), 11 deletions(-) create mode 100644 lcviz/plugins/flux_origin/__init__.py create mode 100644 lcviz/plugins/flux_origin/flux_origin.py create mode 100644 lcviz/plugins/flux_origin/flux_origin.vue diff --git a/docs/plugins.rst b/docs/plugins.rst index 0558f69f..66a0f098 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -35,6 +35,15 @@ This plugin allows viewing of any metadata associated with the selected data. :ref:`Jdaviz Metadata Viewer ` Jdaviz documentation on the Metadata Viewer plugin. +.. _flux-origin:: + +Flux Origin +=========== + +This plugin allows choosing which column in the underlying data should be used as the flux origin +throughout the app (when plotting and in any data analysis plugins). + + .. _plot-options: Plot Options diff --git a/lcviz/helper.py b/lcviz/helper.py index a6453cd2..b7b17904 100644 --- a/lcviz/helper.py +++ b/lcviz/helper.py @@ -72,7 +72,8 @@ class LCviz(ConfigHelper): 'dense_toolbar': False, 'context': {'notebook': {'max_height': '600px'}}}, 'toolbar': ['g-data-tools', 'g-subset-tools', 'lcviz-coords-info'], - 'tray': ['lcviz-metadata-viewer', 'lcviz-plot-options', 'lcviz-subset-plugin', + 'tray': ['lcviz-metadata-viewer', 'flux-origin', + 'lcviz-plot-options', 'lcviz-subset-plugin', 'lcviz-markers', 'flatten', 'frequency-analysis', 'ephemeris', 'binning', 'lcviz-export-plot'], 'viewer_area': [{'container': 'col', @@ -155,12 +156,17 @@ def _phase_comp_lbl(self, component): return f'phase:{component}' def _set_data_component(self, data, component_label, values): - if component_label not in self._component_ids: - self._component_ids[component_label] = ComponentID(component_label) - - if self._component_ids[component_label] in data.components: - data.update_components({self._component_ids[component_label]: values}) + if component_label in self._component_ids: + component_id = self._component_ids[component_label] else: - data.add_component(values, self._component_ids[component_label]) - - data.add_component(values, self._component_ids[component_label]) + existing_components = [component.label for component in data.components] + if component_label in existing_components: + component_id = data.components[existing_components.index(component_label)] + else: + component_id = ComponentID(component_label) + self._component_ids[component_label] = component_id + + if component_id in data.components: + data.update_components({component_id: values}) + else: + data.add_component(values, component_id) diff --git a/lcviz/parsers.py b/lcviz/parsers.py index e9268c83..637e0cd5 100644 --- a/lcviz/parsers.py +++ b/lcviz/parsers.py @@ -27,9 +27,16 @@ def light_curve_parser(app, file_obj, data_label=None, show_in_viewer=True, **kw new_data_label = f'{data_label}' else: new_data_label = light_curve.meta.get('OBJECT', 'Light curve') + + # handle flux_origin default flux_origin = light_curve.meta.get('FLUX_ORIGIN', None) # i.e. PDCSAP or SAP - if flux_origin is not None: - new_data_label += f'[{flux_origin}]' + if flux_origin == 'flux': + # then make a copy of this column so it won't be lost when changing with the flux_column + # plugin + light_curve['flux:orig'] = light_curve['flux'] + if 'flux_err' in light_curve.columns: + light_curve['flux:orig_err'] = light_curve['flux_err'] + light_curve.meta['FLUX_ORIGIN'] = 'flux:orig' data = _data_with_reftime(app, light_curve) app.add_data(data, new_data_label) diff --git a/lcviz/plugins/__init__.py b/lcviz/plugins/__init__.py index dacdb581..90806175 100644 --- a/lcviz/plugins/__init__.py +++ b/lcviz/plugins/__init__.py @@ -3,6 +3,7 @@ from .ephemeris.ephemeris import * # noqa from .export_plot.export_plot import * # noqa from .flatten.flatten import * # noqa +from .flux_origin.flux_origin import * # noqa from .frequency_analysis.frequency_analysis import * # noqa from .markers.markers import * # noqa from .metadata_viewer.metadata_viewer import * # noqa diff --git a/lcviz/plugins/flux_origin/__init__.py b/lcviz/plugins/flux_origin/__init__.py new file mode 100644 index 00000000..2927ba18 --- /dev/null +++ b/lcviz/plugins/flux_origin/__init__.py @@ -0,0 +1 @@ +from .flux_origin import * # noqa diff --git a/lcviz/plugins/flux_origin/flux_origin.py b/lcviz/plugins/flux_origin/flux_origin.py new file mode 100644 index 00000000..65c1f557 --- /dev/null +++ b/lcviz/plugins/flux_origin/flux_origin.py @@ -0,0 +1,91 @@ +from traitlets import List, Unicode, observe + +from jdaviz.core.registries import tray_registry +from jdaviz.core.template_mixin import (PluginTemplateMixin, + DatasetSelectMixin, SelectPluginComponent) +from jdaviz.core.user_api import PluginUserApi + +__all__ = ['FluxOrigin'] + + +@tray_registry('flux-origin', label="Flux Origin") +class FluxOrigin(PluginTemplateMixin, DatasetSelectMixin): + """ + See the :ref:`Flux Origin Plugin Documentation ` for more details. + + Only the following attributes and methods are available through the + public plugin API. + + * ``dataset`` (:class:`~jdaviz.core.template_mixin.DatasetSelect`): + Dataset to bin. + """ + template_file = __file__, "flux_origin.vue" + + origin_items = List().tag(sync=True) + origin_selected = Unicode().tag(sync=True) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.origin = SelectPluginComponent(self, + items='origin_items', + selected='origin_selected') + + @property + def user_api(self): + expose = ['dataset', 'origin'] + return PluginUserApi(self, expose=expose) + + @observe('dataset_selected') + def _on_change_dataset(self, *args): + def _include_col(lk_obj, col): + if col == 'flux' and lk_obj.meta.get('FLUX_ORIGIN') != 'flux': + # this is the currently active column (and should be copied elsewhere unless) + return False + if col in ('time', 'cadn', 'cadenceno', 'quality'): + return False + if col.startswith('phase:'): + # internal jdaviz ephemeris phase columns + return False + if col.startswith('time'): + return False + if col.startswith('centroid'): + return False + if col.startswith('cbv'): + # cotrending basis vector + return False + if col.endswith('_err'): + return False + if col.endswith('quality'): + return False + return lk_obj[col].unit == lk_obj['flux'].unit + + lk_obj = self.dataset.selected_obj + self.origin.choices = [col for col in lk_obj.columns if _include_col(lk_obj, col)] + flux_origin = lk_obj.meta.get('FLUX_ORIGIN') + if flux_origin in self.origin.choices: + self.origin.selected = flux_origin + else: + self.origin.selected = '' + + @observe('origin_selected') + def _on_change_origin(self, *args): + if self.origin_selected == '': + return + + dc_item = self.dataset.selected_dc_item + old_flux_origin = dc_item.meta.get('FLUX_ORIGIN') + if self.origin.selected == old_flux_origin: + # nothing to do here! + return + + # instead of using lightkurve's select_flux and having to reparse the data entry, we'll + # manipulate the arrays in the data-collection directly, and modify FLUX_ORIGIN so that + # exporting back to a lightkurve object works as expected + dc_item = self.dataset.selected_dc_item + + self.app._jdaviz_helper._set_data_component(dc_item, 'flux', dc_item[self.origin.selected]) + dc_item.meta['FLUX_ORIGIN'] = self.origin.selected + # need to clear the cache manually due to the change in metadata made to the data-collection + # entry + self.dataset._clear_cache('selected_obj') diff --git a/lcviz/plugins/flux_origin/flux_origin.vue b/lcviz/plugins/flux_origin/flux_origin.vue new file mode 100644 index 00000000..9ae32805 --- /dev/null +++ b/lcviz/plugins/flux_origin/flux_origin.vue @@ -0,0 +1,31 @@ + From 1842c0d232fed9fdeb6615b2874bfbafb29e0525 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Thu, 28 Dec 2023 15:10:52 -0500 Subject: [PATCH 02/10] basic test coverage --- lcviz/conftest.py | 5 ++++- lcviz/parsers.py | 2 +- lcviz/plugins/flux_origin/flux_origin.py | 2 ++ lcviz/tests/test_plugin_flux_origin.py | 18 ++++++++++++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 lcviz/tests/test_plugin_flux_origin.py diff --git a/lcviz/conftest.py b/lcviz/conftest.py index a067f20e..6d616903 100644 --- a/lcviz/conftest.py +++ b/lcviz/conftest.py @@ -32,9 +32,12 @@ def light_curve_like_kepler_quarter(seed=42): quality = np.zeros(len(time), dtype=np.int32) - return LightCurve( + lc = LightCurve( time=time, flux=flux, flux_err=flux_err, quality=quality ) + lc['flux_alt'] = flux + 1 + lc['flux_alt_err'] = flux_err + return lc try: diff --git a/lcviz/parsers.py b/lcviz/parsers.py index 637e0cd5..c7d20dea 100644 --- a/lcviz/parsers.py +++ b/lcviz/parsers.py @@ -30,7 +30,7 @@ def light_curve_parser(app, file_obj, data_label=None, show_in_viewer=True, **kw # handle flux_origin default flux_origin = light_curve.meta.get('FLUX_ORIGIN', None) # i.e. PDCSAP or SAP - if flux_origin == 'flux': + if flux_origin == 'flux' or (flux_origin is None and 'flux' in light_curve.columns): # then make a copy of this column so it won't be lost when changing with the flux_column # plugin light_curve['flux:orig'] = light_curve['flux'] diff --git a/lcviz/plugins/flux_origin/flux_origin.py b/lcviz/plugins/flux_origin/flux_origin.py index 65c1f557..d1781d8d 100644 --- a/lcviz/plugins/flux_origin/flux_origin.py +++ b/lcviz/plugins/flux_origin/flux_origin.py @@ -61,6 +61,8 @@ def _include_col(lk_obj, col): return lk_obj[col].unit == lk_obj['flux'].unit lk_obj = self.dataset.selected_obj + if lk_obj is None: + return self.origin.choices = [col for col in lk_obj.columns if _include_col(lk_obj, col)] flux_origin = lk_obj.meta.get('FLUX_ORIGIN') if flux_origin in self.origin.choices: diff --git a/lcviz/tests/test_plugin_flux_origin.py b/lcviz/tests/test_plugin_flux_origin.py new file mode 100644 index 00000000..6cb6c6e2 --- /dev/null +++ b/lcviz/tests/test_plugin_flux_origin.py @@ -0,0 +1,18 @@ +from numpy.testing import assert_allclose + + +def test_plugin_flux_origin(helper, light_curve_like_kepler_quarter): + helper.load_data(light_curve_like_kepler_quarter) + + fo = helper.plugins['Flux Origin'] + assert len(fo.origin.choices) == 2 + assert fo.origin.selected == 'flux:orig' + + lc = helper.get_data() + assert lc.meta.get('FLUX_ORIGIN') == 'flux:orig' + assert_allclose(lc['flux'], fo._obj.dataset.selected_dc_item['flux:orig']) + + fo.origin = 'flux_alt' + lc = helper.get_data() + assert lc.meta.get('FLUX_ORIGIN') == 'flux_alt' + assert_allclose(lc['flux'], fo._obj.dataset.selected_dc_item['flux_alt']) From 39d861a53d33d6ef96bcc0bee58b4762eb03cff7 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Thu, 28 Dec 2023 15:31:45 -0500 Subject: [PATCH 03/10] changelog entry --- CHANGES.rst | 3 +++ lcviz/plugins/flux_origin/flux_origin.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 10fa9251..3d8de718 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,9 @@ * Clone viewer tool. [#74] +* Flux origin plugin to choose which column is treated as the flux column for each dataset. [#77] + + 0.1.0 (12-14-2023) ------------------ diff --git a/lcviz/plugins/flux_origin/flux_origin.py b/lcviz/plugins/flux_origin/flux_origin.py index d1781d8d..964f44e6 100644 --- a/lcviz/plugins/flux_origin/flux_origin.py +++ b/lcviz/plugins/flux_origin/flux_origin.py @@ -58,6 +58,8 @@ def _include_col(lk_obj, col): return False if col.endswith('quality'): return False + # TODO: need to think about flatten losing units in the flux column (and that other + # columns still exist but are not flattened) return lk_obj[col].unit == lk_obj['flux'].unit lk_obj = self.dataset.selected_obj From 861134a92e4728051538dcb6706fc08620d81eae Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Fri, 29 Dec 2023 08:34:34 -0500 Subject: [PATCH 04/10] fix typo in docs --- docs/plugins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index 66a0f098..66c862aa 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -35,7 +35,7 @@ This plugin allows viewing of any metadata associated with the selected data. :ref:`Jdaviz Metadata Viewer ` Jdaviz documentation on the Metadata Viewer plugin. -.. _flux-origin:: +.. _flux-origin: Flux Origin =========== From 0bff08d08fb1a03974315b9e518158eed80eb4d2 Mon Sep 17 00:00:00 2001 From: Kyle Conroy Date: Fri, 29 Dec 2023 12:34:34 -0500 Subject: [PATCH 05/10] rework flatten plugin to use flux columns --- lcviz/components/__init__.py | 117 ++++++++++++++++++++++ lcviz/events.py | 12 ++- lcviz/plugins/flatten/flatten.py | 73 +++++++++----- lcviz/plugins/flatten/flatten.vue | 42 ++++---- lcviz/plugins/flux_origin/flux_origin.py | 75 +------------- lcviz/plugins/flux_origin/flux_origin.vue | 4 +- lcviz/tests/test_plugin_flatten.py | 21 +--- lcviz/tests/test_plugin_flux_origin.py | 6 +- 8 files changed, 210 insertions(+), 140 deletions(-) diff --git a/lcviz/components/__init__.py b/lcviz/components/__init__.py index e69de29b..33450407 100644 --- a/lcviz/components/__init__.py +++ b/lcviz/components/__init__.py @@ -0,0 +1,117 @@ +from astropy import units as u +from ipyvuetify import VuetifyTemplate +from glue.core import HubListener +from traitlets import List, Unicode + +from jdaviz.core.template_mixin import SelectPluginComponent + +from lcviz.events import FluxOriginChangedMessage + +__all__ = ['FluxOriginSelect', 'FluxOriginSelectMixin'] + + +class FluxOriginSelect(SelectPluginComponent): + def __init__(self, plugin, items, selected, dataset): + super().__init__(plugin, + items=items, + selected=selected, + dataset=dataset) + + self.add_observe(selected, self._on_change_selected) + self.add_observe(self.dataset._plugin_traitlets['selected'], + self._on_change_dataset) + + # sync between instances in different plugins + self.hub.subscribe(self, FluxOriginChangedMessage, + handler=self._on_flux_origin_changed_msg) + + def _on_change_dataset(self, *args): + def _include_col(lk_obj, col): + if col == 'flux' and lk_obj.meta.get('FLUX_ORIGIN') != 'flux': + # this is the currently active column (and should be copied elsewhere unless) + return False + if col in ('time', 'cadn', 'cadenceno', 'quality'): + return False + if col.startswith('phase:'): + # internal jdaviz ephemeris phase columns + return False + if col.startswith('time'): + return False + if col.startswith('centroid'): + return False + if col.startswith('cbv'): + # cotrending basis vector + return False + if col.endswith('_err'): + return False + if col.endswith('quality'): + return False + # TODO: need to think about flatten losing units in the flux column + return lk_obj[col].unit != u.pix + + lk_obj = self.dataset.selected_obj + if lk_obj is None: + return + self.choices = [col for col in lk_obj.columns if _include_col(lk_obj, col)] + flux_origin = lk_obj.meta.get('FLUX_ORIGIN') + if flux_origin in self.choices: + self.selected = flux_origin + else: + self.selected = '' + + def _on_flux_origin_changed_msg(self, msg): + if msg.dataset != self.dataset.selected: + return + + # need to clear the cache due to the change in metadata made to the data-collection entry + self.dataset._clear_cache('selected_obj', 'selected_dc_item') + self._on_change_dataset() + self.selected = msg.flux_origin + + def _on_change_selected(self, *args): + if self.selected == '': + return + + dc_item = self.dataset.selected_dc_item + old_flux_origin = dc_item.meta.get('FLUX_ORIGIN') + if self.selected == old_flux_origin: + # nothing to do here! + return + + # instead of using lightkurve's select_flux and having to reparse the data entry, we'll + # manipulate the arrays in the data-collection directly, and modify FLUX_ORIGIN so that + # exporting back to a lightkurve object works as expected + self.app._jdaviz_helper._set_data_component(dc_item, 'flux', dc_item[self.selected]) + self.app._jdaviz_helper._set_data_component(dc_item, 'flux_err', dc_item[self.selected+"_err"]) # noqa + dc_item.meta['FLUX_ORIGIN'] = self.selected + + self.hub.broadcast(FluxOriginChangedMessage(dataset=self.dataset.selected, + flux_origin=self.selected, + sender=self)) + + def add_new_flux_column(self, flux, flux_err, label, selected=False): + dc_item = self.dataset.selected_dc_item + self.app._jdaviz_helper._set_data_component(dc_item, + label, + flux) + self.app._jdaviz_helper._set_data_component(dc_item, + f"{label}_err", + flux_err) + + # broadcast so all instances update to get the new column and selection (if applicable) + self.hub.broadcast(FluxOriginChangedMessage(dataset=self.dataset.selected, + flux_origin=label if selected else self.selected, # noqa + sender=self)) + + +class FluxOriginSelectMixin(VuetifyTemplate, HubListener): + flux_origin_items = List().tag(sync=True) + flux_origin_selected = Unicode().tag(sync=True) + # assumes DatasetSelectMixin is also used (DatasetSelectMixin must appear after in inheritance) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.flux_origin = FluxOriginSelect(self, + 'flux_origin_items', + 'flux_origin_selected', + dataset='dataset') diff --git a/lcviz/events.py b/lcviz/events.py index 3ca014c6..724471f4 100644 --- a/lcviz/events.py +++ b/lcviz/events.py @@ -1,7 +1,8 @@ from glue.core.message import Message __all__ = ['EphemerisComponentChangedMessage', - 'EphemerisChangedMessage'] + 'EphemerisChangedMessage', + 'FluxOriginChangedMessage'] class EphemerisComponentChangedMessage(Message): @@ -27,3 +28,12 @@ class EphemerisChangedMessage(Message): in the ephemeris plugin""" def __init__(self, ephemeris_label, *args, **kwargs): self.ephemeris_label = ephemeris_label + + +class FluxOriginChangedMessage(Message): + """Message emitted by the FluxOriginSelect component when the selection has been changed. + To subscribe to a change for a particular dataset, consider using FluxOriginSelect directly + and observing the traitlet, rather than subscribing to this message""" + def __init__(self, dataset, flux_origin, *args, **kwargs): + self.dataset = dataset + self.flux_origin = flux_origin diff --git a/lcviz/plugins/flatten/flatten.py b/lcviz/plugins/flatten/flatten.py index 66b8e94c..55ae8773 100644 --- a/lcviz/plugins/flatten/flatten.py +++ b/lcviz/plugins/flatten/flatten.py @@ -7,11 +7,13 @@ from jdaviz.core.events import ViewerAddedMessage from jdaviz.core.registries import tray_registry from jdaviz.core.template_mixin import (PluginTemplateMixin, - DatasetSelectMixin, AddResultsMixin, + DatasetSelectMixin, + AutoTextField, skip_if_no_updates_since_last_active, with_spinner) from jdaviz.core.user_api import PluginUserApi +from lcviz.components import FluxOriginSelectMixin from lcviz.marks import LivePreviewTrend, LivePreviewFlattened from lcviz.utils import data_not_folded from lcviz.viewers import TimeScatterView, PhaseScatterView @@ -21,7 +23,7 @@ @tray_registry('flatten', label="Flatten") -class Flatten(PluginTemplateMixin, DatasetSelectMixin, AddResultsMixin): +class Flatten(PluginTemplateMixin, FluxOriginSelectMixin, DatasetSelectMixin): """ See the :ref:`Flatten Plugin Documentation ` for more details. @@ -32,16 +34,17 @@ class Flatten(PluginTemplateMixin, DatasetSelectMixin, AddResultsMixin): Whether to show the live-preview of the (unnormalized) flattened light curve * ``show_trend_preview`` : bool Whether to show the live-preview of the trend curve used to flatten the light curve - * ``default_to_overwrite`` * ``dataset`` (:class:`~jdaviz.core.template_mixin.DatasetSelect`): Dataset to flatten. - * ``add_results`` (:class:`~jdaviz.core.template_mixin.AddResults`) * ``window_length`` * ``polyorder`` * ``break_tolerance`` * ``niters`` * ``sigma`` * ``unnormalize`` + * ``flux_label`` (:class:`~jdaviz.core.template_mixin.AutoTextField`): + Label for the resulting flux column added to ``dataset`` and automatically selected as the new + flux origin. * :meth:`flatten` """ template_file = __file__, "flatten.vue" @@ -49,7 +52,6 @@ class Flatten(PluginTemplateMixin, DatasetSelectMixin, AddResultsMixin): show_live_preview = Bool(True).tag(sync=True) show_trend_preview = Bool(True).tag(sync=True) - default_to_overwrite = Bool(True).tag(sync=True) flatten_err = Unicode().tag(sync=True) window_length = IntHandleEmpty(101).tag(sync=True) @@ -59,12 +61,22 @@ class Flatten(PluginTemplateMixin, DatasetSelectMixin, AddResultsMixin): sigma = FloatHandleEmpty(3).tag(sync=True) unnormalize = Bool(False).tag(sync=True) + flux_label_label = Unicode().tag(sync=True) + flux_label_default = Unicode().tag(sync=True) + flux_label_auto = Bool(True).tag(sync=True) + flux_label_invalid_msg = Unicode('').tag(sync=True) + flux_label_overwrite = Bool(False).tag(sync=True) + last_live_time = Float(0).tag(sync=True) previews_temp_disable = Bool(False).tag(sync=True) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.flux_label = AutoTextField(self, 'flux_label_label', + 'flux_label_default', 'flux_label_auto', + 'flux_label_invalid_msg') + # do not support flattening data in phase-space self.dataset.add_filter(data_not_folded) @@ -72,12 +84,14 @@ def __init__(self, *args, **kwargs): # those marks self.hub.subscribe(self, ViewerAddedMessage, handler=lambda _: self._live_update()) + self._set_default_label() + @property def user_api(self): - expose = ['show_live_preview', 'show_trend_preview', 'default_to_overwrite', - 'dataset', 'add_results', + expose = ['show_live_preview', 'show_trend_preview', + 'dataset', 'window_length', 'polyorder', 'break_tolerance', - 'niters', 'sigma', 'unnormalize', 'flatten'] + 'niters', 'sigma', 'unnormalize', 'flux_label', 'flatten'] return PluginUserApi(self, expose=expose) @property @@ -108,18 +122,26 @@ def marks(self): return trend_marks, flattened_marks - @observe('default_to_overwrite', 'dataset_selected') - def _set_default_results_label(self, event={}): + @observe('dataset_selected', 'flux_origin_selected') + def _set_default_label(self, event={}): '''Generate a label and set the results field to that value''' if not hasattr(self, 'dataset'): # pragma: no cover return - self.add_results.label_whitelist_overwrite = [self.dataset_selected] - - if self.default_to_overwrite: - self.results_label_default = self.dataset_selected + # TODO: have an option to create new data entry and drop other columns? + # (or should that just go through future data cloning) + self.flux_label.default = f"{self.flux_origin_selected}_flattened" + + @observe('flux_label_label', 'dataset') + def _update_label_valid(self, event={}): + if self.flux_label.value in self.flux_origin.choices: + self.flux_label.invalid_msg = '' + self.flux_label_overwrite = True + elif self.flux_label.value in getattr(self.dataset.selected_obj, 'columns', []): + self.flux_label.invalid_msg = 'name already in use' else: - self.results_label_default = f"{self.dataset_selected} (flattened)" + self.flux_label.invalid_msg = '' + self.flux_label_overwrite = False @with_spinner() def flatten(self, add_data=True): @@ -129,8 +151,8 @@ def flatten(self, add_data=True): Parameters ---------- add_data : bool - Whether to add the resulting trace to the application, according to the options - defined in the plugin. + Whether to add the resulting light curve as a flux column and select that as the new + flux origin for that data entry. Returns ------- @@ -157,9 +179,13 @@ def flatten(self, add_data=True): output_lc.meta['NORMALIZED'] = False if add_data: - # add data to the collection/viewer + # add data as a new flux and corresponding err columns in the existing data entry + # and select as flux origin data = _data_with_reftime(self.app, output_lc) - self.add_results.add_results_from_plugin(data) + self.flux_origin.add_new_flux_column(flux=data['flux'], + flux_err=data['flux_err'], + label=self.flux_label.value, + selected=True) return output_lc, trend_lc @@ -186,13 +212,16 @@ def _toggle_marks(self, event={}): # then the marks themselves need to be updated self._live_update(event) - @observe('dataset_selected', + @observe('dataset_selected', 'flux_origin_selected', 'window_length', 'polyorder', 'break_tolerance', 'niters', 'sigma', 'previews_temp_disable') @skip_if_no_updates_since_last_active() def _live_update(self, event={}): if self.previews_temp_disable: return + if self.dataset_selected == '' or self.flux_origin_selected == '': + self._clear_marks() + return start = time() try: @@ -232,7 +261,3 @@ def vue_apply(self, *args, **kwargs): self.flatten_err = str(e) else: self.flatten_err = '' - if self.add_results.label_overwrite: - # then this will change the input data without triggering a - # change to dataset_selected - self._live_update() diff --git a/lcviz/plugins/flatten/flatten.vue b/lcviz/plugins/flatten/flatten.vue index b0e503a9..a71bed3f 100644 --- a/lcviz/plugins/flatten/flatten.vue +++ b/lcviz/plugins/flatten/flatten.vue @@ -33,14 +33,6 @@ persistent-hint > - - - @@ -138,6 +130,14 @@ Live preview is unnormalized, but flattening will normalize. + + Live-updating is temporarily disabled (last update took {{last_live_time}}s) @@ -156,21 +156,17 @@ - + + + + Flatten{{flux_label_overwrite ? ' (Overwrite)' : ''}} + + + diff --git a/lcviz/plugins/flux_origin/flux_origin.py b/lcviz/plugins/flux_origin/flux_origin.py index 964f44e6..e6386495 100644 --- a/lcviz/plugins/flux_origin/flux_origin.py +++ b/lcviz/plugins/flux_origin/flux_origin.py @@ -1,15 +1,15 @@ -from traitlets import List, Unicode, observe - from jdaviz.core.registries import tray_registry from jdaviz.core.template_mixin import (PluginTemplateMixin, - DatasetSelectMixin, SelectPluginComponent) + DatasetSelectMixin) from jdaviz.core.user_api import PluginUserApi +from lcviz.components import FluxOriginSelectMixin + __all__ = ['FluxOrigin'] @tray_registry('flux-origin', label="Flux Origin") -class FluxOrigin(PluginTemplateMixin, DatasetSelectMixin): +class FluxOrigin(PluginTemplateMixin, FluxOriginSelectMixin, DatasetSelectMixin): """ See the :ref:`Flux Origin Plugin Documentation ` for more details. @@ -21,75 +21,10 @@ class FluxOrigin(PluginTemplateMixin, DatasetSelectMixin): """ template_file = __file__, "flux_origin.vue" - origin_items = List().tag(sync=True) - origin_selected = Unicode().tag(sync=True) - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.origin = SelectPluginComponent(self, - items='origin_items', - selected='origin_selected') - @property def user_api(self): - expose = ['dataset', 'origin'] + expose = ['dataset', 'flux_origin'] return PluginUserApi(self, expose=expose) - - @observe('dataset_selected') - def _on_change_dataset(self, *args): - def _include_col(lk_obj, col): - if col == 'flux' and lk_obj.meta.get('FLUX_ORIGIN') != 'flux': - # this is the currently active column (and should be copied elsewhere unless) - return False - if col in ('time', 'cadn', 'cadenceno', 'quality'): - return False - if col.startswith('phase:'): - # internal jdaviz ephemeris phase columns - return False - if col.startswith('time'): - return False - if col.startswith('centroid'): - return False - if col.startswith('cbv'): - # cotrending basis vector - return False - if col.endswith('_err'): - return False - if col.endswith('quality'): - return False - # TODO: need to think about flatten losing units in the flux column (and that other - # columns still exist but are not flattened) - return lk_obj[col].unit == lk_obj['flux'].unit - - lk_obj = self.dataset.selected_obj - if lk_obj is None: - return - self.origin.choices = [col for col in lk_obj.columns if _include_col(lk_obj, col)] - flux_origin = lk_obj.meta.get('FLUX_ORIGIN') - if flux_origin in self.origin.choices: - self.origin.selected = flux_origin - else: - self.origin.selected = '' - - @observe('origin_selected') - def _on_change_origin(self, *args): - if self.origin_selected == '': - return - - dc_item = self.dataset.selected_dc_item - old_flux_origin = dc_item.meta.get('FLUX_ORIGIN') - if self.origin.selected == old_flux_origin: - # nothing to do here! - return - - # instead of using lightkurve's select_flux and having to reparse the data entry, we'll - # manipulate the arrays in the data-collection directly, and modify FLUX_ORIGIN so that - # exporting back to a lightkurve object works as expected - dc_item = self.dataset.selected_dc_item - - self.app._jdaviz_helper._set_data_component(dc_item, 'flux', dc_item[self.origin.selected]) - dc_item.meta['FLUX_ORIGIN'] = self.origin.selected - # need to clear the cache manually due to the change in metadata made to the data-collection - # entry - self.dataset._clear_cache('selected_obj') diff --git a/lcviz/plugins/flux_origin/flux_origin.vue b/lcviz/plugins/flux_origin/flux_origin.vue index 9ae32805..f8dd95a7 100644 --- a/lcviz/plugins/flux_origin/flux_origin.vue +++ b/lcviz/plugins/flux_origin/flux_origin.vue @@ -19,8 +19,8 @@