123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437 |
- // Boost.Geometry (aka GGL, Generic Geometry Library)
- // Copyright (c) 2017-2020 Oracle and/or its affiliates.
- // Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle
- // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
- // Use, modification and distribution is subject to the Boost Software License,
- // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- #ifndef BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP
- #define BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP
- #include <cstddef>
- #include <utility>
- #include <boost/core/ignore_unused.hpp>
- #include <boost/numeric/conversion/cast.hpp>
- #include <boost/geometry/algorithms/detail/envelope/transform_units.hpp>
- #include <boost/geometry/core/assert.hpp>
- #include <boost/geometry/core/coordinate_system.hpp>
- #include <boost/geometry/core/coordinate_type.hpp>
- #include <boost/geometry/core/cs.hpp>
- #include <boost/geometry/core/point_type.hpp>
- #include <boost/geometry/core/radian_access.hpp>
- #include <boost/geometry/core/tags.hpp>
- #include <boost/geometry/formulas/meridian_segment.hpp>
- #include <boost/geometry/formulas/vertex_latitude.hpp>
- #include <boost/geometry/geometries/helper_geometry.hpp>
- #include <boost/geometry/strategy/cartesian/envelope_segment.hpp>
- #include <boost/geometry/strategy/envelope.hpp>
- #include <boost/geometry/strategies/normalize.hpp>
- #include <boost/geometry/strategies/spherical/azimuth.hpp>
- #include <boost/geometry/strategy/spherical/expand_box.hpp>
- #include <boost/geometry/util/math.hpp>
- namespace boost { namespace geometry { namespace strategy { namespace envelope
- {
- #ifndef DOXYGEN_NO_DETAIL
- namespace detail
- {
- template <typename CalculationType, typename CS_Tag>
- struct envelope_segment_call_vertex_latitude
- {
- template <typename T1, typename T2, typename Strategy>
- static inline CalculationType apply(T1 const& lat1,
- T2 const& alp1,
- Strategy const& )
- {
- return geometry::formula::vertex_latitude<CalculationType, CS_Tag>
- ::apply(lat1, alp1);
- }
- };
- template <typename CalculationType>
- struct envelope_segment_call_vertex_latitude<CalculationType, geographic_tag>
- {
- template <typename T1, typename T2, typename Strategy>
- static inline CalculationType apply(T1 const& lat1,
- T2 const& alp1,
- Strategy const& strategy)
- {
- return geometry::formula::vertex_latitude<CalculationType, geographic_tag>
- ::apply(lat1, alp1, strategy.model());
- }
- };
- template <typename Units, typename CS_Tag>
- struct envelope_segment_convert_polar
- {
- template <typename T>
- static inline void pre(T & , T & ) {}
- template <typename T>
- static inline void post(T & , T & ) {}
- };
- template <typename Units>
- struct envelope_segment_convert_polar<Units, spherical_polar_tag>
- {
- template <typename T>
- static inline void pre(T & lat1, T & lat2)
- {
- lat1 = math::latitude_convert_ep<Units>(lat1);
- lat2 = math::latitude_convert_ep<Units>(lat2);
- }
- template <typename T>
- static inline void post(T & lat1, T & lat2)
- {
- lat1 = math::latitude_convert_ep<Units>(lat1);
- lat2 = math::latitude_convert_ep<Units>(lat2);
- std::swap(lat1, lat2);
- }
- };
- template <typename CS_Tag>
- class envelope_segment_impl
- {
- private:
- // degrees or radians
- template <typename CalculationType>
- static inline void swap(CalculationType& lon1,
- CalculationType& lat1,
- CalculationType& lon2,
- CalculationType& lat2)
- {
- std::swap(lon1, lon2);
- std::swap(lat1, lat2);
- }
- // radians
- template <typename CalculationType>
- static inline bool contains_pi_half(CalculationType const& a1,
- CalculationType const& a2)
- {
- // azimuths a1 and a2 are assumed to be in radians
- BOOST_GEOMETRY_ASSERT(! math::equals(a1, a2));
- static CalculationType const pi_half = math::half_pi<CalculationType>();
- return (a1 < a2)
- ? (a1 < pi_half && pi_half < a2)
- : (a1 > pi_half && pi_half > a2);
- }
- // radians or degrees
- template <typename Units, typename CoordinateType>
- static inline bool crosses_antimeridian(CoordinateType const& lon1,
- CoordinateType const& lon2)
- {
- typedef math::detail::constants_on_spheroid
- <
- CoordinateType, Units
- > constants;
- return math::abs(lon1 - lon2) > constants::half_period(); // > pi
- }
- // degrees or radians
- template <typename Units, typename CalculationType, typename Strategy>
- static inline void compute_box_corners(CalculationType& lon1,
- CalculationType& lat1,
- CalculationType& lon2,
- CalculationType& lat2,
- CalculationType a1,
- CalculationType a2,
- Strategy const& strategy)
- {
- // coordinates are assumed to be in radians
- BOOST_GEOMETRY_ASSERT(lon1 <= lon2);
- boost::ignore_unused(lon1, lon2);
- CalculationType lat1_rad = math::as_radian<Units>(lat1);
- CalculationType lat2_rad = math::as_radian<Units>(lat2);
- if (math::equals(a1, a2))
- {
- // the segment must lie on the equator or is very short or is meridian
- return;
- }
- if (lat1 > lat2)
- {
- std::swap(lat1, lat2);
- std::swap(lat1_rad, lat2_rad);
- std::swap(a1, a2);
- }
- if (contains_pi_half(a1, a2))
- {
- CalculationType p_max = envelope_segment_call_vertex_latitude
- <CalculationType, CS_Tag>::apply(lat1_rad, a1, strategy);
- CalculationType const mid_lat = lat1 + lat2;
- if (mid_lat < 0)
- {
- // update using min latitude
- CalculationType const lat_min_rad = -p_max;
- CalculationType const lat_min
- = math::from_radian<Units>(lat_min_rad);
- if (lat1 > lat_min)
- {
- lat1 = lat_min;
- }
- }
- else
- {
- // update using max latitude
- CalculationType const lat_max_rad = p_max;
- CalculationType const lat_max
- = math::from_radian<Units>(lat_max_rad);
- if (lat2 < lat_max)
- {
- lat2 = lat_max;
- }
- }
- }
- }
- template <typename Units, typename CalculationType>
- static inline void special_cases(CalculationType& lon1,
- CalculationType& lat1,
- CalculationType& lon2,
- CalculationType& lat2)
- {
- typedef math::detail::constants_on_spheroid
- <
- CalculationType, Units
- > constants;
- bool is_pole1 = math::equals(math::abs(lat1), constants::max_latitude());
- bool is_pole2 = math::equals(math::abs(lat2), constants::max_latitude());
- if (is_pole1 && is_pole2)
- {
- // both points are poles; nothing more to do:
- // longitudes are already normalized to 0
- // but just in case
- lon1 = 0;
- lon2 = 0;
- }
- else if (is_pole1 && !is_pole2)
- {
- // first point is a pole, second point is not:
- // make the longitude of the first point the same as that
- // of the second point
- lon1 = lon2;
- }
- else if (!is_pole1 && is_pole2)
- {
- // second point is a pole, first point is not:
- // make the longitude of the second point the same as that
- // of the first point
- lon2 = lon1;
- }
- if (lon1 == lon2)
- {
- // segment lies on a meridian
- if (lat1 > lat2)
- {
- std::swap(lat1, lat2);
- }
- return;
- }
- BOOST_GEOMETRY_ASSERT(!is_pole1 && !is_pole2);
- if (lon1 > lon2)
- {
- swap(lon1, lat1, lon2, lat2);
- }
- if (crosses_antimeridian<Units>(lon1, lon2))
- {
- lon1 += constants::period();
- swap(lon1, lat1, lon2, lat2);
- }
- }
- template
- <
- typename Units,
- typename CalculationType,
- typename Box
- >
- static inline void create_box(CalculationType lon1,
- CalculationType lat1,
- CalculationType lon2,
- CalculationType lat2,
- Box& mbr)
- {
- typedef typename coordinate_type<Box>::type box_coordinate_type;
- typedef typename helper_geometry
- <
- Box, box_coordinate_type, Units
- >::type helper_box_type;
- helper_box_type helper_mbr;
- geometry::set
- <
- min_corner, 0
- >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lon1));
- geometry::set
- <
- min_corner, 1
- >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lat1));
- geometry::set
- <
- max_corner, 0
- >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lon2));
- geometry::set
- <
- max_corner, 1
- >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lat2));
- geometry::detail::envelope::transform_units(helper_mbr, mbr);
- }
- template <typename Units, typename CalculationType, typename Strategy>
- static inline void apply(CalculationType& lon1,
- CalculationType& lat1,
- CalculationType& lon2,
- CalculationType& lat2,
- Strategy const& strategy)
- {
- special_cases<Units>(lon1, lat1, lon2, lat2);
- CalculationType lon1_rad = math::as_radian<Units>(lon1);
- CalculationType lat1_rad = math::as_radian<Units>(lat1);
- CalculationType lon2_rad = math::as_radian<Units>(lon2);
- CalculationType lat2_rad = math::as_radian<Units>(lat2);
- CalculationType alp1, alp2;
- strategy.apply(lon1_rad, lat1_rad, lon2_rad, lat2_rad, alp1, alp2);
- compute_box_corners<Units>(lon1, lat1, lon2, lat2, alp1, alp2, strategy);
- }
- public:
- template
- <
- typename Units,
- typename CalculationType,
- typename Box,
- typename Strategy
- >
- static inline void apply(CalculationType lon1,
- CalculationType lat1,
- CalculationType lon2,
- CalculationType lat2,
- Box& mbr,
- Strategy const& strategy)
- {
- typedef envelope_segment_convert_polar<Units, typename cs_tag<Box>::type> convert_polar;
- convert_polar::pre(lat1, lat2);
- apply<Units>(lon1, lat1, lon2, lat2, strategy);
- convert_polar::post(lat1, lat2);
- create_box<Units>(lon1, lat1, lon2, lat2, mbr);
- }
- };
- } // namespace detail
- #endif // DOXYGEN_NO_DETAIL
- template
- <
- typename CalculationType = void
- >
- class spherical_segment
- {
- public:
- template <typename Point, typename Box>
- static inline void apply(Point const& point1, Point const& point2,
- Box& box)
- {
- Point p1_normalized, p2_normalized;
- strategy::normalize::spherical_point::apply(point1, p1_normalized);
- strategy::normalize::spherical_point::apply(point2, p2_normalized);
- geometry::strategy::azimuth::spherical<CalculationType> azimuth_spherical;
- typedef typename geometry::detail::cs_angular_units<Point>::type units_type;
- // first compute the envelope range for the first two coordinates
- strategy::envelope::detail::envelope_segment_impl
- <
- spherical_equatorial_tag
- >::template apply<units_type>(geometry::get<0>(p1_normalized),
- geometry::get<1>(p1_normalized),
- geometry::get<0>(p2_normalized),
- geometry::get<1>(p2_normalized),
- box,
- azimuth_spherical);
- // now compute the envelope range for coordinates of
- // dimension 2 and higher
- strategy::envelope::detail::envelope_one_segment
- <
- 2, dimension<Point>::value
- >::apply(point1, point2, box);
- }
- };
- #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
- namespace services
- {
- template <typename CalculationType>
- struct default_strategy<segment_tag, spherical_equatorial_tag, CalculationType>
- {
- typedef strategy::envelope::spherical_segment<CalculationType> type;
- };
- template <typename CalculationType>
- struct default_strategy<segment_tag, spherical_polar_tag, CalculationType>
- {
- typedef strategy::envelope::spherical_segment<CalculationType> type;
- };
- }
- #endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
- }} // namespace strategy::envelope
- }} //namepsace boost::geometry
- #endif // BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP
|