diff --git a/.gitignore b/.gitignore index c614a2d241..b8e4fd0e05 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ MANIFEST pip-wheel-metadata .hypothesis doctests.py +coverage.xml # Sphinx _build diff --git a/CHANGES.rst b/CHANGES.rst index e3ff32a360..9a167d2d44 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,6 +23,12 @@ esa.hubble - Status and maintenance messages from eHST TAP when the module is instantiated. get_status_messages method to retrieve them. [#2597] - Optional parameters in all methods are kwargs keyword only. [#2597] +solarsystem.pds +^^^^^^^^^^^^^^^ + +- New module to access the Planetary Data System's Ring Node System. [#2358] + + Service fixes and enhancements ------------------------------ diff --git a/astroquery/solarsystem/pds/__init__.py b/astroquery/solarsystem/pds/__init__.py new file mode 100644 index 0000000000..fc326f0c62 --- /dev/null +++ b/astroquery/solarsystem/pds/__init__.py @@ -0,0 +1,35 @@ +""" +RingNode +-------- + +:author: Ned Molter (emolter@berkeley.edu) +""" + +from astropy import config as _config + + +class Conf(_config.ConfigNamespace): + """ + Configuration parameters for `astroquery.solarsystem.pds`. + """ + + # server settings + url = _config.ConfigItem( + "https://pds-rings.seti.org/cgi-bin/tools/viewer3_xxx.pl?", "Ring Node" + ) + + # implement later: other pds tools + + timeout = _config.ConfigItem(30, "Time limit for connecting to PDS servers (seconds).") + + +conf = Conf() + +from .core import RingNode, RingNodeClass + +__all__ = [ + "RingNode", + "RingNodeClass", + "Conf", + "conf", +] diff --git a/astroquery/solarsystem/pds/core.py b/astroquery/solarsystem/pds/core.py new file mode 100644 index 0000000000..c4e5028c85 --- /dev/null +++ b/astroquery/solarsystem/pds/core.py @@ -0,0 +1,401 @@ +# 1. standard library imports +import re +import os + +# 2. third party imports +from astropy.time import Time +from astropy.table import QTable, join +import astropy.units as u +from astropy.coordinates import EarthLocation, Angle +from bs4 import BeautifulSoup + +# 3. local imports - use relative imports +# all Query classes should inherit from BaseQuery. +from ...query import BaseQuery +from ...utils import async_to_sync + +# import configurable items declared in __init__.py, e.g. hardcoded dictionaries +from . import conf + +planet_defaults = { + "mars": { + "ephem": "000 MAR097 + DE440", + "moons": "402 Phobos, Deimos", + "center_ansa": "Phobos Ring", + "rings": "Phobos, Deimos", + }, + "jupiter": { + "ephem": "000 JUP365 + DE440", + "moons": "516 All inner moons (J1-J5,J14-J16)", + "center_ansa": "Main Ring", + "rings": "Main & Gossamer", + }, + "saturn": { + "ephem": "000 SAT389 + SAT393 + SAT427 + DE440", + "moons": "653 All inner moons (S1-S18,S32-S35,S49,S53)", + "center_ansa": "A", + "rings": "A,B,C,F,G,E", + }, + "uranus": { + "ephem": "000 URA111 + URA115 + DE440", + "moons": "727 All inner moons (U1-U15,U25-U27)", + "center_ansa": "Epsilon", + "rings": "All rings", + }, + "neptune": { + "ephem": "000 NEP081 + NEP095 + DE440", + "moons": "814 All inner moons (N1-N8,N14)", + "center_ansa": "Adams Ring", + "rings": "Galle, LeVerrier, Arago, Adams", + }, + "pluto": { + "ephem": "000 PLU058 + DE440", + "moons": "905 All moons (P1-P5)", + "center_ansa": "Hydra", + "rings": "Styx, Nix, Kerberos, Hydra", + }, +} + +neptune_arcmodels = { + 1: "#1 (820.1194 deg/day)", + 2: "#2 (820.1118 deg/day)", + 3: "#3 (820.1121 deg/day)", +} + + +@async_to_sync +class RingNodeClass(BaseQuery): + """ + a class for querying the Planetary Ring Node ephemeris tools + + """ + + def __init__(self, url='', timeout=None): + ''' + Instantiate Planetary Ring Node query + ''' + super().__init__() + self.url = url + self.timeout = timeout + + @property + def _url(self): + return self.url or conf.url + + def __str__(self): + + return "PDSRingNode instance" + + def ephemeris_async(self, planet, *, epoch=None, location=None, neptune_arcmodel=3, + get_query_payload=False, cache=True): + """ + send query to Planetary Ring Node server + + Parameters + ---------- + planet : str + One of "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", or "Pluto". + epoch : `~astropy.time.Time` object, or str in format YYYY-MM-DD hh:mm, optional. + If str is provided then UTC is assumed. + If no epoch is provided, the current time is used. + location : str, or array-like, or `~astropy.coordinates.EarthLocation`, optional + If str, named observeratory supported by the ring node, e.g. JWST. + If array-like, observer's location as a + 3-element array of Earth longitude, latitude, altitude + that istantiates an + `~astropy.coordinates.EarthLocation`. Longitude and + latitude should be anything that initializes an + `~astropy.coordinates.Angle` object, and altitude should + initialize an `~astropy.units.Quantity` object (with units + of length). If ``None``, then the geofocus is used. + neptune_arcmodel : int, optional. + which ephemeris to assume for Neptune's ring arcs + Must be one of 1, 2, or 3 (see https://pds-rings.seti.org/tools/viewer3_nep.shtml for details) + has no effect if planet != 'Neptune' + get_query_payload : boolean, optional + When set to `True` the method returns the HTTP request parameters as + a dict, default: False + cache : boolean, optional + When set to `True` the method caches the download, default: True + + + Returns + ------- + response : `requests.Response` + The response of the HTTP request. + + + Examples + -------- + >>> from astroquery.solarsystem.pds import RingNode + >>> import astropy.units as u + >>> bodytable, ringtable = RingNode.ephemeris(planet='Uranus', + ... epoch='2024-05-08 22:39', + ... location = (-23.029 * u.deg, -67.755 * u.deg, 5000 * u.m)) # doctest: +REMOTE_DATA + >>> print(ringtable) # doctest: +REMOTE_DATA + ring pericenter ascending node + deg deg + ------- ---------- -------------- + Six 293.129 52.0 + Five 109.438 81.1 + Four 242.882 66.9 + Alpha 184.498 253.9 + Beta 287.66 299.2 + Eta 0.0 0.0 + Gamma 50.224 0.0 + Delta 0.0 0.0 + Lambda 0.0 0.0 + Epsilon 298.022 0.0 + """ + + planet = planet.lower() + if planet not in planet_defaults: + raise ValueError("illegal value for 'planet' parameter " + "(must be 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', or 'Pluto')") + + if isinstance(epoch, (int, float)): + epoch = Time(epoch, format='jd') + elif isinstance(epoch, str): + epoch = Time(epoch, format='iso') + elif epoch is None: + epoch = Time.now() + + if location is None: + viewpoint = "observatory" + observatory = "Earth's center" + latitude, longitude, altitude = "", "", "" + elif isinstance(location, str): + viewpoint = "observatory" + observatory = location + latitude, longitude, altitude = "", "", "" + else: + viewpoint = "latlon" + observatory = "Earth's center" + if isinstance(location, EarthLocation): + loc = location.geodetic + longitude = loc[0].deg + latitude = loc[1].deg + altitude = loc[2].to_value(u.m) + elif hasattr(location, "__iter__"): + longitude = Angle(location[0]).deg + latitude = Angle(location[1]).deg + altitude = u.Quantity(location[2]).to_value(u.m) + + if neptune_arcmodel not in [1, 2, 3]: + raise ValueError( + f"Illegal Neptune arc model {neptune_arcmodel}. must be one of 1, 2, or 3 (see https://pds-rings.seti.org/tools/viewer3_nep.shtml for details)" + ) + + # configure request_payload for ephemeris query + request_payload = dict( + [ + ("abbrev", planet[:3]), + ("ephem", planet_defaults[planet]["ephem"]), + ("time", epoch.utc.iso[:16]), + ("fov", 10), # figure option - hardcode ok + ("fov_unit", planet.capitalize() + " radii"), + ("center", "body"), + ("center_body", planet.capitalize()), + ("center_ansa", planet_defaults[planet]["center_ansa"]), + ("center_ew", "east"), + ("center_ra", ""), + ("center_ra_type", "hours"), + ("center_dec", ""), + ("center_star", ""), + ("viewpoint", viewpoint), + ("observatory", observatory), + ("latitude", latitude), + ("longitude", longitude), + ("lon_dir", "east"), + ("altitude", altitude), + ("moons", planet_defaults[planet]["moons"]), + ("rings", planet_defaults[planet]["rings"]), + ("arcmodel", neptune_arcmodels[neptune_arcmodel]), + ("extra_ra", ""), # figure options below this line, can all be hardcoded and ignored + ("extra_ra_type", "hours"), + ("extra_dec", ""), + ("extra_name", ""), + ("title", ""), + ("labels", "Small (6 points)"), + ("moonpts", "0"), + ("blank", "No"), + ("opacity", "Transparent"), + ("peris", "None"), + ("peripts", "4"), + ("arcpts", "4"), + ("meridians", "Yes"), + ("output", "html"), + ] + ) + + # return request_payload if desired + if get_query_payload: + return request_payload + + # query and parse + response = self._request( + "GET", self._url, params=request_payload, timeout=self.timeout, cache=cache + ) + + return response + + def _parse_result(self, response, verbose=None): + """ + Routine for parsing data from ring node + + Parameters + ---------- + self : RingNodeClass instance + response : list + raw response from server + + + Returns + ------- + bodytable : `~astropy.table.QTable` + ringtable : `~astropy.table.QTable` + """ + + soup = BeautifulSoup(response.text, "html5lib") + text = soup.get_text() + # need regex because some blank lines have spacebar and some do not + textgroups = re.split("\n\n|\n \n", text.strip()) + ringtable = None + + for group in textgroups: + group = group.strip() + + # input parameters. only thing needed is epoch + if group.startswith("Observation"): + epoch = group.splitlines()[0].split("e: ")[-1].strip() + + # minor body table part 1 + elif group.startswith("Body"): + group = "NAIF " + group # fixing lack of header for NAIF ID + bodytable_names = ("NAIF ID", "Body", "RA", "Dec", "RA (deg)", "Dec (deg)", "dRA", "dDec") + bodytable_units = [None, None, None, None, u.deg, u.deg, u.arcsec, u.arcsec] + bodytable = QTable.read(group, format="ascii.fixed_width", + col_starts=(0, 4, 18, 35, 54, 68, 80, 91), + col_ends=(4, 18, 35, 54, 68, 80, 91, 102), + names=bodytable_names, + units=(bodytable_units)) + + # minor body table part 2 + elif group.startswith("Sub-"): + + group = os.linesep.join(group.splitlines()[2:]) # removing two-row header entirely + bodytable2_names = ("NAIF ID", "Body", "sub_obs_lon", "sub_obs_lat", "sub_sun_lon", "sub_sun_lat", "phase", "distance") + bodytable2_units = [None, None, u.deg, u.deg, u.deg, u.deg, u.deg, u.km * 1e6] + bodytable2 = QTable.read(group, format="ascii.fixed_width", + col_starts=(0, 4, 18, 28, 37, 49, 57, 71), + col_ends=(4, 18, 28, 37, 49, 57, 71, 90), + names=bodytable2_names, + data_start=0, + units=(bodytable2_units)) + + # ring plane data + elif group.startswith("Ring s"): + for line in group.splitlines(): + lines = line.split(":") + if "Ring sub-solar latitude" in lines[0]: + sub_sun_lat, sub_sun_lat_min, sub_sun_lat_max = [ + float(s.strip("()")) for s in re.split(r"\(|to", lines[1]) + ] + systemtable = { + "sub_sun_lat": sub_sun_lat * u.deg, + "sub_sun_lat_min": sub_sun_lat_min * u.deg, + "sub_sun_lat_max": sub_sun_lat_min * u.deg, + } + + elif "Ring plane opening angle" in lines[0]: + systemtable["opening_angle"] = ( + float(re.sub("[a-zA-Z]+", "", lines[1]).strip("()")) * u.deg + ) + elif "Ring center phase angle" in lines[0]: + systemtable["phase_angle"] = float(lines[1].strip()) * u.deg + elif "Sub-solar longitude" in lines[0]: + systemtable["sub_sun_lon"] = ( + float(re.sub("[a-zA-Z]+", "", lines[1]).strip("()")) * u.deg + ) + elif "Sub-observer longitude" in lines[0]: + systemtable["sub_obs_lon"] = float(lines[1].strip()) * u.deg + + # basic info about the planet + elif group.startswith("Sun-planet"): + for line in group.splitlines(): + lines = line.split(":") + if "Sun-planet distance (AU)" in lines[0]: + # this is redundant with sun distance in km + pass + elif "Observer-planet distance (AU)" in lines[0]: + # this is redundant with observer distance in km + pass + elif "Sun-planet distance (km)" in lines[0]: + systemtable["d_sun"] = ( + float(lines[1].split("x")[0].strip()) * 1e6 * u.km + ) + elif "Observer-planet distance (km)" in lines[0]: + systemtable["d_obs"] = ( + float(lines[1].split("x")[0].strip()) * 1e6 * u.km + ) + elif "Light travel time" in lines[0]: + systemtable["light_time"] = float(lines[1].strip()) * u.second + + # --------- below this line, planet-specific info ------------ + # Uranus individual rings data + elif group.startswith("Ring "): + ringtable_names = ("ring", "pericenter", "ascending node") + ringtable_units = [None, u.deg, u.deg] + ringtable = QTable.read(" " + group, format="ascii.fixed_width", + col_starts=(5, 18, 29), + col_ends=(18, 29, 36), + names=ringtable_names, + units=(ringtable_units)) + + # Saturn F-ring data + elif group.startswith("F Ring"): + for line in group.splitlines(): + lines = line.split(":") + if "F Ring pericenter" in lines[0]: + peri = float(re.sub("[a-zA-Z]+", "", lines[1]).strip("()")) + elif "F Ring ascending node" in lines[0]: + ascn = float(lines[1].strip()) + ringtable_names = ("ring", "pericenter", "ascending node") + ringtable_units = [None, u.deg, u.deg] + ringtable = QTable([["F"], [peri], [ascn]], + names=ringtable_names, + units=(ringtable_units)) + + # Neptune ring arcs data + elif group.startswith("Courage"): + for i, line in enumerate(group.splitlines()): + lines = line.split(":") + ring = lines[0].split("longitude")[0].strip() + [min_angle, max_angle] = [ + float(s.strip()) + for s in re.sub("[a-zA-Z]+", "", lines[1]).strip("()").split() + ] + if i == 0: + ringtable_names = ("ring", "min_angle", "max_angle") + ringtable_units = [None, u.deg, u.deg] + ringtable = QTable([[ring], [min_angle], [max_angle]], + names=ringtable_names, + units=ringtable_units) + else: + ringtable.add_row([ring, min_angle*u.deg, max_angle*u.deg]) + + # do some cleanup from the parsing job + # and make system-wide parameters metadata of bodytable and ringtable + systemtable["epoch"] = Time(epoch, format="iso", scale="utc") # add obs time to systemtable + if ringtable is not None: + ringtable.add_index("ring") + ringtable.meta = systemtable + + bodytable = join(bodytable, bodytable2) # concatenate minor body table + bodytable.add_index("Body") + bodytable.meta = systemtable + + return bodytable, ringtable + + +RingNode = RingNodeClass() diff --git a/astroquery/solarsystem/pds/tests/__init__.py b/astroquery/solarsystem/pds/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/astroquery/solarsystem/pds/tests/data/neptune_ephemeris.html b/astroquery/solarsystem/pds/tests/data/neptune_ephemeris.html new file mode 100644 index 0000000000..74f3b2894a --- /dev/null +++ b/astroquery/solarsystem/pds/tests/data/neptune_ephemeris.html @@ -0,0 +1,93 @@ + + +Neptune Viewer 3.0 Results + +

Neptune Viewer 3.0 Results

+

+

+Input Parameters
+----------------
+ 
+Observation time: 2021-10-07 07:25
+       Ephemeris: NEP081 + NEP095 + DE440
+   Field of view: 10 (seconds of arc)
+  Diagram center: Neptune
+       Viewpoint: Earth's center
+    Moon selection: All inner moons (N1-N8,N14)
+  Ring selection: LeVerrier, Adams
+       Arc model: #2 (820.1118 deg/day)
+  Standard stars: No
+ Additional star: No
+    Other bodies: None
+           Title: 
+     Moon labels: Small (6 points)
+Moon enlargement: 0 (points)
+     Blank disks: No
+      Arc weight: 4 (points)
+ Prime meridians: Yes
+ 
+ 
+Field of View Description (J2000)
+---------------------------------
+ 
+     Body          RA                 Dec                 RA (deg)    Dec (deg)    dRA (")     dDec (")
+ 899 Neptune       23h 28m 17.7510s    -4d 41m 40.978s    352.073962   -4.694716      0.000      0.000
+ 801 Triton        23h 28m 17.0094s    -4d 41m 52.414s    352.070872   -4.697893    -11.087    -11.436
+ 802 Nereid        23h 28m 42.2572s    -4d 38m 35.593s    352.176072   -4.643220    366.360    185.385
+ 803 Naiad         23h 28m 17.8057s    -4d 41m 41.639s    352.074190   -4.694900      0.818     -0.661
+ 804 Thalassa      23h 28m 17.6286s    -4d 41m 42.501s    352.073452   -4.695139     -1.830     -1.523
+ 805 Despina       23h 28m 17.8727s    -4d 41m 40.367s    352.074470   -4.694546      1.820      0.611
+ 806 Galatea       23h 28m 17.8035s    -4d 41m 41.811s    352.074181   -4.694948      0.785     -0.833
+ 807 Larissa       23h 28m 17.5622s    -4d 41m 42.983s    352.073176   -4.695273     -2.822     -2.005
+ 808 Proteus       23h 28m 17.9990s    -4d 41m 40.206s    352.074996   -4.694502      3.708      0.772
+ 814 Hippocamp     23h 28m 17.7896s    -4d 41m 42.997s    352.074123   -4.695277      0.577     -2.018
+ 
+                   Sub-Observer         Sub-Solar     
+     Body          Lon(degW) Lat(deg)   Lon(degW) Lat(deg)   Phase(deg)   Distance(10^6 km)
+ 899 Neptune       119.256   -22.782    118.466   -22.610      0.75134    4338.354329
+ 801 Triton        243.335   -35.865    242.578   -35.437      0.75148    4338.237612
+ 802 Nereid        100.118     3.726     99.371     3.746      0.74807    4336.800925
+ 803 Naiad         199.350   -26.858    198.530   -26.720      0.75133    4338.397183
+ 804 Thalassa      236.105   -22.976    235.314   -22.804      0.75136    4338.355604
+ 805 Despina        59.009   -22.769     58.222   -22.594      0.75131    4338.387909
+ 806 Galatea       343.647   -22.825    342.861   -22.651      0.75132    4338.411414
+ 807 Larissa       252.931   -22.983    252.139   -22.813      0.75137    4338.343394
+ 808 Proteus        34.218   -23.003     33.431   -22.822      0.75129    4338.440907
+ 814 Hippocamp     287.004   -22.973    286.217   -22.798      0.75132    4338.449959
+ 
+   Ring sub-solar latitude (deg): -22.61024  (-22.61915  to -22.60134)
+  Ring plane opening angle (deg): -22.78248  (lit)
+   Ring center phase angle (deg):   0.75134
+       Sub-solar longitude (deg): 149.96998  from ring plane ascending node
+    Sub-observer longitude (deg): 149.17958
+
+        Sun-planet distance (AU):  29.92193
+   Observer-planet distance (AU):  29.00011
+        Sun-planet distance (km):  4476.256293 x 10^6
+   Observer-planet distance (km):  4338.354329 x 10^6
+         Light travel time (sec): 14471.192364
+
+  
+       Courage longitude (deg):  63.81977  to  64.81977  from ring plane ascending node
+       Liberte longitude (deg):  55.01978  to  59.11976
+     Egalite A longitude (deg):  44.21976  to  45.21976
+     Egalite B longitude (deg):  40.41978  to  43.41978
+    Fraternite longitude (deg):  26.41978  to  36.01978
+
+
Preview:

+
+Click +here +to download diagram (PDF, 17055 bytes).

+Click +here +to download diagram (JPEG format, 164693 bytes).

+Click +here +to download diagram (PostScript format, 42562 bytes). +


+Neptune Viewer Form | +RMS Node Tools | +Ring-Moon Systems Home + + diff --git a/astroquery/solarsystem/pds/tests/data/pluto_ephemeris.html b/astroquery/solarsystem/pds/tests/data/pluto_ephemeris.html new file mode 100644 index 0000000000..43d48e25ea --- /dev/null +++ b/astroquery/solarsystem/pds/tests/data/pluto_ephemeris.html @@ -0,0 +1,77 @@ + + +Pluto Viewer 3.0 Results + +

Pluto Viewer 3.0 Results

+

+

+Input Parameters
+----------------
+ 
+  Observation time: 2021-10-07 07:25
+         Ephemeris: PLU058 + DE440
+     Field of view: 10 (Pluto-Charon separations (19,571 km))
+    Diagram center: Pluto
+         Viewpoint: Earth's center
+    Moon selection: All moons (P1-P5)
+    Ring selection: None
+    Standard stars: No
+   Additional star: No
+      Other bodies: None
+             Title: 
+       Moon labels: Small (6 points)
+  Moon enlargement: 0 (points)
+       Blank disks: No
+   Prime meridians: Yes
+ 
+ 
+Field of View Description (J2000)
+---------------------------------
+ 
+     Body          RA                 Dec                 RA (deg)    Dec (deg)    dRA (")     dDec (")
+ 999 Pluto         19h 44m 51.0347s   -22d 56m 05.931s    296.212645  -22.934981      0.000      0.000
+ 901 Charon        19h 44m 51.0870s   -22d 56m 05.830s    296.212862  -22.934953      0.722      0.101
+ 902 Nix           19h 44m 50.9270s   -22d 56m 04.738s    296.212196  -22.934650     -1.488      1.192
+ 903 Hydra         19h 44m 51.0560s   -22d 56m 08.227s    296.212733  -22.935619      0.294     -2.297
+ 904 Kerberos      19h 44m 50.9309s   -22d 56m 07.195s    296.212212  -22.935332     -1.434     -1.264
+ 905 Styx          19h 44m 50.9944s   -22d 56m 07.189s    296.212477  -22.935330     -0.557     -1.259
+ 
+                   Sub-Observer         Sub-Solar     
+     Body          Lon(degW) Lat(deg)   Lon(degW) Lat(deg)   Phase(deg)   Distance(10^6 km)
+ 999 Pluto          45.545    56.505     47.823    57.577      1.64048    5114.486814
+ 901 Charon        225.545    56.505    227.823    57.578      1.64048    5114.479237
+ 902 Nix           283.749   -41.845    285.264   -43.043      1.64048    5114.482774
+ 903 Hydra           7.786   -11.624      6.718   -12.887      1.64047    5114.516518
+ 904 Kerberos       12.108    56.881     14.388    57.967      1.64047    5114.516443
+ 905 Styx          350.443    56.472    352.720    57.544      1.64047    5114.509238
+ 
+   Ring sub-solar latitude (deg):  57.57737  ( 57.56961  to  57.58512)
+  Ring plane opening angle (deg):  56.50534  (lit)
+   Ring center phase angle (deg):   1.64048
+       Sub-solar longitude (deg): 116.55873  from ring plane ascending node
+    Sub-observer longitude (deg): 118.83690
+
+        Sun-planet distance (AU):  34.37680
+   Observer-planet distance (AU):  34.18823
+        Sun-planet distance (km):  5142.695995 x 10^6
+   Observer-planet distance (km):  5114.486814 x 10^6
+         Light travel time (sec): 17060.091666
+
+
+
Preview:

+
+Click +here +to download diagram (PDF, 11875 bytes).

+Click +here +to download diagram (JPEG format, 122052 bytes).

+Click +here +to download diagram (PostScript format, 23630 bytes). +


+Pluto Viewer Form | +RMS Node Tools | +Ring-Moon Systems Home + + diff --git a/astroquery/solarsystem/pds/tests/data/saturn_ephemeris.html b/astroquery/solarsystem/pds/tests/data/saturn_ephemeris.html new file mode 100644 index 0000000000..74f67b2f4f --- /dev/null +++ b/astroquery/solarsystem/pds/tests/data/saturn_ephemeris.html @@ -0,0 +1,121 @@ + + +Saturn Viewer 3.0 Results + +

Saturn Viewer 3.0 Results

+

+

+Input Parameters
+----------------
+ 
+  Observation time: 2021-10-07 07:25
+         Ephemeris: SAT389 + SAT393 + SAT427 + DE440
+     Field of view: 10 (Saturn radii)
+    Diagram center: Saturn
+         Viewpoint: Earth's center
+    Moon selection: All inner moons (S1-S18,S32-S35,S49,S53)
+    Ring selection: A,B,C,F,G,E
+    Standard stars: No
+   Additional star: No
+      Other bodies: None
+             Title: 
+       Moon labels: Small (6 points)
+  Moon enlargement: 0 (points)
+       Blank disks: No
+    Ring plot type: Transparent
+Pericenter markers: None
+       Marker size: 4 (points)
+   Prime meridians: Yes
+ 
+ 
+Field of View Description (J2000)
+---------------------------------
+ 
+     Body          RA                 Dec                 RA (deg)    Dec (deg)    dRA (")     dDec (")
+ 699 Saturn        20h 36m 46.6876s   -19d 25m 00.415s    309.194532  -19.416782      0.000      0.000
+ 601 Mimas         20h 36m 48.5158s   -19d 25m 05.162s    309.202149  -19.418101     25.864     -4.747
+ 602 Enceladus     20h 36m 44.5496s   -19d 24m 51.656s    309.185623  -19.414349    -30.246      8.759
+ 603 Tethys        20h 36m 49.1034s   -19d 24m 54.793s    309.204598  -19.415220     34.177      5.623
+ 604 Dione         20h 36m 43.8042s   -19d 25m 08.188s    309.182518  -19.418941    -40.791     -7.772
+ 605 Rhea          20h 36m 49.3735s   -19d 25m 26.733s    309.205723  -19.424092     37.997    -26.318
+ 606 Titan         20h 36m 52.7285s   -19d 24m 20.454s    309.219702  -19.405682     85.460     39.961
+ 607 Hyperion      20h 36m 37.9308s   -19d 23m 59.499s    309.158045  -19.399861   -123.882     60.917
+ 608 Iapetus       20h 36m 35.8802s   -19d 24m 17.459s    309.149501  -19.404850   -152.891     42.956
+ 609 Phoebe        20h 39m 09.0594s   -19d 18m 51.608s    309.787748  -19.314336   2014.117    368.807
+ 610 Janus         20h 36m 47.0356s   -19d 25m 08.132s    309.195982  -19.418926      4.923     -7.717
+ 611 Epimetheus    20h 36m 47.6785s   -19d 25m 07.559s    309.198660  -19.418766     14.018     -7.143
+ 612 Helene        20h 36m 43.0674s   -19d 24m 49.232s    309.179448  -19.413676    -51.214     11.183
+ 613 Telesto       20h 36m 49.4320s   -19d 25m 10.566s    309.205966  -19.419602     38.824    -10.151
+ 614 Calypso       20h 36m 46.3698s   -19d 24m 45.877s    309.193208  -19.412743     -4.495     14.539
+ 615 Atlas         20h 36m 45.7895s   -19d 24m 53.916s    309.190790  -19.414977    -12.705      6.500
+ 616 Prometheus    20h 36m 45.9304s   -19d 25m 04.996s    309.191377  -19.418054    -10.712     -4.580
+ 617 Pandora       20h 36m 47.7201s   -19d 24m 57.123s    309.198834  -19.415867     14.607      3.293
+ 618 Pan           20h 36m 47.7763s   -19d 25m 05.950s    309.199068  -19.418319     15.402     -5.534
+ 632 Methone       20h 36m 47.8837s   -19d 25m 09.747s    309.199515  -19.419374     16.921     -9.332
+ 633 Pallene       20h 36m 48.8120s   -19d 25m 05.768s    309.203383  -19.418269     30.054     -5.352
+ 634 Polydeuces    20h 36m 48.6362s   -19d 25m 18.814s    309.202651  -19.421893     27.567    -18.398
+ 635 Daphnis       20h 36m 46.2492s   -19d 25m 06.047s    309.192705  -19.418346     -6.202     -5.632
+ 649 Anthe         20h 36m 44.6814s   -19d 24m 58.258s    309.186173  -19.416183    -28.381      2.157
+ 653 Aegaeon       20h 36m 48.3925s   -19d 25m 03.255s    309.201635  -19.417571     24.119     -2.839
+ 
+                   Sub-Observer         Sub-Solar     
+     Body          Lon(degW) Lat(deg)   Lon(degW) Lat(deg)   Phase(deg)   Distance(10^6 km)
+ 699 Saturn        210.795    19.443    205.615    17.398      5.32671    1422.644849
+ 601 Mimas          95.037    20.038     89.784    18.112      5.32655    1422.602603
+ 602 Enceladus     299.701    19.450    294.523    17.405      5.32670    1422.744310
+ 603 Tethys         52.165    19.663     46.940    17.711      5.32564    1422.817372
+ 604 Dione         227.249    19.455    222.064    17.409      5.32813    1422.398436
+ 605 Rhea          150.469    19.711    145.269    17.682      5.32782    1422.224763
+ 606 Titan          25.966    19.303     20.800    17.250      5.32194    1423.637413
+ 607 Hyperion      317.655    18.677    312.471    16.700      5.32461    1423.610877
+ 608 Iapetus       348.552     4.329    343.467     2.760      5.31647    1426.011713
+ 609 Phoebe        155.216    10.809    150.314     8.596      5.31799    1417.532721
+ 610 Janus          66.966    19.590     61.780    17.532      5.32716    1422.507868
+ 611 Epimetheus    167.130    19.801    161.935    17.750      5.32693    1422.539360
+ 612 Helene         50.542    19.469     45.369    17.408      5.32695    1422.746473
+ 613 Telesto       107.882    20.476    102.645    18.479      5.32661    1422.543474
+ 614 Calypso         2.436    19.096    357.210    17.191      5.32573    1422.920167
+ 615 Atlas         203.028    19.441    197.852    17.397      5.32650    1422.741315
+ 616 Prometheus    258.847    19.444    253.660    17.399      5.32727    1422.531082
+ 617 Pandora        54.959    19.442     49.784    17.395      5.32616    1422.742504
+ 618 Pan           304.258    19.446    299.076    17.396      5.32679    1422.573404
+ 632 Methone       129.442    19.445    124.257    17.398      5.32703    1422.503412
+ 633 Pallene        99.503    19.444     94.323    17.396      5.32646    1422.612979
+ 634 Polydeuces    155.947    19.447    150.763    17.400      5.32746    1422.353842
+ 635 Daphnis       304.258    19.444    299.071    17.399      5.32725    1422.521175
+ 649 Anthe         139.723    19.451    134.541    17.406      5.32715    1422.620381
+ 653 Aegaeon        99.504    19.444     94.325    17.396      5.32641    1422.646000
+ 
+   Ring sub-solar latitude (deg):  17.39754  ( 17.37071  to  17.42436)
+  Ring plane opening angle (deg):  19.44270  (lit)
+   Ring center phase angle (deg):   5.32671
+       Sub-solar longitude (deg):   6.08926  from ring plane ascending node
+    Sub-observer longitude (deg):   0.90868
+
+        Sun-planet distance (AU):   9.93730
+   Observer-planet distance (AU):   9.50979
+        Sun-planet distance (km):  1486.598500 x 10^6
+   Observer-planet distance (km):  1422.644849 x 10^6
+         Light travel time (sec):  4745.432417
+
+  
+       F Ring pericenter (deg): 249.23097  from ring plane ascending node
+   F Ring ascending node (deg): 250.34081
+
+
Preview:

+
+Click +here +to download diagram (PDF, 24266 bytes).

+Click +here +to download diagram (JPEG format, 198407 bytes).

+Click +here +to download diagram (PostScript format, 85890 bytes). +


+Saturn Viewer Form | +RMS Node Tools | +Ring-Moon Systems Home + + diff --git a/astroquery/solarsystem/pds/tests/data/uranus_ephemeris.html b/astroquery/solarsystem/pds/tests/data/uranus_ephemeris.html new file mode 100644 index 0000000000..03db30f5c0 --- /dev/null +++ b/astroquery/solarsystem/pds/tests/data/uranus_ephemeris.html @@ -0,0 +1,118 @@ + + +Uranus Viewer 3.0 Results + +

Uranus Viewer 3.0 Results

+

+

+Input Parameters
+----------------
+ 
+  Observation time: 2022-05-03 00:00
+         Ephemeris: URA111 + URA115 + DE440
+     Field of view: 10 (Uranus radii)
+    Diagram center: Uranus
+         Viewpoint: Lat = 10 (deg)
+                    Lon = -120.355 (deg east)
+                    Alt = 1000 (m)
+    Moon selection: All inner moons (U1-U15,U25-U27)
+    Ring selection: Nine major rings
+    Standard stars: No
+   Additional star: No
+      Other bodies: None
+             Title: 
+       Moon labels: Small (6 points)
+  Moon enlargement: 0 (points)
+       Blank disks: No
+Pericenter markers: None
+       Marker size: 4 (points)
+   Prime meridians: Yes
+ 
+ 
+Field of View Description (J2000)
+---------------------------------
+ 
+     Body          RA                 Dec                 RA (deg)    Dec (deg)    dRA (")     dDec (")
+ 799 Uranus         2h 48m 02.3164s    15d 48m 04.141s     42.009651   15.801150      0.000      0.000
+ 701 Ariel          2h 48m 02.6985s    15d 48m 14.802s     42.011244   15.804112      5.515     10.661
+ 702 Umbriel        2h 48m 03.0454s    15d 48m 16.080s     42.012689   15.804467     10.522     11.939
+ 703 Titania        2h 48m 02.9240s    15d 47m 36.893s     42.012184   15.793581      8.771    -27.248
+ 704 Oberon         2h 48m 04.1962s    15d 47m 42.409s     42.017484   15.795114     27.133    -21.732
+ 705 Miranda        2h 48m 02.7625s    15d 48m 07.862s     42.011510   15.802184      6.439      3.722
+ 706 Cordelia       2h 48m 02.2509s    15d 48m 07.282s     42.009379   15.802023     -0.944      3.141
+ 707 Ophelia        2h 48m 02.2438s    15d 48m 00.861s     42.009349   15.800239     -1.047     -3.280
+ 708 Bianca         2h 48m 02.0990s    15d 48m 05.324s     42.008746   15.801479     -3.137      1.183
+ 709 Cressida       2h 48m 02.3358s    15d 48m 00.038s     42.009732   15.800011      0.280     -4.102
+ 710 Desdemona      2h 48m 02.4006s    15d 48m 08.008s     42.010003   15.802224      1.217      3.867
+ 711 Juliet         2h 48m 02.1052s    15d 48m 06.438s     42.008772   15.801788     -3.047      2.298
+ 712 Portia         2h 48m 02.3709s    15d 48m 08.408s     42.009879   15.802336      0.787      4.267
+ 713 Rosalind       2h 48m 02.5516s    15d 48m 06.258s     42.010632   15.801738      3.395      2.117
+ 714 Belinda        2h 48m 02.4108s    15d 47m 59.372s     42.010045   15.799825      1.363     -4.769
+ 715 Puck           2h 48m 02.1358s    15d 48m 09.016s     42.008899   15.802505     -2.605      4.876
+ 725 Perdita        2h 48m 02.3249s    15d 48m 09.230s     42.009687   15.802564      0.124      5.089
+ 726 Mab            2h 48m 02.6883s    15d 48m 04.764s     42.011201   15.801323      5.368      0.623
+ 727 Cupid          2h 48m 02.5963s    15d 48m 04.777s     42.010818   15.801327      4.040      0.636
+ 
+                   Sub-Observer         Sub-Solar     
+     Body          Lon(degE) Lat(deg)   Lon(degE) Lat(deg)   Phase(deg)   Distance(10^6 km)
+ 799 Uranus         24.025    56.016     24.014    56.122      0.10924    3098.568884
+ 701 Ariel         124.977    56.141    124.967    56.249      0.10934    3098.505814
+ 702 Umbriel       139.323    56.082    139.313    56.190      0.10941    3098.454209
+ 703 Titania       251.999    56.036    251.987    56.141      0.10931    3098.498297
+ 704 Oberon        216.747    55.830    216.739    55.936      0.10959    3098.307219
+ 705 Miranda       151.067    54.505    151.037    54.611      0.10934    3098.502972
+ 706 Cordelia      308.108    56.084    308.095    56.194      0.10923    3098.576275
+ 707 Ophelia        89.619    56.085     89.605    56.188      0.10922    3098.581622
+ 708 Bianca        334.349    55.945    334.331    56.053      0.10919    3098.599901
+ 709 Cressida      280.863    56.045    280.852    56.149      0.10923    3098.568767
+ 710 Desdemona     145.642    55.889    145.633    55.998      0.10926    3098.554071
+ 711 Juliet         45.698    55.990     45.681    56.098      0.10920    3098.598161
+ 712 Portia        105.643    55.932    105.633    56.041      0.10926    3098.557986
+ 713 Rosalind      147.213    55.738    147.207    55.846      0.10929    3098.533106
+ 714 Belinda       223.015    56.048    223.006    56.152      0.10925    3098.558348
+ 715 Puck           55.434    56.246     55.421    56.355      0.10921    3098.591319
+ 725 Perdita       163.507    55.992    163.496    56.101      0.10925    3098.564229
+ 726 Mab           223.976    55.906    223.969    56.013      0.10932    3098.514000
+ 727 Cupid         246.256    55.885    246.250    55.992      0.10930    3098.527432
+
+     Ring          Pericenter   Ascending Node (deg, from ring plane ascending node)
+     Six            58.012      283.243
+     Five          300.684      245.829
+     Four          128.182      177.661
+     Alpha          13.729       62.922
+     Beta          231.051      353.609
+     Eta             0.000        0.000
+     Gamma         200.019        0.000
+     Delta           0.000        0.000
+     Epsilon        13.383        0.000
+ 
+   Ring sub-solar latitude (deg): -56.12233  (-56.13586  to -56.10881)
+  Ring plane opening angle (deg): -56.01577  (lit)
+   Ring center phase angle (deg):   0.10924
+       Sub-solar longitude (deg): 354.11072  from ring plane ascending node
+    Sub-observer longitude (deg): 354.12204
+
+        Sun-planet distance (AU):  19.70547
+   Observer-planet distance (AU):  20.71265
+        Sun-planet distance (km):  2947.896667 x 10^6
+   Observer-planet distance (km):  3098.568884 x 10^6
+         Light travel time (sec): 10335.713263
+
+
+
Preview:

+
+Click +here +to download diagram (PDF, 21116 bytes).

+Click +here +to download diagram (JPEG format, 185169 bytes).

+Click +here +to download diagram (PostScript format, 62562 bytes). +


+Uranus Viewer Form | +RMS Node Tools | +Ring-Moon Systems Home + + diff --git a/astroquery/solarsystem/pds/tests/setup_package.py b/astroquery/solarsystem/pds/tests/setup_package.py new file mode 100644 index 0000000000..a82c3e3dd9 --- /dev/null +++ b/astroquery/solarsystem/pds/tests/setup_package.py @@ -0,0 +1,7 @@ +import os + + +def get_package_data(): + paths = [os.path.join("data", "*.html")] # etc, add other extensions + + return {"astroquery.solarsystem.pds.tests": paths} diff --git a/astroquery/solarsystem/pds/tests/test_pds.py b/astroquery/solarsystem/pds/tests/test_pds.py new file mode 100644 index 0000000000..471a6b10a6 --- /dev/null +++ b/astroquery/solarsystem/pds/tests/test_pds.py @@ -0,0 +1,229 @@ +import pytest +import os +import numpy as np + +import astropy.units as u +from astropy.table import QTable + +from ....query import AstroQuery + +from astroquery.utils.mocks import MockResponse +from ... import pds + +# files in data/ for different planets +DATA_FILES = {'Uranus': 'uranus_ephemeris.html', + 'Pluto': 'pluto_ephemeris.html', + 'Neptune': 'neptune_ephemeris.html', + 'Saturn': 'saturn_ephemeris.html', + } + + +def data_path(filename): + data_dir = os.path.join(os.path.dirname(__file__), "data") + return os.path.join(data_dir, filename) + + +# monkeypatch replacement request function +def nonremote_request(self, request_type, url, **kwargs): + + planet_name = kwargs['params']['center_body'] + with open(data_path(DATA_FILES[planet_name.capitalize()]), "rb") as f: + response = MockResponse(content=f.read(), url=url) + + return response + + +# use a pytest fixture to create a dummy 'requests.get' function, +# that mocks(monkeypatches) the actual 'requests.get' function: +@pytest.fixture +def patch_request(request): + mp = request.getfixturevalue("monkeypatch") + + mp.setattr(pds.core.RingNodeClass, "_request", nonremote_request) + return mp + + +# --------------------------------- actual test functions + +def test_parse_result(patch_request): + q = pds.RingNode() + # need _last_query to be defined + q._last_query = AstroQuery('GET', 'http://dummy') + with pytest.raises(ValueError): + q.ephemeris('dummy-planet-name') + + +def test_ephemeris_query_Uranus(patch_request): + + pds_inst = pds.RingNode() + pds_inst._last_query = AstroQuery('GET', 'http://dummy') + bodytable, ringtable = pds_inst.ephemeris( + planet="Uranus", + epoch="2022-05-03 00:00", + location=(-120.355 * u.deg, 10.0 * u.deg, 1000 * u.m), + ) + # check system table + systemtable = bodytable.meta + assert isinstance(systemtable, dict) + + assert np.allclose( + [-56.12233, -56.13586, -56.13586, -56.01577, 0.10924, 354.11072, 354.12204, 2947896667.0, 3098568884.0, 10335.713263, ], + [systemtable["sub_sun_lat"].to(u.deg).value, systemtable["sub_sun_lat_min"].to(u.deg).value, systemtable["sub_sun_lat_max"].to(u.deg).value, systemtable["opening_angle"].to(u.deg).value, systemtable["phase_angle"].to(u.deg).value, systemtable["sub_sun_lon"].to(u.deg).value, systemtable["sub_obs_lon"].to(u.deg).value, systemtable["d_sun"].to(u.km).value, systemtable["d_obs"].to(u.km).value, systemtable["light_time"].to(u.second).value, ], + rtol=1e-2, + ) + + expected_mab = """ +NAIF ID,Body, RA,Dec,RA (deg),Dec (deg),dRA,dDec,sub_obs_lon,sub_obs_lat,sub_sun_lon,sub_sun_lat,phase,distance +726,Mab,2h 48m 02.6883s,15d 48m 04.764s,42.011201,15.801323,5.368,0.6233,223.976,55.906,223.969,56.013,0.10932,3098.514 + """ + expected_mab = QTable.read(expected_mab, format='ascii.csv', + units=[None] * 4 + ['deg'] * 2 + ['arcsec'] * 2 + ['deg'] * 5 + ['Gm']) + + # check the moon Mab in body table. Slicing to make sure we get a 1 long QTable instead of a Row + mab = bodytable[16:17] + + assert mab["NAIF ID"] == 726 + assert mab["Body"] == "Mab" + assert expected_mab.values_equal(mab) + + # check a ring in ringtable + beta = ringtable[ringtable.loc_indices["Beta"]] + assert np.isclose(beta["pericenter"].to(u.deg).value, 231.051, rtol=1e-3) + assert np.isclose(beta["ascending node"].to(u.deg).value, 353.6, rtol=1e-2) + + +def test_ephemeris_query_Pluto(patch_request): + + pds_inst = pds.RingNode() + pds_inst._last_query = AstroQuery('GET', 'http://dummy') + bodytable, ringtable = pds_inst.ephemeris( + planet="Pluto", + epoch="2021-10-07 07:25", + ) + systemtable = bodytable.meta + # check system table + assert np.allclose( + [57.57737, 57.56961, 57.56961, 56.50534, 1.64048, 116.55873, 118.8369, 5142696000, 5114486810, 17060.091666, ], + [systemtable["sub_sun_lat"].to(u.deg).value, systemtable["sub_sun_lat_min"].to(u.deg).value, systemtable["sub_sun_lat_max"].to(u.deg).value, systemtable["opening_angle"].to(u.deg).value, systemtable["phase_angle"].to(u.deg).value, systemtable["sub_sun_lon"].to(u.deg).value, systemtable["sub_obs_lon"].to(u.deg).value, systemtable["d_sun"].to(u.km).value, systemtable["d_obs"].to(u.km).value, systemtable["light_time"].to(u.second).value, ], + rtol=1e-2, + ) + + # check a moon in body table + styx = bodytable[bodytable.loc_indices["Styx"]] + assert styx["NAIF ID"] == 905 + assert styx["Body"] == "Styx" + assert np.allclose( + [296.212477, -22.93533, -0.557, -1.259, 350.443, 56.472, 352.72, 57.544, 1.64047, 5114.509238, ], + [styx["RA (deg)"].to(u.deg).value, styx["Dec (deg)"].to(u.deg).value, styx["dRA"].to(u.arcsec).value, styx["dDec"].to(u.arcsec).value, styx["sub_obs_lon"].to(u.deg).value, styx["sub_obs_lat"].to(u.deg).value, styx["sub_sun_lon"].to(u.deg).value, styx["sub_sun_lat"].to(u.deg).value, styx["phase"].to(u.deg).value, styx["distance"].to(u.km * 1e6).value, ], + rtol=1e-2, + ) + + assert ringtable is None + + +def test_ephemeris_query_Neptune(patch_request): + '''Verify that the Neptune ring arcs are queried properly''' + + pds_inst = pds.RingNode() + pds_inst._last_query = AstroQuery('GET', 'http://dummy') + bodytable, ringtable = pds_inst.ephemeris( + planet="Neptune", + epoch="2021-10-07 07:25", + neptune_arcmodel=2 + ) + + expected = """ring,min_angle,max_angle +Courage,63.81977,64.81977 +Liberte,55.01978,59.11976 +Egalite A,44.21976,45.21976 +Egalite B,40.41978,43.41978 +Fraternite,26.41978,36.01978 + """ + expected = QTable.read(expected, format='ascii.csv', units=(None, 'deg', 'deg')) + + assert (expected == ringtable).all() + + +def test_ephemeris_query_Saturn(patch_request): + '''Check Saturn F ring is queried properly''' + pds_inst = pds.RingNode() + pds_inst._last_query = AstroQuery('GET', 'http://dummy') + bodytable, ringtable = pds_inst.ephemeris( + planet="Saturn", + epoch="2021-10-07 07:25", + ) + + expected = """ring,pericenter,ascending node +F,249.23097,250.34081 + """ + + expected = QTable.read(expected, format='ascii.csv', units=(None, 'deg', 'deg')) + assert (expected == ringtable).all() + + +def test_ephemeris_query_payload(): + pds_inst = pds.RingNode() + pds_inst._last_query = AstroQuery('GET', 'http://dummy') + res = pds_inst.ephemeris( + planet="Neptune", + epoch="2022-05-03 00:00", + neptune_arcmodel=1, + location=(-120.355 * u.deg, 10.0 * u.deg, 1000 * u.m), + get_query_payload=True, + ) + + assert res == dict( + [ + ("abbrev", "nep"), + ("ephem", "000 NEP081 + NEP095 + DE440"), + ( + "time", + "2022-05-03 00:00", + ), # UTC. this should be enforced when checking inputs + ("fov", 10), # next few are figure options, can be hardcoded and ignored + ("fov_unit", "Neptune radii"), + ("center", "body"), + ("center_body", "Neptune"), + ("center_ansa", "Adams Ring"), + ("center_ew", "east"), + ("center_ra", ""), + ("center_ra_type", "hours"), + ("center_dec", ""), + ("center_star", ""), + ("viewpoint", "latlon"), + ( + "observatory", + "Earth's center", + ), + ("latitude", 10), + ("longitude", -120.355), + ("lon_dir", "east"), + ("altitude", 1000), + ("moons", "814 All inner moons (N1-N8,N14)"), + ("rings", "Galle, LeVerrier, Arago, Adams"), + ("arcmodel", "#1 (820.1194 deg/day)"), + ( + "extra_ra", + "", + ), # figure options below this line, can all be hardcoded and ignored + ("extra_ra_type", "hours"), + ("extra_dec", ""), + ("extra_name", ""), + ("title", ""), + ("labels", "Small (6 points)"), + ("moonpts", "0"), + ("blank", "No"), + ("opacity", "Transparent"), + ("peris", "None"), + ("peripts", "4"), + ("arcpts", "4"), + ("meridians", "Yes"), + ("output", "html"), + ] + ) + + +def test_bad_query_raise(): + + with pytest.raises(ValueError): + bodytable, ringtable = pds.RingNode.ephemeris(planet="Venus", epoch="2021-10-07 07:25") diff --git a/astroquery/solarsystem/pds/tests/test_pds_remote.py b/astroquery/solarsystem/pds/tests/test_pds_remote.py new file mode 100644 index 0000000000..b7f13c7191 --- /dev/null +++ b/astroquery/solarsystem/pds/tests/test_pds_remote.py @@ -0,0 +1,38 @@ +import pytest +import numpy as np +import astropy.units as u + +from ... import pds + + +@pytest.mark.remote_data +class TestRingNodeClass: + def test_ephemeris_query(self): + + bodytable, ringtable = pds.RingNode.ephemeris( + planet="Uranus", + epoch="2022-05-03 00:00", + location=(-120.355 * u.deg, 10.0 * u.deg, 1000 * u.m), + ) + # check system table + systemtable = bodytable.meta + assert np.allclose( + [-56.12233, -56.13586, -56.13586, -56.01577, 0.10924, 354.11072, 354.12204, 2947896667.0, 3098568884.0, 10335.713263, ], + [systemtable["sub_sun_lat"].to(u.deg).value, systemtable["sub_sun_lat_min"].to(u.deg).value, systemtable["sub_sun_lat_max"].to(u.deg).value, systemtable["opening_angle"].to(u.deg).value, systemtable["phase_angle"].to(u.deg).value, systemtable["sub_sun_lon"].to(u.deg).value, systemtable["sub_obs_lon"].to(u.deg).value, systemtable["d_sun"].to(u.km).value, systemtable["d_obs"].to(u.km).value, systemtable["light_time"].to(u.second).value, ], + rtol=1e-2, + ) + + # check a moon in body table + mab = bodytable[bodytable.loc_indices["Mab"]] + assert mab["NAIF ID"] == 726 + assert mab["Body"] == "Mab" + assert np.allclose( + [42.011201, 15.801323, 5.368, 0.623, 223.976, 55.906, 223.969, 56.013, 0.10932, 3098.514, ], + [mab["RA (deg)"].to(u.deg).value, mab["Dec (deg)"].to(u.deg).value, mab["dRA"].to(u.arcsec).value, mab["dDec"].to(u.arcsec).value, mab["sub_obs_lon"].to(u.deg).value, mab["sub_obs_lat"].to(u.deg).value, mab["sub_sun_lon"].to(u.deg).value, mab["sub_sun_lat"].to(u.deg).value, mab["phase"].to(u.deg).value, mab["distance"].to(u.km * 1e6).value, ], + rtol=1e-2, + ) + + # check a ring in ringtable + beta = ringtable[ringtable.loc_indices["Beta"]] + assert np.isclose(beta["pericenter"].to(u.deg).value, 231.051, rtol=1e-3) + assert np.isclose(beta["ascending node"].to(u.deg).value, 353.6, rtol=1e-2) diff --git a/docs/solarsystem/pds/pds.rst b/docs/solarsystem/pds/pds.rst new file mode 100644 index 0000000000..ded553f3b3 --- /dev/null +++ b/docs/solarsystem/pds/pds.rst @@ -0,0 +1,109 @@ +.. _astroquery.solarsystem.pds: + +*********************************************************************************** +PDS Planetary Ring Node Queries (`astroquery.solarsystem.pds`) +*********************************************************************************** + +Overview +======== + + +The :class:`~astroquery.solarsystem.pds.RingNodeClass` provides an +interface to the ephemeris tools provided by the `NASA Planetary Data System's Ring Node System `_ hosted by SETI institute. + + +Ephemeris +----------- + +In order to query information for a specific Solar System body, a +``RingNode`` object is instantiated and the :meth:`~astroquery.solarsystem.pds.RingNodeClass.ephemeris` method is called. The following example queries the +ephemerides of the rings and small moons around Uranus as viewed from ALMA: + +.. doctest-remote-data:: + + >>> from astroquery.solarsystem.pds import RingNode + >>> import astropy.units as u + >>> bodytable, ringtable = RingNode.ephemeris(planet='Uranus', + ... epoch='2024-05-08 22:39', + ... location = (-67.755 * u.deg, -23.029 * u.deg, 5000 * u.m)) + >>> print(ringtable) + ring pericenter ascending node + deg deg + ------- ---------- -------------- + Six 293.129 52.0 + Five 109.438 81.1 + Four 242.882 66.9 + Alpha 184.498 253.9 + Beta 287.66 299.2 + Eta 0.0 0.0 + Gamma 50.224 0.0 + Delta 0.0 0.0 + Lambda 0.0 0.0 + Epsilon 298.022 0.0 + +``planet`` must be one of ['mars', 'jupiter', 'uranus', 'saturn', 'neptune', 'pluto'] (case-insensitive) + + +.. doctest-remote-data:: + + >>> bodytable, ringtable = RingNode.ephemeris(planet='Venus', + ... epoch='2024-05-08 22:39', + ... location = (-67.755 * u.deg, -23.029 * u.deg, 5000 * u.m)) + Traceback (most recent call last): + ... + ValueError: illegal value for 'planet' parameter (must be 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', or 'Pluto') + +``epoch`` is the datetime to query. Accepts a string in format 'YYYY-MM-DD HH:MM' (UTC assumed), or a `~astropy.time.Time` object. If no epoch is provided, the current time is used. + +``location`` is the observer's location. Accepts an `~astropy.coordinates.EarthLocation`, or any 3-element array-like (e.g. list, tuple) of format (longitude, latitude, elevation). Longitude and latitude should be anything that initializes an `~astropy.coordinates.Angle` object, and altitude should initialize a `~astropy.units.Quantity` object (with units of length). If ``None``, then the geocenter is used. + +``neptune_arcmodel`` is the choice of which ephemeris to assume for Neptune's ring arcs. accepts a float. must be one of 1, 2, or 3 (see https://pds-rings.seti.org/tools/viewer3_nep.shtml for details). default 3. has no effect if planet != 'Neptune' + +Outputs +--------- +``bodytable`` is a `~astropy.table.QTable` containing ephemeris information on the moons in the planetary system. Every column is assigned a unit from `~astropy.units`. We can get a list of all the columns in this table with: + + +.. doctest-remote-data:: + + >>> print(bodytable.columns) + + +``ringtable`` is a `~astropy.table.QTable` containing ephemeris information on the individual rings in the planetary system. Every column is assigned a unit from `~astropy.units`. We can get a list of all the columns in this table with: + + +.. doctest-remote-data:: + + >>> print(ringtable.columns) + + +Note that the behavior of ``ringtable`` changes depending on the planet you query. For Uranus and Saturn the table columns are as above. For Jupiter, Mars, and Pluto, there are no individual named rings returned by the Ring Node, so ``ringtable`` returns None; ephemeris for the ring systems of these bodies is still contained in ``systemtable`` as usual. For Neptune, the ring table shows the minimum and maximum longitudes (from the ring plane ascending node) of the five ring arcs according to the orbital evolution assumed by ``neptune_arcmodel``, e.g.: + + +.. doctest-remote-data:: + + >>> bodytable, ringtable = RingNode.ephemeris(planet='Neptune', epoch='2022-05-24 00:00') + >>> print(ringtable) + ring min_angle max_angle + deg deg + ---------- --------- --------- + Courage 53.4818 54.4818 + Liberte 44.68181 48.78178 + Egalite A 33.88179 34.88179 + Egalite B 30.0818 33.0818 + Fraternite 16.0818 25.68181 + +System-wide data are available as metadata in both ``bodytable`` and ``ringtable`` (if ``ringtable`` exists), e.g.: + +.. doctest-remote-data:: + + >>> systemtable = bodytable.meta + >>> print(systemtable.keys()) + dict_keys(['sub_sun_lat', 'sub_sun_lat_min', 'sub_sun_lat_max', 'opening_angle', 'phase_angle', 'sub_sun_lon', 'sub_obs_lon', 'd_sun', 'd_obs', 'light_time', 'epoch']) + + +Reference/API +============= + +.. automodapi:: astroquery.solarsystem.pds + :no-inheritance-diagram: diff --git a/docs/solarsystem/solarsystem.rst b/docs/solarsystem/solarsystem.rst index 013f8e6139..6730328c14 100644 --- a/docs/solarsystem/solarsystem.rst +++ b/docs/solarsystem/solarsystem.rst @@ -19,6 +19,7 @@ The currently available service providers and services are: imcce/imcce.rst jpl/jpl.rst mpc/mpc.rst + pds/pds.rst Reference/API =============