Skip to content

Commit cb47956

Browse files
eightysixthpoyea
andauthored
Implemented geodesy - Lambert's ellipsoidal distance (TheAlgorithms#1763)
* Implemented Lambert's long line * Update lamberts_ellipsoidal_distance.py Co-authored-by: John Law <[email protected]>
1 parent 6b3bbc7 commit cb47956

File tree

1 file changed

+83
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)