Skip to content

Commit

Permalink
Migrate VO plugin to Jdaviz
Browse files Browse the repository at this point in the history
  • Loading branch information
duytnguyendtn committed May 10, 2024
1 parent f985a7f commit 5a5ed67
Show file tree
Hide file tree
Showing 5 changed files with 214 additions and 0 deletions.
1 change: 1 addition & 0 deletions jdaviz/configs/default/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
from .plot_options.plot_options import * # noqa
from .markers.markers import * # noqa
from .data_quality.data_quality import * # noqa
from .virtual_observatory import * # noqa
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .vo_plugin import * # noqa
140 changes: 140 additions & 0 deletions jdaviz/configs/default/plugins/virtual_observatory/vo_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
from astropy.coordinates import SkyCoord
from astropy.io import fits
from astropy import units as u
from pyvo.utils import vocabularies
from pyvo import registry
from traitlets import Dict, Bool, Unicode, Any, List, Int

from jdaviz.core.events import SnackbarMessage
from jdaviz.core.registries import tray_registry
from jdaviz.core.template_mixin import PluginTemplateMixin, AddResultsMixin, TableMixin

__all__ = ['VoPlugin']


@tray_registry('VoPlugin', label="Virtual Observatory")
class VoPlugin(PluginTemplateMixin, AddResultsMixin, TableMixin):
""" Plugin to query the Virtual Observatory and load data into Imviz """
template_file = __file__, "vo_plugin.vue"

wavebands = List().tag(sync=True)
resources = List([]).tag(sync=True)
resources_loading = Bool(False).tag(sync=True)

source = Unicode().tag(sync=True)
radius_deg = Int(1).tag(sync=True)

results_loading = Bool(False).tag(sync=True)
data_loading = Bool(False).tag(sync=True)

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

# Waveband properties to filter available registry resources
self.wavebands = [w.lower() for w in vocabularies.get_vocabulary("messenger")["terms"]]
self.waveband_selected = None

self._full_registry_results = None
self.resource_selected = None

self.table.headers_avail = ["Title", "Instrument", "DateObs", "URL"]
self.table.headers_visible = ["Title", "Instrument", "DateObs"]

self.table.show_rowselect = True
self.table.item_key = "URL"

def vue_waveband_selected(self,event):
""" Sync waveband selected
When the user selects a waveband, query Virtual Observatory registry
for all SIA services that serve data in that waveband. Then update
the dropdown accordingly.
"""
self.waveband_selected = event
# Clear existing resources list
self.resources = []
self.resource_selected = None
self.resources_loading = True # Start loading bar
try:
if event is not None:
self._full_registry_results = registry.search(registry.Servicetype("sia"), registry.Waveband(self.waveband_selected))
self.resources = list(self._full_registry_results.getcolumn("short_name"))
except Exception:
# TODO: Catch connection error
raise
finally:
self.resources_loading = False # Stop loading bar

def vue_resource_selected(self, event):
"""Sync IVOA resource selected"""
self.resource_selected = event

def vue_query_resource(self, *args, **kwargs):
"""
Once a specific VO resource is selected, query it with the user-specified source target.
User input for source is first attempted to be parsed as a SkyCoord coordinate. If not,
then attempts to parse as a target name.
"""
self.table.items = []
self.results_loading = True # Start loading spinner
try:
# Query SIA service
# Service is indexed via short name (resource_selected), which is the suggested way
# according to PyVO docs. Though disclaimer that collisions COULD occur. If so,
# consider indexing on the full IVOID, which is guaranteed unique.
sia_service = self._full_registry_results[self.resource_selected].get_service(service_type="sia")
try:
# First parse user-provided source as direct coordinates
coord = SkyCoord(self.source)
except:
try:
# If that didn't work, try parsing it as an object name
coord = SkyCoord.from_name(self.source)
except:
raise LookupError(f"Unable to resolve source coordinates: {self.source}")

# Once coordinate lookup is complete, search service using these coords.
sia_results = sia_service.search(
coord,
size=((self.radius_deg * u.deg) if self.radius_deg > 0 else None),
format='image/fits')
if len(sia_results) == 0:
self.hub.broadcast(SnackbarMessage(
f"No observations returned at coords {coord} from VO SIA resource: {sia_service.SOMETHING}", sender=self, color="error"))
else:
self.hub.broadcast(SnackbarMessage(
f"{len(sia_results)} SIA results found!", sender=self, color="success"))
except Exception as e:
self.hub.broadcast(SnackbarMessage(
f"Unable to locate files for source {self.source}: {e}", sender=self, color="error"))
raise
finally:
self.results_loading = False # Stop loading spinner

try:
for result in sia_results:
self.table.add_item({"Title": str(result.title),
"URL": str(result.getdataurl()),
"Instrument": str(result.instr),
"DateObs": str(result.dateobs)})
self.hub.broadcast(SnackbarMessage(
f"{len(sia_results)} SIA results populated!", sender=self, color="success"))
except Exception as e:
self.hub.broadcast(SnackbarMessage(
f"Unable to populate table for source {self.source}: {e}", sender=self, color="error"))
raise

def vue_load_selected_data(self,event):
"""Load the files selected by the user in the table"""
self.data_loading = True # Start loading spinner
for entry in self.table.selected_rows:
try:
self.app._jdaviz_helper.load_data(
fits.open(str(entry["URL"])), # Open URL as FITS object
data_label=f"{self.source}_{self.resource_selected}_{entry['Title']}")
except Exception as e:
self.hub.broadcast(SnackbarMessage(
f"Unable to load file to viewer: {entry['URL']}: {e}", sender=self, color="error"))
# Clear selected entries' checkboxes on table
self.table.selected_rows = []
self.data_loading = False # Stop loading spinner
71 changes: 71 additions & 0 deletions jdaviz/configs/default/plugins/virtual_observatory/vo_plugin.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<template>
<j-tray-plugin
description='Download a data product from the Virtual Observatory'
:link="'https://www.ivoa.net/astronomers/index.html'"
:popout_button="popout_button">

<j-plugin-section-header>Survey Collections</j-plugin-section-header>
<v-row>
<v-select
:menu-props="{ left: true }"
attach
:items="wavebands"
@change="waveband_selected"
label="Resource Waveband"
hint="Select a spectral waveband to filter your surveys"
persistent-hint
></v-select>
</v-row>

<v-row>
<v-select
:menu-props="{ left: true }"
attach
:items="resources"
:loading="resources_loading"
@change="resource_selected"
label="Available Resources"
hint="Select a SIA resource to query"
persistent-hint
></v-select>
</v-row>

<j-plugin-section-header>Source Selection</j-plugin-section-header>

<v-row>
<v-text-field
v-model="source"
label="Source or Coordinates"
hint="Enter a source name or Coordinates to center your query on"
persistent-hint>
</v-text-field>
</v-row>

<j-plugin-section-header>Common Options</j-plugin-section-header>

<v-row>
<v-text-field
v-model.number="radius_deg"
type="number"
label="Radius"
hint="Angular radius of the specified field in degrees"
persistent-hint>
</v-text-field>
</v-row>

<v-row class="row-no-outside-padding">
<v-col>
<v-btn color="primary" :loading="results_loading" text @click="query_resource">Query Archive</v-btn>
</v-col>
</v-row>

<jupyter-widget :widget="table_widget"></jupyter-widget>

<v-row class="row-no-outside-padding">
<v-col>
<v-btn color="primary" :loading="data_loading" text @click="load_selected_data">Load Data</v-btn>
</v-col>
</v-row>

</j-tray-plugin>
</template>
1 change: 1 addition & 0 deletions jdaviz/configs/imviz/imviz.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ toolbar:
tray:
- g-metadata-viewer
- imviz-orientation
- VoPlugin
- g-plot-options
- g-data-quality
- g-subset-plugin
Expand Down

0 comments on commit 5a5ed67

Please sign in to comment.