Sorry I should correct a few confusing mistakes in my previous e-mail:

    - I was talking mainly about authalic latitude ==> geodetic latitude conversion, not the other way around. /pj_authlat()/ returns the geodetic latitude for a given authalic latitude (the name of the function is quite confusing -- I would propose to rename to something less ambiguous if it is only used internally)

    - The function currently uses 3 /sin()/ calls for order 3 (not 2, made a typo)

    - The ISEA projection does not currently make use of /pj_authlat()/ or /pj_qsfn() /for automatically converting authalic / geodetic latitudes, but I am proposing to introduce this when using non-spherical ellipsoid (just like LAEA of which it is a modified form).

Best,

-Jerome

On 9/10/24 9:19 PM, Jérôme St-Louis via PROJ wrote:

Hi Even, all,

As suggested in https://github.com/OSGeo/PROJ/pull/4211 , I've just written an implementation of the authalic / geodetic latitude conversion with the goal to automatically perform this conversion when using ISEA with an non-spherical ellipsoid, and was wondering whether we would want to improve the PROJ version (/pj_authlat()/ for the geodetic ==> authalic), which is used for ISEA, but also by:

- Equal Area Cylindrical (cea.cpp)
- EqualEarth (eqearth.cpp)
- HEALPix (healpix.cpp) -- which might also imply rHEALPix
- Lambert Azimuthal Equal Area (laea.cpp)

The current version of /pj_authlat()/ uses a series expansion based on the eccentricity squared (I believe this is based on a Lagrange reversion <https://en.wikipedia.org/wiki/Lagrange_inversion_theorem> as mentioned here <https://en.wikipedia.org/wiki/Latitude#Inverse_formulae_and_series>), and Charles Karney in this paper <https://arxiv.org/pdf/2212.05818> establishes that:

/series expansions using the third flattening as the small parameter uniformly result in smaller truncation errors compared to using the eccentricity./
The current version also only uses order 3, whereas GeographicLib would normally use order 6 for double precision, with order 4 being the minimum configuration. The current order 3, which requires 2 /sin()/ calls, gives precision up to roughly 8 decimals. Order 6 would mean twice the number of /sin()/ calls, and the 8 decimals should be enough to point to Waldo on a page <https://xkcd.com/2170/>, however C.K. argues that:

/Modern geodetic libraries should strive to achieve full double-precision accuracy /(page 4)/
/

and I would much agree with that.

Based on the direction we want to go, I would submit a PR including adjustments to avoid the separate heap allocation for the doubles (as discussed here <https://github.com/OSGeo/PROJ/pull/4211#pullrequestreview-2233903246>), and adjustments for tests etc.

PROJ currently does not use the full GeographicLib which already includes code for conversion between different auxiliary latitudes, does it?

    ( https://github.com/geographiclib/geographiclib/blob/main/src/AuxLatitude.cpp )

See my proposed new version below, which I wrote based on equation 20 on page 3, and matrix A20 on page 13 from the paper, applying optimizations similar to the current /pj_authlat()/.

Kind regards,

-Jerome

void pj_authset(double a, double b, double cp[6])
{
   // https://arxiv.org/pdf/2212.05818
   // ∆η(ζ) = S^(L)(ζ) · Cηζ · P^(M) (n) + O(n^L+1)    -- (20)
   double n = (a - b) / (a + b);  // Third flattening
   static const double C[21] = // (A20)
   {
      4 / 3.0,  4 / 45.0,   -16/35.0,  -2582 /14175.0,  60136 /467775.0,    28112932/ 212837625.0,                46 / 45.0,  152/945.0, -11966 /14175.0, -21016 / 51975.0,   251310128/ 638512875.0,                          3044/2835.0,   3802 /14175.0, -94388 / 66825.0,    -8797648/  10945935.0,                                         6059 / 4725.0,  41072 / 93555.0, -1472637812/ 638512875.0,                                                        768272 /467775.0,  -455935736/ 638512875.0,
4210684958/1915538625.0
   };
   double p;

   cp[0]  = C[ 0] * n;

   p = n * n;
   cp[0] += C[ 1] * p;
   cp[1]  = C[ 6] * p;

   p *= n;
   cp[0] += C[ 2] * p;
   cp[1] += C[ 7] * p;
   cp[2]  = C[11] * p;

   p *= n;
   cp[0] += C[ 3] * p;
   cp[1] += C[ 8] * p;
   cp[2] += C[12] * p;
   cp[3]  = C[15] * p;

   p *= n;
   cp[0] += C[ 4] * p;
   cp[1] += C[ 9] * p;
   cp[2] += C[13] * p;
   cp[3] += C[16] * p;
   cp[4]  = C[18] * p;

   p *= n;
   cp[0] += C[ 5] * p;
   cp[1] += C[10] * p;
   cp[2] += C[14] * p;
   cp[3] += C[17] * p;
   cp[4] += C[19] * p;
   cp[5]  = C[20] * p;
}

double pj_authlat(double auth, const double * cp)
{
   // https://arxiv.org/pdf/2212.05818
   // ∆η(ζ) = S^(L)(ζ) · Cηζ · P^(M) (n) + O(n^L+1)    -- (20)
   double a2x = auth + auth, a4x = a2x + a2x, a8x = a4x + a4x;
   return auth +
      cp[0] * sin(a2x) +
      cp[1] * sin(a4x) +
      cp[2] * sin(a4x + a2x) +
      cp[3] * sin(a8x) +
      cp[4] * sin(a8x + a2x) +
      cp[5] * sin(a8x + a4x);
}

_______________________________________________
PROJ mailing list
PROJ@lists.osgeo.org
https://lists.osgeo.org/mailman/listinfo/proj
_______________________________________________
PROJ mailing list
PROJ@lists.osgeo.org
https://lists.osgeo.org/mailman/listinfo/proj

Reply via email to