1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
5 // This file was modified by Oracle on 2015.
6 // Modifications copyright (c) 2015, Oracle and/or its affiliates.
8 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
9 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
11 // Use, modification and distribution is subject to the Boost Software License,
12 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
13 // http://www.boost.org/LICENSE_1_0.txt)
15 #ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_HUILLER_HPP
16 #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_HUILLER_HPP
20 #include <boost/geometry/strategies/spherical/distance_haversine.hpp>
22 #include <boost/geometry/core/radian_access.hpp>
23 #include <boost/geometry/util/math.hpp>
26 namespace boost { namespace geometry
29 namespace strategy { namespace area
35 \brief Area calculation by spherical excess / Huiller's formula
37 \tparam PointOfSegment point type of segments of rings/polygons
38 \tparam CalculationType \tparam_calculation
39 \author Barend Gehrels. Adapted from:
40 - http://webdocs.cs.ualberta.ca/~graphics/books/GraphicsGems/gemsiv/sph_poly.c
41 - http://tog.acm.org/resources/GraphicsGems/gemsiv/sph_poly.c
42 - http://williams.best.vwh.net/avform.htm
43 \note The version in Graphics Gems IV (page 132-137) didn't account for
44 polygons crossing the 0 and 180 meridians. The fix for this algorithm
45 can be found in Graphics Gems V (pages 45-46). See:
46 - http://kysmykseka.net/koti/wizardry/Game%20Development/Programming/Graphics%20Gems%204.pdf
47 - http://kysmykseka.net/koti/wizardry/Game%20Development/Programming/Graphics%20Gems%205.pdf
48 \note This version works for convex and non-convex polygons, for 180 meridian
49 crossing polygons and for polygons with holes. However, some cases (especially
50 180 meridian cases) must still be checked.
51 \note The version which sums angles, which is often seen, doesn't handle non-convex
53 \note The version which sums longitudes, see http://hdl.handle.net/2014/40409,
54 is simple and works well in most cases but not in 180 meridian crossing cases.
55 This probably could be solved.
57 \note This version is made for spherical equatorial coordinate systems
63 [area_with_strategy_output]
67 [link geometry.reference.algorithms.area.area_2_with_strategy area (with strategy)]
73 typename PointOfSegment,
74 typename CalculationType = void
78 typedef typename boost::mpl::if_c
80 boost::is_void<CalculationType>::type::value,
81 typename select_most_precise
83 typename coordinate_type<PointOfSegment>::type,
87 >::type calculation_type;
94 // Distances are calculated on unit sphere here
95 strategy::distance::haversine<calculation_type> distance_over_unit_sphere;
100 , distance_over_unit_sphere(1)
102 inline calculation_type area(calculation_type radius) const
104 return - sum * radius * radius;
109 typedef calculation_type return_type;
110 typedef PointOfSegment segment_point_type;
111 typedef excess_sum state_type;
113 inline huiller(calculation_type radius = 1.0)
117 inline void apply(PointOfSegment const& p1,
118 PointOfSegment const& p2,
119 excess_sum& state) const
121 if (! geometry::math::equals(get<0>(p1), get<0>(p2)))
123 calculation_type const half = 0.5;
124 calculation_type const two = 2.0;
125 calculation_type const four = 4.0;
126 calculation_type const pi
127 = geometry::math::pi<calculation_type>();
128 calculation_type const two_pi
129 = geometry::math::two_pi<calculation_type>();
130 calculation_type const half_pi
131 = geometry::math::half_pi<calculation_type>();
134 calculation_type a = state.distance_over_unit_sphere.apply(p1, p2);
136 // Sides on unit sphere to south pole
137 calculation_type b = half_pi - geometry::get_as_radian<1>(p2);
138 calculation_type c = half_pi - geometry::get_as_radian<1>(p1);
141 calculation_type s = half * (a + b + c);
143 // E: spherical excess, using l'Huiller's formula
144 // [tg(e / 4)]2 = tg[s / 2] tg[(s-a) / 2] tg[(s-b) / 2] tg[(s-c) / 2]
145 calculation_type excess = four
146 * atan(geometry::math::sqrt(geometry::math::abs(tan(s / two)
149 * tan((s - c) / two))));
151 excess = geometry::math::abs(excess);
153 // In right direction: positive, add area. In left direction: negative, subtract area.
154 // Longitude comparisons are not so obvious. If one is negative and other is positive,
155 // we have to take the dateline into account.
157 calculation_type lon_diff = geometry::get_as_radian<0>(p2)
158 - geometry::get_as_radian<0>(p1);
173 inline return_type result(excess_sum const& state) const
175 return state.area(m_radius);
179 /// Radius of the sphere
180 calculation_type m_radius;
183 #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
189 template <typename Point>
190 struct default_strategy<spherical_equatorial_tag, Point>
192 typedef strategy::area::huiller<Point> type;
195 // Note: spherical polar coordinate system requires "get_as_radian_equatorial"
196 /***template <typename Point>
197 struct default_strategy<spherical_polar_tag, Point>
199 typedef strategy::area::huiller<Point> type;
202 } // namespace services
204 #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
207 }} // namespace strategy::area
212 }} // namespace boost::geometry
214 #endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_HUILLER_HPP