Added geodesy functions
This commit is contained in:
parent
f79059e6da
commit
fc200348f5
|
@ -0,0 +1,190 @@
|
|||
import RNS
|
||||
import time
|
||||
from math import pi, sin, cos, acos, tan, atan, atan2
|
||||
from math import radians, degrees, sqrt
|
||||
|
||||
|
||||
# Default planetary metrics
|
||||
equatorial_radius = 6378.137 *1e3
|
||||
polar_radius = 6356.7523142 *1e3
|
||||
ellipsoid_flattening = 1-(polar_radius/equatorial_radius)
|
||||
eccentricity_squared = 2*ellipsoid_flattening-pow(ellipsoid_flattening,2)
|
||||
mean_earth_radius = (1/3)*(2*equatorial_radius+polar_radius)
|
||||
|
||||
def central_angle(c1, c2):
|
||||
lat1 = radians(c1[0]); lon1 = radians(c1[1])
|
||||
lat2 = radians(c2[0]); lon2 = radians(c2[1])
|
||||
|
||||
d_lat = abs(lat1-lat2)
|
||||
d_lon = abs(lon1-lon2)
|
||||
ca = acos(
|
||||
sin(lat1) * sin(lat2) +
|
||||
cos(lat1) * cos(lat2) * cos(d_lon)
|
||||
)
|
||||
return ca
|
||||
|
||||
def geocentric_latitude(geodetic_latitude):
|
||||
e2 = eccentricity_squared
|
||||
lat = radians(geodetic_latitude)
|
||||
return degrees(atan((1.0 - e2) * tan(lat)))
|
||||
|
||||
def geodetic_latitude(geocentric_latitude):
|
||||
e2 = eccentricity_squared
|
||||
lat = radians(geocentric_latitude)
|
||||
return degrees(atan( (1/(1.0 - e2)) * tan(lat)))
|
||||
|
||||
def ellipsoid_radius_at(latitude):
|
||||
lat = radians(latitude)
|
||||
a = equatorial_radius; b = polar_radius;
|
||||
a2 = pow(a,2); b2 = pow(b,2)
|
||||
r = sqrt(
|
||||
( pow(a2*cos(lat), 2) + pow(b2*sin(lat), 2) )
|
||||
/
|
||||
( pow(a*cos(lat), 2) + pow(b*sin(lat), 2) )
|
||||
)
|
||||
return r
|
||||
|
||||
def euclidian_point(latitude, longtitude, altitude=0, ellipsoid=True):
|
||||
# Convert latitude and longtitude to radians
|
||||
# and get ellipsoid or sphere radius
|
||||
lat = radians(latitude); lon = radians(longtitude)
|
||||
r = ellipsoid_radius_at(latitude) if ellipsoid else mean_earth_radius
|
||||
|
||||
# Calculate euclidian coordinates from longtitude
|
||||
# and geocentric latitude.
|
||||
gclat = radians(geocentric_latitude(latitude)) if ellipsoid else lat
|
||||
x = cos(lat)*cos(lon)*r
|
||||
y = cos(gclat)*sin(lon)*r
|
||||
z = sin(gclat)*r
|
||||
|
||||
# Calculate surface normal of ellipsoid at
|
||||
# coordinates to add altitude to point
|
||||
normal_x = cos(lat)*cos(lon)
|
||||
normal_y = cos(lat)*sin(lon)
|
||||
normal_z = sin(lat)
|
||||
|
||||
if altitude != 0:
|
||||
x += altitude*normal_x
|
||||
y += altitude*normal_y
|
||||
z += altitude*normal_z
|
||||
|
||||
return (x,y,z)
|
||||
|
||||
def distance(p1, p2):
|
||||
dx = p1[0]-p2[0]
|
||||
dy = p1[1]-p2[1]
|
||||
dz = p1[2]-p2[2]
|
||||
return sqrt(dx*dx+dy*dy+dz*dz)
|
||||
|
||||
def euclidian_distance(c1, c2, ellipsoid=True):
|
||||
if len(c1) >= 2 and len(c2) >= 2:
|
||||
if len(c1) == 2: c1 += (0,)
|
||||
if len(c2) == 2: c2 += (0,)
|
||||
return distance(
|
||||
euclidian_point(c1[0], c1[1], c1[2], ellipsoid=ellipsoid),
|
||||
euclidian_point(c2[0], c2[1], c2[2], ellipsoid=ellipsoid)
|
||||
)
|
||||
else:
|
||||
return None
|
||||
|
||||
def spherical_distance(c1, c2, altitude=0, r=mean_earth_radius):
|
||||
d = (r+altitude)*central_angle(c1, c2)
|
||||
return d
|
||||
|
||||
def ellipsoid_distance(c1, c2):
|
||||
# TODO: Update this to the method described by Karney in 2013
|
||||
# instead of using Vincenty's algorithm.
|
||||
try:
|
||||
if c1[0] == 0.0: c1 = (1e-6, c1[1])
|
||||
a = equatorial_radius
|
||||
f = ellipsoid_flattening
|
||||
b = (1 - f)*a # polar radius
|
||||
tolerance = 1e-9 # to stop iteration
|
||||
|
||||
phi1, phi2 = radians(c1[0]), radians(c2[0])
|
||||
U1 = atan((1-f)*tan(phi1))
|
||||
U2 = atan((1-f)*tan(phi2))
|
||||
L1, L2 = radians(c1[1]), radians(c2[1])
|
||||
L = L2 - L1
|
||||
|
||||
lambda_old = L + 0
|
||||
|
||||
max_iterations = 10000
|
||||
iteration = 0
|
||||
timeout = 1.0
|
||||
st = time.time()
|
||||
while True:
|
||||
iteration += 1
|
||||
t = (cos(U2)*sin(lambda_old))**2
|
||||
t += (cos(U1)*sin(U2) - sin(U1)*cos(U2)*cos(lambda_old))**2
|
||||
sin_sigma = t**0.5
|
||||
cos_sigma = sin(U1)*sin(U2) + cos(U1)*cos(U2)*cos(lambda_old)
|
||||
sigma = atan2(sin_sigma, cos_sigma)
|
||||
|
||||
sin_alpha = cos(U1)*cos(U2)*sin(lambda_old) / sin_sigma
|
||||
cos_sq_alpha = 1 - sin_alpha**2
|
||||
cos_2sigma_m = cos_sigma - 2*sin(U1)*sin(U2)/cos_sq_alpha
|
||||
C = f*cos_sq_alpha*(4 + f*(4-3*cos_sq_alpha))/16
|
||||
|
||||
t = sigma + C*sin_sigma*(cos_2sigma_m + C*cos_sigma*(-1 + 2*cos_2sigma_m**2))
|
||||
lambda_new = L + (1 - C)*f*sin_alpha*t
|
||||
if abs(lambda_new - lambda_old) <= tolerance:
|
||||
break
|
||||
else:
|
||||
lambda_old = lambda_new
|
||||
|
||||
if iteration%1000 == 0:
|
||||
if iteration >= max_iterations:
|
||||
return None
|
||||
|
||||
if time.time() > st+timeout:
|
||||
return None
|
||||
|
||||
u2 = cos_sq_alpha*((a**2 - b**2)/b**2)
|
||||
A = 1 + (u2/16384)*(4096 + u2*(-768+u2*(320 - 175*u2)))
|
||||
B = (u2/1024)*(256 + u2*(-128 + u2*(74 - 47*u2)))
|
||||
t = cos_2sigma_m + 0.25*B*(cos_sigma*(-1 + 2*cos_2sigma_m**2))
|
||||
t -= (B/6)*cos_2sigma_m*(-3 + 4*sin_sigma**2)*(-3 + 4*cos_2sigma_m**2)
|
||||
delta_sigma = B * sin_sigma * t
|
||||
s = b*A*(sigma - delta_sigma)
|
||||
return s
|
||||
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
def orthodromic_distance(c1, c2, spherical=False):
|
||||
if spherical:
|
||||
return spherical_distance(c1, c2)
|
||||
else:
|
||||
return ellipsoid_distance(c1, c2)
|
||||
|
||||
# def tests():
|
||||
# from geographiclib.geodesic import Geodesic
|
||||
# geod = Geodesic.WGS84
|
||||
# coords = [
|
||||
# [(57.758793, 22.605194), (43.048838, -9.241343)],
|
||||
# [(0.0, 0.0), (0.0, 0.0)],
|
||||
# [(-90.0, 0.0), (90.0, 0.0)],
|
||||
# [(-90.0, 0.0), (78.0, 0.0)],
|
||||
# [(0.0, 0.0), (0.5, 179.5)],
|
||||
# [(0.7, 0.0), (0.0, -180.0)],
|
||||
# ]
|
||||
# for cs in coords:
|
||||
# c1 = cs[0]; c2 = cs[1]
|
||||
# print("Testing: "+str(c1)+" -> "+str(c2))
|
||||
# us = time.time()
|
||||
# ld = c1+c2; g = geod.Inverse(*ld)
|
||||
# print("Lib computed in "+str(round((time.time()-us)*1e6, 3))+"us")
|
||||
# us = time.time()
|
||||
# eld = orthodromic_distance(c1,c2,spherical=False)
|
||||
# if eld:
|
||||
# print("Own computed in "+str(round((time.time()-us)*1e6, 3))+"us")
|
||||
# else:
|
||||
# print("Own TIMED OUT in "+str(round((time.time()-us)*1e6, 3))+"us")
|
||||
|
||||
# print("Euclidian = "+RNS.prettydistance(euclidian_distance(c1,c2)))
|
||||
# print("Spherical = "+RNS.prettydistance(orthodromic_distance(c1,c2)))
|
||||
# if eld: print("Ellipsoid = "+RNS.prettydistance(eld))
|
||||
# print("EllipLib = "+RNS.prettydistance(g['s12']))
|
||||
# if eld: print("Diff = "+RNS.prettydistance(g['s12']-eld))
|
||||
# print("")
|
Loading…
Reference in New Issue