Haversine Formula in C# and in SQL
The Haversine Formula class listed below is used to calculate the distance between two latitude/longitude points in either Kilometers or Miles. I’ve seen the formula written in a few other languages but didn’t see any in C#. So, here is the Haversine Formula in C#. Before I show the class, let me give an example of how the class is called and used.
To start we will need 2 latitude/longitude points. I created a struct to hold the latitude/longitude points. Once we have the points, we create the Haversine class and call the Distance method, passing in the points and an enum specifying whether to return the results in Kilometers or Miles. We end up with the following:
Position pos1 = new Position(); pos1.Latitude = 40.7486; pos1.Longitude = 73.9864; Position pos2 = new Position(); pos2.Latitude = 24.7486; pos2.Longitude = 72.9864; Haversine hv = new Haversine(); double result = hv.Distance(pos1, pos2, DistanceType.Kilometers);Here is the code for the class:
using System; namespace HaversineFormula { /// <summary> /// The distance type to return the results in. /// </summary> public enum DistanceType { Miles, Kilometers }; /// <summary> /// Specifies a Latitude / Longitude point. /// </summary> public struct Position { public double Latitude; public double Longitude; } class Haversine { /// <summary> /// Returns the distance in miles or kilometers of any two /// latitude / longitude points. /// </summary> /// <param name=”pos1″></param> /// <param name=”pos2″></param> /// <param name=”type”></param> /// <returns></returns> public double Distance(Position pos1, Position pos2,DistanceType type) { double R = (type == DistanceType.Miles) ? 3960 : 6371; double dLat = this.toRadian(pos2.Latitude – pos1.Latitude); double dLon = this.toRadian(pos2.Longitude – pos1.Longitude); double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) + Math.Cos(this.toRadian(pos1.Latitude)) *Math.Cos(this.toRadian(pos2.Latitude)) * Math.Sin(dLon / 2) * Math.Sin(dLon / 2); double c = 2 * Math.Asin(Math.Min(1, Math.Sqrt(a))); double d = R * c; return d; } /// <summary> /// Convert to Radians. /// </summary> /// <param name=”val”></param> /// <returns></returns> private double toRadian(double val) { return (Math.PI / 180) * val; } } }Here is the same formula as a SQL function. I used Microsoft SQL server for this example.
CREATE FUNCTION [dbo].[GetDistance]
( @lat1 Float(8), @long1 Float(8), @lat2 Float(8), @long2 Float(8) ) RETURNS Float(8) AS BEGIN DECLARE @R Float(8); DECLARE @dLat Float(8); DECLARE @dLon Float(8); DECLARE @a Float(8); DECLARE @c Float(8); DECLARE @d Float(8); SET @R = 3960; SET @dLat = RADIANS(@lat2 – @lat1); SET @dLon = RADIANS(@long2 – @long1); SET @a = SIN(@dLat / 2) * SIN(@dLat / 2) + COS(RADIANS(@lat1)) * COS(RADIANS(@lat2)) * SIN(@dLon / 2) *SIN(@dLon / 2); SET @c = 2 * ASIN(MIN(SQRT(@a))); SET @d = @R * @c; RETURN @d; END GO– Seth Long
18 Comments »
Leave a Reply

Archives
 May 2008 (1)
 February 2008 (3)
 January 2008 (6)

Categories

RSS
Entries RSS
Comments RSS
Nice post, this saves me some work.
This is more of a general Haversine question, not specific to your implementation, but is there a version of the formula which can take Altitude into account?
Thanks,
~Brook
Comment by Brook  March 19, 2008 
Thanks!
Comment by john  July 3, 2008 
To take altitude into account just add the required altitude, in km or miles, to the mean radius figure (R).
Comment by Adrian  September 2, 2008 
hi,
its very usefull…
but can u tell whether can i use decimal datatype instead of float….
regards
vidhya
Comment by vidhya  September 28, 2008 
Hi,
sorry but I would like to ask a follow up question to Brook concerning altitude. Assume two points a and b are given with latitude, longitude and altitude (all from gps device). The distance between the two points does rely on their altitude difference then. Is it really correct then – as proposed by Adrian – to add the altitude difference between the two points in kilometers to R?
Thanks in advance
Patrick
Comment by Patrick  October 7, 2008 
Thanks, good work! FYI:
Math.Sin(dLat / 2) * Math.Sin(dLat / 2)
is equivalent to
(1 – Math.Cos(dLat)) / 2.
Same for the sin2(dLon/2) term.
The Haversine formula on Wikipedia doesn’t have Math.Min(1, Math.Sqrt(a)) in it, just Math.Sqrt(a).
Do you know whether Min is important here? I’ve decided not to use it.
About altitude: I think Adrian misunderstood the question. It boggles my mind. However, here’s something to consider. The radius of earth varies by about 20K from equator to pole, but the tallest mountain in the world is 8K. Is it possible that the there’s more error in driving north and south at sea level than by driving up and down hills along the equator?
Comment by Brad Dre  November 24, 2008 
[…] I’ve got lots of lat/long information for my Silverlight 2 application and I’m now tinkering with getting the elevation view working in my spare time. The problem is that the distances are very small and the haversine formulae I’ve located on the internet are all either miles or kilometers. Not going to work when your next position is only 50 feet away. So I wrote the simple SQL function below based directly on Seth Long’s version here. […]
Pingback by Calculating Distances Between Latitude and Longitude – TSQL – Haversine  Jim Jackson  February 13, 2009 
Thanks for the info! I’ve updated my own version of the SQL function for use in my Silverlight application. My version makes some minor changes to radius values as described here: http://weblogs.asp.net/jimjackson/archive/2009/02/13/calculatingdistancesbetweenlatitudeandlongitudetsqlhaversine.aspx
Jim
Comment by Jim  February 13, 2009 
I’m responding to a few points Brad Dre brings up.
The haversine fomula is more accurate over short distances than some slightly simpler formulas, e.g. based on the law of spherical cosines. So, while it mathematically true:
sin(dLat/2)*sin(dLat/2) = (1 – cos(dLat))/2
the lefthand side avoids any loss of precision by cancellation that could occur on the righthand side when the difference in latitudes dLat is small.
Brad asks about:
“The Haversine formula on Wikipedia doesn’t have Math.Min(1, Math.Sqrt(a)) in it, just Math.Sqrt(a).
“Do you know whether Min is important here? I’ve decided not to use it.”
A comment by the ubiquitous SQL expert Joe Celko cleared this up for me. It is useful at the other extreme of distances when two points are nearly at opposite sides of the sphere (antipodal). Then it guards against roundoff errors leading to a > 1, which would make an arcsine calculation blowup.
Finally, with regard to including altitude (and presumably, a difference in altitudes) in the distance calculation, it seems to me one has now entered a context in which “great circle” distances (produced by the haversine formula) may be less the question than Euclidean (straightline) distances. Someone might want to give the context in which they consider altitude important, e.g. orbital calculations? The haversine formula assumes a spherical model, and as Brad also pointed out, the variation in radius from north/south latitude variation can exceed the variation in altitude.
Adrian’s suggestion, to add the altitude to the Earth’s radius, is correct if one is asking about the distance between two points of equal altitude. However this would be a correction of less than 0.2% even at Mount Everest’s elevation.
When a difference in altitude is smallish relative to the great circle distance, a quick approximation is to add the two. This is essentially the approximation of a right triangle’s hypotenuse by adding the lengths of the two legs, reliable when one leg is much longer than the other. If the two distances were similar in magnitude, the Pythagorean formula would be more accurate, i.e. the square root of the square of altitude difference plus the square of the great circle distance.
regards, mt
Comment by mathtalk  April 19, 2009 
excelente post!
justo lo que necesitaba.
Comment by ALEJANDRO VARELA  May 6, 2009 
Great post!
I’m trying to build a distance search function on basis of a list of zipcodes/longlat coordinates in the Netherlands.
As I’m quite a newby to sql server, do you know if it’s difficult to create an sql function that adds the distance to the customers longlat coordinates to a view with all longlat coordinates in a certain region?
Hope to hear from you
Martin
Comment by Martin  October 2, 2009 
Or is this exactly what your sql function is all about?
Martin
Comment by Martin  October 2, 2009 
Hey,
Great post, thanks very much for the calculations.
Similar to what Jim has done, I have added in the options for feet and inches and have been testing this out in both the C# and SQL functions above.
However, they are returning slightly different answers – here is an example:
Position 1: 54.548576, 5.966298
Position 2: 54.549179, 5.967442
SQL function returns 99.61713 metres, C# function returns 99.64112.
Whilst I am perfectly happy with this level of accuracy (we got the lat long coords by getting the start and finish line of our local athletics track using Google Maps on an iPhone :)) I’m just curious as to why the functions are behaving differently, as they appear to be doing exactly the same mathematical operation?
Comment by PK  November 17, 2009 
Great article. I think it calculates air distance.
Is there any formula/method/webservice/script/api which gives road distance between two coordinates ?
Thanks
Comment by Divyesh  November 27, 2009 
@vidhya – It doesn’t really make sense to use decimal instead of double within the Haversine calculation, because Math.Sin, Math.PI, and other calls return doubles, limiting your precision no matter what type the method returns.
Comment by Forrest  January 20, 2010 
Good job, this worked very well for me, thank you for posting it.
I was looking at some other way of calculating distance, like putting some sensor on my the wheel of my bike and count the revolutions, but this is much easier,I may do the wheel thing anyway, just for the fun of it.
Thanks again.
Kevin
Comment by Kevin  October 5, 2010 
hi seth, i want to know the sql function is returning the distance either kilometers or in miles? please clear this confusion
Comment by uplink2010  April 25, 2011 
oh i got it its in miles right?
Comment by uplink2010  April 26, 2011 