Skip to content

Commit d96fa2e

Browse files
Edward Molterbsipocz
authored andcommitted
minimum working example. parses basic query for all six targets
1 parent 6722d7a commit d96fa2e

File tree

2 files changed

+201
-32
lines changed

2 files changed

+201
-32
lines changed

astroquery/solarsystem/pds/__init__.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,42 @@ class Conf(_config.ConfigNamespace):
2626

2727
# PDS settings - put hardcoded dictionaries of any kind here
2828

29-
planet_defaults = {'jupiter':{
30-
'ephem':'000 URA111 %2B+URA115+%2B+DE440',
31-
'moons':'727+All+inner+moons+%28U1-U15%2CU25-U27%29&'
29+
planet_defaults = {'mars':{
30+
'ephem':'000 MAR097 + DE440',
31+
'moons':'402 Phobos, Deimos',
32+
'center_ansa':'Phobos Ring',
33+
'rings':'Phobos, Deimos',
34+
},
35+
'jupiter':{
36+
'ephem':'000 JUP365 + DE440',
37+
'moons':'516 All inner moons (J1-J5,J14-J16)',
38+
'center_ansa':'Main Ring',
39+
'rings':'Main & Gossamer',
3240
},
3341
'saturn':{
34-
'ephem':'000+URA111+%2B+URA115+%2B+DE440',
35-
'moons':'727+All+inner+moons+%28U1-U15%2CU25-U27%29&'
42+
'ephem':'000 SAT389 + SAT393 + SAT427 + DE440',
43+
'moons':'653 All inner moons (S1-S18,S32-S35,S49,S53)',
44+
'center_ansa':'A',
45+
'rings':'A,B,C,F,G,E',
3646
},
3747
'uranus':{
3848
'ephem':'000 URA111 + URA115 + DE440',
39-
'moons':'727 All inner moons (U1-U15,U25-U27)'
49+
'moons':'727 All inner moons (U1-U15,U25-U27)',
50+
'center_ansa':'Epsilon',
51+
'rings':'All rings',
4052
},
4153
'neptune':{
42-
'ephem':'000+URA111+%2B+URA115+%2B+DE440',
43-
'moons':'727+All+inner+moons+%28U1-U15%2CU25-U27%29&'
44-
}
54+
'ephem':'000 NEP081 + NEP095 + DE440',
55+
'moons':'814 All inner moons (N1-N8,N14)',
56+
'center_ansa':'Adams Ring',
57+
'rings':'Galle, LeVerrier, Arago, Adams',
58+
},
59+
'pluto':{
60+
'ephem':'000 PLU058 + DE440',
61+
'moons':'905 All moons (P1-P5)',
62+
'center_ansa':'Hydra',
63+
'rings':'Styx, Nix, Kerberos, Hydra',
64+
}
4565
}
4666

4767

astroquery/solarsystem/pds/core.py

Lines changed: 172 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22
# 1. standard library imports
33
import numpy as np
44
from collections import OrderedDict
5+
import re
56

67
# 2. third party imports
78
from astropy.time import Time
8-
from astropy.table import Table, Column
9+
from astropy import table
10+
from astropy.io import ascii
11+
from bs4 import BeautifulSoup
912

1013
# 3. local imports - use relative imports
1114
# commonly required local imports shown below as example
@@ -27,25 +30,25 @@ class RingNodeClass(BaseQuery):
2730

2831
TIMEOUT = conf.timeout
2932

30-
def __init__(self, planet=None, obstime=None):
33+
def __init__(self, planet=None, obs_time=None):
3134
'''Instantiate Planetary Ring Node query
3235
3336
Parameters
3437
----------
3538
planet : str, required. one of Jupiter, Saturn, Uranus, or Neptune
3639
Name, number, or designation of the object to be queried.
37-
obstime : str, in JD or MJD format. If no obstime is provided, the current
40+
obs_time : str, in JD or MJD format. If no obs_time is provided, the current
3841
time is used.
3942
'''
4043

4144
super().__init__()
4245
self.planet = planet
4346
if self.planet is not None:
4447
self.planet = self.planet.lower()
45-
if self.planet not in ['jupiter', 'saturn', 'uranus', 'neptune']:
46-
raise ValueError("illegal value for 'planet' parameter (must be Jupiter, Saturn, Uranus, or Neptune")
48+
if self.planet not in ['mars', 'jupiter', 'saturn', 'uranus', 'neptune', 'pluto']:
49+
raise ValueError("illegal value for 'planet' parameter (must be 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune', or 'Pluto'")
4750

48-
self.obstime = obstime
51+
self.obs_time = obs_time
4952

5053

5154
def __str__(self):
@@ -56,13 +59,13 @@ def __str__(self):
5659
--------
5760
>>> from astroquery.solarsystem.pds import RingNode
5861
>>> uranus = Horizons(planet='Uranus',
59-
... obstime='2017-01-01 00:00
62+
... obs_time='2017-01-01 00:00
6063
>>> print(uranus) # doctest: +SKIP
61-
PDSRingNode instance "Uranus"; obstime=2017-01-01 00:00
64+
PDSRingNode instance "Uranus"; obs_time=2017-01-01 00:00
6265
'''
63-
return ('PDSRingNode instance \"{:s}\"; obstime={:s}').format(
66+
return ('PDSRingNode instance \"{:s}\"; obs_time={:s}').format(
6467
str(self.planet),
65-
str(self.obstime))
68+
str(self.obs_time))
6669
# --- pretty stuff above this line, get it working below this line ---
6770

6871
def ephemeris_async(self,
@@ -83,7 +86,7 @@ def ephemeris_async(self,
8386
--------
8487
>>> from astroquery.planetary.pds import RingNode
8588
>>> uranus = Horizons(planet='Uranus',
86-
... obstime='2017-01-01 00:00
89+
... obs_time='2017-01-01 00:00
8790
>>> eph = obj.ephemeris() # doctest: +SKIP
8891
>>> print(eph) # doctest: +SKIP
8992
table here...
@@ -95,20 +98,25 @@ def ephemeris_async(self,
9598
# check for required information
9699
if self.planet is None:
97100
raise ValueError("'planet' parameter not set. Query aborted.")
98-
if self.obstime is None:
99-
self.obstime = Time.now().jd
101+
if self.obs_time is None:
102+
self.obs_time = Time.now().jd
103+
104+
'''
105+
https://pds-rings.seti.org/cgi-bin/tools/viewer3_xxx.pl?abbrev=jup&ephem=000+JUP365+%2B+DE440&time=2021-10-07+07%3A25&fov=10&fov_unit=Jupiter+radii&center=body&center_body=Jupiter&center_ansa=Main+Ring&center_ew=east&center_ra=&center_ra_type=hours&center_dec=&center_star=&viewpoint=observatory&observatory=Earth%27s+center&latitude=&longitude=&lon_dir=east&altitude=&moons=516+All+inner+moons+%28J1-J5%2CJ14-J16%29&rings=Main+%26+Gossamer&torus_inc=6.8&torus_rad=422000&extra_ra=&extra_ra_type=hours&extra_dec=&extra_name=&title=&labels=Small+%286+points%29&moonpts=0&blank=No&meridians=Yes&output=HTML
106+
'''
100107

101108
# configure request_payload for ephemeris query
102109
# start with successful query and incrementally de-hardcode stuff
110+
# thankfully, adding extra planet-specific keywords here does not break query for other planets
103111
request_payload = OrderedDict([
104112
('abbrev', self.planet[:3]),
105113
('ephem', conf.planet_defaults[self.planet]['ephem']), # change hardcoding for other planets
106-
('time', self.obstime),
114+
('time', self.obs_time),
107115
('fov', 10), #for now do not care about the diagram. next few hardcoded
108116
('fov_unit', self.planet.capitalize()+' radii'),
109117
('center', 'body'),
110118
('center_body', self.planet.capitalize()),
111-
('center_ansa', 'Epsilon Ring'),
119+
('center_ansa', conf.planet_defaults[self.planet]['center_ansa']),
112120
('center_ew', 'east'),
113121
('center_ra', ''),
114122
('center_ra_type', 'hours'),
@@ -121,7 +129,8 @@ def ephemeris_async(self,
121129
('lon_dir',''), # de-hardcode later!
122130
('altitude',''), # de-hardcode later!
123131
('moons',conf.planet_defaults[self.planet]['moons']), # change hardcoding for other planets
124-
('rings','All rings'), # check if this works for other planets
132+
('rings',conf.planet_defaults[self.planet]['rings']), # check if this works for other planets
133+
('arcmodel','#3 (820.1121 deg/day)'), # check if this works for other planets
125134
('extra_ra',''), # diagram stuff. next few can be hardcoded
126135
('extra_ra_type','hours'),
127136
('extra_dec',''),
@@ -130,8 +139,10 @@ def ephemeris_async(self,
130139
('labels','Small (6 points)'),
131140
('moonpts','0'),
132141
('blank', 'No'),
142+
('opacity', 'Transparent'),
133143
('peris', 'None'),
134144
('peripts', '4'),
145+
('arcpts', '4'),
135146
('meridians', 'Yes'),
136147
('output', 'html')
137148
])
@@ -160,13 +171,134 @@ def ephemeris_async(self,
160171
def _parse_ringnode(self, src):
161172
'''
162173
Routine for parsing data from ring node
174+
175+
Parameters
176+
----------
177+
self : HorizonsClass instance
178+
src : list
179+
raw response from server
180+
181+
182+
Returns
183+
-------
184+
data : `astropy.Table`
163185
'''
164186

165187
self.raw_response = src
188+
soup = BeautifulSoup(src, 'html.parser')
189+
text = soup.get_text()
190+
#print(repr(text))
191+
textgroups = re.split('\n\n|\n \n', text)
192+
ringtable = None
193+
for group in textgroups:
194+
group = group.strip(', \n')
195+
196+
#input parameters. only thing needed is obs_time
197+
if group.startswith('Observation'):
198+
obs_time = group.split('\n')[0].split('e: ')[-1].strip(', \n')
199+
self.obs_time = obs_time
200+
201+
#minor body table part 1
202+
elif group.startswith('Body'):
203+
group = 'NAIF '+group #fixing lack of header for NAIF ID
204+
bodytable = ascii.read(group, format='fixed_width',
205+
col_starts=(0, 4, 18, 35, 54, 68, 80, 91),
206+
col_ends= (4, 18, 35, 54, 68, 80, 91, 102),
207+
names = ('NAIF ID', 'Body', 'RA', 'Dec', 'RA (deg)', 'Dec (deg)', 'dRA', 'dDec')
208+
)
209+
#minor body table part 2
210+
elif group.startswith('Sub-'):
211+
group = '\n'.join(group.split('\n')[1:]) #fixing two-row header
212+
group = 'NAIF'+group[4:]
213+
bodytable2 = ascii.read(group, format='fixed_width',
214+
col_starts=(0, 4, 18, 28, 37, 49, 57, 71),
215+
col_ends= (4, 18, 28, 37, 49, 57, 71, 90),
216+
names = ('NAIF ID', 'Body', 'sub_obs_lon', 'sub_obs_lat', 'sub_sun_lon', 'sub_sun_lat', 'phase', 'distance')
217+
)
218+
## to do: add units!!
219+
220+
#ring plane data
221+
elif group.startswith('Ring s'):
222+
lines = group.split('\n')
223+
for line in lines:
224+
l = line.split(':')
225+
if 'Ring sub-solar latitude' in l[0]:
226+
[sub_sun_lat, sub_sun_lat_min, sub_sun_lat_max] = [float(s.strip(', \n()')) for s in re.split('\(|to', l[1])]
227+
systemtable = table.Table([['sub_sun_lat','sub_sun_lat_max','sub_sun_lat_min'],
228+
[sub_sun_lat, sub_sun_lat_max, sub_sun_lat_min]],
229+
names = ('Parameter', 'Value'))
230+
231+
elif 'Ring plane opening angle' in l[0]:
232+
systemtable.add_row(['opening_angle', float(re.sub('[a-zA-Z]+', '', l[1]).strip(', \n()'))])
233+
elif 'Ring center phase angle' in l[0]:
234+
systemtable.add_row(['phase_angle', float(l[1].strip(', \n'))])
235+
elif 'Sub-solar longitude' in l[0]:
236+
systemtable.add_row(['sub_sun_lon', float(re.sub('[a-zA-Z]+', '', l[1]).strip(', \n()'))])
237+
elif 'Sub-observer longitude' in l[0]:
238+
systemtable.add_row(['sub_obs_lon', float(l[1].strip(', \n'))])
239+
else:
240+
pass
241+
## to do: add units?
242+
243+
#basic info about the planet
244+
elif group.startswith('Sun-planet'):
245+
lines = group.split('\n')
246+
for line in lines:
247+
l = line.split(':')
248+
if 'Sun-planet distance (AU)' in l[0]:
249+
systemtable.add_row(['d_sun_AU', float(l[1].strip(', \n'))])
250+
elif 'Observer-planet distance (AU)' in l[0]:
251+
systemtable.add_row(['d_obs_AU', float(l[1].strip(', \n'))])
252+
elif 'Sun-planet distance (km)' in l[0]:
253+
systemtable.add_row(['d_sun_km', float(l[1].split('x')[0].strip(', \n'))*1e6])
254+
elif 'Observer-planet distance (km)' in l[0]:
255+
systemtable.add_row(['d_obs_km', float(l[1].split('x')[0].strip(', \n'))*1e6])
256+
elif 'Light travel time' in l[0]:
257+
systemtable.add_row(['light_time', float(l[1].strip(', \n'))])
258+
else:
259+
pass
260+
261+
## to do: add units?
262+
263+
# --------- below this line, planet-specific info ------------
264+
# Uranus individual rings data
265+
elif group.startswith('Ring '):
266+
ringtable = ascii.read(' '+group, format='fixed_width',
267+
col_starts=(5, 18, 29),
268+
col_ends= (18, 29, 36),
269+
names = ('ring', 'pericenter', 'ascending node')
270+
)
271+
272+
# Saturn F-ring data - NEEDS TESTING
273+
elif group.startswith('F Ring'):
274+
lines = group.split('\n')
275+
for line in lines:
276+
l = line.split(':')
277+
if 'F Ring pericenter' in l[0]:
278+
peri = float(re.sub('[a-zA-Z]+', '', l[1]).strip(', \n()'))
279+
elif 'F Ring ascending node' in l[0]:
280+
ascn = float(l[1].strip(', \n'))
281+
ringtable = table.Table([['F'], [peri], [ascn]], names=('ring', 'pericenter', 'ascending node'))
282+
283+
# Neptune ring arcs data - NEEDS TESTING
284+
elif group.startswith('Courage'):
285+
lines = group.split('\n')
286+
for i in range(len(lines)):
287+
l = lines[i].split(':')
288+
ring = l[0].split('longitude')[0].strip(', \n')
289+
[min_angle, max_angle] = [float(s.strip(', \n')) for s in re.sub('[a-zA-Z]+', '', l[1]).strip(', \n()').split()]
290+
if i == 0:
291+
ringtable = table.Table([[ring], [min_angle], [max_angle]], names=('ring', 'min_angle', 'max_angle'))
292+
else:
293+
ringtable.add_row([ring, min_angle, max_angle])
294+
295+
else:
296+
pass
166297

298+
# concatenate minor body table
299+
bodytable = table.join(bodytable, bodytable2)
167300

168-
169-
return src
301+
return systemtable, bodytable, ringtable
170302

171303
def _parse_result(self, response, verbose = None):
172304
'''
@@ -185,7 +317,7 @@ def _parse_result(self, response, verbose = None):
185317
'''
186318
self.last_response = response
187319
try:
188-
data = self._parse_ringnode(response.text)
320+
systemtable, bodytable, ringtable = self._parse_ringnode(response.text)
189321
except:
190322
try:
191323
self._last_query.remove_cache_file(self.cache_location)
@@ -194,13 +326,30 @@ def _parse_result(self, response, verbose = None):
194326
# won't be needed
195327
pass
196328
raise
197-
return data #astropy table
329+
return systemtable, bodytable, ringtable #astropy table, astropy table, astropy table
198330

199331
RingNode = RingNodeClass()
200332

201333
if __name__ == "__main__":
202334

203-
uranus = RingNodeClass('Uranus', '2019-10-28 00:00')
204-
response = uranus.ephemeris()
205-
print(response)
335+
# single basic query
336+
neptune = RingNodeClass('Neptune', '2019-10-28 00:00')
337+
systemtable, bodytable, ringtable = neptune.ephemeris()
338+
339+
'''
340+
# basic query for all six targets
341+
for major_body in ['mars', 'jupiter', 'uranus', 'saturn', 'neptune', 'pluto']:
342+
nodequery = RingNode(major_body, '2022-05-03 00:00')
343+
systemtable, bodytable, ringtable = nodequery.ephemeris()
344+
345+
print(' ')
346+
print(' ')
347+
print('~'*40)
348+
print(major_body)
349+
print('~'*40)
350+
print(systemtable)
351+
print(bodytable)
352+
print(ringtable)
353+
'''
354+
206355

0 commit comments

Comments
 (0)