Skip to content

Commit

Permalink
Merge pull request #489 from vissarion/feature_direct_formulas
Browse files Browse the repository at this point in the history
Add direct formulas
  • Loading branch information
awulkiew committed Jun 20, 2018
2 parents 097f6fd + 25ce113 commit 53ff12a
Show file tree
Hide file tree
Showing 15 changed files with 1,274 additions and 520 deletions.
19 changes: 10 additions & 9 deletions include/boost/geometry/formulas/area_formulas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class area_formulas
Evaluate the polynomial in x using Horner's method.
*/
template <typename NT, typename IteratorType>
static inline NT horner_evaluate(NT x,
static inline NT horner_evaluate(NT const& x,
IteratorType begin,
IteratorType end)
{
Expand All @@ -64,7 +64,7 @@ class area_formulas
https://en.wikipedia.org/wiki/Clenshaw_algorithm
*/
template <typename NT, typename IteratorType>
static inline NT clenshaw_sum(NT cosx,
static inline NT clenshaw_sum(NT const& cosx,
IteratorType begin,
IteratorType end)
{
Expand Down Expand Up @@ -166,7 +166,7 @@ class area_formulas
s/case\sCT(/case /g; s/):/:/g'
*/

static inline void evaluate_coeffs_n(CT n, CT coeffs_n[])
static inline void evaluate_coeffs_n(CT const& n, CT coeffs_n[])
{

switch (SeriesOrder) {
Expand Down Expand Up @@ -245,7 +245,7 @@ class area_formulas
/*
Expand in k2 and ep2.
*/
static inline void evaluate_coeffs_ep(CT ep, CT coeffs_n[])
static inline void evaluate_coeffs_ep(CT const& ep, CT coeffs_n[])
{
switch (SeriesOrder) {
case 0:
Expand Down Expand Up @@ -323,18 +323,20 @@ class area_formulas
Given the set of coefficients coeffs1[] evaluate on var2 and return
the set of coefficients coeffs2[]
*/
static inline void evaluate_coeffs_var2(CT var2,
CT coeffs1[],

static inline void evaluate_coeffs_var2(CT const& var2,
CT const coeffs1[],
CT coeffs2[]){
std::size_t begin(0), end(0);
for(std::size_t i = 0; i <= SeriesOrder; i++){
end = begin + SeriesOrder + 1 - i;
coeffs2[i] = ((i==0) ? CT(1) : pow(var2,int(i)))
coeffs2[i] = ((i==0) ? CT(1) : std::pow(var2, int(i)))
* horner_evaluate(var2, coeffs1 + begin, coeffs1 + end);
begin = end;
}
}


/*
Compute the spherical excess of a geodesic (or shperical) segment
*/
Expand Down Expand Up @@ -403,13 +405,12 @@ class area_formulas
*/
template <
template <typename, bool, bool, bool, bool, bool> class Inverse,
//typename AzimuthStrategy,
typename PointOfSegment,
typename SpheroidConst
>
static inline return_type_ellipsoidal ellipsoidal(PointOfSegment const& p1,
PointOfSegment const& p2,
SpheroidConst spheroid_const)
SpheroidConst const& spheroid_const)
{
return_type_ellipsoidal result;

Expand Down
168 changes: 168 additions & 0 deletions include/boost/geometry/formulas/meridian_direct.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// 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_MERIDIAN_DIRECT_HPP
#define BOOST_GEOMETRY_FORMULAS_MERIDIAN_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/meridian_inverse.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/quarter_meridian.hpp>
#include <boost/geometry/formulas/result_direct.hpp>

namespace boost { namespace geometry { namespace formula
{

/*!
\brief Compute the direct geodesic problem on a meridian
*/

template <
typename CT,
bool EnableCoordinates = true,
bool EnableReverseAzimuth = false,
bool EnableReducedLength = false,
bool EnableGeodesicScale = false,
unsigned int Order = 4
>
class meridian_direct
{
static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;
static const bool CalcCoordinates = EnableCoordinates || CalcRevAzimuth;

public:
typedef result_direct<CT> result_type;

template <typename T, typename Dist, typename Spheroid>
static inline result_type apply(T const& lo1,
T const& la1,
Dist const& distance,
bool north,
Spheroid const& spheroid)
{
result_type result;

CT const half_pi = math::half_pi<CT>();
CT const pi = math::pi<CT>();
CT const one_and_a_half_pi = pi + half_pi;
CT const c0 = 0;

CT azimuth = north ? c0 : pi;

if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))
{
CT s0 = meridian_inverse<CT, Order>::apply(la1, spheroid);
int signed_distance = north ? distance : -distance;
result.lon2 = lo1;
result.lat2 = apply(s0 + signed_distance, spheroid);
}

if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
{
result.reverse_azimuth = azimuth;


if (result.lat2 > half_pi &&
result.lat2 < one_and_a_half_pi)
{
result.reverse_azimuth = pi;
}
else if (result.lat2 < -half_pi &&
result.lat2 > -one_and_a_half_pi)
{
result.reverse_azimuth = c0;
}

}

if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
{
CT const b = CT(get_radius<2>(spheroid));
CT const f = formula::flattening<CT>(spheroid);

boost::geometry::math::normalize_spheroidal_coordinates
<
boost::geometry::radian,
double
>(result.lon2, result.lat2);

typedef differential_quantities
<
CT,
EnableReducedLength,
EnableGeodesicScale,
Order
> quantities;
quantities::apply(lo1, la1, result.lon2, result.lat2,
azimuth, result.reverse_azimuth,
b, f,
result.reduced_length, result.geodesic_scale);
}
return result;
}

// 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_MERIDIAN_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_MERIDIAN_INVERSE_HPP
#define BOOST_GEOMETRY_FORMULAS_MERIDIAN_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 meridian_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_MERIDIAN_INVERSE_HPP
Loading

0 comments on commit 53ff12a

Please sign in to comment.