Predicting planetary visibility with PyEphem
Part II: Predicting Conjunctions
After I'd written a basic script to calculate when planets will be visible, the next step was predicting conjunctions, times when two or more planets are close together in the sky.
Finding separation between two objects is easy in PyEphem: it's just one line once you've set up your objects, observer and date.
p1 = ephem.Mars() p2 = ephem.Jupiter() observer = ephem.Observer() # and then set it to your city, etc. observer.date = ephem.date('2014/8/1') p1.compute(observer) p2.compute(observer) ephem.separation(p1, p2)
So all I have to do is loop over all the visible planets and see when the separation is less than some set minimum, like 4 degrees, right?
Well, not really. That tells me if there's a conjunction between a particular pair of planets, like Mars and Jupiter. But the really interesting events are when you have three or more objects close together in the sky. And events like that often span several days. If there's a conjunction of Mars, Venus, and the moon, I don't want to print something awful like
Friday: Conjunction between Mars and Venus, separation 2.7 degrees. Conjunction between the moon and Mars, separation 3.8 degrees. Saturday: Conjunction between Mars and Venus, separation 2.2 degrees. Conjunction between Venus and the moon, separation 3.9 degrees. Conjunction between the moon and Mars, separation 3.2 degrees. Sunday: Conjunction between Venus and the moon, separation 4.0 degrees. Conjunction between the moon and Mars, separation 2.5 degrees.
... and so on, for each day. I'd prefer something like:
Conjunction between Mars, Venus and the moon lasts from Friday through Sunday. Mars and Venus are closest on Saturday (2.2 degrees). The moon and Mars are closest on Sunday (2.5 degrees).
At first I tried just keeping a list of planets involved in the conjunction. So if I see Mars and Jupiter close together, I'd make a list [mars, jupiter], and then if I see Venus and Mars on the same date, I search through all the current conjunction lists and see if either Venus or Mars is already in a list, and if so, add the other one. But that got out of hand quickly. What if my conjunction list looks like [ [mars, venus], [jupiter, saturn] ] and then I see there's also a conjunction between Mars and Jupiter? Oops -- how do you merge those two lists together?
The solution to taking all these pairs and turning them into a list of groups that are all connected actually lies in graph theory: each conjunction pair, like [mars, venus], is an edge, and the trick is to find all the connected edges. But turning my list of conjunction pairs into a graph so I could use a pre-made graph theory algorithm looked like it was going to be more code -- and a lot harder to read and less maintainable -- than making a bunch of custom Python classes.
I eventually ended up with three classes: ConjunctionPair, for a single conjunction observed between two bodies on a single date; Conjunction, a collection of ConjunctionPairs covering as many bodies and dates as needed; and ConjunctionList, the list of all Conjunctions currently active. That let me write methods to handle merging multiple conjunction events together if they turned out to be connected, as well as a method to summarize the event in a nice, readable way.
So predicting conjunctions ended up being a lot more code than I expected -- but only because of the problem of presenting it neatly to the user. As always, user interface represents the hardest part of coding.
The working script is on github at conjunctions.py.
[ 19:57 Jul 31, 2014 More science/astro | permalink to this entry | ]