Chris@16
|
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
|
Chris@16
|
2
|
Chris@16
|
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
Chris@16
|
4
|
Chris@16
|
5 // Use, modification and distribution is subject to the Boost Software License,
|
Chris@16
|
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
7 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
8
|
Chris@16
|
9 #ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_HUILLER_HPP
|
Chris@16
|
10 #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_HUILLER_HPP
|
Chris@16
|
11
|
Chris@16
|
12
|
Chris@16
|
13
|
Chris@16
|
14 #include <boost/geometry/strategies/spherical/distance_haversine.hpp>
|
Chris@16
|
15
|
Chris@16
|
16 #include <boost/geometry/core/radian_access.hpp>
|
Chris@16
|
17 #include <boost/geometry/util/math.hpp>
|
Chris@16
|
18
|
Chris@16
|
19
|
Chris@16
|
20 namespace boost { namespace geometry
|
Chris@16
|
21 {
|
Chris@16
|
22
|
Chris@16
|
23 namespace strategy { namespace area
|
Chris@16
|
24 {
|
Chris@16
|
25
|
Chris@16
|
26
|
Chris@16
|
27
|
Chris@16
|
28 /*!
|
Chris@16
|
29 \brief Area calculation by spherical excess / Huiller's formula
|
Chris@16
|
30 \ingroup strategies
|
Chris@16
|
31 \tparam PointOfSegment point type of segments of rings/polygons
|
Chris@16
|
32 \tparam CalculationType \tparam_calculation
|
Chris@16
|
33 \author Barend Gehrels. Adapted from:
|
Chris@16
|
34 - http://www.soe.ucsc.edu/~pang/160/f98/Gems/GemsIV/sph_poly.c
|
Chris@16
|
35 - http://williams.best.vwh.net/avform.htm
|
Chris@16
|
36 \note The version in Gems didn't account for polygons crossing the 180 meridian.
|
Chris@16
|
37 \note This version works for convex and non-convex polygons, for 180 meridian
|
Chris@16
|
38 crossing polygons and for polygons with holes. However, some cases (especially
|
Chris@16
|
39 180 meridian cases) must still be checked.
|
Chris@16
|
40 \note The version which sums angles, which is often seen, doesn't handle non-convex
|
Chris@16
|
41 polygons correctly.
|
Chris@16
|
42 \note The version which sums longitudes, see
|
Chris@16
|
43 http://trs-new.jpl.nasa.gov/dspace/bitstream/2014/40409/1/07-03.pdf, is simple
|
Chris@16
|
44 and works well in most cases but not in 180 meridian crossing cases. This probably
|
Chris@16
|
45 could be solved.
|
Chris@16
|
46
|
Chris@16
|
47 \note This version is made for spherical equatorial coordinate systems
|
Chris@16
|
48
|
Chris@16
|
49 \qbk{
|
Chris@16
|
50
|
Chris@16
|
51 [heading Example]
|
Chris@16
|
52 [area_with_strategy]
|
Chris@16
|
53 [area_with_strategy_output]
|
Chris@16
|
54
|
Chris@16
|
55
|
Chris@16
|
56 [heading See also]
|
Chris@16
|
57 [link geometry.reference.algorithms.area.area_2_with_strategy area (with strategy)]
|
Chris@16
|
58 }
|
Chris@16
|
59
|
Chris@16
|
60 */
|
Chris@16
|
61 template
|
Chris@16
|
62 <
|
Chris@16
|
63 typename PointOfSegment,
|
Chris@16
|
64 typename CalculationType = void
|
Chris@16
|
65 >
|
Chris@16
|
66 class huiller
|
Chris@16
|
67 {
|
Chris@16
|
68 typedef typename boost::mpl::if_c
|
Chris@16
|
69 <
|
Chris@16
|
70 boost::is_void<CalculationType>::type::value,
|
Chris@16
|
71 typename select_most_precise
|
Chris@16
|
72 <
|
Chris@16
|
73 typename coordinate_type<PointOfSegment>::type,
|
Chris@16
|
74 double
|
Chris@16
|
75 >::type,
|
Chris@16
|
76 CalculationType
|
Chris@16
|
77 >::type calculation_type;
|
Chris@16
|
78
|
Chris@16
|
79 protected :
|
Chris@16
|
80 struct excess_sum
|
Chris@16
|
81 {
|
Chris@16
|
82 calculation_type sum;
|
Chris@16
|
83
|
Chris@16
|
84 // Distances are calculated on unit sphere here
|
Chris@16
|
85 strategy::distance::haversine<calculation_type> distance_over_unit_sphere;
|
Chris@16
|
86
|
Chris@16
|
87
|
Chris@16
|
88 inline excess_sum()
|
Chris@16
|
89 : sum(0)
|
Chris@16
|
90 , distance_over_unit_sphere(1)
|
Chris@16
|
91 {}
|
Chris@16
|
92 inline calculation_type area(calculation_type radius) const
|
Chris@16
|
93 {
|
Chris@16
|
94 return - sum * radius * radius;
|
Chris@16
|
95 }
|
Chris@16
|
96 };
|
Chris@16
|
97
|
Chris@16
|
98 public :
|
Chris@16
|
99 typedef calculation_type return_type;
|
Chris@16
|
100 typedef PointOfSegment segment_point_type;
|
Chris@16
|
101 typedef excess_sum state_type;
|
Chris@16
|
102
|
Chris@16
|
103 inline huiller(calculation_type radius = 1.0)
|
Chris@16
|
104 : m_radius(radius)
|
Chris@16
|
105 {}
|
Chris@16
|
106
|
Chris@16
|
107 inline void apply(PointOfSegment const& p1,
|
Chris@16
|
108 PointOfSegment const& p2,
|
Chris@16
|
109 excess_sum& state) const
|
Chris@16
|
110 {
|
Chris@16
|
111 if (! geometry::math::equals(get<0>(p1), get<0>(p2)))
|
Chris@16
|
112 {
|
Chris@16
|
113 calculation_type const half = 0.5;
|
Chris@16
|
114 calculation_type const two = 2.0;
|
Chris@16
|
115 calculation_type const four = 4.0;
|
Chris@16
|
116 calculation_type const two_pi = two * geometry::math::pi<calculation_type>();
|
Chris@16
|
117 calculation_type const half_pi = half * geometry::math::pi<calculation_type>();
|
Chris@16
|
118
|
Chris@16
|
119 // Distance p1 p2
|
Chris@16
|
120 calculation_type a = state.distance_over_unit_sphere.apply(p1, p2);
|
Chris@16
|
121
|
Chris@16
|
122 // Sides on unit sphere to south pole
|
Chris@16
|
123 calculation_type b = half_pi - geometry::get_as_radian<1>(p2);
|
Chris@16
|
124 calculation_type c = half_pi - geometry::get_as_radian<1>(p1);
|
Chris@16
|
125
|
Chris@16
|
126 // Semi parameter
|
Chris@16
|
127 calculation_type s = half * (a + b + c);
|
Chris@16
|
128
|
Chris@16
|
129 // E: spherical excess, using l'Huiller's formula
|
Chris@16
|
130 // [tg(e / 4)]2 = tg[s / 2] tg[(s-a) / 2] tg[(s-b) / 2] tg[(s-c) / 2]
|
Chris@101
|
131 calculation_type E = four
|
Chris@101
|
132 * atan(geometry::math::sqrt(geometry::math::abs(tan(s / two)
|
Chris@16
|
133 * tan((s - a) / two)
|
Chris@16
|
134 * tan((s - b) / two)
|
Chris@16
|
135 * tan((s - c) / two))));
|
Chris@16
|
136
|
Chris@16
|
137 E = geometry::math::abs(E);
|
Chris@16
|
138
|
Chris@16
|
139 // In right direction: positive, add area. In left direction: negative, subtract area.
|
Chris@16
|
140 // Longitude comparisons are not so obvious. If one is negative, other is positive,
|
Chris@16
|
141 // we have to take the dateline into account.
|
Chris@16
|
142 // TODO: check this / enhance this, should be more robust. See also the "grow" for ll
|
Chris@16
|
143 // TODO: use minmax or "smaller"/"compare" strategy for this
|
Chris@16
|
144 calculation_type lon1 = geometry::get_as_radian<0>(p1) < 0
|
Chris@16
|
145 ? geometry::get_as_radian<0>(p1) + two_pi
|
Chris@16
|
146 : geometry::get_as_radian<0>(p1);
|
Chris@16
|
147
|
Chris@16
|
148 calculation_type lon2 = geometry::get_as_radian<0>(p2) < 0
|
Chris@16
|
149 ? geometry::get_as_radian<0>(p2) + two_pi
|
Chris@16
|
150 : geometry::get_as_radian<0>(p2);
|
Chris@16
|
151
|
Chris@16
|
152 if (lon2 < lon1)
|
Chris@16
|
153 {
|
Chris@16
|
154 E = -E;
|
Chris@16
|
155 }
|
Chris@16
|
156
|
Chris@16
|
157 state.sum += E;
|
Chris@16
|
158 }
|
Chris@16
|
159 }
|
Chris@16
|
160
|
Chris@16
|
161 inline return_type result(excess_sum const& state) const
|
Chris@16
|
162 {
|
Chris@16
|
163 return state.area(m_radius);
|
Chris@16
|
164 }
|
Chris@16
|
165
|
Chris@16
|
166 private :
|
Chris@16
|
167 /// Radius of the sphere
|
Chris@16
|
168 calculation_type m_radius;
|
Chris@16
|
169 };
|
Chris@16
|
170
|
Chris@16
|
171 #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
|
Chris@16
|
172
|
Chris@16
|
173 namespace services
|
Chris@16
|
174 {
|
Chris@16
|
175
|
Chris@16
|
176
|
Chris@16
|
177 template <typename Point>
|
Chris@16
|
178 struct default_strategy<spherical_equatorial_tag, Point>
|
Chris@16
|
179 {
|
Chris@16
|
180 typedef strategy::area::huiller<Point> type;
|
Chris@16
|
181 };
|
Chris@16
|
182
|
Chris@16
|
183 // Note: spherical polar coordinate system requires "get_as_radian_equatorial"
|
Chris@16
|
184 /***template <typename Point>
|
Chris@16
|
185 struct default_strategy<spherical_polar_tag, Point>
|
Chris@16
|
186 {
|
Chris@16
|
187 typedef strategy::area::huiller<Point> type;
|
Chris@16
|
188 };***/
|
Chris@16
|
189
|
Chris@16
|
190 } // namespace services
|
Chris@16
|
191
|
Chris@16
|
192 #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
|
Chris@16
|
193
|
Chris@16
|
194
|
Chris@16
|
195 }} // namespace strategy::area
|
Chris@16
|
196
|
Chris@16
|
197
|
Chris@16
|
198
|
Chris@16
|
199
|
Chris@16
|
200 }} // namespace boost::geometry
|
Chris@16
|
201
|
Chris@16
|
202 #endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_AREA_HUILLER_HPP
|