Source code for bearing.angle

""" This module includes the Bearing class which handles all the bearings and
azimuth values and performs all the conversions.
"""
import math
from typing import Dict

DEGREE = "\N{DEGREE SIGN}"


[docs]class Bearing: """ The Bearing class handles storing the data for bearing and azimuth. It does the conversion between the two and returns the information in several formats. """ def __init__(self): """ Initialize the private class variables. They are not intended to be addressed directly. Use the included methods instead. """ self._degree: int = 0 self._minute: int = 0 self._second: int = 0 self._azimuth: float = 0 self._north: str = "N" self._east: str = "E" def __str__(self) -> str: """ Text output of both the bearing and azimuth :return: bearing and azimuth :rtype: str """ result: str = ( f"{self._north}{int(self._degree):02d}{DEGREE}" f"{int(self._minute):02d}'" f'{int(self._second):02d}"{self._east}' f" : {self._azimuth:.4f}{DEGREE}" ) return result def __repr__(self) -> str: """ Repr output of both the bearing and azimuth :return: bearing and azimuth :rtype: str """ result: str = ( f"{self._north}{int(self._degree):02d}{DEGREE}" f"{int(self._minute):02d}'" f'{int(self._second):02d}"{self._east}' f" : {self._azimuth:.4f}{DEGREE}" ) return result
[docs] def set_bearing(self, n: str, d: int, m: int, s: int, e: str) -> float: """ Initialize a bearing and perform the azimuth conversion :param n: northing (is always either 'N' or 'S') :type n: str :param d: degrees :type d: int :param m: minutes :type m: int :param s: seconds :type s: int :param e: easting (always either 'E' or 'W') :type e: str :return: the azimuth as a float :rtype: float """ self._north = str(n) self._degree = int(d) self._minute = int(m) self._second = int(s) self._east = str(e) self.calc_azimuth() return self._azimuth
[docs] def set_azimuth(self, az: float) -> str: """ Set the value of the azimuth and perform the bearing conversion. :param az: azimuth value :type az: float :return: The bearing as a string :rtype: str """ self._azimuth = round(float(az), ndigits=4) self.calc_bearing(az) return self.get_bearing()
[docs] def calc_azimuth(self) -> None: """ Performs the azimuth conversion using the private members :return: None """ angle = round(self._degree + self._minute / 60 + self._second / 3600, 4) if self._north == "N" and self._east == "E": self._azimuth = angle if self._north == "N" and self._east == "W": self._azimuth = 360 - angle if self._north == "S" and self._east == "E": self._azimuth = 180 - angle if self._north == "S" and self._east == "W": self._azimuth = 180 + angle
[docs] def calc_bearing(self, az: float) -> None: """ Performs the bearing calculation. :param az: Azimuth as a float :return: None """ if az <= 90.0 or az >= 270.0: self._north = "N" else: self._north = "S" if 0.0 < az < 180.0: self._east = "E" else: self._east = "W" self.dec_to_dms()
[docs] def dec_to_dms(self) -> None: """ Adjust the angle based on north and sets the degrees, minutes, and seconds member variables. :return: None """ if self._azimuth < -360 or self._azimuth > 360: raise ValueError( f"Azimuth angle must be between 0{DEGREE} and 360{DEGREE}." ) angle = self._azimuth # adjust angle based on quadrant if 90 < self._azimuth <= 180: angle = 180 - self._azimuth elif 180 < self._azimuth < 270: angle = self._azimuth - 180 elif 270 <= self._azimuth <= 360: angle = 360 - self._azimuth decimal, self._degree = math.modf(angle) remainder, self._minute = math.modf(decimal * 60) self._second = remainder * 60
[docs] def get_bearing(self) -> str: """ Return just the bearing as a formatted string. :return: bearing value :rtype: str """ result: str = ( f"{self._north}{int(self._degree):02d}{DEGREE}" f"{int(self._minute):02d}'" f'{int(self._second):02d}"{self._east}' ) return result
[docs] def get_bearing_dict(self) -> Dict: """ Return the components of a bearing in a dictionary. :return: bearing :rtype: Dict """ return { "northing": f"{self._north}", "degrees": f"{int(self._degree):02d}", "minutes": f"{int(self._minute):02d}", "seconds": f"{int(self._second):02d}", "easting": f"{self._east}", }
[docs] def get_azimuth(self) -> float: """ Since accessing the member variables directly is discouraged, this method returns the azimuth value. :return: azimuth value :rtype: float """ return self._azimuth
[docs] def submit_bearing(self, n: str, d: str, m: str, s: str, e: str) -> float: """ Initialize a bearing using string values for input. submit_bearing does some extra validation and converts degrees minutes, and seconds to integers. :param n: northing (is always either 'N' or 'S') :type n: str :param d: degrees :type d: str :param m: minutes :type m: str :param s: seconds :type s: str :param e: easting (always either 'E' or 'W') :type e: str :return: the azimuth as a float or 0 on failure :rtype: float """ if len(n) == 1 and len(e) == 1: if n in ["N", "S"] and e in ["E", "W"]: if 0 < len(d) < 3 and 0 < len(m) < 3 and 0 < len(s) < 3: return self.set_bearing(n, int(d), int(m), int(s), e) else: return 0
[docs] def submit_azimuth(self, az: str) -> Dict: """ Sets the azimuth value when the input is a string. Does some validation and returns the bearing as a dictionary. :param az: azimuth value :type az: str :return: bearing components :rtype: Dict """ try: value = float(az) if 0 <= value <= 360: self.set_azimuth(value) except ValueError: self.set_azimuth(0) return self.get_bearing_dict()
if __name__ == "__main__": b = Bearing() b.set_azimuth(90.75) print(b) assert b.__str__() == f"S89{DEGREE}15'00\"E : 90.7500{DEGREE}"