-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplot_sky_location.py
executable file
·178 lines (153 loc) · 6.72 KB
/
plot_sky_location.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#!/usr/bin/env python2.7
# vim: set fileencoding=utf-8> :
"""This code is loosely based on the plot_cycles.py file, but instead of
plotting time along the x-axis, it plots azimuth. This will allow you to see
the path of an object or several objects through the sky. For information about
the time coordinate, annoations can be added. Here is an example for planning
out the viewing of the lunar eclipse of the full moon on Sep 27-28, 2015.
./plot_sky_location.py -t "2015/09/28 00:00" -d 330 -a "2015/09/28 00:11" "Enter Penumbra" -a "2015/09/28 1:07" "Enter Umbra" -a "2015/09/28 2:11" "Total Eclipse Begins" -a "2015/09/28 2:47" "Middle of Eclipse" -a "2015/09/28 3:23" "Total Eclipse Ends" -a "2015/09/28 4:27" "Leave Umbra" -a "2015/09/28 5:22" "Leave Penumbra"
"""
# Note that ephem DOES NOT DO TIMEZONES!!!
# Nor does datetime.datetime.now include a timezone
# It is much safer to use utcnow and convert later
import datetime
import sys
import errno
import ephem
import ephem.stars # auto-import by ephem.star, but not before
import matplotlib.pyplot as plt
import pylab
import matplotlib.dates
import matplotlib.ticker
import matplotlib.gridspec as gs
import config
parser = config.location_parser
parser.description = 'Plot the location of a heaveny body over a particular time period.'
parser.add_argument('-b', '--body', nargs='+', default=['Moon'], help='Heavenly body/bodies to plot (default is Moon)')
parser.add_argument('-d', '--duration', default=120, type=int, help='How many minutes to plot (default is 120)')
# Anything after the second argument will be ignored. Would be nice to specify that nargs can only be one or two.
parser.add_argument('-a', '--annotations', action='append', nargs='+', help='Times to annotate, with optional second label. Multiple arguments add to the list instead of overwriting.')
parser.add_argument('-f', '--file', help='Output file. Will not display to screen')
args = parser.parse_args()
bodies = []
for body in args.body:
if body.title() in dir(ephem):
bodies.append(getattr(ephem, body.title())())
elif body.title() in ephem.stars.stars.keys():
bodies.append(ephem.star(body.title()))
else:
print 'Body %s unknown' % body
sys.exit(errno.EINVAL)
location = config.get_location_from_namespace(args)
n_minutes = args.duration
start_date = location.date
end_date = start_date + ephem.minute*n_minutes
utc_timestamps = []
date = start_date
while date < end_date:
utc_timestamps.append(date)
date = date + ephem.minute
if args.annotations:
annotations = [ephem.Date(a[0]) for a in args.annotations]
# Check to be sure they're actually on the plot?
else:
annotations = None
body_alt = []
body_az = []
for i, body in enumerate(bodies):
body_alt.append([])
body_az.append([])
for t in utc_timestamps:
# Set location's time, compute body's altitude, convert to degrees
location.date = t
for i, body in enumerate(bodies):
body.compute(location)
body_alt[i].append(body.alt*180/ephem.pi)
body_az[i].append(body.az*180/ephem.pi)
if annotations:
annotation_alt = []
annotation_az = []
for i, body in enumerate(bodies):
annotation_alt.append([])
annotation_az.append([])
for a in annotations:
location.date = a
for i, body in enumerate(bodies):
body.compute(location)
annotation_alt[i].append(body.alt*180/ephem.pi)
annotation_az[i].append(body.az*180/ephem.pi)
sun = ephem.Sun()
local_sunrises = []
next_sunrise = location.next_rising(sun, start=start_date)
ephem_end_date = ephem.Date(end_date)
while next_sunrise < ephem_end_date:
local_sunrises.append(config.time_conversion(next_sunrise))
next_sunrise = location.next_rising(sun, start=next_sunrise)
local_sunsets = []
next_sunset = location.next_setting(sun, start=start_date)
while next_sunset < ephem_end_date:
local_sunsets.append(config.time_conversion(next_sunset))
next_sunset = location.next_setting(sun, start=next_sunset)
fig = plt.figure()
alt_colors = []
phase_colors = []
if len(bodies) == 1:
alt_colors.append('green')
phase_colors.append('blue')
alt_axis_color = 'green'
phase_axis_color = 'blue'
else:
colors = ('blue', 'red', 'green', 'orange', 'purple', 'yellow')
alt_axis_color = 'black'
phase_axis_color = 'black'
for i, body in enumerate(bodies):
alt_colors.append(colors[i])
phase_colors.append(colors[i])
compass16 = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW')
compass16_dict = dict([(-i*22.5, value) for i, value in enumerate(compass16)])
def deg2compass16(deg, pos):
if deg in compass16_dict:
return compass16_dict[deg]
else:
return u'%.1f°' % deg
ax1 = fig.add_subplot(111) # rows, columns, n-plot
for i, (az, alt) in enumerate(zip(body_az, body_alt)):
ax1.plot(az, alt, c=alt_colors[i], label=bodies[i].name)
ax1.set_ylabel('Altitude (Degrees Above Horizon)', color=alt_axis_color)
ax1.set_xlabel('Azimuth (East of North)')
ax1.xaxis.set_major_formatter(matplotlib.ticker.FuncFormatter(deg2compass16))
ax1.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter(u'%d°'))
for tl in ax1.get_yticklabels():
tl.set_color(alt_axis_color)
title = '%i Minutes from %s Staring at %s' % (n_minutes, location.name, config.time_conversion(start_date).strftime('%b %d, %Y %H:%M'))
if len(bodies) > 1:
ax1.legend()
ax1.set_title(title)
else:
ax1.set_title('%s for %s' % (bodies[0].name, title))
if annotations:
labels = [config.time_conversion(a).strftime('%Y/%m/%d\n%H:%M') for a in annotations]
for i, body in enumerate(bodies):
for n, (label, x, y) in enumerate(zip(labels, annotation_az[i], annotation_alt[i])):
plt.annotate(label,
xy = (x, y),
xytext = (-20, 20),
arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0'),
bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5),
textcoords = 'offset points',
ha = 'right',
va = 'bottom')
if len(args.annotations[n]) > 1:
plt.annotate(args.annotations[n][1],
xy = (x, y),
xytext = (20, -20),
arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0'),
bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5),
textcoords = 'offset points',
ha = 'left',
va = 'top')
plt.tight_layout()
if args.file:
pylab.savefig(args.file)
else:
plt.show()