import math import doctest # ======================================================== # ======================================================== # === ANGLE # ======================================================== # ======================================================== class Angle: """ Class to convert angles for astronomy Angle formats are: ----------------- deg = degrees. e.g. rad = radian. e.g. Usage: ------ First, instanciate an object from the class: angle = Angle() Second, assign a date in any angle format: angle.angle("-0d3m28.56s") Third, get the converted angle: rad = angle.rad() deg = angle.deg() arcmin = angle.arcmin() arcsec = angle.arcsec() dms = angle.dms() uspzad = angle.sexagesimal(sexagesimal_format) Informations: ------------- help(Angle) Angle().infos("doctest") Angle().infos("doc_methods") """ # ======================================================== # === attributs # ======================================================== # --- multiplicators for units _r2d = 180.0/math.pi _d2r = 1./_r2d _r2h = _r2d/15.0 _h2r = 1./_r2h _r2m = _r2d*60.0 _m2r = 1./_r2m _r2s = _r2d*3600.0 _s2r = 1./_r2s _r2mh = _r2m/15.0 _mh2r = 1./_r2mh _r2sh = _r2s/15.0 _sh2r = 1./_r2sh # --- correspondances between units and multiplicator ->r _u2r = {} _u2r["R"] = 1 _u2r["D"] = _d2r _u2r["M"] = _m2r _u2r["S"] = _s2r _u2r["H"] = _h2r _u2r["MH"] = _mh2r _u2r["SH"] = _sh2r # ======================================================== # === internal methods # ======================================================== def _init(self,angle=""): """ Initialize internal attributes. Inputs: ------- angle is an angle in any supported format (cf. help(Angle)) Usage: ------ objangle = Angle() objangle._init() """ self._init_angle = angle self._init_angleformat = [] self._computed_rad = 0 self._angle_redefined() self._computed_sexagesimal_format = "D.3" self._rad = 0 def _angle_redefined(self): """ Initialize internal attributes when a radian angle is just defined or redefined. """ self._computed_deg = 0 self._computed_arcmin = 0 self._computed_arcsec = 0 self._computed_sexagesimal = 0 self._deg = 0 self._arcmin = 0 self._arcsec = 0 self._sexagesimal = 0 # Attention, python3.5+ only: self._cos_rad_modulo = math.nan self._sin_rad_modulo = math.nan def _is_number(self,s): """ Return True if the string is a number else return False. Usage: ------ >>> objangle = Angle() >>> objangle._is_number("3e5") True >>> objangle._is_number("3a5") False """ try: float(s) return True except ValueError: pass try: import unicodedata unicodedata.numeric(s) return True except (TypeError, ValueError): pass return False def _angle_compare(self, angle, operator): """ Comparaison of angles for various operators. :param angle : An angle in any supported format (cf. help(Angle)) :type angle : Angle() :param operator : Operator such as == != > >= < <= :type operator : string :return: The logic result of the comparison. :rtype: bool :Example: >>> objangle1 = Angle() >>> objangle2 = Angle() >>> objangle1.angle(12.345) ; objangle2.angle("56d28m") ; objangle1._angle_compare(objangle2,">") 12.345 '56d28m' False .. note:: Does not account for the modulo. """ if isinstance(angle, Angle) == False: angle = Angle(angle) if self._computed_rad == 0: self.rad() if angle._computed_rad == 0: angle.rad() res = False if (self._computed_rad == 1) and (angle._computed_rad == 1): toeval = str(self._rad)+" "+operator+" "+str(angle._rad) res = eval(toeval) return res # ======================================================== # === angle methods # ======================================================== def angle_angle2rad(self,angle): """ Compute radian from any angle format Inputs: ------- angle is an angle in any supported format (cf. help(Angle)) Usage: ------ >>> objangle = Angle() >>> objangle.angle_angle2rad("-57d45m34s") ([('-57', 'D'), ('45', 'M'), ('34', 'S')], -1.0080924796783026) First integer is used to check the recognized input date format: 0 = Error, format not known Related topics: --------------- Prefer using objangle.angle() followed by objangle.rad() """ str_angle = str(angle).upper() #print("str_angle = "+str_angle) cars = "+-.0123456789" # --- eliminate leading characters before the first numeric cheracter k = 0 for car in str_angle: if (car in cars): break k += 1 if (k==0): # problem, no valid numeric value found. pass str_angle = str_angle[k:] # --- # "-0 45 ' 34 sec" # angle_numb = [ "-0", "45", "34" ] # angle_numb = [ " ", " ' ", " sec" ] ind_numb = -1 ind_unit = -1 angle_numbs = [] angle_units = [] knumb = 0 kunit = 0 for car in str_angle: if (car in cars) or (car=='E' and knumb>0): # --- numeric case for value if (knumb==0): ind_numb += 1 angle_numbs.append('') angle_numbs[ind_numb] += car knumb += 1 kunit = 0 else: # --- alpha case for units if (kunit==0): ind_unit += 1 angle_units.append('') if ( (car!=" ") and (car!=":") ): if (car=="'"): car = "M" if (car=='"'): car = "S" angle_units[ind_unit] += car kunit += 1 knumb = 0 # --- reformat units #print("angle_numbs={}".format(angle_numbs)) #print("angle_units={}".format(angle_units)) n_numb = len(angle_numbs) n_unit = len(angle_units) tmps = []; for angle_unit in angle_units: if angle_unit is '': angle_unit = ' ' tmps.append(angle_unit) angle_units = tmps.copy() # --- simplify units angle_fnumbs = [] angle_funits = [] if (n_unit==0) and (n_numb==0): angle_fnumbs.append("0") angle_funits.append("R") elif (n_unit==0): angle_fnumbs.append(angle_numbs[0]) angle_funits.append("D") elif (n_unit==1): angle_fnumbs.append(angle_numbs[0]) u1 = angle_units[0] if (u1[0]=="R"): angle_funits.append("R") elif (u1[0]=="M"): angle_funits.append("M") if (n_numb==2): angle_funits.append("S") angle_fnumbs.append(angle_numbs[1]) elif (u1[0]=="S"): angle_funits.append("S") elif (u1[0]=="H"): angle_funits.append("H") if (n_numb==2): angle_funits.append("MH") angle_fnumbs.append(angle_numbs[1]) else: angle_funits.append("D") if (n_numb==2): angle_funits.append("M") angle_fnumbs.append(angle_numbs[1]) elif (n_numb==2): # cases DM or MS HM angle_fnumbs.append(angle_numbs[0]) angle_fnumbs.append(angle_numbs[1]) u1 = angle_units[0] u2 = angle_units[1] if (u1[0]=="M"): angle_funits.append("M") angle_funits.append("S") elif (u1[0]=="H" and u2[0]=="S"): angle_funits.append("H") angle_funits.append("SH") elif (u1[0]=="H"): angle_funits.append("H") angle_funits.append("MH") else: angle_funits.append("D") angle_funits.append("M") elif (n_numb==3): # cases DMS or HMS angle_fnumbs.append(angle_numbs[0]) angle_fnumbs.append(angle_numbs[1]) angle_fnumbs.append(angle_numbs[2]) u1 = angle_units[0] if (u1[0]=="H"): angle_funits.append("H") angle_funits.append("MH") angle_funits.append("SH") else: angle_funits.append("D") angle_funits.append("M") angle_funits.append("S") # --- compute the angle if (angle_fnumbs=="."): angle_fnumbs="0." #print(angle_fnumbs) #print(angle_funits) init_angleformat = list ( zip (angle_fnumbs, angle_funits) ) sign = 1 k=0 rad = 0 for init_angle in init_angleformat: numb = init_angle[0] unit = init_angle[1] if (k==0) and ('-' in numb): sign = -1 fnumb = math.fabs(float(numb)) mult = self._u2r[unit] rad += fnumb*mult rad *= sign self._rad = rad self._rad_modulo_2pi = math.fmod(rad,2*math.pi) if (self._rad_modulo_2pi<0): self._rad_modulo_2pi += (2*math.pi) return init_angleformat, rad def angle_rad2deg(self,rad): """ Compute a angle in degrees from a angle in radian Inputs: ------- rad is an angle in radian. Usage: ------ >>> objangle = Angle() >>> objangle.angle_rad2deg(1) (0, 57.29577951308232) First integer is an error code. 0 = no problem. Related topics: --------------- Prefer using objangle.angle() followed by objangle.deg() """ error = 0 res = rad * self._r2d return error, res def angle_rad2sexagesimal(self,rad, sexagesimal_format): """ Compute a sexagesimal format string from radian Inputs: ------- rad is an angle in radian sexagesimal_format is u (unit) = h,H,d,D (default=D). Capital mean module [0:360[, lower case means module [-180:180[ s (separator) = " ",:,"" (default="" means letters hms or dms) p (plus/minus) = +,"" (default="") z (zeros) = 0,"" (default="") a (angle_limits) = "",90, (+/-limit if unit D,H, default="" means 360) d (sec_digits) = "",".1",".2",... (default="") --- style 1: E.g. To Display a R.A.: "H0.2" => 23h07m42.49s E.g. To Display a Decl.: "d+090.1" => +00d34m22.6s E.g. To Display a H.A.: "h0.2" => -08h43m16.05s --- style 2: E.g. To Display a R.A.: "H 0.2" => 23 07 42.49 E.g. To Display a Decl.: "d +090.1" => -00 34 22.6 E.g. To Display a H.A.: "h 0.2" => -08 43 16.05 --- style 3: E.g. To Display a R.A.: "H:0.2" => 23:07:42.49 E.g. To Display a Decl.: "d:+090.1" => -00:34:22.6 E.g. To Display a H.A.: "h:0.2" => -08:43:16.05 Usage: ------ >>> objangle = Angle() >>> objangle.angle_rad2sexagesimal(-0.01,"d:-090.1") (0, '-00:34:22.6') First integer is used to check the recognized input date format: 0 = Error, format not known Related topics: --------------- Prefer using objangle.angle() followed by objangle.sexagesimal() """ symbols = str(sexagesimal_format) digits = "" unit=0; # 0=deg 1=hours separator=0; # 0=hdms 1=space 2=: modulo=0; # 0=360 1=180 sign=0; # 0=[0:modulo] 1=[-modulo/2:modulo/2] zeros=0; # 0=spaces 1=leading zeros k=0 for car in symbols: if (car=='D'): unit=0 modulo=0 if (car=='d'): unit=0 modulo=1 if (car=='H'): unit=1 modulo=0 if (car=='h'): unit=1 modulo=1 if (car==' '): separator=1 if (car=='_'): separator=1 if (car==':'): separator=2 if (car=='+'): sign=1 if (self._is_number(car)) or car=='.': digits = symbols[k:] break k += 1 # int unit=0; // 0=deg 1=hours # int modulo=0; // 0=360 1=180 # int separator=0; // 0=hdms 1=space 2=: # int sign=0; // 0=[0:modulo] 1=[-modulo/2:modulo/2] # === trailing format (digits) angle_limit = 360 nb_decimalsec = 2 ld = len(digits) if (ld>0): # e.g. digits = "090.3" car = digits[0] if car=='0': zeros=1 digits = digits[1:] # now digits = "90.3" and zeros=1 kd = digits.find(".") if kd==-1: if (self._is_number(digits)==True): angle_limit = int(digits) else: if (self._is_number(digits[0:kd])==True): angle_limit = int(digits[0:kd]) if (self._is_number(digits[kd+1:])==True): nb_decimalsec = int(digits[kd+1:]) # now digits_angle_limit = "90", digits_nb_decimal="3" #print("ld="+str(ld)) #print("symbols="+symbols) #print("digits="+digits) #print("unit="+str(unit)) #print("modulo="+str(modulo)) #print("separator="+str(separator)) #print("sign="+str(sign)) #print("zeros="+str(zeros)) #print("angle_limit="+str(angle_limit)) #print("nb_decimal="+str(nb_decimalsec)) error, deg = self.angle_rad2deg(rad) # --- all angle in [0:360[ deg = math.fmod(deg, 360) if (deg<0): deg = math.fmod(deg+360, 360) #print("deg 1 ="+str(deg)) # --- case modulo [-180:180[ if modulo==1: if deg>180: deg-=360 #print("deg 2 ="+str(deg)) # --- limit angle if deg>angle_limit: deg = angle_limit elif deg<-angle_limit: deg = -angle_limit #print("deg 3 ="+str(deg)) # --- sign of the input angle s = 1 if deg<0: s = -1 deg = -deg # --- case hour/deg if (unit==1): angle = deg/15 angle_limit = angle_limit/15 else: angle = deg #print("angle 1 ="+str(angle)) # --- compute the trheee components of xx mm ss r = angle xx = int(math.floor(r)) mm = 0 ss = 0 r = (r-xx)*60 mm = int(math.floor(r)) ss = (r-mm)*60 #print("xx ="+str(xx)) #print("mm ="+str(mm)) #print("ss ="+str(ss)) # === Compute the result result = "" # --- sign if sign==1 and s>=0: result += "+" elif s<0: result += "-" # --- xx n = 1+int(math.log10(angle_limit)) if zeros==0: fstring = "{:d}" else: fstring = "{:0"+str(n)+"d}" result += fstring.format(xx) # --- separator xx mm if separator==1: result += " " elif separator==2: result += ":" else: if unit==0: result += "d" else: result += "h" # --- mm if zeros==0: fstring = "{:d}" else: fstring = "{:02d}" result += fstring.format(mm) # --- separator mm ss if separator==1: result += " " elif separator==2: result += ":" else: result += "m" # --- ss if zeros==0: fstring = "{:."+str(nb_decimalsec)+"f}" else: if nb_decimalsec==0: fstring = "{:0"+str(nb_decimalsec+2)+".0f}" else: fstring = "{:0"+str(nb_decimalsec+3)+"."+str(nb_decimalsec)+"f}" #print("fstring="+fstring) result += fstring.format(ss) # --- separator ss if separator==0: result += "s" # -- end return 0, result # ======================================================== # === get/set methods # ======================================================== def angle(self, angle=""): """ Set the input angle in any format Inputs: ------- angle is an angle in any supported format (cf. help(Angle)) Usage: ------ >>> objangle = Angle() >>> objangle.angle("23d 27m") '23d 27m' Related topics: --------------- After using objangle.angle() get conversions with methods as objangle.deg() or objdate.rad(). """ if isinstance(angle, Angle) == False: if angle != "": if (angle != self._init_angle): self._init(angle) else: self = angle return self._init_angle def rad(self): """ Get the angle in radian Usage: ------ >>> objangle = Angle() >>> objangle.angle("-23 d 56'") "-23 d 56'" >>> objangle.rad() -0.4177154676439762 Related topics: --------------- Before use objdate.angle() to set the input angle. """ if (self._computed_rad == 0): init_angleformat, rad = self.angle_angle2rad(self._init_angle) self._init_angleformat = init_angleformat self._computed_rad = 1 self._rad = rad return self._rad def deg(self): """ Get the angle in degrees Usage: ------ >>> objangle = Angle() >>> objangle.angle("-23 d 56'") "-23 d 56'" >>> objangle.deg() -23.933333333333334 Related topics: --------------- Before use objdate.angle() to set the input angle. """ if (self._computed_deg == 0): if (self._computed_rad == 0): self.rad() if (len(self._init_angleformat) > 0): error, deg = self.angle_rad2deg(self._rad) if error==0: self._computed_deg = 1 self._deg = deg return self._deg return -1 return self._deg def arcmin(self): """ Get the angle in arcmin Usage: ------ >>> objangle = Angle() >>> objangle.angle("-23 d 56'") "-23 d 56'" >>> objangle.arcmin() -1436.0 Related topics: --------------- Before use objdate.angle() to set the input angle. """ if (self._computed_arcmin == 0): if (self._computed_rad == 0): self.rad() if (len(self._init_angleformat) > 0): error, deg = self.angle_rad2deg(self._rad) arcmin = deg*60 if error==0: self._computed_arcmin = 1 self._arcmin = arcmin return self._arcmin return -1 return self._arcmin def arcsec(self): """ Get the angle in arcsec Usage: ------ >>> objangle = Angle() >>> objangle.angle("-23 d 56'") "-23 d 56'" >>> objangle.arcsec() -86160.0 Related topics: --------------- Before use objdate.angle() to set the input angle. """ if (self._computed_arcsec == 0): if (self._computed_rad == 0): self.rad() if (len(self._init_angleformat) > 0): error, deg = self.angle_rad2deg(self._rad) arcsec = deg*3600 if error==0: self._computed_arcsec = 1 self._arcsec = arcsec return self._arcsec return -1 return self._arcsec def sexagesimal(self,sexagesimal_format): """ Get the angle in sexagesimal Inputs: ------- rad is an angle in radian sexagesimal_format is u (unit) = h,H,d,D (default=D). Capital mean module [0:360[, lower case means module [-180:180[ s (separator) = " ",:,"" (default="" means letters hms or dms) p (plus/minus) = +,"" (default="") z (zeros) = 0,"" (default="") a (angle_limits) = "",90, (+/-limit if unit D,H, default="" means 360) d (sec_digits) = "",".1",".2",... (default="") --- style 1: E.g. To Display a R.A.: "H0.2" => 23h07m42.49s E.g. To Display a Decl.: "d+090.1" => +00d34m22.6s E.g. To Display a H.A.: "h0.2" => -08h43m16.05s --- style 2: E.g. To Display a R.A.: "H 0.2" => 23 07 42.49 E.g. To Display a Decl.: "d +090.1" => -00 34 22.6 E.g. To Display a H.A.: "h 0.2" => -08 43 16.05 --- style 3: E.g. To Display a R.A.: "H:0.2" => 23:07:42.49 E.g. To Display a Decl.: "d:+090.1" => -00:34:22.6 E.g. To Display a H.A.: "h:0.2" => -08:43:16.05 Usage: ------ >>> objangle = Angle() >>> objangle.angle(-0.57) -0.57 >>> objangle.sexagesimal("d:-090.1") '-00:34:12.0' Related topics: --------------- Before use objdate.angle() to set the input angle. """ if (self._computed_sexagesimal == 0) or (sexagesimal_format != self._computed_sexagesimal_format): if (self._computed_rad == 0): self.rad() if (len(self._init_angleformat) > 0): error, sexagesimal = self.angle_rad2sexagesimal(self._rad, sexagesimal_format) if error==0: self._computed_sexagesimal = 1 self._sexagesimal = sexagesimal self._computed_sexagesimal_format = sexagesimal_format return self._sexagesimal return -1 return self._sexagesimal # ======================================================== # === debug methods # ======================================================== def infos(self, action) -> None: """ To get informations about this class :param action: A command to run a debug action (see examples). :type action: string :Example: Angle().infos("doctest") Angle().infos("doc_methods") Angle().infos("internal_attributes") Angle().infos("public_methods") """ if (action == "doc_methods"): publics = [x for x in dir(self) if x[0]!="_"] for public in publics: varname = "{}".format(public) if (callable(getattr(self,varname))==True): print("\n{:=^40}".format(" method "+varname+" ")) t = "Angle()."+varname+".__doc__" tt =eval(t) print(tt) if (action == "doctest"): if __name__ == "__main__": print("\n{:~^40}".format("doctest")) #doctest.testmod(verbose=True, extraglobs={'objangle': Angle()}) doctest.testmod(verbose=True) if (action == "internal_attributes"): internals = [x for x in dir(self) if x[0]=="_" and x[1]!="_"] for internal in internals: varname = "{}".format(internal) #if (hasattr(self,varname)==True): if (callable(getattr(self,varname))==False): print(varname + "=" + str(getattr(self,varname))) if (action == "public_methods"): publics = [x for x in dir(self) if x[0]!="_"] for public in publics: varname = "{}".format(public) if (callable(getattr(self,varname))==True): print(varname) # ======================================================== # === special methods # ======================================================== def __init__(self, angle=""): """ Object initialization where angle is the input in any format. :param angle : An angle in any supported format (cf. help(Angle)) :type angle : string """ self._init(angle) def __add__(self, angle): """ Add an angle to an angle. :param angle : An angle in any supported format (cf. help(Angle)) :type angle : Angle() :return: The result of the addition :rtype: Angle() :Example: >>> objangle1 = Angle() >>> objangle2 = Angle() >>> objangle1.angle(12.345) ; objangle2.angle("56d28m") ; objangle = objangle1 + objangle2 ; objangle.sexagesimal("D") 12.345 '56d28m' '68d48m42.00s' """ if isinstance(angle, Angle) == False: angle = Angle(angle) res = Angle() if self._computed_rad == 0: self.rad() if angle._computed_rad == 0: angle.rad() if (self._computed_rad == 1) and (angle._computed_rad == 1): rad = self._rad + angle._rad deg = rad * self._r2d res = Angle(deg) return res def __radd__(self, angle): """ Right addition an angle to an angle. """ return self + angle def __iadd__(self, angle): """ Add an angle to an angle. """ return self + angle def __sub__(self, angle): """ Subtract an angle to an angle. :param angle : An angle in any supported format (cf. help(Angle)) :type angle : Angle() :return: The result of the subtraction :rtype: Angle() :Example: >>> objangle1 = Angle() >>> objangle2 = Angle() >>> objangle1.angle(12.345) ; objangle2.angle("56d28m") ; objangle = objangle1 - objangle2 ; objangle.sexagesimal("D") 12.345 '56d28m' '315d52m42.00s' """ if isinstance(angle, Angle) == False: angle = Angle(angle) res = Angle() if self._computed_rad == 0: self.rad() if angle._computed_rad == 0: angle.rad() if (self._computed_rad == 1) and (angle._computed_rad == 1): rad = self._rad - angle._rad deg = rad * self._r2d res = Angle(deg) return res def __rsub__(self, angle): """ Right subtraction only an angle to an angle. """ if isinstance(angle, Angle) == True: return self - angle else: return angle def __isub__(self, angle): """ Subtract an angle to an angle. :param angle : An angle in any supported format (cf. help(Angle)) :type angle : Angle() :return: The result of the subtraction :rtype: Angle() :Example: >>> objangle1 = Angle() >>> objangle2 = Angle() >>> objangle1.angle(12.345) ; objangle2.angle("56d28m") ; objangle1 -= objangle2 ; objangle1.sexagesimal("D") 12.345 '56d28m' '315d52m42.00s' """ return self - angle def __eq__(self, angle): """ Comparaison of angles. Return True if angles are defined and equals. :param angle : An angle in any supported format (cf. help(Angle)) :type angle : Angle() :return: The logic result of the comparison. :rtype: bool :Example: >>> objangle1 = Angle() >>> objangle2 = Angle() >>> objangle1.angle(12.345) ; objangle2.angle("56d28m") ; objangle1 == objangle2 12.345 '56d28m' False .. note:: Does not account for the modulo. """ return self._angle_compare(angle, "==") def __ne__(self, angle): """ Comparaison of angles. Return True if angles are defined and not equals. :param angle : An angle in any supported format (cf. help(Angle)) :type angle : Angle() :return: The logic result of the comparison. :rtype: bool :Example: >>> objangle1 = Angle() >>> objangle2 = Angle() >>> objangle1.angle(12.345) ; objangle2.angle("56d28m") ; objangle1 != objangle2 12.345 '56d28m' True .. note:: Does not account for the modulo. """ return self._angle_compare(angle, "!=") def __gt__(self, angle): """ Comparaison of angles. Return True if self > angle :param angle : An angle in any supported format (cf. help(Angle)) :type angle : Angle() :return: The logic result of the comparison. :rtype: bool :Example: >>> objangle1 = Angle() >>> objangle2 = Angle() >>> objangle1.angle(12.345) ; objangle2.angle("56d28m") ; objangle1 > objangle2 12.345 '56d28m' False .. note:: Does not account for the modulo. """ return self._angle_compare(angle,">") def __ge__(self, angle): """ Comparaison of angles. Return True if self >= angle :param angle : An angle in any supported format (cf. help(Angle)) :type angle : Angle() :return: The logic result of the comparison. :rtype: bool :Example: >>> objangle1 = Angle() >>> objangle2 = Angle() >>> objangle1.angle(12.345) ; objangle2.angle("56d28m") ; objangle1 >= objangle2 12.345 '56d28m' False .. note:: Does not account for the modulo. """ return self._angle_compare(angle,">=") def __lt__(self, angle): """ Comparaison of angles. Return True if self < angle :param angle : An angle in any supported format (cf. help(Angle)) :type angle : Angle() :return: The logic result of the comparison. :rtype: bool :Example: >>> objangle1 = Angle() >>> objangle2 = Angle() >>> objangle1.angle(12.345) ; objangle2.angle("56d28m") ; objangle1 < objangle2 12.345 '56d28m' True .. note:: Does not account for the modulo. """ return self._angle_compare(angle,"<") def __le__(self, angle): """ Comparaison of angles. Return True if self <= angle :param angle : An angle in any supported format (cf. help(Angle)) :type angle : Angle() :return: The logic result of the comparison. :rtype: bool :Example: >>> objangle1 = Angle() >>> objangle2 = Angle() >>> objangle1.angle(12.345) ; objangle2.angle("56d28m") ; objangle1 <= objangle2 12.345 '56d28m' True .. note:: Does not account for the modulo. """ return self._angle_compare(angle,"<=") def __mod__(self, angle): """ Modulo of an angle by another angle. Return an angle :param angle : An angle in any supported format (cf. help(Angle)) :type angle : Angle() :return: An angle :rtype: Angle() :Example: >>> objangle1 = Angle() >>> objangle2 = Angle() >>> objangle1.angle(30.56) ; objangle2.angle(2.34) ; (objangle1 % objangle2).deg() 30.56 2.34 0.14000000000000412 """ if isinstance(angle, Angle) == False: angle = Angle(angle) if self._computed_deg == 0: self.deg() if angle._computed_deg == 0: angle.deg() anglemod = Angle() if (self._computed_deg == 1) and (angle._computed_deg == 1): deg = math.fmod(self._deg, angle._deg) if (deg<0): deg += angle._deg anglemod.angle(deg) return anglemod def __mul__(self, multiplier): """ multiplication of an angle by a float or int. Return an angle :param multiplier: A real number :type multiplier: float :return: An angle :rtype: Angle() :Example: >>> objangle = Angle() >>> objangle.angle(30.43) ; (objangle*2).deg() 30.43 60.86000000000001 """ if isinstance(multiplier, (int, float)) == False: raise TypeError return "" if self._computed_deg == 0: self.deg() anglemult = Angle() if (self._computed_deg == 1): deg = self._deg * multiplier anglemult.angle(deg) return anglemult def __rmul__(self, multiplier): """ Right multiplication of an angle by a float or int. """ return self * multiplier def __truediv__(self, divisor): """ division of an angle by a float or int. Return an angle :param divisor: A real number :type divisor: float :return: An angle :rtype: Angle() :Example: >>> objangle = Angle() >>> objangle.angle(30.43) ; (objangle/2).deg() 30.43 15.215000000000002 """ if isinstance(divisor, (int, float)) == False: raise TypeError return "" if (divisor==0): raise ZeroDivisionError if self._computed_deg == 0: self.deg() anglediv = Angle() if (self._computed_deg == 1): deg = self._deg / divisor anglediv.angle(deg) return anglediv # ======================================================== # ======================================================== # ======================================================== # Examples of execution if __name__ == "__main__": kex = 0 print("==========================") print("=== Self documentation ===") print("==========================") kex+=1; print("\n=== Example {}: Simple conversion to degrees".format(kex)) angle_inp = "2h3m27s" a = Angle(angle_inp) print("{} = {} degrees".format(angle_inp,a.deg())) kex+=1; print("\n=== Example {}: Simple conversion to radians".format(kex)) angle_inp = "2h3m27s" a = Angle(angle_inp) print("{} = {} radians".format(angle_inp,a.rad())) kex+=1; print("\n=== Example {}: Simple conversion to sexagesimal".format(kex)) angle_inp = "2h3m27s" a = Angle(angle_inp) print("{} = {}".format(angle_inp,a.sexagesimal("H:0.2"))) print("{} = {}".format(angle_inp,a.sexagesimal("d:+090.1"))) print("{} = {}".format(angle_inp,a.sexagesimal("d+090.1"))) kex+=1; print("\n=== Example {}: Addition of angles + format conversion".format(kex)) angle_inp1 = "2h3m27s" angle_inp2 = "-0d28m12.4s" a = Angle(angle_inp1) b = Angle(angle_inp2) c = a + b print("{} + {} = {}".format(angle_inp1,angle_inp2,c.sexagesimal("H0.2"))) cl = "Angle" print("\n=== To perform unitary tests: {}().infos(\"doctest\")".format(cl)) print("=== To list docs of all methods: {}().infos(\"doc_methods\")".format(cl)) print("\n=== List of public methods using {}().infos(\"public_methods\"):".format(cl)) Angle().infos("public_methods")