Bryan Reynolds

Software, Business, Life . . .

Contact

Bryan Reynolds
  Bryan Reynolds Linked In
E-mail me Send mail

Recent comments

Authors

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2014

Latitude, Longitude, Bearing, Cardinal Direction, Distance, and C#

If you are looking for some examples of using latitude and longitude to get bearing and distance information with C# you have come to the right place.  If you want to get straight to the code and the download click here.

 

I usually try to blog about things that I am working on that also could be interesting to other developers.  Recently I have been working on an application that stores information about commercial buildings and there competition.  Each location has a latitude and longitude which is used to display there data on a map.

 

To visualize the end result I have added a picture below of an application that is using using ASP.Net, Virtual Earth, while calculating bearing and distance.  This was inspire by Alessandro Gallo article.  If you are interesting in building a Virtual Earth mash-up using ASP.NET this is a must read article.  When the user puts there mouse over a location a pop-up with the distance and bearing are shown.  The distance is in miles while the bearing is represented by its cardinal value or compass direction.

MapVirtualEarth

 

Calculating Distance between two points of Latitude and Longitude

The function below uses the Haversine formula to calculate the distance between the to coordinates and a enumeration to request the type of unit of length. 

 

   1: /// <summary>
   2: /// Calculates the distance between two points of latitude and longitude.
   3: /// Great Link - http://www.movable-type.co.uk/scripts/latlong.html
   4: /// </summary>
   5: /// <param name="coordinate1">First coordinate.</param>
   6: /// <param name="coordinate2">Second coordinate.</param>
   7: /// <param name="unitsOfLength">Sets the return value unit of length.</param>
   8: public static Double Distance(Coordinate coordinate1, Coordinate coordinate2, UnitsOfLength unitsOfLength)
   9: {
  10:  
  11:     var theta = coordinate1.Longitude - coordinate2.Longitude;
  12:     var distance = Math.Sin(coordinate1.Latitude.ToRadian()) * Math.Sin(coordinate2.Latitude.ToRadian()) +
  13:                    Math.Cos(coordinate1.Latitude.ToRadian()) * Math.Cos(coordinate2.Latitude.ToRadian()) *
  14:                    Math.Cos(theta.ToRadian());
  15:  
  16:     distance = Math.Acos(distance);
  17:     distance = distance.ToDegree();
  18:     distance = distance * 60 * 1.1515;
  19:  
  20:     if (unitsOfLength == UnitsOfLength.Kilometer)
  21:         distance = distance * _MilesToKilometers;
  22:     else if (unitsOfLength == UnitsOfLength.NauticalMiles)
  23:         distance = distance * _MilesToNautical;
  24:  
  25:     return (distance);
  26:  
  27: }
Originally the function was made with 4 double values representing the latitude and longitudes for each coordinate.  While unit testing I tested incorrect coordinates. I found myself adding ArgumentOutOfRangeExceptions directly into the distance function above.  I did not like the feeling of this so I encapsulated the throwing of the exceptions in a coordinate class.  This reduced the code for the distance function and made the code clearer.
   1: public class Coordinate
   2:    {
   3:        private double latitude, longitude;
   4:  
   5:        /// <summary>
   6:        /// Latitude in degrees. -90 to 90
   7:        /// </summary>
   8:        public Double Latitude
   9:        {
  10:            get { return latitude; }
  11:            set
  12:            {
  13:                if (value > 90) throw new ArgumentOutOfRangeException("value", "Latitude value cannot be greater than 90.");
  14:                if (value < -90) throw new ArgumentOutOfRangeException("value", "Latitude value cannot be less than -90.");
  15:                latitude = value;
  16:            }
  17:        }
  18:  
  19:        /// <summary>
  20:        /// Longitude in degree. -180 to 180
  21:        /// </summary>
  22:        public Double Longitude
  23:        {
  24:            get { return longitude; }
  25:            set
  26:            {
  27:                if (value > 180) throw new ArgumentOutOfRangeException("value", "Longitude value cannot be greater than 180.");
  28:                if (value < -180) throw new ArgumentOutOfRangeException("value", "Longitude value cannot be less than -180.");
  29:                longitude = value;
  30:            }
  31:        }
  32:    }

 

Here are some of the unit tests that for the coordinate class. 

   1: [Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
   2:      public void CoordinateLatitudeGreater90() { var coordinate = new Coordinate() { Latitude = 100, Longitude = -90 }; }
   3:  
   4:      [Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
   5:      public void CoordinateLatitudeLessN90() { var coordinate = new Coordinate() { Latitude = -91, Longitude = -90 }; }
   6:  
   7:      [Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
   8:      public void CoordinateLongitudeGreater180() { var coordinate = new Coordinate() { Latitude = 90, Longitude = 190 }; }
   9:  
  10:      [Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
  11:      public void CoordinateLongitudeLessN180() { var coordinate = new Coordinate() { Latitude = 90, Longitude = -190 }; }
Here is an example usage of the distance function.
   1: var distance = Helper.Distance(new Coordinate() { Latitude = 45, Longitude = 0 },
   2:                                new Coordinate() { Latitude = 0, Longitude = 45 }, UnitsOfLength.NauticalMiles);
   3:  
   4: Assert.AreEqual(3599.8653599999993d, distance, "");

Calculating Bearing and Cardinal Direction

First, I have to give thanks to wikipedia for explaining what a cardinal point was. I never really thought about it until I thought about writing this article.  So for all of you who don't already know the location on your compass that say, "N,S,E,W" are called cardinal points. I am not generally one for being a stickler for using the correct nomenclature, but since I went and looked it up,  you can benefit from it.

First things first, we need to create a function that takes two coordinates that returns a direction in degrees.  Here is where my decision to encapsulate the latitude and longitude values paid some dividends.  Now the code will check the latitude and longitude values before it evaluates this function.

   1: /// <summary>
   2:       /// Accepts two coordinates in degrees.
   3:       /// </summary>
   4:       /// <returns>A double value in degrees.  From 0 to 360.</returns>
   5:       public static Double Bearing(Coordinate coordinate1, Coordinate coordinate2)
   6:       {
   7:           var latitude1 = coordinate1.Latitude.ToRadian();
   8:           var latitude2 = coordinate2.Latitude.ToRadian();
   9:  
  10:           var longitudeDifference = (coordinate2.Longitude - coordinate1.Longitude).ToRadian();
  11:  
  12:           var y = Math.Sin(longitudeDifference) * Math.Cos(latitude2);
  13:           var x = Math.Cos(latitude1) * Math.Sin(latitude2) -
  14:                   Math.Sin(latitude1) * Math.Cos(latitude2) * Math.Cos(longitudeDifference);
  15:  
  16:           return (Math.Atan2(y, x).ToDegree() + 360) % 360;
  17:       }

 

Now that we have a function that will return the bearing in degrees we need to convert those degrees to there cardinal value.

An enumeration to store the cardinal points.

   1: public enum CardinalPoints { N, E, W, S, NE, NW, SE, SW }
A class to store the ranges used to evaluated the cardinal points.
   1: /// <summary>
   2: /// Class is used in a calculation to determin cardinal point enumeration values from degrees.
   3: /// </summary>
   4: private struct CardinalRanges
   5: {
   6:     public CardinalPoints CardinalPoint;
   7:     /// <summary>
   8:     /// Low range value associated with the cardinal point.
   9:     /// </summary>
  10:     public Double LowRange;
  11:     /// <summary>
  12:     /// High range value associated with the cardinal point.
  13:     /// </summary>
  14:     public Double HighRange;
  15: }
The function to evaluate and return the cardinal direction.
   1: /// <summary>
   2:        /// Method extension for Doubles. Converts a degree to a cardinal point enumeration.
   3:        /// </summary>
   4:        /// <returns>Returns a cardinal point enumeration representing a compass direction.</returns>
   5:        public static CardinalPoints ToCardinalMark(this Double degree)
   6:        {
   7:  
   8:            var CardinalRanges = new List<CardinalRanges>
   9:                       {
  10:                         new CardinalRanges {CardinalPoint = CardinalPoints.N, LowRange = 0, HighRange = 22.5},
  11:                         new CardinalRanges {CardinalPoint = CardinalPoints.NE, LowRange = 22.5, HighRange = 67.5},
  12:                         new CardinalRanges {CardinalPoint = CardinalPoints.E, LowRange = 67.5, HighRange = 112.5},
  13:                         new CardinalRanges {CardinalPoint = CardinalPoints.SE, LowRange = 112.5, HighRange = 157.5},
  14:                         new CardinalRanges {CardinalPoint = CardinalPoints.S, LowRange = 157.5, HighRange = 202.5},
  15:                         new CardinalRanges {CardinalPoint = CardinalPoints.SW, LowRange = 202.5, HighRange = 247.5},
  16:                         new CardinalRanges {CardinalPoint = CardinalPoints.W, LowRange = 247.5, HighRange = 292.5},
  17:                         new CardinalRanges {CardinalPoint = CardinalPoints.NW, LowRange = 292.5, HighRange = 337.5},
  18:                         new CardinalRanges {CardinalPoint = CardinalPoints.N, LowRange = 337.5, HighRange = 360.1}
  19:                       };
  20:  
  21:  
  22:            if (!(degree >= 0 && degree <= 360))
  23:                throw new ArgumentOutOfRangeException("degree",
  24:                                                      "Degree value must be greater than or equal to 0 and less than or equal to 360.");
  25:  
  26:  
  27:            return CardinalRanges.Find(p => (degree >= p.LowRange && degree < p.HighRange)).CardinalPoint;
  28:  
  29:  
  30:        }
It seems like there could be a better way to check the value ranges but I could not think of one.  One thought was to use a simple array of values, but I concluded clearer larger code was better for long term readability.

Unit tests

Some basic unit tests to cover the code.
   1: /// <summary>
   2:    /// Summary description for UnitTest1
   3:    /// </summary>
   4:    [TestFixture]
   5:    public class Maps
   6:    {
   7:  
   8:        [Test]
   9:        public void DistanceSamePoint0()
  10:        {
  11:            var distance = Helper.Distance(new Coordinate() { Latitude = 90, Longitude = -90 },
  12:                                           new Coordinate() { Latitude = 90, Longitude = -90 }, UnitsOfLength.Mile);
  13:  
  14:            Assert.AreEqual(0, distance, "");
  15:  
  16:        }
  17:  
  18:        [Test]
  19:        public void DistanceSampleDataNauticalMiles()
  20:        {
  21:            var distance = Helper.Distance(new Coordinate() { Latitude = 45, Longitude = 0 },
  22:                                           new Coordinate() { Latitude = 0, Longitude = 45 }, UnitsOfLength.NauticalMiles);
  23:  
  24:            Assert.AreEqual(3599.8653599999993d, distance, "");
  25:        }
  26:  
  27:  
  28:        [Test]
  29:        public void DistanceSampleDataMiles()
  30:        {
  31:            var distance = Helper.Distance(new Coordinate() { Latitude = 45, Longitude = 0 },
  32:                                           new Coordinate() { Latitude = 0, Longitude = 45 }, UnitsOfLength.Mile);
  33:  
  34:            Assert.AreEqual(4145.3999999999996d, distance, "");
  35:        }
  36:  
  37:        [Test]
  38:        public void DistanceSampleDataKilometer()
  39:        {
  40:            var distance = Helper.Distance(new Coordinate() { Latitude = 45, Longitude = 0 },
  41:                                           new Coordinate() { Latitude = 0, Longitude = 45 }, UnitsOfLength.Kilometer);
  42:  
  43:            Assert.AreEqual(6671.3746175999995d, distance, "");
  44:        }
  45:  
  46:        [Test]
  47:        public void BearingsTests()
  48:        {
  49:            var bearing = Helper.Bearing(new Coordinate() { Latitude = 45, Longitude = 1 },
  50:                                         new Coordinate() { Latitude = 45, Longitude = 0 });
  51:  
  52:            Assert.AreEqual(270.35355787806577d, bearing, "");
  53:            Assert.AreEqual(CardinalPoints.W, bearing.ToCardinalMark());
  54:  
  55:        }
  56:  
  57:        [Test]
  58:        public void CardinalMarkValues()
  59:        {
  60:            Assert.AreEqual(CardinalPoints.N, 2D.ToCardinalMark());
  61:            Assert.AreEqual(CardinalPoints.NE, 46D.ToCardinalMark());
  62:            Assert.AreEqual(CardinalPoints.SE, 120D.ToCardinalMark());
  63:            Assert.AreEqual(CardinalPoints.S, 170D.ToCardinalMark());
  64:            Assert.AreEqual(CardinalPoints.SW, 220D.ToCardinalMark());
  65:            Assert.AreEqual(CardinalPoints.W, 273D.ToCardinalMark());
  66:            Assert.AreEqual(CardinalPoints.NW, 320D.ToCardinalMark());
  67:  
  68:        }
  69:  
  70:  
  71:  
  72:        [Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
  73:        public void ToCardinalMarkOutOfRange() { 390D.ToCardinalMark(); }
  74:  
  75:        [Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
  76:        public void CoordinateLatitudeGreater90() { var coordinate = new Coordinate() { Latitude = 100, Longitude = -90 }; }
  77:  
  78:        [Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
  79:        public void CoordinateLatitudeLessN90() { var coordinate = new Coordinate() { Latitude = -91, Longitude = -90 }; }
  80:  
  81:        [Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
  82:        public void CoordinateLongitudeGreater180() { var coordinate = new Coordinate() { Latitude = 90, Longitude = 190 }; }
  83:  
  84:        [Test, ExpectedException(typeof(ArgumentOutOfRangeException))]
  85:        public void CoordinateLongitudeLessN180() { var coordinate = new Coordinate() { Latitude = 90, Longitude = -190 }; }
  86:  
  87:  
  88:    }

Summary

Hope this helps someone out there.  Let me know if there is anything I left out.

 

Bryan Reynolds

C# Developer

 

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 3.2 by 44 people

  • Currently 3.181818/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by Bryan on Monday, June 02, 2008 12:01 AM
Permalink | Comments (3) | Post RSSRSS comment feed

Related posts

Comments

Mick Walker gb

Thursday, June 04, 2009 11:13 PM

Mick Walker

I like you're code, I was just wondering if you would have any examples of converting X and Y coordinates to Lat and Long

ChuckC us

Tuesday, August 11, 2009 3:03 PM

ChuckC

Bryan, thank you! I have a .net app that calculates the distance from a billboard to the scene of an accident. The company I work for owns digital billboards and also a traffic reporting service. kind of cool! anyway, I needed something to calculate the distance from the billboard to the accident... I have done that... but then they said...well we want to know if the accident is in front of the billboard, or behind it. SO, I googled for this info, and yours was the first hit. I hope to try it out tomorrow. This is a static .exe app, not asp net but I am sure it would still work. I am not mapping anything, but I might want to do that later. It seems you need to do Java to do the mapping, is that correct? I can host a asp .net app, but for the most part, I just need an exe that runs on a server as this is a utility app, not a forward facing web app.

Divyesh Chapaneri in

Thursday, November 26, 2009 7:38 PM

Divyesh Chapaneri

Hi,

There is one coordinate as for example 19.447754, 72.75541 now I want list or coordinates that covers circular area and it has center coordinate as I have given above.

Means it should give me circumference or diameter and I will pass miles/kilometers.

Is there any api/web services or any method/function/formula to achieve this?

Help me out !

Thanks, Smile
Divyesh Chapaneri