Source code for astroquery.ipac.irsa.ibe.core

# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
IBE
===

API from

 https://irsa.ipac.caltech.edu/ibe/
"""

import os
import webbrowser
from bs4 import BeautifulSoup

import astropy.coordinates as coord
from astropy.table import Table

from astroquery.exceptions import InvalidQueryError
from astroquery.query import BaseQuery
from astroquery.utils import commons
from astroquery.ipac.irsa.ibe import conf

__all__ = ['Ibe', 'IbeClass']


[docs] class IbeClass(BaseQuery): URL = conf.server MISSION = conf.mission DATASET = conf.dataset TABLE = conf.table TIMEOUT = conf.timeout
[docs] def query_region(self, *, coordinate=None, where=None, mission=None, dataset=None, table=None, columns=None, width=None, height=None, intersect='OVERLAPS', most_centered=False): """ For certain missions, this function can be used to search for image and catalog files based on a point, a box (bounded by great circles) and/or an SQL-like ``where`` clause. If ``coordinates`` is specified, then the optional ``width`` and ``height`` arguments control the width and height of the search box. If neither ``width`` nor ``height`` are provided, then the search area is a point. If only one of ``width`` or ``height`` are specified, then the search area is a square with that side length centered at the coordinate. Parameters ---------- coordinate : str, `astropy.coordinates` object Gives the position of the center of the box if performing a box search. If it is a string, then it must be a valid argument to `~astropy.coordinates.SkyCoord`. Required if ``where`` is absent. where : str SQL-like query string. Required if ``coordinates`` is absent. mission : str The mission to be used (if not the default mission). dataset : str The dataset to be used (if not the default dataset). table : str The table to be queried (if not the default table). columns : str, list A space-separated string or a list of strings of the names of the columns to return. width : str or `~astropy.units.Quantity` object Width of the search box if ``coordinates`` is present. The string must be parsable by `~astropy.coordinates.Angle`. The appropriate `~astropy.units.Quantity` object from `astropy.units` may also be used. height : str, `~astropy.units.Quantity` object Height of the search box if ``coordinates`` is present. The string must be parsable by `~astropy.coordinates.Angle`. The appropriate `~astropy.units.Quantity` object from `astropy.units` may also be used. intersect : ``'COVERS'``, ``'ENCLOSED'``, ``'CENTER'``, ``'OVERLAPS'`` Spatial relationship between search box and image footprint. ``'COVERS'``: X must completely contain S. Equivalent to ``'CENTER'`` and ``'OVERLAPS'`` if S is a point. ``'ENCLOSED'``: S must completely contain X. If S is a point, the query will always return an empty image table. ``'CENTER'``: X must contain the center of S. If S is a point, this is equivalent to ``'COVERS'`` and ``'OVERLAPS'``. ``'OVERLAPS'``: The intersection of S and X is non-empty. If S is a point, this is equivalent to ``'CENTER'`` and ``'COVERS'``. most_centered : bool If True, then only the most centered image is returned. Returns ------- table : `~astropy.table.Table` A table containing the results of the query """ response = self.query_region_async( coordinate=coordinate, where=where, mission=mission, dataset=dataset, table=table, columns=columns, width=width, height=height, intersect=intersect, most_centered=most_centered) # Raise exception, if request failed response.raise_for_status() return Table.read(response.text, format='ipac', guess=False)
[docs] def query_region_sia(self, *, coordinate=None, mission=None, dataset=None, table=None, width=None, height=None, intersect='OVERLAPS', most_centered=False): """ Query using simple image access protocol. See ``query_region`` for details. The returned table will include a list of URLs. """ response = self.query_region_async( coordinate=coordinate, mission=mission, dataset=dataset, table=table, width=width, height=height, intersect=intersect, most_centered=most_centered, action='sia') # Raise exception, if request failed response.raise_for_status() return commons.parse_votable( response.text).get_first_table().to_table()
[docs] def query_region_async(self, *, coordinate=None, where=None, mission=None, dataset=None, table=None, columns=None, width=None, height=None, action='search', intersect='OVERLAPS', most_centered=False): """ For certain missions, this function can be used to search for image and catalog files based on a point, a box (bounded by great circles) and/or an SQL-like ``where`` clause. If ``coordinates`` is specified, then the optional ``width`` and ``height`` arguments control the width and height of the search box. If neither ``width`` nor ``height`` are provided, then the search area is a point. If only one of ``width`` or ``height`` are specified, then the search area is a square with that side length centered at the coordinate. Parameters ---------- coordinate : str, `astropy.coordinates` object Gives the position of the center of the box if performing a box search. If it is a string, then it must be a valid argument to `~astropy.coordinates.SkyCoord`. Required if ``where`` is absent. where : str SQL-like query string. Required if ``coordinates`` is absent. mission : str The mission to be used (if not the default mission). dataset : str The dataset to be used (if not the default dataset). table : str The table to be queried (if not the default table). columns : str, list A space-separated string or a list of strings of the names of the columns to return. width : str or `~astropy.units.Quantity` object Width of the search box if ``coordinates`` is present. The string must be parsable by `~astropy.coordinates.Angle`. The appropriate `~astropy.units.Quantity` object from `astropy.units` may also be used. height : str, `~astropy.units.Quantity` object Height of the search box if ``coordinates`` is present. The string must be parsable by `~astropy.coordinates.Angle`. The appropriate `~astropy.units.Quantity` object from `astropy.units` may also be used. intersect : ``'COVERS'``, ``'ENCLOSED'``, ``'CENTER'``, ``'OVERLAPS'`` Spatial relationship between search box and image footprint. ``'COVERS'``: X must completely contain S. Equivalent to ``'CENTER'`` and ``'OVERLAPS'`` if S is a point. ``'ENCLOSED'``: S must completely contain X. If S is a point, the query will always return an empty image table. ``'CENTER'``: X must contain the center of S. If S is a point, this is equivalent to ``'COVERS'`` and ``'OVERLAPS'``. ``'OVERLAPS'``: The intersection of S and X is non-empty. If S is a point, this is equivalent to ``'CENTER'`` and ``'COVERS'``. most_centered : bool If True, then only the most centered image is returned. action : ``'search'``, ``'data'``, or ``'sia'`` The action to perform at the server. The default is ``'search'``, which returns a table of the available data. ``'data'`` requires advanced path construction that is not yet supported. ``'sia'`` provides access to the 'simple image access' IVOA protocol Returns ------- response : `~requests.Response` The HTTP response returned from the service """ if coordinate is None and where is None: raise InvalidQueryError( 'At least one of `coordinate` or `where` is required') intersect = intersect.upper() if intersect not in ('COVERS', 'ENCLOSED', 'CENTER', 'OVERLAPS'): raise InvalidQueryError( "Invalid value for `intersects` " + "(must be 'COVERS', 'ENCLOSED', 'CENTER', or 'OVERLAPS')") if action not in ('sia', 'data', 'search'): raise InvalidQueryError("Valid actions are: sia, data, search.") if action == 'data': raise NotImplementedError( "The action='data' option is a placeholder for future " + "functionality.") args = { 'INTERSECT': intersect } # Note: in IBE, if 'mcen' argument is present, it is true. # If absent, it is false. if most_centered: args['mcen'] = '1' if coordinate is not None: c = commons.parse_coordinates(coordinate).transform_to(coord.ICRS) args['POS'] = '{0},{1}'.format(c.ra.deg, c.dec.deg) if width and height: args['SIZE'] = '{0},{1}'.format( coord.Angle(width).value, coord.Angle(height).value) elif width or height: args['SIZE'] = str(coord.Angle(width or height).value) if where: args['where'] = where if columns: if isinstance(columns, str): columns = columns.split() args['columns'] = ','.join(columns) url = "{URL}{action}/{mission}/{dataset}/{table}".format( URL=self.URL, action=action, mission=mission or self.MISSION, dataset=dataset or self.DATASET, table=table or self.TABLE) return self._request('GET', url, args, timeout=self.TIMEOUT)
[docs] def list_missions(self, *, cache=True): """ Return a list of the available missions Parameters ---------- cache : bool Defaults to True. If set overrides global caching behavior. See :ref:`caching documentation <astroquery_cache>`. """ if hasattr(self, '_missions') and cache: # extra level caching to avoid redoing the BeautifulSoup parsing # unnecessarily missions = self._missions else: url = self.URL + "search/" response = self._request('GET', url, timeout=self.TIMEOUT, cache=cache) root = BeautifulSoup(response.text, 'html5lib') links = root.findAll('a') missions = [os.path.basename(a.attrs['href'].rstrip('/')) for a in links] self._missions = missions return missions
[docs] def list_datasets(self, *, mission=None, cache=True): """ For a given mission, list the available datasets Parameters ---------- mission : str A mission name. Must be one of the valid missions from `~astroquery.ipac.irsa.ibe.IbeClass.list_missions`. Defaults to the configured Mission cache : bool Defaults to True. If set overrides global caching behavior. See :ref:`caching documentation <astroquery_cache>`. Returns ------- datasets : list A list of dataset names """ if mission is None: mission = self.MISSION if mission not in self.list_missions(): raise ValueError("Invalid mission specified: {0}." "Must be one of: {1}" .format(mission, self.list_missions())) url = "{URL}search/{mission}/".format(URL=self.URL, mission=mission) response = self._request('GET', url, timeout=self.TIMEOUT, cache=cache) root = BeautifulSoup(response.text, 'html5lib') links = root.findAll('a') datasets = [a.text for a in links if a.attrs['href'].count('/') >= 4 # shown as '..'; ignore ] return datasets
[docs] def list_tables(self, *, mission=None, dataset=None, cache=True): """ For a given mission and dataset (see `~astroquery.ipac.irsa.ibe.IbeClass.list_missions`, `~astroquery.ipac.irsa.ibe.IbeClass.list_datasets`), returns the list of valid table names to query. Parameters ---------- mission : str A mission name. Must be one of the valid missions from `~astroquery.ipac.irsa.ibe.IbeClass.list_missions`. Defaults to the configured Mission dataset : str A dataset name. Must be one of the valid dataset from ``list_datsets(mission)``. Defaults to the configured Dataset cache : bool Defaults to True. If set overrides global caching behavior. See :ref:`caching documentation <astroquery_cache>`. Returns ------- tables : list A list of table names """ if mission is None: mission = self.MISSION if dataset is None: dataset = self.DATASET if mission not in self.list_missions(): raise ValueError("Invalid mission specified: {0}." "Must be one of: {1}" .format(mission, self.list_missions())) if dataset not in self.list_datasets(mission=mission, cache=cache): raise ValueError("Invalid dataset {0} specified for mission {1}." "Must be one of: {2}" .format(dataset, mission, self.list_datasets(mission=mission, cache=True))) url = "{URL}search/{mission}/{dataset}/".format(URL=self.URL, mission=mission, dataset=dataset) response = self._request('GET', url, timeout=self.TIMEOUT, cache=cache) root = BeautifulSoup(response.text, 'html5lib') return [tr.find('td').string for tr in root.findAll('tr')[1:]]
# Unfortunately, the URL construction for each data set is different, and # they're not obviously accessible via API # def get_data(self, **kwargs): # return self.query_region_async(retrieve_data=True, **kwargs)
[docs] def show_docs(self, *, mission=None, dataset=None, table=None): """ Open the documentation for a given table in a web browser. Parameters ---------- mission : str The mission to be used (if not the default mission). dataset : str The dataset to be used (if not the default dataset). table : str The table to be queried (if not the default table). """ url = "{URL}docs/{mission}/{dataset}/{table}".format( URL=self.URL, mission=mission or self.MISSION, dataset=dataset or self.DATASET, table=table or self.TABLE) return webbrowser.open(url)
[docs] def get_columns(self, *, mission=None, dataset=None, table=None): """ Get the schema for a given table. Parameters ---------- mission : str The mission to be used (if not the default mission). dataset : str The dataset to be used (if not the default dataset). table : str The table to be queried (if not the default table). Returns ------- table : `~astropy.table.Table` A table containing a description of the columns """ url = "{URL}search/{mission}/{dataset}/{table}".format( URL=self.URL, mission=mission or self.MISSION, dataset=dataset or self.DATASET, table=table or self.TABLE) response = self._request( 'GET', url, {'FORMAT': 'METADATA'}, timeout=self.TIMEOUT) # Raise exception, if request failed response.raise_for_status() return Table.read(response.text, format='ipac', guess=False)
Ibe = IbeClass()