def _get_intermediate_coord(self, fraction: float) -> List[Union[float, float]]: """ Calculates the intermediate point on a great circle line http://www.edwilliams.org/avform.htm#Intermediate :param fraction: input fraction of the whole great circle :return: a tuple of cordinates """ A = math.sin((1 - fraction) * self.distance) / math.sin(self.distance) B = math.sin(fraction * self.distance) / math.sin(self.distance) x = A * math.cos(self.start.y) * math.cos(self.start.x) + B * math.cos( self.end.y) * math.cos(self.end.x) y = A * math.cos(self.start.y) * math.sin(self.start.x) + B * math.cos( self.end.y) * math.sin(self.end.x) z = A * math.sin(self.start.y) + B * math.sin(self.end.y) lat = radians_to_degrees( math.atan2(z, math.sqrt(math.pow(x, 2) + math.pow(y, 2)))) lon = radians_to_degrees(math.atan2(y, x)) return Coordinate(lon, lat).coords
def bearing(start, end, options=None): """ Takes two points and finds the geographic bearing between them, i.e. the angle measured in degrees from the north line (0 degrees) :param start: starting point [lng, lat] or Point feature :param end: ending point [lng, lat] or Point feature :param options: dictionary with options: [options["final"]] - calculates the final bearing if true :return: bearing in decimal degrees, between -180 and 180 (positive clockwise) """ if not options: options = {} if isinstance(options, dict) and "final" in options: return calculate_final_bearing(start, end) start = get_coords_from_features(start, ["Point"]) end = get_coords_from_features(end, ["Point"]) lon1 = degrees_to_radians(start[0]) lon2 = degrees_to_radians(end[0]) lat1 = degrees_to_radians(start[1]) lat2 = degrees_to_radians(end[1]) a = np.sin(lon2 - lon1) * np.cos(lat2) b = np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(lon2 - lon1) return radians_to_degrees(np.arctan2(a, b))
def destination(origin, distance, bearing, options=None): """ Takes a Point and calculates the location of a destination point given a distance in degrees, radians, miles, or kilometers; and bearing in degrees. This uses the [Haversine formula](http://en.wikipedia.org/wiki/Haversine_formula) to account for global curvature. :param origin: starting point :param distance: distance from the origin point :param bearing: bearing ranging from -180 to 180 :param options: optional parameters [options["units"]='kilometers'] miles, kilometers, degrees, or radians [options["properties"]={}] Translate properties to Point :return: destination GeoJSON Point feature """ if not options: options = {} kwargs = {} if "units" in options: kwargs["units"] = options.get("units") coords = get_coords_from_features(origin, ["Point"]) longitude1 = degrees_to_radians(coords[0]) latitude1 = degrees_to_radians(coords[1]) bearing_rads = degrees_to_radians(bearing) radians = length_to_radians(distance, **kwargs) latitude2 = asin( sin(latitude1) * cos(radians) + cos(latitude1) * sin(radians) * cos(bearing_rads)) longitude2 = longitude1 + atan2( sin(bearing_rads) * sin(radians) * cos(latitude1), cos(radians) - sin(latitude1) * sin(latitude2), ) lng = truncate(radians_to_degrees(longitude2), 6) lat = truncate(radians_to_degrees(latitude2), 6) return point([lng, lat], options.get("properties", None))
def calculate_rhumb_bearing(origin: Sequence, destination: Sequence) -> float: """ Calculates the bearing from origin to destination point along a rhumb line. http://www.edwilliams.org/avform.htm#Rhumb """ phi_1 = degrees_to_radians(origin[1]) phi_2 = degrees_to_radians(destination[1]) delta_lambda = degrees_to_radians(destination[0] - origin[0]) # if delta_lambda over 180° take shorter rhumb line across the anti-meridian: if abs(delta_lambda) > pi: if delta_lambda > 0: delta_lambda = -(2 * pi - delta_lambda) if delta_lambda < 0: delta_lambda = 2 * pi + delta_lambda delta_psi = log(tan(phi_2 / 2 + pi / 4) / tan(phi_1 / 2 + pi / 4)) theta = atan2(delta_lambda, delta_psi) return fmod(radians_to_degrees(theta) + 360, 360)
def test_radians_to_degrees(value, result): assert round(radians_to_degrees(value), 6) == result