|
23 | 23 | from astropy.table import unique |
24 | 24 | from astropy.units import Quantity |
25 | 25 | from astropy.utils import deprecated_renamed_argument |
| 26 | +import astroquery.esa.utils.utils as esautils |
| 27 | + |
26 | 28 | from requests.exceptions import HTTPError |
27 | 29 |
|
28 | 30 | from astroquery import log |
@@ -52,19 +54,21 @@ class EuclidClass(TapPlus): |
52 | 54 | __regex_designation = re.compile(r"\s*(\S+)\s(-?\d+)\s*", flags=re.MULTILINE | re.UNICODE) |
53 | 55 |
|
54 | 56 | def __init__(self, *, environment='PDR', tap_plus_conn_handler=None, datalink_handler=None, cutout_handler=None, |
55 | | - verbose=False, show_server_messages=True): |
| 57 | + sia_handler=None, verbose=False, show_server_messages=True): |
56 | 58 | """Constructor for EuclidClass. |
57 | 59 |
|
58 | 60 | Parameters |
59 | 61 | ---------- |
60 | 62 | environment : str, mandatory if no tap, data or cutout hosts is specified, default 'PDR' |
61 | 63 | The Euclid Science Archive environment: 'PDR', 'IDR', 'OTF' and 'REG' |
62 | 64 | tap_plus_conn_handler : tap connection handler object, optional, default None |
63 | | - HTTP(s) connection hander (creator). If no handler is provided, a new one is created. |
64 | | - datalink_handler : dataliink connection handler object, optional, default None |
65 | | - HTTP(s) connection hander (creator). If no handler is provided, a new one is created. |
| 65 | + HTTP(s) connection handler (creator). If no handler is provided, a new one is created. |
| 66 | + datalink_handler : datalink connection handler object, optional, default None |
| 67 | + HTTP(s) connection handler (creator). If no handler is provided, a new one is created. |
66 | 68 | cutout_handler : cutout connection handler object, optional, default None |
67 | | - HTTP(s) connection hander (creator). If no handler is provided, a new one is created. |
| 69 | + HTTP(s) connection handler (creator). If no handler is provided, a new one is created. |
| 70 | + sia_handler : siap connection handler object, optional, default None |
| 71 | + HTTP(s) connection handler (creator). If no handler is provided, a new one is created. |
68 | 72 | verbose : bool, optional, default 'True' |
69 | 73 | flag to display information about the process |
70 | 74 | show_server_messages : bool, optional, default 'True' |
@@ -125,6 +129,20 @@ def __init__(self, *, environment='PDR', tap_plus_conn_handler=None, datalink_ha |
125 | 129 | else: |
126 | 130 | self.__euclidcutout = cutout_handler |
127 | 131 |
|
| 132 | + if sia_handler is None: |
| 133 | + self.__euclidsia = TapPlus(url=url_server, |
| 134 | + server_context="sas-sia", |
| 135 | + tap_context="tap-server", |
| 136 | + upload_context="Upload", |
| 137 | + table_edit_context="TableTool", |
| 138 | + data_context="sia2/query", |
| 139 | + datalink_context="datalink", |
| 140 | + verbose=verbose, |
| 141 | + client_id='ASTROQUERY', |
| 142 | + use_names_over_ids=conf.USE_NAMES_OVER_IDS) |
| 143 | + else: |
| 144 | + self.__euclidsia = sia_handler |
| 145 | + |
128 | 146 | if show_server_messages: |
129 | 147 | self.get_status_messages() |
130 | 148 |
|
@@ -667,6 +685,8 @@ def login(self, *, user=None, password=None, credentials_file=None, verbose=Fals |
667 | 685 | self.__eucliddata.login(user=tap_user, password=tap_password, verbose=verbose) |
668 | 686 | log.info(f"Login to Euclid cutout service: {self.__euclidcutout._TapPlus__getconnhandler().get_host_url()}") |
669 | 687 | self.__euclidcutout.login(user=tap_user, password=tap_password, verbose=verbose) |
| 688 | + log.info(f"Login to Euclid sia service: {self.__euclidsia._TapPlus__getconnhandler().get_host_url()}") |
| 689 | + self.__euclidsia.login(user=tap_user, password=tap_password, verbose=verbose) |
670 | 690 | except HTTPError as err: |
671 | 691 | log.error('Error logging in data or cutout services: %s' % (str(err))) |
672 | 692 | log.error("Logging out from TAP server") |
@@ -711,6 +731,14 @@ def login_gui(self, verbose=False): |
711 | 731 | log.error("Logging out from TAP server") |
712 | 732 | TapPlus.logout(self, verbose=verbose) |
713 | 733 |
|
| 734 | + try: |
| 735 | + log.info(f"Login to Euclid sia server: {self.__euclidsia._TapPlus__getconnhandler().get_host_url()}") |
| 736 | + self.__euclidsia.login(user=tap_user, password=tap_password, verbose=verbose) |
| 737 | + except HTTPError as err: |
| 738 | + log.error('Error logging in sia server: %s' % (str(err))) |
| 739 | + log.error("Logging out from TAP server") |
| 740 | + TapPlus.logout(self, verbose=verbose) |
| 741 | + |
714 | 742 | def logout(self, verbose=False): |
715 | 743 | """ |
716 | 744 | Performs a logout |
@@ -746,6 +774,12 @@ def logout(self, verbose=False): |
746 | 774 | except HTTPError as err: |
747 | 775 | log.error('Error logging out cutout server: %s' % (str(err))) |
748 | 776 |
|
| 777 | + try: |
| 778 | + self.__euclidsia.logout(verbose=verbose) |
| 779 | + log.info("Euclid sia server logout OK") |
| 780 | + except HTTPError as err: |
| 781 | + log.error('Error logging out sia server: %s' % (str(err))) |
| 782 | + |
749 | 783 | @staticmethod |
750 | 784 | def __get_quantity_input(value, msg): |
751 | 785 | if value is None: |
@@ -1087,7 +1121,7 @@ def get_product_list(self, *, observation_id=None, tile_index=None, product_type |
1087 | 1121 | observation id for observations. It is not compatible with parameter tile_index. |
1088 | 1122 |
|
1089 | 1123 | Searchable products by observation_id: 'dpdVisRawFrame', 'dpdNispRawFrame', |
1090 | | - ,'DpdVisCalibratedQuadFrame','DpdVisCalibratedFrameCatalog', 'DpdVisStackedFrame', |
| 1124 | + 'DpdVisCalibratedQuadFrame','DpdVisCalibratedFrameCatalog', 'DpdVisStackedFrame', |
1091 | 1125 | 'DpdVisStackedFrameCatalog', |
1092 | 1126 | 'DpdNirCalibratedFrame', 'DpdNirCalibratedFrameCatalog', 'DpdNirStackedFrameCatalog', 'DpdNirStackedFrame', |
1093 | 1127 | 'DpdMerSegmentationMap', 'dpdMerFinalCatalog', |
@@ -1482,6 +1516,106 @@ def __is_multiple(self, value): |
1482 | 1516 |
|
1483 | 1517 | return not isinstance(value, int) and ((isinstance(value, (list, tuple)) and len(value) > 1) or ',' in value) |
1484 | 1518 |
|
| 1519 | + def get_sia(self, *, coordinates, radius=1.0, search_type='CIRCLE', calibration=2, instrument='ALL', band=None, |
| 1520 | + collection='sedm', dsr_part1=None, dsr_part2=None, dsr_part3=None, output_file=None, verbose=False): |
| 1521 | + """ |
| 1522 | + Access the Euclid Observation Images by VO SIAP v2.0. This service will return public images from Calibrated |
| 1523 | + and Stacked NISP and VIS images, MER Mosaics from VIS and NISP and Level 1 (RAW) images for NISP and VIS |
| 1524 | +
|
| 1525 | + Parameters |
| 1526 | + ---------- |
| 1527 | + coordinates: str or SkyCoord, mandatory, default None |
| 1528 | + coordinates of the center in the cone search |
| 1529 | + radius: float or quantity, optional, default value 1 degree |
| 1530 | + radius in degrees for (int, float) or quantity of the cone_search |
| 1531 | + search_type : str, mandatory, default CIRCLE |
| 1532 | + search region: CIRCLE or BOX |
| 1533 | + calibration: int, optional, default 2 |
| 1534 | + calibration level according to ObsCore VO standard: 0 (raw instrumental data), 1 (instrumental data in a |
| 1535 | + standard format), 2 (science ready data) or 3 (enhanced data products). |
| 1536 | + instrument: str, mandatory, default ALL |
| 1537 | + instrument name: ALL, VIS or NISP |
| 1538 | + band: str, optional, default None |
| 1539 | + filter name only valid if instrument is different from ALL: VIS for instrument VIS or NIR_H, NIR_J, NIR_Y |
| 1540 | + or NISP for instrument NISP |
| 1541 | + collection : str, mandatory, default sedm |
| 1542 | + the name of the data collection |
| 1543 | + dsr_part1: str, optional, default None |
| 1544 | + the data set release part 1: for OTF environment, the activity code; for REG and IDR, the target environment |
| 1545 | + dsr_part2: str, optional, default None |
| 1546 | + the data set release part 2: for OTF environment, the patch id (a positive integer); for REG and IDR, |
| 1547 | + the activity code |
| 1548 | + dsr_part3: str, optional, default None |
| 1549 | + the data set release part 3: for OTF, REG and IDR environment, the version (an integer greater than 1) |
| 1550 | + output_file : string, optional, default None |
| 1551 | + file where the results are saved. |
| 1552 | + verbose : bool, optional, default 'False' |
| 1553 | + flag to display information about the process |
| 1554 | +
|
| 1555 | + Returns |
| 1556 | + ------- |
| 1557 | + An astropy.table or a votable file |
| 1558 | + """ |
| 1559 | + |
| 1560 | + valid_search_types = {'CIRCLE', 'BOX'} |
| 1561 | + valid_calibrations = {0: 'CALIB_ZERO', 1: 'CALIB_ONE', 2: 'CALIB_TWO', 3: 'CALIB_THREE'} |
| 1562 | + valid_instruments = {'ALL', 'VIS', 'NISP'} |
| 1563 | + valid_band_vis = {'VIS'} |
| 1564 | + valid_band_nisp = {'NIR_H', 'NIR_J', 'NIR_Y', 'NISP'} |
| 1565 | + |
| 1566 | + if search_type not in valid_search_types: |
| 1567 | + raise ValueError(f"Invalid search tyype {search_type}") |
| 1568 | + |
| 1569 | + if calibration is not None and calibration not in valid_calibrations: |
| 1570 | + raise ValueError(f"Invalid calibration {calibration}") |
| 1571 | + |
| 1572 | + if instrument not in valid_instruments: |
| 1573 | + raise ValueError(f"Invalid instrument {instrument}") |
| 1574 | + |
| 1575 | + if instrument == 'ALL' and band is not None: |
| 1576 | + raise ValueError(f"For instrument {instrument} band must be None") |
| 1577 | + |
| 1578 | + if instrument == 'VIS' and band is not None and band not in valid_band_vis: |
| 1579 | + raise ValueError(f"Invalid band {band} for instrument {instrument}") |
| 1580 | + |
| 1581 | + if instrument == 'NISP' and band is not None and band not in valid_band_nisp: |
| 1582 | + raise ValueError(f"Invalid band {band} for instrument {instrument}") |
| 1583 | + |
| 1584 | + if radius is not None: |
| 1585 | + coord = commons.parse_coordinates(coordinates=coordinates) |
| 1586 | + ra_deg = coord.ra.degree |
| 1587 | + dec_deg = coord.dec.degree |
| 1588 | + radius_deg = esautils.get_degree_radius(radius) |
| 1589 | + |
| 1590 | + if radius_deg <= 0.0: |
| 1591 | + raise ValueError(f"Search radius is zero or negative: {radius}") |
| 1592 | + else: |
| 1593 | + raise ValueError(f"Invalid coordinates or search radius: {coordinates}, {radius}") |
| 1594 | + |
| 1595 | + params_dict = dict() |
| 1596 | + params_dict['TAPCLIENT'] = 'ASTROQUERY' |
| 1597 | + params_dict['POS'] = f"{search_type},{ra_deg}, {dec_deg},{radius_deg}" |
| 1598 | + params_dict['INSTRUMENT'] = instrument |
| 1599 | + params_dict['COLLECTION'] = collection |
| 1600 | + |
| 1601 | + if calibration is not None: |
| 1602 | + params_dict['CALIB'] = valid_calibrations[calibration] |
| 1603 | + |
| 1604 | + if instrument != 'ALL' and band is not None: |
| 1605 | + params_dict['BAND'] = band |
| 1606 | + |
| 1607 | + if dsr_part1 is not None: |
| 1608 | + params_dict['DSP1'] = dsr_part1 |
| 1609 | + |
| 1610 | + if dsr_part2 is not None: |
| 1611 | + params_dict['DSP2'] = dsr_part2 |
| 1612 | + |
| 1613 | + if dsr_part3 is not None: |
| 1614 | + params_dict['DSP3'] = dsr_part3 |
| 1615 | + |
| 1616 | + return self.__euclidsia.load_data(params_dict=params_dict, output_file=output_file, http_method='GET', |
| 1617 | + verbose=verbose) |
| 1618 | + |
1485 | 1619 | @deprecated_renamed_argument(('instrument', 'id'), (None, None), since='0.4.12') |
1486 | 1620 | def get_cutout(self, *, file_path=None, coordinate, radius, output_file=None, verbose=False, instrument=None, |
1487 | 1621 | id=None): |
|
0 commit comments