Source code for geodepy.angles

#!/usr/bin/env python3

"""
Geoscience Australia - Python Geodesy Package
Angles Module

GeodePy supports Angular Notation in 9 different formats

ABRV - FORMAT (type)
--------------------
rad  - Radians (stored as float)
dec  - Decimal Degrees (stored as float)
deca - Decimal Degrees (via DECAngle class)
hp   - Hewlett Packard (HP) Notation (stored as float)
hpa  - Hewlett Packard (HP) Notation (via HPAngle class)
gon  - Gradians (stored as float)
gona - Gradians (via GONAngle class)
dms  - Degrees, Minutes and Seconds Notation (via DMSAngle class)
ddm  - Degrees and Decimal Minutes Notation (via DDMAngle class)

Conversion between all formats is supported as shown below:

Radians to/from Decimal Degrees via builtin math.radians and math.degrees

Formats as floats to all other types via functions in the form abrv2abrv
e.g. gon2hpa()

DECAngle, HPAngle, GONAngle, DMSAngle and DDMAngle class objects via methods in
the form CLASS.abrv()
e.g. HPAngle(value).dec()

"""

from math import radians


[docs] class DECAngle(float): """ Class for working with angles in Decimal Degrees Note: GeodePy also supports working with angles in Decimal Degrees as floats """ def __init__(self, dec_angle=0.0): """ :param dec_angle: float Decimal Degrees angle """ super().__init__() self.dec_angle = float(dec_angle) def __repr__(self): if self.dec_angle >= 0: return "{DECAngle: +" + str(self.dec_angle) + "}" else: # negative return "{DECAngle: " + str(self.dec_angle) + "}" def __add__(self, other): try: return DECAngle(self.dec() + other.dec()) except AttributeError: raise TypeError("Can only add Angle objects with .dec() method " "together") def __radd__(self, other): try: return DECAngle(other.dec() + self.dec()) except AttributeError: raise TypeError("Can only add Angle objects with .dec() method " "together") def __sub__(self, other): try: return DECAngle(self.dec() - other.dec()) except AttributeError: raise TypeError( "Can only subtract Angle objects with .dec() method" " together" ) def __rsub__(self, other): try: return DECAngle(other.dec() - self.dec()) except AttributeError: raise TypeError( "Can only subtract Angle objects with .dec() method" " together" ) def __mul__(self, other): try: return DECAngle(self.dec() * other) except TypeError: raise TypeError( "Multiply only defined between DECAngle Object " "and Int or Float" ) def __rmul__(self, other): try: return DECAngle(other * self.dec()) except TypeError: raise TypeError( "Multiply only defined between DECAngle Object " "and Int or Float" ) def __truediv__(self, other): try: return DECAngle(self.dec() / other) except TypeError: raise TypeError( "Division only defined between DECAngle Object " "and Int or Float" ) def __abs__(self): return DECAngle(abs(self.dec_angle)) def __neg__(self): return DECAngle(-self.dec()) def __eq__(self, other): return self.dec() == other.dec() def __ne__(self, other): return self.dec() != other.dec() def __lt__(self, other): return self.dec() < other.dec() def __gt__(self, other): return self.dec() > other.dec() def __int__(self): return int(self.dec_angle) def __float__(self): return float(self.dec_angle) def __str__(self): return str(self.dec_angle) def __round__(self, n=None): return DECAngle(round(self.dec_angle, n))
[docs] def rad(self): """ Convert to radians :return: radians :rtype: float """ return radians(self.dec_angle)
[docs] def dec(self): """ Convert to Decimal Degrees (float) :return: Decimal Degrees :rtype: float """ return self.dec_angle
[docs] def hp(self): """ Convert to HP Notation :return: HP Notation (DDD.MMSSSS) :rtype: float """ return dec2hp(self.dec_angle)
[docs] def hpa(self): """ Convert to HP Notation (class) :return: HP Notation (DDD.MMSSSS) :rtype: HPAngle """ return HPAngle(self.hp())
[docs] def gon(self): """ Convert to Gradians (float) :return: Gradians :rtype: float """ return dec2gon(self.dec_angle)
[docs] def gona(self): """ Convert to Gradians (class) :return: Gradians :rtype: GONAngle """ return GONAngle(dec2gon(self.dec_angle))
[docs] def dms(self): """ Convert to Degrees, Minutes, Seconds Object :return: Degrees, Minutes, Seconds Object :rtype: DMSAngle """ return dec2dms(self.dec_angle)
[docs] def ddm(self): """ Convert to Degrees, Decimal Minutes Object :return: Degrees, Decimal Minutes Object :rtype: DDMAngle """ return dec2ddm(self.dec_angle)
[docs] class HPAngle(object): """ Class for working with angles in Hewlett-Packard (HP) format Note: GeodePy also supports working with angles in HP format as floats """ def __init__(self, hp_angle=0.0): """ :param hp_angle: float HP angle """ self.hp_angle = float(hp_angle) hp_dec_str = f"{self.hp_angle:.17f}".split(".")[1] if int(hp_dec_str[0]) > 5: raise ValueError( f"Invalid HP Notation: 1st decimal place greater " f"than 5: {self.hp_angle}" ) if len(hp_dec_str) > 2: if int(hp_dec_str[2]) > 5: raise ValueError( f"Invalid HP Notation: 3rd decimal place greater " f"than 5: {self.hp_angle}" ) def __repr__(self): if self.hp_angle >= 0: return "{HPAngle: +" + str(self.hp_angle) + "}" else: # negative return "{HPAngle: " + str(self.hp_angle) + "}" def __add__(self, other): try: return HPAngle(dec2hp(self.dec() + other.dec())) except AttributeError: raise TypeError("Can only add Angle objects with .dec() method " "together") def __radd__(self, other): try: return HPAngle(dec2hp(other.dec() + self.dec())) except AttributeError: raise TypeError("Can only add Angle objects with .dec() method " "together") def __sub__(self, other): try: return HPAngle(dec2hp(self.dec() - other.dec())) except AttributeError: raise TypeError( "Can only subtract Angle objects with .dec() method" " together" ) def __rsub__(self, other): try: return HPAngle(dec2hp(other.dec() - self.dec())) except AttributeError: raise TypeError( "Can only subtract Angle objects with .dec() method" " together" ) def __mul__(self, other): try: return HPAngle(dec2hp(self.dec() * other)) except TypeError: raise TypeError( "Multiply only defined between Angle objects and " "Int or Float" ) def __rmul__(self, other): try: return HPAngle(dec2hp(other * self.dec())) except TypeError: raise TypeError( "Multiply only defined between Angle objects and " "Int or Float" ) def __truediv__(self, other): try: return HPAngle(dec2hp(self.dec() / other)) except TypeError: raise TypeError( "Division only defined between HPAngle objects " "and Int or Float" ) def __abs__(self): return HPAngle(abs(self.hp_angle)) def __neg__(self): return HPAngle(self.hp_angle.__neg__()) def __eq__(self, other): return self.dec() == other.dec() def __ne__(self, other): return self.dec() != other.dec() def __lt__(self, other): return self.dec() < other.dec() def __gt__(self, other): return self.dec() > other.dec() def __int__(self): return int(self.hp_angle) def __float__(self): return float(self.hp_angle) def __str__(self): return str(self.hp_angle) def __round__(self, n=None): return HPAngle(round(self.hp_angle, n))
[docs] def rad(self): """ Convert to Radians :return: Radians :rtype: float """ return radians(hp2dec(self.hp_angle))
[docs] def dec(self): """ Convert to Decimal Degrees (float) :return: Decimal Degrees :rtype: float """ return hp2dec(self.hp_angle)
[docs] def deca(self): """ Convert to Decimal Degrees (class) :return: Decimal Degrees :rtype: DECAngle """ return DECAngle(self.dec())
[docs] def hp(self): """ Convert to HP Notation (float) :return: HP Notation (DDD.MMSSSS) :rtype: float """ return float(self.hp_angle)
[docs] def gon(self): """ Convert to Gradians (float) :return: Gradians :rtype: float """ return hp2gon(self.hp_angle)
[docs] def gona(self): """ Convert to Gradians (class) :return: Gradians :rtype: GONAngle """ return GONAngle(self.gon())
[docs] def dms(self): """ Convert to Degrees, Minutes, Seconds Object :return: Degrees, Minutes, Seconds Object :rtype: DMSAngle """ return hp2dms(self.hp_angle)
[docs] def ddm(self): """ Convert to Degrees, Decimal Minutes Object :return: Degrees, Decimal Minutes Object :rtype: DDMAngle """ return hp2ddm(self.hp_angle)
[docs] class GONAngle(object): """ Class for working with angles in Gradians (90 degrees == 100 Gradians) Note: GeodePy also supports working with angles in Gradians as floats """ def __init__(self, gon_angle=0.0): """ :param gon_angle: float Gradian angle """ super().__init__() self.gon_angle = float(gon_angle) def __repr__(self): if self.gon_angle >= 0: return "{GONAngle: +" + str(self.gon_angle) + "}" else: # negative return "{GONAngle: " + str(self.gon_angle) + "}" def __add__(self, other): try: return GONAngle(dec2gon(self.dec() + other.dec())) except AttributeError: raise TypeError("Can only add Angle objects with .dec() method " "together") def __radd__(self, other): try: return GONAngle(dec2gon(other.dec() + self.dec())) except AttributeError: raise TypeError("Can only add Angle objects with .dec() method " "together") def __sub__(self, other): try: return GONAngle(dec2gon(self.dec() - other.dec())) except AttributeError: raise TypeError( "Can only subtract Angle objects with .dec() method" " together" ) def __rsub__(self, other): try: return GONAngle(dec2gon(other.dec() - self.dec())) except AttributeError: raise TypeError( "Can only subtract Angle objects with .dec() method" " together" ) def __mul__(self, other): try: return GONAngle(dec2gon(self.dec() * other)) except TypeError: raise TypeError( "Multiply only defined between Angle objects and " "Int or Float" ) def __rmul__(self, other): try: return GONAngle(dec2gon(other * self.dec())) except TypeError: raise TypeError( "Multiply only defined between Angle objects and " "Int or Float" ) def __truediv__(self, other): try: return GONAngle(dec2gon(self.dec() / other)) except TypeError: raise TypeError( "Division only defined between HPAngle objects " "and Int or Float" ) def __abs__(self): return GONAngle(abs(self.gon_angle)) def __neg__(self): return GONAngle(self.gon_angle.__neg__()) def __eq__(self, other): return self.dec() == other.dec() def __ne__(self, other): return self.dec() != other.dec() def __lt__(self, other): return self.dec() < other.dec() def __gt__(self, other): return self.dec() > other.dec() def __int__(self): return int(self.gon_angle) def __float__(self): return float(self.gon_angle) def __str__(self): return str(self.gon_angle) def __round__(self, n=None): return GONAngle(round(self.gon_angle, n))
[docs] def rad(self): """ Convert to Radians :return: Radians :rtype: float """ return radians(gon2dec(self.gon_angle))
[docs] def dec(self): """ Convert to Decimal Degrees (float) :return: Decimal Degrees :rtype: float """ return gon2dec(self.gon_angle)
[docs] def deca(self): """ Convert to Decimal Degrees (class) :return: Decimal Degrees :rtype: DECAngle """ return DECAngle(self.dec())
[docs] def hp(self): """ Convert to HP Notation (float) :return: HP Notation (DDD.MMSSSS) :rtype: float """ return gon2hp(self.gon_angle)
[docs] def hpa(self): """ Convert to HP Notation (class) :return: HP Notation (DDD.MMSSSS) :rtype: HPAngle """ return HPAngle(gon2hp(self.gon_angle))
[docs] def gon(self): """ Convert to Gradians (float) :return: Gradians :rtype: float """ return float(self.gon_angle)
[docs] def dms(self): """ Convert to Degrees, Minutes, Seconds Object :return: Degrees, Minutes, Seconds Object :rtype: DMSAngle """ return gon2dms(self.gon_angle)
[docs] def ddm(self): """ Convert to Degrees, Decimal Minutes Object :return: Degrees, Decimal Minutes Object :rtype: DDMAngle """ return gon2ddm(self.gon_angle)
[docs] class DMSAngle(object): """ Class for working with angles in Degrees, Minutes and Seconds format """ def __init__(self, degree, minute=0, second=0.0, positive=None): """ :param degree: Angle: whole degrees component (floats truncated) Alt: formatted string '±DDD MM SS.SSS' :type degree: float | str :param minute: Angle: whole minutes component (floats truncated) :type minute: float :param second: Angle: seconds component (floats preserved) :type second: float :param positive: Optional. True is positive, False is negative. Evaluated from deg/min/sec where None :type positive: bool """ # evaluate sign if positive is False or str(degree)[0] == "-": self.positive = False else: self.positive = True # check sign provided for minute and second where positive not provided and degree is int == 0 if degree == 0 and positive is None: if minute < 0: self.positive = False elif second < 0: self.positive = False # Convert formatted string 'DDD MM SS.SSS' to DMSAngle if type(degree) == str: str_pts = degree.split() degree = int(str_pts[0]) minute = int(str_pts[1]) second = float(str_pts[2]) self.degree = abs(int(degree)) self.minute = abs(int(minute)) self.second = abs(second) def __repr__(self): if self.positive: signsymbol = "+" else: signsymbol = "-" return ( "{DMSAngle: " + signsymbol + str(self.degree) + "d " + str(self.minute) + "m " + str(self.second) + "s}" ) def __add__(self, other): try: return dec2dms(self.dec() + other.dec()) except AttributeError: raise TypeError("Can only add Angle objects with .dec() method " "together") def __radd__(self, other): try: return dec2dms(other.dec() + self.dec()) except AttributeError: raise TypeError("Can only add Angle objects with .dec() method " "together") def __sub__(self, other): try: return dec2dms(self.dec() - other.dec()) except AttributeError: raise TypeError( "Can only subtract Angle objects with .dec() method" " together" ) def __rsub__(self, other): try: return dec2dms(other.dec() - self.dec()) except AttributeError: raise TypeError( "Can only subtract Angle objects with .dec() method" " together" ) def __mul__(self, other): try: return dec2dms(self.dec() * other) except TypeError: raise TypeError( "Multiply only defined between DMSAngle Object " "and Int or Float" ) def __rmul__(self, other): try: return dec2dms(other * self.dec()) except TypeError: raise TypeError( "Multiply only defined between DMSAngle Object " "and Int or Float" ) def __truediv__(self, other): try: return dec2dms(self.dec() / other) except TypeError: raise TypeError( "Division only defined between DMSAngle Object " "and Int or Float" ) def __abs__(self): return DMSAngle(self.degree, self.minute, self.second) def __neg__(self): if self.positive: return DMSAngle(-self.degree, -self.minute, -self.second) else: # positive == False return DMSAngle(self.degree, self.minute, self.second) def __eq__(self, other): return self.dec() == other.dec() def __ne__(self, other): return self.dec() != other.dec() def __lt__(self, other): return self.dec() < other.dec() def __gt__(self, other): return self.dec() > other.dec() def __str__(self): if self.positive: return str(self.degree) + " " + str(self.minute) + " " + str(self.second) else: return ( "-" + str(self.degree) + " " + str(self.minute) + " " + str(self.second) ) def __round__(self, n=None): if self.positive: return DMSAngle(self.degree, self.minute, round(self.second, n)) else: return -DMSAngle(self.degree, self.minute, round(self.second, n)) def __mod__(self, other): return dec2dms(self.dec() % other)
[docs] def rad(self): """ Convert to Radians :return: Radians :rtype: float """ return radians(self.dec())
[docs] def dec(self): """ Convert to Decimal Degrees (float) :return: Decimal Degrees :rtype: float """ if self.positive: return self.degree + (self.minute / 60) + (self.second / 3600) else: return -(self.degree + (self.minute / 60) + (self.second / 3600))
[docs] def deca(self): """ Convert to Decimal Degrees (class) :return: Decimal Degrees :rtype: DECAngle """ return DECAngle(self.dec())
[docs] def hp(self): """ Convert to HP Notation (float) :return: HP Notation (DDD.MMSSSS) :rtype: float """ if self.positive: return self.degree + (self.minute / 100) + (self.second / 10000) else: return -(self.degree + (self.minute / 100) + (self.second / 10000))
[docs] def hpa(self): """ Convert to HP Notation (class) :return: HP Notation (DDD.MMSSSS) :rtype: HPAngle """ return HPAngle(self.hp())
[docs] def gon(self): """ Convert to Gradians (float) :return: Gradians :rtype: float """ return dec2gon(self.dec())
[docs] def gona(self): """ Convert to Gradians (class) :return: Gradians :rtype: GONAngle """ return GONAngle(self.gon())
[docs] def ddm(self): """ Convert to Degrees, Decimal Minutes Object :return: Degrees, Decimal Minutes Object :rtype: DDMAngle """ if self.positive: return DDMAngle(self.degree, self.minute + (self.second / 60)) else: return -DDMAngle(self.degree, self.minute + (self.second / 60))
[docs] class DDMAngle(object): """ Class for working with angles in Degrees, Decimal Minutes format """ def __init__(self, degree, minute=0.0, positive=None): """ :param degree: Angle: whole degrees component (floats truncated) :type degree: float | str :param minute: Angle: minutes component (floats preserved) :type minute: float :param positive: Optional. True is positive, False is negative. Evaluated from deg/min/sec where None :type positive: bool """ # evaluate sign if positive is False or str(degree)[0] == "-": self.positive = False else: self.positive = True # check sign provided for minute where positive not provided and degree is int == 0 if degree == 0 and positive is None: if minute < 0: self.positive = False # Convert formatted string 'DDD MM.MMMM' to DDMAngle if type(degree) == str: str_pts = degree.split(" ") degree = int(str_pts[0]) minute = float(str_pts[1]) self.degree = abs(int(degree)) self.minute = abs(minute) def __repr__(self): if self.positive: signsymbol = "+" else: signsymbol = "-" return ( "{DDMAngle: " + signsymbol + str(self.degree) + "d " + str(self.minute) + "m}" ) def __add__(self, other): try: return dec2ddm(self.dec() + other.dec()) except AttributeError: raise TypeError("Can only add Angle objects with .dec() method " "together") def __radd__(self, other): try: return dec2ddm(other.dec() + self.dec()) except AttributeError: raise TypeError("Can only add Angle objects with .dec() method " "together") def __sub__(self, other): try: return dec2ddm(self.dec() - other.dec()) except AttributeError: raise TypeError( "Can only subtract Angle objects with .dec() method" " together" ) def __rsub__(self, other): try: return dec2ddm(other.dec() - self.dec()) except AttributeError: raise TypeError( "Can only subtract Angle objects with .dec() method" " together" ) def __mul__(self, other): try: return dec2ddm(self.dec() * other) except TypeError: raise TypeError( "Multiply only defined between DMSAngle Object " "and Int or Float" ) def __rmul__(self, other): try: return dec2ddm(other * self.dec()) except TypeError: raise TypeError( "Multiply only defined between DMSAngle Object " "and Int or Float" ) def __truediv__(self, other): try: return dec2ddm(self.dec() / other) except TypeError: raise TypeError( "Division only defined between DMSAngle Object " "and Int or Float" ) def __abs__(self): return DDMAngle(self.degree, self.minute) def __neg__(self): if self.positive: return DDMAngle(-self.degree, -self.minute) else: # sign == -1 return DDMAngle(self.degree, self.minute) def __eq__(self, other): return self.dec() == other.dec() def __ne__(self, other): return self.dec() != other.dec() def __lt__(self, other): return self.dec() < other.dec() def __gt__(self, other): return self.dec() > other.dec() def __str__(self): if self.positive: return str(self.degree) + " " + str(self.minute) else: return "-" + str(self.degree) + " " + str(self.minute) def __round__(self, n=None): if self.positive: return DDMAngle(self.degree, round(self.minute, n)) else: return DDMAngle(-self.degree, -round(self.minute, n)) def __mod__(self, other): return dec2ddm(self.dec() % other)
[docs] def rad(self): """ Convert to Radians :return: Radians :rtype: float """ return radians(self.dec())
[docs] def dec(self): """ Convert to Decimal Degrees (float) :return: Decimal Degrees :rtype: float """ if self.positive: return self.degree + (self.minute / 60) else: return -(self.degree + (self.minute / 60))
[docs] def deca(self): """ Convert to Decimal Degrees (class) :return: Decimal Degrees :rtype: DECAngle """ return DECAngle(self.dec())
[docs] def hp(self): """ Convert to HP Notation (float) :return: HP Notation (DDD.MMSSSS) :rtype: float """ minute_int, second = divmod(self.minute, 1) if self.positive: return self.degree + (minute_int / 100) + (second * 0.006) else: return -(self.degree + (minute_int / 100) + (second * 0.006))
[docs] def hpa(self): """ Convert to HP Notation (class) :return: HP Notation (DDD.MMSSSS) :rtype: HPAngle """ return HPAngle(self.hp())
[docs] def gon(self): """ Convert to Gradians (float) :return: Gradians :rtype: float """ return dec2gon(self.dec())
[docs] def gona(self): """ Convert to Gradians (class) :return: Gradians :rtype: GONAngle """ return GONAngle(self.gon())
[docs] def dms(self): """ Convert to Degrees, Minutes, Seconds Object :return: Degrees, Minutes, Seconds Object :rtype: DMSAngle """ minute_int, second = divmod(self.minute, 1) if self.positive: return DMSAngle(self.degree, int(minute_int), second * 60) else: return -DMSAngle(self.degree, int(minute_int), second * 60)
# Functions converting from Decimal Degrees (float) to other formats
[docs] def dec2hp(dec): """ Converts Decimal Degrees to HP Notation (float) :param dec: Decimal Degrees :type dec: float :return: HP Notation (DDD.MMSSSS) :rtype: float """ minute, second = divmod(abs(dec) * 3600, 60) degree, minute = divmod(minute, 60) # floating point precision is 13 places for the variable 'dec' where values # are between 256 and 512 degrees. Precision improves for smaller angles. # In calculating the variable 'second' the precision is degraded by a factor of 3600 # Therefore 'second' should be rounded to 9 DP and tested for carry. if round(second, 9) == 60: second = 0 minute += 1 # to avoid precision issues with floating point operations # a string will be built to represent a sexagesimal number and then converted to float degree = f"{int(degree)}" minute = f"{int(minute):02}" second = f"{second:012.9f}".rstrip("0").replace(".", "") hp_string = f"{degree}.{minute}{second}" hp = float(hp_string) return hp if dec >= 0 else -hp
[docs] def dec2hpa(dec): """ Converts Decimal Degrees to HP Angle Object :param dec: Decimal Degrees :type dec: float :return: HP Angle Object (DDD.MMSSSS) :rtype: HPAngle """ return HPAngle(dec2hp(dec))
[docs] def dec2gon(dec): """ Converts Decimal Degrees to Gradians :param dec: Decimal Degrees :type dec: float :return: Gradians :rtype: float """ return 10 / 9 * dec
[docs] def dec2gona(dec): """ Converts Decimal Degrees to Gradians (class) :param dec: Decimal Degrees :type dec: float :return: Gradians :rtype: GONAngle """ return GONAngle(dec2gon(dec))
[docs] def dec2dms(dec): """ Converts Decimal Degrees to Degrees, Minutes, Seconds Object :param dec: Decimal Degrees :type dec: float :return: Degrees, Minutes, Seconds Object :rtype: DMSAngle """ minute, second = divmod(abs(dec) * 3600, 60) degree, minute = divmod(minute, 60) return ( DMSAngle(degree, minute, second, positive=True) if dec >= 0 else DMSAngle(degree, minute, second, positive=False) )
[docs] def dec2ddm(dec): """ Converts Decimal Degrees to Degrees, Decimal Minutes Object :param dec: Decimal Degrees :type dec: float :return: Degrees, Decimal Minutes Object :rtype: DDMAngle """ minute, second = divmod(abs(dec) * 3600, 60) degree, minute = divmod(minute, 60) minute = minute + (second / 60) return ( DDMAngle(degree, minute, positive=True) if dec >= 0 else DDMAngle(degree, minute, positive=False) )
# Functions converting from Hewlett-Packard (HP) format to other formats
[docs] def hp2dec(hp): """ Converts HP Notation to Decimal Degrees :param hp: HP Notation (DDD.MMSSSS) :type hp: float :return: Decimal Degrees :rtype: float """ # Check if 1st and 3rd decimal place greater than 5 (invalid HP Notation) hp = float(hp) hp_deg_str, hp_mmss_str = f"{hp:.13f}".split(".") if int(hp_mmss_str[0]) > 5: raise ValueError( f"Invalid HP Notation: 1st decimal place greater " f"than 5: {hp}" ) if len(hp_mmss_str) > 2: if int(hp_mmss_str[2]) > 5: raise ValueError( f"Invalid HP Notation: 3rd decimal place greater " f"than 5: {hp}" ) # parse string to avoid precision problems with floating point ops and base 10 numbers deg = abs(int(hp_deg_str)) min = int(hp_mmss_str[:2]) sec = float(hp_mmss_str[2:4] + "." + hp_mmss_str[4:]) dec = sec / 3600 + min / 60 + deg return dec if hp >= 0 else -dec
[docs] def hp2deca(hp): """ Converts HP Notation to DECAngle Object :param hp: HP Notation (DDD.MMSSSS) :type hp: float :return: Decimal Degrees Object :rtype: DECAngle """ return DECAngle(hp2dec(hp))
[docs] def hp2rad(hp): """ Converts HP Notation to radians :param hp: HP Notation (DDD.MMSSSS) :type hp: float :return: radians :rtype: float """ return radians(hp2dec(hp))
[docs] def hp2gon(hp): """ Converts HP Notation to Gradians :param hp: HP Notation (DDD.MMSSSS) :type hp: float :return: Gradians :rtype: float """ return dec2gon(hp2dec(hp))
[docs] def hp2gona(hp): """ Converts HP Notation to Gradians (class) :param hp: HP Notation (DDD.MMSSSS) :type hp: float :return: Gradians :rtype: GONAngle """ return GONAngle(hp2gon(hp))
[docs] def hp2dms(hp): """ Converts HP Notation to Degrees, Minutes, Seconds Object :param hp: HP Notation (DDD.MMSSSS) :type hp: float :return: Degrees, Minutes, Seconds Object :rtype: DMSAngle """ degmin, second = divmod(abs(hp) * 1000, 10) degree, minute = divmod(degmin, 100) return ( DMSAngle(degree, minute, second * 10, positive=True) if hp >= 0 else DMSAngle(degree, minute, second * 10, positive=False) )
[docs] def hp2ddm(hp): """ Converts HP Notation to Degrees, Decimal Minutes Object :param hp: HP Notation (DDD.MMSSSS) :type hp: float :return: Degrees, Decimal Minutes Object :rtype: DDMAngle """ degmin, second = divmod(abs(hp) * 1000, 10) degree, minute = divmod(degmin, 100) minute = minute + (second / 6) return ( DDMAngle(degree, minute, positive=True) if hp >= 0 else DDMAngle(degree, minute, positive=False) )
# Functions converting from Gradians format to other formats
[docs] def gon2dec(gon): """ Converts Gradians to Decimal Degrees :param gon: Gradians :type gon: float :return: Decimal Degrees :rtype: float """ return 9 / 10 * gon
[docs] def gon2deca(gon): """ Converts Gradians to DECAngle Object :param gon: Gradians :type gon: float :return: Decimal Degrees Object :rtype: DECAngle """ return DECAngle(gon2dec(gon))
[docs] def gon2hp(gon): """ Converts Gradians to HP Notation (float) :param gon: Gradians :type gon: float :return: HP Notation (DDD.MMSSSS) :rtype: float """ return dec2hp(gon2dec(gon))
[docs] def gon2hpa(gon): """ Converts Gradians to HP Angle Object :param gon: Gradians :type gon: float :return: HP Angle Object (DDD.MMSSSS) :rtype: HPAngle """ return HPAngle(gon2hp(gon))
[docs] def gon2rad(gon): """ Converts Gradians to radians :param gon: Gradians :type gon: float :return: Radians :rtype: float """ return radians(gon2dec(gon))
[docs] def gon2dms(gon): """ Converts Gradians to Degrees, Minutes, Seconds Object :param gon: Gradians :type gon: float :return: Degrees, Minutes, Seconds Object :rtype: DMSAngle """ return dec2dms(gon2dec(gon))
[docs] def gon2ddm(gon): """ Converts Gradians to Degrees, Decimal Minutes Object :param gon: Gradians :type gon: float :return: Degrees, Decimal Minutes Object :rtype: DDMAngle """ return dec2ddm(gon2dec(gon))
# Miscellaneous other useful functions
[docs] def dd2sec(dd): """ Converts angle in decimal degrees to angle in seconds :param dd: Decimal Degrees :return: Seconds """ minute, second = divmod(abs(dd) * 3600, 60) degree, minute = divmod(minute, 60) sec = (degree * 3600) + (minute * 60) + second return sec if dd >= 0 else -sec
[docs] def dec2hp_v(dec): minute, second = divmod(abs(dec) * 3600, 60) degree, minute = divmod(minute, 60) hp = degree + (minute / 100) + (second / 10000) hp[dec <= 0] = -hp[dec <= 0] return hp
[docs] def hp2dec_v(hp): degmin, second = divmod(abs(hp) * 1000, 10) degree, minute = divmod(degmin, 100) dec = degree + (minute / 60) + (second / 360) dec[hp <= 0] = -dec[hp <= 0] return dec
[docs] def angular_typecheck(angle): """ Converts Angle Objects to Decimal Degrees (float) for computations or returns float input as-is. :param angle: Angle Object or float :return: Decimal Degrees :rtype: float """ supported_types = [DMSAngle, DDMAngle, DECAngle, HPAngle, GONAngle] if type(angle) in supported_types: return angle.dec() else: return float(angle)