Updated: I noticed a floating point error when using this that I discuss correcting at the end of this post.
I thought I would post some of the bite-sized coding pieces I've done recently. To lead off, here's Ruby function to find the distance between two points given their latitude and longitude.
Latitude is given in degrees north of the equator (use negatives for the Southern Hemisphere) and longitude is given in degrees east of the Prime Meridian (optionally use negatives for the Western Hemisphere).
include Math DEG2RAD = PI/180.0 def lldist(lat1, lon1, lat2, lon2) rho = 3960.0 theta1 = lon1*DEG2RAD phi1 = (90.0-lat1)*DEG2RAD theta2 = lon2*DEG2RAD phi2 = (90.0-lat2)*DEG2RAD val = sin(phi1)*sin(phi2)*cos(theta1-theta2)+cos(phi1)*cos(phi2) val = [-1.0, val].max val = [ val, 1.0].min psi = acos(val) return psi*rho end
A couple of notes:
- Everything with
val
at the bottom is to deal with an edge case that can crop up when you try to get the distance between a point and itself. In that caseval
should be equal to 1.0, but on my systems some floating-point errors creep in and I get 1.0000000000000002, which is out of range for theacos()
function. - This returns the distance in miles. If you want some other unit, redefine
rho
with the appropriate value for the radius of the earth in your desired unit (6371 km, 1137 leagues, 4304730 passus, or what have you). - This assumes the Earth is spherical, which is a decent first approximation, but is still just that: a first approximation. ((As far as I'm concerned, this is my canonical example of the difference between a first and second approximation. The Earth isn't really a oblate spheroid either, but that makes a very good second approximation — about 100 m. (See John Cook here and here.) ))
I am currently writing a second version to account for the difference between geographic and geocentric latitude which should do a good job of accounting for the Earth's eccentricity. The math is not hard, but finding ground truth to validate my results against is, since the online calculators I've tried to check against do not make their assumptions clear. I did find a promising suite of tools for pilots, and I'd hope if you're doing something as fraught with consequences as flying that you've accounted for these sorts of things.
Update:
I've been putting this to use in a new project, and I've noticed an edge case that didn't crop up before. Due to some floating-point oddities, trying to get the distance between a point and itself will throw an error. In that case the value passed to acos()
should be 1.0 but ends up being 1.0000000000000002 on my system. Since the domain of acos()
is [-1,1] this is no good.
If you want to be on the safe side you can replace this...
psi = acos(sin(phi1)*sin(phi2)*cos(theta1-theta2)+cos(phi1)*cos(phi2))
...with this...
val = sin(phi1)*sin(phi2)*cos(theta1-theta2)+cos(phi1)*cos(phi2) val = [-1.0, val].max val = [ val, 1.0].min psi = acos(val)
...and that will take care of things. (Yes, this is a verbose way of doing things, but I often prefer the verbose-and-overt to the laconic-and-recondite.)