When approximate is better than exact

The subject of this post is a computational problem that is solved thousands of times each day (not just as part of my day job, but in almost every GPS receiver in the world).  It is a problem with an interesting history, and dozens of different solutions… but the arguably “best” solution seems to be relatively unknown.

The problem is that of converting Earth-centered Cartesian (x,y,z) coordinates to geodetic latitude, longitude, and height.  The transformation from geodetic to Cartesian coordinates is straightforward, but the reverse transformation is significantly more involved, and as a result has been addressed repeatedly in the literature.  Featherstone (see Reference (4) at the end of this post) provides a nearly comprehensive list of over 70 papers describing solutions or comparisons of solutions, spanning five decades of study.

I think this problem is interesting for three reasons.  First, it still seems to be not universally known that closed-form solutions even exist, usually obtained by finding the roots of an appropriately transformed quartic equation.  Even the first non-Wikipedia entry in a quick Google search claims that “there is no closed-form solution… if the altitude is not zero.”

Second, although all of these various closed-form solutions are “equal,” some are more equal than others.  That is, the accuracies of many of these “exact” solutions suffer when executed on a computer with limited precision.  Or worse, many papers only provide equations, with actual implementations requiring additional details not made explicit, such as handling singularities at the poles, or taking square roots of expressions that “should” be non-negative, but in double precision may yield negative values.  Borkowski’s formula in Reference (1), for example, breaks down for a spherical earth (i.e., zero eccentricity), when the transformation is in fact simplest.  And Bowring’s formula in Reference (2), which I see most often in others’ code, has a usually-neglected but easily-remedied danger of a singularity at the poles.

Which brings us to the third interesting feature of this problem: there is a solution provided by Olson in Reference (6), that, mathematically, is actually an approximation (essentially involving a truncated series expansion), but when implemented in code, and executed with limited double-precision, is not only more accurate than all of the “exact” closed-form solutions that I have evaluated in the references, but is also faster.

(It is worth emphasizing the distinction between this approximation, which although inexact is still essentially “closed-form,” and a whole class of other solutions that involve iterative approximation (e.g., Newton-Raphson), where execution time is more difficult to predict.)

Olson also actually provides working C code as a bonus; following is a port of his implementation to Python:

import math

# WGS-84 ellipsoid parameters
a = 6378137
f = 1 / 298.257223563

# Derived parameters
e2 = f * (2 - f)
a1 = a * e2
a2 = a1 * a1
a3 = a1 * e2 / 2
a4 = 2.5 * a2
a5 = a1 + a3
a6 = 1 - e2

def ecef_to_lla(ecef):
    """Convert ECEF (meters) to LLA (radians and meters).
    """
    # Olson, D. K., Converting Earth-Centered, Earth-Fixed Coordinates to
    # Geodetic Coordinates, IEEE Transactions on Aerospace and Electronic
    # Systems, 32 (1996) 473-476.
    w = math.sqrt(ecef[0] * ecef[0] + ecef[1] * ecef[1])
    z = ecef[2]
    zp = abs(z)
    w2 = w * w
    r2 = z * z + w2
    r  = math.sqrt(r2)
    s2 = z * z / r2
    c2 = w2 / r2
    u = a2 / r
    v = a3 - a4 / r
    if c2 > 0.3:
        s = (zp / r) * (1 + c2 * (a1 + u + s2 * v) / r)
        lat = math.asin(s)
        ss = s * s
        c = math.sqrt(1 - ss)
    else:
        c = (w / r) * (1 - s2 * (a5 - u - c2 * v) / r)
        lat = math.acos(c)
        ss = 1 - c * c
        s = math.sqrt(ss)
    g = 1 - e2 * ss
    rg = a / math.sqrt(g)
    rf = a6 * rg
    u = w - rg * c
    v = zp - rf * s
    f = c * u + s * v
    m = c * v - s * u
    p = m / (rf / g + f)
    lat = lat + p
    if z < 0:
        lat = -lat
    return (lat, math.atan2(ecef[1], ecef[0]), f + m * p / 2)

References:

  1. K. M. Borkowski, “Accurate Algorithms to Transform Geocentric to Geodetic Coordinates,” Bulletin Geodesique, 63 (1989) 50-56 [HTML]
  2. B. R. Bowring, “Transformation from Spatial to Geographical Coordinates,” Survey Review, 23:181 (1976) 323-327
  3. R. Burtch, “A Comparison of Methods Used in Rectangular to Geodetic Coordinate Transformations,” Proceedings of the American Congress on Surveying and Mapping (ACSM) Annual Conference and Technology Exhibition, Orlando, FL, 2006
  4. W. E. Featherstone and S. J. Claessens, “Closed-Form Transformation Between Geodetic and Ellipsoidal Coordinates,” Studia Geophysica et Geodaetica, 52 (2008) 1-18
  5. M. Heikkinen, “Geschlossene Formeln zur Berechnung raumlicher geodatischer Koordinaten aus rechtwinkligen Koordinaten,” Zeitschrift Vermess. (in German), 107 (1982) 207-211
  6. D. K. Olson, “Converting Earth-Centered, Earth-Fixed Coordinates to Geodetic Coordinates,” IEEE Transactions on Aerospace and Electronic Systems, 32 (1996) 473-476
  7. I. Sofair, “Improved Method for Calculating Exact Geodetic Latitude and Altitude Revisited,” Journal of Guidance, Control, and Dynamics, 23:2 (2000) 369
  8. J. Zhu, “Conversion of Earth-Centered Earth-Fixed Coordinates to Geodetic Coordinates,” IEEE Transactions on Aerospace and Electronic Systems, 30:3 (1994) 957-962
  9. J. Zhu, “Exact Conversion of Earth-Centered, Earth-Fixed Coordinates to Geodetic Coordinates,” Journal of Guidance, Control, and Dynamics, 16:2 (1993) 389-391

18 thoughts on “When approximate is better than exact

  1. Pingback: Cartesian to Geodetic | Degenerate Conic

    • Right. The returned height is in meters above the reference ellipsoid. Sea level height depends on which sea level you mean :). That is, usually (e.g., most GPS receivers) report a height above a geoid, a bilinearly interpolated grid of heights above the reference ellipsoid, of which there are at least two common variants. Many receivers, especially older ones, use EGM96, although the latest is EGM2008. Details and data files are here.

  2. Why not simply create the local-vertical-vector to the ellipsoid at the x,y,z point, lv = vec(x/a^2, y/a^2, z/b^2).unit(), then calculate the geodetic latitude with atan2( lv.z, sqrt(lv.x^2 + lv.y^2) )?

    • The formula also needs to work for points above the surface of the ellipsoid; see this example, where the correct local vertical is shown in blue (whose angle with the horizontal is the geodetic latitude), compared with your approach shown in red.

      • Good catch. After studying your response, I was surprised at how well that approach actually works. But you’re right, it’s imperfect.

        I’m also surprised that they didn’t quickly settle on a curve fit of geodetic latitude from geocentric, something like this: gdLatDeg = gcLatDeg + ( -1.291e-6 + 1225233.7 / ecef.mag() ) * sin((2.010698 * gcLatDeg – 1.32074e-06 * gcLatDeg ** 3) * pi/180) . If I didn’t mess that up, it’s accurate to 0.00033 deg, ~36m.

  3. @BHarris, [I corrected the typesetting; WordPress’s comment syntax is a weird combination of markdown and HTML.]

    That approximation works, as do any number of others; indeed, just a single iteration of Newton’s method (one of the many iterative methods mentioned in the post) is similarly accurate with similarly simple implementation.

    But again, that translation of angular error to positional error is only appropriate at/near the surface of the ellipsoid. At higher (e.g., orbital) altitudes, that lever arm is much longer, and the resulting positional error much greater.

    But even at the surface, those kinds of error are already too large for many applications (e.g., a GPS solution is much better than this, so it would be undesirable to “lose” that accuracy just in reporting LLA). The formulas discussed here preserve positional accuracy on the order of nanometers, and not just at the surface, but all the way to GEO.

    • A quick note after playing with the Olson code, it crashes for unit vectors and points far within the earth. Obviously a GIGO situation, but something to watch for.

      • This is a good point not mentioned in the original post: those errors aren’t bugs, they are appropriate, since for a non-spherical ellipsoid geodetic latitude is not well-defined for points sufficiently close to the origin. For example, from Sofair: “the excluded region is a closed prolate spheroid that is concentric with and contained within the Earth ellipsoid and whose semimajor axis is less than 43 km.”

        This is essentially radius (a^2-b^2)/a. This is actually a larger region than must necessarily be excluded, but only because the actual (subset) region within which latitude is not well-defined is weirdly-shaped.

      • As long as we’re talking about this, something else is bothering me — Olson’s original C code has some precalculated terms: e2 and a1-a6. Curiously, if f is f=1/298.257223563 and e2=f(2-f), then Olson’s precalculated a2 looks like it’s off by nearly 0.002: a2 = a1a1 = (a*e2)**2 = 1823091254.6094613 vs. precalculated value of 1823091254.6075455. The other ax terms are off too, but not as much.

        It’s at the 13th decimal and doesn’t meaningfully affect the results, just seems like an odd error even for 1996.

  4. @BHarris, yep, I’m unable to find a “reasonable error” that would lead to the specific value of e2 in the paper. Note that (1) Olson’s earlier paper uses 32-bit constants and calculations, and (2) his test code used 80-bit long doubles, suggesting that there could have been some issues in tracking the appropriate precision of his computed constant value of e2.

    However, all of the other constants are computed in terms of e2, so they are all consistent… and interestingly, the most common/straightforward implementation of the conversion from geodetic to cartesian also uses the constant e2, making it likely that his accuracy numbers are correct, just for a different ellipsoid than the WGS-84 one that we care about. (At any rate, I’ve done my own comparison of accuracy and speed of conversions among C++ implementations of Olson, Heikkinen, Zhu, Borkowski, Bowring, and Sofair, that are consistent with those presented by Olson.)

  5. First, thank you for the kind words about Latlon in your interesting website.

    Back in 1995, my copy of the WGS84 manual DMA TR8350.2 of 1987 gave the defining parameter a = 6378137 and 3 others I couldn’t use, so I chose one of their “derived” constants, e = 0.0818191908426, and squared it to get the e2 in Latlon. Later, NIMA TR8350.2 3rd edition of Jan 2000 replaced the defining parameter C2 with 1/f = 298.257223563. A better way to define the constants would be one statement

    double a = 6378137., f = 1./298.257223563, e2 = f(2.-f), a1 = ae2, …, a6 = 1. – e2;

    which I hope users of Latlon have put in place of the 8 data declaration statements.

    @B.Harris, you do have an eagle eye. Also, I don’t think computers crash anymore. They just set a variable = NaN and go right on computing. But Latlon does screen out points within 50 km of the earth’s center (sq. root of negative numbers, etc).

    • Don, very cool to hear directly from you! Thanks for the source information; agree about the updated constant derivations, I should have made that more explicit in my post that I made that change from the original C source.

  6. “First, it still seems to be not universally known that closed-form solutions even exist, usually obtained by finding the roots of an appropriately transformed quartic equation.”

    Yes!! Until I read this blog post of yours I felt like I was going crazy. There seems to be a lot of (understandable) misinfo out there about the current state of research on this topic. There’s Olson as you mention, and also Vermeilles and others I’m sure.

    What a messy problem!

  7. Many thanks for this interesting blog. I went into this problem as I was looking for an evaluation of Heikkinen’s method among others. Can you tell me if there is a published literature that summarizes those closed-form algorithms?
    Thank!

    • Thanks! See the references at the end of this post, although the summaries/survey papers tend to describe both the closed form and iterative algorithms together. I have done my own analysis comparing Borkowski, Bowring, Heikkinen, Olson, Sofair, and Zhu’s closed form algorithms; only Heikkinen and Olson preserve round-trip conversion accuracy within a couple of nanometers, without any problems with singularities, while also being the fastest of the bunch.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.