The library can be used to solve geodesic and rhumb line calculations, convert between geographic, UTM, UPS, MGRS, geocentric, and local cartesian coordinates, and solve gravity and geomagnetic field calculations.

Implemented in C++/CLI to provide access to the functionality of the GeographicLib library.

Harlinn.GeographicLib.Net works with the release build of Harlinn.GeographicLib.

The information below was copied from the original GeographicLib documentation and altered slightly, as Harlinn.GeographicLib.Net will mostly be used from apps written in C#.

Geodesic calculations

The shortest path between two points on an ellipsoid at (lat1,lon1) and (lat2,lon2) is called the geodesic. Its length is s12 and the geodesic from point 1 to point 2 has azimuths azi1 and azi2 at the two end points. (The azimuth is the heading measured clockwise from north. azi2 is the “forward” azimuth, i.e., the heading that takes you beyond point 2 not back to point 1.) In the figure below, latitude is labeled φ, longitude λ (with λ12=λ2λ1), and azimuth α.

Given lat1, lon1, azi1, and s12, we can determine lat2, lon2, and azi2. This is the direct geodesic problem and its solution is given by the function Geodesic.Direct. (If s12 is sufficiently large that the geodesic wraps more than halfway around the earth, there will be another geodesic between the points with a smaller s12.)

Given lat1, lon1, lat2, and lon2, we can determine azi1, azi2, and s12. This is the inverse geodesic problem, whose solution is given by Geodesic.Inverse. Usually, the solution to the inverse problem is unique. In cases where there are multiple solutions (all with the same s12, of course), all the solutions can be easily generated once a particular solution is provided.

The standard way of specifying the direct problem is the specify the distance s12 to the second point. However it is sometimes useful instead to specify the arc length a12 (in degrees) on the auxiliary sphere. This is a mathematical construct used in solving the geodesic problems. The solution of the direct problem in this form is provided by Geodesic.ArcDirect. An arc length in excess of 180° indicates that the geodesic is not a shortest path. In addition, the arc length between an equatorial crossing and the next extremum of latitude for a geodesic is 90°.

This class can also calculate several other quantities related to geodesics. These are:

  • reduced length. If we fix the first point and increase azi1 by dazi1 (radians), the second point is displaced m12dazi1 in the direction azi2+90°. The quantity m12 is called the “reduced length” and is symmetric under interchange of the two points. On a curved surface the reduced length obeys a symmetry relation, m12+m21=0. On a flat surface, we have m12=s12. The ratio s12/m12 gives the azimuthal scale for an azimuthal equidistant projection.
  • geodesic scale. Consider a reference geodesic and a second geodesic parallel to this one at point 1 and separated by a small distance dt. The separation of the two geodesics at point 2 is M12dt where M12 is called the “geodesic scale”. M21 is defined similarly (with the geodesics being parallel at point 2). On a flat surface, we have M12=M21=1. The quantity 1/M12 gives the scale of the Cassini-Soldner projection.
  • area. The area between the geodesic from point 1 to point 2 and the equation is represented by S12; it is the area, measured counter-clockwise, of the geodesic quadrilateral with corners (lat1,lon1), (0,lon1), (0,lon2), and (lat2,lon2). It can be used to compute the area of any geodesic polygon.

Overloaded versions of Geodesic.Direct, Geodesic.ArcDirect, and Geodesic.Inverse allow these quantities to be returned. In addition there are general functions Geodesic.GenDirect, and Geodesic.GenInverse which allow an arbitrary set of results to be computed. The quantities m12, M12, M21 which all specify the behavior of nearby geodesics obey addition rules. If points 1, 2, and 3 all lie on a single geodesic, then the following rules hold:

s13=s12+s23a13=a12+a23S13=S12+S23m13=m12M23+m23M21M13=M12M23(1M12M21)m23/m12M31=M32M21(1M23M32)m12/m23

Additional functionality is provided by the GeodesicLine class, which allows a sequence of points along a geodesic to be computed.

Geodesic example

using Harlinn.GeographicLib.Net;

namespace GeodesicExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Geodesic geod = Geodesic.WGS84;

            // Sample direct calculation, travelling about NE from JFK
            double lat1 = 40.6, lon1 = -73.8, s12 = 5.5e6, azi1 = 51;
            double lat2, lon2;
            geod.Direct(lat1, lon1, azi1, out s12, out lat2, out lon2);

            Console.WriteLine($"Latitude: {lat2}, Longitude:{lon2}");

            // Sample inverse calculation, JFK to LHR

            // JFK Airport
            lat1 = 40.6;
            lon1 = -73.8;

            // LHR Airport
            lat2 = 51.6; 
            lon2 = -0.5;  
            
            geod.Inverse(lat1, lon1, lat2, lon2, out s12);

            Console.WriteLine($"Distance: {s12}");
        }
    }
}
Latitude: 51,88456450560677, Longitude:-1,1411728612008574
Distance: 5551759,400318679

GeodesicLine

GeodesicLine facilitates the determination of a series of points on a single geodesic. The starting point (lat1,lon1) and the azimuth azi1 are specified in the constructor; alternatively, the Geodesic::Line method can be used to create a GeodesicLine. GeodesicLine.Position returns the location of point 2 a distance s12 along the geodesic. In addition, GeodesicLine.ArcPosition gives the position of point 2 an arc length a12 along the geodesic.

You can register the position of a reference point 3 a distance (arc length), s13 (a13) along the geodesic with the GeodesicLine.SetDistance (GeodesicLine.SetArc) functions. Points a fractional distance along the line can be found by providing, for example, 0.5 * Distance as an argument to GeodesicLine.Position. The Geodesic.InverseLine or Geodesic.DirectLine methods return GeodesicLine objects with point 3 set to the point 2 of the corresponding geodesic problem. GeodesicLine objects created with the public constructor or with Geodesic.Line have s13 and a13 set to NaNs.

The calculations are accurate to better than 15 nm (15 nanometers). See Sec. 9 of arXiv:1102.1215v1 for details. With exact = false (the default) in the constructor for the Geodesic object, the algorithms used by this class are based on series expansions using the flattening f as a small parameter. These are only accurate for |f|<0.02; however reasonably accurate results will be obtained for |f|<0.2. For very eccentric ellipsoids, set exact = true in the constructor for the Geodesic object; this will delegate the calculations to GeodesicLineExact.

GeodesicLine example

using Harlinn.GeographicLib.Net;

namespace GeodesicLineExample
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Geodesic geod = Geodesic.WGS84;

            double
              // JFK airport
              lat1 = 40.640, lon1 = -73.779,
              // SIN airport
              lat2 = 1.359, lon2 = 103.989; 

            GeodesicLine line = geod.InverseLine(lat1, lon1, lat2, lon2);

            // Nominal distance between points = 500 km
            double ds0 = 500e3;

            // The number of intervals
            int num = (int)Math.Ceiling(line.Distance / ds0);

            double ds = line.Distance / num;
            for (int i = 0; i <= num; ++i)
            {
                double lat, lon;
                line.Position(i * ds, out lat, out lon);
                Console.WriteLine($"{i} -> Latitude: {lat}, Longitude: {lon} ");
            }
        }
    }
}
0 -> Latitude: 40,64, Longitude: -73,779
1 -> Latitude: 45,088624522948315, Longitude: -73,41639124870959
2 -> Latitude: 49,53243435521155, Longitude: -72,99265573206968
3 -> Latitude: 53,97096253429303, Longitude: -72,48430287040067
4 -> Latitude: 58,40353976434537, Longitude: -71,85489098898533
5 -> Latitude: 62,829059340035606, Longitude: -71,04452466778625
6 -> Latitude: 67,24547195373592, Longitude: -69,94724090357691
7 -> Latitude: 71,64856286681426, Longitude: -68,35623186483937
8 -> Latitude: 76,02845249253366, Longitude: -65,80772484891887
9 -> Latitude: 80,3567290132899, Longitude: -61,014890489628954
10 -> Latitude: 84,51420549018647, Longitude: -48,83492431665228
11 -> Latitude: 87,45474332671364, Longitude: 3,894014378845668
12 -> Latitude: 85,295259509238, Longitude: 71,94358213809562
13 -> Latitude: 81,22103811744249, Longitude: 87,60160059653242
14 -> Latitude: 76,91141529030632, Longitude: 93,21228048415551
15 -> Latitude: 72,53859298991506, Longitude: 96,06333035970746
16 -> Latitude: 68,13910398636213, Longitude: 97,79733028285946
17 -> Latitude: 63,72494286554836, Longitude: 98,97301258675924
18 -> Latitude: 59,301058336202594, Longitude: 99,83082330184723
19 -> Latitude: 54,86980145924297, Longitude: 100,49102303937627
20 -> Latitude: 50,432415581007945, Longitude: 101,02040715447234
21 -> Latitude: 45,98963410041732, Longitude: 101,45907366928466
22 -> Latitude: 41,54195282757638, Longitude: 101,83259447654174
23 -> Latitude: 37,089765877640964, Longitude: 102,15809755054522
24 -> Latitude: 32,633437629751434, Longitude: 102,44754031410375
25 -> Latitude: 28,17334176771984, Longitude: 102,70958072995484
26 -> Latitude: 23,709881876563497, Longitude: 102,95070239501462
27 -> Latitude: 19,243500881260854, Longitude: 103,17592172947425
28 -> Latitude: 14,774683272647815, Longitude: 103,38925110492858
29 -> Latitude: 10,303952429663326, Longitude: 103,59401483935383
30 -> Latitude: 5,831864516291208, Longitude: 103,79307473649479
31 -> Latitude: 1,3589999999999611, Longitude: 103,989