Skip to content

Commit

Permalink
Stitch plugin (#107)
Browse files Browse the repository at this point in the history
* stitch plugin: basic implementation
* only shown in plugin tray if at least two light curves are loaded into a viewer
* optionally replace input datasets when stitching
  • Loading branch information
kecnry committed Apr 22, 2024
1 parent 3afe010 commit 8196447
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

* Default data labels no longer include flux-origin, but do include quarter/campaign/sector. [#111]

* Basic stitch plugin to combine light curves into a single entry. [#107]

0.3.1 (unreleased)
------------------

Expand Down
41 changes: 41 additions & 0 deletions docs/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,47 @@ The time selector plugin allows defining the time indicated in all light curve v



.. _stitch:

Stitch
======

This plugin allows for combining multiple light curves into a single entry. Note that this plugin
is only available if there are at least two light curves loaded into a light curve viewer.

.. admonition:: User API Example
:class: dropdown

See the :class:`~lcviz.plugins.stitch.stitch.Stitch` user API documentation for more details.

.. code-block:: python
from lcviz import LCviz
lc1 = search_lightcurve("HAT-P-11", mission="Kepler",
cadence="long", quarter=9).download()
lc2 = search_lightcurve("HAT-P-11", mission="Kepler",
cadence="long", quarter=10).download()
lcviz = LCviz()
lcviz.load_data(lc1, 'lc1')
lcviz.load_data(lc2, 'lc2')
# NOTE: this line is not technically considered public API - alternatively manually add
# the second light curve to the light curve viewer from the data menu
lcviz.app.add_data_to_viewer('flux-vs-time', 'lc1')
lcviz.show()
stitch = lcviz.plugins['Stitch']
stitch.open_in_tray()
stitch.dataset.select_all()
stitched_lc = stitch.stitch()
print(stitched_lc)
.. seealso::

This plugin uses the following ``lightkurve`` implementations:

* :meth:`lightkurve.LightCurveCollection.stitch`


.. _flatten:

Flatten
Expand Down
2 changes: 1 addition & 1 deletion lcviz/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class LCviz(ConfigHelper):
'tray': ['lcviz-metadata-viewer', 'flux-column',
'lcviz-plot-options', 'lcviz-subset-plugin',
'lcviz-markers', 'time-selector',
'flatten', 'frequency-analysis', 'ephemeris',
'stitch', 'flatten', 'frequency-analysis', 'ephemeris',
'binning', 'lcviz-export'],
'viewer_area': [{'container': 'col',
'children': [{'container': 'row',
Expand Down
1 change: 1 addition & 0 deletions lcviz/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
from .time_selector.time_selector import * # noqa
from .metadata_viewer.metadata_viewer import * # noqa
from .plot_options.plot_options import * # noqa
from .stitch.stitch import * # noqa
from .subset_plugin.subset_plugin import * # noqa
1 change: 1 addition & 0 deletions lcviz/plugins/stitch/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .stitch import * # noqa
96 changes: 96 additions & 0 deletions lcviz/plugins/stitch/stitch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from traitlets import Bool, Unicode, observe
from lightkurve import LightCurve, LightCurveCollection

from jdaviz.core.registries import tray_registry
from jdaviz.core.template_mixin import (PluginTemplateMixin,
DatasetMultiSelectMixin,
AddResultsMixin,
with_spinner)
from jdaviz.core.user_api import PluginUserApi

from lcviz.utils import data_not_folded

__all__ = ['Stitch']


@tray_registry('stitch', label="Stitch")
class Stitch(PluginTemplateMixin, DatasetMultiSelectMixin, AddResultsMixin):
"""
See the :ref:`Stitch Plugin Documentation <stitch>` 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`
* ``dataset`` (:class:`~jdaviz.core.template_mixin.DatasetSelect`):
Datasets to stitch.
* ``remove_input_datasets``
* ``add_results`` (:class:`~jdaviz.core.template_mixin.AddResults`)
* :meth:`stitch`
"""
template_file = __file__, "stitch.vue"
uses_active_status = Bool(False).tag(sync=False)

remove_input_datasets = Bool(False).tag(sync=True)
stitch_err = Unicode().tag(sync=True)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.dataset.multiselect = True
# do not support stitching data in phase-space
self.dataset.add_filter(data_not_folded)

self.results_label_default = 'stitched'

@property
def user_api(self):
expose = ['dataset', 'stitch', 'remove_input_datasets', 'add_results']
return PluginUserApi(self, expose=expose)

@observe('dataset_items')
def _set_relevent(self, *args):
if len(self.dataset_items) < 2:
self.irrelevant_msg = 'Requires at least two datasets loaded into viewers'
else:
self.irrelevant_msg = ''

@with_spinner()
def stitch(self, add_data=True):
"""
Stitch multiple light curves (``dataset``) together using lightkurve.stitch.
Parameters
----------
add_data : bool
Whether to add the resulting light curve to the app.
Returns
-------
output_lc : `~lightkurve.LightCurve`
The flattened light curve.
"""
if not self.dataset.multiselect:
raise ValueError("dataset must be in multiselect mode")
if len(self.dataset.selected) < 2:
raise ValueError("multiple datasets must be selected")
lcc = LightCurveCollection([dci.get_object(LightCurve)
for dci in self.dataset.selected_dc_item])
stitched_lc = lcc.stitch(corrector_func=lambda x: x)

if add_data:
self.add_results.add_results_from_plugin(stitched_lc)
if self.remove_input_datasets:
for dataset in self.dataset.selected:
self.app.vue_data_item_remove({'item_name': dataset})
return stitched_lc

def vue_apply(self, *args, **kwargs):
try:
self.stitch(add_data=True)
except Exception as e:
self.stitch_err = str(e)
else:
self.stitch_err = ''
55 changes: 55 additions & 0 deletions lcviz/plugins/stitch/stitch.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<j-tray-plugin
description='Stitch light curves together.'
:link="'https://lcviz.readthedocs.io/en/'+vdocs+'/plugins.html#stitch'"
:popout_button="popout_button">

<plugin-dataset-select
:items="dataset_items"
:selected.sync="dataset_selected"
:show_if_single_entry="true"
:multiselect="true"
label="Data"
hint="Select the light curves as input."
/>

<v-row v-if="dataset_selected.length < 2">
<span class="v-messages v-messages__message text--secondary">
<b style="color: red !important">Must select at least two input light curves to stitch.</b>
</span>
</v-row>

<plugin-add-results v-else
:label.sync="results_label"
:label_default="results_label_default"
:label_auto.sync="results_label_auto"
:label_invalid_msg="results_label_invalid_msg"
:label_overwrite="results_label_overwrite"
label_hint="Label for the binned data."
:add_to_viewer_items="add_to_viewer_items"
:add_to_viewer_selected.sync="add_to_viewer_selected"
action_label="Stitch"
action_tooltip="Stitch data"
:action_disabled="dataset_selected.length < 2"
:action_spinner="spinner"
@click:action="apply"
>
<v-row>
<v-switch
v-model="remove_input_datasets"
label="Remove input datasets"
hint='Delete input datasets from the app'
persistent-hint
>
</v-switch>
</v-row>
</plugin-add-results>

<v-row v-if="stitch_err">
<span class="v-messages v-messages__message text--secondary">
<b style="color: red !important">ERROR:</b> {{stitch_err}}
</span>
</v-row>

</j-tray-plugin>
</template>
30 changes: 30 additions & 0 deletions lcviz/tests/test_plugin_stitch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
def test_docs_snippets(helper, light_curve_like_kepler_quarter):
lcviz, lc = helper, light_curve_like_kepler_quarter
lc1 = lc
lc2 = lc.copy()

lcviz.load_data(lc1, 'lc1')
lcviz.load_data(lc2, 'lc2')
lcviz.app.add_data_to_viewer('flux-vs-time', 'lc2')
# lcviz.show()

stitch = lcviz.plugins['Stitch']
stitch.open_in_tray()
stitch.dataset.select_all()
stitched_lc = stitch.stitch()
print(stitched_lc)


def test_plugin_stitch(helper, light_curve_like_kepler_quarter):
helper.load_data(light_curve_like_kepler_quarter)

assert "Stitch" not in helper.plugins.keys()

helper.load_data(light_curve_like_kepler_quarter.copy())
assert "Stitch" in helper.plugins.keys()

stitch = helper.plugins['Stitch']
stitch.dataset.select_all()
stitched_lc = stitch.stitch()

assert len(stitched_lc) == 2 * len(light_curve_like_kepler_quarter)

0 comments on commit 8196447

Please sign in to comment.