-
Notifications
You must be signed in to change notification settings - Fork 216
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
Introduce formula for Karney's direct geodesic method #486
Introduce formula for Karney's direct geodesic method #486
Conversation
The paper can be found at: https://arxiv.org/pdf/1109.4448.pdf This commit also introduces the evaluate_series_A1 function for evaluating the series expantion, which was generated using Maxima: http://maxima.sourceforge.net
- Add SED script for converting x to CT(x) - Improve code documentation
- Update function headers
- Fix conversion from degree to radian in sin_cos_degrees function
- Add separate function for evaluating C3 from C3x coefficient
- Add minus sign for B12 evaluation
Dataset is collected from: https://zenodo.org/record/32156 It is then parsed using a Python script.
The geodesic length is calculated manually using GeographicLib/Geodesic.hpp in C++. However, this value differs when calculated using the CLI tool GeodSolve.
*/ | ||
template < | ||
typename CT, | ||
size_t SeriesOrder = 8, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe place it at the end of parameters' list to be more consistent with similar strategies
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Just updated this.
CT const lat1 = la1; | ||
Azi const azi12 = math::normalize_angle<CT>(azimuth12); | ||
|
||
if (math::equals(distance, Dist(0)) || distance < Dist(0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not creating a const for 0?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Just updated this.
math::normalize_angle(lon12)); | ||
} | ||
|
||
if (BOOST_GEOMETRY_CONDITION(CalcQuantities)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this part of code duplicates code from https://github.com/boostorg/geometry/blob/develop/include/boost/geometry/formulas/differential_quantities.hpp is it possible to integrate ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually tried using that code. However, almost all the test cases fail if I do. I find that odd, since it seems to be working well with Vincenty and Thomas. I will try and dig a little deeper into the code and see if I'm missing out on a step.
|
||
namespace boost { namespace geometry { namespace series_expansion { | ||
|
||
/* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a lot of duplication in the maxima scripts inside comments, could you try to decrease it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just removed the duplicated code in this commit. Please let me know if I should further reduce the comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be more useful to have all maxima script in one place. Because for example if someone want to reproduce series for I2
then the definition of ataylor
is missing.
Also the following script (taken from area formula, right?) is repeated 7 times!
To replace each number x by CT(x) the following script can be used: sed -e 's/[0-9]\+/CT(&)/g; s/\[CT/\[/g; s/)\]/\]/g; s/case\sCT(/case /g; s/):/:/g; s/epsCT(2)/eps2/g;'
Finally, is the script taken from http://geographiclib.sourceforge.net/html/geod.mac without modifications? If so you can just add the link in the comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also the following script (taken from area formula, right?) is repeated 7 times!
...
Yes, it matches the one from area formula, except the last part s/epsCT(2)/eps2/g;
. Before that the script was converting eps2
-> epsCT(2)
. So, I will just keep a single copy of this script and remove the duplicated ones.
Finally, is the script taken from http://geographiclib.sourceforge.net/html/geod.mac without modifications? If so you can just add the link in the comments.
The scripts are indeed taken from (http://geographiclib.sourceforge.net/html/geod.mac), however, I had to make minor adjustments e.g. change the function header and data types to keep it consistent with Boost Geometry guidelines. Is it okay to discard those changes? If so, I will only provide a link to geod.mac
, otherwise, I will add them in a separate file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The scripts are indeed taken from (http://geographiclib.sourceforge.net/html/geod.mac), however, I had to make minor adjustments e.g. change the function header and data types to keep it consistent with Boost Geometry guidelines. Is it okay to discard those changes? If so, I will only provide a link to geod.mac, otherwise, I will add them in a separate file.
Ok, since they contain changes, I think it is useful to keep them in Boost.Geometry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have moved the Maxima scripts to: doc/other/maxima/geod.mac.
Thanks for this PR!
lon2 is inaccurate with respect to the data set in https://zenodo.org/record/32156 ? How do you know that Geodesic::C3coeff() is more accurate than the function generated from Maxima?
Could you please add this information in the code as a comment for future reference? |
In some cases, this allows the caller to ignore the CT template argument, as it is deduced from the argument list.
Other changes include the update of series expansion function calls, as the template arguments are reversed.
Previously, the array size was passed in as a separate parameter.
…n function Modified functions are: - evaluate_coeffs_C3x - normalize_values
|
||
math::normalize_values<CT>(sin_beta1, cos_beta1); | ||
|
||
cos_beta1 = std::max(math::sqrt(c0), cos_beta1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::max
won't compile with msvc. Take it in brackets. Furthermore sqrt(0) is 0. So:
(std::max)(c0, cos_beta1);
or am I missing something?
CT const lat1 = la1; | ||
|
||
Azi azi12 = azimuth12; | ||
math::normalize_angle<degree, Azi>(azi12); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if you called it normalize_azimuth()
then it'd be clear that it has to be in range (-180, 180].
include/boost/geometry/util/math.hpp
Outdated
// Convert to radians. | ||
remainder *= d2r<T>(); | ||
|
||
T s = std::sin(remainder), c = std::cos(remainder); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before you used math functions without std::
. If you specify std::
then the compiler will not use functions from the namespace where a user-defined numeric type is defined. So please remove std::
. If you like you can bring std::
math functions into scope:
using std::floor;
using std::sin;
using std::cos;
but they are not used like that in the whole library so I'd say this is not needed.
include/boost/geometry/util/math.hpp
Outdated
// the argument to the range [-45, 45] before converting it to radians. | ||
T remainder; int quotient; | ||
|
||
remainder = std::fmod(x, T(360)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use math::mod()
include/boost/geometry/util/math.hpp
Outdated
return 0; | ||
} | ||
|
||
T y = std::abs(x); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
math::abs()
template <typename Coeffs, typename CT> | ||
inline void evaluate_coeffs_C3x(Coeffs &c, size_t const& SeriesOrder, CT const& n) { | ||
size_t const coeff_size = Coeffs::static_size; | ||
BOOST_GEOMETRY_ASSERT(coeff_size == (SeriesOrder * (SeriesOrder - 1)) / 2); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you took SeriesOrder
as template parameter you could check this at compile time:
static size_t const coeff_size = Coeffs::static_size;
static size_t const expected_size = (SeriesOrder * (SeriesOrder - 1)) / 2;
BOOST_MPL_ASSERT((coeff_size == expected_size));
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having some trouble passing SeriesOrder
as the template parameter.
I changed the evaluate_coeffs_C3x
function header to:
template <typename Coeffs, typename CT, size_t SeriesOrder>
inline void evaluate_coeffs_C3x(Coeffs &c, CT const& n) {
and called it as:
evaluate_coeffs_C3x(*this, n);
but I get this error:
error: ‘evaluate_coeffs_C3x’ was not declared in this scope
I also tried changing the order of parameters as:
template <size_t SeriesOrder, typename CT, typename Coeffs>
inline void evaluate_coeffs_C3x(CT const& n, Coeffs &c) {
but calling the function as:
evaluate_coeffs_C3x(n, *this);
or:
evaluate_coeffs_C3x<SeriesOrder, CT>(n, *this);
doesn't work either.
It produces the error:
error: expected primary-expression before ‘>’ token
and:
couldn't deduce template parameter ‘SeriesOrder’
respectively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're calling these functions inside structs' constructors then they should be defined before structs.
Either of these should work:
template <size_t SeriesOrder, typename CT, typename Coeffs>
inline void evaluate_coeffs_C3x(CT const& n, Coeffs &c){}
// this is more strict, it takes only the struct you like
template <size_t SeriesOrder, typename CT>
inline void evaluate_coeffs_C3x(CT const& n, coeffs_C3x<SeriesOrder, CT> &c){}
and either of these should work:
evaluate_coeffs_C3x<SeriesOrder>(n, *this);
evaluate_coeffs_C3x<SeriesOrder, CT>(n, *this);
evaluate_coeffs_C3x<SeriesOrder, CT, coeffs_C3x>(n, *this); // assuming it's called in coeffs_C3x's constructor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this worked.
Out of curiosity, why was this working before for the previous function calls? Does this have something to do with passing explicit template parameters? As I only got the error with evaluate_coeffs_C3x<SeriesOrder>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No idea. You'd have to know how exactly type deduction is done in your compiler. Are you using Visual Studio? Its compiler allows things it shouldn't sometimes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I'm using g++
version 7.2.0
on Ubuntu.
int m = Coeffs1::static_size - l - 1; | ||
mult *= eps; | ||
|
||
std::vector<CT> coeffs2_slice(coeffs2.begin(), coeffs2.begin() + offset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't have to create a container here, one each iteration btw. This will be horribly slow. Pass range into math::polyval()
.
include/boost/geometry/util/math.hpp
Outdated
\brief Evaluate the polynomial. | ||
*/ | ||
template<typename CT> | ||
inline CT polyval(std::vector<CT> coeff, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
template <typename It>
inline CT polyval(It first, It last, std::iterator_traits<It>::value_type const& eps)
or just
template <typename It, typename T>
inline CT polyval(It first, It last, T const& eps)
include/boost/geometry/util/math.hpp
Outdated
inline CT polyval(std::vector<CT> coeff, | ||
const CT eps) | ||
{ | ||
int N = boost::size(coeff) - 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::distance(first, last) - 1
include/boost/geometry/util/math.hpp
Outdated
int N = boost::size(coeff) - 1; | ||
int index = 0; | ||
|
||
CT y = N < 0 ? 0 : coeff[index++]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
*(first + (index++))
For normalizing longitudes, the normalize_longitude function is used instead.
…) function The coefficient container structs are moved to the bottom of the file.
This is done to avoid creating a separate container in each iteration.
ab45a02
to
6219503
Compare
Conflicts: include/boost/geometry/util/math.hpp test/formulas/direct.cpp The conflicting files have been updated.
Thank you for the detailed reviews. I think all the requested changes have been addressed. I have also merged Please let me know if there are any further changes required. |
Thanks! I'm ok with merging. I'd prefer to wait until after the 1.68 release. Or do you need this PR merged in order to work on the next step of the project? |
That's fine. I can work on local branches for the next part i.e. the inverse method. Btw, when is the next release scheduled for? |
Here is the schedule: https://www.boost.org/development/index.html I think the whole month is too long to keep you wait so I'm ok with merging it now. If needed I'll create a branch for bugfixing based on previous develop state. |
Okay, as you deem fit. If it is merged before August 14th, I can mention this in my final evaluation report. |
This pull request introduces the direct geodesic method as proposed by Karney (2011). The following are the highlights of the changes introduced:
lon2
to be inaccurate. Replacing the function withGeodesic::C3coeff()
from GeographicLib removed this inaccuracy, so I have used that instead.However, the tests fail when comparing geodesic scale (M12) with Boost implementation of the direct method. Also, I noticed that using the C++ header
GeographicLib/Geodesic.hpp
andGeodSolve
CLI tool both provided different results. As an example, using these points:with GeographicLib, GeodSolve CLI, and Boost gave the following results:
I'm not sure why this happens, because
GeodSolve
uses the same C++ code at the back-end. The difference might not seem much, but since only a tolerance within1e-7
is acceptable, these tests fail.