// Boost.Geometry // Copyright (c) 2021, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html #ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_CLOSEST_POINTS_CROSS_TRACK_HPP #define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_CLOSEST_POINTS_CROSS_TRACK_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK # include #endif namespace boost { namespace geometry { namespace strategy { namespace closest_points { template < typename CalculationType = void, typename Strategy = distance::comparable::haversine > class cross_track { public: template struct calculation_type : promote_floating_point < typename select_calculation_type < Point, PointOfSegment, CalculationType >::type > {}; using radius_type = typename Strategy::radius_type; cross_track() = default; explicit inline cross_track(typename Strategy::radius_type const& r) : m_strategy(r) {} inline cross_track(Strategy const& s) : m_strategy(s) {} template inline auto apply(Point const& p, PointOfSegment const& sp1, PointOfSegment const& sp2) const { using CT = typename calculation_type::type; // http://williams.best.vwh.net/avform.htm#XTE CT d3 = m_strategy.apply(sp1, sp2); if (geometry::math::equals(d3, 0.0)) { // "Degenerate" segment, return either d1 or d2 return sp1; } CT d1 = m_strategy.apply(sp1, p); CT d2 = m_strategy.apply(sp2, p); auto d_crs_pair = distance::detail::compute_cross_track_pair::apply( p, sp1, sp2); // d1, d2, d3 are in principle not needed, only the sign matters CT projection1 = cos(d_crs_pair.first) * d1 / d3; CT projection2 = cos(d_crs_pair.second) * d2 / d3; #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK std::cout << "Course " << dsv(sp1) << " to " << dsv(p) << " " << crs_AD * geometry::math::r2d() << std::endl; std::cout << "Course " << dsv(sp1) << " to " << dsv(sp2) << " " << crs_AB * geometry::math::r2d() << std::endl; std::cout << "Course " << dsv(sp2) << " to " << dsv(sp1) << " " << crs_BA * geometry::math::r2d() << std::endl; std::cout << "Course " << dsv(sp2) << " to " << dsv(p) << " " << crs_BD * geometry::math::r2d() << std::endl; std::cout << "Projection AD-AB " << projection1 << " : " << d_crs1 * geometry::math::r2d() << std::endl; std::cout << "Projection BD-BA " << projection2 << " : " << d_crs2 * geometry::math::r2d() << std::endl; std::cout << " d1: " << (d1 ) << " d2: " << (d2 ) << std::endl; #endif if (projection1 > 0.0 && projection2 > 0.0) { #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK CT XTD = radius() * geometry::math::abs( asin( sin( d1 ) * sin( d_crs1 ) )); std::cout << "Projection ON the segment" << std::endl; std::cout << "XTD: " << XTD << " d1: " << (d1 * radius()) << " d2: " << (d2 * radius()) << std::endl; #endif auto distance = distance::detail::compute_cross_track_distance::apply( d_crs_pair.first, d1); CT lon1 = geometry::get_as_radian<0>(sp1); CT lat1 = geometry::get_as_radian<1>(sp1); CT lon2 = geometry::get_as_radian<0>(sp2); CT lat2 = geometry::get_as_radian<1>(sp2); CT dist = CT(2) * asin(math::sqrt(distance)) * m_strategy.radius(); CT dist_d1 = CT(2) * asin(math::sqrt(d1)) * m_strategy.radius(); // Note: this is similar to spherical computation in geographic // point_segment_distance formula CT earth_radius = m_strategy.radius(); CT cos_frac = cos(dist_d1 / earth_radius) / cos(dist / earth_radius); CT s14_sph = cos_frac >= 1 ? CT(0) : cos_frac <= -1 ? math::pi() * earth_radius : acos(cos_frac) * earth_radius; CT a12 = geometry::formula::spherical_azimuth<>(lon1, lat1, lon2, lat2); auto res_direct = geometry::formula::spherical_direct < true, false >(lon1, lat1, s14_sph, a12, srs::sphere(earth_radius)); model::point < CT, dimension::value, typename coordinate_system::type > cp; geometry::set_from_radian<0>(cp, res_direct.lon2); geometry::set_from_radian<1>(cp, res_direct.lat2); return cp; } else { #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK std::cout << "Projection OUTSIDE the segment" << std::endl; #endif return d1 < d2 ? sp1 : sp2; } } template inline radius_type vertical_or_meridian(T1 lat1, T2 lat2) const { return m_strategy.radius() * (lat1 - lat2); } inline typename Strategy::radius_type radius() const { return m_strategy.radius(); } private : Strategy m_strategy; }; }} // namespace strategy::closest_points }} // namespace boost::geometry #endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_CLOSEST_POINTS_CROSS_TRACK_HPP