As more and more Web apps are run on mobile devices, the more demand for geolocation capabilities increase. As the name suggests, Geolocation is about the reporting of your location to other users, as well as associating real-world locations (such as landmarks) to your location. Today’s article reveals the main obstacle to accurately calculating the distance between two locations and presents a practical solution.
The Haversine Formula
One of the main challenges to calculating distances – especially large ones – is accounting for the curvature of the Earth. If only the Earth were flat, calculating the distance between two points would be as simple as for that of a straight line! The Haversine formula includes a constant (it’s the R variable below) that represents the radius of the Earth. Depending on whether you are measuring in miles or kilometers, it would equal 3956 mi or 6367 km respectively.
The basic formula is:
dlon = lon2 - lon1 dlat = lat2 - lat1 a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2 c = 2 * atan2( sqrt(a), sqrt(1-a) ) distance = R * c (where R is the radius of the Earth) R = 6367 km OR 3956 mi
Keep in mind that the Haversine formula is not 100% accurate because the Earth is not a perfect sphere – it bulges at the equator. Hence, distances near the equator would be underestimated while those near the polar regions would be overestimated. Nonetheless, the Haversine gives a good enough approximation for most applications.
The distance() Function Implementations
The GeoDataSource.com site has implementations of the Haversine formula for a variety of languages, including Java, JavaScript, PHP, Perl, SQL, and others.
I included the JavaScript and Java versions here because those are the two primary mobile app development languages. In both cases, the distance function accepts five parameters:
- lat1, lon1: The Latitude and Longitude of point 1 (in decimal degrees)
- lat2, lon2: The Latitude and Longitude of point 2 (in decimal degrees)
- unit: The unit of measurement in which to calculate the results where:
- ‘M’ is statute miles (default)
- ‘K’ is kilometers
- ‘N’ is nautical miles
The JavaScript Code
The JavaScript version of the Haversine formula as implemented by the GeoDataSource.com site is perfectly usable, but don’t compress it without adding the semi-colon line terminators first!
function distance(lat1, lon1, lat2, lon2, unit) { var radlat1 = Math.PI * lat1/180 var radlat2 = Math.PI * lat2/180 var radlon1 = Math.PI * lon1/180 var radlon2 = Math.PI * lon2/180 var theta = lon1-lon2 var radtheta = Math.PI * theta/180 var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta); dist = Math.acos(dist) dist = dist * 180/Math.PI dist = dist * 60 * 1.1515 if (unit=="K") { dist = dist * 1.609344 } if (unit=="N") { dist = dist * 0.8684 } return dist }
Using the JavaScript distance() Function
Here is an example that calls the distance() function to calculate the number of kilometers between centretown London and Paris. Notice that I am rounding the results to three decimal places. You can omit that step if you don’t mind a lot of decimal places.
var distance = distance(51.5112139, -0.119824, 48.8567, 2.3508, 'K'); //round to 3 decimal places console.log(Math.round(distance*1000)/1000); //displays 343.548
That matches the number from distancefromto.net of 344 km.
The Haversine Formula in Java
I took the liberty of plugging the java code on the GeoDataSource.com site into a some static functions within a class named Geolocation. That would allow for testing of the distance() function and calling it without having to instantiate a class.
package com.robertgravelle.geolocation; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.NumberFormat; public class Geolocation { public static double distance(double lat1, double lon1, double lat2, double lon2, char unit) { double theta = lon1 - lon2; double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta)); dist = Math.acos(dist); dist = rad2deg(dist); dist = dist * 60 * 1.1515; if (unit == 'K') { dist = dist * 1.609344; } else if (unit == 'N') { dist = dist * 0.8684; } return dist; } // This function converts decimal degrees to radians private static double deg2rad(double deg) { return (deg * Math.PI / 180.0); } // This function converts radians to decimal degrees private static double rad2deg(double rad) { return (rad * 180 / Math.PI); } public static void main(String[] args) { //place test code here } }
Using the Geolocation distance() Method
Here is an example that calls the distance() function to calculate the number of miles between centretown London and Paris. Notice the use of single quotes around the unit character (and not double quotes).
double distance = Geolocation.distance(51.5112139, -0.119824, 48.8567, 2.3508, 'M'); NumberFormat df = DecimalFormat.getInstance(); df.setMaximumFractionDigits(3); df.setRoundingMode(RoundingMode.CEILING); System.out.println(df.format(distance) + " Milesn"); //displays 213.471 Miles
Once again, the answer matches up with that of the distancefromto.net site.
Conclusion
The Haversine formula is certainly not the only way to calculate distances, but it is easy enough to use and accurate enough to satisfy most requirements.