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 2010

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 5.0 by 4 people

  • Currently 5/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

Using LINQ to SQL instead of a sub report.

The post described how to use "Linq to SQL" with XtraReports and DevExpress for displaying relational data within a column of a report.

 

As a developer's we have all made many reports and used many tools to generate reports. Currently I am working with DevExpress's XtraReports Suite and "LINQ to SQL".  For web based relational database system reporting there tools are pretty easy to work with. 

 

Below is a sample output from a report that I am working on.  The interesting challenge was the 1 to many relationship within the Competitor column.

 

  XtraReport1

 

The competitor column needs to report all competitors from a 1 to many relationship table. There are many possible solutions to this common problem.  Using XtraReports you can make a subreport, you could set the text of the control by pulling the data from the database, or you could have a stored procedure format that data in the SQL engine you use prior to printing the report.

 

I chose to use "LINQ to SQL" for its quick implementation and ease of use.  If you have not had a chance to look at this technology take a look at Scott Gu's post.

 

In shorts order I was able to have a strongly type set of code that loaded a string with the exact format I needed to solve the problem.

   1: private void Detail_BeforePrint(object sender, System.Drawing.Printing.PrintEventArgs e)
   2:    {
   3:        var projectID = Convert.ToInt32(GetCurrentColumnValue("ProjectID"));
   4:        var db = new dbDataContext();
   5:  
   6:        var queryCompitetion = from recs in db.ProjectCompetitions
   7:                               join comprecs in db.Competitions on recs.CompetitionID equals comprecs.CompetitionID
   8:                               where recs.ProjectID == projectID
   9:                               select new
  10:                                          {
  11:                                              recs.Distance,
  12:                                              recs.Direction,
  13:                                              comprecs.Name,
  14:  
  15:                                          };
  16:  
  17:        var stringBuiler = new StringBuilder();
  18:  
  19:        for (int i = 0; i < queryCompitetion.ToList().Count-1; i++)
  20:        {
  21:            var recs = queryCompitetion.ToList()[i];
  22:            stringBuiler.AppendFormat("{0}/ {1}{2}    ", recs.Name, recs.Distance, recs.Direction);
  23:  
  24:            if ((i + 1) % 2 == 0) stringBuiler.Append("\r\n");
  25:        }
  26:  
  27:        
  28:        xrCompetitorCell.Text = stringBuiler.ToString();
  29:    }

 

The "Detail_BeforePrint" function fires off before each report detail line is displayed.   Using the GetCurrentColumnValue function I retrieved the ProjectID for the current line.  Used "Linq to SQL" to grab the appropriate data using strong typing.  Iterated through the result and add the formatted string to the "xrCompetitorCell".  The "xrCompetitorCell" is a property of the report and generated via DevExpress's designer.

 

Hope this was useful from you.

 

Bryan

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

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

Posted by Bryan on Wednesday, April 30, 2008 7:21 AM
Permalink | Comments (1) | Post RSSRSS comment feed

LLBLGen Pro and Microsoft’s Dynamic Data will play nice!

I am a big fan of LLBLGen Pro, an n-tier generator and O/R mapper, written mostly by Frans Bouma.  For those of you who are not familiar with this product the true value is its creator and evangelist Frans Bouma.  The quality product, support and enthusiasm brought by him and his organization is refreshing.

Also recently Microsoft has been working on a ASP.NET extension called Dynamic Data.  Dynamic Data currently was scheduled to work with Microsoft O/R mapping initiatives LINQ to SQL and the Entity Framework.  Dynamic Data is designed to allow developers to quickly create CRUD pages surrounded by a base scaffolding with a central place to update UI and Validation logic.  I know that was a mouthful, and if you are looking for more information there are numerous blogs and sites that you can look at to get familiar with this technology. Some of the more prominent links are below.

Good news!

Both Dynamic Data and LLBLgen will be playing nice.  It started about a month ago when I was talking David Ebbo whether or not the extension was going to work with other O/R tools and with Frans Bouma about Dynamic Data.  After I took Frans through a demo he immediately contacted Microsoft to make it happen.  Today I got the email from him telling me they were going to release the product with the appropriate API to allow his product to integrate with Dynamic Data.

I will hopefully have time to go through the bits Frans sent me today.  I am excited this produced a situation where I get what I believe to be the best of both worlds, for small projects.

Microsoft was not planning on releasing this API until this happened and I applaud both Microsoft and Frans for bringing the tools together for the community to enjoy.

Thanks!

Bryan Reynolds

C# Developer

Untitled

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 5.0 by 2 people

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

Posted by Bryan on Friday, April 25, 2008 3:29 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Dynamic Data - New Code Drop

If you have not had a chance to check out Microsoft's DynamicData you should.  I have been working on the latest bits via Microsoft Connect and I have been pleased.  It does not fit every need of course.  Its not "Enterprise Level" and we can debate what that means for ever.  Suffice it to say,  if you have a large complex architecture with many developers,  this is not the tool.  Good news is not all projects are like that.  For the many small applications that offices and companies of all size need on a regular basis.  This will fill the bill. 

Below with virtually no code I was able to modify the default templates to make a customer detail screen that shows the customers jobs.  The field captions all the way to the validation are described in a single location to reduce the amount of work you have to do.  Its all about being lazy!  Or better yet code reduction without functionality reduction.

You can see links on the left below that shows a list of links to function of this tiny program.  After talking with the client for about 3 hours.  I generated in 30 minutes a complete site for data entry.  As the customer requested changes I was able to quickly respond to there requests. 

DynamicData1 

If a field is required via a not null property on your data field via SQL Server.  Then the data validation on the screen is linked as well.

New Picture

Conclusion

Looks like Microsoft is listening to there customers.  As a software developer you must take a look at this tool.

Bryan MCPD

C# Developer

Untitled

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

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

Categories: ASP.NET | Dynamic Data
Posted by Bryan on Thursday, April 10, 2008 8:34 AM
Permalink | Comments (1) | Post RSSRSS comment feed

Fraudulent Companies

Yesterday I got a frantic call from one of our clients. They were concerned that one of the marketing campaigns we manage through our lead system was not performing. They mentioned after looking at some reports they were concerned that this campaign had shut down. Leads were being submitted perfectly fine one day then not at all the next. They thought the system was broken. It was a valid question. I investigated.

During the conversation I looked at the reports myself and quickly realized that someone was messing with us. They were submitting leads at an almost 1 for 1 basis to visits. This was not the pattern we saw for any other lead campaign. 50 visits to a lead was more realistic. This campaign was not only performing at an almost 1 to 1 basis, but all leads were valid on entry. This is of course not realistic either. Normally we see a 5 to 1 ratio on a user submitting a lead and it passing all the validation checks to make sure it’s a real lead. This is not standard for everyone, I am sure. Depending on the validation logic your system has the numbers can be very different. Either way, this was a problem.

We are dealing with this now by adding preventative logic to the system.

Why do they do it? What can be done with people like this?

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Currently rated 1.0 by 1 people

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

Posted by Bryan on Tuesday, March 18, 2008 7:25 AM
Permalink | Comments (2) | Post RSSRSS comment feed

How-To: New ASP.NET 3.5 Extensions Video Screencasts

Scott Hanselman's Just posted some new videos on ASP.NET 3.5 Extensions. 

Click Here

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

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

Tags: , , ,
Categories: AJAX | ASP.NET | LINQ | MVC
Posted by Bryan on Friday, December 21, 2007 2:22 PM
Permalink | Comments (0) | Post RSSRSS comment feed