File indexing completed on 2025-01-18 09:36:52
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #ifndef BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP
0012 #define BOOST_GEOMETRY_STRATEGY_SPHERICAL_ENVELOPE_SEGMENT_HPP
0013
0014
0015 #include <cstddef>
0016 #include <utility>
0017
0018 #include <boost/core/ignore_unused.hpp>
0019 #include <boost/numeric/conversion/cast.hpp>
0020
0021 #include <boost/geometry/algorithms/detail/envelope/transform_units.hpp>
0022
0023 #include <boost/geometry/core/assert.hpp>
0024 #include <boost/geometry/core/coordinate_system.hpp>
0025 #include <boost/geometry/core/coordinate_type.hpp>
0026 #include <boost/geometry/core/cs.hpp>
0027 #include <boost/geometry/core/point_type.hpp>
0028 #include <boost/geometry/core/radian_access.hpp>
0029 #include <boost/geometry/core/tags.hpp>
0030
0031 #include <boost/geometry/formulas/meridian_segment.hpp>
0032 #include <boost/geometry/formulas/vertex_latitude.hpp>
0033
0034 #include <boost/geometry/geometries/helper_geometry.hpp>
0035
0036 #include <boost/geometry/strategy/cartesian/envelope_segment.hpp>
0037 #include <boost/geometry/strategy/envelope.hpp>
0038 #include <boost/geometry/strategies/normalize.hpp>
0039 #include <boost/geometry/strategies/spherical/azimuth.hpp>
0040 #include <boost/geometry/strategy/spherical/expand_box.hpp>
0041
0042 #include <boost/geometry/util/math.hpp>
0043
0044 namespace boost { namespace geometry { namespace strategy { namespace envelope
0045 {
0046
0047 #ifndef DOXYGEN_NO_DETAIL
0048 namespace detail
0049 {
0050
0051 template <typename CalculationType, typename CS_Tag>
0052 struct envelope_segment_call_vertex_latitude
0053 {
0054 template <typename T1, typename T2, typename Strategy>
0055 static inline CalculationType apply(T1 const& lat1,
0056 T2 const& alp1,
0057 Strategy const& )
0058 {
0059 return geometry::formula::vertex_latitude<CalculationType, CS_Tag>
0060 ::apply(lat1, alp1);
0061 }
0062 };
0063
0064 template <typename CalculationType>
0065 struct envelope_segment_call_vertex_latitude<CalculationType, geographic_tag>
0066 {
0067 template <typename T1, typename T2, typename Strategy>
0068 static inline CalculationType apply(T1 const& lat1,
0069 T2 const& alp1,
0070 Strategy const& strategy)
0071 {
0072 return geometry::formula::vertex_latitude<CalculationType, geographic_tag>
0073 ::apply(lat1, alp1, strategy.model());
0074 }
0075 };
0076
0077 template <typename Units, typename CS_Tag>
0078 struct envelope_segment_convert_polar
0079 {
0080 template <typename T>
0081 static inline void pre(T & , T & ) {}
0082
0083 template <typename T>
0084 static inline void post(T & , T & ) {}
0085 };
0086
0087 template <typename Units>
0088 struct envelope_segment_convert_polar<Units, spherical_polar_tag>
0089 {
0090 template <typename T>
0091 static inline void pre(T & lat1, T & lat2)
0092 {
0093 lat1 = math::latitude_convert_ep<Units>(lat1);
0094 lat2 = math::latitude_convert_ep<Units>(lat2);
0095 }
0096
0097 template <typename T>
0098 static inline void post(T & lat1, T & lat2)
0099 {
0100 lat1 = math::latitude_convert_ep<Units>(lat1);
0101 lat2 = math::latitude_convert_ep<Units>(lat2);
0102 std::swap(lat1, lat2);
0103 }
0104 };
0105
0106 template <typename CS_Tag>
0107 class envelope_segment_impl
0108 {
0109 private:
0110
0111
0112 template <typename CalculationType>
0113 static inline void swap(CalculationType& lon1,
0114 CalculationType& lat1,
0115 CalculationType& lon2,
0116 CalculationType& lat2)
0117 {
0118 std::swap(lon1, lon2);
0119 std::swap(lat1, lat2);
0120 }
0121
0122
0123 template <typename CalculationType>
0124 static inline bool contains_pi_half(CalculationType const& a1,
0125 CalculationType const& a2)
0126 {
0127
0128
0129 static CalculationType const pi_half = math::half_pi<CalculationType>();
0130
0131 return (a1 < a2)
0132 ? (a1 < pi_half && pi_half < a2)
0133 : (a1 > pi_half && pi_half > a2);
0134 }
0135
0136
0137 template <typename Units, typename CoordinateType>
0138 static inline bool crosses_antimeridian(CoordinateType const& lon1,
0139 CoordinateType const& lon2)
0140 {
0141 typedef math::detail::constants_on_spheroid
0142 <
0143 CoordinateType, Units
0144 > constants;
0145
0146 return math::abs(lon1 - lon2) > constants::half_period();
0147 }
0148
0149
0150 template <typename Units, typename CalculationType, typename Strategy>
0151 static inline void compute_box_corners(CalculationType& lon1,
0152 CalculationType& lat1,
0153 CalculationType& lon2,
0154 CalculationType& lat2,
0155 CalculationType a1,
0156 CalculationType a2,
0157 Strategy const& strategy)
0158 {
0159
0160 BOOST_GEOMETRY_ASSERT(lon1 <= lon2);
0161 boost::ignore_unused(lon1, lon2);
0162
0163 CalculationType lat1_rad = math::as_radian<Units>(lat1);
0164 CalculationType lat2_rad = math::as_radian<Units>(lat2);
0165
0166 if (lat1 > lat2)
0167 {
0168 std::swap(lat1, lat2);
0169 std::swap(lat1_rad, lat2_rad);
0170 std::swap(a1, a2);
0171 }
0172
0173 if (contains_pi_half(a1, a2))
0174 {
0175 CalculationType p_max = envelope_segment_call_vertex_latitude
0176 <CalculationType, CS_Tag>::apply(lat1_rad, a1, strategy);
0177
0178 CalculationType const mid_lat = lat1 + lat2;
0179 if (mid_lat < 0)
0180 {
0181
0182 CalculationType const lat_min_rad = -p_max;
0183 CalculationType const lat_min
0184 = math::from_radian<Units>(lat_min_rad);
0185
0186 if (lat1 > lat_min)
0187 {
0188 lat1 = lat_min;
0189 }
0190 }
0191 else
0192 {
0193
0194 CalculationType const lat_max_rad = p_max;
0195 CalculationType const lat_max
0196 = math::from_radian<Units>(lat_max_rad);
0197
0198 if (lat2 < lat_max)
0199 {
0200 lat2 = lat_max;
0201 }
0202 }
0203 }
0204 }
0205
0206 template <typename Units, typename CalculationType>
0207 static inline void special_cases(CalculationType& lon1,
0208 CalculationType& lat1,
0209 CalculationType& lon2,
0210 CalculationType& lat2)
0211 {
0212 typedef math::detail::constants_on_spheroid
0213 <
0214 CalculationType, Units
0215 > constants;
0216
0217 bool is_pole1 = math::equals(math::abs(lat1), constants::max_latitude());
0218 bool is_pole2 = math::equals(math::abs(lat2), constants::max_latitude());
0219
0220 if (is_pole1 && is_pole2)
0221 {
0222
0223
0224
0225 lon1 = 0;
0226 lon2 = 0;
0227 }
0228 else if (is_pole1 && !is_pole2)
0229 {
0230
0231
0232
0233 lon1 = lon2;
0234 }
0235 else if (!is_pole1 && is_pole2)
0236 {
0237
0238
0239
0240 lon2 = lon1;
0241 }
0242
0243 if (lon1 == lon2)
0244 {
0245
0246 if (lat1 > lat2)
0247 {
0248 std::swap(lat1, lat2);
0249 }
0250 return;
0251 }
0252
0253 BOOST_GEOMETRY_ASSERT(!is_pole1 && !is_pole2);
0254
0255 if (lon1 > lon2)
0256 {
0257 swap(lon1, lat1, lon2, lat2);
0258 }
0259
0260 if (crosses_antimeridian<Units>(lon1, lon2))
0261 {
0262 lon1 += constants::period();
0263 swap(lon1, lat1, lon2, lat2);
0264 }
0265 }
0266
0267 template
0268 <
0269 typename Units,
0270 typename CalculationType,
0271 typename Box
0272 >
0273 static inline void create_box(CalculationType lon1,
0274 CalculationType lat1,
0275 CalculationType lon2,
0276 CalculationType lat2,
0277 Box& mbr)
0278 {
0279 typedef typename coordinate_type<Box>::type box_coordinate_type;
0280
0281 typedef typename helper_geometry
0282 <
0283 Box, box_coordinate_type, Units
0284 >::type helper_box_type;
0285
0286 helper_box_type helper_mbr;
0287
0288 geometry::set
0289 <
0290 min_corner, 0
0291 >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lon1));
0292
0293 geometry::set
0294 <
0295 min_corner, 1
0296 >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lat1));
0297
0298 geometry::set
0299 <
0300 max_corner, 0
0301 >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lon2));
0302
0303 geometry::set
0304 <
0305 max_corner, 1
0306 >(helper_mbr, boost::numeric_cast<box_coordinate_type>(lat2));
0307
0308 geometry::detail::envelope::transform_units(helper_mbr, mbr);
0309 }
0310
0311
0312 template <typename Units, typename CalculationType, typename Strategy>
0313 static inline void apply(CalculationType& lon1,
0314 CalculationType& lat1,
0315 CalculationType& lon2,
0316 CalculationType& lat2,
0317 Strategy const& strategy)
0318 {
0319 special_cases<Units>(lon1, lat1, lon2, lat2);
0320
0321 CalculationType lon1_rad = math::as_radian<Units>(lon1);
0322 CalculationType lat1_rad = math::as_radian<Units>(lat1);
0323 CalculationType lon2_rad = math::as_radian<Units>(lon2);
0324 CalculationType lat2_rad = math::as_radian<Units>(lat2);
0325 CalculationType alp1, alp2;
0326 strategy.apply(lon1_rad, lat1_rad, lon2_rad, lat2_rad, alp1, alp2);
0327
0328 compute_box_corners<Units>(lon1, lat1, lon2, lat2, alp1, alp2, strategy);
0329 }
0330
0331 public:
0332 template
0333 <
0334 typename Units,
0335 typename CalculationType,
0336 typename Box,
0337 typename Strategy
0338 >
0339 static inline void apply(CalculationType lon1,
0340 CalculationType lat1,
0341 CalculationType lon2,
0342 CalculationType lat2,
0343 Box& mbr,
0344 Strategy const& strategy)
0345 {
0346 typedef envelope_segment_convert_polar<Units, typename cs_tag<Box>::type> convert_polar;
0347
0348 convert_polar::pre(lat1, lat2);
0349
0350 apply<Units>(lon1, lat1, lon2, lat2, strategy);
0351
0352 convert_polar::post(lat1, lat2);
0353
0354 create_box<Units>(lon1, lat1, lon2, lat2, mbr);
0355 }
0356
0357 };
0358
0359 }
0360 #endif
0361
0362
0363 template
0364 <
0365 typename CalculationType = void
0366 >
0367 class spherical_segment
0368 {
0369 public:
0370 template <typename Point, typename Box>
0371 static inline void apply(Point const& point1, Point const& point2,
0372 Box& box)
0373 {
0374 Point p1_normalized, p2_normalized;
0375 strategy::normalize::spherical_point::apply(point1, p1_normalized);
0376 strategy::normalize::spherical_point::apply(point2, p2_normalized);
0377
0378 geometry::strategy::azimuth::spherical<CalculationType> azimuth_spherical;
0379
0380 typedef typename geometry::detail::cs_angular_units<Point>::type units_type;
0381
0382
0383 strategy::envelope::detail::envelope_segment_impl
0384 <
0385 spherical_equatorial_tag
0386 >::template apply<units_type>(geometry::get<0>(p1_normalized),
0387 geometry::get<1>(p1_normalized),
0388 geometry::get<0>(p2_normalized),
0389 geometry::get<1>(p2_normalized),
0390 box,
0391 azimuth_spherical);
0392
0393
0394
0395 strategy::envelope::detail::envelope_one_segment
0396 <
0397 2, dimension<Point>::value
0398 >::apply(point1, point2, box);
0399 }
0400 };
0401
0402 #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
0403
0404 namespace services
0405 {
0406
0407 template <typename CalculationType>
0408 struct default_strategy<segment_tag, spherical_equatorial_tag, CalculationType>
0409 {
0410 typedef strategy::envelope::spherical_segment<CalculationType> type;
0411 };
0412
0413
0414 template <typename CalculationType>
0415 struct default_strategy<segment_tag, spherical_polar_tag, CalculationType>
0416 {
0417 typedef strategy::envelope::spherical_segment<CalculationType> type;
0418 };
0419
0420 }
0421
0422 #endif
0423
0424
0425 }}
0426
0427 }}
0428
0429 #endif
0430