Skip to content

Commit b3b54e9

Browse files
author
C. E. Brasseur
authored
Merge pull request #2121 from ceb8/moving_target_tesscut
MAST: Moving target TESScut
2 parents 28bcee6 + 2a127ae commit b3b54e9

File tree

4 files changed

+479
-105
lines changed

4 files changed

+479
-105
lines changed

astroquery/mast/cutouts.py

Lines changed: 146 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,13 @@ def __init__(self):
104104
super().__init__()
105105

106106
services = {"sector": {"path": "sector"},
107-
"astrocut": {"path": "astrocut"}}
107+
"astrocut": {"path": "astrocut"},
108+
"mt_sector": {"path": "moving_target/sector"},
109+
"mt_astrocut": {"path": "moving_target/astrocut"}
110+
}
108111
self._service_api_connection.set_service_params(services, "tesscut")
109112

110-
def get_sectors(self, coordinates=None, radius=0*u.deg, objectname=None):
113+
def get_sectors(self, coordinates=None, radius=0*u.deg, objectname=None, moving_target=False, mt_type=None):
111114
"""
112115
Get a list of the TESS data sectors whose footprints intersect
113116
with the given search area.
@@ -117,36 +120,73 @@ def get_sectors(self, coordinates=None, radius=0*u.deg, objectname=None):
117120
coordinates : str or `astropy.coordinates` object, optional
118121
The target around which to search. It may be specified as a
119122
string or as the appropriate `astropy.coordinates` object.
120-
One and only one of coordinates and objectname must be supplied.
123+
124+
NOTE: If moving_target or objectname is supplied, this argument cannot be used.
121125
radius : str, float, or `~astropy.units.Quantity` object, optional
122126
Default 0 degrees.
123127
If supplied as a float degrees is the assumed unit.
124128
The string must be parsable by `~astropy.coordinates.Angle`. The
125129
appropriate `~astropy.units.Quantity` object from
126130
`astropy.units` may also be used.
131+
132+
NOTE: If moving_target is supplied, this argument is ignored.
127133
objectname : str, optional
128134
The target around which to search, by name (objectname="M104")
129-
or TIC ID (objectname="TIC 141914082").
130-
One and only one of coordinates and objectname must be supplied.
135+
or TIC ID (objectname="TIC 141914082"). If moving_target is True, input must be the name or ID (as understood by the
136+
`JPL ephemerides service <https://ssd.jpl.nasa.gov/horizons.cgi>`__)
137+
of a moving target such as an asteroid or comet.
138+
139+
NOTE: If coordinates is supplied, this argument cannot be used.
140+
moving_target : bool, optional
141+
Indicate whether the object is a moving target or not. Default is set to False, in other words, not a moving target.
142+
143+
NOTE: If coordinates is supplied, this argument cannot be used.
144+
mt_type : str, optional
145+
The moving target type, valid inputs are majorbody and smallbody. If not supplied
146+
first majorbody is tried and then smallbody if a matching majorbody is not found.
147+
148+
NOTE: If moving_target is supplied, this argument is ignored.
131149
132150
Returns
133151
-------
134152
response : `~astropy.table.Table`
135-
Sector/camera/chip information for given coordinates/raduis.
153+
Sector/camera/chip information for given coordinates/objectname/moving_target.
136154
"""
137155

138-
# Get Skycoord object for coordinates/object
139-
coordinates = parse_input_location(coordinates, objectname)
156+
if moving_target:
140157

141-
# If radius is just a number we assume degrees
142-
radius = Angle(radius, u.deg)
158+
# Check that objectname has been passed in and coordinates
159+
# is not
160+
if coordinates:
161+
raise InvalidQueryError("Only one of moving_target and coordinates may be specified. Please remove coordinates if using moving_target and objectname.")
143162

144-
params = {"ra": coordinates.ra.deg,
145-
"dec": coordinates.dec.deg,
146-
"radius": radius.deg}
163+
if not objectname:
164+
raise InvalidQueryError("Please specify the object name or ID (as understood by the `JPL ephemerides service <https://ssd.jpl.nasa.gov/horizons.cgi>`__) of a moving target such as an asteroid or comet.")
147165

148-
response = self._service_api_connection.service_request_async("sector", params)
149-
response.raise_for_status() # Raise any errors
166+
params = {"obj_id": objectname}
167+
168+
# Add optional parameter is present
169+
if mt_type:
170+
params["obj_type"] = mt_type
171+
172+
response = self._service_api_connection.service_request_async("mt_sector", params)
173+
174+
else:
175+
176+
# Get Skycoord object for coordinates/object
177+
coordinates = parse_input_location(coordinates, objectname)
178+
179+
# If radius is just a number we assume degrees
180+
radius = Angle(radius, u.deg)
181+
182+
params = {"ra": coordinates.ra.deg,
183+
"dec": coordinates.dec.deg,
184+
"radius": radius.deg}
185+
186+
response = self._service_api_connection.service_request_async("sector", params)
187+
188+
# Raise any errors
189+
response.raise_for_status()
150190

151191
sector_json = response.json()['results']
152192
sector_dict = {'sectorName': [],
@@ -164,7 +204,8 @@ def get_sectors(self, coordinates=None, radius=0*u.deg, objectname=None):
164204
warnings.warn("Coordinates are not in any TESS sector.", NoResultsWarning)
165205
return Table(sector_dict)
166206

167-
def download_cutouts(self, coordinates=None, size=5, sector=None, path=".", inflate=True, objectname=None):
207+
def download_cutouts(self, coordinates=None, size=5, sector=None, path=".", inflate=True,
208+
objectname=None, moving_target=False, mt_type=None):
168209
"""
169210
Download cutout target pixel file(s) around the given coordinates with indicated size.
170211
@@ -173,7 +214,8 @@ def download_cutouts(self, coordinates=None, size=5, sector=None, path=".", infl
173214
coordinates : str or `astropy.coordinates` object, optional
174215
The target around which to search. It may be specified as a
175216
string or as the appropriate `astropy.coordinates` object.
176-
One and only one of coordinates and objectname must be supplied.
217+
218+
NOTE: If moving_target or objectname is supplied, this argument cannot be used.
177219
size : int, array-like, `~astropy.units.Quantity`
178220
Optional, default 5 pixels.
179221
The size of the cutout array. If ``size`` is a scalar number or
@@ -197,28 +239,56 @@ def download_cutouts(self, coordinates=None, size=5, sector=None, path=".", infl
197239
Set inflate to false to stop before the inflate step.
198240
objectname : str, optional
199241
The target around which to search, by name (objectname="M104")
200-
or TIC ID (objectname="TIC 141914082").
201-
One and only one of coordinates and objectname must be supplied.
242+
or TIC ID (objectname="TIC 141914082"). If moving_target is True, input must be the name or ID (as understood by the
243+
`JPL ephemerides service <https://ssd.jpl.nasa.gov/horizons.cgi>`__)
244+
of a moving target such as an asteroid or comet.
245+
246+
NOTE: If coordinates is supplied, this argument cannot be used.
247+
moving_target : str, optional
248+
Indicate whether the object is a moving target or not. Default is set to False, in other words, not a moving target.
249+
250+
NOTE: If coordinates is supplied, this argument cannot be used.
251+
mt_type : str, optional
252+
The moving target type, valid inputs are majorbody and smallbody. If not supplied
253+
first majorbody is tried and then smallbody if a matching majorbody is not found.
254+
255+
NOTE: If moving_target is supplied, this argument is ignored.
202256
203257
Returns
204258
-------
205259
response : `~astropy.table.Table`
206260
"""
207261

208-
# Get Skycoord object for coordinates/object
209-
coordinates = parse_input_location(coordinates, objectname)
262+
if moving_target:
263+
264+
# Check that objectname has been passed in and coordinates
265+
# is not
266+
if coordinates:
267+
raise InvalidQueryError("Only one of moving_target and coordinates may be specified. Please remove coordinates if using moving_target and objectname.")
268+
269+
if not objectname:
270+
raise InvalidQueryError("Please specify the object name or ID (as understood by the `JPL ephemerides service <https://ssd.jpl.nasa.gov/horizons.cgi>`__) of a moving target such as an asteroid or comet.")
271+
272+
astrocut_request = f"moving_target/astrocut?obj_id={objectname}"
273+
if mt_type:
274+
astrocut_request += f"&obj_type={mt_type}"
275+
276+
else:
277+
278+
# Get Skycoord object for coordinates/object
279+
coordinates = parse_input_location(coordinates, objectname)
280+
281+
astrocut_request = f"astrocut?ra={coordinates.ra.deg}&dec={coordinates.dec.deg}"
282+
283+
# Adding the arguments that are common between moving/still astrocut requests
210284
size_dict = _parse_cutout_size(size)
285+
astrocut_request += f"&y={size_dict['y']}&x={size_dict['x']}&units={size_dict['units']}"
211286

212-
path = os.path.join(path, '')
213-
astrocut_request = "ra={}&dec={}&y={}&x={}&units={}".format(coordinates.ra.deg,
214-
coordinates.dec.deg,
215-
size_dict["y"],
216-
size_dict["x"],
217-
size_dict["units"])
218287
if sector:
219288
astrocut_request += "&sector={}".format(sector)
220289

221-
astrocut_url = self._service_api_connection.REQUEST_URL + "astrocut?" + astrocut_request
290+
astrocut_url = self._service_api_connection.REQUEST_URL + astrocut_request
291+
path = os.path.join(path, '')
222292
zipfile_path = "{}tesscut_{}.zip".format(path, time.strftime("%Y%m%d%H%M%S"))
223293
self._download_file(astrocut_url, zipfile_path)
224294

@@ -246,7 +316,8 @@ def download_cutouts(self, coordinates=None, size=5, sector=None, path=".", infl
246316
localpath_table['Local Path'] = [path+x for x in cutout_files]
247317
return localpath_table
248318

249-
def get_cutouts(self, coordinates=None, size=5, sector=None, objectname=None):
319+
def get_cutouts(self, coordinates=None, size=5, sector=None,
320+
objectname=None, moving_target=False, mt_type=None):
250321
"""
251322
Get cutout target pixel file(s) around the given coordinates with indicated size,
252323
and return them as a list of `~astropy.io.fits.HDUList` objects.
@@ -256,7 +327,8 @@ def get_cutouts(self, coordinates=None, size=5, sector=None, objectname=None):
256327
coordinates : str or `astropy.coordinates` object, optional
257328
The target around which to search. It may be specified as a
258329
string or as the appropriate `astropy.coordinates` object.
259-
One and only one of coordinates and objectname must be supplied.
330+
331+
NOTE: If moving_target or objectname is supplied, this argument cannot be used.
260332
size : int, array-like, `~astropy.units.Quantity`
261333
Optional, default 5 pixels.
262334
The size of the cutout array. If ``size`` is a scalar number or
@@ -271,25 +343,61 @@ def get_cutouts(self, coordinates=None, size=5, sector=None, objectname=None):
271343
from all available sectors on which the coordinate appears will be returned.
272344
objectname : str, optional
273345
The target around which to search, by name (objectname="M104")
274-
or TIC ID (objectname="TIC 141914082").
275-
One and only one of coordinates and objectname must be supplied.
346+
or TIC ID (objectname="TIC 141914082"). If moving_target is True, input must be the name or ID (as understood by the
347+
`JPL ephemerides service <https://ssd.jpl.nasa.gov/horizons.cgi>`__)
348+
of a moving target such as an asteroid or comet.
349+
350+
NOTE: If coordinates is supplied, this argument cannot be used.
351+
moving_target : str, optional
352+
Indicate whether the object is a moving target or not. Default is set to False, in other words, not a moving target.
353+
354+
NOTE: If coordinates is supplied, this argument cannot be used.
355+
mt_type : str, optional
356+
The moving target type, valid inputs are majorbody and smallbody. If not supplied
357+
first majorbody is tried and then smallbody if a matching majorbody is not found.
358+
359+
NOTE: If moving_target is supplied, this argument is ignored.
276360
277361
Returns
278362
-------
279363
response : A list of `~astropy.io.fits.HDUList` objects.
280364
"""
281365

282-
# Get Skycoord object for coordinates/object
283-
coordinates = parse_input_location(coordinates, objectname)
284-
366+
# Setting up the cutout size
285367
param_dict = _parse_cutout_size(size)
286-
param_dict["ra"] = coordinates.ra.deg
287-
param_dict["dec"] = coordinates.dec.deg
288368

369+
# Add sector if present
289370
if sector:
290371
param_dict["sector"] = sector
291372

292-
response = self._service_api_connection.service_request_async("astrocut", param_dict)
373+
if moving_target:
374+
375+
# Check that objectname has been passed in and coordinates
376+
# is not
377+
if coordinates:
378+
raise InvalidQueryError("Only one of moving_target and coordinates may be specified. Please remove coordinates if using moving_target and objectname.")
379+
380+
if not objectname:
381+
raise InvalidQueryError("Please specify the object name or ID (as understood by the `JPL ephemerides service <https://ssd.jpl.nasa.gov/horizons.cgi>`__) of a moving target such as an asteroid or comet.")
382+
383+
param_dict["obj_id"] = objectname
384+
385+
# Add optional parameter if present
386+
if mt_type:
387+
param_dict["obj_type"] = mt_type
388+
389+
response = self._service_api_connection.service_request_async("mt_astrocut", param_dict)
390+
391+
else:
392+
393+
# Get Skycoord object for coordinates/object
394+
coordinates = parse_input_location(coordinates, objectname)
395+
396+
param_dict["ra"] = coordinates.ra.deg
397+
param_dict["dec"] = coordinates.dec.deg
398+
399+
response = self._service_api_connection.service_request_async("astrocut", param_dict)
400+
293401
response.raise_for_status() # Raise any errors
294402

295403
try:

astroquery/mast/tests/test_mast.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import astropy.units as u
1414

1515
from ...utils.testing_tools import MockResponse
16-
from ...exceptions import (InvalidQueryError, InputWarning)
16+
from ...exceptions import InvalidQueryError, InputWarning
1717

1818
from ... import mast
1919

@@ -589,6 +589,23 @@ def test_tesscut_get_sector(patch_post):
589589
assert sector_table['camera'][0] == 1
590590
assert sector_table['ccd'][0] == 3
591591

592+
# Exercising the search by moving target
593+
sector_table = mast.Tesscut.get_sectors(objectname="Ceres",
594+
moving_target=True)
595+
assert isinstance(sector_table, Table)
596+
assert len(sector_table) == 1
597+
assert sector_table['sectorName'][0] == "tess-s0001-1-3"
598+
assert sector_table['sector'][0] == 1
599+
assert sector_table['camera'][0] == 1
600+
assert sector_table['ccd'][0] == 3
601+
602+
# Testing catch for multiple designators'
603+
error_str = "Only one of moving_target and coordinates may be specified. Please remove coordinates if using moving_target and objectname."
604+
605+
with pytest.raises(InvalidQueryError) as invalid_query:
606+
mast.Tesscut.get_sectors(objectname='Ceres', moving_target=True, coordinates=coord)
607+
assert error_str in str(invalid_query.value)
608+
592609

593610
def test_tesscut_download_cutouts(patch_post, tmpdir):
594611

@@ -616,6 +633,27 @@ def test_tesscut_download_cutouts(patch_post, tmpdir):
616633
assert manifest["Local Path"][0][-4:] == "fits"
617634
assert os.path.isfile(manifest[0]['Local Path'])
618635

636+
# Exercising the search by moving target
637+
manifest = mast.Tesscut.download_cutouts(objectname="Eleonora",
638+
moving_target=True,
639+
size=5,
640+
path=str(tmpdir))
641+
assert isinstance(manifest, Table)
642+
assert len(manifest) == 1
643+
assert manifest["Local Path"][0][-4:] == "fits"
644+
assert os.path.isfile(manifest[0]['Local Path'])
645+
646+
# Testing catch for multiple designators'
647+
error_str = "Only one of moving_target and coordinates may be specified. Please remove coordinates if using moving_target and objectname."
648+
649+
with pytest.raises(InvalidQueryError) as invalid_query:
650+
mast.Tesscut.download_cutouts(objectname="Eleonora",
651+
moving_target=True,
652+
coordinates=coord,
653+
size=5,
654+
path=str(tmpdir))
655+
assert error_str in str(invalid_query.value)
656+
619657

620658
def test_tesscut_get_cutouts(patch_post, tmpdir):
621659

@@ -631,6 +669,25 @@ def test_tesscut_get_cutouts(patch_post, tmpdir):
631669
assert len(cutout_hdus_list) == 1
632670
assert isinstance(cutout_hdus_list[0], fits.HDUList)
633671

672+
# Exercising the search by object name
673+
cutout_hdus_list = mast.Tesscut.get_cutouts(objectname='Eleonora',
674+
moving_target=True,
675+
size=5)
676+
assert isinstance(cutout_hdus_list, list)
677+
assert len(cutout_hdus_list) == 1
678+
assert isinstance(cutout_hdus_list[0], fits.HDUList)
679+
680+
# Testing catch for multiple designators'
681+
error_str = "Only one of moving_target and coordinates may be specified. Please remove coordinates if using moving_target and objectname."
682+
683+
with pytest.raises(InvalidQueryError) as invalid_query:
684+
mast.Tesscut.get_cutouts(objectname="Eleonora",
685+
moving_target=True,
686+
coordinates=coord,
687+
size=5)
688+
assert error_str in str(invalid_query.value)
689+
690+
634691
######################
635692
# ZcutClass tests #
636693
######################

0 commit comments

Comments
 (0)