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

Add direct formulas #489

Merged
merged 17 commits into from
Jun 20, 2018
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d22c39b
[formulas] Elliptic meridian arc direct formula
vissarion Jun 12, 2018
c572601
[formulas] [strategies] Rename elliptic_arc_length formula to ellipti…
vissarion Jun 12, 2018
b7406fd
[formulas] Spherical direct formula
vissarion Jun 12, 2018
ccd9edf
[formulas] [strategies] Thomas first order direct formula
vissarion Jun 12, 2018
afb575f
[tests] Add tests for meridian direct formula
vissarion Jun 12, 2018
97fa9c1
[tests] Adding tests for spherical and thomas 1st order direct formulas
vissarion Jun 12, 2018
4c4a91f
[formulas] Add quarter meridian formula for spheroids
vissarion Jun 13, 2018
d04c621
[formulas] Return 0 in horner's rule special case of empty input
vissarion Jun 14, 2018
bc3189f
[formulas] [tests] Interface for direct meridian formula and tests
vissarion Jun 15, 2018
c1299d7
[formulas] [tests] Add revarse_azimuth and quantities computation to …
vissarion Jun 18, 2018
8846989
[formulas] Rename elliptic_meridian_arc formula to meridian
vissarion Jun 18, 2018
cc2ded0
[formulas] [tests] Change thomas direct interface
vissarion Jun 18, 2018
738c0da
[formulas] Add missing include file needed by spherical formulas
vissarion Jun 18, 2018
12f7a22
[formulas] Add coordinates and reverse azimuth flags to spherical dir…
vissarion Jun 18, 2018
10b340e
[formulas] Call to pow function with both arguments having the same type
vissarion Jun 18, 2018
4dacbfa
[formulas] Use pass by reference in area formula functions
vissarion Jun 19, 2018
25ce113
[formulas] Use std pow in are formulas
vissarion Jun 19, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions include/boost/geometry/formulas/elliptic_meridian_arc_direct.hpp
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)
Copy link
Member

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?

Copy link
Member Author

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.

{
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
@@ -1,15 +1,15 @@
// Boost.Geometry

// Copyright (c) 2017 Oracle and/or its affiliates.
// Copyright (c) 2017-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_ARC_LENGTH_HPP
#define BOOST_GEOMETRY_FORMULAS_ELLIPTIC_ARC_LENGTH_HPP
#ifndef BOOST_GEOMETRY_FORMULAS_ELLIPTIC_MERIDIAN_ARC_INVERSE_HPP
#define BOOST_GEOMETRY_FORMULAS_ELLIPTIC_MERIDIAN_ARC_INVERSE_HPP

#include <boost/math/constants/constants.hpp>

Expand All @@ -29,7 +29,7 @@ namespace boost { namespace geometry { namespace formula
*/

template <typename CT, unsigned int Order = 1>
class elliptic_arc_length
class elliptic_meridian_arc_inverse
{

public :
Expand Down Expand Up @@ -167,68 +167,9 @@ public :
+ C6 * sin(6*lat) + C8 * sin(8*lat) + C10 * sin(10*lat));

}

// Iterative method to elliptic arc length based on
// http://www.codeguru.com/cpp/cpp/algorithms/article.php/c5115/
// Geographic-Distance-and-Azimuth-Calculations.htm
// latitudes are assumed to be in radians and in [-pi/2,pi/2]
template <typename T1, typename T2, typename Spheroid>
CT interative_method(T1 lat1,
T2 lat2,
Spheroid const& spheroid)
{
CT result = 0;
CT const zero = 0;
CT const one = 1;
CT const c1 = 2;
CT const c2 = 0.5;
CT const c3 = 4000;

CT const a = get_radius<0>(spheroid);
CT const f = formula::flattening<CT>(spheroid);

// how many steps to use

CT lat1_deg = lat1 * geometry::math::r2d<CT>();
CT lat2_deg = lat2 * geometry::math::r2d<CT>();

int steps = c1 + (c2 + (lat2_deg > lat1_deg) ? CT(lat2_deg - lat1_deg)
: CT(lat1_deg - lat2_deg));
steps = (steps > c3) ? c3 : steps;

//std::cout << "Steps=" << steps << std::endl;

CT snLat1 = sin(lat1);
CT snLat2 = sin(lat2);
CT twoF = 2 * f - f * f;

// limits of integration
CT x1 = a * cos(lat1) /
sqrt(1 - twoF * snLat1 * snLat1);
CT x2 = a * cos(lat2) /
sqrt(1 - twoF * snLat2 * snLat2);

CT dx = (x2 - x1) / (steps - one);
CT x, y1, y2, dy, dydx;
CT adx = (dx < zero) ? -dx : dx; // absolute value of dx

CT a2 = a * a;
CT oneF = 1 - f;

// now loop through each step adding up all the little
// hypotenuses
for (int i = 0; i < (steps - 1); i++){
x = x1 + dx * i;
dydx = ((a * oneF * sqrt((one - ((x+dx)*(x+dx))/a2))) -
(a * oneF * sqrt((one - (x*x)/a2)))) / dx;
result += adx * sqrt(one + dydx*dydx);
}

return result;
}
};

}}} // namespace boost::geometry::formula


#endif // BOOST_GEOMETRY_FORMULAS_ELLIPTIC_ARC_LENGTH_HPP
#endif // BOOST_GEOMETRY_FORMULAS_ELLIPTIC_MERIDIAN_ARC_INVERSE_HPP
102 changes: 102 additions & 0 deletions include/boost/geometry/formulas/quarter_meridian.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];
Copy link
Member

@awulkiew awulkiew Jun 13, 2018

Choose a reason for hiding this comment

The 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?

Copy link
Member Author

Choose a reason for hiding this comment

The 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;
Copy link
Member

Choose a reason for hiding this comment

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

What if begin == end? Either return 0 or check assertion.

}
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
45 changes: 44 additions & 1 deletion include/boost/geometry/formulas/spherical.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

Expand All @@ -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 {
Expand Down Expand Up @@ -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,
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Member Author

Choose a reason for hiding this comment

The 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;
Copy link
Member

Choose a reason for hiding this comment

The 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
Expand Down
Loading