Coding Notes

Ideas and thoughts from Seth Long

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


About these ads

February 5, 2008 - Posted by | Algorithms

18 Comments »

  1. 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 | Reply

  2. Thanks!

    Comment by john | July 3, 2008 | Reply

  3. 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 | Reply

  4. 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 | Reply

  5. 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 | Reply

  6. 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 | Reply

  7. [...] 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 – T-SQL – Haversine - Jim Jackson | February 13, 2009 | Reply

  8. 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/calculating-distances-between-latitude-and-longitude-t-sql-haversine.aspx

    Jim

    Comment by Jim | February 13, 2009 | Reply

  9. 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 left-hand side avoids any loss of precision by cancellation that could occur on the right-hand 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 round-off errors leading to a > 1, which would make an arcsine calculation blow-up.

    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 | Reply

  10. excelente post!
    justo lo que necesitaba.

    Comment by ALEJANDRO VARELA | May 6, 2009 | Reply

  11. Great post!

    I’m trying to build a distance search function on basis of a list of zipcodes/long-lat 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 long-lat coordinates to a view with all long-lat coordinates in a certain region?

    Hope to hear from you

    Martin

    Comment by Martin | October 2, 2009 | Reply

  12. Or is this exactly what your sql function is all about?

    Martin

    Comment by Martin | October 2, 2009 | Reply

  13. 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 co-ords 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 | Reply

  14. 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 | Reply

  15. @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 | Reply

  16. 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 | Reply

  17. 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 | Reply

  18. oh i got it its in miles right?

    Comment by uplink2010 | April 26, 2011 | Reply


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: