]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // Boost.Geometry |
2 | ||
3 | // Copyright (c) 2015-2016 Oracle and/or its affiliates. | |
4 | ||
5 | // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle | |
6 | ||
7 | // Use, modification and distribution is subject to the Boost Software License, | |
8 | // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | |
9 | // http://www.boost.org/LICENSE_1_0.txt) | |
10 | ||
11 | #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_ANDOYER_INVERSE_HPP | |
12 | #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_ANDOYER_INVERSE_HPP | |
13 | ||
14 | ||
15 | #include <boost/math/constants/constants.hpp> | |
16 | ||
17 | #include <boost/geometry/core/radius.hpp> | |
18 | #include <boost/geometry/core/srs.hpp> | |
19 | ||
20 | #include <boost/geometry/util/condition.hpp> | |
21 | #include <boost/geometry/util/math.hpp> | |
22 | ||
23 | #include <boost/geometry/algorithms/detail/flattening.hpp> | |
24 | #include <boost/geometry/algorithms/detail/result_inverse.hpp> | |
25 | ||
26 | ||
27 | namespace boost { namespace geometry { namespace detail | |
28 | { | |
29 | ||
30 | /*! | |
31 | \brief The solution of the inverse problem of geodesics on latlong coordinates, | |
32 | Forsyth-Andoyer-Lambert type approximation with first order terms. | |
33 | \author See | |
34 | - Technical Report: PAUL D. THOMAS, MATHEMATICAL MODELS FOR NAVIGATION SYSTEMS, 1965 | |
35 | http://www.dtic.mil/docs/citations/AD0627893 | |
36 | - Technical Report: PAUL D. THOMAS, SPHEROIDAL GEODESICS, REFERENCE SYSTEMS, AND LOCAL GEOMETRY, 1970 | |
37 | http://www.dtic.mil/docs/citations/AD703541 | |
38 | */ | |
39 | ||
40 | template <typename CT, bool EnableDistance, bool EnableAzimuth> | |
41 | struct andoyer_inverse | |
42 | { | |
43 | typedef result_inverse<CT> result_type; | |
44 | ||
45 | template <typename T1, typename T2, typename Spheroid> | |
46 | static inline result_type apply(T1 const& lon1, | |
47 | T1 const& lat1, | |
48 | T2 const& lon2, | |
49 | T2 const& lat2, | |
50 | Spheroid const& spheroid) | |
51 | { | |
52 | CT const c0 = CT(0); | |
53 | CT const c1 = CT(1); | |
54 | CT const pi = math::pi<CT>(); | |
55 | ||
56 | result_type result; | |
57 | ||
58 | // coordinates in radians | |
59 | ||
60 | if ( math::equals(lon1, lon2) && math::equals(lat1, lat2) ) | |
61 | { | |
62 | result.set(c0, c0); | |
63 | return result; | |
64 | } | |
65 | ||
66 | CT const dlon = lon2 - lon1; | |
67 | CT const sin_dlon = sin(dlon); | |
68 | CT const cos_dlon = cos(dlon); | |
69 | CT const sin_lat1 = sin(lat1); | |
70 | CT const cos_lat1 = cos(lat1); | |
71 | CT const sin_lat2 = sin(lat2); | |
72 | CT const cos_lat2 = cos(lat2); | |
73 | ||
74 | // H,G,T = infinity if cos_d = 1 or cos_d = -1 | |
75 | // lat1 == +-90 && lat2 == +-90 | |
76 | // lat1 == lat2 && lon1 == lon2 | |
77 | CT cos_d = sin_lat1*sin_lat2 + cos_lat1*cos_lat2*cos_dlon; | |
78 | // on some platforms cos_d may be outside valid range | |
79 | if (cos_d < -c1) | |
80 | cos_d = -c1; | |
81 | else if (cos_d > c1) | |
82 | cos_d = c1; | |
83 | ||
84 | CT const d = acos(cos_d); // [0, pi] | |
85 | CT const sin_d = sin(d); // [-1, 1] | |
86 | ||
87 | CT const f = detail::flattening<CT>(spheroid); | |
88 | ||
89 | if ( BOOST_GEOMETRY_CONDITION(EnableDistance) ) | |
90 | { | |
91 | CT const K = math::sqr(sin_lat1-sin_lat2); | |
92 | CT const L = math::sqr(sin_lat1+sin_lat2); | |
93 | CT const three_sin_d = CT(3) * sin_d; | |
94 | ||
95 | CT const one_minus_cos_d = c1 - cos_d; | |
96 | CT const one_plus_cos_d = c1 + cos_d; | |
97 | // cos_d = 1 or cos_d = -1 means that the points are antipodal | |
98 | ||
99 | CT const H = math::equals(one_minus_cos_d, c0) ? | |
100 | c0 : | |
101 | (d + three_sin_d) / one_minus_cos_d; | |
102 | CT const G = math::equals(one_plus_cos_d, c0) ? | |
103 | c0 : | |
104 | (d - three_sin_d) / one_plus_cos_d; | |
105 | ||
106 | CT const dd = -(f/CT(4))*(H*K+G*L); | |
107 | ||
108 | CT const a = get_radius<0>(spheroid); | |
109 | ||
110 | result.distance = a * (d + dd); | |
111 | } | |
112 | else | |
113 | { | |
114 | result.distance = c0; | |
115 | } | |
116 | ||
117 | if ( BOOST_GEOMETRY_CONDITION(EnableAzimuth) ) | |
118 | { | |
119 | // sin_d = 0 <=> antipodal points | |
120 | if (math::equals(sin_d, c0)) | |
121 | { | |
122 | // T = inf | |
123 | // dA = inf | |
124 | // azimuth = -inf | |
125 | if (lat1 <= lat2) | |
126 | result.azimuth = c0; | |
127 | else | |
128 | result.azimuth = pi; | |
129 | } | |
130 | else | |
131 | { | |
132 | CT const c2 = CT(2); | |
133 | ||
134 | CT A = c0; | |
135 | CT U = c0; | |
136 | if ( ! math::equals(cos_lat2, c0) ) | |
137 | { | |
138 | CT const tan_lat2 = sin_lat2/cos_lat2; | |
139 | CT const M = cos_lat1*tan_lat2-sin_lat1*cos_dlon; | |
140 | A = atan2(sin_dlon, M); | |
141 | CT const sin_2A = sin(c2*A); | |
142 | U = (f/ c2)*math::sqr(cos_lat1)*sin_2A; | |
143 | } | |
144 | ||
145 | CT V = c0; | |
146 | if ( ! math::equals(cos_lat1, c0) ) | |
147 | { | |
148 | CT const tan_lat1 = sin_lat1/cos_lat1; | |
149 | CT const N = cos_lat2*tan_lat1-sin_lat2*cos_dlon; | |
150 | CT const B = atan2(sin_dlon, N); | |
151 | CT const sin_2B = sin(c2*B); | |
152 | V = (f/ c2)*math::sqr(cos_lat2)*sin_2B; | |
153 | } | |
154 | ||
155 | CT const T = d / sin_d; | |
156 | CT const dA = V*T-U; | |
157 | ||
158 | result.azimuth = A - dA; | |
159 | ||
160 | // even with sin_d == 0 checked above if the second point | |
161 | // is somewhere in the antipodal area T may still be great | |
162 | // therefore dA may be great and the resulting azimuth | |
163 | // may be some more or less arbitrary angle | |
164 | if (A >= c0) // A indicates Eastern hemisphere | |
165 | { | |
166 | if (dA >= c0) // A altered towards 0 | |
167 | { | |
168 | if ((result.azimuth) < c0) | |
169 | result.azimuth = c0; | |
170 | } | |
171 | else // dA < 0, A altered towards pi | |
172 | { | |
173 | if (result.azimuth > pi) | |
174 | result.azimuth = pi; | |
175 | } | |
176 | } | |
177 | else // A indicates Western hemisphere | |
178 | { | |
179 | if (dA <= c0) // A altered towards 0 | |
180 | { | |
181 | if (result.azimuth > c0) | |
182 | result.azimuth = c0; | |
183 | } | |
184 | else // dA > 0, A altered towards -pi | |
185 | { | |
186 | CT const minus_pi = -pi; | |
187 | if ((result.azimuth) < minus_pi) | |
188 | result.azimuth = minus_pi; | |
189 | } | |
190 | } | |
191 | } | |
192 | } | |
193 | else | |
194 | { | |
195 | result.azimuth = c0; | |
196 | } | |
197 | ||
198 | return result; | |
199 | } | |
200 | }; | |
201 | ||
202 | }}} // namespace boost::geometry::detail | |
203 | ||
204 | ||
205 | #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_ANDOYER_INVERSE_HPP |