Click here to Skip to main content
15,892,746 members
Articles / Programming Languages / C#

GPS Receivers, Geodesy, and Geocaching: Vincenty’s Formula

Rate me:
Please Sign up or sign in to vote.
5.00/5 (21 votes)
9 Jan 2008CPOL5 min read 94.9K   698   79  
Vincenty's Formula is an iterative solution for calculating the distance and direction between two points along the surface of Earth.
/* Gavaghan.Geodesy by Mike Gavaghan
 * 
 * http://www.gavaghan.org/blog/free-source-code/geodesy-library-vincentys-formula/
 * 
 * This code may be freely used and modified on any personal or professional
 * project.  It comes with no warranty.
 */
using System;
using System.Text;

namespace Gavaghan.Geodesy
{
  /// <summary>
  /// Encapsulation of latitude and longitude coordinates on a globe.  Negative
  /// latitude is southern hemisphere.  Negative longitude is western hemisphere.
  /// 
  /// Any angle may be specified for longtiude and latitude, but all angles will
  /// be canonicalized such that:
  /// 
  ///      -90 <= latitude <= +90
  ///     -180 < longitude <= +180
  /// </summary>
  [Serializable]
  public struct GlobalCoordinates : IComparable<GlobalCoordinates>
  {
    /// <summary>Latitude.  Negative latitude is southern hemisphere.</summary>
    private Angle mLatitude;

    /// <summary>Longitude.  Negative longitude is western hemisphere.</summary>
    private Angle mLongitude;

    /// <summary>
    /// Canonicalize the current latitude and longitude values such that:
    /// 
    ///      -90 <= latitude <= +90
    ///     -180 < longitude <= +180
    /// </summary>
    private void Canonicalize()
    {
      double latitude = mLatitude.Degrees;
      double longitude = mLongitude.Degrees;

      latitude = (latitude + 180) % 360;
      if (latitude < 0) latitude += 360;
      latitude -= 180;

      if (latitude > 90)
      {
        latitude = 180 - latitude;
        longitude += 180;
      }
      else if (latitude < -90)
      {
        latitude = -180 - latitude;
        longitude += 180;
      }

      longitude = ((longitude + 180) % 360);
      if (longitude <= 0) longitude += 360;
      longitude -= 180;

      mLatitude = new Angle(latitude);
      mLongitude = new Angle(longitude);
    }

    /// <summary>
    /// Construct a new GlobalCoordinates.  Angles will be canonicalized.
    /// </summary>
    /// <param name="latitude">latitude</param>
    /// <param name="longitude">longitude</param>
    public GlobalCoordinates(Angle latitude, Angle longitude)
    {
      mLatitude = latitude;
      mLongitude = longitude;
      Canonicalize();
    }

    /// <summary>
    /// Get/set latitude.  The latitude value will be canonicalized (which might
    /// result in a change to the longitude). Negative latitude is southern hemisphere.
    /// </summary>
    public Angle Latitude
    {
      get { return mLatitude; }
      set 
      { 
        mLatitude = value; 
        Canonicalize(); 
      }
    }

    /// <summary>
    /// Get/set longitude.  The longitude value will be canonicalized. Negative
    /// longitude is western hemisphere.
    /// </summary>
    public Angle Longitude
    {
      get { return mLongitude; }
      set 
      { 
        mLongitude = value; 
        Canonicalize(); 
      }
    }

    /// <summary>
    /// Compare these coordinates to another set of coordiates.  Western
    /// longitudes are less than eastern logitudes.  If longitudes are equal,
    /// then southern latitudes are less than northern latitudes.
    /// </summary>
    /// <param name="other">instance to compare to</param>
    /// <returns>-1, 0, or +1 as per IComparable contract</returns>
    public int CompareTo(GlobalCoordinates other)
    {
      int retval;

      if (mLongitude < other.mLongitude) retval = -1;
      else if (mLongitude > other.mLongitude) retval = +1;
      else if (mLatitude < other.mLatitude) retval = -1;
      else if (mLatitude > other.mLatitude) retval = +1;
      else retval = 0;

      return retval;
    }

    /// <summary>
    /// Get a hash code for these coordinates.
    /// </summary>
    /// <returns></returns>
    public override int GetHashCode()
    {
      return ((int)(mLongitude.GetHashCode() * (mLatitude.GetHashCode() + 1021))) * 1000033;
    }

    /// <summary>
    /// Compare these coordinates to another object for equality.
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public override bool Equals(object obj)
    {
      if (!(obj is GlobalCoordinates)) return false;

      GlobalCoordinates other = (GlobalCoordinates)obj;

      return (mLongitude == other.mLongitude) && (mLatitude == other.mLatitude);
    }

    /// <summary>
    /// Get coordinates as a string.
    /// </summary>
    /// <returns></returns>
    public override string ToString()
    {
      StringBuilder builder = new StringBuilder();

      builder.Append(mLatitude.Abs().ToString());
      builder.Append((mLatitude >= Angle.Zero) ? 'N' : 'S');
      builder.Append(';');
      builder.Append(mLongitude.Abs().ToString());
      builder.Append((mLongitude >= Angle.Zero) ? 'E' : 'W');
      builder.Append(';');

      return builder.ToString();
    }
  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Mike Gavaghan opines on C# and .Net in his blog Talk Nerdy To Me[^]. He is a Microsoft Certified Professional Developer working as a C#/.Net software consultant based in Dallas, Texas.

Since 1992, he has supported clients in diverse businesses including financial services, travel, airlines, and telecom. He has consulted at both mammoth enterprises and small startups (and sees merits and problems in both).

You may also view his profile on LinkedIn[^].

Comments and Discussions