Improve short-distance accuracy of Andoyer inverse#1461
Improve short-distance accuracy of Andoyer inverse#1461mjacobse wants to merge 5 commits intoboostorg:developfrom
Conversation
Use haversine formula instead of law of cosines to avoid numerical loss of precision for close points as suggested in boostorg#1217. This had the side-effect of returning non-zero azimuths for smaller angles than before, which resulted in quite inaccurate non-zero reduced length and geodesic scale values too. To fix this, division by the cosine of the latitudes was changed to be done implicitly within atan2, which together with use of the haversine formula improves accuracy for all result values.
tinko92
left a comment
There was a problem hiding this comment.
Thanks for this PR. I find your test method and results convincing regarding the numeric improvements.
The CI failures are unrelated to the code changes, I believe (saw them in other runs as well). Running the test suite manually on an x86_64 (GCC14) machine with this PR, I get new test failures, e.g. one in test/algorithms/area/area_sph_geo.cpp and two failures in test/algorithms/area/area.cpp . Can you look into those? You can run the full test suite with b2 test from the geometry main directory, if you have a setup as discussed at https://github.com/boostorg/geometry/wiki/Contribution-Tutorial
Regarding performance, with a naive benchmark based on your testing code (calling only the inverse function in a loop, separated from the random generation of the test cases), I find a performance cost of ~14%. To me this seems acceptable for the improved precision but I don't know the typical use cases, maybe @vissarion can comment on this (I remember you did work in the past on the tradeoffs of different geographic distance formulas).
| CT const one_minus_cos_d = c2 * hav_d; | ||
| CT const one_plus_cos_d = c2 - one_minus_cos_d; | ||
| // cos_d = 1 means that the points are very close | ||
| // cos_d = -1 means that the points are antipodal |
There was a problem hiding this comment.
(Minor) These names and comments may be less obvious now with the cos_d identifier no longer being defined in this file (meaning can still be inferred from the other _d identifiers though and I don't have proposals for other names).
There was a problem hiding this comment.
Maybe adding a comment like
// follows from definition of haversine and trigonometric power reduction formula:
// hav(d) = sin^2(d/2) = (1 - cos(d))/2could help?
| CT const tan_lat2 = sin_lat2/cos_lat2; | ||
| CT const M = cos_lat1*tan_lat2-sin_lat1*cos_dlon; | ||
| A = atan2(sin_dlon, M); | ||
| A = atan2(sin_dlon*cos_lat2, M - N*cos_dlon); |
There was a problem hiding this comment.
Also here the * could use spaces for readability
Area result is now more accurate with more accurate azimuths from andoyer
Sure, sorry for not checking this before. The failure in The failures in Does the library guarantee some behavior for such out-of-range values or are such calls out of contract? Even if it is out of contract, should I try to restore the same behavior for out-of-range coordinates with the haversine formula to before to not break users who rely on this behavior? |
|
hi @mjacobse , could you maybe add this testcase? in a new file It should then also be added to It compares with Vincenty, that has a higher precision (we cannot compare with geographicLib without adding hardcoded expectations) The cases themselves are suggested by Claude, feel free to adapt it - or adapt the tolerance Current behaviour: I didn't test on your PR |
Use haversine formula instead of law of cosines to avoid numerical loss of precision for close points as suggested in #1217.
This has the side-effect of returning non-zero azimuths for smaller angles than before, which results in quite inaccurate non-zero reduced length and geodesic scale values too. To fix this, division by the cosine of the latitudes is changed to be done implicitly within
atan2, which together with use of the haversine formula improves accuracy for all result values.I planned to add tests for very close points like
{8.81, 53.08}, {8.81000001, 53.08}toinverse_cases.hpp, since Andoyer returns a distance of about 9cm for such examples before this change, which has a very large relative error. Unfortunately the other formulas (especially Thomas) have some issues with examples like that too, so I could not easily get such tests to pass. Suggestions would be welcome.The following plots (generated using a simple test executable and a Python script, see mjacobse@d2930ae) show the errors of the different quantities compared to GeographicLib for many randomly generated pairs of points depending on the order of magnitude of the distance between them (top row absolute error, bottom row relative error. red before these changes, green after):
distance:

azimuth:

reverse_azimuth:

reduced_length:

geodesic_scale:
