Distance underpins much of what TackTracker shows you: how far a boat has sailed, its speed, the length of each leg, and Navigation Rally results. This page explains how the default distance calculation works and how accurate it is.
It also gives every constant and formula needed to reproduce the calculation in your own software, with worked examples to check your results against.
By default, TackTracker uses the haversine formula, which gives the "great circle" distance between two points on a sphere. TackTracker adds two refinements to the basic Haversine formula:
Together, these keep the result within 0.001% of the exact distance on the WGS-84 ellipsoid, for segments up to 500 km. That is 1 cm per kilometre. We believe this algorithm provides the best balance of accuracy and performance for typical sailing applications.
The TackTracker desktop player adds a message to the lower left corner of the map to advise when an alternative distance method is in use.
If you would like to reproduce TackTracker's results, this is a step by step description of the algorithm.
The inputs are two position coordinates in decimal degrees on WGS-84 from (φ1, λ1) to (φ2, λ2), where φ is latitude and λ is longitude. The result is in metres.
| Symbol | Value | Meaning |
|---|---|---|
| a | 6378137.0 m | WGS-84 equatorial radius |
| f | 1 / 298.257223563 | WGS-84 flattening |
| e² | f (2 − f) = 0.0066943799901413165 | First eccentricity squared |
| DEG | 0.017453292519943295 | Radians per degree, π / 180 |
Δφ = |φ2 − φ1|
Δλ = |λ2 − λ1|, and if Δλ > 180 then Δλ = 360 − Δλ
φm = (φ1 + φ2) / 2
The wrap on Δλ handles segments that cross the 180° meridian. Convert angles to radians before any trigonometry.
M, the meridian radius, is the Earth's radius of curvature for travel north or south. N, the prime vertical radius, is its radius for travel east or west.
W = 1 − e² sin²(φm)
M = a (1 − e²) / W3/2
N = a / √W
Resolve the line segment into north and east components, in metres. Their ratio gives the direction of travel, α, without computing α itself.
northing = Δφ · M (Δφ in radians)
easting = Δλ · cos(φm) · N (Δλ in radians)
cos²α = northing² / (northing² + easting²)
sin²α = easting² / (northing² + easting²)
If both components are zero, the two positions are the same and the distance is zero.
Euler's theorem gives the radius of curvature in any direction:
R = 1 / (cos²α / M + sin²α / N)
R equals M for travel due north or south, and N for travel due east or west. In any other direction it lies between the two.
span² = Δφ² + (Δλ · cos(φm))² (Δφ and Δλ in degrees)
span² ≤ 0.45² use the equirectangular formula
span² > 0.45² use the haversine formula
A span of 0.45° is about 50 km. Below it, the flat-earth formula is as accurate as haversine, and faster. The test uses the whole span, so a diagonal segment switches formula at the same length as a north-south segment.
x = Δλ · cos(φm) (Δλ in radians)
y = φ2 − φ1 (radians)
distance = R · √(x² + y²)
h = sin²((φ2 − φ1) / 2) + cos φ1 · cos φ2 · sin²(Δλ / 2)
distance = R · 2 · atan2(√h, √(1 − h))
The atan2 form stays accurate for very long segments, where the more common arcsine form loses precision.
This is the complete calculation in C-style pseudocode. It performs the arithmetic in the same order as TackTracker's own code, so an implementation using standard double-precision arithmetic will reproduce the worked examples below.
const double a = 6378137.0;
const double f = 1.0 / 298.257223563;
const double e2 = f * (2.0 - f);
const double DEG = 0.017453292519943295; // pi / 180
double distance(double lat1, double lon1, double lat2, double lon2)
{
// 2. Differences and mean latitude
double dLat = abs(lat2 - lat1);
double dLon = abs(lon2 - lon1);
if (dLon > 180.0) dLon = 360.0 - dLon;
double phim = ((lat1 + lat2) * 0.5) * DEG;
// 3. Radii of curvature
double s = sin(phim);
double w = 1.0 - e2 * s * s;
double M = a * (1.0 - e2) / (w * sqrt(w));
double N = a / sqrt(w);
// 4. Direction of travel
double northing = (dLat * DEG) * M;
double easting = (dLon * DEG) * cos(phim) * N;
double sum = northing * northing + easting * easting;
if (sum == 0.0) return 0.0;
double cos2 = (northing * northing) / sum;
double sin2 = (easting * easting) / sum;
// 5. Radius in the direction of travel
double R = 1.0 / ((cos2 / M) + (sin2 / N));
// 6. Choose the formula
double spanX = dLon * cos(phim);
if (dLat * dLat + spanX * spanX <= 0.45 * 0.45)
{
// 7a. Equirectangular
double x = (dLon * DEG) * cos(phim);
double y = (lat2 - lat1) * DEG;
return R * sqrt(x * x + y * y);
}
// 7b. Haversine
double sp = sin(((lat2 - lat1) * DEG) * 0.5);
double sl = sin((dLon * DEG) * 0.5);
double h = sp * sp + cos(lat1 * DEG) * cos(lat2 * DEG) * (sl * sl);
return R * (2.0 * atan2(sqrt(h), sqrt(1.0 - h)));
}
Use these segments to check an implementation. The values are given to full double precision.
| Segment | From | To | Formula | Distance |
|---|---|---|---|---|
| A. Sydney Harbour | -33.8500, 151.2500 | -33.8488, 151.2514 | Equirectangular | 185.75315091018112 m |
| B. Offshore run | -33.8500, 151.2500 | -33.7200, 151.4200 | Equirectangular | 21350.004072398544 m |
| C. Coastal passage | -33.8500, 151.2500 | -32.9300, 151.7800 | Haversine | 113328.56675719352 m |
| D. Across 180° | -17.7000, 179.9500 | -17.6900, -179.9600 | Equirectangular | 9611.63652076639 m |
| E. On the equator | 0.0000, 150.0000 | 0.0010, 150.0010 | Equirectangular | 156.90435678766934 m |
Intermediate values, for tracking down a difference:
| Leg | M | N | cos²α | R | span² |
|---|---|---|---|---|---|
| A | 6355228.884729252 | 6384771.073691976 | 0.513463497234543 | 6369568.013004188 | 0.000002791886026243269 |
| B | 6355162.413904408 | 6384748.813676533 | 0.4561431406884345 | 6371219.082899541 | 0.0368634613005486 |
| C | 6354756.09890743 | 6384612.741912364 | 0.8106746933770365 | 6360387.268972049 | 1.0422241996339714 |
| D | 6341321.236166183 | 6380110.239458078 | 0.013259274999946358 | 6379592.82137228 | 0.007451676992154509 |
| E | 6335439.327297664 | 6378137.000001626 | 0.49664160659370266 | 6356859.86224921 | 0.0000019999999999333956 |
Maths libraries differ slightly in how they round sine and cosine and cause small differences in your results.
These are the worst errors against the exact distance on the WGS-84 ellipsoid, computed with Karney's algorithm. They are taken over all directions of travel, at latitudes from the equator to 60°.
| Leg length | Worst error | As a percentage |
|---|---|---|
| 10 m | 0.1 mm | 0.00056% |
| 100 m | 0.6 mm | 0.00056% |
| 1 km | 6 mm | 0.00056% |
| 10 km | 57 mm | 0.00057% |
| 50 km | 0.47 m | 0.00093% |
| 100 km | 0.56 m | 0.00056% |
| 1000 km | 20.6 m | 0.0021% |
The error peaks on legs just under 50 km, where the flat-earth formula is about to hand over to haversine. It drops again beyond that, and grows once more on legs of many hundreds of kilometres.
The worked examples above fall well within these limits. Leg C, for instance, is 187 mm longer than the exact distance over 113 km.
For comparison, a GPS fix is typically accurate to a few metres. Over the distances sailed in a race, the calculation adds far less error than the GPS itself.