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/astroquery/solarsystem/pds/core.py b/astroquery/solarsystem/pds/core.py index 98e804401a..d24081dbbe 100644 --- a/astroquery/solarsystem/pds/core.py +++ b/astroquery/solarsystem/pds/core.py @@ -11,6 +11,7 @@ import astropy.units as u from astropy.coordinates import EarthLocation, Angle from bs4 import BeautifulSoup +from astroquery import log # 3. local imports - use relative imports # all Query classes should inherit from BaseQuery. @@ -35,6 +36,8 @@ def __init__(self): Instantiate Planetary Ring Node query ''' super().__init__() + self.URL = conf.pds_server + self.planet_defaults = conf.planet_defaults def __str__(self): """ @@ -49,28 +52,17 @@ def __str__(self): """ return "PDSRingNode instance" - # --- pretty stuff above this line, get it working below this line --- - - def ephemeris_async( - self, - planet, - obs_time=None, - location=None, - neptune_arcmodel=3, - get_query_payload=False, - get_raw_response=False, - cache=True, - ): + def ephemeris_async(self, planet, *, epoch=None, location=None, neptune_arcmodel=3, + get_query_payload=False, get_raw_response=False, cache=True): """ send query to Planetary Ring Node server Parameters ---------- - self : `~RingNodeClass` instance planet : str, required. one of Mars, Jupiter, Saturn, Uranus, Neptune, or Pluto - obs_time : `~astropy.time.Time` object, or str in format YYYY-MM-DD hh:mm, optional. + 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 obs_time is provided, the current time is used. + If no epoch is provided, the current time is used. location : array-like, or `~astropy.coordinates.EarthLocation`, optional Observer's location as a 3-element array of Earth longitude, latitude, altitude, or @@ -85,9 +77,6 @@ def ephemeris_async( get_query_payload : boolean, optional When set to `True` the method returns the HTTP request parameters as a dict, default: False - get_raw_response : boolean, optional - Return raw data as obtained by the Planetary Ring Node without parsing the data - into a table, default: False Returns @@ -99,10 +88,11 @@ def ephemeris_async( Examples -------- >>> from astroquery.solarsystem.pds import RingNode + >>> import astropy.units as u >>> systemtable, bodytable, ringtable = RingNode.ephemeris(planet='Uranus', - ... obs_time='2024-05-08 22:39', - ... location = (-23.029 * u.deg, -67.755 * u.deg, 5000 * u.m)) # doctest: +SKIP - >>> print(ringtable) # doctest: +SKIP + ... 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 ------- ---------- -------------- @@ -118,62 +108,26 @@ def ephemeris_async( Epsilon 298.022 0.0 """ - URL = conf.pds_server - # URL = 'https://pds-rings.seti.org/cgi-bin/tools/viewer3_xxx.pl?' + planet = planet.lower() + if planet not in ["mars", "jupiter", "saturn", "uranus", "neptune", "pluto", ]: + raise ValueError( + "illegal value for 'planet' parameter (must be 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', or 'Pluto')" + ) - # check inputs and set defaults for optional inputs - if planet is None: - raise ValueError("'planet' parameter not set. Query aborted.") - else: - planet = planet.lower() - if planet not in [ - "mars", - "jupiter", - "saturn", - "uranus", - "neptune", - "pluto", - ]: - raise ValueError( - "illegal value for 'planet' parameter (must be 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', or 'Pluto')" - ) - - if obs_time is None: - obs_time = Time.now().strftime("%Y-%m-%d %H:%M") - warnings.warn("obs_time not set. using current time instead.") - elif type(obs_time) == str: - try: - Time.strptime(obs_time, "%Y-%m-%d %H:%M").jd - except Exception as ex: - raise ValueError( - "illegal value for 'obs_time' parameter. string must have format 'yyyy-mm-dd hh:mm'" - ) - elif type(obs_time) == Time: - try: - obs_time = obs_time.utc.to_value("iso", subfmt="date_hm") - except Exception as ex: - raise ValueError( - "illegal value for 'obs_time' parameter. could not parse astropy.time.core.Time object into format 'yyyy-mm-dd hh:mm' (UTC)" - ) + 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() + log.warning("Observation time not set. Using current time.") if location is None: viewpoint = "observatory" latitude, longitude, altitude = "", "", "" - print("Observatory coordinates not set. Using center of Earth.") + log.warning("Observatory coordinates not set. Using center of Earth.") else: viewpoint = "latlon" - if type(location) != EarthLocation: - if hasattr(location, "__iter__"): - if len(location) != 3: - raise ValueError( - "location arrays require three values:" - " longitude, latitude, and altitude" - ) - else: - raise TypeError( - "location must be array-like or astropy EarthLocation" - ) - if isinstance(location, EarthLocation): loc = location.geodetic longitude = loc[0].deg @@ -193,38 +147,29 @@ def ephemeris_async( # thankfully, adding extra planet-specific keywords here does not break query for other planets request_payload = OrderedDict( [ - ("abbrev", planet.lower()[:3]), - ("ephem", conf.planet_defaults[planet]["ephem"],), - ("time", obs_time), # UTC. this should be enforced when checking inputs - ( - "fov", - 10, - ), # next few are figure options, can be hardcoded and ignored + ("abbrev", planet[:3]), + ("ephem", self.planet_defaults[planet]["ephem"]), + ("time", epoch.utc.to_value("iso", subfmt="date_hm")), # UTC. this should be enforced when checking inputs + ("fov", 10), # next few are figure options, can be hardcoded and ignored ("fov_unit", planet.capitalize() + " radii"), ("center", "body"), ("center_body", planet.capitalize()), - ("center_ansa", conf.planet_defaults[planet]["center_ansa"]), + ("center_ansa", self.planet_defaults[planet]["center_ansa"]), ("center_ew", "east"), ("center_ra", ""), ("center_ra_type", "hours"), ("center_dec", ""), ("center_star", ""), ("viewpoint", viewpoint), - ( - "observatory", - "Earth's center", - ), # has no effect if viewpoint != observatory so can hardcode. no plans to implement calling observatories by name since ring node only names like 8 observatories + ("observatory", "Earth's center"), # has no effect if viewpoint != observatory so can hardcode. no plans to implement calling observatories by name since ring node only names like 8 observatories ("latitude", latitude), ("longitude", longitude), ("lon_dir", "east"), ("altitude", altitude), - ("moons", conf.planet_defaults[planet]["moons"]), - ("rings", conf.planet_defaults[planet]["rings"]), + ("moons", self.planet_defaults[planet]["moons"]), + ("rings", self.planet_defaults[planet]["rings"]), ("arcmodel", conf.neptune_arcmodels[int(neptune_arcmodel)]), - ( - "extra_ra", - "", - ), # figure options below this line, can all be hardcoded and ignored + ("extra_ra", ""), # figure options below this line, can all be hardcoded and ignored ("extra_ra_type", "hours"), ("extra_dec", ""), ("extra_name", ""), @@ -245,26 +190,21 @@ def ephemeris_async( if get_query_payload: return request_payload - # set return_raw flag, if raw response desired - if get_raw_response: - self.return_raw = True - # query and parse response = self._request( - "GET", URL, params=request_payload, timeout=self.TIMEOUT, cache=cache + "GET", self.URL, params=request_payload, timeout=self.TIMEOUT, cache=cache ) - self.uri = response.url return response - def _parse_ringnode(self, src): + def _parse_result(self, response, verbose=None): """ Routine for parsing data from ring node Parameters ---------- self : RingNodeClass instance - src : list + response : list raw response from server @@ -274,9 +214,15 @@ def _parse_ringnode(self, src): bodytable : `astropy.Table` ringtable : `astropy.Table` """ + self.last_response = response + try: + self._last_query.remove_cache_file(self.cache_location) + except OSError: + # this is allowed: if `cache` was set to False, this + # won't be needed + pass - self.raw_response = src - soup = BeautifulSoup(src, "html.parser") + soup = BeautifulSoup(response.text, "html.parser") text = soup.get_text() # print(repr(text)) textgroups = re.split("\n\n|\n \n", text) @@ -284,57 +230,28 @@ def _parse_ringnode(self, src): for group in textgroups: group = group.strip(", \n") - # input parameters. only thing needed is obs_time + # input parameters. only thing needed is epoch if group.startswith("Observation"): - obs_time = group.split("\n")[0].split("e: ")[-1].strip(", \n") + epoch = group.split("\n")[0].split("e: ")[-1].strip(", \n") # minor body table part 1 elif group.startswith("Body"): group = "NAIF " + group # fixing lack of header for NAIF ID - bodytable = ascii.read( - group, - format="fixed_width", - col_starts=(0, 4, 18, 35, 54, 68, 80, 91), - col_ends=(4, 18, 35, 54, 68, 80, 91, 102), - names=( - "NAIF ID", - "Body", - "RA", - "Dec", - "RA (deg)", - "Dec (deg)", - "dRA", - "dDec", - ), - ) - units_list = [None, None, None, None, u.deg, u.deg, u.arcsec, u.arcsec] - bodytable = table.QTable(bodytable, units=units_list) - # for i in range(len(bodytable.colnames)): - # bodytable[bodytable.colnames[i]].unit = units_list[i] + bodytable = table.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=("NAIF ID", "Body", "RA", "Dec", "RA (deg)", "Dec (deg)", "dRA", "dDec"), + units=([None, None, None, None, u.deg, u.deg, u.arcsec, u.arcsec])) + # minor body table part 2 elif group.startswith("Sub-"): group = "\n".join(group.split("\n")[1:]) # fixing two-row header group = "NAIF" + group[4:] - bodytable2 = ascii.read( - group, - format="fixed_width", - col_starts=(0, 4, 18, 28, 37, 49, 57, 71), - col_ends=(4, 18, 28, 37, 49, 57, 71, 90), - names=( - "NAIF ID", - "Body", - "sub_obs_lon", - "sub_obs_lat", - "sub_sun_lon", - "sub_sun_lat", - "phase", - "distance", - ), - ) - units_list = [None, None, u.deg, u.deg, u.deg, u.deg, u.deg, u.km * 1e6] - bodytable2 = table.QTable(bodytable2, units=units_list) - # for i in range(len(bodytable2.colnames)): - # bodytable2[bodytable2.colnames[i]].unit = units_list[i] + bodytable2 = table.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=("NAIF ID", "Body", "sub_obs_lon", "sub_obs_lat", "sub_sun_lon", "sub_sun_lat", "phase", "distance"), + units=([None, None, u.deg, u.deg, u.deg, u.deg, u.deg, u.km * 1e6])) # ring plane data elif group.startswith("Ring s"): @@ -345,8 +262,6 @@ def _parse_ringnode(self, src): [sub_sun_lat, sub_sun_lat_min, sub_sun_lat_max] = [ float(s.strip(", \n()")) for s in re.split("\(|to", l[1]) ] - # systemtable = table.Table([[sub_sun_lat], [sub_sun_lat_max], [sub_sun_lat_min]], - # names = ('sub_sun_lat', 'sub_sun_lat_max', 'sub_sun_lat_min')) systemtable = { "sub_sun_lat": sub_sun_lat * u.deg, "sub_sun_lat_min": sub_sun_lat_min * u.deg, @@ -374,10 +289,10 @@ def _parse_ringnode(self, src): for line in lines: l = line.split(":") if "Sun-planet distance (AU)" in l[0]: - # systemtable["d_sun_AU"] = float(l[1].strip(", \n")) + # this is redundant with sun distance in km pass elif "Observer-planet distance (AU)" in l[0]: - # systemtable["d_obs_AU"] = float(l[1].strip(", \n")) + # this is redundant with observer distance in km pass elif "Sun-planet distance (km)" in l[0]: systemtable["d_sun"] = ( @@ -395,16 +310,11 @@ def _parse_ringnode(self, src): # --------- below this line, planet-specific info ------------ # Uranus individual rings data elif group.startswith("Ring "): - ringtable = ascii.read( - " " + group, - format="fixed_width", + ringtable = table.QTable.read(" " + group, format="ascii.fixed_width", col_starts=(5, 18, 29), col_ends=(18, 29, 36), names=("ring", "pericenter", "ascending node"), - ) - - units_list = [None, u.deg, u.deg] - ringtable = table.QTable(ringtable, units=units_list) + units=([None, u.deg, u.deg])) # Saturn F-ring data elif group.startswith("F Ring"): @@ -450,44 +360,9 @@ def _parse_ringnode(self, src): bodytable = table.join(bodytable, bodytable2) # concatenate minor body table bodytable.add_index("Body") - systemtable["obs_time"] = Time( - obs_time, format="iso", scale="utc" - ) # add obs time to systemtable + systemtable["epoch"] = Time(epoch, format="iso", scale="utc") # add obs time to systemtable return systemtable, bodytable, ringtable - def _parse_result(self, response, verbose=None): - """ - Routine for managing parser calls - - Parameters - ---------- - self : RingNodeClass instance - response : string - raw response from server - - Returns - ------- - systemtable : dict - bodytable : `astropy.Table` - ringtable : `astropy.Table` - """ - self.last_response = response - try: - systemtable, bodytable, ringtable = self._parse_ringnode(response.text) - except Exception as ex: - try: - self._last_query.remove_cache_file(self.cache_location) - except OSError: - # this is allowed: if `cache` was set to False, this - # won't be needed - pass - raise - return ( - systemtable, - bodytable, - ringtable, - ) - RingNode = RingNodeClass() diff --git a/astroquery/solarsystem/pds/tests/test_ringnode.py b/astroquery/solarsystem/pds/tests/test_ringnode.py index e13bc1a052..dc69fb258c 100644 --- a/astroquery/solarsystem/pds/tests/test_ringnode.py +++ b/astroquery/solarsystem/pds/tests/test_ringnode.py @@ -1,6 +1,5 @@ import pytest import os -from collections import OrderedDict import numpy as np from astropy.tests.helper import assert_quantity_allclose @@ -48,7 +47,7 @@ def test_ephemeris_query_Uranus(patch_request): systemtable, bodytable, ringtable = pds.RingNode.ephemeris( planet="Uranus", - obs_time="2022-05-03 00:00", + epoch="2022-05-03 00:00", location=(10.0 * u.deg, -120.355 * u.deg, 1000 * u.m), ) # check system table @@ -122,7 +121,7 @@ def test_ephemeris_query_Pluto(patch_request): systemtable, bodytable, ringtable = pds.RingNode.ephemeris( planet="Pluto", - obs_time="2021-10-07 07:25", + epoch="2021-10-07 07:25", ) print(systemtable) print(bodytable[bodytable.loc_indices["Styx"]]) @@ -196,7 +195,7 @@ def test_ephemeris_query_Neptune(patch_request): systemtable, bodytable, ringtable = pds.RingNode.ephemeris( planet="Neptune", - obs_time="2021-10-07 07:25", + epoch="2021-10-07 07:25", neptune_arcmodel=2 ) @@ -230,7 +229,7 @@ def test_ephemeris_query_Saturn(patch_request): '''Check Saturn F ring is queried properly''' systemtable, bodytable, ringtable = pds.RingNode.ephemeris( planet="Saturn", - obs_time="2021-10-07 07:25", + epoch="2021-10-07 07:25", ) print(ringtable) @@ -247,13 +246,13 @@ def test_ephemeris_query_Saturn(patch_request): def test_ephemeris_query_payload(): res = pds.RingNode.ephemeris( planet="Neptune", - obs_time="2022-05-03 00:00", + epoch="2022-05-03 00:00", neptune_arcmodel=1, location=(10.0 * u.deg, -120.355 * u.deg, 1000 * u.m), get_query_payload=True, ) - assert res == OrderedDict( + assert res == dict( [ ("abbrev", "nep"), ("ephem", "000 NEP081 + NEP095 + DE440"), @@ -302,27 +301,3 @@ def test_ephemeris_query_payload(): ("output", "html"), ] ) - - -def test_bad_query_exception_throw(): - - with pytest.raises(ValueError): - pds.RingNode.ephemeris(planet="Mercury", obs_time="2022-05-03 00:00") - - with pytest.raises(ValueError): - pds.RingNode.ephemeris(planet="Uranus", obs_time="2022-13-03 00:00") - - with pytest.raises(ValueError): - pds.RingNode.ephemeris( - planet="Neptune", - obs_time="2022-05-03 00:00", - location=(10.0 * u.deg, -120.355 * u.deg), - ) - - with pytest.raises(ValueError): - pds.RingNode.ephemeris( - planet="Neptune", - obs_time="2022-05-03 00:00", - location=(10.0 * u.deg, -120.355 * u.deg, 1000 * u.m), - neptune_arcmodel=0, - ) diff --git a/astroquery/solarsystem/pds/tests/test_ringnode_remote.py b/astroquery/solarsystem/pds/tests/test_ringnode_remote.py index 2aed624341..b683a3c64a 100644 --- a/astroquery/solarsystem/pds/tests/test_ringnode_remote.py +++ b/astroquery/solarsystem/pds/tests/test_ringnode_remote.py @@ -20,7 +20,7 @@ def test_ephemeris_query(self): systemtable, bodytable, ringtable = pds.RingNode.ephemeris( planet="Uranus", - obs_time="2022-05-03 00:00", + epoch="2022-05-03 00:00", location=(10.0 * u.deg, -120.355 * u.deg, 1000 * u.m), ) # check system table diff --git a/docs/solarsystem/pds/pds.rst b/docs/solarsystem/pds/pds.rst index b6e9c49769..eee748b5b5 100644 --- a/docs/solarsystem/pds/pds.rst +++ b/docs/solarsystem/pds/pds.rst @@ -1,5 +1,3 @@ -.. doctest-skip-all - .. _astroquery.solarsystem.pds: *********************************************************************************** @@ -10,7 +8,7 @@ Overview ======== -The :class:`~astroquery.solarsystem.pds.RingNodeClass` class provides an +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 @@ -23,9 +21,10 @@ ephemerides of the rings and small moons around Uranus as viewed from ALMA: .. code-block:: python >>> from astroquery.solarsystem.pds import RingNode + >>> import astropy.units as u >>> systemtable, bodytable, ringtable = RingNode.ephemeris(planet='Uranus', - ... obs_time='2024-05-08 22:39', - ... location = (-23.029 * u.deg, -67.755 * u.deg, 5000 * u.m)) # doctest: +SKIP + ... epoch='2024-05-08 22:39', + ... location = (-23.029 * u.deg, -67.755 * u.deg, 5000 * u.m)) # doctest: +REMOTE_DATA >>> print(ringtable) ring pericenter ascending node deg deg @@ -43,29 +42,29 @@ ephemerides of the rings and small moons around Uranus as viewed from ALMA: ``planet`` must be one of ['mars', 'jupiter', 'uranus', 'saturn', 'neptune', 'pluto'] (case-insensitive) -``obs_time`` is the datetime to query. Accepts a string in format 'YYYY-MM-DD HH:MM' (UTC assumed), or an ```astropy.Time`` object. If no obs_time is provided, the current time is used. +``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 (latitude, longitude, elevation). 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 geocenter 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 (latitude, longitude, 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 --------- -``systemtable`` is a dictionary containing system-wide ephemeris data. Every value is an `astropy Quantity`_ object. We can get a list of all the keys in this dictionary with: +``systemtable`` is a dict containing system-wide ephemeris data. Every value is a `~astropy.units.Quantity`. We can get a list of all the keys in this dictionary with: .. code-block:: python >>> 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', 'obs_time']) + 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']) -``bodytable`` is an `astropy table`_ 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: +``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: .. code-block:: python >>> print(bodytable.columns) -``ringtable`` is an `astropy table`_ 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: +``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: .. code-block:: python @@ -76,41 +75,16 @@ Note that the behavior of ``ringtable`` changes depending on the planet you quer .. code-block:: python - >>> systemtable, bodytable, ringtable = RingNode.ephemeris(planet='Neptune') + >>> systemtable, bodytable, ringtable = RingNode.ephemeris(planet='Neptune', epoch='2022-05-24 00:00') # doctest: +REMOTE_DATA >>> print(ringtable) - ring min_angle max_angle - deg deg - ---------- --------- --------- - Courage 46.39438 47.39438 - Liberte 37.59439 41.69437 - Egalite A 26.79437 27.79437 - Egalite B 22.99439 25.99439 - Fraternite 8.99439 18.59439 - - - -Hints and Tricks -================ - -Checking the original RingNode output ------------------------------------------ - -Once the query method has been called, the retrieved raw response is -stored in the attribute ``raw_response``. Inspecting this response can help to -understand issues with your query, or you can process the results differently. - -The query URI (the URI is what you would put into the URL -field of your web browser) that is used to request the data from the Planetary Ring Node server can be obtained from the -:class:`~astroquery.solarsystem.pds.RingNodeClass` object after a query has been -performed (before the query only ``None`` would be returned): - - >>> print(obj.uri) - https://pds-rings.seti.org/cgi-bin/tools/viewer3_xxx.pl?abbrev=ura&ephem=000+URA111+%2B+URA115+%2B+DE440&time=2024-05-08+22%3A39&fov=10&fov_unit=Uranus+radii¢er=body¢er_body=Uranus¢er_ansa=Epsilon¢er_ew=east¢er_ra=¢er_ra_type=hours¢er_dec=¢er_star=&viewpoint=latlon&observatory=Earth%27s+center&latitude=-67.75499999999998&longitude=-23.028999999999996&lon_dir=east&altitude=4999.999999999843&moons=727+All+inner+moons+%28U1-U15%2CU25-U27%29&rings=All+rings&arcmodel=%233+%28820.1121+deg%2Fday%29&extra_ra=&extra_ra_type=hours&extra_dec=&extra_name=&title=&labels=Small+%286+points%29&moonpts=0&blank=No&opacity=Transparent&peris=None&peripts=4&arcpts=4&meridians=Yes&output=html - -If your query failed, it might be useful for you to put the URI into a web -browser to get more information why it failed. Please note that ``uri`` is an -attribute of :class:`~astroquery.solarsystem.pds.RingNodeClass` and not the results -table. + 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 Acknowledgements