diff --git a/astroquery/esasky/__init__.py b/astroquery/esasky/__init__.py new file mode 100644 index 0000000000..9cd769f256 --- /dev/null +++ b/astroquery/esasky/__init__.py @@ -0,0 +1,25 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from astropy import config as _config + + +class Conf(_config.ConfigNamespace): + """ + Configuration parameters for `astroquery.esasky`. + """ + urlBase = _config.ConfigItem( + 'http://sky.esa.int/esasky-tap', + 'ESASky base URL') + + timeout = _config.ConfigItem( + 1000, + 'Time limit for connecting to template_module server.') + + +conf = Conf() + +from .core import ESASky, ESASkyClass + +__all__ = ['ESASky', 'ESASkyClass', + 'Conf', 'conf', + ] + diff --git a/astroquery/esasky/core.py b/astroquery/esasky/core.py new file mode 100644 index 0000000000..1e85db2236 --- /dev/null +++ b/astroquery/esasky/core.py @@ -0,0 +1,762 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from __future__ import print_function +import json +import os +import tempfile +import tarfile +import sys + +from astropy.extern import six +from astropy.io import fits +from astropy import log +import astropy.units +import astropy.io.votable as votable + +from ..query import BaseQuery +from ..utils import commons +from ..utils import async_to_sync +from . import conf +from ..exceptions import TableParseError + +@async_to_sync +class ESASkyClass(BaseQuery): + + URLbase = conf.urlBase + TIMEOUT = conf.timeout + + __FITS_STRING = ".fits" + __FTZ_STRING = ".FTZ" + __TAR_STRING = ".tar" + __ALL_STRING = "all" + __CATALOGS_STRING = "catalogs" + __OBSERVATIONS_STRING = "observations" + __MISSION_STRING = "mission" + __TAP_TABLE_STRING = "tapTable" + __TAP_NAME_STRING = "tapName" + __FILTER_STRING = "filter" + __LABEL_STRING = "label" + __OBSERVATION_ID_STRING = "observation_id" + __METADATA_STRING = "metadata" + __MAPS_STRING = "Maps" + __PRODUCT_URL_STRING = "product_url" + __SOURCE_LIMIT_STRING = "sourceLimit" + __POLYGON_NAME_STRING = "polygonNameTapColumn" + __POLYGON_RA_STRING = "polygonRaTapColumn" + __POLYGON_DEC_STRING = "polygonDecTapColumn" + __POS_TAP_STRING = "posTapColumn" + __ORDER_BY_STRING = "orderBy" + __IS_SURVEY_MISSION_STRING = "isSurveyMission" + __ZERO_ARCMIN_STRING = "0 arcmin" + __MIN_RADIUS_CATALOG_STRING = "5 arcsec" + + __HERSCHEL_STRING = 'herschel' + __HST_STRING = 'hst' + __INTEGRAL_STRING = 'integral' + + + def list_maps(self): + """ + Get a list of the mission names of the available observations in ESASky + """ + return self._json_object_field_to_list( + self._get_observation_json(cache = True), self.__MISSION_STRING) + + def list_catalogs(self): + """ + Get a list of the mission names of the available catalogs in ESASky + """ + return self._json_object_field_to_list( + self._get_catalogs_json(cache = True), self.__MISSION_STRING) + + def query_object_maps(self, position, missions=__ALL_STRING, + get_query_payload=False, cache=True): + """ + This method queries a chosen object or coordinate for all available maps + which have observation data on the chosen position. It returns a + TableList with all the found maps metadata for the chosen missions + and object. + + Parameters + ---------- + position : str or `astropy.coordinates` object + Can either be a string of the location, eg 'M51', or the coordinates + of the object. + missions : string or list, optional + Can be either a specific mission or a list of missions (all mission + names are found in list_missions()) or 'all' to search in all + missions. Defaults to 'all'. + get_query_payload : bool, optional + When set to True the method returns the HTTP request parameters. + Defaults to False. + cache : bool, optional + When set to True the method will use a cache located at + .astropy/astroquery/cache. Defaults to True. + + Returns + ------- + table_list : `astroquery.utils.TableList` + Each mission returns a `astroquery.table.Table` with the metadata + and observations available for the chosen missions and object. + It is structured in a TableList like this: + TableList with 8 tables: + '0:HERSCHEL' with 8 column(s) and 25 row(s) + '1:HST' with 8 column(s) and 735 row(s) + + Examples + -------- + query_object_maps("m101", "all") + + query_object_maps("265.05, 69.0", "Herschel") + query_object_maps("265.05, 69.0", ["Herschel", "HST"]) + """ + return self.query_region_maps(position, self.__ZERO_ARCMIN_STRING, missions, + get_query_payload, cache) + + def query_object_catalogs(self, position, catalogs=__ALL_STRING, + get_query_payload=False, cache=True): + """ + This method queries a chosen object or coordinate for all available + catalogs and returns a TableList with all the found catalogs metadata + for the chosen missions and object. To account for errors in telescope + position, the method will look for any sources within a radius of + 5 arcsec of the chosen position. + + Parameters + ---------- + position : str or `astropy.coordinates` object + Can either be a string of the location, eg 'M51', or the coordinates + of the object. + catalogs : string or list, optional + Can be either a specific catalog or a list of catalogs (all catalog + names are found in list_catalogs()) or 'all' to search in all + catalogs. Defaults to 'all'. + get_query_payload : bool, optional + When set to True the method returns the HTTP request parameters. + Defaults to False. + cache : bool, optional + When set to True the method will use a cache located at + .astropy/astroquery/cache. Defaults to True. + Returns + ------- + table_list : `astroquery.utils.TableList` + Each mission returns a `astroquery.table.Table` with the metadata + of the catalogs available for the chosen mission and object. + It is structured in a TableList like this: + TableList with 8 tables: + '0:Gaia DR1 TGA' with 8 column(s) and 25 row(s) + '1:HSC' with 8 column(s) and 75 row(s) + + Examples + -------- + query_object_catalogs("m101", "all") + + query_object_catalogs("265.05, 69.0", "Gaia DR1 TGA") + query_object_catalogs("265.05, 69.0", ["Gaia DR1 TGA", "HSC"]) + """ + return self.query_region_catalogs(position, self.__ZERO_ARCMIN_STRING, + catalogs, get_query_payload, cache) + + def query_region_maps(self, position, radius, missions=__ALL_STRING, + get_query_payload=False, cache=True): + """ + This method queries a chosen region for all available maps and returns a + TableList with all the found maps metadata for the chosen missions and + region. + + Parameters + ---------- + position : str or `astropy.coordinates` object + Can either be a string of the location, eg 'M51', or the coordinates + of the object. + radius : str or `~astropy.units.Quantity` + The radius of a region. + missions : string or list, optional + Can be either a specific mission or a list of missions (all mission + names are found in list_missions()) or 'all' to search in all + missions. Defaults to 'all'. + get_query_payload : bool, optional + When set to True the method returns the HTTP request parameters. + Defaults to False. + cache : bool, optional + When set to True the method will use a cache located at + .astropy/astroquery/cache. Defaults to True. + + Returns + ------- + table_list : `astroquery.utils.TableList` + Each mission returns a `astroquery.table.Table` with the metadata + and observations available for the chosen missions and region. + It is structured in a TableList like this: + TableList with 8 tables: + '0:HERSCHEL' with 8 column(s) and 25 row(s) + '1:HST' with 8 column(s) and 735 row(s) + + Examples + -------- + query_region_maps("m101", "14'", "all") + + import astropy.units as u + query_region_maps("265.05, 69.0", 14*u.arcmin, "Herschel") + query_region_maps("265.05, 69.0", ["Herschel", "HST"]) + """ + sanitized_position = self._sanitize_input_position(position) + sanitized_radius = self._sanitize_input_radius(radius) + sanitized_missions = self._sanitize_input_mission(missions) + + query_result = {} + + coordinates = commons.parse_coordinates(sanitized_position) + + self._store_query_result_maps(query_result, sanitized_missions, + coordinates, sanitized_radius, + get_query_payload, cache) + + if (get_query_payload): + return query_result + + return commons.TableList(query_result) + + def query_region_catalogs(self, position, radius, catalogs=__ALL_STRING, + get_query_payload=False, cache=True): + """ + This method queries a chosen region for all available catalogs and + returns a TableList with all the found catalogs metadata for the chosen + missions and region. + + Parameters + ---------- + position : str or `astropy.coordinates` object + Can either be a string of the location, eg 'M51', or the coordinates + of the object. + radius : str or `~astropy.units.Quantity` + The radius of a region. + catalogs : string or list, optional + Can be either a specific catalog or a list of catalogs (all catalog + names are found in list_catalogs()) or 'all' to search in all + catalogs. Defaults to 'all'. + get_query_payload : bool, optional + When set to True the method returns the HTTP request parameters. + Defaults to False. + cache : bool, optional + When set to True the method will use a cache located at + .astropy/astroquery/cache. Defaults to True. + + Returns + ------- + table_list : `astroquery.utils.TableList` + Each mission returns a `astroquery.table.Table` with the metadata of + the catalogs available + for the chosen mission and region. + It is structured in a TableList like this: + TableList with 8 tables: + '0:Gaia DR1 TGA' with 8 column(s) and 25 row(s) + '1:HSC' with 8 column(s) and 75 row(s) + + Examples + -------- + query_region_catalogs("m101", "14'", "all") + + import astropy.units as u + query_region_catalogs("265.05, 69.0", 14*u.arcmin, "Gaia DR1 TGA") + query_region_catalogs("265.05, 69.0", 14*u.arcmin, ["Gaia DR1 TGA", "HSC"]) + """ + sanitized_position = self._sanitize_input_position(position) + sanitized_radius = self._sanitize_input_radius(radius) + sanitized_catalogs = self._sanitize_input_catalogs(catalogs) + + coordinates = commons.parse_coordinates(sanitized_position) + + query_result = {} + + self._store_query_result_catalogs(query_result, sanitized_catalogs, + coordinates, sanitized_radius, + get_query_payload, cache) + + if (get_query_payload): + return query_result + + return commons.TableList(query_result) + + def get_maps(self, query_table_list, missions=__ALL_STRING, + download_directory=__MAPS_STRING, cache=True): + """ + This method takes the dictionary of missions and metadata as returned by + query_region_maps and downloads all maps to the selected folder. + The method returns a dictionary which is divided by mission. + All mission except Herschel returns a list of HDULists. + For Herschel each item in the list is a dictionary where the used + filter is the key and the HDUList is the value. + + Parameters + ---------- + query_table_list : `astroquery.utils.TableList` + A TableList with all the missions wanted and their respective + metadata. Usually the return value of query_region_maps. + missions : string or list, optional + Can be either a specific mission or a list of missions (all mission + names are found in list_missions()) or 'all' to search in all + missions. Defaults to 'all'. + download_directory : string, optional + The folder where all downloaded maps should be stored. + Defaults to a folder called 'Maps' in the current working directory. + cache : bool, optional + When set to True the method will use a cache located at + .astropy/astroquery/cache. Defaults to True. + + Returns + ------- + maps : `dict` + All mission except Herschel returns a list of HDULists. + For Herschel each item in the list is a dictionary where the used + filter is the key and the HDUList is the value. + It is structured in a dictionary like this: + dict: { + 'HERSCHEL': [{'70': [HDUList], ' 160': [HDUList]}, {'70': [HDUList], ' 160': [HDUList]}, ...], + 'HST':[[HDUList], HDUList], HDUList], HDUList], HDUList], ...], + 'XMM-EPIC' : [HDUList], HDUList], HDUList], HDUList], ...] + ... + } + + Examples + -------- + get_maps(query_region_catalogs("m101", "14'", "all")) + + """ + sanitized_query_table_list = self._sanitize_input_table_list(query_table_list) + sanitized_missions = self._sanitize_input_mission(missions) + + maps = dict() + + for query_mission in sanitized_query_table_list.keys(): + for mission in sanitized_missions: + #INTEGRAL does not have a product url yet. + if (query_mission.lower() == self.__INTEGRAL_STRING): + print("INTEGRAL does not yet support downloading of " + "fits files") + break + if (query_mission.lower() == mission.lower()): + maps[query_mission] = ( + self._get_maps_for_mission( + sanitized_query_table_list[query_mission], + query_mission, + download_directory, + cache)) + break + if (len(sanitized_query_table_list) > 0): + log.info("Maps available at %s" %os.path.abspath(download_directory)) + else: + print("No maps found") + return maps + + def get_images(self, position, radius=__ZERO_ARCMIN_STRING, missions=__ALL_STRING, + download_directory=__MAPS_STRING, cache=True): + """ + This method gets the fits files available for the selected position and + mission and downloads all maps to the the selected folder. + The method returns a dictionary which is divided by mission. + All mission except Herschel returns a list of HDULists. + For Herschel each item in the list is a dictionary where the used + filter is the key and the HDUList is the value. + + Parameters + ---------- + position : str or `astropy.coordinates` object + Can either be a string of the location, eg 'M51', or the coordinates + of the object. + radius : str or `~astropy.units.Quantity`, optional + The radius of a region. Defaults to 0. + missions : string or list, optional + Can be either a specific mission or a list of missions (all mission + names are found in list_missions()) or 'all' to search in all + missions. Defaults to 'all'. + download_directory : string, optional + The folder where all downloaded maps should be stored. + Defaults to a folder called 'Maps' in the current working directory. + cache : bool, optional + When set to True the method will use a cache located at + .astropy/astroquery/cache. Defaults to True. + + Returns + ------- + maps : `dict` + All mission except Herschel returns a list of HDULists. + For Herschel each item in the list is a dictionary where the used + filter is the key and the HDUList is the value. + It is structured in a dictionary like this: + dict: { + 'HERSCHEL': [{'70': [HDUList], ' 160': [HDUList]}, {'70': [HDUList], ' 160': [HDUList]}, ...], + 'HST':[[HDUList], HDUList], HDUList], HDUList], HDUList], ...], + 'XMM-EPIC' : [HDUList], HDUList], HDUList], HDUList], ...] + ... + } + + Examples + -------- + get_images("m101", "14'", "all") + + """ + sanitized_position = self._sanitize_input_position(position) + sanitized_radius = self._sanitize_input_radius(radius) + sanitized_missions = self._sanitize_input_mission(missions) + + maps = dict() + + map_query_result = self.query_region_maps(sanitized_position, + sanitized_radius, + sanitized_missions, + get_query_payload=False, + cache=cache) + + for query_mission in map_query_result.keys(): + #INTEGRAL does not have a product url yet. + if (query_mission.lower() == self.__INTEGRAL_STRING): + print("INTEGRAL does not yet support downloading of " + "fits files") + continue + maps[query_mission] = ( + self._get_maps_for_mission( + map_query_result[query_mission], + query_mission, + download_directory, + cache)) + + print("Maps available at %s" %os.path.abspath(download_directory)) + return maps + + def _sanitize_input_position(self, position): + if (isinstance(position, str) or isinstance(position, + commons.CoordClasses)): + return position + else: + raise ValueError("Position must be either a string or " + "astropy.coordinates") + + def _sanitize_input_radius(self, radius): + if (isinstance(radius, str) or isinstance(radius, + astropy.units.Quantity)): + return radius + else: + raise ValueError("Radius must be either a string or " + "astropy.units.Quantity") + + def _sanitize_input_mission(self, missions): + if (isinstance(missions, list)): + return missions + if (isinstance(missions, str)): + if (missions.lower() == self.__ALL_STRING): + return self.list_maps() + else: + return [missions] + raise ValueError("Mission must be either a string or a list of " + "missions") + + def _sanitize_input_catalogs(self, catalogs): + if (isinstance(catalogs, list)): + return catalogs + if (isinstance(catalogs, str)): + if (catalogs.lower() == self.__ALL_STRING): + return self.list_catalogs() + else: + return [catalogs] + raise ValueError("Catalog must be either a string or a list of " + "catalogs") + + def _sanitize_input_table_list(self, table_list): + if (isinstance(table_list, commons.TableList)): + return table_list + raise ValueError("Query_table_list must be an astropy.utils.TableList") + + def _get_maps_for_mission(self, maps_table, mission, download_directory, cache): + maps = [] + + if (len(maps_table[self.__PRODUCT_URL_STRING]) > 0): + mission_directory = self._create_mission_directory(mission, + download_directory) + print("Starting download of %s data. (%d files)" + %(mission, len(maps_table[self.__PRODUCT_URL_STRING]))) + for index in range(len(maps_table)): + product_url = (maps_table[self.__PRODUCT_URL_STRING][index] + .decode('utf-8')) + observation_id = (maps_table[self.__OBSERVATION_ID_STRING][index] + .decode('utf-8')) + print("Downloading Observation ID: %s from %s" + %(observation_id, product_url), end=" ") + sys.stdout.flush() + directory_path = mission_directory + "/" + if (mission.lower() == self.__HERSCHEL_STRING): + herschel_filter = (maps_table[self.__FILTER_STRING][index] + .decode('utf-8').split(",")) + maps.append(self._get_herschel_observation(product_url, + directory_path, + herschel_filter, + cache)) + + else: + response = self._request('GET', product_url, cache = cache) + file_name = "" + if (product_url.endswith(self.__FITS_STRING)): + file_name = (directory_path + + self._extract_file_name_from_url(product_url)) + else: + file_name = (directory_path + + self._extract_file_name_from_response_header(response.headers)) + + fits_data = response.content + with open(file_name, 'wb') as fits_file: + fits_file.write(fits_data) + fits_file.close() + maps.append(fits.open(file_name)) + + print("[Done]") + print("Downloading of %s data complete." %mission) + + return maps + + def _get_herschel_observation(self, product_url, directory_path, filters, + cache): + observation = dict() + tar_file = tempfile.NamedTemporaryFile() + response = self._request('GET', product_url, cache = cache) + tar_file.write(response.content) + with tarfile.open(tar_file.name,'r') as tar: + i = 0 + for member in tar.getmembers(): + member_name = member.name.lower() + if ('hspire' in member_name or 'hpacs' in member_name): + tar.extract(member, directory_path) + member.name = ( + self._remove_extra_herschel_directory(member.name, + directory_path)) + observation[filters[i]] = fits.open(directory_path + + member.name) + i += 1 + return observation + + def _remove_extra_herschel_directory(self, file_and_directory_name, + directory_path): + full_directory_path = os.path.abspath(directory_path) + file_name = file_and_directory_name[file_and_directory_name.index("/") + 1:] + os.renames(os.path.join(full_directory_path, file_and_directory_name), + os.path.join(full_directory_path, file_name)) + return file_name + + def _create_mission_directory(self, mission, download_directory): + if (download_directory == self.__MAPS_STRING): + mission_directory = self.__MAPS_STRING + "/" + mission + else: + mission_directory = (download_directory + "/" + self.__MAPS_STRING + + "/" + mission) + if not os.path.exists(mission_directory): + os.makedirs(mission_directory) + return mission_directory + + def _extract_file_name_from_response_header(self, headers): + content_disposition = headers.get('Content-Disposition') + filename_string = "filename=" + start_index = (content_disposition.index(filename_string) + + len(filename_string)) + if (content_disposition[start_index] == '\"'): + start_index += 1 + + if (self.__FITS_STRING in content_disposition[start_index : ]): + end_index = ( + content_disposition.index(self.__FITS_STRING, start_index + 1) + + len(self.__FITS_STRING)) + return content_disposition[start_index : end_index] + elif (self.__FTZ_STRING in content_disposition[start_index : ]): + end_index = ( + content_disposition.index(self.__FTZ_STRING, start_index + 1) + + len(self.__FTZ_STRING)) + return content_disposition[start_index : end_index] + elif (self.__TAR_STRING in content_disposition[start_index : ]): + end_index = ( + content_disposition.index(self.__TAR_STRING, start_index + 1) + + len(self.__TAR_STRING)) + return content_disposition[start_index : end_index] + else: + raise ValueError("Could not find file name in header. " + "Content disposition: %s." %content_disposition) + + def _extract_file_name_from_url(self, product_url): + start_index = product_url.rindex("/") + 1 + return product_url[start_index:] + + def _query_region_maps(self, coordinates, radius, observation_name, + get_query_payload, cache): + observation_tap_name = ( + self._find_observation_tap_table_name(observation_name, cache)) + query = ( + self._build_observation_query(coordinates, radius, + self._find_observation_parameters(observation_tap_name, + cache))) + request_payload = self._create_request_payload(query) + if (get_query_payload): + return request_payload + return self._get_and_parse_from_tap(request_payload, cache) + + def _query_region_catalog(self, coordinates, radius, catalog_name, + get_query_payload, cache): + catalog_tap_name = self._find_catalog_tap_table_name(catalog_name, cache) + query = self._build_catalog_query(coordinates, radius, + self._find_catalog_parameters(catalog_tap_name, + cache)) + request_payload = self._create_request_payload(query) + if (get_query_payload): + return request_payload + return self._get_and_parse_from_tap(request_payload, cache) + + def _build_observation_query(self, coordinates, radius, json): + raHours, dec = commons.coord_to_radec(coordinates) + ra = raHours * 15.0 # Converts to degrees + radiusDeg = commons.radius_to_unit(radius, unit='deg') + + select_query = "SELECT DISTINCT " + + metadata = json[self.__METADATA_STRING] + metadata_tap_names = ", ".join(["%s" % entry[self.__TAP_NAME_STRING] + for entry in metadata]) + + from_query = " FROM %s" %json[self.__TAP_TABLE_STRING] + if (radiusDeg != 0 or json[self.__IS_SURVEY_MISSION_STRING]): + if (json[self.__IS_SURVEY_MISSION_STRING]): + area_or_point_string = "pos" + else: + area_or_point_string = "fov" + where_query = (" WHERE 1=CONTAINS(%s, CIRCLE('ICRS', %f, %f, %f));" + %(area_or_point_string, ra, dec, radiusDeg)) + else: + area_or_point_string = "fov" + where_query = (" WHERE 1=CONTAINS(POINT('ICRS', %f, %f), %s);" + %(ra, dec, area_or_point_string)) + + query = "".join([select_query, metadata_tap_names, from_query, + where_query]) + return query + + def _build_catalog_query(self, coordinates, radius, json): + raHours, dec = commons.coord_to_radec(coordinates) + ra = raHours * 15.0 # Converts to degrees + radiusDeg = commons.radius_to_unit(radius, unit='deg') + + select_query = "SELECT TOP %s " %json[self.__SOURCE_LIMIT_STRING] + + metadata = json[self.__METADATA_STRING] + metadata_tap_names = ", ".join(["%s" % entry[self.__TAP_NAME_STRING] + for entry in metadata]) + + from_query = " FROM %s" %json[self.__TAP_TABLE_STRING] + if (radiusDeg == 0): + where_query = (" WHERE 1=CONTAINS(%s, CIRCLE('ICRS', %f, %f, %f))" + %(json[self.__POS_TAP_STRING], ra, dec, + commons.radius_to_unit( + self.__MIN_RADIUS_CATALOG_STRING, unit='deg'))) + else: + where_query = (" WHERE 1=CONTAINS(%s, CIRCLE('ICRS', %f, %f, %f))" + %(json[self.__POS_TAP_STRING], ra, dec, radiusDeg)) + order_by_query = " ORDER BY %s;" %json[self.__ORDER_BY_STRING] + + query = "".join([select_query, metadata_tap_names, from_query, + where_query, order_by_query]) + + return query + + def _store_query_result_maps(self, query_result, missions, coordinates, + radius, get_query_payload, cache): + for mission in missions: + mission_table = self._query_region_maps(coordinates, radius, + mission, get_query_payload, + cache) + if (len(mission_table) > 0): + query_result[mission.upper()] = mission_table + + def _store_query_result_catalogs(self, query_result, catalogs, coordinates, + radius, get_query_payload, cache): + for catalog in catalogs: + catalog_table = self._query_region_catalog(coordinates, radius, + catalog, get_query_payload, + cache) + if (len(catalog_table) > 0): + query_result[catalog.upper()] = catalog_table + + def _find_observation_parameters(self, mission_name, cache): + return self._find_mission_parameters_in_json(mission_name, + self._get_observation_json(cache)) + + def _find_catalog_parameters(self, catalog_name, cache): + return self._find_mission_parameters_in_json(catalog_name, + self._get_catalogs_json(cache)) + + def _find_mission_parameters_in_json(self, mission_tap_name, json): + for mission in json: + if (mission[self.__TAP_TABLE_STRING] == mission_tap_name): + return mission + raise ValueError("Input tap name %s not available." %mission_tap_name) + + def _find_observation_tap_table_name(self, mission_name, cache): + return self._find_mission_tap_table_name( + self._fetch_and_parse_json(self.__OBSERVATIONS_STRING, cache), + mission_name) + + def _find_catalog_tap_table_name(self, mission_name, cache): + return self._find_mission_tap_table_name( + self._fetch_and_parse_json(self.__CATALOGS_STRING, cache), + mission_name) + + def _find_mission_tap_table_name(self, json, mission_name): + for index in range(len(json)): + if (json[index][self.__MISSION_STRING].lower() == mission_name.lower()): + return json[index][self.__TAP_TABLE_STRING] + + raise ValueError("Input %s not available." %mission_name) + return None + + def _get_observation_json(self, cache): + return self._fetch_and_parse_json(self.__OBSERVATIONS_STRING, cache) + + def _get_catalogs_json(self, cache): + return self._fetch_and_parse_json(self.__CATALOGS_STRING, cache) + + def _fetch_and_parse_json(self, object_name, cache): + url = self.URLbase + "/" + object_name + response = self._request('GET', url, cache = cache) + string_response = response.content.decode('utf-8') + json_response = json.loads(string_response) + return json_response[object_name] + + def _json_object_field_to_list(self, json, field_name): + response_list = [] + for index in range(len(json)): + response_list.append(json[index][field_name]) + return response_list + + def _create_request_payload(self, query): + return {'REQUEST':'doQuery', 'LANG':'ADQL', 'FORMAT': 'VOTABLE', + 'QUERY': query} + + def _get_and_parse_from_tap(self, request_payload, cache): + response = self._send_get_request("/tap/sync", request_payload, cache) + return self._parse_xml_table(response) + + def _send_get_request(self, url_extension, request_payload, cache): + url = self.URLbase + url_extension + return self._request('GET', url, params=request_payload, + timeout=self.TIMEOUT, cache=cache) + + def _parse_xml_table(self, response): + # try to parse the result into an astropy.Table, else + # return the raw result with an informative error message. + try: + tf = six.BytesIO(response.content) + vo_table = votable.parse(tf, pedantic = False) + first_table = vo_table.get_first_table() + table = first_table.to_table(use_names_over_ids = True) + return table + except Exception as ex: + self.response = response + self.table_parse_error = ex + raise TableParseError( + "Failed to parse ESASky VOTABLE result! The raw response can be " + "found in self.response, and the error in " + "self.table_parse_error.") + +ESASky = ESASkyClass() diff --git a/astroquery/esasky/tests/__init__.py b/astroquery/esasky/tests/__init__.py new file mode 100755 index 0000000000..e69de29bb2 diff --git a/astroquery/esasky/tests/data/catalogs.txt b/astroquery/esasky/tests/data/catalogs.txt new file mode 100644 index 0000000000..1a48bb1e3d --- /dev/null +++ b/astroquery/esasky/tests/data/catalogs.txt @@ -0,0 +1,845 @@ +{ + "catalogs" : [ { + "mission" : "INTEGRAL", + "wavelength" : "HARD_X_RAY", + "tapTable" : "integral_40_fits", + "countColumn" : "cat_integral40", + "countFovLimit" : 3.0, + "guiLabel" : "INTEGRAL", + "histoColor" : "#87CEFA", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by Flux at 20-60 keV", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "isgr_flux_1 DESC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 1 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA) (J2000)", + "visible" : true, + "type" : "RA", + "index" : 2 + }, { + "tapName" : "dec", + "label" : "Declination
(DEC) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 3 + }, { + "tapName" : "isgr_flux_1", + "label" : "Flux20-60 keV
(erg cm-2s-1)", + "visible" : true, + "type" : "DOUBLE", + "index" : 4 + }, { + "tapName" : "isgr_flux_2", + "label" : "Flux60-200 keV
(erg cm-2s-1)", + "visible" : true, + "type" : "DOUBLE", + "index" : 5 + }, { + "tapName" : "isgri_flag", + "label" : "isgri flag1", + "visible" : true, + "type" : "DOUBLE", + "index" : 6 + }, { + "tapName" : "isgri_flag2", + "label" : "isgri flag2", + "visible" : true, + "type" : "DOUBLE", + "index" : 7 + } ] + }, { + "mission" : "XMM-EPIC", + "wavelength" : "SOFT_X_RAY", + "tapTable" : "mv_xsa_epic_source_cat", + "countColumn" : "cat_xmm_epic", + "countFovLimit" : 3.0, + "guiLabel" : "3XMM EPIC", + "histoColor" : "#0000FF", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by Flux", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "http://nxsa.esac.esa.int/nxsa-web/#", + "archiveProductURI" : "iauname=@@@Name@@@", + "fovLimit" : 90.0, + "orderBy" : "ep_8_flux DESC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "LINK2ARCHIVE", + "index" : 2 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA) (J2000)", + "visible" : true, + "type" : "RA", + "index" : 3 + }, { + "tapName" : "dec", + "label" : "Declination
(DEC) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 4 + }, { + "tapName" : "ep_8_flux", + "label" : "Flux
(erg cm-2s-1)", + "visible" : true, + "type" : "DOUBLE", + "index" : 5 + } ] + }, { + "mission" : "XMM-OM", + "wavelength" : "UV", + "tapTable" : " mv_xsa_om_source_cat", + "countColumn" : "cat_xmm_om", + "countFovLimit" : 3.0, + "guiLabel" : "XMM OM", + "histoColor" : "#800080", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by flux starting with the UVW1 filter", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "uvw1_ab_flux,uvw2_ab_flux,uvm2_ab_flux,u_ab_flux,v_ab_flux,b_ab_flux DESC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 2 + }, { + "tapName" : "observation_id", + "label" : "ObservationId", + "visible" : true, + "type" : "STRING", + "index" : 5 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA) (J2000)", + "visible" : true, + "type" : "RA", + "index" : 3 + }, { + "tapName" : "dec", + "label" : "Declination
(DEC) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 4 + }, { + "tapName" : "poserr", + "label" : "ΔPos", + "visible" : true, + "type" : "STRING", + "index" : 5 + }, { + "tapName" : "date_obs", + "label" : "Date obs", + "visible" : true, + "type" : "STRING", + "index" : 4 + }, { + "tapName" : "uvw1_ab_flux", + "label" : "UVW1 Flux
(erg cm-2s-1)", + "visible" : true, + "type" : "STRING", + "index" : 5 + }, { + "tapName" : "uvw2_ab_flux", + "label" : "UVW2 Flux
(erg cm-2s-1)", + "visible" : true, + "type" : "STRING", + "index" : 5 + }, { + "tapName" : "uvm2_ab_flux", + "label" : "UVM2 Flux
(erg cm-2s-1)", + "visible" : true, + "type" : "STRING", + "index" : 5 + }, { + "tapName" : "u_ab_flux", + "label" : "U Flux
(erg cm-2s-1)", + "visible" : true, + "type" : "STRING", + "index" : 5 + }, { + "tapName" : "v_ab_flux", + "label" : "V Flux
(erg cm-2s-1)", + "visible" : true, + "type" : "STRING", + "index" : 5 + }, { + "tapName" : "b_ab_flux", + "label" : "B Flux
(erg cm-2s-1)", + "visible" : true, + "type" : "STRING", + "index" : 5 + } ] + }, { + "mission" : "XMM-SLEW", + "wavelength" : "SOFT_X_RAY", + "tapTable" : "xmm_slew_source_cat", + "countColumn" : "cat_xmm_slew", + "countFovLimit" : 3.0, + "guiLabel" : "XMM Slew", + "histoColor" : "#3F96C3", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by Flux", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "flux DESC", + "metadata" : [ { + "tapName" : "postcard_url", + "label" : "", + "visible" : true, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 2 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA) (J2000)", + "visible" : true, + "type" : "RA", + "index" : 3 + }, { + "tapName" : "dec", + "label" : "Declination
(Dec) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 4 + }, { + "tapName" : "flux", + "label" : "Flux
(erg cm-2s-1)", + "visible" : true, + "type" : "STRING", + "index" : 5 + } ] + }, { + "mission" : "Tycho-2", + "wavelength" : "VISIBLE", + "tapTable" : "mv_tycho2_fdw", + "countColumn" : "cat_tycho2", + "countFovLimit" : 3.0, + "guiLabel" : "Tycho-2", + "histoColor" : "#DAA520", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by BT Magnitude", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "bt_mag DESC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 2 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA) (J2000)", + "visible" : true, + "type" : "RA", + "index" : 3 + }, { + "tapName" : "dec", + "label" : "Declination
(Dec) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 4 + }, { + "tapName" : "pm_ra", + "label" : "Proper Motion in RA
(mas/yr)", + "visible" : true, + "type" : "DOUBLE", + "index" : 3 + }, { + "tapName" : "pm_de", + "label" : "Proper Motion in Dec
(mas/yr)", + "visible" : true, + "type" : "DOUBLE", + "index" : 4 + }, { + "tapName" : "bt_mag", + "label" : "BT Magnitude
(mag)", + "visible" : true, + "type" : "STRING", + "index" : 5 + }, { + "tapName" : "vt_mag", + "label" : "VT Magnitude
(mag)", + "visible" : true, + "type" : "STRING", + "index" : 6 + } ] + }, { + "mission" : "Gaia DR1 TGAS", + "wavelength" : "VISIBLE", + "tapTable" : "mv_tgas_source_fdw", + "countColumn" : "cat_tgas", + "countFovLimit" : 3.0, + "guiLabel" : "Gaia DR1 TGAS", + "histoColor" : "#ffb74d", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by G magnitude", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "phot_g_mean_mag ASC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 2 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA) (J2015)", + "visible" : true, + "type" : "RA", + "index" : 3 + }, { + "tapName" : "dec", + "label" : "Declination
(Dec) (J2015)", + "visible" : true, + "type" : "DEC", + "index" : 4 + }, { + "tapName" : "parallax", + "label" : "Parallax
(mas)", + "visible" : true, + "type" : "DOUBLE", + "index" : 5 + }, { + "tapName" : "parallax_error", + "label" : "Parallax error
(mas)", + "visible" : true, + "type" : "DOUBLE", + "index" : 5 + }, { + "tapName" : "pmra", + "label" : "Proper Motion in RA
(mas/yr)", + "visible" : true, + "type" : "DOUBLE", + "index" : 3 + }, { + "tapName" : "pmra_error", + "label" : "PM in RA error
(mas/yr)", + "visible" : true, + "type" : "DOUBLE", + "index" : 3 + }, { + "tapName" : "pmdec", + "label" : "Proper Motion in Dec
(mas/yr)", + "visible" : true, + "type" : "DOUBLE", + "index" : 4 + }, { + "tapName" : "pmdec_error", + "label" : "PM in Dec error
(mas/yr)", + "visible" : true, + "type" : "DOUBLE", + "index" : 4 + }, { + "tapName" : "phot_g_mean_mag", + "label" : "G mean Mag
(mag)", + "visible" : true, + "type" : "DOUBLE", + "index" : 6 + } ] + }, { + "mission" : "Hipparcos-2", + "wavelength" : "VISIBLE", + "tapTable" : "mv_hipparcos_fdw", + "countColumn" : "cat_hip2_mv", + "countFovLimit" : 3.0, + "guiLabel" : "Hipparcos-2", + "histoColor" : "#ffb74d", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by Hp magnitude", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "hp_mag DESC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 2 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA) (J2000)", + "visible" : true, + "type" : "RA", + "index" : 3 + }, { + "tapName" : "dec", + "label" : "Declination
(Dec) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 4 + }, { + "tapName" : "pm_ra", + "label" : "Proper Motion in RA
(mas/yr)", + "visible" : true, + "type" : "DOUBLE", + "index" : 3 + }, { + "tapName" : "pm_de", + "label" : "Proper Motion in Dec
(mas/yr)", + "visible" : true, + "type" : "DOUBLE", + "index" : 4 + }, { + "tapName" : "plx", + "label" : "Parallax
(mas)", + "visible" : true, + "type" : "STRING", + "index" : 5 + }, { + "tapName" : "hp_mag", + "label" : "Magnitude
(mag)", + "visible" : true, + "type" : "STRING", + "index" : 6 + } ] + }, { + "mission" : "HSC", + "wavelength" : "VISIBLE", + "tapTable" : "mv_hubble_sc_fdw", + "countColumn" : "cat_hubble", + "countFovLimit" : 3.0, + "guiLabel" : "HSC", + "histoColor" : "#FFD700", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the 2000 most observed sources (NumImages)", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "numimages DESC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 1 + }, { + "tapName" : "numimages", + "label" : "NumImages", + "visible" : true, + "type" : "DOUBLE", + "index" : 2 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA) (J2000)", + "visible" : true, + "type" : "RA", + "index" : 3 + }, { + "tapName" : "dec", + "label" : "Declination
(Dec) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 4 + }, { + "tapName" : "w2_f450w", + "label" : "F450W
(mag)", + "visible" : true, + "type" : "DOUBLE", + "index" : 5 + }, { + "tapName" : "w2_f606w", + "label" : "F606W
(mag)", + "visible" : true, + "type" : "DOUBLE", + "index" : 6 + }, { + "tapName" : "w2_f702w", + "label" : "F702W
(mag)", + "visible" : true, + "type" : "DOUBLE", + "index" : 7 + }, { + "tapName" : "w2_f814w", + "label" : "F814W
(mag)", + "visible" : true, + "type" : "DOUBLE", + "index" : 8 + } ] + }, { + "mission" : "Planck-PGCC2", + "wavelength" : "IR_RADIO", + "tapTable" : "mv_v_gcc_catalog_fdw", + "countColumn" : "cat_plank_pgss", + "countFovLimit" : 3.0, + "guiLabel" : "PGCC2", + "histoColor" : "#FF4500", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by Signal to Noise", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "snr DESC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 1 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA) (J2000)", + "visible" : true, + "type" : "RA", + "index" : 2 + }, { + "tapName" : "dec", + "label" : "Declination
(Dec) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 3 + }, { + "tapName" : "snr", + "label" : "Signal to Noise", + "visible" : true, + "type" : "DOUBLE", + "index" : 4 + }, { + "tapName" : "gau_major_axis", + "label" : "Major
FWHM
(arcmin)", + "visible" : true, + "type" : "DOUBLE", + "index" : 5 + }, { + "tapName" : "gau_major_axis_sig", + "label" : "Major FWHM error
(arcmin)", + "visible" : true, + "type" : "DOUBLE", + "index" : 6 + }, { + "tapName" : "gau_minor_axis", + "label" : "Minor FWHM
(arcmin)", + "visible" : true, + "type" : "DOUBLE", + "index" : 7 + }, { + "tapName" : "gau_minor_axis_sig", + "label" : "Minor FWHM error
(arcmin)", + "visible" : true, + "type" : "DOUBLE", + "index" : 8 + } ] + }, { + "mission" : "Planck-PCCS2E", + "wavelength" : "IR_RADIO", + "tapTable" : "mv_pcss_catalog_excluded_fdw", + "countColumn" : "cat_planck_pccse", + "countFovLimit" : 3.0, + "guiLabel" : "PCCS2E", + "histoColor" : "#ff4000", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by Flux density", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "detflux DESC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 1 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA)(J2000)", + "visible" : true, + "type" : "RA", + "index" : 2 + }, { + "tapName" : "dec", + "label" : "Declination
(Dec) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 3 + }, { + "tapName" : "frequency", + "label" : "Frequency
(GHz)", + "visible" : true, + "type" : "DOUBLE", + "index" : 4 + }, { + "tapName" : "detflux", + "label" : "Flux density
(mJy)", + "visible" : true, + "type" : "DOUBLE", + "index" : 5 + }, { + "tapName" : "detflux_err", + "label" : "Flux density error
(mJy)", + "visible" : true, + "type" : "DOUBLE", + "index" : 6 + }, { + "tapName" : "extended", + "label" : "Extended
source flag", + "visible" : true, + "type" : "DOUBLE", + "index" : 7 + }, { + "tapName" : "highest_reliability_cat", + "label" : "Cat. reliability", + "visible" : true, + "type" : "DOUBLE", + "index" : 8 + } ] + }, { + "mission" : "Planck-PCCS2-HFI", + "wavelength" : "IR_RADIO", + "tapTable" : "mv_pcss_catalog_hfi_fdw", + "countColumn" : "cat_planck_pccs_hfi", + "countFovLimit" : 3.0, + "guiLabel" : "PCCS2-HFI", + "histoColor" : "#ff0000", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by Flux density", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "detflux DESC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 1 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA)(J2000)", + "visible" : true, + "type" : "RA", + "index" : 2 + }, { + "tapName" : "dec", + "label" : "Declination
(Dec) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 3 + }, { + "tapName" : "frequency", + "label" : "Frequency
(GHz)", + "visible" : true, + "type" : "DOUBLE", + "index" : 4 + }, { + "tapName" : "detflux", + "label" : "Flux density
(mJy)", + "visible" : true, + "type" : "DOUBLE", + "index" : 5 + }, { + "tapName" : "detflux_err", + "label" : "Flux density error
(mJy)", + "visible" : true, + "type" : "DOUBLE", + "index" : 6 + }, { + "tapName" : "extended", + "label" : "Extended
source flag", + "visible" : true, + "type" : "DOUBLE", + "index" : 7 + }, { + "tapName" : "highest_reliability_cat", + "label" : "Cat. reliability", + "visible" : true, + "type" : "DOUBLE", + "index" : 8 + } ] + }, { + "mission" : "Planck-PCCS2-LFI", + "wavelength" : "IR_RADIO", + "tapTable" : "mv_pcss_catalog_lfi_fdw", + "countColumn" : "cat_planck_pccs_lfi", + "countFovLimit" : 3.0, + "guiLabel" : "PCCS2-LFI", + "histoColor" : "#ff8566", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by Flux density", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "detflux DESC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 1 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA)(J2000)", + "visible" : true, + "type" : "RA", + "index" : 2 + }, { + "tapName" : "dec", + "label" : "Declination
(Dec) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 3 + }, { + "tapName" : "frequency", + "label" : "Frequency
(GHz)", + "visible" : true, + "type" : "DOUBLE", + "index" : 4 + }, { + "tapName" : "detflux", + "label" : "Flux density
(mJy)", + "visible" : true, + "type" : "DOUBLE", + "index" : 5 + }, { + "tapName" : "detflux_err", + "label" : "Flux density error
(mJy)", + "visible" : true, + "type" : "DOUBLE", + "index" : 6 + }, { + "tapName" : "extended", + "label" : "Extended
source flag", + "visible" : true, + "type" : "DOUBLE", + "index" : 7 + }, { + "tapName" : "highest_reliability_cat", + "label" : "Cat. reliability", + "visible" : true, + "type" : "DOUBLE", + "index" : 8 + } ] + }, { + "mission" : "Planck-PSZ", + "wavelength" : "IR_RADIO", + "tapTable" : "mv_v_sz_catalog_fdw", + "countColumn" : "cat_plank_psz", + "countFovLimit" : 3.0, + "guiLabel" : "PSZ", + "histoColor" : "#C71585", + "sourceLimit" : 2000, + "sourceLimitDescription" : "Showing the first 2000 sources ordered by Signal to Noise", + "posTapColumn" : "pos", + "polygonRaTapColumn" : "ra", + "polygonDecTapColumn" : "dec", + "polygonNameTapColumn" : "name", + "archiveURL" : "", + "archiveProductURI" : "", + "fovLimit" : 90.0, + "orderBy" : "snr DESC", + "metadata" : [ { + "tapName" : "name", + "label" : "Name", + "visible" : true, + "type" : "STRING", + "index" : 1 + }, { + "tapName" : "ra", + "label" : "Right Ascension
(RA) (J2000)", + "visible" : true, + "type" : "RA", + "index" : 2 + }, { + "tapName" : "dec", + "label" : "Declination
(Dec) (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 3 + }, { + "tapName" : "snr", + "label" : "Signal to Noise", + "visible" : true, + "type" : "DOUBLE", + "index" : 4 + }, { + "tapName" : "redshift", + "label" : "Redshift", + "visible" : true, + "type" : "DOUBLE", + "index" : 5 + }, { + "tapName" : "msz", + "label" : "Sunyaev-Zeldovich Mass
(1014 Msolar)", + "visible" : true, + "type" : "DOUBLE", + "index" : 6 + }, { + "tapName" : "validation", + "label" : "Validation", + "visible" : true, + "type" : "DOUBLE", + "index" : 7 + } ] + } ], + "total" : 13 +} diff --git a/astroquery/esasky/tests/data/observations.txt b/astroquery/esasky/tests/data/observations.txt new file mode 100644 index 0000000000..395e9a4762 --- /dev/null +++ b/astroquery/esasky/tests/data/observations.txt @@ -0,0 +1,626 @@ +{ + "observations" : [ { + "mission" : "INTEGRAL", + "wavelength" : "HARD_X_RAY", + "tapTable" : "integral_data", + "countColumn" : "sur_integral", + "countFovLimit" : 3.0, + "tapSTCSColumn" : "", + "tapObservationId" : "scw_id", + "guiLabel" : "INTEGRAL", + "histoColor" : "#87CEFA", + "fovLimit" : 90.0, + "mocTapTable" : "integral_moc", + "mocSTCSColumn" : "aladin_lite_polygon", + "mocLimit" : 3000, + "metadata" : [ { + "tapName" : "postcard_url", + "label" : "", + "visible" : false, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "product_url", + "label" : "ProductURL", + "visible" : false, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "observation_id", + "label" : "Science Window Id", + "visible" : true, + "type" : "STRING", + "index" : 3 + }, { + "tapName" : "instrument", + "label" : "Instrument", + "visible" : true, + "type" : "STRING", + "index" : 4 + }, { + "tapName" : "ra_deg", + "label" : "RA (J2000)", + "visible" : true, + "type" : "RA", + "index" : 5 + }, { + "tapName" : "dec_deg", + "label" : "DEC (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 6 + }, { + "tapName" : "tstart_iso", + "label" : "Start time", + "visible" : true, + "type" : "STRING", + "index" : 7 + }, { + "tapName" : "telapse", + "label" : "Duration (s)", + "visible" : true, + "type" : "STRING", + "index" : 7 + } ], + "archiveURL" : "http://www.isdc.unige.ch/browse/isdcvo2Browse.pl?instrument=integral_jemx&goodTime=%3E200&radius=180&resultMax=1000&", + "archiveProductURI" : "ra=@@@RA (J2000)@@@&dec=@@@DEC (J2000)@@@", + "ddProductURI" : "", + "ddBaseURL" : "", + "ddProductIDParameter" : "", + "ddProductIDColumn" : null, + "sampEnabled" : true, + "isSurveyMission" : true + }, { + "mission" : "XMM-EPIC", + "wavelength" : "SOFT_X_RAY", + "tapTable" : "xmm_data", + "countColumn" : "obs_xmm_epic", + "countFovLimit" : 3.0, + "tapSTCSColumn" : "stc_s", + "tapObservationId" : "observation_id", + "guiLabel" : "XMM-Newton", + "histoColor" : "#0000cc", + "fovLimit" : 90.0, + "mocTapTable" : "xmm_moc", + "mocSTCSColumn" : "aladin_lite_polygon", + "mocLimit" : 2000, + "metadata" : [ { + "tapName" : "postcard_url", + "label" : "", + "visible" : true, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "product_url", + "label" : "ProductURL", + "visible" : false, + "type" : "LINK", + "index" : 2 + }, { + "tapName" : "observation_id", + "label" : "ObservationId", + "visible" : true, + "type" : "LINK2ARCHIVE", + "index" : 3 + }, { + "tapName" : "instrument", + "label" : "Instrument", + "visible" : true, + "type" : "STRING", + "index" : 4 + }, { + "tapName" : "ra_deg", + "label" : "RA (J2000)", + "visible" : true, + "type" : "RA", + "index" : 5 + }, { + "tapName" : "dec_deg", + "label" : "DEC (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 6 + }, { + "tapName" : "start_time", + "label" : "Start Time", + "visible" : true, + "type" : "STRING", + "index" : 7 + }, { + "tapName" : "duration", + "label" : "Duration (s)", + "visible" : true, + "type" : "STRING", + "index" : 8 + }, { + "tapName" : "stc_s", + "label" : "", + "visible" : false, + "type" : "STRING", + "index" : 9 + } ], + "archiveURL" : "http://nxsa.esac.esa.int/nxsa-web/#", + "archiveProductURI" : "obsid=@@@ObservationId@@@", + "ddProductURI" : "obsno=@@@ObservationId@@@", + "ddBaseURL" : "", + "ddProductIDParameter" : "obsno", + "ddProductIDColumn" : "ObservationId", + "sampEnabled" : true, + "isSurveyMission" : false + }, { + "mission" : "SUZAKU", + "wavelength" : "SOFT_X_RAY", + "tapTable" : "suzaku_data", + "countColumn" : "obs_suzaku", + "countFovLimit" : 3.0, + "tapSTCSColumn" : "stc_s", + "tapObservationId" : "observation_id", + "guiLabel" : "SUZAKU", + "histoColor" : "#0099ff", + "fovLimit" : 90.0, + "mocTapTable" : "suzaku_moc", + "mocSTCSColumn" : "aladin_lite_polygon", + "mocLimit" : 1000, + "metadata" : [ { + "tapName" : "postcard_url", + "label" : "", + "visible" : true, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "product_url", + "label" : "ProductURL", + "visible" : false, + "type" : "LINK", + "index" : 2 + }, { + "tapName" : "observation_id", + "label" : "ObservationId", + "visible" : true, + "type" : "LINK2ARCHIVE", + "index" : 3 + }, { + "tapName" : "instrument", + "label" : "Instrument", + "visible" : true, + "type" : "STRING", + "index" : 4 + }, { + "tapName" : "ra_deg", + "label" : "RA (J2000)", + "visible" : true, + "type" : "RA", + "index" : 5 + }, { + "tapName" : "dec_deg", + "label" : "DEC (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 6 + }, { + "tapName" : "stc_s", + "label" : "", + "visible" : false, + "type" : "STRING", + "index" : 0 + } ], + "archiveURL" : "http://darts.isas.jaxa.jp/astro/judo2/meta_info_page/html/SUZAKU/", + "archiveProductURI" : "@@@ObservationId@@@.html", + "ddProductURI" : "", + "ddBaseURL" : "", + "ddProductIDParameter" : "", + "ddProductIDColumn" : "", + "sampEnabled" : true, + "isSurveyMission" : false + }, { + "mission" : "XMM-OM-OPTICAL", + "wavelength" : "VISIBLE", + "tapTable" : "xmm_om_optical_data", + "countColumn" : "obs_xmm_om_optical", + "countFovLimit" : 3.0, + "tapSTCSColumn" : "stc_s", + "tapObservationId" : "observation_id", + "guiLabel" : "XMM-OM(Optical)", + "histoColor" : "#DAA520", + "fovLimit" : 90.0, + "mocTapTable" : "xmm_om_vub_moc", + "mocSTCSColumn" : "icrs_polygon", + "mocLimit" : 2000, + "metadata" : [ { + "tapName" : "postcard_url", + "label" : "", + "visible" : true, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "product_url", + "label" : "ProductURL", + "visible" : false, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "observation_id", + "label" : "ObservationId", + "visible" : true, + "type" : "LINK2ARCHIVE", + "index" : 3 + }, { + "tapName" : "instrument", + "label" : "Instrument", + "visible" : true, + "type" : "STRING", + "index" : 4 + }, { + "tapName" : "filter", + "label" : "Filter", + "visible" : true, + "type" : "STRING", + "index" : 4 + }, { + "tapName" : "ra_deg", + "label" : "RA (J2000)", + "visible" : true, + "type" : "RA", + "index" : 5 + }, { + "tapName" : "dec_deg", + "label" : "DEC (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 6 + }, { + "tapName" : "start_utc", + "label" : "Start Time", + "visible" : true, + "type" : "STRING", + "index" : 6 + }, { + "tapName" : "duration", + "label" : "Duration (s)", + "visible" : true, + "type" : "STRING", + "index" : 6 + }, { + "tapName" : "stc_s", + "label" : "", + "visible" : false, + "type" : "STRING", + "index" : 0 + } ], + "archiveURL" : "http://nxsa.esac.esa.int/nxsa-web/#", + "archiveProductURI" : "obsid=@@@ObservationId@@@", + "ddProductURI" : "", + "ddBaseURL" : "", + "ddProductIDParameter" : "", + "ddProductIDColumn" : "", + "sampEnabled" : true, + "isSurveyMission" : false + }, { + "mission" : "XMM-OM-UV", + "wavelength" : "UV", + "tapTable" : "xmm_om_uv_data", + "countColumn" : "obs_xmm_om_uv", + "countFovLimit" : 3.0, + "tapSTCSColumn" : "stc_s", + "tapObservationId" : "observation_id", + "guiLabel" : "XMM-OM(UV)", + "histoColor" : "#800080", + "fovLimit" : 90.0, + "mocTapTable" : "xmm_om_uv_moc", + "mocSTCSColumn" : "icrs_polygon", + "mocLimit" : 2000, + "metadata" : [ { + "tapName" : "postcard_url", + "label" : "", + "visible" : true, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "product_url", + "label" : "ProductURL", + "visible" : false, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "observation_id", + "label" : "ObservationId", + "visible" : true, + "type" : "LINK2ARCHIVE", + "index" : 3 + }, { + "tapName" : "instrument", + "label" : "Instrument", + "visible" : true, + "type" : "STRING", + "index" : 4 + }, { + "tapName" : "filter", + "label" : "Filter", + "visible" : true, + "type" : "STRING", + "index" : 4 + }, { + "tapName" : "ra_deg", + "label" : "RA (J2000)", + "visible" : true, + "type" : "RA", + "index" : 5 + }, { + "tapName" : "dec_deg", + "label" : "DEC (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 6 + }, { + "tapName" : "start_utc", + "label" : "Start Time", + "visible" : true, + "type" : "STRING", + "index" : 6 + }, { + "tapName" : "duration", + "label" : "Duration (s)", + "visible" : true, + "type" : "STRING", + "index" : 6 + }, { + "tapName" : "stc_s", + "label" : "", + "visible" : false, + "type" : "STRING", + "index" : 0 + } ], + "archiveURL" : "http://nxsa.esac.esa.int/nxsa-web/#", + "archiveProductURI" : "obsid=@@@ObservationId@@@", + "ddProductURI" : "", + "ddBaseURL" : "", + "ddProductIDParameter" : "", + "ddProductIDColumn" : "", + "sampEnabled" : true, + "isSurveyMission" : false + }, { + "mission" : "HST", + "wavelength" : "VISIBLE", + "tapTable" : "mv_hst_observation_fdw", + "countColumn" : "obs_hst", + "countFovLimit" : 3.0, + "tapSTCSColumn" : "stc_s", + "tapObservationId" : "observation_id", + "guiLabel" : "HST", + "histoColor" : "#FFD700", + "fovLimit" : 90.0, + "mocTapTable" : "hst_moc7", + "mocSTCSColumn" : "aladin_lite_polygon", + "mocLimit" : 5000, + "metadata" : [ { + "tapName" : "postcard_url", + "label" : "", + "visible" : true, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "product_url", + "label" : "ProductURL", + "visible" : false, + "type" : "LINK", + "index" : 2 + }, { + "tapName" : "observation_id", + "label" : "ObservationId", + "visible" : true, + "type" : "LINK2ARCHIVE", + "index" : 3 + }, { + "tapName" : "collection", + "label" : "Collection", + "visible" : true, + "type" : "STRING", + "index" : 4 + }, { + "tapName" : "instrument_name", + "label" : "Instrument", + "visible" : true, + "type" : "STRING", + "index" : 5 + }, { + "tapName" : "filter", + "label" : "Filter", + "visible" : true, + "type" : "STRING", + "index" : 6 + }, { + "tapName" : "ra_deg", + "label" : "RA (J2000)", + "visible" : true, + "type" : "RA", + "index" : 7 + }, { + "tapName" : "dec_deg", + "label" : "DEC (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 8 + }, { + "tapName" : "start_time", + "label" : "Start Time", + "visible" : true, + "type" : "STRING", + "index" : 9 + }, { + "tapName" : "exposure_duration", + "label" : "Duration (s)", + "visible" : true, + "type" : "STRING", + "index" : 10 + }, { + "tapName" : "stc_s", + "label" : "", + "visible" : false, + "type" : "STRING", + "index" : 11 + } ], + "archiveURL" : "http://archives.esac.esa.int/ehst/#", + "archiveProductURI" : "observationid=@@@ObservationId@@@", + "ddProductURI" : "ARTIFACT_ID=@@@ObservationId@@@_DRZ", + "ddBaseURL" : "", + "ddProductIDParameter" : "ARTIFACT_ID", + "ddProductIDColumn" : "ObservationId", + "sampEnabled" : true, + "isSurveyMission" : false + }, { + "mission" : "Herschel", + "wavelength" : "IR_RADIO", + "tapTable" : "mv_hsa_esasky_photo_table_fdw", + "countColumn" : "obs_hsa_photo", + "countFovLimit" : 3.0, + "tapSTCSColumn" : "stc_s", + "tapObservationId" : "observation_id", + "guiLabel" : "Herschel", + "histoColor" : "#20B2AA", + "fovLimit" : 90.0, + "mocTapTable" : "hsa_moc", + "mocSTCSColumn" : "aladin_lite_polygon", + "mocLimit" : 2000, + "metadata" : [ { + "tapName" : "postcard_url", + "label" : "", + "visible" : true, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "product_url", + "label" : "ProductURL", + "visible" : false, + "type" : "LINK", + "index" : 2 + }, { + "tapName" : "observation_id", + "label" : "ObservationId", + "visible" : true, + "type" : "LINK2ARCHIVE", + "index" : 3 + }, { + "tapName" : "observation_oid", + "label" : "ObservationOid", + "visible" : false, + "type" : "STRING", + "index" : 4 + }, { + "tapName" : "instrument", + "label" : "Instrument", + "visible" : true, + "type" : "STRING", + "index" : 5 + }, { + "tapName" : "filter", + "label" : "Filter (microns)", + "visible" : true, + "type" : "STRING", + "index" : 6 + }, { + "tapName" : "ra_deg", + "label" : "RA (J2000)", + "visible" : true, + "type" : "RA", + "index" : 7 + }, { + "tapName" : "dec_deg", + "label" : "DEC (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 8 + }, { + "tapName" : "start_time", + "label" : "Start Time", + "visible" : true, + "type" : "STRING", + "index" : 9 + }, { + "tapName" : "duration", + "label" : "Duration (s)", + "visible" : true, + "type" : "STRING", + "index" : 10 + }, { + "tapName" : "stc_s", + "label" : "", + "visible" : false, + "type" : "STRING", + "index" : 11 + } ], + "archiveURL" : "http://archives.esac.esa.int/hsa/aio/jsp/postcardPage.jsp?", + "archiveProductURI" : "OBSERVATION_ID=@@@ObservationId@@@&INSTRUMENT=@@@Instrument@@@", + "ddProductURI" : "OBSERVATION.OBSERVATION_OID=@@@ObservationOid@@@", + "ddBaseURL" : "", + "ddProductIDParameter" : "OBSERVATION.OBSERVATION_OID", + "ddProductIDColumn" : "ObservationOid", + "sampEnabled" : false, + "isSurveyMission" : false + }, { + "mission" : "ISO", + "wavelength" : "IR_RADIO", + "tapTable" : "ida_data", + "countColumn" : "obs_iso", + "countFovLimit" : 3.0, + "tapSTCSColumn" : "stc_s", + "tapObservationId" : "observation_id", + "guiLabel" : "ISO", + "histoColor" : "#BF3030", + "fovLimit" : 90.0, + "mocTapTable" : "ida_moc", + "mocSTCSColumn" : "aladin_lite_polygon", + "mocLimit" : 2000, + "metadata" : [ { + "tapName" : "postcard_url", + "label" : "", + "visible" : true, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "product_url", + "label" : "ProductURL", + "visible" : false, + "type" : "LINK", + "index" : 1 + }, { + "tapName" : "observation_id", + "label" : "ObservationId", + "visible" : true, + "type" : "LINK2ARCHIVE", + "index" : 3 + }, { + "tapName" : "instrument", + "label" : "Instrument", + "visible" : true, + "type" : "STRING", + "index" : 4 + }, { + "tapName" : "ra_deg", + "label" : "RA (J2000)", + "visible" : true, + "type" : "RA", + "index" : 5 + }, { + "tapName" : "dec_deg", + "label" : "DEC (J2000)", + "visible" : true, + "type" : "DEC", + "index" : 6 + }, { + "tapName" : "stc_s", + "label" : "", + "visible" : false, + "type" : "STRING", + "index" : 0 + } ], + "archiveURL" : "http://archives.esac.esa.int/ida/aio/jsp/createPostcards.jsp?", + "archiveProductURI" : "obsno=@@@ObservationId@@@", + "ddProductURI" : "", + "ddBaseURL" : "", + "ddProductIDParameter" : "", + "ddProductIDColumn" : null, + "sampEnabled" : true, + "isSurveyMission" : false + } ], + "total" : 8 +} \ No newline at end of file diff --git a/astroquery/esasky/tests/setup_package.py b/astroquery/esasky/tests/setup_package.py new file mode 100644 index 0000000000..302c602298 --- /dev/null +++ b/astroquery/esasky/tests/setup_package.py @@ -0,0 +1,16 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from __future__ import absolute_import + +import os + + +# setup paths to the test data +# can specify a single file or a list of files +def get_package_data(): + paths = [os.path.join('data', '*.dat'), + os.path.join('data', '*.xml'), + os.path.join('data', '*.txt'), + ] # etc, add other extensions + # you can also enlist files individually by names + # finally construct and return a dict for the sub module + return {'astroquery.esasky.tests': paths} diff --git a/astroquery/esasky/tests/test_esasky.py b/astroquery/esasky/tests/test_esasky.py new file mode 100755 index 0000000000..e7e1c142aa --- /dev/null +++ b/astroquery/esasky/tests/test_esasky.py @@ -0,0 +1,52 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from __future__ import print_function + +from astropy.tests.helper import pytest + +import os +import unittest + +from ...utils.testing_tools import MockResponse +from ...esasky import ESASky + + +DATA_FILES = {'GET': + {'http://ammidev.n1data.lan:8080/esasky-tap/observations': + 'observations.txt', + 'http://sky.esa.int/esasky-tap/catalogs': + 'catalogs.txt' + }, + } + +def data_path(filename): + data_dir = os.path.join(os.path.dirname(__file__), 'data') + return os.path.join(data_dir, filename) + +def nonremote_request(request_type, url, **kwargs): + with open(data_path(DATA_FILES[request_type][url]), 'rb') as f: + response = MockResponse(content=f.read(), url=url) + return response + +@pytest.fixture +def esasky_request(request): + mp = request.getfuncargvalue("monkeypatch") + mp.setattr(ESASky, '_request', nonremote_request) + return mp + +@pytest.mark.usefixtures("esasky_request") +class TestEsaSkyLocal(unittest.TestCase): + def test_esasky_query_region_maps_invalid_position(self): + with self.assertRaises(ValueError): + ESASky.query_region_maps(51, "5 arcmin") + + def test_esasky_query_region_maps_invalid_radius(self): + with self.assertRaises(ValueError): + ESASky.query_region_maps("M51", 5) + + def test_esasky_query_region_maps_invalid_mission(self): + with self.assertRaises(ValueError): + ESASky.query_region_maps("M51", "5 arcmin", missions=True) + + def test_list_catalogs(self): + result = ESASky.list_catalogs() + assert (len(result) == 13) diff --git a/astroquery/esasky/tests/test_esasky_remote.py b/astroquery/esasky/tests/test_esasky_remote.py new file mode 100755 index 0000000000..edacf3ae53 --- /dev/null +++ b/astroquery/esasky/tests/test_esasky_remote.py @@ -0,0 +1,26 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from __future__ import print_function + +from astroquery.utils.commons import TableList +from astropy.tests.helper import remote_data + +from ... import esasky + +@remote_data +class TestESASky: + + def test_esasky_query_region_maps(self): + result = esasky.core.ESASkyClass().query_region_maps("M51", "5 arcmin") + assert isinstance(result, TableList) + + def test_esasky_query_object_maps(self): + result = esasky.core.ESASkyClass().query_object_maps("M51") + assert isinstance(result, TableList) + + def test_esasky_query_region_catalogs(self): + result = esasky.core.ESASkyClass().query_region_catalogs("M51", "5 arcmin") + assert isinstance(result, TableList) + + def test_esasky_query_object_catalogs(self): + result = esasky.core.ESASkyClass().query_object_maps("M51") + assert isinstance(result, TableList) diff --git a/docs/esasky/esasky.rst b/docs/esasky/esasky.rst new file mode 100644 index 0000000000..36cfd208a5 --- /dev/null +++ b/docs/esasky/esasky.rst @@ -0,0 +1,248 @@ +.. doctest-skip-all + +.. _astroquery.esasky: + +************************************ +ESASky Queries (`astroquery.esasky`) +************************************ + +Getting started +=============== + +This is a python interface for querying the ESASky web service. This supports +querying an object as well as querying a region around the target. For region +queries, the region dimensions may be specified as a +radius. The queries may be further constrained by specifying +a choice of catalogs or missions. + +Get the available catalog names +------------------------------- + +If you know the names of all the available catalogs you can use +:meth:`~astroquery.esasky.ESASkyClass.list_catalogs`: + +.. code-block:: python + + >>> catalog_list = ESASky.list_catalogs() + >>> print(catalog_list) + ['INTEGRAL', 'XMM-EPIC', 'XMM-OM', 'XMM-SLEW', 'Tycho-2', + 'Gaia DR1 TGAS', 'Hipparcos-2', 'HSC', 'Planck-PGCC2', 'Planck-PCCS2E', + 'Planck-PCCS2-HFI', 'Planck-PCCS2-LFI', 'Planck-PSZ'] + +Get the available maps mission names +------------------------------------ + +If you know the names of all the available maps missions you can use +:meth:`~astroquery.esasky.ESASkyClass.list_maps`: + +.. code-block:: python + + >>> maps_list = ESASky.list_maps() + >>> print(maps_list) + ['INTEGRAL', 'XMM-EPIC', 'SUZAKU', 'XMM-OM-OPTICAL', 'XMM-OM-UV', + 'HST', 'Herschel', 'ISO'] + +Query an object +--------------- + +There are two query objects methods in this module +:meth:`~astroquery.esasky.ESASkyClass.query_object_catalogs` and +:meth:`~astroquery.esasky.ESASkyClass.query_object_maps`. They both work in +almost the same way except that one has catalogs as input and output and the +other one has mission names and observations as input and output. + +For catalogs, the query returns a maximum of 2000 sources per mission. +To account for observation errors, this method will search for any sources +within 5 arcsec from the object. + +For instance to query an object around M51 in the integral catalog: + +.. code-block:: python + + >>> from astroquery.esasky import ESASky + >>> result = ESASky.query_object_catalogs("M51", "integral") + +Note that the catalog may also be specified as a list. +So the above query may also be written as: + +.. code-block:: python + + >>> result = ESASky.query_object_catalogs("M51", ["integral", "XMM-OM"]) + +To search in all available catalogs you can write "all" instead of a catalog +name. The same thing will happen if you don't write any catalog name. + +.. code-block:: python + + >>> result = ESASky.query_object_catalogs("M51", "all") + >>> result = ESASky.query_object_catalogs("M51") + +To see the result: + +.. code-block:: python + + >>> print(result) + TableList with 4 tables: + '0:XMM-EPIC' with 4 column(s) and 3 row(s) + '1:HSC' with 8 column(s) and 2000 row(s) + '2:XMM-OM' with 12 column(s) and 220 row(s) + '3:PLANCK-PCCS2-HFI' with 8 column(s) and 1 row(s) + +All the results are returned as a `astroquery.utils.TableList` object. This is a +container for `~astropy.table.Table` objects. It is basically an extension to +`collections.OrderedDict` for storing a `~astropy.table.Table` against its name. + +To access an individual table from the `astroquery.utils.TableList` object + +.. code-block:: python + + >>> interesting_table = result['PLANCK-PCCS2-HFI'] + >>> print(interesting_table) + name ra [1] dec [1] + ----------------------- ------------- ------------- + PCCS2 217 G104.83+68.55 202.485459453 47.2001843799 + +To do some common processing to all the tables in the returned +`astroquery.utils.TableList` object, do just what you would do for a python +dictionary: + +.. code-block:: python + + >>> for table_name in result: + ... table = result[table_name] + ... # table is now an `astropy.table.Table` object + ... # some code to apply on table + +As mentioned earlier, :meth:`astroquery.esasky.ESASkyClass.query_object_maps` +works extremely similar. It will return all maps that contain the chosen object +or coordinate. To execute the same command as above you write this: + +.. code-block:: python + + >>> result = ESASky.query_object_maps("M51", "all") + +The parameters are interchangeable in the same way as in query_object_catalogs + +Query a region +-------------- +The region queries work in a similar way as query_object, except that you must +choose a radius as well. There are two query region methods in this module +:meth:`astroquery.esasky.ESASkyClass.query_region_catalogs` and +:meth:`astroquery.esasky.ESASkyClass.query_region_maps`. +The query returns a maximum of 2000 sources per mission. + +To query a region either the coordinates or the object name around which to +query should be specified along with the value for the radius of the region. +For instance to query region around M51 in the integral catalog: + +.. code-block:: python + + >>> from astroquery.esasky import ESASky + >>> import astropy.units as u + >>> result = ESASky.query_region_catalogs("M51", 10 * u.arcmin, "integral") + +Note that the catalog may also be specified as a list. +So the above query may also be written as: + +.. code-block:: python + + >>> result = ESASky.query_region_catalogs("M51", 10 * u.arcmin, ["integral", "XMM-OM"]) + +To search in all available catalogs you can write "all" instead of a catalog +name. The same thing will happen if you don't write any catalog name. + +.. code-block:: python + + >>> result = ESASky.query_region_catalogs("M51", 10 * u.arcmin, "all") + >>> result = ESASky.query_region_catalogs("M51", 10 * u.arcmin) + +In the same manner, the radius can be specified with either +a string or any `~astropy.units.Quantity` + +.. code-block:: python + + >>> result = ESASKY.query_region_catalogs("M51", "10 arcmin") + +To see the result: + +.. code-block:: python + + >>> print(result) + TableList with 4 tables: + '0:XMM-EPIC' with 4 column(s) and 3 row(s) + '1:HSC' with 8 column(s) and 2000 row(s) + '2:XMM-OM' with 12 column(s) and 220 row(s) + '3:PLANCK-PCCS2-HFI' with 8 column(s) and 1 row(s) + +As mentioned earlier, query_region_maps works extremely similar. +To execute the same command as above you write this: + +.. code-block:: python + + >>> result = ESASky.query_region_maps("M51", 10 * u.arcmin, "all") + +The parameters are interchangeable in the same way as in query_region_catalogs + +Get images +---------- + +You can fetch images around the specified target or coordinates. When a target +name is used rather than the coordinates, this will be resolved to coordinates +using astropy name resolving methods that utilize online services like +SESAME. Coordinates may be entered using the suitable object from +`astropy.coordinates`. + +The method returns a `dict` to separate the different +missions. All mission except Herschel returns a list of +`~astropy.io.fits.HDUList`. For Herschel each item in the list is a +dictionary where the used filter is the key and the HDUList is the value. + +.. code-block:: python + + >>> from astroquery.esasky import ESASky + >>> images = ESASky.get_images("m51", radius="20 arcmin", missions=['Herschel', 'XMM-EPIC']) + + Starting download of HERSCHEL data. (12 files) + Downloading Observation ID: 1342183910 from http://archives.esac.esa.int/hsa/aio/jsp/standaloneproduct.jsp?RETRIEVAL_TYPE=STANDALONE&OBSERVATION.OBSERVATION_ID=1342183910&OBSERVING_MODE.OBSERVING_MODE_NAME=PacsPhoto&INSTRUMENT.INSTRUMENT_NAME=PACS [Done] + Downloading Observation ID: 1342183907 from http://archives.esac.esa.int/hsa/aio/jsp/standaloneproduct.jsp?RETRIEVAL_TYPE=STANDALONE&OBSERVATION.OBSERVATION_ID=1342183907&OBSERVING_MODE.OBSERVING_MODE_NAME=PacsPhoto&INSTRUMENT.INSTRUMENT_NAME=PACS [Done] + ... + + >>> print(images) + { + 'HERSCHEL': [{'70': [HDUList], ' 160': [HDUList]}, {'70': [HDUList], ' 160': [HDUList]}, ...], + 'XMM-EPIC' : [HDUList], HDUList], HDUList], HDUList], ...] + ... + } + +Note that the fits files also are stored to disk. By default they are saved to +the working directory but the location can be chosen by the download_directory +parameter: + +.. code-block:: python + + >>> images = ESASky.get_images("m51", radius="20 arcmin", missions=['Herschel', 'XMM-EPIC'], download_directory="/home/user/esasky") + +Get maps +-------- + +You can also fetch images using :meth:`astroquery.esasky.ESASkyClass.get_maps`. +It works exactly as :meth:`astroquery.esasky.ESASkyClass.get_images` except that +it takes a `~astropy.utils.TableList` instead of position, radius and missions. + +.. code-block:: python + + >>> table_list = ESASky.query_region_maps("m51", radius="20 arcmin", missions=['Herschel', 'XMM-EPIC']) + >>> images = ESASky.get_maps(table_list, download_directory="/home/user/esasky") + +This example is equivalent to: + +.. code-block:: python + + >>> images = ESASky.get_images("m51", radius="20 arcmin", missions=['Herschel', 'XMM-EPIC'], download_directory="/home/user/esasky") + + +Reference/API +============= + +.. automodapi:: astroquery.esasky + :no-inheritance-diagram: diff --git a/docs/index.rst b/docs/index.rst index a50463ac03..9fa796f25a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -134,6 +134,7 @@ The following modules have been completed using a common API: simbad/simbad.rst vizier/vizier.rst + esasky/esasky.rst irsa/irsa_dust.rst ned/ned.rst splatalogue/splatalogue.rst