summaryrefslogtreecommitdiff
path: root/boost/geometry/formulas/area_formulas.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/geometry/formulas/area_formulas.hpp')
-rw-r--r--boost/geometry/formulas/area_formulas.hpp170
1 files changed, 98 insertions, 72 deletions
diff --git a/boost/geometry/formulas/area_formulas.hpp b/boost/geometry/formulas/area_formulas.hpp
index d459528c27..b8da8a6727 100644
--- a/boost/geometry/formulas/area_formulas.hpp
+++ b/boost/geometry/formulas/area_formulas.hpp
@@ -1,7 +1,8 @@
// Boost.Geometry
-// Copyright (c) 2015-2021 Oracle and/or its affiliates.
+// Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
+// Copyright (c) 2015-2022 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
@@ -342,6 +343,15 @@ public:
}
}
+ static inline CT trapezoidal_formula(CT lat1r, CT lat2r, CT lon21r)
+ {
+ CT const c1 = CT(1);
+ CT const c2 = CT(2);
+ CT const tan_lat1 = tan(lat1r / c2);
+ CT const tan_lat2 = tan(lat2r / c2);
+
+ return c2 * atan(((tan_lat1 + tan_lat2) / (c1 + tan_lat1 * tan_lat2))* tan(lon21r / c2));
+ }
/*
Compute the spherical excess of a geodesic (or shperical) segment
@@ -354,43 +364,44 @@ public:
static inline CT spherical(PointOfSegment const& p1,
PointOfSegment const& p2)
{
+ CT const pi = math::pi<CT>();
+
CT excess;
- if (LongSegment) // not for segments parallel to equator
+ CT const lon1r = get_as_radian<0>(p1);
+ CT const lat1r = get_as_radian<1>(p1);
+ CT const lon2r = get_as_radian<0>(p2);
+ CT const lat2r = get_as_radian<1>(p2);
+
+ CT lon12r = lon2r - lon1r;
+ math::normalize_longitude<radian, CT>(lon12r);
+
+ if (lon12r == pi || lon12r == -pi)
{
- CT cbet1 = cos(geometry::get_as_radian<1>(p1));
- CT sbet1 = sin(geometry::get_as_radian<1>(p1));
- CT cbet2 = cos(geometry::get_as_radian<1>(p2));
- CT sbet2 = sin(geometry::get_as_radian<1>(p2));
+ return pi;
+ }
- CT omg12 = geometry::get_as_radian<0>(p1)
- - geometry::get_as_radian<0>(p2);
- CT comg12 = cos(omg12);
- CT somg12 = sin(omg12);
+ if (BOOST_GEOMETRY_CONDITION(LongSegment) && lat1r != lat2r) // not for segments parallel to equator
+ {
+ CT const cbet1 = cos(lat1r);
+ CT const sbet1 = sin(lat1r);
+ CT const cbet2 = cos(lat2r);
+ CT const sbet2 = sin(lat2r);
- CT alp1 = atan2(cbet1 * sbet2
- - sbet1 * cbet2 * comg12,
- cbet2 * somg12);
+ CT const omg12 = lon2r - lon1r;
+ CT const comg12 = cos(omg12);
+ CT const somg12 = sin(omg12);
- CT alp2 = atan2(cbet1 * sbet2 * comg12
- - sbet1 * cbet2,
- cbet1 * somg12);
+ CT const cbet1_sbet2 = cbet1 * sbet2;
+ CT const sbet1_cbet2 = sbet1 * cbet2;
+ CT const alp1 = atan2(cbet1_sbet2 - sbet1_cbet2 * comg12, cbet2 * somg12);
+ CT const alp2 = atan2(cbet1_sbet2 * comg12 - sbet1_cbet2, cbet1 * somg12);
excess = alp2 - alp1;
} else {
- // Trapezoidal formula
-
- CT tan_lat1 =
- tan(geometry::get_as_radian<1>(p1) / 2.0);
- CT tan_lat2 =
- tan(geometry::get_as_radian<1>(p2) / 2.0);
-
- excess = CT(2.0)
- * atan(((tan_lat1 + tan_lat2) / (CT(1) + tan_lat1 * tan_lat2))
- * tan((geometry::get_as_radian<0>(p2)
- - geometry::get_as_radian<0>(p1)) / 2));
+ excess = trapezoidal_formula(lat1r, lat2r, lon12r);
}
return excess;
@@ -437,8 +448,13 @@ public:
// Constants
+ CT const c0 = CT(0);
+ CT const c1 = CT(1);
+ CT const c2 = CT(2);
+ CT const pi = math::pi<CT>();
+ CT const half_pi = pi / c2;
CT const ep = spheroid_const.m_ep;
- CT const one_minus_f = CT(1) - spheroid_const.m_f;
+ CT const one_minus_f = c1 - spheroid_const.m_f;
// Basic trigonometric computations
// the compiler could optimize here using sincos function
@@ -472,43 +488,47 @@ public:
CT excess;
- auto const half_pi = math::pi<CT>() / 2;
- bool meridian = lon2r - lon1r == CT(0)
- || lat1r == half_pi || lat1r == -half_pi
- || lat2r == half_pi || lat2r == -half_pi;
+ CT lon12r = lon2r - lon1r;
+ math::normalize_longitude<radian, CT>(lon12r);
- if (!meridian && (i_res.distance)
- < mean_radius<CT>(spheroid_const.m_spheroid) / CT(638)) // short segment
+ // Comparing with "==" works with all test cases here, but could potential create numerical issues
+ if (lon12r == pi || lon12r == -pi)
{
- CT tan_lat1 = tan(lat1r / 2.0);
- CT tan_lat2 = tan(lat2r / 2.0);
-
- excess = CT(2.0)
- * atan(((tan_lat1 + tan_lat2) / (CT(1) + tan_lat1 * tan_lat2))
- * tan((lon2r - lon1r) / 2));
+ result.spherical_term = pi;
}
else
{
- /* in some cases this formula gives more accurate results
- *
- * CT sin_omg12 = cos_omg1 * sin_omg2 - sin_omg1 * cos_omg2;
- normalize(sin_omg12, cos_omg12);
-
- CT cos_omg12p1 = CT(1) + cos_omg12;
- CT cos_bet1p1 = CT(1) + cos_bet1;
- CT cos_bet2p1 = CT(1) + cos_bet2;
- excess = CT(2) * atan2(sin_omg12 * (sin_bet1 * cos_bet2p1 + sin_bet2 * cos_bet1p1),
- cos_omg12p1 * (sin_bet1 * sin_bet2 + cos_bet1p1 * cos_bet2p1));
- */
-
- excess = alp2 - alp1;
+ bool const meridian = lon12r == c0
+ || lat1r == half_pi || lat1r == -half_pi
+ || lat2r == half_pi || lat2r == -half_pi;
+
+ if (!meridian && (i_res.distance)
+ < mean_radius<CT>(spheroid_const.m_spheroid) / CT(638)) // short segment
+ {
+ excess = trapezoidal_formula(lat1r, lat2r, lon12r);
+ }
+ else
+ {
+ /* in some cases this formula gives more accurate results
+ CT sin_omg12 = cos_omg1 * sin_omg2 - sin_omg1 * cos_omg2;
+ normalize(sin_omg12, cos_omg12);
+
+ CT cos_omg12p1 = CT(1) + cos_omg12;
+ CT cos_bet1p1 = CT(1) + cos_bet1;
+ CT cos_bet2p1 = CT(1) + cos_bet2;
+ excess = CT(2) * atan2(sin_omg12 * (sin_bet1 * cos_bet2p1 + sin_bet2 * cos_bet1p1),
+ cos_omg12p1 * (sin_bet1 * sin_bet2 + cos_bet1p1 * cos_bet2p1));
+ */
+
+ excess = alp2 - alp1;
+ }
+
+ result.spherical_term = excess;
}
- result.spherical_term = excess;
-
// Ellipsoidal term computation (uses integral approximation)
- CT const cos_alp0 = math::sqrt(CT(1) - math::sqr(sin_alp0));
+ CT const cos_alp0 = math::sqrt(c1 - math::sqr(sin_alp0));
//CT const cos_alp0 = hypot(cos_alp1, sin_alp1 * sin_bet1);
CT cos_sig1 = cos_alp1 * cos_bet1;
CT cos_sig2 = cos_alp2 * cos_bet2;
@@ -523,8 +543,8 @@ public:
if (ExpandEpsN) // expand by eps and n
{
CT const k2 = math::sqr(ep * cos_alp0);
- CT const sqrt_k2_plus_one = math::sqrt(CT(1) + k2);
- CT const eps = (sqrt_k2_plus_one - CT(1)) / (sqrt_k2_plus_one + CT(1));
+ CT const sqrt_k2_plus_one = math::sqrt(c1 + k2);
+ CT const eps = (sqrt_k2_plus_one - c1) / (sqrt_k2_plus_one + c1);
// Generate and evaluate the polynomials on eps (i.e. var2 = eps)
// to get the final series coefficients
@@ -563,20 +583,26 @@ public:
static inline bool crosses_prime_meridian(PointOfSegment const& p1,
PointOfSegment const& p2)
{
- CT const pi
- = geometry::math::pi<CT>();
- CT const two_pi
- = geometry::math::two_pi<CT>();
-
- CT p1_lon = get_as_radian<0>(p1)
- - ( floor( get_as_radian<0>(p1) / two_pi )
- * two_pi );
- CT p2_lon = get_as_radian<0>(p2)
- - ( floor( get_as_radian<0>(p2) / two_pi )
- * two_pi );
-
- CT max_lon = (std::max)(p1_lon, p2_lon);
- CT min_lon = (std::min)(p1_lon, p2_lon);
+ CT const pi = geometry::math::pi<CT>();
+ CT const two_pi = geometry::math::two_pi<CT>();
+
+ CT const lon1r = get_as_radian<0>(p1);
+ CT const lon2r = get_as_radian<0>(p2);
+
+ CT lon12 = lon2r - lon1r;
+ math::normalize_longitude<radian, CT>(lon12);
+
+ // Comparing with "==" works with all test cases here, but could potential create numerical issues
+ if (lon12 == pi || lon12 == -pi)
+ {
+ return true;
+ }
+
+ CT const p1_lon = lon1r - ( std::floor( lon1r / two_pi ) * two_pi );
+ CT const p2_lon = lon2r - ( std::floor( lon2r / two_pi ) * two_pi );
+
+ CT const max_lon = (std::max)(p1_lon, p2_lon);
+ CT const min_lon = (std::min)(p1_lon, p2_lon);
return max_lon > pi && min_lon < pi && max_lon - min_lon > pi;
}