Click here to Skip to main content
12,068,508 members (67,245 online)
Click here to Skip to main content
Add your own
alternative version


77 bookmarked

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

, 9 Jan 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
Vincenty's Formula is an iterative solution for calculating the distance and direction between two points along the surface of Earth.


Vincenty's Formula is an iterative solution for calculating the distance and direction between two points along the surface of Earth. For clarity, I've stripped out portions of the code I've put up for discussion, but you can download the entire C# source code from the link at the top of this article.


Several years ago, I stumbled on a great pastime called "geocaching[^]." It's a worldwide treasure hunting game where participants use handheld GPS receivers to find hidden "caches" - small boxes filled with prizes, trinkets, and "travel bugs[^]". The caches are hidden by other participants who post nothing more than the latitude and longitude on a website like[^]. My children and I have had a blast. It's a great way for a grown man to justify playing in the woods (and buying an expensive gadget!) under the pretence of "playing with the kids." With over 420,000 caches in 222 countries on all continents (including Antarctica!) there are bound to be several near you.

Want to know more? Check out this video of my son on a geocache hunt[^].

So, being the true geek I am, I coded up a bunch of applications to record where we've been, where we'd like to go, and interesting waypoints along the way. Often, I'd ask the question "How far is it from here to there?"

The Geodetic Inverse Problem

"How far?" turns out to be a fairly complicated question. For centuries, humankind has known that Earth is round - but everything else about the shape of Earth is less understood. In fact, an entire field called "geodesy[^]", or "geodetics" focuses on understanding the shape of Earth.

If we assume that Earth is a perfect sphere, the math is easy. Given the size of Earth and the latitude and longitude of two points, we can quickly calculate the "great circle[^]" distance and direction from one point to the next.

But, Earth is 'not' a perfect sphere. It's an irregular shape geodesists call the "geoid." Because precise measurements of the geoid and the corresponding distance calculations would be impractical, the geoid is often modeled as an ellipsoid - an object resembling a sphere but slightly flattened at the poles. It's still an inexact model, but it performs far better than the spherical model.

That's where the math gets hard. The Geodetic Inverse Problem - finding the distance and direction from one point to another along an ellipsoid - does not have a closed form solution. However, in 1975, Thaddeus Vincenty[^] developed an extremely accurate iterative solution for which he won the Department of Commerce Medal for Meritorious Service. If you want to sprain your brain, you can read the full publication of his solution[^].

Or, you can just download my C# implementation of Vincenty's Formula[^].

Building the Code

I'll start with a convenient type for encapsulating angles. We'll need this for latitudes and longitudes. It might seem to be an overkill since we could simply uses double instead of a custom Angle type, but humans think in "degrees" while computers do math in "radians." Rather than risk confusion and sprinkle conversion code throughout, I thought this would be easier:

namespace Gavaghan.Geodesy
    public struct Angle : IComparable<angle />
        private const double PiOver180 = Math.PI / 180.0;
        private double mDegrees;

        static public readonly Angle Zero = new Angle(0);
        static public readonly Angle Angle180 = new Angle(180);

        public Angle( double degrees )
              mDegrees = degrees;

        public double Degrees
                return mDegrees;
                mDegrees = value;

        public double Radians
                return mDegrees * PiOver180;
                mDegrees = value / PiOver180;

        // Equals, IComparable<T> implementation and
        // comparison operators omitted for clarity.
        // See full source code download

Some critical portions of the Angle type above are omitted for clarity - like all of the comparison operators. You can check out the complete implementation in the source code download[^].

Next, we have a class that encapsulates a location on an ellipsoid.

namespace Gavaghan.Geodesy
    public struct GlobalCoordinates : IComparable<GlobalCoordinates>
        private Angle mLatitude;
        private Angle mLongitude;

        public GlobalCoordinates( Angle latitude, Angle longitude )
            mLatitude = latitude;
            mLongitude = longitude;

        public Angle Latitude
                return mLatitude;
                mLatitude = value;

        public Angle Longitude
                return mLongitude;
                mLongitude = value;

Negative latitudes are in the southern hemisphere, and negative longitudes are in the western hemisphere. Once again, the IComparable<T> implementation is omitted for clarity.

Now, we start getting to the fun part. The next type encapsulates the "geodetic curve."

namespace Gavaghan.Geodesy
    public struct GeodeticCurve
        private readonly double mEllipsoidalDistance;
        private readonly Angle mAzimuth;
        private readonly Angle mReverseAzimuth;

        public GeodeticCurve( double ellipsoidalDistance,
        Angle azimuth, Angle reverseAzimuth )
            mEllipsoidalDistance = ellipsoidalDistance;
            mAzimuth = azimuth;
            mReverseAzimuth = reverseAzimuth;

        public double EllipsoidalDistance
                return mEllipsoidalDistance;

        public Angle Azimuth
                return mAzimuth;

        public Angle ReverseAzimuth
                return mReverseAzimuth;

A "geodetic curve" is the solution we're looking for. It describes how to get from one point on an ellipsoid to another. The ellipsoidal distance is the distance, in meters, between the two points along the surface of the ellipsoid. The azimuth is the direction of travel from the starting point to the ending point. The reverse azimuth, of course, is the direction back from the endpoint (which isn't necessarily a 180 degree turn on an ellipsoid).

The final input we need to Vincenty's Formula is an ellipsoid. We hold onto four parameters of an ellipsoid: the length of each semi-axis (in meters), the flattening ratio, and the inverse of the flattening ratio. Technically, we only need the length of one semi-axis and any one of the other three parameters. We'll record all four for convenience.

namespace Gavaghan.Geodesy
    public struct Ellipsoid
        private readonly double mSemiMajorAxis;
        private readonly double mSemiMinorAxis;
        private readonly double mFlattening;
        private readonly double mInverseFlattening;

        private Ellipsoid(double semiMajor, double semiMinor,
                        double flattening, double inverseFlattening)
            mSemiMajorAxis = semiMajor;
            mSemiMinorAxis = semiMinor;
            mFlattening = flattening;
            mInverseFlattening = inverseFlattening;

        static public readonly Ellipsoid WGS84 =
            FromAAndInverseF( 6378137.0, 298.257223563 );
        static public readonly Ellipsoid GRS80 =
            FromAAndInverseF( 6378137.0, 298.257222101 );
        static public readonly Ellipsoid GRS67 =
            FromAAndInverseF( 6378160.0, 298.25 );
        static public readonly Ellipsoid ANS   =
            FromAAndInverseF( 6378160.0, 298.25 );
        static public readonly Ellipsoid Clarke1880 =
            FromAAndInverseF( 6378249.145, 293.465 );

        static public Ellipsoid FromAAndInverseF
            ( double semiMajor, double inverseFlattening )
            double f = 1.0 / inverseFlattening;
            double b = (1.0 - f) * semiMajor;
            return new Ellipsoid(semiMajor, b, f, inverseFlattening);

        public double SemiMajorAxis
                return mSemiMajorAxis;

        public double SemiMinorAxis
            return mSemiMinorAxis;

        public double Flattening
            return mFlattening;

        public double InverseFlattening
                return mInverseFlattening;

Generally, you won't need to specify ellipsoid parameters. The Ellipsoid type has a number of static instances that define "reference ellipsoids." Reference ellipsoids represent some organization's consensus on the "best" ellipsoidal parameters to use to model Earth. Two of the most widely accepted reference ellipsoids are defined above: WGS84 (the 1984 World Geodetic System[^]) and GRS80 (the 1980 Geodetic Reference System[^]).

Finally, we have the class that actually implements Vincenty's Formula to solve the Geodetic Inverse Problem given a reference ellipsoid and two sets of global coordinates (equation numbers in comments relate directly to Vincenty's publication[^]):

namespace Gavaghan.Geodesy
    public class GeodeticCalculator
        private const double TwoPi = 2.0 * Math.PI;

        public GeodeticCurve CalculateGeodeticCurve( Ellipsoid ellipsoid,
                     GlobalCoordinates start, GlobalCoordinates end )
            // get constants
            double a = ellipsoid.SemiMajorAxis;
            double b = ellipsoid.SemiMinorAxis;
            double f = ellipsoid.Flattening;

            // get parameters as radians
            double phi1 = start.Latitude.Radians;
            double lambda1 = start.Longitude.Radians;
            double phi2 = end.Latitude.Radians;
            double lambda2 = end.Longitude.Radians;

            // calculations
            double a2 = a * a;
            double b2 = b * b;
            double a2b2b2 = (a2 - b2) / b2;

            double omega = lambda2 - lambda1;

            double tanphi1 = Math.Tan(phi1);
            double tanU1 = (1.0 - f) * tanphi1;
            double U1 = Math.Atan(tanU1);
            double sinU1 = Math.Sin(U1);
            double cosU1 = Math.Cos(U1);

            double tanphi2 = Math.Tan(phi2);
            double tanU2 = (1.0 - f) * tanphi2;
            double U2 = Math.Atan(tanU2);
            double sinU2 = Math.Sin(U2);
            double cosU2 = Math.Cos(U2);

            double sinU1sinU2 = sinU1 * sinU2;
            double cosU1sinU2 = cosU1 * sinU2;
            double sinU1cosU2 = sinU1 * cosU2;
            double cosU1cosU2 = cosU1 * cosU2;

            // eq. 13
            double lambda = omega;

            // intermediates we'll need to compute 's'
            double A = 0.0;
            double B = 0.0;
            double sigma = 0.0;
            double deltasigma = 0.0;
            double lambda0;
            bool converged = false;

            for (int i = 0; i < 10; i++)
                lambda0 = lambda;

                double sinlambda = Math.Sin(lambda);
                double coslambda = Math.Cos(lambda);

                // eq. 14
                double sin2sigma = (cosU2 * sinlambda * cosU2 * sinlambda) +
                     (cosU1sinU2 - sinU1cosU2 * coslambda) *
                     (cosU1sinU2 - sinU1cosU2 * coslambda);

                double sinsigma = Math.Sqrt(sin2sigma);

                // eq. 15
                double cossigma = sinU1sinU2 + (cosU1cosU2 * coslambda);

                // eq. 16
                sigma = Math.Atan2(sinsigma, cossigma);

                // eq. 17 Careful! sin2sigma might be almost 0!
                double sinalpha = (sin2sigma == 0) ? 0.0 :
                      cosU1cosU2 * sinlambda / sinsigma;

                double alpha = Math.Asin(sinalpha);
                double cosalpha = Math.Cos(alpha);
                double cos2alpha = cosalpha * cosalpha;

                // eq. 18 Careful! cos2alpha might be almost 0!
                double cos2sigmam = cos2alpha == 0.0 ? 0.0 :
                    cossigma - 2 * sinU1sinU2 / cos2alpha;

                double u2 = cos2alpha * a2b2b2;

                double cos2sigmam2 = cos2sigmam * cos2sigmam;

                // eq. 3
                A = 1.0 + u2 / 16384 * (4096 + u2 *
                    (-768 + u2 * (320 - 175 * u2)));

                // eq. 4
                B = u2 / 1024 * (256 + u2 * (-128 + u2 * (74 - 47 * u2)));

                // eq. 6
                deltasigma = B * sinsigma * (cos2sigmam + B / 4
                    * (cossigma * (-1 + 2 * cos2sigmam2) - B / 6
                    * cos2sigmam * (-3 + 4 * sin2sigma)
                    * (-3 + 4 * cos2sigmam2)));

                // eq. 10
                double C = f / 16 * cos2alpha * (4 + f * (4 - 3 * cos2alpha));

                // eq. 11 (modified)
                lambda = omega + (1 - C) * f * sinalpha
                    * (sigma + C * sinsigma * (cos2sigmam + C
                    * cossigma * (-1 + 2 * cos2sigmam2)));

                // see how much improvement we got
                double change = Math.Abs((lambda - lambda0) / lambda);

                if ((i > 1) && (change < 0.0000000000001))
                     converged = true;

            // eq. 19
            double s = b * A * (sigma - deltasigma);
            Angle alpha1;
            Angle alpha2;

            // didn't converge? must be N/S
            if (!converged)
                if (phi1 > phi2)
                    alpha1 = Angle.Angle180;
                    alpha2 = Angle.Zero;
                else if (phi1 < phi2)
                    alpha1 = Angle.Zero;
                    alpha2 = Angle.Angle180;
                    alpha1 = new Angle(Double.NaN);
                    alpha2 = new Angle(Double.NaN);
            // else, it converged, so do the math
                double radians;
                alpha1 = new Angle();
                alpha2 = new Angle();

                // eq. 20
                radians = Math.Atan2(cosU2 * Math.Sin(lambda),
                (cosU1sinU2 - sinU1cosU2 * Math.Cos(lambda)));

                if (radians < 0.0) radians += TwoPi;
                alpha1.Radians = radians;

                // eq. 21
                radians = Math.Atan2(cosU1 * Math.Sin(lambda),
                        (-sinU1cosU2 + cosU1sinU2 *
                Math.Cos(lambda))) + Math.PI;

                if (radians < 0.0) radians += TwoPi;
                alpha2.Radians = radians;

            return new GeodeticCurve(s, alpha1, alpha2);

There you have it! You have the tools you need to know the answer to the question "How far is it from here to there?"

Here's an example of using the code to calculate how far it is from the Lincoln Memorial[^] in Washington, D.C. to the Eiffel Tower[^] in Paris:

using System;
using System.Text;
using Gavaghan.Geodesy;

namespace Gavaghan.Geodesy.Example
    public class Example
        static void Main()
            // instantiate the calculator
            GeodeticCalculator geoCalc = new GeodeticCalculator();

            // select a reference elllipsoid
            Ellipsoid reference = Ellipsoid.WGS84;

            // set Lincoln Memorial coordinates
            GlobalCoordinates lincolnMemorial;
            lincolnMemorial = new GlobalCoordinates( new Angle(38.88922),
            new Angle(-77.04978) );

            // set Eiffel Tower coordinates
            GlobalCoordinates eiffelTower;
            eiffelTower = new GlobalCoordinates( new Angle(48.85889),
            new Angle(2.29583) );

            // calculate the geodetic curve
            GeodeticCurve geoCurve = geoCalc.CalculateGeodeticCurve(
                    reference, lincolnMemorial, eiffelTower );

            double ellipseKilometers = geoCurve.EllipsoidalDistance / 1000.0;

            Console.WriteLine("Lincoln Memorial to Eiffel Tower using WGS84");
            Console.WriteLine("  Ellipsoidal Distance: {0:0.00} kilometers",
                    ellipseKilometers );
            Console.WriteLine("  Azimuth: {0:0.00} degrees",
                    geoCurve.Azimuth.Degrees );
            Console.WriteLine("  Reverse Azimuth: {0:0.00} degrees",
                    geoCurve.ReverseAzimuth.Degrees );

The downloadable library also supports 3-D geodetic calculations (measurements that account for the elevation above or below the reference ellipsoid). For complete source code, documentation, and examples, download the entire C# library[^].

Next Steps

The Vincenty implementation is just one of many useful tools for building a wide variety of applications to support the use of GPS receivers. I think mobile Web apps would be a great choice for GPS users traipsing through the outdoors. I plan to blog about[^] additional tools and ideas I'll be putting together based on my own experiences and reader feedback. Until then, buy your own GPS receiver and go play in the woods!


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


About the Author

Mike Gavaghan
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[^].

You may also be interested in...

Comments and Discussions

QuestionVincenty Formula Pin
Member 84463421-May-12 4:54
memberMember 84463421-May-12 4:54 
GeneralMy vote of 5 Pin
see_seA15-Jun-11 6:11
membersee_seA15-Jun-11 6:11 
GeneralWell done! Pin
see_seA15-Jun-11 6:10
membersee_seA15-Jun-11 6:10 
General3D direct calculation Pin
dash832-Apr-09 7:02
memberdash832-Apr-09 7:02 
GeneralPB to understand alpha1 and alpha2 of the article of Vincentry Pin
estrade30-Jul-08 11:33
memberestrade30-Jul-08 11:33 
GeneralFirst rate job! Pin
Dave1975199528-Sep-07 6:41
memberDave1975199528-Sep-07 6:41 
GeneralVery nice work + an addition Pin
iq-man14-Sep-07 8:20
memberiq-man14-Sep-07 8:20 
Generalerror estimation Pin
Friedhelm Schuetz14-Aug-07 0:01
memberFriedhelm Schuetz14-Aug-07 0:01 
GeneralRe: error estimation Pin
Mike Gavaghan14-Aug-07 2:48
memberMike Gavaghan14-Aug-07 2:48 
GeneralLooks like GFC# Pin
Sam Blackburn9-Aug-07 3:45
memberSam Blackburn9-Aug-07 3:45 
Geodesy Foundation Classes in C# at

It also does these calculations and a little bit more. There's also a C++ NMEA0183 library there so you can take data directly from a GPS receiver.


GeneralNice one there... Pin
Paul Selormey8-Aug-07 19:12
memberPaul Selormey8-Aug-07 19:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.160208.1 | Last Updated 9 Jan 2008
Article Copyright 2007 by Mike Gavaghan
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid