]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // Boost.Geometry (aka GGL, Generic Geometry Library) |
2 | ||
3 | // Copyright (c) 2017-2020 Oracle and/or its affiliates. | |
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_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP | |
12 | #define BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP | |
13 | ||
14 | ||
15 | #include <cstddef> | |
16 | #include <utility> | |
17 | ||
18 | #include <boost/core/ignore_unused.hpp> | |
19 | #include <boost/numeric/conversion/cast.hpp> | |
20 | ||
21 | #include <boost/geometry/algorithms/detail/envelope/transform_units.hpp> | |
22 | ||
23 | #include <boost/geometry/core/assert.hpp> | |
24 | #include <boost/geometry/core/coordinate_system.hpp> | |
25 | #include <boost/geometry/core/coordinate_type.hpp> | |
26 | #include <boost/geometry/core/cs.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/formulas/meridian_segment.hpp> | |
32 | #include <boost/geometry/formulas/vertex_latitude.hpp> | |
33 | ||
34 | #include <boost/geometry/geometries/helper_geometry.hpp> | |
35 | ||
36 | #include <boost/geometry/strategy/cartesian/envelope_segment.hpp> | |
37 | #include <boost/geometry/strategy/envelope.hpp> | |
38 | #include <boost/geometry/strategies/normalize.hpp> | |
39 | #include <boost/geometry/strategies/spherical/azimuth.hpp> | |
40 | #include <boost/geometry/strategy/spherical/expand_box.hpp> | |
41 | ||
42 | #include <boost/geometry/util/math.hpp> | |
43 | ||
44 | namespace boost { namespace geometry { namespace strategy { namespace envelope | |
45 | { | |
46 | ||
47 | #ifndef DOXYGEN_NO_DETAIL | |
48 | namespace detail | |
49 | { | |
50 | ||
51 | template <typename CalculationType, typename CS_Tag> | |
52 | struct envelope_segment_call_vertex_latitude | |
53 | { | |
54 | template <typename T1, typename T2, typename Strategy> | |
55 | static inline CalculationType apply(T1 const& lat1, | |
56 | T2 const& alp1, | |
57 | Strategy const& ) | |
58 | { | |
59 | return geometry::formula::vertex_latitude<CalculationType, CS_Tag> | |
60 | ::apply(lat1, alp1); | |
61 | } | |
62 | }; | |
63 | ||
64 | template <typename CalculationType> | |
65 | struct envelope_segment_call_vertex_latitude<CalculationType, geographic_tag> | |
66 | { | |
67 | template <typename T1, typename T2, typename Strategy> | |
68 | static inline CalculationType apply(T1 const& lat1, | |
69 | T2 const& alp1, | |
70 | Strategy const& strategy) | |
71 | { | |
72 | return geometry::formula::vertex_latitude<CalculationType, geographic_tag> | |
73 | ::apply(lat1, alp1, strategy.model()); | |
74 | } | |
75 | }; | |
76 | ||
77 | template <typename Units, typename CS_Tag> | |
78 | struct envelope_segment_convert_polar | |
79 | { | |
80 | template <typename T> | |
81 | static inline void pre(T & , T & ) {} | |
82 | ||
83 | template <typename T> | |
84 | static inline void post(T & , T & ) {} | |
85 | }; | |
86 | ||
87 | template <typename Units> | |
88 | struct envelope_segment_convert_polar<Units, spherical_polar_tag> | |
89 | { | |
90 | template <typename T> | |
91 | static inline void pre(T & lat1, T & lat2) | |
92 | { | |
93 | lat1 = math::latitude_convert_ep<Units>(lat1); | |
94 | lat2 = math::latitude_convert_ep<Units>(lat2); | |
95 | } | |
96 | ||
97 | template <typename T> | |
98 | static inline void post(T & lat1, T & lat2) | |
99 | { | |
100 | lat1 = math::latitude_convert_ep<Units>(lat1); | |
101 | lat2 = math::latitude_convert_ep<Units>(lat2); | |
102 | std::swap(lat1, lat2); | |
103 | } | |
104 | }; | |
105 | ||
106 | template <typename CS_Tag> | |
107 | class envelope_segment_impl | |
108 | { | |
109 | private: | |
110 | ||
111 | // degrees or radians | |
112 | template <typename CalculationType> | |
113 | static inline void swap(CalculationType& lon1, | |
114 | CalculationType& lat1, | |
115 | CalculationType& lon2, | |
116 | CalculationType& lat2) | |
117 | { | |
118 | std::swap(lon1, lon2); | |
119 | std::swap(lat1, lat2); | |
120 | } | |
121 | ||
122 | // radians | |
123 | template <typename CalculationType> | |
124 | static inline bool contains_pi_half(CalculationType const& a1, | |
125 | CalculationType const& a2) | |
126 | { | |
127 | // azimuths a1 and a2 are assumed to be in radians | |
128 | BOOST_GEOMETRY_ASSERT(! math::equals(a1, a2)); | |
129 | ||
130 | static CalculationType const pi_half = math::half_pi<CalculationType>(); | |
131 | ||
132 | return (a1 < a2) | |
133 | ? (a1 < pi_half && pi_half < a2) | |
134 | : (a1 > pi_half && pi_half > a2); | |
135 | } | |
136 | ||
137 | // radians or degrees | |
138 | template <typename Units, typename CoordinateType> | |
139 | static inline bool crosses_antimeridian(CoordinateType const& lon1, | |
140 | CoordinateType const& lon2) | |
141 | { | |
142 | typedef math::detail::constants_on_spheroid | |
143 | < | |
144 | CoordinateType, Units | |
145 | > constants; | |
146 | ||
147 | return math::abs(lon1 - lon2) > constants::half_period(); // > pi | |
148 | } | |
149 | ||
150 | // degrees or radians | |
151 | template <typename Units, typename CalculationType, typename Strategy> | |
152 | static inline void compute_box_corners(CalculationType& lon1, | |
153 | CalculationType& lat1, | |
154 | CalculationType& lon2, | |
155 | CalculationType& lat2, | |
156 | CalculationType a1, | |
157 | CalculationType a2, | |
158 | Strategy const& strategy) | |
159 | { | |
160 | // coordinates are assumed to be in radians | |
161 | BOOST_GEOMETRY_ASSERT(lon1 <= lon2); | |
162 | boost::ignore_unused(lon1, lon2); | |
163 | ||
164 | CalculationType lat1_rad = math::as_radian<Units>(lat1); | |
165 | CalculationType lat2_rad = math::as_radian<Units>(lat2); | |
166 | ||
167 | if (math::equals(a1, a2)) | |
168 | { | |
169 | // the segment must lie on the equator or is very short or is meridian | |
170 | return; | |
171 | } | |
172 | ||
173 | if (lat1 > lat2) | |
174 | { | |
175 | std::swap(lat1, lat2); | |
176 | std::swap(lat1_rad, lat2_rad); | |
177 | std::swap(a1, a2); | |
178 | } | |
179 | ||
180 | if (contains_pi_half(a1, a2)) | |
181 | { | |
182 | CalculationType p_max = envelope_segment_call_vertex_latitude | |
183 | <CalculationType, CS_Tag>::apply(lat1_rad, a1, strategy); | |
184 | ||
185 | CalculationType const mid_lat = lat1 + lat2; | |
186 | if (mid_lat < 0) | |
187 | { | |
188 | // update using min latitude | |
189 | CalculationType const lat_min_rad = -p_max; | |
190 | CalculationType const lat_min | |
191 | = math::from_radian<Units>(lat_min_rad); | |
192 | ||
193 | if (lat1 > lat_min) | |
194 | { | |
195 | lat1 = lat_min; | |
196 | } | |
197 | } | |
198 | else | |
199 | { | |
200 | // update using max latitude | |
201 | CalculationType const lat_max_rad = p_max; | |
202 | CalculationType const lat_max | |
203 | = math::from_radian<Units>(lat_max_rad); | |
204 | ||
205 | if (lat2 < lat_max) | |
206 | { | |
207 | lat2 = lat_max; | |
208 | } | |
209 | } | |
210 | } | |
211 | } | |
212 | ||
213 | template <typename Units, typename CalculationType> | |
214 | static inline void special_cases(CalculationType& lon1, | |
215 | CalculationType& lat1, | |
216 | CalculationType& lon2, | |
217 | CalculationType& lat2) | |
218 | { | |
219 | typedef math::detail::constants_on_spheroid | |
220 | < | |
221 | CalculationType, Units | |
222 | > constants; | |
223 | ||
224 | bool is_pole1 = math::equals(math::abs(lat1), constants::max_latitude()); | |
225 | bool is_pole2 = math::equals(math::abs(lat2), constants::max_latitude()); | |
226 | ||
227 | if (is_pole1 && is_pole2) | |
228 | { | |
229 | // both points are poles; nothing more to do: | |
230 | // longitudes are already normalized to 0 | |
231 | // but just in case | |
232 | lon1 = 0; | |
233 | lon2 = 0; | |
234 | } | |
235 | else if (is_pole1 && !is_pole2) | |
236 | { | |
237 | // first point is a pole, second point is not: | |
238 | // make the longitude of the first point the same as that | |
239 | // of the second point | |
240 | lon1 = lon2; | |
241 | } | |
242 | else if (!is_pole1 && is_pole2) | |
243 | { | |
244 | // second point is a pole, first point is not: | |
245 | // make the longitude of the second point the same as that | |
246 | // of the first point | |
247 | lon2 = lon1; | |
248 | } | |
249 | ||
250 | if (lon1 == lon2) | |
251 | { | |
252 | // segment lies on a meridian | |
253 | if (lat1 > lat2) | |
254 | { | |
255 | std::swap(lat1, lat2); | |
256 | } | |
257 | return; | |
258 | } | |
259 | ||
260 | BOOST_GEOMETRY_ASSERT(!is_pole1 && !is_pole2); | |
261 | ||
262 | if (lon1 > lon2) | |
263 | { | |
264 | swap(lon1, lat1, lon2, lat2); | |
265 | } | |
266 | ||
267 | if (crosses_antimeridian<Units>(lon1, lon2)) | |
268 | { | |
269 | lon1 += constants::period(); | |
270 | swap(lon1, lat1, lon2, lat2); | |
271 | } | |
272 | } | |
273 | ||
274 | template | |
275 | < | |
276 | typename Units, | |
277 | typename CalculationType, | |
278 | typename Box | |
279 | > | |
280 | static inline void create_box(CalculationType lon1, | |
281 | CalculationType lat1, | |
282 | CalculationType lon2, | |
283 | CalculationType lat2, | |
284 | Box& mbr) | |
285 | { | |
286 | typedef typename coordinate_type<Box>::type box_coordinate_type; | |
287 | ||
288 | typedef typename helper_geometry | |
289 | < | |
290 | Box, box_coordinate_type, Units | |
291 | >::type helper_box_type; | |
292 | ||
293 | helper_box_type helper_mbr; | |
294 | ||
295 | geometry::set | |
296 | < | |
297 | min_corner, 0 | |
298 | >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lon1)); | |
299 | ||
300 | geometry::set | |
301 | < | |
302 | min_corner, 1 | |
303 | >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lat1)); | |
304 | ||
305 | geometry::set | |
306 | < | |
307 | max_corner, 0 | |
308 | >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lon2)); | |
309 | ||
310 | geometry::set | |
311 | < | |
312 | max_corner, 1 | |
313 | >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lat2)); | |
314 | ||
315 | geometry::detail::envelope::transform_units(helper_mbr, mbr); | |
316 | } | |
317 | ||
318 | ||
319 | template <typename Units, typename CalculationType, typename Strategy> | |
320 | static inline void apply(CalculationType& lon1, | |
321 | CalculationType& lat1, | |
322 | CalculationType& lon2, | |
323 | CalculationType& lat2, | |
324 | Strategy const& strategy) | |
325 | { | |
326 | special_cases<Units>(lon1, lat1, lon2, lat2); | |
327 | ||
328 | CalculationType lon1_rad = math::as_radian<Units>(lon1); | |
329 | CalculationType lat1_rad = math::as_radian<Units>(lat1); | |
330 | CalculationType lon2_rad = math::as_radian<Units>(lon2); | |
331 | CalculationType lat2_rad = math::as_radian<Units>(lat2); | |
332 | CalculationType alp1, alp2; | |
333 | strategy.apply(lon1_rad, lat1_rad, lon2_rad, lat2_rad, alp1, alp2); | |
334 | ||
335 | compute_box_corners<Units>(lon1, lat1, lon2, lat2, alp1, alp2, strategy); | |
336 | } | |
337 | ||
338 | public: | |
339 | template | |
340 | < | |
341 | typename Units, | |
342 | typename CalculationType, | |
343 | typename Box, | |
344 | typename Strategy | |
345 | > | |
346 | static inline void apply(CalculationType lon1, | |
347 | CalculationType lat1, | |
348 | CalculationType lon2, | |
349 | CalculationType lat2, | |
350 | Box& mbr, | |
351 | Strategy const& strategy) | |
352 | { | |
353 | typedef envelope_segment_convert_polar<Units, typename cs_tag<Box>::type> convert_polar; | |
354 | ||
355 | convert_polar::pre(lat1, lat2); | |
356 | ||
357 | apply<Units>(lon1, lat1, lon2, lat2, strategy); | |
358 | ||
359 | convert_polar::post(lat1, lat2); | |
360 | ||
361 | create_box<Units>(lon1, lat1, lon2, lat2, mbr); | |
362 | } | |
363 | ||
364 | }; | |
365 | ||
366 | } // namespace detail | |
367 | #endif // DOXYGEN_NO_DETAIL | |
368 | ||
369 | ||
370 | template | |
371 | < | |
372 | typename CalculationType = void | |
373 | > | |
374 | class spherical_segment | |
375 | { | |
376 | public: | |
377 | template <typename Point, typename Box> | |
378 | static inline void apply(Point const& point1, Point const& point2, | |
379 | Box& box) | |
380 | { | |
381 | Point p1_normalized, p2_normalized; | |
382 | strategy::normalize::spherical_point::apply(point1, p1_normalized); | |
383 | strategy::normalize::spherical_point::apply(point2, p2_normalized); | |
384 | ||
385 | geometry::strategy::azimuth::spherical<CalculationType> azimuth_spherical; | |
386 | ||
387 | typedef typename geometry::detail::cs_angular_units<Point>::type units_type; | |
388 | ||
389 | // first compute the envelope range for the first two coordinates | |
390 | strategy::envelope::detail::envelope_segment_impl | |
391 | < | |
392 | spherical_equatorial_tag | |
393 | >::template apply<units_type>(geometry::get<0>(p1_normalized), | |
394 | geometry::get<1>(p1_normalized), | |
395 | geometry::get<0>(p2_normalized), | |
396 | geometry::get<1>(p2_normalized), | |
397 | box, | |
398 | azimuth_spherical); | |
399 | ||
400 | // now compute the envelope range for coordinates of | |
401 | // dimension 2 and higher | |
402 | strategy::envelope::detail::envelope_one_segment | |
403 | < | |
404 | 2, dimension<Point>::value | |
405 | >::apply(point1, point2, box); | |
406 | } | |
407 | }; | |
408 | ||
409 | #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS | |
410 | ||
411 | namespace services | |
412 | { | |
413 | ||
414 | template <typename CalculationType> | |
415 | struct default_strategy<segment_tag, spherical_equatorial_tag, CalculationType> | |
416 | { | |
417 | typedef strategy::envelope::spherical_segment<CalculationType> type; | |
418 | }; | |
419 | ||
420 | ||
421 | template <typename CalculationType> | |
422 | struct default_strategy<segment_tag, spherical_polar_tag, CalculationType> | |
423 | { | |
424 | typedef strategy::envelope::spherical_segment<CalculationType> type; | |
425 | }; | |
426 | ||
427 | } | |
428 | ||
429 | #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS | |
430 | ||
431 | ||
432 | }} // namespace strategy::envelope | |
433 | ||
434 | }} //namepsace boost::geometry | |
435 | ||
436 | #endif // BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP | |
437 |