Skip to content

Implement 'exact' authalic latitude->geographic latitude and use it in +proj=aea/cea/laea/eqearth/healpix/rhealpix#4441

Merged
rouault merged 2 commits intoOSGeo:masterfrom
rouault:fix_4440
Mar 22, 2025
Merged

Implement 'exact' authalic latitude->geographic latitude and use it in +proj=aea/cea/laea/eqearth/healpix/rhealpix#4441
rouault merged 2 commits intoOSGeo:masterfrom
rouault:fix_4440

Conversation

@rouault
Copy link
Member

@rouault rouault commented Mar 20, 2025

Fixes #4440

$ echo 1020000 1562000 | bin/cs2cs epsg:3035 epsg:4937 -d 10 | bin/cs2cs epsg:4937 epsg:3035 -d 4
1020000.0000 1562000.0000 0.0000

…n +proj=aea/cea/laea/eqearth/healpix/rhealpix

Fixes OSGeo#4440
@rouault rouault added this to the 9.7.0 milestone Mar 20, 2025
@rouault rouault added the funded through GSP Work funded through the GDAL Sponsorship Program label Mar 20, 2025
@rouault
Copy link
Member Author

rouault commented Mar 20, 2025

@kbevers I'm not sure if this is appropriate for backport. This is somewhere in between a new feature and a bugfix. This could cause downstream software to potentially have failures in their test suite if they test coordinates with sufficient precision, so perhaps not appropriate for a bugfix release.

@jjimenezshaw
Copy link
Contributor

@cffk was suggesting something in 2023 about the authalic latitude in https://lists.osgeo.org/pipermail/proj/2023-March/010956.html

@kbevers
Copy link
Member

kbevers commented Mar 20, 2025

I'm not sure if this is appropriate for backport.

Let's call it a new feature, this is a changed approach that delivers better accuracy in some cases.

@cffk since you've written the book (well, sort of) on this topic it would be foolish not request your input here :-)

@kbevers kbevers requested a review from cffk March 20, 2025 19:58
@jjimenezshaw
Copy link
Contributor

jjimenezshaw commented Mar 20, 2025

I was going to add the reference in docs/source/references.bib in a PR, but I think that it does not appear if it is not referenced anywhere.
So here it is

@Article{Karney2023,
  author                   = {Charles F. F. Karney},
  title                    = {On auxiliary latitudes},
  journal                  = {Survey Review},
  year                     = {2024},
  volume                   = {56},
  number                   = {395},
  pages                    = {165-180},
  eprint                   = {2212.05818},
  doi                      = {10.1080/00396265.2023.2217604}
}

@cffk
Copy link
Contributor

cffk commented Mar 20, 2025

I'm not sure if this is appropriate for backport.

Let's call it a new feature, this is a changed approach that delivers better accuracy in some cases.

@cffk since you've written the book (well, sort of) on this topic it would be foolish not request your input here :-)

I'll take a look and give feedback tomorrow (03-21).

@cffk
Copy link
Contributor

cffk commented Mar 21, 2025

A. Addressing this PR is the narrowest way...

  1. Presumably the last line of pj_authalic_lat_inverse_exact should be
return phi;
  1. When implementing Newton's method (the same function), it's helpful
    to include a comment as to how the problem is being formulated. How about
// Given beta, solve
//   f(phi) = qp*sin(beta)/(1-e^2) - q(phi)/(1-e^2)
// for phi, where
//   q(phi)/(1-e^2) = sin(phi)/(1 - e^2*sin(phi)^2) + atanh(e*sin(phi))/e
// and
//   df(phi)/dphi = - dq(phi)/dphi / (1-e^2)
//                = - 2 * (1-e^2) * cos(phi) / (1 - e^2*sinphi^2)^2
  1. Given that pj_authalic_lat_inverse_exact replaces the logarithm
    expression with atanh, then so too should pj_authalic_lat_q_coeff.
    This way the function is exactly odd and maintains high relative
    accuracy near the equator.

B. However, widening the scope of this comment ...

The method for computing the auxiliary latitude pj_authalic_lat is
badly ill-conditioned near the poles because of the properties of
arcsine. (And also near the equator, unless atanh is used.) The
spatial resolution of asin(q/qp) at the equator is about 90 mm.

Hence, pj_authalic_lat_inverse_exact is also inaccurate near the
poles. A better fix would therefore be to upgrade
pj_authalic_lat_inverse_approx so that it returns an accurate result.
The current implementation uses the series of Adams (1921) which is 3rd
order in e^2. I recommend using the 6th order series in n with the
coefficients given by (A20) of my paper. (Here is the link to the
preprint: https://arxiv.org/abs/2212.05818.) This is accurate to
roundoff for |f| < 1/150. The series can be evaluated with the
existing machinery in mlfn.cpp which evaluates similar series (for the
conversions between geographic and rectifying latitude) with Horner and
Clenshaw. The result would then be more accurate than
pj_authalic_lat_inverse_exact

C. Finally ...

A longer range goal would be to replace the ill-conditioned formula for
pj_authatic_lat by a similar series, Eq. (A19) in my paper.

Indeed latitude.cpp might be fixed also to provide the series results
for pj_conformal_lat_inverse (currently using a simple-minded and slow
iterative method). (As an aside, in pj_conformal_lat, Snyder's (3-1)
should be replaced with my better-conditioned Eqs. (8) and (10).)

And it might be extended to support the conversions in mlfn.cpp and in
tmerc.cpp. (The latter would require the implementation of complex
Clenshaw from that file.)

In summary, I recommend A. Now also would be a good time to implement
B. C should be considered later with the replacement of
pj_authalic_lat being at the top of the queue.

@rouault
Copy link
Member Author

rouault commented Mar 21, 2025

@cffk Thanks for the review. I've added a commit using your comments A. B and C are a bit beyond my understanding / available time unfortunately. If we were going to do this, I believe the most straightforward way would probably to just borrow your implementation AuxLatitude.hpp, AuxLatitude.cpp from https://doi.org/10.5281/zenodo.7382666 .

Copy link
Contributor

@cffk cffk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go with this. To address Even's remark about absorbing my AuxLatitude implementation: I fear this may be overkill. Perhaps @jjimenezshaw can be persuaded to start a cleanup of latitude.cpp. Or maybe I'll take a crack at doing the two authalic conversions.

@rouault
Copy link
Member Author

rouault commented Mar 21, 2025

hum it looks like using atanh() in the forward path degrades the accuracy on i386 at the pole. Maybe we should revert that part ?

@cffk
Copy link
Contributor

cffk commented Mar 21, 2025

hum it looks like using atanh() in the forward path degrades the accuracy on i386 at the pole. Maybe we should revert that part ?

How are you measuring this? The log expression entails significant loss of precision (you're evalutating log(1 + small) with the result being small -- but you've lost significant bits with 1 + small); so don't compare the atanh expression to this unless you evaluation the log expression with long double.

My recommendation: keep the atanh formulation. The accuracy at the pole is already shot with the asin.

@rouault
Copy link
Member Author

rouault commented Mar 21, 2025

How are you measuring this?

well, https://github.com/OSGeo/PROJ/actions/runs/13995771272/job/39190443832?pr=4441 now fails, whereas it didn't before my additional commit

I can comment out the problematic test points / relax their precision requirements for now

@rouault rouault merged commit ff66216 into OSGeo:master Mar 22, 2025
26 checks passed
cffk added a commit to cffk/proj.4 that referenced this pull request Mar 23, 2025
This follows on the discussion in OSGeo#4441.  In particular...

latitudes.cpp now offers methods for converting between auxiliary
laitudes using trigonometric series whose coefficients are power series
in n.  The series are evaluated using Clenshaw summation and Horner's
method.  For small flattening, these often offer better accuracy than
the analytical expressions.

Now mlfn.cpp and tmerc.cpp use these conversions, simplifying the
implementations in these files.  In particular tmerc's functions gatg
and clens (which puzzlingly seemed to do exactly the same thing!) have
been removed.  tmerc's clenS (with a capital "S") stays -- it does
complex Clenshaw summation

Along the way, I've simplified pj_conformal_lat and
pj_conform_lat_inverse in latitudes.cpp.  For the former, I've
substituted a better conditioned formula; for the latter, I used the
existing (and more efficent) approach implemented in pj_sinhpsi2tanphi.

The computation of authalic latitudes in latitudes.cpp now uses the
series method if the flattening is small.  However some of the test
cases of the spillhaus projection use large flattening (is this really
necessary?); so I've had to retain the poorly conditioned analytical
formulas as well.

If we really wanted to support large flattening in this case, it's
necessary to evaluate the authalic latitude using Eqs. (52), (53), (54)
of "On auxiliary latitudes" (https://arxiv.org/abs/2212.05818) and using
Newton's method on the tangents of the angles for the inverse, using
Eq. (57).  I don't see the need for these at present.

I've simplified the interface somewhat: passing P instead of P->e,
P->onees, etc. separately; dropping "_coeffs" off the q function name
and the tagging of the inverse function name with "_exact".  I had hoped
to retire the external use of Snyder's q(sinphi).  However, some equal
area projections, e.g., the cylindrical version, really do need this
function instead of the authalic latitude directly.
rouault pushed a commit that referenced this pull request Mar 29, 2025
This follows on the discussion in #4441.  In particular
latitudes.cpp now offers methods for converting between auxiliary
laitudes using trigonometric series whose coefficients are power series
in n.  The series are evaluated using Clenshaw summation and Horner's
method.  For small flattening, these often offer better accuracy than
the analytical expressions.

Now mlfn.cpp and tmerc.cpp use these conversions, simplifying the
implementations in these files.  In particular tmerc's functions gatg
and clens (which puzzlingly seemed to do exactly the same thing!) have
been removed.  tmerc's clenS (with a capital "S") stays -- it does
complex Clenshaw summation

Along the way, I've simplified pj_conformal_lat and
pj_conform_lat_inverse in latitudes.cpp.  For the former, I've
substituted a better conditioned formula; for the latter, I used the
existing (and more efficent) approach implemented in pj_sinhpsi2tanphi.

The computation of authalic latitudes in latitudes.cpp now uses the
series method if the flattening is small.  However some of the test
cases of the spillhaus projection use large flattening (is this really
necessary?); so I've had to retain the poorly conditioned analytical
formulas as well.

If we really wanted to support large flattening in this case, it's
necessary to evaluate the authalic latitude using Eqs. (52), (53), (54)
of "On auxiliary latitudes" (https://arxiv.org/abs/2212.05818) and using
Newton's method on the tangents of the angles for the inverse, using
Eq. (57).  I don't see the need for these at present.

I've simplified the interface somewhat: passing P instead of P->e,
P->onees, etc. separately; dropping "_coeffs" off the q function name
and the tagging of the inverse function name with "_exact".  I had hoped
to retire the external use of Snyder's q(sinphi).  However, some equal
area projections, e.g., the cylindrical version, really do need this
function instead of the authalic latitude directly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

funded through GSP Work funded through the GDAL Sponsorship Program

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Accuracy of ETRS89-LAEA

4 participants