Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved series for the meridian length and its inverse #3516

Merged
merged 1 commit into from
Dec 17, 2022

Conversation

cffk
Copy link
Contributor

@cffk cffk commented Dec 16, 2022

This PR addresses #2387 point 3.

Replace the series for the meridian length in the functions

pj_enfn
pj_mlfn
pj_inv_mlfn

in mlfn.cpp with improved versions. The old version of pj_mlfn used Snyder's Eq. (3-21) extended from 3rd to 4th order in e^2 and pj_inv_mlfn inverted this by an iterative method.

The new version uses 6th order expansions in the third flattening, n, for both pj_mlfn and pj_inv_mlfn. Expansions in n have a smaller truncation error that the corresponding expansion in e^2. Increasing the order to 6th (from 4th) means that the routines give full double precision accuracy for |f| <= 1/150. Because the meridian length is used in several projections, it's important that it be accurate as possible. For example, now the azimuthal equidistant projection is equally accurate in the polar (uses pj_mlfn) and oblique (uses the geodesic routines) aspects.

The increased accuracy tripped one of the tests: an instance of the projection +proj=utm +approx. The fix is to use the non-approx results for the expected values.

Presumably this change results in pj_mlfn being a little slower while pj_inv_mlfn is considerably faster (since there's no iteration involved). Incidentally the new method for pj_inv_mlfn uses Snyder's recommended series Eq. (3-23) extended from 4th to 6th order (Snyder writes n as e_1).

More discussion of the series used here is given in my recent paper

On auxiliary latitudes, https://arxiv.org/abs/2212.05818

Replace the series for the meridian length in the functions

  pj_enfn
  pj_mlfn
  pj_inv_mlfn

in mlfn.cpp with improved versions.  The old version of pj_mlfn used
Snyder's Eq. (3-21) extended from 3rd to 4th order in e^2 and
pj_inv_mlfn inverted this by an iterative method.

The new version uses 6th order expansions in the third flattening, n,
for both pj_mlfn and pj_inv_mlfn.  Expansions in n have a smaller
truncation error that the corresponding expansion in e^2.  Increasing
the order to 6th (from 4th) means that the routines give full double
precision accuracy for |f| <= 1/150.  Because the meridian length is
used in several projections, it's important that it be accurate as
possible.  For example, now the azimuthal equidistant projection is
equally accurate in the polar (uses pj_mlfn) and oblique (uses the
geodesic routines) aspects.

The increased accuracy tripped one of the tests: an instance of the
projection +proj=utm +approx.  The fix is to use the non-approx results
for the expected values.

Presumably this change results in pj_mlfn being a little slower while
pj_inv_mlfn is considerably faster (since there's no iteration
involved).  Incidentally the new method for pj_inv_mlfn uses Snyder's
recommended series Eq. (3-23) extended from 4th to 6th order (Snyder
writes n as e_1).

More discussion of the series used here is given in my recent paper

  On auxiliary latitudes, https://arxiv.org/abs/2212.05818
@cffk cffk requested review from rouault and kbevers December 17, 2022 12:47
@rouault
Copy link
Member

rouault commented Dec 17, 2022

Can you try running the new bench_proj_trans binary before and after your changes ?
I get for example the following with current master:

$ bin/bench_proj_trans --pipeline "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=utm +zone=31 +ellps=GRS80 +approx" 2 49
2 49 -> 426857.98771659 5427937.52334353
Duration: 407 ms
Throughput: 12.29 million coordinates/s

$ bin/bench_proj_trans --pipeline "+proj=pipeline +step +inv +proj=utm +zone=31 +ellps=GRS80 +approx +step +proj=unitconvert +xy_in=rad +xy_out=deg" 426857.98771659 5427937.52334353
426857.98771659 5427937.52334353 -> 1.99999999999991 48.9999999999999
Duration: 714 ms
Throughput: 7.00 million coordinates/s

@cffk
Copy link
Contributor Author

cffk commented Dec 17, 2022

Here you go:

BEFORE
2 49 -> 426857.98771659 5427937.52334353
Duration: 305 ms
Throughput: 16.39 million coordinates/s
426857.98771659 5427937.52334353 -> 1.99999999999991 48.9999999999999
Duration: 546 ms
Throughput: 9.16 million coordinates/s

AFTER
2 49 -> 426857.98771659 5427937.52334229
Duration: 333 ms
Throughput: 15.02 million coordinates/s
426857.98771659 5427937.52334353 -> 1.99999999999969 49.000000000011
Duration: 496 ms
Throughput: 10.08 million coordinates/s

These timings are roughly in line with my expectations.

Copy link
Member

@rouault rouault left a comment

Choose a reason for hiding this comment

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

I've given this a try against GDAL autotest suite whose one test is sensitive to +proj=utm +approx results, and things are good.
It is also very cool to have a non-iterative implementation of inv_mlfn, in the perspective of PROJ having a day vectorized implementations of coordinate transformations, transforming several coordinates at once in SIMD registers.
So +1 from me

Copy link
Member

@kbevers kbevers left a comment

Choose a reason for hiding this comment

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

Looks good to me. Great addition!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Some suggestions for code cleanup
3 participants