3

Why was the decision made for the observers lat/lon numeric values to be treated as radians but string values to be treated as decimal degrees?

When dealing with lat / lon values in Python I usually deal with floats objects. If the stars are aligned right, there may be an int object or two floating in there but mostly floats. I always end up naively writing some code like this:

lat = 0.0
lon = 0.0

observer = ephem.Observer()
observer.lat = lat
observer.lon = lon
# etc... etc...

And then, I get burned. I get burned because my lat / lon values are in decimal degrees, not radians. It is usually bad because I end up scratching my head wondering what i've done wrong. In the end, I end up converting to radians. Now theoretically, I could do this to get the right result:

observer = ephem.Observer()
observer.lat = str(lat)
observer.lon = str(lon)

But that seems wonky also, I thought my values had to be in radians! Oh wait, only if it's a float. I understand accepting string or float objects is nice but accepting different numeric types based on the python types seems inconsistent. I would expect both assignments to take the same numeric types, like this:

lat = lon = 0.55
observer.lat = lat  # float lat is assumed to be in radians
observer.lon = lon  # float lon is assumed to be in radians

lat = lon = '0.55'
observer.lat = lat  # string lat is assumed to be in radians
observer.lon = lon  # string lon is assumed to be in radians

Then conversion operators could be provided for decimal degrees / dms:

lat = '25.0'
lon = 25.0
observer.lat = ephem.to_rad(lat)
observer.lon = ephem.to_rad(lon)

Then the to_rad function would explicitly assume that decimal degrees were being provided as either a float or string object. At this point, the interfaces would be consistent. I very briefly dug through _libastro.c and looked into the to_angle function, which lies at the core of this question.

I can not think of a good reason to not implement this. In fact, I will volunteer to do it! But before I go gun-ho on this, I know that i'm not a libastro / pyephem expert, so I wanted to open this question up to make sure this makes sense. It is entirely possible that this was done for very good reason and i'm just hopelessly confused.

If experienced users or someone familiar with the core of the software could comment, I would greatly appreciate it.

Thank You in advance!

1 Answers1

2

First: when you need to make an explicit conversion of degrees to radians in the current version of PyEphem, you can use the degree constant, which is one degree measured in radians. So you can write something like this:

observer.lat = 33.7 * ephem.degree

The idea was that the reader would learn to see this as “33.7 times one degree” — my traditional worry with a function name like to_rad() is that it would not necessarily be clear what units it was converting from. If a function or method were to be introduced, then a name like from_degrees() might actually be preferable — but I am not sure that I myself would find the code any clearer than simply multiplying a number by one degree as illustrated above.

To make one step further back: the reason that radians are the default angle measure is not for any theoretical reason — like the fact that radians are the “natural” way to measure angles in math, or the fact that they are the unit expected by Python's sin() and cos() routine for people who take PyEphem angles and do trigonometry — but is primarily because that is the underlying unit used by the libastro library which PyEphem is a wrapper around. It seemed to be the most obvious move, especially for people who might already have used libastro in their C code, to simply expose the underlying library's units instead of hiding them and always forcing a conversion in each direction.

It might have been slightly obscure, in retrospect, that printing an angle performs the convenience of displaying hours (for RA) or degrees (for declination), but once that decision had been made ­— that a float → str conversion moved to hours or degrees — then symmetry itself dictated that providing a str should be interpreted as hours or degrees as well, so that string outputs and string inputs would be equivalent and in the same units.

The consequence of your argument seems to be that the latter step — the automatic str → float conversion if a string is provided for an attribute like .lat or .lon — is too obscure and should simply be disabled, since providing a string where a number should be traditionally results in a TypeError in Python, not a magical silent conversion. I am actually inclined to agree, but I think it is too late at this point to disable the conversion and force people to multiply by ephem.degree explicitly, because that would break so much existing code. I want PyEphem to keep working for people who have invested time in writing programs against it.

But I think your point is a good one, and in my new skyfield library I am not planning on having any of the kinds of magic conversion that you have run into in PyEphem. Instead, whether radians or degrees are provided, I am planning on making the programming be explicit about what they are providing and never allow them to provide an “unlabelled” number. You can check out the project on GitHub if you would like to weigh in on its syntax as it evolves:

https://github.com/brandon-rhodes/python-skyfield

Brandon Rhodes
  • 83,755
  • 16
  • 106
  • 147