File indexing completed on 2025-09-15 08:36:34
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018 #ifndef BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP
0019 #define BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP
0020
0021
0022 #include <boost/geometry/core/access.hpp>
0023 #include <boost/geometry/core/coordinate_system.hpp>
0024 #include <boost/geometry/core/cs.hpp>
0025 #include <boost/geometry/core/tags.hpp>
0026
0027 #include <boost/geometry/util/math.hpp>
0028 #include <boost/geometry/util/select_calculation_type.hpp>
0029 #include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
0030
0031 #include <boost/geometry/strategy/spherical/expand_point.hpp>
0032
0033 #include <boost/geometry/strategies/cartesian/point_in_box.hpp>
0034 #include <boost/geometry/strategies/covered_by.hpp>
0035 #include <boost/geometry/strategies/side.hpp>
0036 #include <boost/geometry/strategies/spherical/disjoint_box_box.hpp>
0037 #include <boost/geometry/strategies/spherical/ssf.hpp>
0038 #include <boost/geometry/strategies/within.hpp>
0039
0040
0041 namespace boost { namespace geometry
0042 {
0043
0044 namespace strategy { namespace within
0045 {
0046
0047
0048 #ifndef DOXYGEN_NO_DETAIL
0049 namespace detail
0050 {
0051
0052 template <typename SideStrategy, typename CalculationType>
0053 class spherical_winding_base
0054 {
0055 template <typename Point, typename PointOfSegment>
0056 struct calculation_type
0057 : select_calculation_type
0058 <
0059 Point,
0060 PointOfSegment,
0061 CalculationType
0062 >
0063 {};
0064
0065
0066 class counter
0067 {
0068 int m_count;
0069
0070 int m_count_s;
0071 int m_raw_count;
0072 int m_raw_count_anti;
0073 bool m_touches;
0074
0075 inline int code() const
0076 {
0077 if (m_touches)
0078 {
0079 return 0;
0080 }
0081
0082 if (m_raw_count != 0 && m_raw_count_anti != 0)
0083 {
0084 if (m_raw_count > 0)
0085 {
0086 return (m_count + m_count_s) == 0 ? -1 : 1;
0087 }
0088 else
0089 {
0090
0091
0092 return m_count == 0 ? -1 : 1;
0093 }
0094 }
0095
0096 return m_count == 0 ? -1 : 1;
0097 }
0098
0099 public :
0100 friend class spherical_winding_base;
0101
0102 inline counter()
0103 : m_count(0)
0104
0105 , m_count_s(0)
0106 , m_raw_count(0)
0107 , m_raw_count_anti(0)
0108 , m_touches(false)
0109 {}
0110
0111 };
0112
0113 struct count_info
0114 {
0115 explicit count_info(int c = 0, bool ia = false)
0116 : count(c)
0117 , is_anti(ia)
0118 {}
0119
0120 int count;
0121 bool is_anti;
0122 };
0123
0124 public:
0125 using cs_tag = typename SideStrategy::cs_tag;
0126
0127 spherical_winding_base() = default;
0128
0129 template <typename Model>
0130 explicit spherical_winding_base(Model const& model)
0131 : m_side_strategy(model)
0132 {}
0133
0134
0135 typedef counter state_type;
0136
0137 template <typename Point, typename PointOfSegment>
0138 inline bool apply(Point const& point,
0139 PointOfSegment const& s1, PointOfSegment const& s2,
0140 counter& state) const
0141 {
0142 typedef typename calculation_type<Point, PointOfSegment>::type calc_t;
0143 typedef typename geometry::detail::cs_angular_units<Point>::type units_t;
0144 typedef math::detail::constants_on_spheroid<calc_t, units_t> constants;
0145
0146 bool eq1 = false;
0147 bool eq2 = false;
0148 bool s_antipodal = false;
0149
0150 count_info ci = check_segment(point, s1, s2, state, eq1, eq2, s_antipodal);
0151 if (ci.count != 0)
0152 {
0153 if (! ci.is_anti)
0154 {
0155 int side = 0;
0156 if (ci.count == 1 || ci.count == -1)
0157 {
0158 side = side_equal(point, eq1 ? s1 : s2, ci);
0159 }
0160 else
0161 {
0162 if (! s_antipodal)
0163 {
0164
0165 side = m_side_strategy.apply(s1, s2, point);
0166 }
0167 else
0168 {
0169 calc_t const pi = constants::half_period();
0170 calc_t const s1_lat = get<1>(s1);
0171 calc_t const s2_lat = get<1>(s2);
0172
0173 side = math::sign(ci.count)
0174 * (pi - s1_lat - s2_lat <= pi
0175 ? -1
0176 : 1);
0177 }
0178 }
0179
0180 if (side == 0)
0181 {
0182
0183 state.m_touches = true;
0184 state.m_count = 0;
0185 return false;
0186 }
0187
0188
0189
0190
0191
0192 if (side * ci.count > 0)
0193 {
0194 state.m_count += ci.count;
0195 }
0196
0197 state.m_raw_count += ci.count;
0198 }
0199 else
0200 {
0201
0202
0203
0204
0205
0206
0207
0208
0209
0210 state.m_count_s -= ci.count;
0211
0212 state.m_raw_count_anti -= ci.count;
0213 }
0214 }
0215 return ! state.m_touches;
0216 }
0217
0218 static inline int result(counter const& state)
0219 {
0220 return state.code();
0221 }
0222
0223 protected:
0224 template <typename Point, typename PointOfSegment>
0225 static inline count_info check_segment(Point const& point,
0226 PointOfSegment const& seg1,
0227 PointOfSegment const& seg2,
0228 counter& state,
0229 bool& eq1, bool& eq2, bool& s_antipodal)
0230 {
0231 if (check_touch(point, seg1, seg2, state, eq1, eq2, s_antipodal))
0232 {
0233 return count_info(0, false);
0234 }
0235
0236 return calculate_count(point, seg1, seg2, eq1, eq2, s_antipodal);
0237 }
0238
0239 template <typename Point, typename PointOfSegment>
0240 static inline int check_touch(Point const& point,
0241 PointOfSegment const& seg1,
0242 PointOfSegment const& seg2,
0243 counter& state,
0244 bool& eq1,
0245 bool& eq2,
0246 bool& s_antipodal)
0247 {
0248 typedef typename calculation_type<Point, PointOfSegment>::type calc_t;
0249 typedef typename geometry::detail::cs_angular_units<Point>::type units_t;
0250 typedef math::detail::constants_on_spheroid<calc_t, units_t> constants;
0251
0252 calc_t const c0 = 0;
0253 calc_t const c2 = 2;
0254 calc_t const pi = constants::half_period();
0255 calc_t const half_pi = pi / c2;
0256
0257 calc_t const p_lon = get<0>(point);
0258 calc_t const s1_lon = get<0>(seg1);
0259 calc_t const s2_lon = get<0>(seg2);
0260 calc_t const p_lat = get<1>(point);
0261 calc_t const s1_lat = get<1>(seg1);
0262 calc_t const s2_lat = get<1>(seg2);
0263
0264
0265
0266
0267
0268
0269 bool eq1_strict = longitudes_equal<units_t>(s1_lon, p_lon);
0270 bool eq2_strict = longitudes_equal<units_t>(s2_lon, p_lon);
0271 bool eq1_anti = false;
0272 bool eq2_anti = false;
0273
0274 calc_t const anti_p_lon = p_lon + (p_lon <= c0 ? pi : -pi);
0275
0276 eq1 = eq1_strict
0277 || (eq1_anti = longitudes_equal<units_t>(s1_lon, anti_p_lon));
0278 eq2 = eq2_strict
0279 || (eq2_anti = longitudes_equal<units_t>(s2_lon, anti_p_lon));
0280
0281
0282 calc_t const s_lon_diff = math::longitude_distance_signed<units_t>(s1_lon, s2_lon);
0283 s_antipodal = math::equals(s_lon_diff, pi);
0284 if (s_antipodal)
0285 {
0286 eq1 = eq2 = eq1 || eq2;
0287
0288
0289 if (math::equals(math::abs(p_lat), half_pi))
0290 {
0291 eq1 = eq2 = true;
0292 }
0293 }
0294
0295
0296 if (math::longitude_distance_signed<units_t>(s2_lon, p_lon) == c0)
0297 {
0298 bool const s1_north = math::equals(get<1>(seg1), half_pi);
0299 bool const s1_south = math::equals(get<1>(seg1), -half_pi);
0300 if (s1_north || s1_south)
0301 {
0302 state.m_touches = s1_south ? s2_lat > p_lat : s2_lat < p_lat;
0303 return state.m_touches;
0304 }
0305 }
0306 if (math::longitude_distance_signed<units_t>(s1_lon, p_lon) == c0)
0307 {
0308 bool const s2_north = math::equals(get<1>(seg2), half_pi);
0309 bool const s2_south = math::equals(get<1>(seg2), -half_pi);
0310 if (s2_north || s2_south)
0311 {
0312 state.m_touches = s2_south ? s1_lat > p_lat : s1_lat < p_lat;
0313 return state.m_touches;
0314 }
0315 }
0316
0317
0318
0319 if (eq1 && eq2)
0320 {
0321
0322 if (! s_antipodal)
0323 {
0324
0325 if ( (s1_lat <= p_lat && s2_lat >= p_lat) || (s2_lat <= p_lat && s1_lat >= p_lat) )
0326 {
0327 if (!eq1_anti || !eq2_anti)
0328 {
0329 state.m_touches = true;
0330 }
0331 }
0332 }
0333 else
0334 {
0335
0336 if (pi - s1_lat - s2_lat <= pi)
0337 {
0338 if ( (eq1_strict && s1_lat <= p_lat) || (eq2_strict && s2_lat <= p_lat)
0339 || math::equals(p_lat, half_pi) )
0340 {
0341 state.m_touches = true;
0342 }
0343 else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, -half_pi) )
0344 {
0345 return false;
0346 }
0347 }
0348 else
0349 {
0350 if ( (eq1_strict && s1_lat >= p_lat) || (eq2_strict && s2_lat >= p_lat)
0351 || math::equals(p_lat, -half_pi) )
0352 {
0353 state.m_touches = true;
0354 }
0355 else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, half_pi) )
0356 {
0357 return false;
0358 }
0359 }
0360 }
0361
0362 return true;
0363 }
0364
0365 return false;
0366 }
0367
0368
0369 template <typename Point, typename PointOfSegment>
0370 static inline count_info calculate_count(Point const& point,
0371 PointOfSegment const& seg1,
0372 PointOfSegment const& seg2,
0373 bool eq1, bool eq2, bool s_antipodal)
0374 {
0375 typedef typename calculation_type<Point, PointOfSegment>::type calc_t;
0376 typedef typename geometry::detail::cs_angular_units<Point>::type units_t;
0377 typedef math::detail::constants_on_spheroid<calc_t, units_t> constants;
0378
0379
0380
0381
0382
0383
0384 calc_t const c0 = 0;
0385 calc_t const c2 = 2;
0386 calc_t const pi = constants::half_period();
0387 calc_t const half_pi = pi / c2;
0388
0389 bool const s1_is_pole = math::equals(std::abs(get<1>(seg1)), half_pi);
0390 bool const s2_is_pole = math::equals(std::abs(get<1>(seg2)), half_pi);
0391
0392 if (s1_is_pole && s2_is_pole)
0393 {
0394 return count_info(0, false);
0395 }
0396
0397 calc_t const p = get<0>(point);
0398 calc_t s1 = get<0>(seg1);
0399 calc_t s2 = get<0>(seg2);
0400
0401
0402
0403 if (s1_is_pole) s1 = (p != 0) ? 0 : 1;
0404 if (s2_is_pole) s2 = (p != 0) ? 0 : 1;
0405
0406 calc_t const s1_p = math::longitude_distance_signed<units_t>(s1, p);
0407
0408 if (s_antipodal)
0409 {
0410 return count_info(s1_p < c0 ? -2 : 2, false);
0411 }
0412
0413 calc_t const s1_s2 = math::longitude_distance_signed<units_t>(s1, s2);
0414
0415 if (eq1 || eq2)
0416 {
0417 return count_info(s1_s2 < c0 ? -1 : 1,
0418 longitudes_equal<units_t>(p + pi, (eq1 ? s1 : s2)));
0419 }
0420
0421
0422 if ( math::sign(s1_p) == math::sign(s1_s2)
0423 && math::abs(s1_p) < math::abs(s1_s2) )
0424 {
0425 return count_info(s1_s2 < c0 ? -2 : 2, false);
0426 }
0427
0428 calc_t const s1_p_anti = math::longitude_distance_signed<units_t>(s1, p + pi);
0429
0430
0431 if ( math::sign(s1_p_anti) == math::sign(s1_s2)
0432 && math::abs(s1_p_anti) < math::abs(s1_s2) )
0433 {
0434 return count_info(s1_s2 < c0 ? -2 : 2, true);
0435 }
0436
0437 return count_info(0, false);
0438 }
0439
0440
0441
0442
0443
0444
0445
0446
0447
0448
0449
0450
0451
0452
0453
0454
0455
0456
0457
0458
0459
0460
0461
0462
0463 template <typename Point, typename PointOfSegment>
0464 inline int side_equal(Point const& point,
0465 PointOfSegment const& se,
0466 count_info const& ci) const
0467 {
0468 using scoord_t = coordinate_type_t<PointOfSegment>;
0469 using units_t = typename geometry::detail::cs_angular_units<Point>::type;
0470
0471 if (math::equals(get<1>(point), get<1>(se)))
0472 {
0473 return 0;
0474 }
0475
0476
0477
0478 PointOfSegment ss1, ss2;
0479 set<1>(ss1, get<1>(se));
0480 set<0>(ss1, get<0>(se));
0481 set<1>(ss2, get<1>(se));
0482 scoord_t ss20 = get<0>(se);
0483 if (ci.count > 0)
0484 {
0485 ss20 += small_angle<scoord_t, units_t>();
0486 }
0487 else
0488 {
0489 ss20 -= small_angle<scoord_t, units_t>();
0490 }
0491 math::normalize_longitude<units_t>(ss20);
0492 set<0>(ss2, ss20);
0493
0494
0495 return m_side_strategy.apply(ss1, ss2, point);
0496 }
0497
0498
0499 template <typename CalcT, typename Units>
0500 static inline CalcT small_angle()
0501 {
0502 typedef math::detail::constants_on_spheroid<CalcT, Units> constants;
0503
0504 return constants::half_period() / CalcT(180);
0505 }
0506
0507 template <typename Units, typename CalcT>
0508 static inline bool longitudes_equal(CalcT const& lon1, CalcT const& lon2)
0509 {
0510 return math::equals(
0511 math::longitude_distance_signed<Units>(lon1, lon2),
0512 CalcT(0));
0513 }
0514
0515 SideStrategy m_side_strategy;
0516 };
0517
0518
0519 }
0520 #endif
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530
0531
0532
0533
0534
0535 template
0536 <
0537 typename Point = void,
0538 typename PointOfSegment = Point,
0539 typename CalculationType = void
0540 >
0541 class spherical_winding
0542 : public within::detail::spherical_winding_base
0543 <
0544 side::spherical_side_formula<CalculationType>,
0545 CalculationType
0546 >
0547 {};
0548
0549
0550 #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
0551
0552 namespace services
0553 {
0554
0555 template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
0556 struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, polygonal_tag, spherical_tag, spherical_tag>
0557 {
0558 typedef within::spherical_winding<> type;
0559 };
0560
0561 template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
0562 struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, linear_tag, spherical_tag, spherical_tag>
0563 {
0564 typedef within::spherical_winding<> type;
0565 };
0566
0567 }
0568
0569 #endif
0570
0571
0572 }}
0573
0574
0575 #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS
0576 namespace strategy { namespace covered_by { namespace services
0577 {
0578
0579 template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
0580 struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, polygonal_tag, spherical_tag, spherical_tag>
0581 {
0582 typedef within::spherical_winding<> type;
0583 };
0584
0585 template <typename PointLike, typename Geometry, typename AnyTag1, typename AnyTag2>
0586 struct default_strategy<PointLike, Geometry, AnyTag1, AnyTag2, pointlike_tag, linear_tag, spherical_tag, spherical_tag>
0587 {
0588 typedef within::spherical_winding<> type;
0589 };
0590
0591 }}}
0592 #endif
0593
0594
0595 }}
0596
0597
0598 #endif