Chris@16
|
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
|
Chris@16
|
2
|
Chris@101
|
3 // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
|
Chris@101
|
4
|
Chris@101
|
5 // This file was modified by Oracle on 2014.
|
Chris@101
|
6 // Modifications copyright (c) 2014, Oracle and/or its affiliates.
|
Chris@101
|
7
|
Chris@101
|
8 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
|
Chris@16
|
9
|
Chris@16
|
10 // Use, modification and distribution is subject to the Boost Software License,
|
Chris@16
|
11 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
12 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
13
|
Chris@16
|
14 #ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_HPP
|
Chris@16
|
15 #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_HPP
|
Chris@16
|
16
|
Chris@101
|
17 #include <algorithm>
|
Chris@16
|
18
|
Chris@16
|
19 #include <boost/config.hpp>
|
Chris@16
|
20 #include <boost/concept_check.hpp>
|
Chris@16
|
21 #include <boost/mpl/if.hpp>
|
Chris@101
|
22 #include <boost/type_traits/is_void.hpp>
|
Chris@16
|
23
|
Chris@16
|
24 #include <boost/geometry/core/cs.hpp>
|
Chris@16
|
25 #include <boost/geometry/core/access.hpp>
|
Chris@16
|
26 #include <boost/geometry/core/radian_access.hpp>
|
Chris@101
|
27 #include <boost/geometry/core/tags.hpp>
|
Chris@16
|
28
|
Chris@101
|
29 #include <boost/geometry/algorithms/detail/course.hpp>
|
Chris@16
|
30
|
Chris@16
|
31 #include <boost/geometry/strategies/distance.hpp>
|
Chris@16
|
32 #include <boost/geometry/strategies/concepts/distance_concept.hpp>
|
Chris@16
|
33 #include <boost/geometry/strategies/spherical/distance_haversine.hpp>
|
Chris@16
|
34
|
Chris@101
|
35 #include <boost/geometry/util/math.hpp>
|
Chris@16
|
36 #include <boost/geometry/util/promote_floating_point.hpp>
|
Chris@101
|
37 #include <boost/geometry/util/select_calculation_type.hpp>
|
Chris@16
|
38
|
Chris@16
|
39 #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
|
Chris@16
|
40 # include <boost/geometry/io/dsv/write.hpp>
|
Chris@16
|
41 #endif
|
Chris@16
|
42
|
Chris@16
|
43
|
Chris@16
|
44 namespace boost { namespace geometry
|
Chris@16
|
45 {
|
Chris@16
|
46
|
Chris@16
|
47 namespace strategy { namespace distance
|
Chris@16
|
48 {
|
Chris@16
|
49
|
Chris@101
|
50
|
Chris@101
|
51 namespace comparable
|
Chris@101
|
52 {
|
Chris@101
|
53
|
Chris@101
|
54 /*
|
Chris@101
|
55 Given a spherical segment AB and a point D, we are interested in
|
Chris@101
|
56 computing the distance of D from AB. This is usually known as the
|
Chris@101
|
57 cross track distance.
|
Chris@101
|
58
|
Chris@101
|
59 If the projection (along great circles) of the point D lies inside
|
Chris@101
|
60 the segment AB, then the distance (cross track error) XTD is given
|
Chris@101
|
61 by the formula (see http://williams.best.vwh.net/avform.htm#XTE):
|
Chris@101
|
62
|
Chris@101
|
63 XTD = asin( sin(dist_AD) * sin(crs_AD-crs_AB) )
|
Chris@101
|
64
|
Chris@101
|
65 where dist_AD is the great circle distance between the points A and
|
Chris@101
|
66 B, and crs_AD, crs_AB is the course (bearing) between the points A,
|
Chris@101
|
67 D and A, B, respectively.
|
Chris@101
|
68
|
Chris@101
|
69 If the point D does not project inside the arc AB, then the distance
|
Chris@101
|
70 of D from AB is the minimum of the two distances dist_AD and dist_BD.
|
Chris@101
|
71
|
Chris@101
|
72 Our reference implementation for this procedure is listed below
|
Chris@101
|
73 (this was the old Boost.Geometry implementation of the cross track distance),
|
Chris@101
|
74 where:
|
Chris@101
|
75 * The member variable m_strategy is the underlying haversine strategy.
|
Chris@101
|
76 * p stands for the point D.
|
Chris@101
|
77 * sp1 stands for the segment endpoint A.
|
Chris@101
|
78 * sp2 stands for the segment endpoint B.
|
Chris@101
|
79
|
Chris@101
|
80 ================= reference implementation -- start =================
|
Chris@101
|
81
|
Chris@101
|
82 return_type d1 = m_strategy.apply(sp1, p);
|
Chris@101
|
83 return_type d3 = m_strategy.apply(sp1, sp2);
|
Chris@101
|
84
|
Chris@101
|
85 if (geometry::math::equals(d3, 0.0))
|
Chris@101
|
86 {
|
Chris@101
|
87 // "Degenerate" segment, return either d1 or d2
|
Chris@101
|
88 return d1;
|
Chris@101
|
89 }
|
Chris@101
|
90
|
Chris@101
|
91 return_type d2 = m_strategy.apply(sp2, p);
|
Chris@101
|
92
|
Chris@101
|
93 return_type crs_AD = geometry::detail::course<return_type>(sp1, p);
|
Chris@101
|
94 return_type crs_AB = geometry::detail::course<return_type>(sp1, sp2);
|
Chris@101
|
95 return_type crs_BA = crs_AB - geometry::math::pi<return_type>();
|
Chris@101
|
96 return_type crs_BD = geometry::detail::course<return_type>(sp2, p);
|
Chris@101
|
97 return_type d_crs1 = crs_AD - crs_AB;
|
Chris@101
|
98 return_type d_crs2 = crs_BD - crs_BA;
|
Chris@101
|
99
|
Chris@101
|
100 // d1, d2, d3 are in principle not needed, only the sign matters
|
Chris@101
|
101 return_type projection1 = cos( d_crs1 ) * d1 / d3;
|
Chris@101
|
102 return_type projection2 = cos( d_crs2 ) * d2 / d3;
|
Chris@101
|
103
|
Chris@101
|
104 if (projection1 > 0.0 && projection2 > 0.0)
|
Chris@101
|
105 {
|
Chris@101
|
106 return_type XTD
|
Chris@101
|
107 = radius() * math::abs( asin( sin( d1 / radius() ) * sin( d_crs1 ) ));
|
Chris@101
|
108
|
Chris@101
|
109 // Return shortest distance, projected point on segment sp1-sp2
|
Chris@101
|
110 return return_type(XTD);
|
Chris@101
|
111 }
|
Chris@101
|
112 else
|
Chris@101
|
113 {
|
Chris@101
|
114 // Return shortest distance, project either on point sp1 or sp2
|
Chris@101
|
115 return return_type( (std::min)( d1 , d2 ) );
|
Chris@101
|
116 }
|
Chris@101
|
117
|
Chris@101
|
118 ================= reference implementation -- end =================
|
Chris@101
|
119
|
Chris@101
|
120
|
Chris@101
|
121 Motivation
|
Chris@101
|
122 ----------
|
Chris@101
|
123 In what follows we develop a comparable version of the cross track
|
Chris@101
|
124 distance strategy, that meets the following goals:
|
Chris@101
|
125 * It is more efficient than the original cross track strategy (less
|
Chris@101
|
126 operations and less calls to mathematical functions).
|
Chris@101
|
127 * Distances using the comparable cross track strategy can not only
|
Chris@101
|
128 be compared with other distances using the same strategy, but also with
|
Chris@101
|
129 distances computed with the comparable version of the haversine strategy.
|
Chris@101
|
130 * It can serve as the basis for the computation of the cross track distance,
|
Chris@101
|
131 as it is more efficient to compute its comparable version and
|
Chris@101
|
132 transform that to the actual cross track distance, rather than
|
Chris@101
|
133 follow/use the reference implementation listed above.
|
Chris@101
|
134
|
Chris@101
|
135 Major idea
|
Chris@101
|
136 ----------
|
Chris@101
|
137 The idea here is to use the comparable haversine strategy to compute
|
Chris@101
|
138 the distances d1, d2 and d3 in the above listing. Once we have done
|
Chris@101
|
139 that we need also to make sure that instead of returning XTD (as
|
Chris@101
|
140 computed above) that we return a distance CXTD that is compatible
|
Chris@101
|
141 with the comparable haversine distance. To achieve this CXTD must satisfy
|
Chris@101
|
142 the relation:
|
Chris@101
|
143 XTD = 2 * R * asin( sqrt(XTD) )
|
Chris@101
|
144 where R is the sphere's radius.
|
Chris@101
|
145
|
Chris@101
|
146 Below we perform the mathematical analysis that show how to compute CXTD.
|
Chris@101
|
147
|
Chris@101
|
148
|
Chris@101
|
149 Mathematical analysis
|
Chris@101
|
150 ---------------------
|
Chris@101
|
151 Below we use the following trigonometric identities:
|
Chris@101
|
152 sin(2 * x) = 2 * sin(x) * cos(x)
|
Chris@101
|
153 cos(asin(x)) = sqrt(1 - x^2)
|
Chris@101
|
154
|
Chris@101
|
155 Observation:
|
Chris@101
|
156 The distance d1 needed when the projection of the point D is within the
|
Chris@101
|
157 segment must be the true distance. However, comparable::haversine<>
|
Chris@101
|
158 returns a comparable distance instead of the one needed.
|
Chris@101
|
159 To remedy this, we implicitly compute what is needed.
|
Chris@101
|
160 More precisely, we need to compute sin(true_d1):
|
Chris@101
|
161
|
Chris@101
|
162 sin(true_d1) = sin(2 * asin(sqrt(d1)))
|
Chris@101
|
163 = 2 * sin(asin(sqrt(d1)) * cos(asin(sqrt(d1)))
|
Chris@101
|
164 = 2 * sqrt(d1) * sqrt(1-(sqrt(d1))^2)
|
Chris@101
|
165 = 2 * sqrt(d1 - d1 * d1)
|
Chris@101
|
166 This relation is used below.
|
Chris@101
|
167
|
Chris@101
|
168 As we mentioned above the goal is to find CXTD (named "a" below for
|
Chris@101
|
169 brevity) such that ("b" below stands for "d1", and "c" for "d_crs1"):
|
Chris@101
|
170
|
Chris@101
|
171 2 * R * asin(sqrt(a)) == R * asin(2 * sqrt(b-b^2) * sin(c))
|
Chris@101
|
172
|
Chris@101
|
173 Analysis:
|
Chris@101
|
174 2 * R * asin(sqrt(a)) == R * asin(2 * sqrt(b-b^2) * sin(c))
|
Chris@101
|
175 <=> 2 * asin(sqrt(a)) == asin(sqrt(b-b^2) * sin(c))
|
Chris@101
|
176 <=> sin(2 * asin(sqrt(a))) == 2 * sqrt(b-b^2) * sin(c)
|
Chris@101
|
177 <=> 2 * sin(asin(sqrt(a))) * cos(asin(sqrt(a))) == 2 * sqrt(b-b^2) * sin(c)
|
Chris@101
|
178 <=> 2 * sqrt(a) * sqrt(1-a) == 2 * sqrt(b-b^2) * sin(c)
|
Chris@101
|
179 <=> sqrt(a) * sqrt(1-a) == sqrt(b-b^2) * sin(c)
|
Chris@101
|
180 <=> sqrt(a-a^2) == sqrt(b-b^2) * sin(c)
|
Chris@101
|
181 <=> a-a^2 == (b-b^2) * (sin(c))^2
|
Chris@101
|
182
|
Chris@101
|
183 Consider the quadratic equation: x^2-x+p^2 == 0,
|
Chris@101
|
184 where p = sqrt(b-b^2) * sin(c); its discriminant is:
|
Chris@101
|
185 d = 1 - 4 * p^2 = 1 - 4 * (b-b^2) * (sin(c))^2
|
Chris@101
|
186
|
Chris@101
|
187 The two solutions are:
|
Chris@101
|
188 a_1 = (1 - sqrt(d)) / 2
|
Chris@101
|
189 a_2 = (1 + sqrt(d)) / 2
|
Chris@101
|
190
|
Chris@101
|
191 Which one to choose?
|
Chris@101
|
192 "a" refers to the distance (on the unit sphere) of D from the
|
Chris@101
|
193 supporting great circle Circ(A,B) of the segment AB.
|
Chris@101
|
194 The two different values for "a" correspond to the lengths of the two
|
Chris@101
|
195 arcs delimited D and the points of intersection of Circ(A,B) and the
|
Chris@101
|
196 great circle perperdicular to Circ(A,B) passing through D.
|
Chris@101
|
197 Clearly, the value we want is the smallest among these two distances,
|
Chris@101
|
198 hence the root we must choose is the smallest root among the two.
|
Chris@101
|
199
|
Chris@101
|
200 So the answer is:
|
Chris@101
|
201 CXTD = ( 1 - sqrt(1 - 4 * (b-b^2) * (sin(c))^2) ) / 2
|
Chris@101
|
202
|
Chris@101
|
203 Therefore, in order to implement the comparable version of the cross
|
Chris@101
|
204 track strategy we need to:
|
Chris@101
|
205 (1) Use the comparable version of the haversine strategy instead of
|
Chris@101
|
206 the non-comparable one.
|
Chris@101
|
207 (2) Instead of return XTD when D projects inside the segment AB, we
|
Chris@101
|
208 need to return CXTD, given by the following formula:
|
Chris@101
|
209 CXTD = ( 1 - sqrt(1 - 4 * (d1-d1^2) * (sin(d_crs1))^2) ) / 2;
|
Chris@101
|
210
|
Chris@101
|
211
|
Chris@101
|
212 Complexity Analysis
|
Chris@101
|
213 -------------------
|
Chris@101
|
214 In the analysis that follows we refer to the actual implementation below.
|
Chris@101
|
215 In particular, instead of computing CXTD as above, we use the more
|
Chris@101
|
216 efficient (operation-wise) computation of CXTD shown here:
|
Chris@101
|
217
|
Chris@101
|
218 return_type sin_d_crs1 = sin(d_crs1);
|
Chris@101
|
219 return_type d1_x_sin = d1 * sin_d_crs1;
|
Chris@101
|
220 return_type d = d1_x_sin * (sin_d_crs1 - d1_x_sin);
|
Chris@101
|
221 return d / (0.5 + math::sqrt(0.25 - d));
|
Chris@101
|
222
|
Chris@101
|
223 Notice that instead of computing:
|
Chris@101
|
224 0.5 - 0.5 * sqrt(1 - 4 * d) = 0.5 - sqrt(0.25 - d)
|
Chris@101
|
225 we use the following formula instead:
|
Chris@101
|
226 d / (0.5 + sqrt(0.25 - d)).
|
Chris@101
|
227 This is done for numerical robustness. The expression 0.5 - sqrt(0.25 - x)
|
Chris@101
|
228 has large numerical errors for values of x close to 0 (if using doubles
|
Chris@101
|
229 the error start to become large even when d is as large as 0.001).
|
Chris@101
|
230 To remedy that, we re-write 0.5 - sqrt(0.25 - x) as:
|
Chris@101
|
231 0.5 - sqrt(0.25 - d)
|
Chris@101
|
232 = (0.5 - sqrt(0.25 - d) * (0.5 - sqrt(0.25 - d)) / (0.5 + sqrt(0.25 - d)).
|
Chris@101
|
233 The numerator is the difference of two squares:
|
Chris@101
|
234 (0.5 - sqrt(0.25 - d) * (0.5 - sqrt(0.25 - d))
|
Chris@101
|
235 = 0.5^2 - (sqrt(0.25 - d))^ = 0.25 - (0.25 - d) = d,
|
Chris@101
|
236 which gives the expression we use.
|
Chris@101
|
237
|
Chris@101
|
238 For the complexity analysis, we distinguish between two cases:
|
Chris@101
|
239 (A) The distance is realized between the point D and an
|
Chris@101
|
240 endpoint of the segment AB
|
Chris@101
|
241
|
Chris@101
|
242 Gains:
|
Chris@101
|
243 Since we are using comparable::haversine<> which is called
|
Chris@101
|
244 3 times, we gain:
|
Chris@101
|
245 -> 3 calls to sqrt
|
Chris@101
|
246 -> 3 calls to asin
|
Chris@101
|
247 -> 6 multiplications
|
Chris@101
|
248
|
Chris@101
|
249 Loses: None
|
Chris@101
|
250
|
Chris@101
|
251 So the net gain is:
|
Chris@101
|
252 -> 6 function calls (sqrt/asin)
|
Chris@101
|
253 -> 6 arithmetic operations
|
Chris@101
|
254
|
Chris@101
|
255 If we use comparable::cross_track<> to compute
|
Chris@101
|
256 cross_track<> we need to account for a call to sqrt, a call
|
Chris@101
|
257 to asin and 2 multiplications. In this case the net gain is:
|
Chris@101
|
258 -> 4 function calls (sqrt/asin)
|
Chris@101
|
259 -> 4 arithmetic operations
|
Chris@101
|
260
|
Chris@101
|
261
|
Chris@101
|
262 (B) The distance is realized between the point D and an
|
Chris@101
|
263 interior point of the segment AB
|
Chris@101
|
264
|
Chris@101
|
265 Gains:
|
Chris@101
|
266 Since we are using comparable::haversine<> which is called
|
Chris@101
|
267 3 times, we gain:
|
Chris@101
|
268 -> 3 calls to sqrt
|
Chris@101
|
269 -> 3 calls to asin
|
Chris@101
|
270 -> 6 multiplications
|
Chris@101
|
271 Also we gain the operations used to compute XTD:
|
Chris@101
|
272 -> 2 calls to sin
|
Chris@101
|
273 -> 1 call to asin
|
Chris@101
|
274 -> 1 call to abs
|
Chris@101
|
275 -> 2 multiplications
|
Chris@101
|
276 -> 1 division
|
Chris@101
|
277 So the total gains are:
|
Chris@101
|
278 -> 9 calls to sqrt/sin/asin
|
Chris@101
|
279 -> 1 call to abs
|
Chris@101
|
280 -> 8 multiplications
|
Chris@101
|
281 -> 1 division
|
Chris@101
|
282
|
Chris@101
|
283 Loses:
|
Chris@101
|
284 To compute a distance compatible with comparable::haversine<>
|
Chris@101
|
285 we need to perform a few more operations, namely:
|
Chris@101
|
286 -> 1 call to sin
|
Chris@101
|
287 -> 1 call to sqrt
|
Chris@101
|
288 -> 2 multiplications
|
Chris@101
|
289 -> 1 division
|
Chris@101
|
290 -> 1 addition
|
Chris@101
|
291 -> 2 subtractions
|
Chris@101
|
292
|
Chris@101
|
293 So roughly speaking the net gain is:
|
Chris@101
|
294 -> 8 fewer function calls and 3 fewer arithmetic operations
|
Chris@101
|
295
|
Chris@101
|
296 If we were to implement cross_track directly from the
|
Chris@101
|
297 comparable version (much like what haversine<> does using
|
Chris@101
|
298 comparable::haversine<>) we need additionally
|
Chris@101
|
299 -> 2 function calls (asin/sqrt)
|
Chris@101
|
300 -> 2 multiplications
|
Chris@101
|
301
|
Chris@101
|
302 So it pays off to re-implement cross_track<> to use
|
Chris@101
|
303 comparable::cross_track<>; in this case the net gain would be:
|
Chris@101
|
304 -> 6 function calls
|
Chris@101
|
305 -> 1 arithmetic operation
|
Chris@101
|
306
|
Chris@101
|
307 Summary/Conclusion
|
Chris@101
|
308 ------------------
|
Chris@101
|
309 Following the mathematical and complexity analysis above, the
|
Chris@101
|
310 comparable cross track strategy (as implemented below) satisfies
|
Chris@101
|
311 all the goal mentioned in the beginning:
|
Chris@101
|
312 * It is more efficient than its non-comparable counter-part.
|
Chris@101
|
313 * Comparable distances using this new strategy can also be compared
|
Chris@101
|
314 with comparable distances computed with the comparable haversine
|
Chris@101
|
315 strategy.
|
Chris@101
|
316 * It turns out to be more efficient to compute the actual cross
|
Chris@101
|
317 track distance XTD by first computing CXTD, and then computing
|
Chris@101
|
318 XTD by means of the formula:
|
Chris@101
|
319 XTD = 2 * R * asin( sqrt(CXTD) )
|
Chris@101
|
320 */
|
Chris@101
|
321
|
Chris@101
|
322 template
|
Chris@101
|
323 <
|
Chris@101
|
324 typename CalculationType = void,
|
Chris@101
|
325 typename Strategy = comparable::haversine<double, CalculationType>
|
Chris@101
|
326 >
|
Chris@101
|
327 class cross_track
|
Chris@101
|
328 {
|
Chris@101
|
329 public :
|
Chris@101
|
330 template <typename Point, typename PointOfSegment>
|
Chris@101
|
331 struct return_type
|
Chris@101
|
332 : promote_floating_point
|
Chris@101
|
333 <
|
Chris@101
|
334 typename select_calculation_type
|
Chris@101
|
335 <
|
Chris@101
|
336 Point,
|
Chris@101
|
337 PointOfSegment,
|
Chris@101
|
338 CalculationType
|
Chris@101
|
339 >::type
|
Chris@101
|
340 >
|
Chris@101
|
341 {};
|
Chris@101
|
342
|
Chris@101
|
343 inline cross_track()
|
Chris@101
|
344 {}
|
Chris@101
|
345
|
Chris@101
|
346 explicit inline cross_track(typename Strategy::radius_type const& r)
|
Chris@101
|
347 : m_strategy(r)
|
Chris@101
|
348 {}
|
Chris@101
|
349
|
Chris@101
|
350 inline cross_track(Strategy const& s)
|
Chris@101
|
351 : m_strategy(s)
|
Chris@101
|
352 {}
|
Chris@101
|
353
|
Chris@101
|
354
|
Chris@101
|
355 // It might be useful in the future
|
Chris@101
|
356 // to overload constructor with strategy info.
|
Chris@101
|
357 // crosstrack(...) {}
|
Chris@101
|
358
|
Chris@101
|
359
|
Chris@101
|
360 template <typename Point, typename PointOfSegment>
|
Chris@101
|
361 inline typename return_type<Point, PointOfSegment>::type
|
Chris@101
|
362 apply(Point const& p, PointOfSegment const& sp1, PointOfSegment const& sp2) const
|
Chris@101
|
363 {
|
Chris@101
|
364
|
Chris@101
|
365 #if !defined(BOOST_MSVC)
|
Chris@101
|
366 BOOST_CONCEPT_ASSERT
|
Chris@101
|
367 (
|
Chris@101
|
368 (concept::PointDistanceStrategy<Strategy, Point, PointOfSegment>)
|
Chris@101
|
369 );
|
Chris@101
|
370 #endif
|
Chris@101
|
371
|
Chris@101
|
372 typedef typename return_type<Point, PointOfSegment>::type return_type;
|
Chris@101
|
373
|
Chris@101
|
374 #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
|
Chris@101
|
375 std::cout << "Course " << dsv(sp1) << " to " << dsv(p) << " " << crs_AD * geometry::math::r2d << std::endl;
|
Chris@101
|
376 std::cout << "Course " << dsv(sp1) << " to " << dsv(sp2) << " " << crs_AB * geometry::math::r2d << std::endl;
|
Chris@101
|
377 std::cout << "Course " << dsv(sp2) << " to " << dsv(p) << " " << crs_BD * geometry::math::r2d << std::endl;
|
Chris@101
|
378 std::cout << "Projection AD-AB " << projection1 << " : " << d_crs1 * geometry::math::r2d << std::endl;
|
Chris@101
|
379 std::cout << "Projection BD-BA " << projection2 << " : " << d_crs2 * geometry::math::r2d << std::endl;
|
Chris@101
|
380 #endif
|
Chris@101
|
381
|
Chris@101
|
382 // http://williams.best.vwh.net/avform.htm#XTE
|
Chris@101
|
383 return_type d1 = m_strategy.apply(sp1, p);
|
Chris@101
|
384 return_type d3 = m_strategy.apply(sp1, sp2);
|
Chris@101
|
385
|
Chris@101
|
386 if (geometry::math::equals(d3, 0.0))
|
Chris@101
|
387 {
|
Chris@101
|
388 // "Degenerate" segment, return either d1 or d2
|
Chris@101
|
389 return d1;
|
Chris@101
|
390 }
|
Chris@101
|
391
|
Chris@101
|
392 return_type d2 = m_strategy.apply(sp2, p);
|
Chris@101
|
393
|
Chris@101
|
394 return_type crs_AD = geometry::detail::course<return_type>(sp1, p);
|
Chris@101
|
395 return_type crs_AB = geometry::detail::course<return_type>(sp1, sp2);
|
Chris@101
|
396 return_type crs_BA = crs_AB - geometry::math::pi<return_type>();
|
Chris@101
|
397 return_type crs_BD = geometry::detail::course<return_type>(sp2, p);
|
Chris@101
|
398 return_type d_crs1 = crs_AD - crs_AB;
|
Chris@101
|
399 return_type d_crs2 = crs_BD - crs_BA;
|
Chris@101
|
400
|
Chris@101
|
401 // d1, d2, d3 are in principle not needed, only the sign matters
|
Chris@101
|
402 return_type projection1 = cos( d_crs1 ) * d1 / d3;
|
Chris@101
|
403 return_type projection2 = cos( d_crs2 ) * d2 / d3;
|
Chris@101
|
404
|
Chris@101
|
405 if (projection1 > 0.0 && projection2 > 0.0)
|
Chris@101
|
406 {
|
Chris@101
|
407 #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
|
Chris@101
|
408 return_type XTD = radius() * geometry::math::abs( asin( sin( d1 ) * sin( d_crs1 ) ));
|
Chris@101
|
409
|
Chris@101
|
410 std::cout << "Projection ON the segment" << std::endl;
|
Chris@101
|
411 std::cout << "XTD: " << XTD
|
Chris@101
|
412 << " d1: " << (d1 * radius())
|
Chris@101
|
413 << " d2: " << (d2 * radius())
|
Chris@101
|
414 << std::endl;
|
Chris@101
|
415 #endif
|
Chris@101
|
416 return_type const half(0.5);
|
Chris@101
|
417 return_type const quarter(0.25);
|
Chris@101
|
418
|
Chris@101
|
419 return_type sin_d_crs1 = sin(d_crs1);
|
Chris@101
|
420 /*
|
Chris@101
|
421 This is the straightforward obvious way to continue:
|
Chris@101
|
422
|
Chris@101
|
423 return_type discriminant
|
Chris@101
|
424 = 1.0 - 4.0 * (d1 - d1 * d1) * sin_d_crs1 * sin_d_crs1;
|
Chris@101
|
425 return 0.5 - 0.5 * math::sqrt(discriminant);
|
Chris@101
|
426
|
Chris@101
|
427 Below we optimize the number of arithmetic operations
|
Chris@101
|
428 and account for numerical robustness:
|
Chris@101
|
429 */
|
Chris@101
|
430 return_type d1_x_sin = d1 * sin_d_crs1;
|
Chris@101
|
431 return_type d = d1_x_sin * (sin_d_crs1 - d1_x_sin);
|
Chris@101
|
432 return d / (half + math::sqrt(quarter - d));
|
Chris@101
|
433 }
|
Chris@101
|
434 else
|
Chris@101
|
435 {
|
Chris@101
|
436 #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK
|
Chris@101
|
437 std::cout << "Projection OUTSIDE the segment" << std::endl;
|
Chris@101
|
438 #endif
|
Chris@101
|
439
|
Chris@101
|
440 // Return shortest distance, project either on point sp1 or sp2
|
Chris@101
|
441 return return_type( (std::min)( d1 , d2 ) );
|
Chris@101
|
442 }
|
Chris@101
|
443 }
|
Chris@101
|
444
|
Chris@101
|
445 inline typename Strategy::radius_type radius() const
|
Chris@101
|
446 { return m_strategy.radius(); }
|
Chris@101
|
447
|
Chris@101
|
448 private :
|
Chris@101
|
449 Strategy m_strategy;
|
Chris@101
|
450 };
|
Chris@101
|
451
|
Chris@101
|
452 } // namespace comparable
|
Chris@101
|
453
|
Chris@101
|
454
|
Chris@16
|
455 /*!
|
Chris@16
|
456 \brief Strategy functor for distance point to segment calculation
|
Chris@16
|
457 \ingroup strategies
|
Chris@16
|
458 \details Class which calculates the distance of a point to a segment, for points on a sphere or globe
|
Chris@16
|
459 \see http://williams.best.vwh.net/avform.htm
|
Chris@16
|
460 \tparam CalculationType \tparam_calculation
|
Chris@16
|
461 \tparam Strategy underlying point-point distance strategy, defaults to haversine
|
Chris@16
|
462
|
Chris@16
|
463 \qbk{
|
Chris@16
|
464 [heading See also]
|
Chris@16
|
465 [link geometry.reference.algorithms.distance.distance_3_with_strategy distance (with strategy)]
|
Chris@16
|
466 }
|
Chris@16
|
467
|
Chris@16
|
468 */
|
Chris@16
|
469 template
|
Chris@16
|
470 <
|
Chris@16
|
471 typename CalculationType = void,
|
Chris@16
|
472 typename Strategy = haversine<double, CalculationType>
|
Chris@16
|
473 >
|
Chris@16
|
474 class cross_track
|
Chris@16
|
475 {
|
Chris@16
|
476 public :
|
Chris@16
|
477 template <typename Point, typename PointOfSegment>
|
Chris@16
|
478 struct return_type
|
Chris@16
|
479 : promote_floating_point
|
Chris@16
|
480 <
|
Chris@16
|
481 typename select_calculation_type
|
Chris@16
|
482 <
|
Chris@16
|
483 Point,
|
Chris@16
|
484 PointOfSegment,
|
Chris@16
|
485 CalculationType
|
Chris@16
|
486 >::type
|
Chris@16
|
487 >
|
Chris@16
|
488 {};
|
Chris@16
|
489
|
Chris@16
|
490 inline cross_track()
|
Chris@16
|
491 {}
|
Chris@16
|
492
|
Chris@16
|
493 explicit inline cross_track(typename Strategy::radius_type const& r)
|
Chris@16
|
494 : m_strategy(r)
|
Chris@16
|
495 {}
|
Chris@16
|
496
|
Chris@16
|
497 inline cross_track(Strategy const& s)
|
Chris@16
|
498 : m_strategy(s)
|
Chris@16
|
499 {}
|
Chris@16
|
500
|
Chris@16
|
501
|
Chris@16
|
502 // It might be useful in the future
|
Chris@16
|
503 // to overload constructor with strategy info.
|
Chris@16
|
504 // crosstrack(...) {}
|
Chris@16
|
505
|
Chris@16
|
506
|
Chris@16
|
507 template <typename Point, typename PointOfSegment>
|
Chris@16
|
508 inline typename return_type<Point, PointOfSegment>::type
|
Chris@16
|
509 apply(Point const& p, PointOfSegment const& sp1, PointOfSegment const& sp2) const
|
Chris@16
|
510 {
|
Chris@16
|
511
|
Chris@16
|
512 #if !defined(BOOST_MSVC)
|
Chris@16
|
513 BOOST_CONCEPT_ASSERT
|
Chris@16
|
514 (
|
Chris@16
|
515 (concept::PointDistanceStrategy<Strategy, Point, PointOfSegment>)
|
Chris@16
|
516 );
|
Chris@16
|
517 #endif
|
Chris@101
|
518 typedef typename return_type<Point, PointOfSegment>::type return_type;
|
Chris@101
|
519 typedef cross_track<CalculationType, Strategy> this_type;
|
Chris@16
|
520
|
Chris@101
|
521 typedef typename services::comparable_type
|
Chris@101
|
522 <
|
Chris@101
|
523 this_type
|
Chris@101
|
524 >::type comparable_type;
|
Chris@16
|
525
|
Chris@101
|
526 comparable_type cstrategy
|
Chris@101
|
527 = services::get_comparable<this_type>::apply(m_strategy);
|
Chris@16
|
528
|
Chris@101
|
529 return_type const a = cstrategy.apply(p, sp1, sp2);
|
Chris@101
|
530 return_type const c = return_type(2.0) * asin(math::sqrt(a));
|
Chris@101
|
531 return c * radius();
|
Chris@16
|
532 }
|
Chris@16
|
533
|
Chris@16
|
534 inline typename Strategy::radius_type radius() const
|
Chris@16
|
535 { return m_strategy.radius(); }
|
Chris@16
|
536
|
Chris@16
|
537 private :
|
Chris@16
|
538
|
Chris@16
|
539 Strategy m_strategy;
|
Chris@16
|
540 };
|
Chris@16
|
541
|
Chris@16
|
542
|
Chris@16
|
543
|
Chris@16
|
544 #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
|
Chris@16
|
545 namespace services
|
Chris@16
|
546 {
|
Chris@16
|
547
|
Chris@16
|
548 template <typename CalculationType, typename Strategy>
|
Chris@16
|
549 struct tag<cross_track<CalculationType, Strategy> >
|
Chris@16
|
550 {
|
Chris@16
|
551 typedef strategy_tag_distance_point_segment type;
|
Chris@16
|
552 };
|
Chris@16
|
553
|
Chris@16
|
554
|
Chris@16
|
555 template <typename CalculationType, typename Strategy, typename P, typename PS>
|
Chris@16
|
556 struct return_type<cross_track<CalculationType, Strategy>, P, PS>
|
Chris@16
|
557 : cross_track<CalculationType, Strategy>::template return_type<P, PS>
|
Chris@16
|
558 {};
|
Chris@16
|
559
|
Chris@16
|
560
|
Chris@16
|
561 template <typename CalculationType, typename Strategy>
|
Chris@16
|
562 struct comparable_type<cross_track<CalculationType, Strategy> >
|
Chris@16
|
563 {
|
Chris@101
|
564 typedef comparable::cross_track
|
Chris@101
|
565 <
|
Chris@101
|
566 CalculationType, typename comparable_type<Strategy>::type
|
Chris@101
|
567 > type;
|
Chris@16
|
568 };
|
Chris@16
|
569
|
Chris@16
|
570
|
Chris@16
|
571 template
|
Chris@16
|
572 <
|
Chris@16
|
573 typename CalculationType,
|
Chris@16
|
574 typename Strategy
|
Chris@16
|
575 >
|
Chris@16
|
576 struct get_comparable<cross_track<CalculationType, Strategy> >
|
Chris@16
|
577 {
|
Chris@16
|
578 typedef typename comparable_type
|
Chris@16
|
579 <
|
Chris@16
|
580 cross_track<CalculationType, Strategy>
|
Chris@16
|
581 >::type comparable_type;
|
Chris@16
|
582 public :
|
Chris@101
|
583 static inline comparable_type
|
Chris@101
|
584 apply(cross_track<CalculationType, Strategy> const& strategy)
|
Chris@16
|
585 {
|
Chris@16
|
586 return comparable_type(strategy.radius());
|
Chris@16
|
587 }
|
Chris@16
|
588 };
|
Chris@16
|
589
|
Chris@16
|
590
|
Chris@16
|
591 template
|
Chris@16
|
592 <
|
Chris@16
|
593 typename CalculationType,
|
Chris@16
|
594 typename Strategy,
|
Chris@101
|
595 typename P,
|
Chris@101
|
596 typename PS
|
Chris@16
|
597 >
|
Chris@16
|
598 struct result_from_distance<cross_track<CalculationType, Strategy>, P, PS>
|
Chris@16
|
599 {
|
Chris@16
|
600 private :
|
Chris@101
|
601 typedef typename cross_track
|
Chris@101
|
602 <
|
Chris@101
|
603 CalculationType, Strategy
|
Chris@101
|
604 >::template return_type<P, PS>::type return_type;
|
Chris@16
|
605 public :
|
Chris@16
|
606 template <typename T>
|
Chris@101
|
607 static inline return_type
|
Chris@101
|
608 apply(cross_track<CalculationType, Strategy> const& , T const& distance)
|
Chris@16
|
609 {
|
Chris@16
|
610 return distance;
|
Chris@16
|
611 }
|
Chris@16
|
612 };
|
Chris@16
|
613
|
Chris@16
|
614
|
Chris@101
|
615 // Specializations for comparable::cross_track
|
Chris@101
|
616 template <typename RadiusType, typename CalculationType>
|
Chris@101
|
617 struct tag<comparable::cross_track<RadiusType, CalculationType> >
|
Chris@101
|
618 {
|
Chris@101
|
619 typedef strategy_tag_distance_point_segment type;
|
Chris@101
|
620 };
|
Chris@101
|
621
|
Chris@101
|
622
|
Chris@16
|
623 template
|
Chris@16
|
624 <
|
Chris@101
|
625 typename RadiusType,
|
Chris@16
|
626 typename CalculationType,
|
Chris@101
|
627 typename P,
|
Chris@101
|
628 typename PS
|
Chris@16
|
629 >
|
Chris@101
|
630 struct return_type<comparable::cross_track<RadiusType, CalculationType>, P, PS>
|
Chris@101
|
631 : comparable::cross_track
|
Chris@101
|
632 <
|
Chris@101
|
633 RadiusType, CalculationType
|
Chris@101
|
634 >::template return_type<P, PS>
|
Chris@101
|
635 {};
|
Chris@101
|
636
|
Chris@101
|
637
|
Chris@101
|
638 template <typename RadiusType, typename CalculationType>
|
Chris@101
|
639 struct comparable_type<comparable::cross_track<RadiusType, CalculationType> >
|
Chris@16
|
640 {
|
Chris@101
|
641 typedef comparable::cross_track<RadiusType, CalculationType> type;
|
Chris@101
|
642 };
|
Chris@101
|
643
|
Chris@101
|
644
|
Chris@101
|
645 template <typename RadiusType, typename CalculationType>
|
Chris@101
|
646 struct get_comparable<comparable::cross_track<RadiusType, CalculationType> >
|
Chris@101
|
647 {
|
Chris@101
|
648 private :
|
Chris@101
|
649 typedef comparable::cross_track<RadiusType, CalculationType> this_type;
|
Chris@101
|
650 public :
|
Chris@101
|
651 static inline this_type apply(this_type const& input)
|
Chris@101
|
652 {
|
Chris@101
|
653 return input;
|
Chris@101
|
654 }
|
Chris@101
|
655 };
|
Chris@101
|
656
|
Chris@101
|
657
|
Chris@101
|
658 template
|
Chris@101
|
659 <
|
Chris@101
|
660 typename RadiusType,
|
Chris@101
|
661 typename CalculationType,
|
Chris@101
|
662 typename P,
|
Chris@101
|
663 typename PS
|
Chris@101
|
664 >
|
Chris@101
|
665 struct result_from_distance
|
Chris@101
|
666 <
|
Chris@101
|
667 comparable::cross_track<RadiusType, CalculationType>, P, PS
|
Chris@101
|
668 >
|
Chris@101
|
669 {
|
Chris@101
|
670 private :
|
Chris@101
|
671 typedef comparable::cross_track<RadiusType, CalculationType> strategy_type;
|
Chris@101
|
672 typedef typename return_type<strategy_type, P, PS>::type return_type;
|
Chris@101
|
673 public :
|
Chris@101
|
674 template <typename T>
|
Chris@101
|
675 static inline return_type apply(strategy_type const& strategy,
|
Chris@101
|
676 T const& distance)
|
Chris@101
|
677 {
|
Chris@101
|
678 return_type const s
|
Chris@101
|
679 = sin( (distance / strategy.radius()) / return_type(2.0) );
|
Chris@101
|
680 return s * s;
|
Chris@101
|
681 }
|
Chris@16
|
682 };
|
Chris@16
|
683
|
Chris@16
|
684
|
Chris@16
|
685
|
Chris@16
|
686 /*
|
Chris@16
|
687
|
Chris@16
|
688 TODO: spherical polar coordinate system requires "get_as_radian_equatorial<>"
|
Chris@16
|
689
|
Chris@16
|
690 template <typename Point, typename PointOfSegment, typename Strategy>
|
Chris@16
|
691 struct default_strategy
|
Chris@16
|
692 <
|
Chris@16
|
693 segment_tag, Point, PointOfSegment,
|
Chris@16
|
694 spherical_polar_tag, spherical_polar_tag,
|
Chris@16
|
695 Strategy
|
Chris@16
|
696 >
|
Chris@16
|
697 {
|
Chris@16
|
698 typedef cross_track
|
Chris@16
|
699 <
|
Chris@16
|
700 void,
|
Chris@16
|
701 typename boost::mpl::if_
|
Chris@16
|
702 <
|
Chris@16
|
703 boost::is_void<Strategy>,
|
Chris@16
|
704 typename default_strategy
|
Chris@16
|
705 <
|
Chris@16
|
706 point_tag, Point, PointOfSegment,
|
Chris@16
|
707 spherical_polar_tag, spherical_polar_tag
|
Chris@16
|
708 >::type,
|
Chris@16
|
709 Strategy
|
Chris@16
|
710 >::type
|
Chris@16
|
711 > type;
|
Chris@16
|
712 };
|
Chris@16
|
713 */
|
Chris@16
|
714
|
Chris@16
|
715 template <typename Point, typename PointOfSegment, typename Strategy>
|
Chris@16
|
716 struct default_strategy
|
Chris@16
|
717 <
|
Chris@101
|
718 point_tag, segment_tag, Point, PointOfSegment,
|
Chris@16
|
719 spherical_equatorial_tag, spherical_equatorial_tag,
|
Chris@16
|
720 Strategy
|
Chris@16
|
721 >
|
Chris@16
|
722 {
|
Chris@16
|
723 typedef cross_track
|
Chris@16
|
724 <
|
Chris@16
|
725 void,
|
Chris@16
|
726 typename boost::mpl::if_
|
Chris@16
|
727 <
|
Chris@16
|
728 boost::is_void<Strategy>,
|
Chris@16
|
729 typename default_strategy
|
Chris@16
|
730 <
|
Chris@101
|
731 point_tag, point_tag, Point, PointOfSegment,
|
Chris@16
|
732 spherical_equatorial_tag, spherical_equatorial_tag
|
Chris@16
|
733 >::type,
|
Chris@16
|
734 Strategy
|
Chris@16
|
735 >::type
|
Chris@16
|
736 > type;
|
Chris@16
|
737 };
|
Chris@16
|
738
|
Chris@16
|
739
|
Chris@101
|
740 template <typename PointOfSegment, typename Point, typename Strategy>
|
Chris@101
|
741 struct default_strategy
|
Chris@101
|
742 <
|
Chris@101
|
743 segment_tag, point_tag, PointOfSegment, Point,
|
Chris@101
|
744 spherical_equatorial_tag, spherical_equatorial_tag,
|
Chris@101
|
745 Strategy
|
Chris@101
|
746 >
|
Chris@101
|
747 {
|
Chris@101
|
748 typedef typename default_strategy
|
Chris@101
|
749 <
|
Chris@101
|
750 point_tag, segment_tag, Point, PointOfSegment,
|
Chris@101
|
751 spherical_equatorial_tag, spherical_equatorial_tag,
|
Chris@101
|
752 Strategy
|
Chris@101
|
753 >::type type;
|
Chris@101
|
754 };
|
Chris@101
|
755
|
Chris@16
|
756
|
Chris@16
|
757 } // namespace services
|
Chris@16
|
758 #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
|
Chris@16
|
759
|
Chris@16
|
760 }} // namespace strategy::distance
|
Chris@16
|
761
|
Chris@16
|
762 }} // namespace boost::geometry
|
Chris@16
|
763
|
Chris@16
|
764 #endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_HPP
|