-
Notifications
You must be signed in to change notification settings - Fork 214
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
Add direct formulas #489
Add direct formulas #489
Changes from 7 commits
d22c39b
c572601
b7406fd
ccd9edf
afb575f
97fa9c1
4c4a91f
d04c621
bc3189f
c1299d7
8846989
cc2ded0
738c0da
12f7a22
10b340e
4dacbfa
25ce113
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Boost.Geometry | ||
|
||
// Copyright (c) 2018 Oracle and/or its affiliates. | ||
|
||
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle | ||
|
||
// Use, modification and distribution is subject to the Boost Software License, | ||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | ||
// http://www.boost.org/LICENSE_1_0.txt) | ||
|
||
#ifndef BOOST_GEOMETRY_FORMULAS_ELLIPTIC_MERIDIAN_ARC_DIRECT_HPP | ||
#define BOOST_GEOMETRY_FORMULAS_ELLIPTIC_MERIDIAN_ARC_DIRECT_HPP | ||
|
||
#include <boost/math/constants/constants.hpp> | ||
|
||
#include <boost/geometry/core/radius.hpp> | ||
|
||
#include <boost/geometry/util/condition.hpp> | ||
#include <boost/geometry/util/math.hpp> | ||
|
||
#include <boost/geometry/formulas/flattening.hpp> | ||
#include <boost/geometry/formulas/quarter_meridian.hpp> | ||
|
||
namespace boost { namespace geometry { namespace formula | ||
{ | ||
|
||
/*! | ||
\brief Compute the direct geodesic problem on a meridian | ||
*/ | ||
|
||
template <typename CT, unsigned int Order = 1> | ||
class elliptic_meridian_arc_direct | ||
{ | ||
|
||
public : | ||
|
||
// https://en.wikipedia.org/wiki/Meridian_arc#The_inverse_meridian_problem_for_the_ellipsoid | ||
// latitudes are assumed to be in radians and in [-pi/2,pi/2] | ||
template <typename T, typename Spheroid> | ||
static CT apply(T m, Spheroid const& spheroid) | ||
{ | ||
CT const f = formula::flattening<CT>(spheroid); | ||
CT n = f / (CT(2) - f); | ||
CT mp = formula::quarter_meridian<CT>(spheroid); | ||
CT mu = geometry::math::pi<CT>()/CT(2) * m / mp; | ||
|
||
if (Order == 0) | ||
{ | ||
return mu; | ||
} | ||
|
||
CT H2 = 1.5 * n; | ||
|
||
if (Order == 1) | ||
{ | ||
return mu + H2 * sin(2*mu); | ||
} | ||
|
||
CT n2 = n * n; | ||
CT H4 = 1.3125 * n2; | ||
|
||
if (Order == 2) | ||
{ | ||
return mu + H2 * sin(2*mu) + H4 * sin(4*mu); | ||
} | ||
|
||
CT n3 = n2 * n; | ||
H2 -= 0.84375 * n3; | ||
CT H6 = 1.572916667 * n3; | ||
|
||
if (Order == 3) | ||
{ | ||
return mu + H2 * sin(2*mu) + H4 * sin(4*mu) + H6 * sin(6*mu); | ||
} | ||
|
||
CT n4 = n2 * n2; | ||
H4 -= 1.71875 * n4; | ||
CT H8 = 2.142578125 * n4; | ||
|
||
// Order 4 or higher | ||
return mu + H2 * sin(2*mu) + H4 * sin(4*mu) + H6 * sin(6*mu) + H8 * sin(8*mu); | ||
} | ||
}; | ||
|
||
}}} // namespace boost::geometry::formula | ||
|
||
#endif // BOOST_GEOMETRY_FORMULAS_ELLIPTIC_MERIDIAN_ARC_DIRECT_HPP |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// Boost.Geometry | ||
|
||
// Copyright (c) 2018 Oracle and/or its affiliates. | ||
|
||
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle | ||
|
||
// Use, modification and distribution is subject to the Boost Software License, | ||
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | ||
// http://www.boost.org/LICENSE_1_0.txt) | ||
|
||
#ifndef BOOST_GEOMETRY_FORMULAS_QUARTER_MERIDIAN_HPP | ||
#define BOOST_GEOMETRY_FORMULAS_QUARTER_MERIDIAN_HPP | ||
|
||
#include <boost/geometry/core/radius.hpp> | ||
#include <boost/geometry/core/tag.hpp> | ||
#include <boost/geometry/core/tags.hpp> | ||
|
||
#include <boost/geometry/algorithms/not_implemented.hpp> | ||
|
||
namespace boost { namespace geometry | ||
{ | ||
|
||
#ifndef DOXYGEN_NO_DISPATCH | ||
namespace formula_dispatch | ||
{ | ||
|
||
template <typename ResultType, typename Geometry, typename Tag = typename tag<Geometry>::type> | ||
struct quarter_meridian | ||
: not_implemented<Tag> | ||
{}; | ||
|
||
template <typename ResultType, typename Geometry> | ||
struct quarter_meridian<ResultType, Geometry, srs_spheroid_tag> | ||
{ | ||
//https://en.wikipedia.org/wiki/Meridian_arc#Generalized_series | ||
static inline ResultType apply(Geometry const& geometry) | ||
{ | ||
//order 8 expansion | ||
ResultType const C[] = | ||
{ | ||
1073741824, | ||
268435456, | ||
16777216, | ||
4194304, | ||
1638400, | ||
802816, | ||
451584, | ||
278784, | ||
184041 | ||
}; | ||
|
||
ResultType const c2 = 2; | ||
ResultType const c4 = 4; | ||
ResultType const f = formula::flattening<ResultType>(geometry); | ||
ResultType const n = f / (c2 - f); | ||
ResultType const ab4 = (get_radius<0>(geometry) | ||
+ get_radius<2>(geometry)) / c4; | ||
return geometry::math::pi<ResultType>() * ab4 * | ||
horner_evaluate(n*n, C, C+8) / C[0]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this function will calculate 8 nested levels of products of products or products, etc. My guess is that the error propagation will be big. Is this a problem? Would representing this series in a "typical" way improve the accuracy of the result, i.e. as sum of ratios multiplied by x^i? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First, using horner rule (i.e. nested evaluation) should be faster than the standard evaluation, the first requires 2n operations while the second 3n-1 (if powers are computed iteratively). Second, horner seems to me more accurate since it uses less operations. Third, if this function is placed somewhere in geometry::math or in utilities then it could be used wherever we have to evaluate some polynomial. This will make the code shorter and more elegant than implementing manually polynomial evaluations in standard form when it is needed. |
||
} | ||
|
||
private : | ||
//TODO: move the following to a more general space to be used by other | ||
// classes as well | ||
/* | ||
Evaluate the polynomial in x using Horner's method. | ||
*/ | ||
template <typename NT, typename IteratorType> | ||
static inline NT horner_evaluate(NT x, | ||
IteratorType begin, | ||
IteratorType end) | ||
{ | ||
NT result(0); | ||
IteratorType it = end; | ||
do | ||
{ | ||
result = result * x + *--it; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if |
||
} | ||
while (it != begin); | ||
return result; | ||
} | ||
}; | ||
|
||
} // namespace formula_dispatch | ||
#endif // DOXYGEN_NO_DISPATCH | ||
|
||
#ifndef DOXYGEN_NO_DETAIL | ||
namespace formula | ||
{ | ||
|
||
template <typename ResultType, typename Geometry> | ||
ResultType quarter_meridian(Geometry const& geometry) | ||
{ | ||
return formula_dispatch::quarter_meridian<ResultType, Geometry>::apply(geometry); | ||
} | ||
|
||
} // namespace formula | ||
#endif // DOXYGEN_NO_DETAIL | ||
|
||
}} // namespace boost::geometry | ||
|
||
#endif // BOOST_GEOMETRY_FORMULAS_QUARTER_MERIDIAN_HPP |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
// Boost.Geometry | ||
|
||
// Copyright (c) 2016, Oracle and/or its affiliates. | ||
// Copyright (c) 2016-2018, Oracle and/or its affiliates. | ||
// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle | ||
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle | ||
|
||
|
@@ -24,6 +24,8 @@ | |
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp> | ||
#include <boost/geometry/util/select_coordinate_type.hpp> | ||
|
||
#include <boost/geometry/formulas/result_direct.hpp> | ||
|
||
namespace boost { namespace geometry { | ||
|
||
namespace formula { | ||
|
@@ -217,6 +219,47 @@ inline int azimuth_side_value(T const& azi_a1_p, T const& azi_a1_a2) | |
: 1; // left | ||
} | ||
|
||
template <typename CT, typename Sphere> | ||
inline result_direct<CT> spherical_direct(CT const& lon1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An alternative formula would convert to 3D cartesian ECEF and calculate the result using vector algebra. This is how it's done in the spherical intersection and side strategies. So should be careful to not mix things. But AFAIU this formula is a building block for other geographic formulas and not used in spherical for now. Is my understanding correct? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes this is not used anywhere for now, I added it for completeness. I could be used in geographic pt-seg distance formula for optimizations. |
||
CT const& lat1, | ||
CT const& sig12, | ||
CT const& alp1, | ||
Sphere const& sphere) | ||
{ | ||
result_direct<CT> result; | ||
|
||
CT const sin_alp1 = sin(alp1); | ||
CT const sin_lat1 = sin(lat1); | ||
CT const cos_alp1 = cos(alp1); | ||
CT const cos_lat1 = cos(lat1); | ||
|
||
CT const norm = math::sqrt(cos_alp1 * cos_alp1 + sin_alp1 * sin_alp1 | ||
* sin_lat1 * sin_lat1); | ||
CT const alp0 = atan2(sin_alp1 * cos_lat1, norm); | ||
CT const sig1 = atan2(sin_lat1, cos_alp1 * cos_lat1); | ||
CT const sig2 = sig1 + sig12 / get_radius<0>(sphere); | ||
|
||
CT const sin_sig2 = sin(sig2); | ||
CT const cos_sig2 = cos(sig2); | ||
CT const sin_sig1 = sin(sig1); | ||
CT const cos_sig1 = cos(sig1); | ||
CT const sin_alp0 = sin(alp0); | ||
CT const cos_alp0 = cos(alp0); | ||
|
||
CT const norm2 = math::sqrt(cos_alp0 * cos_alp0 * cos_sig2 * cos_sig2 | ||
+ sin_alp0 * sin_alp0); | ||
CT const lat2 = atan2(cos_alp0 * sin_sig2, norm2); | ||
CT const alp2 = atan2(sin_alp0, cos_alp0 * cos_sig2); | ||
CT const omg1 = atan2(sin_alp0 * sin_sig1, cos_sig1); | ||
CT const lon2 = atan2(sin_alp0 * sin_sig2, cos_sig2); | ||
|
||
result.lon2 = lon1 + lon2 - omg1; | ||
result.lat2 = lat2; | ||
result.reverse_azimuth = alp2; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are all of these results needed in all cases? In other formulas they are only calculated if compile-time flag is set. The same approach could be used here. |
||
|
||
return result; | ||
} | ||
|
||
} // namespace formula | ||
|
||
}} // namespace 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.
What is the input and result of this formula? Why does it have different interface than the other direct formulas? AFAIU direct formula should take latitude, direction (north or south) and distance. The result should be the coordinates of the resulting point since they may be on the other side of the globe. What am I missing here?
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.
That was just the formula for computation. I added the interface.