Source code for momxml.angles

r'''
Uniform handling of angles.
'''

from math import floor, pi

[docs]def signum(number): r''' Returns the sign of ``number``. **Parameters** number : number The number for which to calculate the sign. **Returns** Integer +1 if `number`` >= 0.0, -1 if ``number`` < 0.0 **Examples** >>> signum(3) 1 >>> signum(0.0) 1 >>> signum(0) 1 >>> signum(-1e-30) -1 >>> signum(-0) 1 ''' if number < 0.0: return -1 else: return +1
[docs]def sign_char(number): r''' Returns the sign of ``number``. **Parameters** number : number The number for which to calculate the sign. **Returns** String '+' if number >= 0.0, '-' if number < 0.0 **Examples** >>> sign_char(3) '+' >>> sign_char(0.0) '+' >>> sign_char(0) '+' >>> sign_char(-1e-30) '-' >>> sign_char(-0) '+' ''' return ['-', None, '+'][1+signum(number)]
[docs]def int_from_sign_char(char): r''' Returns -1 if ``char`` == '-', +1 if ``char`` == '+' **Parameters** char: a one-character string Either '+' or '-'. **Returns** The integer -1 or +1, depending on the value of ``char``. **Raises** ValueError If ``char`` is something other than '+' or '-'. **Examples** >>> int_from_sign_char('+') 1 >>> int_from_sign_char('-') -1 >>> int_from_sign_char('f') Traceback (most recent call last): ... ValueError: char must be either '+' or '-', not 'f' ''' if char == '+': return +1 elif char == '-': return -1 else: raise ValueError('char must be either \'+\' or \'-\', not %r' % char)
[docs]class Angle(object): r''' A simple container for angles. It can be created from various units of angles. Specify exactly one of ``shms``, ``sdms``, ``rad``, or ``deg``. **Parameters** rad : None or float An angle in radians. hms : None or tuple An angle in hours, minutes, and seconds, e.g. (13, 59, 12.4). shms : None or tuple A signed angle in hours, minutes, and seconds, e.g. ('+', 13, 59, 12.4). The sign is required. sdms : None or tuple An angle in degrees, minutes, and seconds, e.g. ('-', 359, 59, 12.4). The sign is required. deg : None or float An angle in degrees. **Raises** ValueError In case of problems with the provided arguments. **Examples** Direct values: >>> Angle(deg = 360.0) Angle(rad = 6.283185307179586) >>> Angle(rad = pi) Angle(rad = 3.141592653589793) Hours: >>> Angle(hms = (3, 15, 30.2)) Angle(rad = 0.8530442163226618) >>> Angle(shms = ('-', 3, 15, 30.2)) Angle(rad = -0.8530442163226618) Degrees: >>> Angle(sdms = ('+', 3, 15, 30.2)) Angle(rad = 0.05686961442151079) >>> Angle(sdms = ('-', 3, 15, 30.2)) Angle(rad = -0.05686961442151079) Whoops: >>> Angle(rad = -0.0568696144215, deg = 12) Traceback (most recent call last): ... ValueError: Specify *one* of hms, shms, sdms, rad, or deg. ''' def __init__(self, rad = None, hms = None, shms = None, sdms = None, deg = None): none_count = [hms, shms, sdms, rad, deg].count(None) if none_count != 4: raise ValueError('Specify *one* of hms, shms, sdms, rad, or deg.') self.rad = 0.0 if hms is not None: self.set_shms('+', *hms) if shms is not None: self.set_shms(*shms) if sdms is not None: self.set_sdms(*sdms) if rad is not None: self.set_rad(rad) if deg is not None: self.set_deg(deg) def __repr__(self): return 'Angle(rad = %s)' % str(self.rad)
[docs] def set_shms(self, sign, hours, minutes, seconds): r''' Explicitly set angle with sign, hours, minutes, and seconds. **Parameters** sign : string One of '+' or '-' hours : positive number Number of hours. Does not need to be restricted to the [0..24] range. May also be a floating point number. minutes : positive number Number of minutes. Does not need to be restricted to [0..60]. May also be floating point. seconds : positive number Number of seconds. Does not need to be restricted to [0..60]. May also be floating point. **Returns** A float containing the angle in radians. **Examples** >>> a = Angle(rad = 2.0) >>> rad = a.set_shms('-', 2, 30, 45.2) >>> str(a.as_rad()) '-0.6577855062557962' ''' sgn = int_from_sign_char(sign) self.rad = sgn*pi*(hours + minutes/60.0 + seconds/3600.0)/12.0 return self.rad
[docs] def set_sdms(self, sign, degrees, minutes, seconds): r''' Explicitly set angle with sign, degrees, minutes, and seconds. **Parameters** sign : string One of '+' or '-' degrees : positive number Number of degrees. Does not need to be restricted to the [0..360] range. May also be a floating point number. minutes : positive number Number of minutes. Does not need to be restricted to [0..60]. May also be floating point. seconds : positive number Number of seconds. Does not need to be restricted to [0..60]. May also be floating point. **Returns** A float containing the angle in radians. **Examples** >>> a = Angle(rad = 2.0) >>> rad = a.set_sdms('-', 2, 30, 45.2) >>> str(a.as_rad()) '-0.04385236708371975' ''' sgn = int_from_sign_char(sign) self.rad = sgn*pi*(degrees + minutes/60.0 + seconds/3600.0)/180.0 return self.rad
[docs] def set_deg(self, deg): r''' Set the angle in degrees. **Parameters** degrees : float The angle in degrees. **Returns** A float containing the angle in radians. **Examples** >>> a = Angle(rad = 2.0) >>> str(a.set_deg(180.0)) '3.141592653589793' >>> str(a.set_deg(-90.0)) '-1.5707963267948966' ''' self.rad = deg*pi/180.0 return self.rad
[docs] def set_rad(self, rad): r''' Set the angle in radians. **Parameters** rad : float The angle in radians. **Returns** A float containing the angle in radians. **Examples** >>> a = Angle(rad = 2.0) >>> a.set_rad(3.0) 3.0 >>> a.set_rad(-1.2) -1.2 ''' self.rad = rad return self.rad
[docs] def as_rad(self): r''' Get angle in radians. **Returns** A float containing the angle in radians. **Examples** >>> a = Angle (rad = 3.0) >>> a.as_rad() 3.0 ''' return self.rad
[docs] def as_deg(self): r''' Get angle in degrees. **Returns** A float containing the angle in degrees. **Examples** >>> a = Angle (rad = 3.0) >>> str(a.as_deg()) '171.88733853924697' ''' return self.rad*180.0/pi
[docs] def as_hours(self): r''' Get angle in hours. **Returns** A float containing the angle in hours. **Examples** >>> a = Angle (rad = 3.0) >>> str(a.as_hours()) '11.459155902616464' ''' return self.rad*12.0/pi
[docs] def as_shms(self): r''' Get the angle in hours, minutes, and seconds. It does not round off the hours and minutes based on the seconds. See examples. **Returns** A tuple containing (sign, hours, minutes, seconds) **Examples** >>> fmt = '%s%02dh %02dm %06.3fs' >>> fmt % Angle(shms = ('+', 3, 10, 59.99999)).as_shms() '+03h 10m 60.000s' >>> fmt % Angle(shms = ('+', 3, 11, 0.0)).as_shms() '+03h 10m 60.000s' >>> fmt % Angle(shms = ('-', 3, 11, 0.1)).as_shms() '-03h 11m 00.100s' ''' sgn = sign_char(self.rad) abs_rad = abs(self.rad) in_hours = abs_rad * 12/pi hours = int(floor(in_hours)) in_minutes = (in_hours-hours)*60.0 minutes = int(floor(in_minutes)) in_seconds = (in_minutes - minutes)*60.0 return (sgn, hours, minutes, in_seconds)
[docs] def as_sdms(self): r''' Get the angle in degrees, minutes, and seconds. It does not round off the degrees and minutes based on the seconds. See examples. **Returns** A tuple containing (sign, degrees, minutes, seconds) **Examples** >>> fmt = '%s%02dd %02dm %06.3fs' >>> fmt % Angle(sdms = ('+', 3, 10, 59.99999)).as_sdms() '+03d 10m 60.000s' >>> fmt % Angle(sdms = ('+', 3, 11, 0.0)).as_sdms() '+03d 10m 60.000s' >>> fmt % Angle(sdms = ('-', 3, 11, 0.1)).as_sdms() '-03d 11m 00.100s' ''' sgn = sign_char(self.rad) abs_rad = abs(self.rad) in_degrees = abs_rad * 180/pi degrees = int(floor(in_degrees)) in_minutes = (in_degrees - degrees)*60.0 minutes = int(floor(in_minutes)) in_seconds = (in_minutes - minutes)*60.0 return (sgn, degrees, minutes, in_seconds)
def __float__(self): r''' Implement float(angle). **Returns** A float containing the angle in radians **Examples** >>> from math import sin, cos >>> str(sin(float(Angle(deg = 180.0)))) '1.2246467991473532e-16' >>> cos(float(Angle(deg = 180.0))) -1.0 ''' return self.rad def __add__(self, angle): r''' Implement Angle() + something, where float(something) is in radians. **Returns** An Angle instance. **Examples** >>> (Angle(deg = 90) + Angle(deg = 45)).as_deg() 135.0 >>> Angle(rad = 2.0) + 2.0 Angle(rad = 4.0) ''' return Angle(rad = self.as_rad() + float(angle)) def __sub__(self, angle): r''' Implement Angle() - something, where float(something) is in radians. **Returns** An Angle instance. **Examples** >>> (Angle(deg = 90) - Angle(deg = 45)).as_deg() 45.0 >>> Angle(rad = 2.0) - 0.5 Angle(rad = 1.5) ''' return Angle(rad = self.as_rad() - float(angle)) def __mul__(self, factor): r''' Implement Angle() * factor **Parameters** factor : number The factor to multiply the angle by. **Returns** An Angle instance. **Examples** >>> (Angle(deg = 90)*3).as_deg() 270.0 >>> Angle(rad = 2.0)*3.0 Angle(rad = 6.0) ''' return Angle(rad = self.as_rad() * float(factor)) def __div__(self, divisor): r''' Implement Angle()/divisor **Parameters** divisor : number The factor to divide the angle by. **Returns** An Angle instance. **Examples** >>> (Angle(deg = -90)/2).as_deg() -45.0 >>> Angle(rad = 2.0)/4.0 Angle(rad = 0.5) ''' return Angle(rad = self.as_rad() / float(divisor)) def __truediv__(self, divisor): r''' Implement Angle()/divisor **Parameters** divisor : number The factor to divide the angle by. **Returns** An Angle instance. **Examples** >>> (Angle(deg = -90)/2).as_deg() -45.0 >>> Angle(rad = 2.0)/4.0 Angle(rad = 0.5) ''' return Angle(rad = self.as_rad() / float(divisor))