Skip to content

Commit c09c9eb

Browse files
authored
Merge pull request #3573 from esdc-esac-esa-int/ESA_plato-new-module
ESA Plato Astroquery module
2 parents f94605a + 237a3e0 commit c09c9eb

10 files changed

Lines changed: 1050 additions & 0 deletions

File tree

CHANGES.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
New Tools and Services
55
----------------------
66

7+
esa.plato
8+
^^^^^^^^^
9+
- New module to access the ESA PLATO Science Archive. [#3573]
10+
711
noirlab
812
^^^^^^^
913

astroquery/esa/plato/__init__.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""
2+
==========
3+
PLATO Init
4+
==========
5+
6+
European Space Astronomy Centre (ESAC)
7+
European Space Agency (ESA)
8+
9+
"""
10+
11+
from astropy import config as _config
12+
13+
PLATO_DOMAIN = 'https://pax.esac.esa.int/tap/'
14+
PLATO_TAP_URL = PLATO_DOMAIN + 'tap'
15+
16+
17+
class Conf(_config.ConfigNamespace):
18+
"""
19+
Configuration parameters for `astroquery.esa.plato`.
20+
"""
21+
PLATO_TAP_SERVER = _config.ConfigItem(PLATO_TAP_URL, "PLATO TAP Server")
22+
PLATO_DATA_SERVER = _config.ConfigItem(PLATO_DOMAIN + 'data?', "PLATO Data Server")
23+
PLATO_LOGIN_SERVER = _config.ConfigItem(PLATO_DOMAIN + 'login', "PLATO Login Server")
24+
PLATO_LOGOUT_SERVER = _config.ConfigItem(PLATO_DOMAIN + 'logout', "PLATO Logout Server")
25+
PLATO_SERVLET = _config.ConfigItem(PLATO_TAP_URL + "/sync/?PHASE=RUN",
26+
"plato Sync Request")
27+
PLATO_TARGET_RESOLVER = _config.ConfigItem(PLATO_DOMAIN + "servlet/target-resolver?TARGET_NAME={}"
28+
"&RESOLVER_TYPE={}&FORMAT=json",
29+
"PLATO Target Resolver Request")
30+
PLATO_LOGO = _config.ConfigItem('https://pax.esac.esa.int/plato/assets/images/plato_logo.png', "PLATO logo")
31+
32+
TIMEOUT = 60
33+
34+
35+
conf = Conf()
36+
37+
from .core import Plato, PlatoClass
38+
39+
__all__ = ['Plato', 'PlatoClass', 'Conf', 'conf']

astroquery/esa/plato/core.py

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
# Licensed under a 3-clause BSD style license - see LICENSE.rst
2+
"""
3+
=======================
4+
PLATO Astroquery Module
5+
=======================
6+
7+
European Space Astronomy Centre (ESAC)
8+
European Space Agency (ESA)
9+
10+
"""
11+
from astroquery.utils import commons
12+
13+
from . import conf
14+
import astroquery.esa.utils.utils as esautils
15+
import astropy.units as u
16+
17+
__all__ = ['Plato', 'PlatoClass']
18+
19+
20+
class PlatoClass(esautils.EsaTap):
21+
22+
"""
23+
This module connects with ESA Plato TAP
24+
"""
25+
26+
ESA_ARCHIVE_NAME = "PLATO"
27+
TAP_URL = conf.PLATO_TAP_SERVER
28+
LOGIN_URL = conf.PLATO_LOGIN_SERVER
29+
LOGOUT_URL = conf.PLATO_LOGOUT_SERVER
30+
31+
def search_catalogue(self, table_name, *, target_name=None, coordinates=None, radius=1*u.arcmin, columns=None,
32+
get_metadata=False, output_file=None, **filters):
33+
"""
34+
Execute a search in one of the catalogues and associated tables available in PLATO TAP.
35+
36+
Parameters
37+
----------
38+
table_name: str, mandatory
39+
Table name of the catalogue to be searched
40+
target_name: str, optional
41+
Name of the target to be resolved against SIMBAD/NED/VIZIER
42+
coordinates: str or SkyCoord, optional
43+
coordinates of the center in the cone search
44+
radius: float or quantity, optional, default value 14 degrees
45+
radius in degrees (int, float) or quantity of the cone_search
46+
columns : str or list of str, optional, default None
47+
Columns from the table to be retrieved. They can be checked using
48+
get_metadata=True
49+
get_metadata : bool, optional, default False
50+
Get the table metadata to verify the columns that can be filtered
51+
output_file : str, optional, default None
52+
file name where the results are saved.
53+
If this parameter is not provided, the jobid is used instead
54+
**filters : str, optional, default None
55+
Filters to be applied to the search. The column name is the keyword and the value is any
56+
value accepted by the column datatype. They will be
57+
used to generate the SQL filters for the query. Some examples are described below,
58+
where the left side is the parameter defined for this method and the right side the
59+
SQL filter generated:
60+
StarName='star1' -> StarName = 'star1'
61+
StarName='star*' -> StarName ILIKE 'star%'
62+
StarName='star%' -> StarName ILIKE 'star%'
63+
StarName=['star1', 'star2'] -> StarName = 'star1' OR StarName - 'star2'
64+
ra=('>', 30) -> ra > 30
65+
ra=(20, 30) -> ra >= 20 AND ra <= 30
66+
67+
Returns
68+
-------
69+
An astropy.table object containing the results of the filters in the PIC target catalogue
70+
"""
71+
cone_search_filter = None
72+
if radius is not None:
73+
radius = esautils.get_degree_radius(radius)
74+
75+
if target_name and coordinates:
76+
raise TypeError("Please use only target_name or coordinates as "
77+
"parameter, not both.")
78+
elif target_name:
79+
coordinates = esautils.resolve_target(conf.PLATO_TARGET_RESOLVER,
80+
self.tap._session, target_name,
81+
'ALL')
82+
cone_search_filter = self.create_cone_search_query(coordinates.ra.deg, coordinates.dec.deg,
83+
"RAdeg", "DEdeg", radius)
84+
elif coordinates:
85+
coord = commons.parse_coordinates(coordinates=coordinates)
86+
ra = coord.ra.degree
87+
dec = coord.dec.degree
88+
cone_search_filter = self.create_cone_search_query(ra, dec, "RAdeg", "DEdeg", radius)
89+
90+
return self.query_table(table_name=table_name, columns=columns,
91+
custom_filters=cone_search_filter, get_metadata=get_metadata, async_job=True,
92+
output_file=output_file, **filters)
93+
94+
def search_pic_target_go(self, *, target_name=None, coordinates=None, radius=1*u.arcmin, columns=None,
95+
get_metadata=False, output_file=None, **filters):
96+
"""
97+
Execute a search for sources in the PLATO Input Catalogue (PIC).
98+
99+
Parameters
100+
----------
101+
target_name: str, optional
102+
Name of the target to be resolved against SIMBAD/NED/VIZIER
103+
coordinates: str or SkyCoord, optional
104+
coordinates of the center in the cone search
105+
radius: float or quantity, optional, default value 14 degrees
106+
radius in degrees (int, float) or quantity of the cone_search
107+
columns : str or list of str, optional, default None
108+
Columns from the table to be retrieved. They can be checked using
109+
get_metadata=True
110+
get_metadata : bool, optional, default False
111+
Get the table metadata to verify the columns that can be filtered
112+
output_file : str, optional, default None
113+
file name where the results are saved.
114+
If this parameter is not provided, the jobid is used instead
115+
**filters : str, optional, default None
116+
Filters to be applied to the search. The column name is the keyword and the value is any
117+
value accepted by the column datatype. They will be
118+
used to generate the SQL filters for the query. Some examples are described below,
119+
where the left side is the parameter defined for this method and the right side the
120+
SQL filter generated:
121+
StarName='star1' -> StarName = 'star1'
122+
StarName='star*' -> StarName ILIKE 'star%'
123+
StarName='star%' -> StarName ILIKE 'star%'
124+
StarName=['star1', 'star2'] -> StarName = 'star1' OR StarName - 'star2'
125+
ra=('>', 30) -> ra > 30
126+
ra=(20, 30) -> ra >= 20 AND ra <= 30
127+
128+
Returns
129+
-------
130+
An astropy.table object containing the results of the filters in the PIC target catalogue
131+
"""
132+
133+
return self.search_catalogue(table_name='pic_go.pic_target_go', target_name=target_name,
134+
coordinates=coordinates, radius=radius, columns=columns,
135+
get_metadata=get_metadata, output_file=output_file, **filters)
136+
137+
def search_pic_contaminant_go(self, *, target_name=None, coordinates=None, radius=1*u.arcmin, columns=None,
138+
get_metadata=False, output_file=None, **filters):
139+
"""
140+
Execute a search for contaminant sources in the PLATO Input Catalogue (PIC).
141+
142+
Parameters
143+
----------
144+
target_name: str, optional
145+
Name of the target to be resolved against SIMBAD/NED/VIZIER
146+
coordinates: str or SkyCoord, optional
147+
coordinates of the center in the cone search
148+
radius: float or quantity, optional, default value 14 degrees
149+
radius in degrees (int, float) or quantity of the cone_search
150+
columns : str or list of str, optional, default None
151+
Columns from the table to be retrieved. They can be checked using
152+
get_metadata=True
153+
get_metadata : bool, optional, default False
154+
Get the table metadata to verify the columns that can be filtered
155+
output_file : str, optional, default None
156+
file name where the results are saved.
157+
If this parameter is not provided, the jobid is used instead
158+
**filters : str, optional, default None
159+
Filters to be applied to the search. The column name is the keyword and the value is any
160+
value accepted by the column datatype. They will be
161+
used to generate the SQL filters for the query. Some examples are described below,
162+
where the left side is the parameter defined for this method and the right side the
163+
SQL filter generated:
164+
StarName='star1' -> StarName = 'star1'
165+
StarName='star*' -> StarName ILIKE 'star%'
166+
StarName='star%' -> StarName ILIKE 'star%'
167+
StarName=['star1', 'star2'] -> StarName = 'star1' OR StarName - 'star2'
168+
ra=('>', 30) -> ra > 30
169+
ra=(20, 30) -> ra >= 20 AND ra <= 30
170+
171+
Returns
172+
-------
173+
An astropy.table object containing the results of the filters in the PIC target catalogue
174+
"""
175+
176+
return self.search_catalogue(table_name='pic_go.pic_contaminant_go', target_name=target_name,
177+
coordinates=coordinates, radius=radius, columns=columns,
178+
get_metadata=get_metadata, output_file=output_file, **filters)
179+
180+
def plot_plato_results(self, x, y, x_label, y_label, plot_title, *, z=None, z_label=None, error_x=None,
181+
error_y=None, log_scale=False, color='gray', fig=None, ax=None):
182+
"""
183+
Draw two columns in a 2D plot with/without a third column drawn with a colormap
184+
185+
Parameters
186+
----------
187+
x : array of numbers, mandatory
188+
values for the X series
189+
y : array of numbers, mandatory
190+
values for the Y series
191+
x_label : str, mandatory
192+
title of the X axis
193+
y_label : str, mandatory
194+
title of the Y axis
195+
plot_title : str, mandatory
196+
title of the plot
197+
z : array of numbers, optional
198+
values to assign a color to each dot
199+
z_label : str, optional
200+
title of the colormap
201+
error_x : array of numbers, optional
202+
error on the X series
203+
error_y : array of numbers, optional
204+
error on the Y series
205+
log_scale : boolean, optional, default False
206+
Draw X and Y axes using log scale
207+
color : str, optional, default gray
208+
Color to be used in the 2D plot or a colormap if Z axis is defined
209+
fig : matplotlib.figure.Figure, optional
210+
Existing figure to draw on. If None, a new figure is created.
211+
ax : matplotlib.axes.Axes, optional
212+
Existing axes to draw on. If None, a new axes object is created.
213+
214+
Returns
215+
-------
216+
fig : matplotlib.figure.Figure
217+
The figure containing the plot.
218+
ax : matplotlib.axes.Axes
219+
The axes object used for plotting.
220+
"""
221+
222+
# Matplotlib is needed to execute this method
223+
try:
224+
import matplotlib.pyplot as plt
225+
# Enable interactive mode
226+
plt.ion()
227+
except ImportError:
228+
raise ImportError(
229+
"This feature requires 'matplotlib' library. "
230+
"Install it with: pip install matplotlib"
231+
)
232+
233+
if fig is None or ax is None:
234+
fig, ax = plt.subplots()
235+
236+
# Draw the PLATO logo in the image
237+
try:
238+
logo = esautils.load_image_from_url(conf.PLATO_LOGO)
239+
logo = logo.convert("RGBA")
240+
# Create a small axes for the image (figure coordinates! Not data)
241+
logo_ax = fig.add_axes((0.89, 0.85, 0.15, 0.15), anchor='NW')
242+
logo_ax.imshow(logo)
243+
logo_ax.axis("off")
244+
except Exception:
245+
pass # If the image cannot be loaded, just ignore it
246+
247+
# Error bars behind the data series
248+
if error_x is not None or error_y is not None:
249+
ax.errorbar(
250+
x, y,
251+
xerr=error_x,
252+
yerr=error_y,
253+
fmt='none',
254+
ecolor='black',
255+
capsize=2,
256+
zorder=2
257+
)
258+
259+
# Scatter plot with the selected data (2 or 3 lists)
260+
if z is not None:
261+
sc = ax.scatter(x, y, c=z, cmap=color, zorder=3)
262+
cbar = fig.colorbar(sc, ax=ax)
263+
if z_label:
264+
cbar.set_label(z_label)
265+
else:
266+
ax.scatter(x, y, color=color, zorder=3)
267+
268+
# Labels, scale and title
269+
ax.set_xlabel(x_label)
270+
ax.set_ylabel(y_label)
271+
272+
if log_scale:
273+
ax.set_xscale('log')
274+
ax.set_yscale('log')
275+
276+
if 'RA (deg)' in x_label:
277+
ax.invert_xaxis()
278+
279+
ax.set_title(plot_title)
280+
281+
plt.show(block=False)
282+
283+
return fig, ax
284+
285+
286+
Plato = PlatoClass()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Licensed under a 3-clause BSD style license - see LICENSE.rst
2+
"""
3+
===============
4+
PLATO TEST Init
5+
===============
6+
7+
European Space Astronomy Centre (ESAC)
8+
European Space Agency (ESA)
9+
10+
"""
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Licensed under a 3-clause BSD style license - see LICENSE.rst
2+
"""
3+
=================
4+
PLATO Mocks tests
5+
=================
6+
7+
European Space Astronomy Centre (ESAC)
8+
European Space Agency (ESA)
9+
10+
"""
11+
from unittest.mock import Mock
12+
from requests import HTTPError
13+
14+
from pyvo.dal import DALResults
15+
from astropy.io.votable.tree import VOTableFile
16+
from astropy.table import Table
17+
18+
instruments_value = ["i1"]
19+
bands_value = ["b1"]
20+
21+
22+
def get_dal_table():
23+
data = {
24+
"source_id": [123456789012345, 234567890123456, 345678901234567],
25+
"ra": [266.404997, 266.424512, 266.439127],
26+
"dec": [-28.936173, -28.925636, -28.917813]
27+
}
28+
29+
# Create an Astropy Table with the mock data
30+
astropy_table = Table(data)
31+
32+
return DALResults(VOTableFile.from_table(astropy_table))
33+
34+
35+
def get_empty_table():
36+
data = {
37+
"source_id": [],
38+
}
39+
40+
# Create an Astropy Table with the mock data
41+
return Table(data)
42+
43+
44+
def get_mock_response():
45+
error_message = "Mocked HTTP error"
46+
mock_response = Mock()
47+
mock_response.raise_for_status.side_effect = HTTPError(error_message)
48+
return mock_response

0 commit comments

Comments
 (0)