Skip to content

Commit f4a18a6

Browse files
authored
Merge branch 'astropy:main' into feature/ISSUE-3138-TAP
2 parents 685c807 + 01939a4 commit f4a18a6

File tree

24 files changed

+999
-589
lines changed

24 files changed

+999
-589
lines changed

CHANGES.rst

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ gaia
2121
response any more. [#3278]
2222
- New datalink retrieve types EPOCH_PHOTOMETRY_CROWDED_FIELD, EPOCH_ASTROMETRY_BRIGHT, XP_MEAN_SPECTRUM_GRAVLENS,
2323
EPOCH_FLAGS_NSS, EPOCH_PARAMETERS_RVS_SINGLE, EPOCH_PARAMETERS_RVS_DOUBLE, EPOCH_FLAGS_VARI. [#3371, #3372]
24+
- Rename datalink retrieval types EPOCH_SPECTRUM_RVS, EPOCH_SPECTRUM_XP_SSO, EPOCH_SPECTRUM_XP_CROWDING, MEAN_SPECTRUM_XP,
25+
EPOCH_SPECTRUM_XP and MEAN_SPECTRUM_XP_GRAVLENS. [#3382]
26+
27+
mast
28+
^^^^
29+
30+
- Deprecated the ``product`` parameter in the ``Tesscut.get_sectors``, ``Tesscut.get_cutouts``, and ``Tesscut.download_cutouts`` methods.
31+
Support for TESS Image Calibration (TICA) high-level science products has been removed; only Science Processing Operations Center (SPOC)
32+
products are now supported. [#3391]
2433

2534
Service fixes and enhancements
2635
------------------------------
@@ -36,6 +45,7 @@ esa.euclid
3645

3746
- New method, get_scientific_product_list, to retrieve scientific LE3
3847
products. [#3313]
48+
- New cross-match method [#3386]
3949

4050
eso
4151
^^^
@@ -122,6 +132,9 @@ mast
122132

123133
- Improved ``MastMissions`` queries to accept lists for query critieria values, in addition to comma-delimited strings. [#3319]
124134

135+
- Enhanced ``filter_products`` methods in ``MastMissions`` and ``Observations`` to support advanced filtering expressions
136+
for numeric columns. [#3365]
137+
125138

126139
Infrastructure, Utility and Other Changes and Additions
127140
-------------------------------------------------------
@@ -144,6 +157,8 @@ utils
144157

145158
- Raising cleaner errors earlier when server returns with error. [#3284]
146159

160+
- ``return_frame`` parameter in ``utils.commons.parse_coordinates`` returns coordinates in the specified frame. [#3164]
161+
147162

148163
0.4.10 (2025-03-18)
149164
===================
@@ -2280,4 +2295,4 @@ Infrastructure, Utility and Other Changes and Additions
22802295
0.1 (2013-09-19)
22812296
================
22822297

2283-
- Initial release. Includes features!
2298+
- Initial release. Includes features!

astroquery/alma/tests/test_alma_remote.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737

3838
download_hostname = 'almascience.eso.org'
3939

40+
# The MAXREC related overflow message is different in pyvo 1.7+, remove workaround when we have it as a minimum
41+
overflow_message = r"Partial result set. Potential causes MAXREC|Result set limited by user- or server-supplied MAXREC"
42+
4043

4144
@pytest.fixture
4245
def alma(request):
@@ -56,14 +59,12 @@ def alma(request):
5659
@pytest.mark.remote_data
5760
class TestAlma:
5861
def test_public(self, alma):
59-
with pytest.warns(expected_warning=DALOverflowWarning,
60-
match="Partial result set. Potential causes MAXREC, async storage space, etc."):
62+
with pytest.warns(expected_warning=DALOverflowWarning, match=overflow_message):
6163
results = alma.query(payload=None, public=True, maxrec=100)
6264
assert len(results) == 100
6365
for row in results:
6466
assert row['data_rights'] == 'Public'
65-
with pytest.warns(expected_warning=DALOverflowWarning,
66-
match="Partial result set. Potential causes MAXREC, async storage space, etc."):
67+
with pytest.warns(expected_warning=DALOverflowWarning, match=overflow_message):
6768
results = alma.query(payload=None, public=False, maxrec=100)
6869
assert len(results) == 100
6970
for row in results:
@@ -105,8 +106,7 @@ def test_freq(self, alma):
105106
def test_bands(self, alma):
106107
payload = {'band_list': ['5', '7']}
107108
# Added maxrec here as downloading and reading the results take too long.
108-
with pytest.warns(expected_warning=DALOverflowWarning,
109-
match="Partial result set. Potential causes MAXREC, async storage space, etc."):
109+
with pytest.warns(expected_warning=DALOverflowWarning, match=overflow_message):
110110
result = alma.query(payload, maxrec=1000)
111111
assert len(result) > 0
112112
for row in result:
@@ -342,8 +342,7 @@ def test_misc(self, alma):
342342

343343
result = alma.query_object('M83', public=True, science=True)
344344
assert len(result) > 0
345-
with pytest.warns(expected_warning=DALOverflowWarning,
346-
match="Partial result set. Potential causes MAXREC, async storage space, etc."):
345+
with pytest.warns(expected_warning=DALOverflowWarning, match=overflow_message):
347346
result = alma.query(payload={'pi_name': 'Bally*'}, public=True,
348347
maxrec=10)
349348
assert result

astroquery/esa/euclid/core.py

Lines changed: 154 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from astroquery import log
2424
from astroquery.utils import commons
2525
from astroquery.utils.tap import TapPlus
26+
from astroquery.utils.tap import taputils
2627
from . import conf
2728

2829

@@ -116,6 +117,156 @@ def __init__(self, *, environment='PDR', tap_plus_conn_handler=None, datalink_ha
116117
if show_server_messages:
117118
self.get_status_messages()
118119

120+
def cross_match_basic(self, *, table_a_full_qualified_name, table_a_column_ra, table_a_column_dec,
121+
table_b_full_qualified_name=None, table_b_column_ra=None, table_b_column_dec=None,
122+
results_name=None,
123+
radius=1.0, background=False, verbose=False):
124+
"""Performs a positional cross-match between the specified tables.
125+
126+
This method carries out the following steps in one step:
127+
128+
1. updates the user table metadata to flag the positional RA/Dec columns;
129+
2. launches a positional cross-match as an asynchronous query;
130+
3. returns all the columns from both tables plus the angular distance (deg) for the cross-matched sources.
131+
132+
The result is a join table with the identifies of both tables and the distance (degrees), that is returned
133+
without metadata units. If desired, units can be added using the Units package of Astropy as follows:
134+
``results[‘separation’].unit = u.degree``. To speed up the cross-match, pass the biggest table to the
135+
``table_b_full_qualified_name`` parameter.
136+
TAP+ only
137+
138+
Parameters
139+
----------
140+
table_a_full_qualified_name : str, mandatory
141+
a full qualified table name (i.e. schema name and table name)
142+
table_a_column_ra : str, mandatory
143+
the ‘ra’ column in the table table_a_full_qualified_name
144+
table_a_column_dec : str, mandatory
145+
the ‘dec’ column in the table table_a_full_qualified_name
146+
table_b_full_qualified_name : str, optional, default the main_table associated to the selected environment
147+
a full qualified table name (i.e. schema name and table name)
148+
table_b_column_ra : str, optional, default the main_table_ra_column associated to the selected environment
149+
the ‘ra’ column in the table table_b_full_qualified_name
150+
table_b_column_dec : str, default the main_table_dec_column associated to the selected environment
151+
the ‘dec’ column in the table table_b_full_qualified_name
152+
results_name : str, optional, default None
153+
custom name defined by the user for the job that is going to be created
154+
radius : float (arc. seconds), str or astropy.coordinate, optional, default 1.0
155+
radius (valid range: 0.1-10.0). For an astropy.coordinate any angular unit is valid, but its value in arc
156+
sec must be contained within the valid range.
157+
background : bool, optional, default 'False'
158+
when the job is executed in asynchronous mode, this flag specifies
159+
whether the execution will wait until results are available
160+
verbose : bool, optional, default 'False'
161+
flag to display information about the process
162+
163+
Returns
164+
-------
165+
A Job object
166+
"""
167+
168+
radius_quantity = self.__get_radius_as_quantity_arcsec(radius)
169+
170+
radius_arc_sec = radius_quantity.value
171+
172+
if radius_arc_sec < 0.1 or radius_arc_sec > 10.0:
173+
raise ValueError(f"Invalid radius value. Found {radius_quantity}, valid range is: 0.1 to 10.0")
174+
175+
schema_a = self.__get_schema_name(table_a_full_qualified_name)
176+
if not schema_a:
177+
raise ValueError(f"Schema name is empty in full qualified table: '{table_a_full_qualified_name}'")
178+
179+
if table_b_full_qualified_name is None:
180+
table_b_full_qualified_name = self.main_table
181+
table_b_column_ra = self.main_table_ra
182+
table_b_column_dec = self.main_table_dec
183+
else:
184+
if table_b_column_ra is None or table_b_column_dec is None:
185+
raise ValueError(f"Invalid ra or dec column names: '{table_b_column_ra}' and '{table_b_column_dec}'")
186+
187+
schema_b = self.__get_schema_name(table_b_full_qualified_name)
188+
if not schema_b:
189+
raise ValueError(f"Schema name is empty in full qualified table: '{table_b_full_qualified_name}'")
190+
191+
table_metadata_a = self.__get_table_metadata(table_a_full_qualified_name, verbose)
192+
193+
table_metadata_b = self.__get_table_metadata(table_b_full_qualified_name, verbose)
194+
195+
self.__check_columns_exist(table_metadata_a, table_a_full_qualified_name, table_a_column_ra, table_a_column_dec)
196+
197+
self.__update_ra_dec_columns(table_a_full_qualified_name, table_a_column_ra, table_a_column_dec,
198+
table_metadata_a, verbose)
199+
200+
self.__check_columns_exist(table_metadata_b, table_b_full_qualified_name, table_b_column_ra, table_b_column_dec)
201+
202+
self.__update_ra_dec_columns(table_b_full_qualified_name, table_b_column_ra, table_b_column_dec,
203+
table_metadata_b, verbose)
204+
205+
query = (
206+
f"SELECT a.*, DISTANCE(a.{table_a_column_ra}, a.{table_a_column_dec}, b.{table_b_column_ra}, "
207+
f"b.{table_b_column_dec}) AS separation, b.* "
208+
f"FROM {table_a_full_qualified_name} AS a JOIN {table_b_full_qualified_name} AS b "
209+
f"ON DISTANCE(a.{table_a_column_ra}, a.{table_a_column_dec}, b.{table_b_column_ra}, b.{table_b_column_dec})"
210+
f" < {radius_quantity.to(u.deg).value}")
211+
212+
return self.launch_job_async(query=query, name=results_name, output_file=None, output_format="votable_gzip",
213+
verbose=verbose, dump_to_file=False, background=background, upload_resource=None,
214+
upload_table_name=None)
215+
216+
def __get_radius_as_quantity_arcsec(self, radius):
217+
"""
218+
transform the input radius into an astropy.Quantity in arc seconds
219+
"""
220+
if not isinstance(radius, units.Quantity):
221+
radius_quantity = Quantity(value=radius, unit=u.arcsec)
222+
else:
223+
radius_quantity = radius.to(u.arcsec)
224+
return radius_quantity
225+
226+
def __update_ra_dec_columns(self, full_qualified_table_name, column_ra, column_dec, table_metadata, verbose):
227+
"""
228+
Update table metadata for the ‘ra’ and the ‘dec’ columns in the input table
229+
"""
230+
if full_qualified_table_name.startswith("user_"):
231+
list_of_changes = list()
232+
for column in table_metadata.columns:
233+
if column.name == column_ra and column.flags != '1':
234+
list_of_changes.append([column_ra, "flags", "Ra"])
235+
list_of_changes.append([column_ra, "indexed", True])
236+
if column.name == column_dec and column.flags != '2':
237+
list_of_changes.append([column_dec, "flags", "Dec"])
238+
list_of_changes.append([column_dec, "indexed", True])
239+
240+
if list_of_changes:
241+
TapPlus.update_user_table(self, table_name=full_qualified_table_name, list_of_changes=list_of_changes,
242+
verbose=verbose)
243+
244+
def __check_columns_exist(self, table_metadata_a, full_qualified_table_name, column_ra, column_dec):
245+
"""
246+
Check whether the ‘ra’ and the ‘dec’ columns exists the input table
247+
"""
248+
column_names = [column.name for column in table_metadata_a.columns]
249+
if column_ra not in column_names or column_dec not in column_names:
250+
raise ValueError(
251+
f"Please check: columns {column_ra} or {column_dec} not available in the table '"
252+
f"{full_qualified_table_name}'")
253+
254+
def __get_table_metadata(self, full_qualified_table_name, verbose):
255+
"""
256+
Get the table metadata for the input table
257+
"""
258+
259+
return self.load_table(table=full_qualified_table_name, verbose=verbose)
260+
261+
def __get_schema_name(self, full_qualified_table_name):
262+
"""
263+
Get the schema name from the full qualified table
264+
"""
265+
schema = taputils.get_schema_name(full_qualified_table_name)
266+
if schema is None:
267+
raise ValueError(f"Not found schema name in full qualified table: '{full_qualified_table_name}'")
268+
return schema
269+
119270
def launch_job(self, query, *, name=None, dump_to_file=False, output_file=None, output_format="csv", verbose=False,
120271
upload_resource=None, upload_table_name=None):
121272
"""
@@ -282,10 +433,10 @@ def __cone_search(self, coordinate, radius, *, table_name=None, ra_column_name=N
282433
coordinates center point
283434
radius : astropy.units, mandatory
284435
radius
285-
table_name : str, optional, default main gaia table name doing the cone search against
286-
ra_column_name : str, optional, default ra column in main gaia table
436+
table_name : str, optional, default main table name doing the cone search against
437+
ra_column_name : str, optional, default ra column in main table
287438
ra column doing the cone search against
288-
dec_column_name : str, optional, default dec column in main gaia table
439+
dec_column_name : str, optional, default dec column in main table
289440
dec column doing the cone search against
290441
async_job : bool, optional, default 'False'
291442
executes the job in asynchronous/synchronous mode (default

0 commit comments

Comments
 (0)