]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // Boost.Geometry (aka GGL, Generic Geometry Library) |
2 | ||
3 | // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. | |
4 | // Copyright (c) 2008-2015 Barend Gehrels, Amsterdam, the Netherlands. | |
5 | // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. | |
6 | ||
7 | // This file was modified by Oracle on 2014, 2015. | |
8 | // Modifications copyright (c) 2014-2015, Oracle and/or its affiliates. | |
9 | ||
10 | // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle | |
11 | // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle | |
12 | ||
13 | // Use, modification and distribution is subject to the Boost Software License, | |
14 | // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | |
15 | // http://www.boost.org/LICENSE_1_0.txt) | |
16 | ||
17 | #ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_POINT_BOX_HPP | |
18 | #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_POINT_BOX_HPP | |
19 | ||
20 | #include <boost/config.hpp> | |
21 | #include <boost/concept_check.hpp> | |
22 | #include <boost/mpl/if.hpp> | |
23 | #include <boost/type_traits/is_void.hpp> | |
24 | ||
25 | #include <boost/geometry/core/access.hpp> | |
26 | #include <boost/geometry/core/assert.hpp> | |
27 | #include <boost/geometry/core/point_type.hpp> | |
28 | #include <boost/geometry/core/radian_access.hpp> | |
29 | #include <boost/geometry/core/tags.hpp> | |
30 | ||
31 | #include <boost/geometry/strategies/distance.hpp> | |
32 | #include <boost/geometry/strategies/concepts/distance_concept.hpp> | |
33 | #include <boost/geometry/strategies/spherical/distance_cross_track.hpp> | |
34 | ||
35 | #include <boost/geometry/util/math.hpp> | |
36 | #include <boost/geometry/algorithms/detail/assign_box_corners.hpp> | |
37 | ||
38 | ||
39 | namespace boost { namespace geometry | |
40 | { | |
41 | ||
42 | namespace strategy { namespace distance | |
43 | { | |
44 | ||
45 | ||
46 | /*! | |
47 | \brief Strategy functor for distance point to box calculation | |
48 | \ingroup strategies | |
49 | \details Class which calculates the distance of a point to a box, for | |
50 | points and boxes on a sphere or globe | |
51 | \tparam CalculationType \tparam_calculation | |
52 | \tparam Strategy underlying point-segment distance strategy, defaults | |
53 | to cross track | |
54 | ||
55 | \qbk{ | |
56 | [heading See also] | |
57 | [link geometry.reference.algorithms.distance.distance_3_with_strategy distance (with strategy)] | |
58 | } | |
59 | ||
60 | */ | |
61 | template | |
62 | < | |
63 | typename CalculationType = void, | |
64 | typename Strategy = cross_track<CalculationType> | |
65 | > | |
66 | class cross_track_point_box | |
67 | { | |
68 | public: | |
69 | template <typename Point, typename Box> | |
70 | struct return_type | |
71 | : services::return_type<Strategy, Point, typename point_type<Box>::type> | |
72 | {}; | |
73 | ||
74 | typedef typename Strategy::radius_type radius_type; | |
75 | ||
76 | inline cross_track_point_box() | |
77 | {} | |
78 | ||
79 | explicit inline cross_track_point_box(typename Strategy::radius_type const& r) | |
80 | : m_ps_strategy(r) | |
81 | {} | |
82 | ||
83 | inline cross_track_point_box(Strategy const& s) | |
84 | : m_ps_strategy(s) | |
85 | {} | |
86 | ||
87 | ||
88 | // It might be useful in the future | |
89 | // to overload constructor with strategy info. | |
90 | // crosstrack(...) {} | |
91 | ||
92 | template <typename Point, typename Box> | |
93 | inline typename return_type<Point, Box>::type | |
94 | apply(Point const& point, Box const& box) const | |
95 | { | |
96 | #if !defined(BOOST_MSVC) | |
97 | BOOST_CONCEPT_ASSERT | |
98 | ( | |
99 | (concepts::PointSegmentDistanceStrategy | |
100 | < | |
101 | Strategy, Point, typename point_type<Box>::type | |
102 | >) | |
103 | ); | |
104 | #endif | |
105 | ||
106 | // this method assumes that the coordinates of the point and | |
107 | // the box are normalized | |
108 | ||
109 | typedef typename return_type<Point, Box>::type return_type; | |
110 | typedef typename point_type<Box>::type box_point_type; | |
111 | ||
112 | // TODO: This strategy as well as other cross-track strategies | |
113 | // and therefore e.g. spherical within(Point, Box) may not work | |
114 | // properly for a Box degenerated to a Segment or Point | |
115 | ||
116 | box_point_type bottom_left, bottom_right, top_left, top_right; | |
117 | geometry::detail::assign_box_corners(box, | |
118 | bottom_left, bottom_right, | |
119 | top_left, top_right); | |
120 | ||
121 | return_type const plon = geometry::get_as_radian<0>(point); | |
122 | return_type const plat = geometry::get_as_radian<1>(point); | |
123 | ||
124 | return_type const lon_min = geometry::get_as_radian<0>(bottom_left); | |
125 | return_type const lat_min = geometry::get_as_radian<1>(bottom_left); | |
126 | return_type const lon_max = geometry::get_as_radian<0>(top_right); | |
127 | return_type const lat_max = geometry::get_as_radian<1>(top_right); | |
128 | ||
129 | return_type const pi = math::pi<return_type>(); | |
130 | return_type const two_pi = math::two_pi<return_type>(); | |
131 | ||
132 | // First check if the point is within the band defined by the | |
133 | // minimum and maximum longitude of the box; if yes, determine | |
134 | // if the point is above, below or inside the box and compute | |
135 | // the distance (easy in this case) | |
136 | // | |
137 | // Notice that the point may not be inside the longitude range | |
138 | // of the box, but the shifted point may be inside the | |
139 | // longitude range of the box; in this case the point is still | |
140 | // considered as inside the longitude range band of the box | |
141 | if ((plon >= lon_min && plon <= lon_max) || plon + two_pi <= lon_max) | |
142 | { | |
143 | if (plat > lat_max) | |
144 | { | |
145 | return services::result_from_distance | |
146 | < | |
147 | Strategy, Point, box_point_type | |
148 | >::apply(m_ps_strategy, radius() * (plat - lat_max)); | |
149 | } | |
150 | else if (plat < lat_min) | |
151 | { | |
152 | return services::result_from_distance | |
153 | < | |
154 | Strategy, Point, box_point_type | |
155 | >::apply(m_ps_strategy, radius() * (lat_min - plat)); | |
156 | } | |
157 | else | |
158 | { | |
159 | BOOST_GEOMETRY_ASSERT(plat >= lat_min && plat <= lat_max); | |
160 | return return_type(0); | |
161 | } | |
162 | } | |
163 | ||
164 | // Otherwise determine which among the two medirian segments of the | |
165 | // box the point is closest to, and compute the distance of | |
166 | // the point to this closest segment | |
167 | ||
168 | // Below lon_midway is the longitude of the meridian that: | |
169 | // (1) is midway between the meridians of the left and right | |
170 | // meridians of the box, and | |
171 | // (2) does not intersect the box | |
172 | return_type const two = 2.0; | |
173 | bool use_left_segment; | |
174 | if (lon_max > pi) | |
175 | { | |
176 | // the box crosses the antimeridian | |
177 | ||
178 | // midway longitude = lon_min - (lon_min + (lon_max - 2 * pi)) / 2; | |
179 | return_type const lon_midway = (lon_min - lon_max) / two + pi; | |
180 | BOOST_GEOMETRY_ASSERT(lon_midway >= -pi && lon_midway <= pi); | |
181 | ||
182 | use_left_segment = plon > lon_midway; | |
183 | } | |
184 | else | |
185 | { | |
186 | // the box does not cross the antimeridian | |
187 | ||
188 | return_type const lon_sum = lon_min + lon_max; | |
189 | if (math::equals(lon_sum, return_type(0))) | |
190 | { | |
191 | // special case: the box is symmetric with respect to | |
192 | // the prime meridian; the midway meridian is the antimeridian | |
193 | ||
194 | use_left_segment = plon < lon_min; | |
195 | } | |
196 | else | |
197 | { | |
198 | // midway long. = lon_min - (2 * pi - (lon_max - lon_min)) / 2; | |
199 | return_type lon_midway = (lon_min + lon_max) / two - pi; | |
200 | ||
201 | // normalize the midway longitude | |
202 | if (lon_midway > pi) | |
203 | { | |
204 | lon_midway -= two_pi; | |
205 | } | |
206 | else if (lon_midway < -pi) | |
207 | { | |
208 | lon_midway += two_pi; | |
209 | } | |
210 | BOOST_GEOMETRY_ASSERT(lon_midway >= -pi && lon_midway <= pi); | |
211 | ||
212 | // if lon_sum is positive the midway meridian is left | |
213 | // of the box, or right of the box otherwise | |
214 | use_left_segment = lon_sum > 0 | |
215 | ? (plon < lon_min && plon >= lon_midway) | |
216 | : (plon <= lon_max || plon > lon_midway); | |
217 | } | |
218 | } | |
219 | ||
220 | return use_left_segment | |
221 | ? m_ps_strategy.apply(point, bottom_left, top_left) | |
222 | : m_ps_strategy.apply(point, bottom_right, top_right); | |
223 | } | |
224 | ||
225 | inline typename Strategy::radius_type radius() const | |
226 | { | |
227 | return m_ps_strategy.radius(); | |
228 | } | |
229 | ||
230 | private: | |
231 | Strategy m_ps_strategy; | |
232 | }; | |
233 | ||
234 | ||
235 | ||
236 | #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS | |
237 | namespace services | |
238 | { | |
239 | ||
240 | template <typename CalculationType, typename Strategy> | |
241 | struct tag<cross_track_point_box<CalculationType, Strategy> > | |
242 | { | |
243 | typedef strategy_tag_distance_point_box type; | |
244 | }; | |
245 | ||
246 | ||
247 | template <typename CalculationType, typename Strategy, typename P, typename Box> | |
248 | struct return_type<cross_track_point_box<CalculationType, Strategy>, P, Box> | |
249 | : cross_track_point_box | |
250 | < | |
251 | CalculationType, Strategy | |
252 | >::template return_type<P, Box> | |
253 | {}; | |
254 | ||
255 | ||
256 | template <typename CalculationType, typename Strategy> | |
257 | struct comparable_type<cross_track_point_box<CalculationType, Strategy> > | |
258 | { | |
259 | typedef cross_track_point_box | |
260 | < | |
261 | CalculationType, typename comparable_type<Strategy>::type | |
262 | > type; | |
263 | }; | |
264 | ||
265 | ||
266 | template <typename CalculationType, typename Strategy> | |
267 | struct get_comparable<cross_track_point_box<CalculationType, Strategy> > | |
268 | { | |
269 | typedef cross_track_point_box<CalculationType, Strategy> this_strategy; | |
270 | typedef typename comparable_type<this_strategy>::type comparable_type; | |
271 | ||
272 | public: | |
273 | static inline comparable_type apply(this_strategy const& strategy) | |
274 | { | |
275 | return comparable_type(strategy.radius()); | |
276 | } | |
277 | }; | |
278 | ||
279 | ||
280 | template <typename CalculationType, typename Strategy, typename P, typename Box> | |
281 | struct result_from_distance | |
282 | < | |
283 | cross_track_point_box<CalculationType, Strategy>, P, Box | |
284 | > | |
285 | { | |
286 | private: | |
287 | typedef cross_track_point_box<CalculationType, Strategy> this_strategy; | |
288 | ||
289 | typedef typename this_strategy::template return_type | |
290 | < | |
291 | P, Box | |
292 | >::type return_type; | |
293 | ||
294 | public: | |
295 | template <typename T> | |
296 | static inline return_type apply(this_strategy const& strategy, | |
297 | T const& distance) | |
298 | { | |
299 | Strategy s(strategy.radius()); | |
300 | ||
301 | return result_from_distance | |
302 | < | |
303 | Strategy, P, typename point_type<Box>::type | |
304 | >::apply(s, distance); | |
305 | } | |
306 | }; | |
307 | ||
308 | ||
309 | // define cross_track_point_box<default_point_segment_strategy> as | |
310 | // default point-box strategy for the spherical equatorial coordinate system | |
311 | template <typename Point, typename Box, typename Strategy> | |
312 | struct default_strategy | |
313 | < | |
314 | point_tag, box_tag, Point, Box, | |
315 | spherical_equatorial_tag, spherical_equatorial_tag, | |
316 | Strategy | |
317 | > | |
318 | { | |
319 | typedef cross_track_point_box | |
320 | < | |
321 | void, | |
322 | typename boost::mpl::if_ | |
323 | < | |
324 | boost::is_void<Strategy>, | |
325 | typename default_strategy | |
326 | < | |
327 | point_tag, segment_tag, | |
328 | Point, typename point_type<Box>::type, | |
329 | spherical_equatorial_tag, spherical_equatorial_tag | |
330 | >::type, | |
331 | Strategy | |
332 | >::type | |
333 | > type; | |
334 | }; | |
335 | ||
336 | ||
337 | template <typename Box, typename Point, typename Strategy> | |
338 | struct default_strategy | |
339 | < | |
340 | box_tag, point_tag, Box, Point, | |
341 | spherical_equatorial_tag, spherical_equatorial_tag, | |
342 | Strategy | |
343 | > | |
344 | { | |
345 | typedef typename default_strategy | |
346 | < | |
347 | point_tag, box_tag, Point, Box, | |
348 | spherical_equatorial_tag, spherical_equatorial_tag, | |
349 | Strategy | |
350 | >::type type; | |
351 | }; | |
352 | ||
353 | ||
354 | } // namespace services | |
355 | #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS | |
356 | ||
357 | ||
358 | }} // namespace strategy::distance | |
359 | ||
360 | ||
361 | }} // namespace boost::geometry | |
362 | ||
363 | ||
364 | #endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_DISTANCE_CROSS_TRACK_POINT_BOX_HPP |