]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/geometry/strategy/spherical/envelope_segment.hpp
import quincy beta 17.1.0
[ceph.git] / ceph / src / boost / boost / geometry / strategy / spherical / envelope_segment.hpp
CommitLineData
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
44namespace boost { namespace geometry { namespace strategy { namespace envelope
45{
46
47#ifndef DOXYGEN_NO_DETAIL
48namespace detail
49{
50
51template <typename CalculationType, typename CS_Tag>
52struct 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
64template <typename CalculationType>
65struct 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
77template <typename Units, typename CS_Tag>
78struct 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
87template <typename Units>
88struct 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
106template <typename CS_Tag>
107class envelope_segment_impl
108{
109private:
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
338public:
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
370template
371<
372 typename CalculationType = void
373>
374class spherical_segment
375{
376public:
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
411namespace services
412{
413
414template <typename CalculationType>
415struct default_strategy<segment_tag, spherical_equatorial_tag, CalculationType>
416{
417 typedef strategy::envelope::spherical_segment<CalculationType> type;
418};
419
420
421template <typename CalculationType>
422struct 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