]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // Boost.Geometry (aka GGL, Generic Geometry Library) |
2 | ||
20effc67 | 3 | // Copyright (c) 2016-2020 Oracle and/or its affiliates. |
11fdf7f2 TL |
4 | // Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle |
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_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_BOX_BOX_HPP | |
12 | #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_BOX_BOX_HPP | |
13 | ||
20effc67 TL |
14 | |
15 | #include <type_traits> | |
16 | ||
11fdf7f2 TL |
17 | #include <boost/config.hpp> |
18 | #include <boost/concept_check.hpp> | |
11fdf7f2 TL |
19 | |
20 | #include <boost/geometry/core/access.hpp> | |
21 | #include <boost/geometry/core/assert.hpp> | |
22 | #include <boost/geometry/core/point_type.hpp> | |
23 | #include <boost/geometry/core/radian_access.hpp> | |
24 | #include <boost/geometry/core/tags.hpp> | |
25 | ||
26 | #include <boost/geometry/strategies/distance.hpp> | |
27 | #include <boost/geometry/strategies/concepts/distance_concept.hpp> | |
28 | #include <boost/geometry/strategies/spherical/distance_cross_track.hpp> | |
29 | ||
30 | #include <boost/geometry/util/math.hpp> | |
31 | #include <boost/geometry/algorithms/detail/assign_box_corners.hpp> | |
32 | ||
33 | ||
34 | namespace boost { namespace geometry | |
35 | { | |
36 | ||
37 | namespace strategy { namespace distance | |
38 | { | |
39 | ||
40 | namespace details | |
41 | { | |
42 | ||
43 | template <typename ReturnType> | |
44 | class cross_track_box_box_generic | |
45 | { | |
46 | public : | |
47 | ||
92f5a8d4 | 48 | template <typename Point, typename PPStrategy, typename PSStrategy> |
11fdf7f2 TL |
49 | ReturnType static inline diagonal_case(Point topA, |
50 | Point topB, | |
51 | Point bottomA, | |
52 | Point bottomB, | |
53 | bool north_shortest, | |
54 | bool non_overlap, | |
92f5a8d4 TL |
55 | PPStrategy pp_strategy, |
56 | PSStrategy ps_strategy) | |
11fdf7f2 TL |
57 | { |
58 | if (north_shortest && non_overlap) | |
59 | { | |
92f5a8d4 | 60 | return pp_strategy.apply(topA, bottomB); |
11fdf7f2 TL |
61 | } |
62 | if (north_shortest && !non_overlap) | |
63 | { | |
64 | return ps_strategy.apply(topA, topB, bottomB); | |
65 | } | |
66 | if (!north_shortest && non_overlap) | |
67 | { | |
92f5a8d4 | 68 | return pp_strategy.apply(bottomA, topB); |
11fdf7f2 TL |
69 | } |
70 | return ps_strategy.apply(bottomA, topB, bottomB); | |
71 | } | |
72 | ||
73 | ||
74 | template | |
75 | < | |
76 | typename Box1, | |
77 | typename Box2, | |
92f5a8d4 TL |
78 | typename PPStrategy, |
79 | typename PSStrategy | |
11fdf7f2 TL |
80 | > |
81 | ReturnType static inline apply (Box1 const& box1, | |
82 | Box2 const& box2, | |
92f5a8d4 TL |
83 | PPStrategy pp_strategy, |
84 | PSStrategy ps_strategy) | |
11fdf7f2 TL |
85 | { |
86 | ||
87 | // this method assumes that the coordinates of the point and | |
88 | // the box are normalized | |
89 | ||
90 | typedef typename point_type<Box1>::type box_point_type1; | |
91 | typedef typename point_type<Box2>::type box_point_type2; | |
92 | ||
93 | box_point_type1 bottom_left1, bottom_right1, top_left1, top_right1; | |
94 | geometry::detail::assign_box_corners(box1, | |
95 | bottom_left1, bottom_right1, | |
96 | top_left1, top_right1); | |
97 | ||
98 | box_point_type2 bottom_left2, bottom_right2, top_left2, top_right2; | |
99 | geometry::detail::assign_box_corners(box2, | |
100 | bottom_left2, bottom_right2, | |
101 | top_left2, top_right2); | |
102 | ||
103 | ReturnType lon_min1 = geometry::get_as_radian<0>(bottom_left1); | |
104 | ReturnType const lat_min1 = geometry::get_as_radian<1>(bottom_left1); | |
105 | ReturnType lon_max1 = geometry::get_as_radian<0>(top_right1); | |
106 | ReturnType const lat_max1 = geometry::get_as_radian<1>(top_right1); | |
107 | ||
108 | ReturnType lon_min2 = geometry::get_as_radian<0>(bottom_left2); | |
109 | ReturnType const lat_min2 = geometry::get_as_radian<1>(bottom_left2); | |
110 | ReturnType lon_max2 = geometry::get_as_radian<0>(top_right2); | |
111 | ReturnType const lat_max2 = geometry::get_as_radian<1>(top_right2); | |
112 | ||
113 | ReturnType const two_pi = math::two_pi<ReturnType>(); | |
114 | ||
115 | // Test which sides of the boxes are closer and if boxes cross | |
116 | // antimeridian | |
117 | bool right_wrap; | |
118 | ||
119 | if (lon_min2 > 0 && lon_max2 < 0) // box2 crosses antimeridian | |
120 | { | |
121 | #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX | |
122 | std::cout << "(box2 crosses antimeridian)"; | |
123 | #endif | |
124 | right_wrap = lon_min2 - lon_max1 < lon_min1 - lon_max2; | |
125 | lon_max2 += two_pi; | |
126 | if (lon_min1 > 0 && lon_max1 < 0) // both boxes crosses antimeridian | |
127 | { | |
128 | lon_max1 += two_pi; | |
129 | } | |
130 | } | |
131 | else if (lon_min1 > 0 && lon_max1 < 0) // only box1 crosses antimeridian | |
132 | { | |
133 | #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX | |
134 | std::cout << "(box1 crosses antimeridian)"; | |
135 | #endif | |
92f5a8d4 | 136 | return apply(box2, box1, pp_strategy, ps_strategy); |
11fdf7f2 TL |
137 | } |
138 | else | |
139 | { | |
140 | right_wrap = lon_max1 <= lon_min2 | |
141 | ? lon_min2 - lon_max1 < two_pi - (lon_max2 - lon_min1) | |
142 | : lon_min1 - lon_max2 > two_pi - (lon_max1 - lon_min2); | |
143 | ||
144 | } | |
145 | ||
146 | // Check1: if box2 crosses the band defined by the | |
147 | // minimum and maximum longitude of box1; if yes, determine | |
148 | // if the box2 is above, below or intersects/is inside box1 and compute | |
149 | // the distance (easy in this case) | |
150 | ||
151 | bool lon_min12 = lon_min1 <= lon_min2; | |
152 | bool right = lon_max1 <= lon_min2; | |
153 | bool left = lon_min1 >= lon_max2; | |
154 | bool lon_max12 = lon_max1 <= lon_max2; | |
155 | ||
156 | if ((lon_min12 && !right) | |
157 | || (!left && !lon_max12) | |
158 | || (!lon_min12 && lon_max12)) | |
159 | { | |
160 | #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX | |
161 | std::cout << "(up-down)\n"; | |
162 | #endif | |
163 | if (lat_min1 > lat_max2) | |
164 | { | |
165 | return geometry::strategy::distance::services::result_from_distance | |
166 | < | |
92f5a8d4 TL |
167 | PSStrategy, box_point_type1, box_point_type2 |
168 | >::apply(ps_strategy, ps_strategy | |
169 | .vertical_or_meridian(lat_min1, lat_max2)); | |
11fdf7f2 TL |
170 | } |
171 | else if (lat_max1 < lat_min2) | |
172 | { | |
173 | return geometry::strategy::distance::services::result_from_distance | |
174 | < | |
92f5a8d4 TL |
175 | PSStrategy, box_point_type1, box_point_type2 |
176 | >::apply(ps_strategy, ps_strategy | |
177 | .vertical_or_meridian(lat_min2, lat_max1)); | |
11fdf7f2 TL |
178 | } |
179 | else | |
180 | { | |
181 | //BOOST_GEOMETRY_ASSERT(plat >= lat_min && plat <= lat_max); | |
182 | return ReturnType(0); | |
183 | } | |
184 | } | |
185 | ||
186 | // Check2: if box2 is right/left of box1 | |
187 | // the max lat of box2 should be less than the max lat of box1 | |
188 | bool bottom_max; | |
189 | ||
190 | ReturnType top_common = (std::min)(lat_max1, lat_max2); | |
191 | ReturnType bottom_common = (std::max)(lat_min1, lat_min2); | |
192 | ||
193 | // true if the closest points are on northern hemisphere | |
194 | bool north_shortest = top_common + bottom_common > 0; | |
195 | // true if box bands do not overlap | |
196 | bool non_overlap = top_common < bottom_common; | |
197 | ||
198 | if (north_shortest) | |
199 | { | |
200 | bottom_max = lat_max1 >= lat_max2; | |
201 | } | |
202 | else | |
203 | { | |
204 | bottom_max = lat_min1 <= lat_min2; | |
205 | } | |
206 | ||
207 | #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX | |
208 | std::cout << "(diagonal)"; | |
209 | #endif | |
210 | if (bottom_max && !right_wrap) | |
211 | { | |
212 | #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX | |
213 | std::cout << "(bottom left)"; | |
214 | #endif | |
215 | return diagonal_case(top_right2, top_left1, | |
216 | bottom_right2, bottom_left1, | |
217 | north_shortest, non_overlap, | |
92f5a8d4 | 218 | pp_strategy, ps_strategy); |
11fdf7f2 TL |
219 | } |
220 | if (bottom_max && right_wrap) | |
221 | { | |
222 | #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX | |
223 | std::cout << "(bottom right)"; | |
224 | #endif | |
225 | return diagonal_case(top_left2, top_right1, | |
226 | bottom_left2, bottom_right1, | |
227 | north_shortest, non_overlap, | |
92f5a8d4 | 228 | pp_strategy, ps_strategy); |
11fdf7f2 TL |
229 | } |
230 | if (!bottom_max && !right_wrap) | |
231 | { | |
232 | #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX | |
233 | std::cout << "(top left)"; | |
234 | #endif | |
235 | return diagonal_case(top_left1, top_right2, | |
236 | bottom_left1, bottom_right2, | |
237 | north_shortest, non_overlap, | |
92f5a8d4 | 238 | pp_strategy, ps_strategy); |
11fdf7f2 TL |
239 | } |
240 | if (!bottom_max && right_wrap) | |
241 | { | |
242 | #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK_BOX_BOX | |
243 | std::cout << "(top right)"; | |
244 | #endif | |
245 | return diagonal_case(top_right1, top_left2, | |
246 | bottom_right1, bottom_left2, | |
247 | north_shortest, non_overlap, | |
92f5a8d4 | 248 | pp_strategy, ps_strategy); |
11fdf7f2 TL |
249 | } |
250 | return ReturnType(0); | |
251 | } | |
252 | }; | |
253 | ||
254 | } //namespace details | |
255 | ||
256 | /*! | |
257 | \brief Strategy functor for distance box to box calculation | |
258 | \ingroup strategies | |
259 | \details Class which calculates the distance of a box to a box, for | |
260 | boxes on a sphere or globe | |
261 | \tparam CalculationType \tparam_calculation | |
262 | \tparam Strategy underlying point-segment distance strategy, defaults | |
263 | to cross track | |
264 | \qbk{ | |
265 | [heading See also] | |
266 | [link geometry.reference.algorithms.distance.distance_3_with_strategy distance (with strategy)] | |
267 | } | |
268 | */ | |
269 | template | |
270 | < | |
271 | typename CalculationType = void, | |
92f5a8d4 | 272 | typename Strategy = haversine<double, CalculationType> |
11fdf7f2 TL |
273 | > |
274 | class cross_track_box_box | |
275 | { | |
276 | public: | |
277 | template <typename Box1, typename Box2> | |
278 | struct return_type | |
279 | : services::return_type<Strategy, | |
280 | typename point_type<Box1>::type, | |
281 | typename point_type<Box2>::type> | |
282 | {}; | |
283 | ||
284 | typedef typename Strategy::radius_type radius_type; | |
285 | ||
92f5a8d4 TL |
286 | // strategy getters |
287 | ||
288 | // point-segment strategy getters | |
289 | struct distance_ps_strategy | |
290 | { | |
291 | typedef cross_track<CalculationType, Strategy> type; | |
292 | }; | |
293 | ||
294 | typedef typename strategy::distance::services::comparable_type | |
295 | < | |
296 | Strategy | |
297 | >::type pp_comparable_strategy; | |
298 | ||
20effc67 | 299 | typedef std::conditional_t |
92f5a8d4 | 300 | < |
20effc67 | 301 | std::is_same |
92f5a8d4 TL |
302 | < |
303 | pp_comparable_strategy, | |
304 | Strategy | |
20effc67 | 305 | >::value, |
92f5a8d4 TL |
306 | typename strategy::distance::services::comparable_type |
307 | < | |
308 | typename distance_ps_strategy::type | |
309 | >::type, | |
310 | typename distance_ps_strategy::type | |
20effc67 | 311 | > ps_strategy_type; |
92f5a8d4 TL |
312 | |
313 | // constructors | |
314 | ||
11fdf7f2 TL |
315 | inline cross_track_box_box() |
316 | {} | |
317 | ||
318 | explicit inline cross_track_box_box(typename Strategy::radius_type const& r) | |
92f5a8d4 | 319 | : m_strategy(r) |
11fdf7f2 TL |
320 | {} |
321 | ||
322 | inline cross_track_box_box(Strategy const& s) | |
92f5a8d4 | 323 | : m_strategy(s) |
11fdf7f2 TL |
324 | {} |
325 | ||
326 | ||
327 | // It might be useful in the future | |
328 | // to overload constructor with strategy info. | |
329 | // crosstrack(...) {} | |
330 | ||
331 | template <typename Box1, typename Box2> | |
332 | inline typename return_type<Box1, Box2>::type | |
333 | apply(Box1 const& box1, Box2 const& box2) const | |
334 | { | |
335 | #if !defined(BOOST_MSVC) | |
336 | BOOST_CONCEPT_ASSERT | |
337 | ( | |
92f5a8d4 | 338 | (concepts::PointDistanceStrategy |
11fdf7f2 TL |
339 | < |
340 | Strategy, | |
341 | typename point_type<Box1>::type, | |
342 | typename point_type<Box2>::type | |
343 | >) | |
344 | ); | |
345 | #endif | |
346 | typedef typename return_type<Box1, Box2>::type return_type; | |
347 | return details::cross_track_box_box_generic | |
92f5a8d4 TL |
348 | <return_type>::apply(box1, box2, |
349 | m_strategy, | |
350 | ps_strategy_type(m_strategy)); | |
11fdf7f2 TL |
351 | } |
352 | ||
353 | inline typename Strategy::radius_type radius() const | |
354 | { | |
92f5a8d4 | 355 | return m_strategy.radius(); |
11fdf7f2 TL |
356 | } |
357 | ||
358 | private: | |
92f5a8d4 | 359 | Strategy m_strategy; |
11fdf7f2 TL |
360 | }; |
361 | ||
362 | ||
363 | ||
364 | #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS | |
365 | namespace services | |
366 | { | |
367 | ||
368 | template <typename CalculationType, typename Strategy> | |
369 | struct tag<cross_track_box_box<CalculationType, Strategy> > | |
370 | { | |
371 | typedef strategy_tag_distance_box_box type; | |
372 | }; | |
373 | ||
374 | ||
375 | template <typename CalculationType, typename Strategy, typename Box1, typename Box2> | |
376 | struct return_type<cross_track_box_box<CalculationType, Strategy>, Box1, Box2> | |
377 | : cross_track_box_box | |
378 | < | |
379 | CalculationType, Strategy | |
380 | >::template return_type<Box1, Box2> | |
381 | {}; | |
382 | ||
383 | ||
384 | template <typename CalculationType, typename Strategy> | |
385 | struct comparable_type<cross_track_box_box<CalculationType, Strategy> > | |
386 | { | |
387 | typedef cross_track_box_box | |
388 | < | |
389 | CalculationType, typename comparable_type<Strategy>::type | |
390 | > type; | |
391 | }; | |
392 | ||
393 | ||
394 | template <typename CalculationType, typename Strategy> | |
395 | struct get_comparable<cross_track_box_box<CalculationType, Strategy> > | |
396 | { | |
397 | typedef cross_track_box_box<CalculationType, Strategy> this_strategy; | |
398 | typedef typename comparable_type<this_strategy>::type comparable_type; | |
399 | ||
400 | public: | |
401 | static inline comparable_type apply(this_strategy const& strategy) | |
402 | { | |
403 | return comparable_type(strategy.radius()); | |
404 | } | |
405 | }; | |
406 | ||
407 | ||
408 | template <typename CalculationType, typename Strategy, typename Box1, typename Box2> | |
409 | struct result_from_distance | |
410 | < | |
411 | cross_track_box_box<CalculationType, Strategy>, Box1, Box2 | |
412 | > | |
413 | { | |
414 | private: | |
415 | typedef cross_track_box_box<CalculationType, Strategy> this_strategy; | |
416 | ||
417 | typedef typename this_strategy::template return_type | |
418 | < | |
419 | Box1, Box2 | |
420 | >::type return_type; | |
421 | ||
422 | public: | |
423 | template <typename T> | |
424 | static inline return_type apply(this_strategy const& strategy, | |
425 | T const& distance) | |
426 | { | |
427 | Strategy s(strategy.radius()); | |
428 | ||
429 | return result_from_distance | |
430 | < | |
431 | Strategy, | |
432 | typename point_type<Box1>::type, | |
433 | typename point_type<Box2>::type | |
434 | >::apply(s, distance); | |
435 | } | |
436 | }; | |
437 | ||
438 | ||
439 | // define cross_track_box_box<default_point_segment_strategy> as | |
440 | // default box-box strategy for the spherical equatorial coordinate system | |
441 | template <typename Box1, typename Box2, typename Strategy> | |
442 | struct default_strategy | |
443 | < | |
444 | box_tag, box_tag, Box1, Box2, | |
445 | spherical_equatorial_tag, spherical_equatorial_tag, | |
446 | Strategy | |
447 | > | |
448 | { | |
449 | typedef cross_track_box_box | |
450 | < | |
451 | void, | |
20effc67 | 452 | std::conditional_t |
11fdf7f2 | 453 | < |
20effc67 | 454 | std::is_void<Strategy>::value, |
11fdf7f2 TL |
455 | typename default_strategy |
456 | < | |
92f5a8d4 | 457 | point_tag, point_tag, |
11fdf7f2 TL |
458 | typename point_type<Box1>::type, typename point_type<Box2>::type, |
459 | spherical_equatorial_tag, spherical_equatorial_tag | |
460 | >::type, | |
461 | Strategy | |
20effc67 | 462 | > |
11fdf7f2 TL |
463 | > type; |
464 | }; | |
465 | ||
466 | } // namespace services | |
467 | #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS | |
468 | ||
469 | ||
470 | }} // namespace strategy::distance | |
471 | ||
472 | ||
473 | }} // namespace boost::geometry | |
474 | ||
475 | #endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_BOX_BOX_HPP |