Python classes and functions for representing angles
The Python module angles provides classes and functions for representing and manipulating angles.
Contents
Overview
The angles module provides a host of functions, and three classes for representing angles. It also provides a class for representing points on a sphere. A utility class for representing Cartesian vectors is also defined in this module.
All classes can create string representations of themselves. The format can be customized using attributes of the objects.
The class for representing points on a sphere can calculate bearing / position angle of another point. It can also calculate the great circle separation between two points.
Almost all the methods of the various classes call functions defined in the module. Functions in the module include those for converting between different units, normalizing angles into different ranges, parsing sexagesimal strings, creating sexagesimal strings and others. The sep() and bear() functions return the great circle distance and position angle between two points on a sphere, respectively.
Docstring of functions and classes have detailed documentation and examples. Some of these examples are also listed in the examples section below.
Installation
The module can be installed using easy_install or pip.
$ pip install angles
or,
$ easy_install angles
The module can be downloaded from the pypi page for the module.
The source code repository is at http://github.com/phn/angles.
The module is available under the BSD license (http://www.opensource.org/licenses/bsd-license.php).
Functions
Convert angles between various units
Radians to other units:
- r2d
- r2h
- r2arcs
Degrees to other units:
- d2h
- d2r
- d2arcs
Hours to other units:
- h2d
- h2r
- h2arcs
Arc-seconds to other units:
- arcs2d
- arcs2h
- arcs2r
Normalize angles into a given range
- r2r
- d2d
- h2h
- normalize
Convert between decimal and sexagesimal representations
- deci2sexa
- sexa2deci
Create string representation of angles
- fmt_angle
Parse sexagesimal string
- phmsdms
- pposition
Bearing and separation
- bear
- sep
Classes
- Angle
- AlphaAngle
- DeltaAngle
- AngularPosition
- CartesianVector
Examples
The following are some examples of using the facilities of the module. See docstrings of functions and classes for detailed documentation and examples.
Normalizing angles
Two types of normalizations are possible. The first method is used for normalizing longitudinal angles and the second method is used for normalizing latitudinal angles. In the former, an angle of 361 degrees become 1 degree, and -1 degrees become 359 degrees. In the latter, an angle of 91 becomes 89, and -91 becomes -89 degrees. The first method is used if b=False in normalize(). The second method is used if b=True.
>>> normalize(-270,-180,180)
90.0
>>> import math
>>> math.degrees(normalize(-2*math.pi,-math.pi,math.pi))
0.0
>>> normalize(-180, -180, 180)
-180.0
>>> normalize(180, -180, 180)
-180.0
>>> normalize(180, -180, 180, b=True)
180.0
>>> normalize(181,-180,180)
-179.0
>>> normalize(181, -180, 180, b=True)
179.0
>>> normalize(-180,0,360)
180.0
>>> normalize(36,0,24)
12.0
>>> normalize(368.5,-180,180)
8.5
>>> normalize(-100, -90, 90)
80.0
>>> normalize(-100, -90, 90, b=True)
-80.0
>>> normalize(100, -90, 90, b=True)
80.0
>>> normalize(181, -90, 90, b=True)
-1.0
>>> normalize(270, -90, 90, b=True)
-90.0
>>> normalize(271, -90, 90, b=True)
-89.0
Also see d2d, r2r and h2h.
Decimal and sexagesimal
>>> deci2sexa(-11.2345678)
(-1, 11, 14, 4.444)
>>> deci2sexa(-11.2345678, pre=5)
(-1, 11, 14, 4.44408)
>>> deci2sexa(-11.2345678, pre=4)
(-1, 11, 14, 4.4441)
>>> deci2sexa(-11.2345678, pre=4, trunc=True)
(-1, 11, 14, 4.444)
>>> deci2sexa(-11.2345678, pre=1)
(-1, 11, 14, 4.4)
>>> deci2sexa(-11.2345678, pre=0)
(-1, 11, 14, 4.0)
>>> deci2sexa(-11.2345678, pre=-1)
(-1, 11, 14, 0.0)
In the following code fragment x is 24 or 0 hours, to within 3 decimal places.
>>> x = 23+59/60.0+59.99999/3600.0
>>> deci2sexa(x, pre=3, lower=0, upper=24, upper_trim=True)
(1, 0, 0, 0.0)
>>> deci2sexa(x, pre=3, lower=0, upper=24, upper_trim=False)
(1, 24, 0, 0.0)
To 5 decimal places, we get back the full value of x.
>>> deci2sexa(x, pre=5, lower=0, upper=24, upper_trim=True)
(1, 23, 59, 59.99999)
String representation of angles
>>> fmt_angle(12.348978659, pre=4, trunc=True)
'+12 20 56.3231 '
>>> fmt_angle(12.348978659, pre=5)
'+12 20 56.32317 '
>>> fmt_angle(12.348978659, s1='HH ', s2='MM ', s3='SS', pre=5)
'+12HH 20MM 56.32317SS'
>>> x = 23+59/60.0+59.99999/3600.0
>>> fmt_angle(x)
'+24 00 00.000 '
>>> fmt_angle(x, lower=0, upper=24, upper_trim=True)
'+00 00 00.000 '
>>> fmt_angle(x, pre=5)
'+23 59 59.99999 '
>>> fmt_angle(-x, lower=0, upper=24, upper_trim=True)
'+00 00 00.000 '
>>> fmt_angle(-x)
'-24 00 00.000 '
Parsing sexagesimal strings
>>> phmsdms("12")
{'parts': [12.0, None, None],
'sign': 1,
'units': 'degrees',
'vals': [12.0, 0.0, 0.0]}
>>> phmsdms("12h")
{'parts': [12.0, None, None],
'sign': 1,
'units': 'hours',
'vals': [12.0, 0.0, 0.0]}
>>> phmsdms("12d13m14.56")
{'parts': [12.0, 13.0, 14.56],
'sign': 1,
'units': 'degrees',
'vals': [12.0, 13.0, 14.56]}
>>> phmsdms("12d13m14.56")
{'parts': [12.0, 13.0, 14.56],
'sign': 1,
'units': 'degrees',
'vals': [12.0, 13.0, 14.56]}
>>> phmsdms("12d14.56ss")
{'parts': [12.0, None, 14.56],
'sign': 1,
'units': 'degrees',
'vals': [12.0, 0.0, 14.56]}
>>> phmsdms("14.56ss")
{'parts': [None, None, 14.56],
'sign': 1,
'units': 'degrees',
'vals': [0.0, 0.0, 14.56]}
>>> phmsdms("12h13m12.4s")
{'parts': [12.0, 13.0, 12.4],
'sign': 1,
'units': 'hours',
'vals': [12.0, 13.0, 12.4]}
>>> phmsdms("12:13:12.4s")
{'parts': [12.0, 13.0, 12.4],
'sign': 1,
'units': 'degrees',
'vals': [12.0, 13.0, 12.4]}
But phmsdms("12:13mm:12.4s") will not work.
Use pposition to parse coordinates of a point given as a string:
>>> ra, de = pposition("12 22 54.899 +15 49 20.57")
>>> ra
12.381916388888889
>>> de
15.822380555555556
>>> pposition("12 22")
(12.0, 22.0)
Separation and bearing of points on unit sphere
Separation i.e., great circle distance can be calculated using sep().
>>> r2d(sep(0, 0, 0, d2r(90.0)))
90.0
>>> r2d(sep(0, d2r(45.0), 0, d2r(90.0)))
45.00000000000001
>>> r2d(sep(0, d2r(-45.0), 0, d2r(90.0)))
135.0
>>> r2d(sep(0, d2r(-90.0), 0, d2r(90.0)))
180.0
>>> r2d(sep(d2r(45.0), d2r(-90.0), d2r(45.0), d2r(90.0)))
180.0
>>> r2d(sep(0, 0, d2r(90.0), 0))
90.0
>>> r2d(sep(0, d2r(45.0), d2r(90.0), d2r(45.0)))
60.00000000000001
>>> import math
>>> 90.0 * math.cos(d2r(45.0)) # Distance along latitude circle.
63.63961030678928
Bearing or position angle can be calculated using bear().
>>> angles.bear(0, 0, 0, -angles.d2r(90.0))
3.141592653589793
>>> angles.bear(0, -angles.d2r(90.0), 0, 0)
0.0
>>> angles.bear(0, -angles.d2r(45.0), 0, 0)
0.0
>>> angles.bear(0, -angles.d2r(89.678), 0, 0)
0.0
>>> r2d(bear(angles.d2r(45.0), angles.d2r(45.0),
angles.d2r(60.0), angles.d2r(45.0)))
84.68152816060062
>>> r2d(bear(angles.d2r(45.0), angles.d2r(45.0),
angles.d2r(46.0), angles.d2r(45.0)))
89.64644212193384
>>> r2d(bear(angles.d2r(45.0), angles.d2r(45.0),
angles.d2r(44.0), angles.d2r(45.0)))
-89.64644212193421
Both of these functions are also available as methods of AngularPosition objects.
Angle
>>> a = Angle(sg="12h34m16.592849219")
>>> print a.r, a.d, a.h, a.arcs
3.29115230606 188.569136872 12.5712757914 678848.892738
>>> print a.ounit
hours
>>> print a
+12 34 16.593
>>> print a.pre, a.trunc
3 False
>>> a.pre = 4
>>> print a
+12 34 16.5928
>>> a.pre = 3
>>> a.trunc = True
>>> print a
+12 34 16.592
>>> a.ounit = "degrees"
>>> print a
+188 34 08.8927
>>> a.ounit = "radians"
>>> print a
3.29115230606
>>> a.ounit = "degrees"
>>> a.s1 = "DD "
>>> a.s2 = "MM "
>>> a.s3 = "SS"
>>> print a
+188DD 34MM 08.892SS
Unicode characters can be used. But this will cause problems when converting to string in Python 2.x, i.e., print a will raise UnicodeEncodeError.
>>> a.s1 = u"\u00B0 "
>>> print unicode(a)
+12° 34MM 16.593SS
The default unit is inferred from the input values.
>>> a = Angle("35d24m34.5")
>>> print a
+35 24 34.500
>>> print a
+35 24 34.500
>>> a = Angle("35:24:34.5")
>>> print a
+35 24 34.500
>>> a = Angle("35h24m34.5")
>>> print a
+35 24 34.500
>>> a.ounit
'hours'
Angle objects can be added to and subtracted from each other.
>>> a = Angle(h=12.5)
>>> b = Angle(h=13.0)
>>> c = a - b
>>> c.h
-0.5000000000000011
>>> c = a + b
>>> c.h
25.5
AlphaAngle
>>> a = AlphaAngle(d=180.5)
>>> print a
+12HH 02MM 00.000SS
>>> a = AlphaAngle(h=12.0)
>>> print a
+12HH 00MM 00.000SS
>>> a = AlphaAngle(h=-12.0)
The attribute ounit is read-only.
>>> a.ounit
"hours"
>>> print a
+12HH 00MM 00.000SS
If no keyword is provided then the input is taken to a sexagesimal string and the units will be determined from it. The numerical value of the angle in radians, hours, degrees and arc-seconds can be extracted from appropriate attributes.
>>> a = angles.AlphaAngle("12h14m23.4s")
>>> print a
+12HH 14MM 23.400SS
>>> print a.r, a.d, a.h, a.arcs
3.20438087343 183.5975 12.2398333333 660951.0
The hms attribute contains the sexagesimal represenation. These are also accessible as a.sign, a.hh`, a.mm and a.ss. The pre and trunc attributes are taken into account while generating the hms attribute.
>>> a.hms
(1, 12, 0, 0.0)
>>> a = AlphaAngle(h=12.54678345)
>>> a.hms
(1, 12, 32, 48.42)
>>> a.sign, a.hh, a.mm, a.ss
(1, 12, 32, 48.42)
>>> print a
+12HH 32MM 48.420SS
>>> a.pre = 5
>>> a.hms
(1, 12, 32, 48.42042)
>>> print a
+12HH 32MM 48.42042SS
Separators can be changed.
>>> a.s1 = " : "
>>> a.s2 = " : "
>>> a.s3 = ""
>>> print a
+12 : 32 : 48.420
Angles are properly normalized.
>>> a = AlphaAngle(h=25.0)
>>> print a
+01HH 00MM 00.000SS
>>> a = AlphaAngle(h=-1.0)
>>> print a
+23HH 00MM 00.000SS
The sexagesimal parts are properly converted into their respective ranges.
>>> a.hh = 23
>>> a.mm = 59
>>> a.ss = 59.99999
>>> a.hms
(1, 0, 0, 0.0)
>>> print a
+00HH 00MM 00.000SS
>>> a.pre = 5
>>> a.hms
(1, 23, 59, 59.99999)
>>> print a
+23HH 59MM 59.99999SS
Angles can be added to and subtracted from each other.
>>> a = AlphaAngle(h=12.0)
>>> b = AlphaAngle(h=13.0)
>>> c = a - b
>>> c.h
-1.0000000000000007
>>> c = a + b
>>> c.h
25.0
DeltaAngle
>>> a = DeltaAngle(d=-45.0)
>>> print a
-45DD 00MM 00.000SS
>>> a = DeltaAngle(d=180.0)
>>> print a
+00DD 00MM 00.000SS
>>> a = DeltaAngle(h=12.0)
>>> print a
+00DD 00MM 00.000SS
>>> a = DeltaAngle(sg="91d")
>>> print a
+89DD 00MM 00.000SS
Attribute ounit is always "degrees".
>>> a.ounit
'degrees'
If no keyword is provided then the input is taken to a sexagesimal string and the units will be determined from it. The numerical value of the angle in radians, hours, degrees and arc-seconds can be extracted from appropriate attributes.
>>> a = DeltaAngle("12d23m14.2s")
>>> print a
+12DD 23MM 14.200SS
>>> print a.r, a.d, a.h, a.arcs
0.216198782581 12.3872777778 0.825818518519 44594.2
The dms attribute contains the sexagesimal represenation. These are also accessible as a.sign, a.dd`, a.mm and a.ss. The pre and trunc attributes are taken into account.
>>> a = DeltaAngle(d=12.1987546)
>>> a.dms
(1, 12, 11, 55.517)
>>> a.pre = 5
>>> a.dms
(1, 12, 11, 55.51656)
>>> a.dd, a.mm, a.ss
(12, 11, 55.51656)
>>> a.pre = 0
>>> a.dms
(1, 12, 11, 56.0)
The separators can be changed.
>>> a = DeltaAngle(d=12.3459876)
>>> a.s1 = " : "
>>> a.s2 = " : "
>>> a.s3 = ""
>>> print a
+12 : 20 : 45.555
Angles are properly normalized.
>>> a = DeltaAngle(d=-91.0)
>>> print a
-89DD 00MM 00.000SS
>>> a = DeltaAngle(d=91.0)
>>> print a
+89DD 00MM 00.000SS
The sexagesimal parts are properly normalized into their respective ranges.
>>> a.dd = 89
>>> a.mm = 59
>>> a.ss = 59.9999
>>> print a
+90DD 00MM 00.000SS
>>> a.pre = 5
>>> print a
+89DD 59MM 59.99990SS
>>> a.dd = 89
>>> a.mm = 60
>>> a.ss = 60
>>> print a
+89DD 59MM 00.000SS
Angles can be added to and subtracted from each other.
>>> a = DeltaAngle(d=12.0)
>>> b = DeltaAngle(d=13.0)
>>> c = a - b
>>> c.d
-0.9999999999999998
>>> c = a + b
>>> c.d
25.000000000000004
>>> print c
+25DD 00MM 00.000SS
>>> c = a - b
>>> print c
-01DD 00MM 00.000SS
AngularPosition
This class represent a point on a sphere.
>>> a = angles.AngularPosition(hd="12 22 54.899 +15 49 20.57")
>>> print a
+12HH 22MM 54.899SS +15DD 49MM 20.570SS
>>> a = angles.AngularPosition(hd="12dd 22 54.899 +15 49 20.57")
>>> print a
+00HH 49MM 31.660SS +15DD 49MM 20.570SS
>>> a = angles.AngularPosition(hd="12d 22 54.899 +15 49 20.57")
>>> print a
+00HH 49MM 31.660SS +15DD 49MM 20.570SS
Note that in the above examples, the "alpha" angle is assumed to be in hours if unit cannot be inferred from the string.
>>> pos1 = AngularPosition(alpha=12.0, delta=90.0)
>>> pos2 = AngularPosition(alpha=12.0, delta=0.0)
>>> angles.r2d(pos2.bear(pos1))
0.0
>>> angles.r2d(pos1.bear(pos2))
0.0
>>> angles.r2d(pos1.sep(pos2))
90.0
>>> pos1.alpha.h = 0.0
>>> pos2.alpha.h = 0.0
>>> angles.r2d(pos1.sep(pos2))
90.0
>>> angles.r2d(pos2.bear(pos1))
0.0
>>> angles.r2d(pos1.bear(pos2))
0.0
>>> pos2.delta.d = -90
>>> angles.r2d(pos1.bear(pos2))
0.0
>>> angles.r2d(pos1.sep(pos2))
180.0
>>> print pos1
+00HH 00MM 00.000SS +90DD 00MM 00.000SS
>>> print pos2
+00HH 00MM 00.000SS +00DD 00MM 00.000SS
>>> pos1.dlim = " | "
>>> print pos1
+00HH 00MM 00.000SS | +90DD 00MM 00.000SS

A quick similar package is the astropysics.coords module (http://packages.python.org/Astropysics/coremods/coords.html), which includes very similar classes that are also integrated with coordinate conversions from/to various astronomical coordinate systems.