Chris@102
|
1 // Boost.Geometry
|
Chris@102
|
2
|
Chris@102
|
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
Chris@102
|
4
|
Chris@102
|
5 // This file was modified by Oracle on 2014.
|
Chris@102
|
6 // Modifications copyright (c) 2014 Oracle and/or its affiliates.
|
Chris@102
|
7
|
Chris@102
|
8 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
Chris@102
|
9
|
Chris@102
|
10 // Use, modification and distribution is subject to the Boost Software License,
|
Chris@102
|
11 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@102
|
12 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@102
|
13
|
Chris@102
|
14 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_INVERSE_HPP
|
Chris@102
|
15 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_INVERSE_HPP
|
Chris@102
|
16
|
Chris@102
|
17
|
Chris@102
|
18 #include <boost/math/constants/constants.hpp>
|
Chris@102
|
19
|
Chris@102
|
20 #include <boost/geometry/core/radius.hpp>
|
Chris@102
|
21 #include <boost/geometry/core/srs.hpp>
|
Chris@102
|
22
|
Chris@102
|
23 #include <boost/geometry/util/math.hpp>
|
Chris@102
|
24
|
Chris@102
|
25 #include <boost/geometry/algorithms/detail/flattening.hpp>
|
Chris@102
|
26
|
Chris@102
|
27
|
Chris@102
|
28 #ifndef BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS
|
Chris@102
|
29 #define BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS 1000
|
Chris@102
|
30 #endif
|
Chris@102
|
31
|
Chris@102
|
32
|
Chris@102
|
33 namespace boost { namespace geometry { namespace detail
|
Chris@102
|
34 {
|
Chris@102
|
35
|
Chris@102
|
36 /*!
|
Chris@102
|
37 \brief The solution of the inverse problem of geodesics on latlong coordinates, after Vincenty, 1975
|
Chris@102
|
38 \author See
|
Chris@102
|
39 - http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
|
Chris@102
|
40 - http://www.icsm.gov.au/gda/gdav2.3.pdf
|
Chris@102
|
41 \author Adapted from various implementations to get it close to the original document
|
Chris@102
|
42 - http://www.movable-type.co.uk/scripts/LatLongVincenty.html
|
Chris@102
|
43 - http://exogen.case.edu/projects/geopy/source/geopy.distance.html
|
Chris@102
|
44 - http://futureboy.homeip.net/fsp/colorize.fsp?fileName=navigation.frink
|
Chris@102
|
45
|
Chris@102
|
46 */
|
Chris@102
|
47 template <typename CT>
|
Chris@102
|
48 class vincenty_inverse
|
Chris@102
|
49 {
|
Chris@102
|
50 public:
|
Chris@102
|
51 template <typename T1, typename T2, typename Spheroid>
|
Chris@102
|
52 vincenty_inverse(T1 const& lon1,
|
Chris@102
|
53 T1 const& lat1,
|
Chris@102
|
54 T2 const& lon2,
|
Chris@102
|
55 T2 const& lat2,
|
Chris@102
|
56 Spheroid const& spheroid)
|
Chris@102
|
57 : is_result_zero(false)
|
Chris@102
|
58 {
|
Chris@102
|
59 if (math::equals(lat1, lat2) && math::equals(lon1, lon2))
|
Chris@102
|
60 {
|
Chris@102
|
61 is_result_zero = true;
|
Chris@102
|
62 return;
|
Chris@102
|
63 }
|
Chris@102
|
64
|
Chris@102
|
65 CT const c1 = 1;
|
Chris@102
|
66 CT const c2 = 2;
|
Chris@102
|
67 CT const c3 = 3;
|
Chris@102
|
68 CT const c4 = 4;
|
Chris@102
|
69 CT const c16 = 16;
|
Chris@102
|
70 CT const c_e_12 = CT(1e-12);
|
Chris@102
|
71
|
Chris@102
|
72 CT const pi = geometry::math::pi<CT>();
|
Chris@102
|
73 CT const two_pi = c2 * pi;
|
Chris@102
|
74
|
Chris@102
|
75 // lambda: difference in longitude on an auxiliary sphere
|
Chris@102
|
76 CT L = lon2 - lon1;
|
Chris@102
|
77 CT lambda = L;
|
Chris@102
|
78
|
Chris@102
|
79 if (L < -pi) L += two_pi;
|
Chris@102
|
80 if (L > pi) L -= two_pi;
|
Chris@102
|
81
|
Chris@102
|
82 radius_a = CT(get_radius<0>(spheroid));
|
Chris@102
|
83 radius_b = CT(get_radius<2>(spheroid));
|
Chris@102
|
84 CT const flattening = geometry::detail::flattening<CT>(spheroid);
|
Chris@102
|
85
|
Chris@102
|
86 // U: reduced latitude, defined by tan U = (1-f) tan phi
|
Chris@102
|
87 CT const one_min_f = c1 - flattening;
|
Chris@102
|
88 CT const tan_U1 = one_min_f * tan(lat1); // above (1)
|
Chris@102
|
89 CT const tan_U2 = one_min_f * tan(lat2); // above (1)
|
Chris@102
|
90
|
Chris@102
|
91 // calculate sin U and cos U using trigonometric identities
|
Chris@102
|
92 CT const temp_den_U1 = math::sqrt(c1 + math::sqr(tan_U1));
|
Chris@102
|
93 CT const temp_den_U2 = math::sqrt(c1 + math::sqr(tan_U2));
|
Chris@102
|
94 // cos = 1 / sqrt(1 + tan^2)
|
Chris@102
|
95 cos_U1 = c1 / temp_den_U1;
|
Chris@102
|
96 cos_U2 = c1 / temp_den_U2;
|
Chris@102
|
97 // sin = tan / sqrt(1 + tan^2)
|
Chris@102
|
98 sin_U1 = tan_U1 / temp_den_U1;
|
Chris@102
|
99 sin_U2 = tan_U2 / temp_den_U2;
|
Chris@102
|
100
|
Chris@102
|
101 // calculate sin U and cos U directly
|
Chris@102
|
102 //CT const U1 = atan(tan_U1);
|
Chris@102
|
103 //CT const U2 = atan(tan_U2);
|
Chris@102
|
104 //cos_U1 = cos(U1);
|
Chris@102
|
105 //cos_U2 = cos(U2);
|
Chris@102
|
106 //sin_U1 = tan_U1 * cos_U1; // sin(U1);
|
Chris@102
|
107 //sin_U2 = tan_U2 * cos_U2; // sin(U2);
|
Chris@102
|
108
|
Chris@102
|
109 CT previous_lambda;
|
Chris@102
|
110
|
Chris@102
|
111 int counter = 0; // robustness
|
Chris@102
|
112
|
Chris@102
|
113 do
|
Chris@102
|
114 {
|
Chris@102
|
115 previous_lambda = lambda; // (13)
|
Chris@102
|
116 sin_lambda = sin(lambda);
|
Chris@102
|
117 cos_lambda = cos(lambda);
|
Chris@102
|
118 sin_sigma = math::sqrt(math::sqr(cos_U2 * sin_lambda) + math::sqr(cos_U1 * sin_U2 - sin_U1 * cos_U2 * cos_lambda)); // (14)
|
Chris@102
|
119 CT cos_sigma = sin_U1 * sin_U2 + cos_U1 * cos_U2 * cos_lambda; // (15)
|
Chris@102
|
120 sin_alpha = cos_U1 * cos_U2 * sin_lambda / sin_sigma; // (17)
|
Chris@102
|
121 cos2_alpha = c1 - math::sqr(sin_alpha);
|
Chris@102
|
122 cos2_sigma_m = math::equals(cos2_alpha, 0) ? 0 : cos_sigma - c2 * sin_U1 * sin_U2 / cos2_alpha; // (18)
|
Chris@102
|
123
|
Chris@102
|
124 CT C = flattening/c16 * cos2_alpha * (c4 + flattening * (c4 - c3 * cos2_alpha)); // (10)
|
Chris@102
|
125 sigma = atan2(sin_sigma, cos_sigma); // (16)
|
Chris@102
|
126 lambda = L + (c1 - C) * flattening * sin_alpha *
|
Chris@102
|
127 (sigma + C * sin_sigma * ( cos2_sigma_m + C * cos_sigma * (-c1 + c2 * math::sqr(cos2_sigma_m)))); // (11)
|
Chris@102
|
128
|
Chris@102
|
129 ++counter; // robustness
|
Chris@102
|
130
|
Chris@102
|
131 } while ( geometry::math::abs(previous_lambda - lambda) > c_e_12
|
Chris@102
|
132 && geometry::math::abs(lambda) < pi
|
Chris@102
|
133 && counter < BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS ); // robustness
|
Chris@102
|
134 }
|
Chris@102
|
135
|
Chris@102
|
136 inline CT distance() const
|
Chris@102
|
137 {
|
Chris@102
|
138 if ( is_result_zero )
|
Chris@102
|
139 {
|
Chris@102
|
140 return CT(0);
|
Chris@102
|
141 }
|
Chris@102
|
142
|
Chris@102
|
143 // Oops getting hard here
|
Chris@102
|
144 // (again, problem is that ttmath cannot divide by doubles, which is OK)
|
Chris@102
|
145 CT const c1 = 1;
|
Chris@102
|
146 CT const c2 = 2;
|
Chris@102
|
147 CT const c3 = 3;
|
Chris@102
|
148 CT const c4 = 4;
|
Chris@102
|
149 CT const c6 = 6;
|
Chris@102
|
150 CT const c47 = 47;
|
Chris@102
|
151 CT const c74 = 74;
|
Chris@102
|
152 CT const c128 = 128;
|
Chris@102
|
153 CT const c256 = 256;
|
Chris@102
|
154 CT const c175 = 175;
|
Chris@102
|
155 CT const c320 = 320;
|
Chris@102
|
156 CT const c768 = 768;
|
Chris@102
|
157 CT const c1024 = 1024;
|
Chris@102
|
158 CT const c4096 = 4096;
|
Chris@102
|
159 CT const c16384 = 16384;
|
Chris@102
|
160
|
Chris@102
|
161 //CT sqr_u = cos2_alpha * (math::sqr(radius_a) - math::sqr(radius_b)) / math::sqr(radius_b); // above (1)
|
Chris@102
|
162 CT sqr_u = cos2_alpha * ( math::sqr(radius_a / radius_b) - c1 ); // above (1)
|
Chris@102
|
163
|
Chris@102
|
164 CT A = c1 + sqr_u/c16384 * (c4096 + sqr_u * (-c768 + sqr_u * (c320 - c175 * sqr_u))); // (3)
|
Chris@102
|
165 CT B = sqr_u/c1024 * (c256 + sqr_u * ( -c128 + sqr_u * (c74 - c47 * sqr_u))); // (4)
|
Chris@102
|
166 CT delta_sigma = B * sin_sigma * ( cos2_sigma_m + (B/c4) * (cos(sigma)* (-c1 + c2 * cos2_sigma_m)
|
Chris@102
|
167 - (B/c6) * cos2_sigma_m * (-c3 + c4 * math::sqr(sin_sigma)) * (-c3 + c4 * cos2_sigma_m))); // (6)
|
Chris@102
|
168
|
Chris@102
|
169 return radius_b * A * (sigma - delta_sigma); // (19)
|
Chris@102
|
170 }
|
Chris@102
|
171
|
Chris@102
|
172 inline CT azimuth12() const
|
Chris@102
|
173 {
|
Chris@102
|
174 return is_result_zero ?
|
Chris@102
|
175 CT(0) :
|
Chris@102
|
176 atan2(cos_U2 * sin_lambda, cos_U1 * sin_U2 - sin_U1 * cos_U2 * cos_lambda); // (20)
|
Chris@102
|
177 }
|
Chris@102
|
178
|
Chris@102
|
179 inline CT azimuth21() const
|
Chris@102
|
180 {
|
Chris@102
|
181 // NOTE: signs of X and Y are different than in the original paper
|
Chris@102
|
182 return is_result_zero ?
|
Chris@102
|
183 CT(0) :
|
Chris@102
|
184 atan2(-cos_U1 * sin_lambda, sin_U1 * cos_U2 - cos_U1 * sin_U2 * cos_lambda); // (21)
|
Chris@102
|
185 }
|
Chris@102
|
186
|
Chris@102
|
187 private:
|
Chris@102
|
188 // alpha: azimuth of the geodesic at the equator
|
Chris@102
|
189 CT cos2_alpha;
|
Chris@102
|
190 CT sin_alpha;
|
Chris@102
|
191
|
Chris@102
|
192 // sigma: angular distance p1,p2 on the sphere
|
Chris@102
|
193 // sigma1: angular distance on the sphere from the equator to p1
|
Chris@102
|
194 // sigma_m: angular distance on the sphere from the equator to the midpoint of the line
|
Chris@102
|
195 CT sigma;
|
Chris@102
|
196 CT sin_sigma;
|
Chris@102
|
197 CT cos2_sigma_m;
|
Chris@102
|
198
|
Chris@102
|
199 CT sin_lambda;
|
Chris@102
|
200 CT cos_lambda;
|
Chris@102
|
201
|
Chris@102
|
202 // set only once
|
Chris@102
|
203 CT cos_U1;
|
Chris@102
|
204 CT cos_U2;
|
Chris@102
|
205 CT sin_U1;
|
Chris@102
|
206 CT sin_U2;
|
Chris@102
|
207
|
Chris@102
|
208 // set only once
|
Chris@102
|
209 CT radius_a;
|
Chris@102
|
210 CT radius_b;
|
Chris@102
|
211
|
Chris@102
|
212 bool is_result_zero;
|
Chris@102
|
213 };
|
Chris@102
|
214
|
Chris@102
|
215 }}} // namespace boost::geometry::detail
|
Chris@102
|
216
|
Chris@102
|
217
|
Chris@102
|
218 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_INVERSE_HPP
|