TheAlgorithms-Python
85 строк · 3.4 Кб
1from math import atan, cos, radians, sin, tan
2
3from .haversine_distance import haversine_distance
4
5AXIS_A = 6378137.0
6AXIS_B = 6356752.314245
7EQUATORIAL_RADIUS = 6378137
8
9
10def lamberts_ellipsoidal_distance(
11lat1: float, lon1: float, lat2: float, lon2: float
12) -> float:
13"""
14Calculate the shortest distance along the surface of an ellipsoid between
15two points on the surface of earth given longitudes and latitudes
16https://en.wikipedia.org/wiki/Geographical_distance#Lambert's_formula_for_long_lines
17
18NOTE: This algorithm uses geodesy/haversine_distance.py to compute central angle,
19sigma
20
21Representing the earth as an ellipsoid allows us to approximate distances between
22points on the surface much better than a sphere. Ellipsoidal formulas treat the
23Earth as an oblate ellipsoid which means accounting for the flattening that happens
24at the North and South poles. Lambert's formulae provide accuracy on the order of
2510 meteres over thousands of kilometeres. Other methods can provide
26millimeter-level accuracy but this is a simpler method to calculate long range
27distances without increasing computational intensity.
28
29Args:
30lat1, lon1: latitude and longitude of coordinate 1
31lat2, lon2: latitude and longitude of coordinate 2
32Returns:
33geographical distance between two points in metres
34
35>>> from collections import namedtuple
36>>> point_2d = namedtuple("point_2d", "lat lon")
37>>> SAN_FRANCISCO = point_2d(37.774856, -122.424227)
38>>> YOSEMITE = point_2d(37.864742, -119.537521)
39>>> NEW_YORK = point_2d(40.713019, -74.012647)
40>>> VENICE = point_2d(45.443012, 12.313071)
41>>> f"{lamberts_ellipsoidal_distance(*SAN_FRANCISCO, *YOSEMITE):0,.0f} meters"
42'254,351 meters'
43>>> f"{lamberts_ellipsoidal_distance(*SAN_FRANCISCO, *NEW_YORK):0,.0f} meters"
44'4,138,992 meters'
45>>> f"{lamberts_ellipsoidal_distance(*SAN_FRANCISCO, *VENICE):0,.0f} meters"
46'9,737,326 meters'
47"""
48
49# CONSTANTS per WGS84 https://en.wikipedia.org/wiki/World_Geodetic_System
50# Distance in metres(m)
51# Equation Parameters
52# https://en.wikipedia.org/wiki/Geographical_distance#Lambert's_formula_for_long_lines
53flattening = (AXIS_A - AXIS_B) / AXIS_A
54# Parametric latitudes
55# https://en.wikipedia.org/wiki/Latitude#Parametric_(or_reduced)_latitude
56b_lat1 = atan((1 - flattening) * tan(radians(lat1)))
57b_lat2 = atan((1 - flattening) * tan(radians(lat2)))
58
59# Compute central angle between two points
60# using haversine theta. sigma = haversine_distance / equatorial radius
61sigma = haversine_distance(lat1, lon1, lat2, lon2) / EQUATORIAL_RADIUS
62
63# Intermediate P and Q values
64p_value = (b_lat1 + b_lat2) / 2
65q_value = (b_lat2 - b_lat1) / 2
66
67# Intermediate X value
68# X = (sigma - sin(sigma)) * sin^2Pcos^2Q / cos^2(sigma/2)
69x_numerator = (sin(p_value) ** 2) * (cos(q_value) ** 2)
70x_demonimator = cos(sigma / 2) ** 2
71x_value = (sigma - sin(sigma)) * (x_numerator / x_demonimator)
72
73# Intermediate Y value
74# Y = (sigma + sin(sigma)) * cos^2Psin^2Q / sin^2(sigma/2)
75y_numerator = (cos(p_value) ** 2) * (sin(q_value) ** 2)
76y_denominator = sin(sigma / 2) ** 2
77y_value = (sigma + sin(sigma)) * (y_numerator / y_denominator)
78
79return EQUATORIAL_RADIUS * (sigma - ((flattening / 2) * (x_value + y_value)))
80
81
82if __name__ == "__main__":
83import doctest
84
85doctest.testmod()
86