// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2014-2023, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, 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_ALGORITHMS_DETAIL_RELATE_TOPOLOGY_CHECK_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TOPOLOGY_CHECK_HPP #include #include #include #include #include #include namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace relate { // TODO: change the name for e.g. something with the word "exterior" template < typename Geometry, typename Strategy, typename Tag = typename geometry::tag::type > struct topology_check : not_implemented {}; //template //struct topology_check //{ // static const char interior = '0'; // static const char boundary = 'F'; // // static const bool has_interior = true; // static const bool has_boundary = false; // // topology_check(Point const&) {} // template // topology_check(Point const&, IgnoreBoundaryPoint const&) {} //}; template struct topology_check { static const char interior = '1'; static const char boundary = '0'; topology_check(Linestring const& ls, Strategy const& strategy) : m_ls(ls) , m_strategy(strategy) , m_is_initialized(false) {} bool has_interior() const { init(); return m_has_interior; } bool has_boundary() const { init(); return m_has_boundary; } /*template bool check_boundary_point(Point const& point) const { init(); return m_has_boundary && ( equals::equals_point_point(point, range::front(m_ls)) || equals::equals_point_point(point, range::back(m_ls)) ); }*/ template void for_each_boundary_point(Visitor & visitor) const { init(); if (m_has_boundary) { if (visitor.apply(range::front(m_ls), m_strategy)) visitor.apply(range::back(m_ls), m_strategy); } } private: void init() const { if (m_is_initialized) return; std::size_t count = boost::size(m_ls); m_has_interior = count > 0; // NOTE: Linestring with all points equal is treated as 1d linear ring m_has_boundary = count > 1 && ! detail::equals::equals_point_point(range::front(m_ls), range::back(m_ls), m_strategy); m_is_initialized = true; } Linestring const& m_ls; Strategy const& m_strategy; mutable bool m_is_initialized; mutable bool m_has_interior; mutable bool m_has_boundary; }; template struct topology_check { static const char interior = '1'; static const char boundary = '0'; topology_check(MultiLinestring const& mls, Strategy const& strategy) : m_mls(mls) , m_strategy(strategy) , m_is_initialized(false) {} bool has_interior() const { init(); return m_has_interior; } bool has_boundary() const { init(); return m_has_boundary; } template bool check_boundary_point(Point const& point) const { init(); if (! m_has_boundary) return false; std::size_t count = count_equal(m_endpoints.begin(), m_endpoints.end(), point); return count % 2 != 0; // odd count -> boundary } template void for_each_boundary_point(Visitor & visitor) const { init(); if (m_has_boundary) { for_each_boundary_point(m_endpoints.begin(), m_endpoints.end(), visitor); } } private: typedef geometry::less less_type; void init() const { if (m_is_initialized) { return; } m_endpoints.reserve(boost::size(m_mls) * 2); m_has_interior = false; for (auto it = boost::begin(m_mls); it != boost::end(m_mls); ++it) { auto const& ls = *it; std::size_t count = boost::size(ls); if (count > 0) { m_has_interior = true; } if (count > 1) { auto const& front_pt = range::front(ls); auto const& back_pt = range::back(ls); // don't store boundaries of linear rings, this doesn't change anything if (! equals::equals_point_point(front_pt, back_pt, m_strategy)) { // do not add points containing NaN coordinates // because they cannot be reasonably compared, e.g. with MSVC // an assertion failure is reported in std::equal_range() // NOTE: currently ignoring_counter calling std::equal_range() // is not used anywhere in the code, still it's safer this way if (! geometry::has_nan_coordinate(front_pt)) { m_endpoints.push_back(front_pt); } if (! geometry::has_nan_coordinate(back_pt)) { m_endpoints.push_back(back_pt); } } } } m_has_boundary = false; if (! m_endpoints.empty()) { std::sort(m_endpoints.begin(), m_endpoints.end(), less_type()); m_has_boundary = find_odd_count(m_endpoints.begin(), m_endpoints.end()); } m_is_initialized = true; } template static inline std::size_t count_equal(It first, It last, Point const& point) { std::pair rng = std::equal_range(first, last, point, less_type()); return (std::size_t)std::distance(rng.first, rng.second); } template inline bool find_odd_count(It first, It last) const { interrupting_visitor visitor; for_each_boundary_point(first, last, visitor); return visitor.found; } struct interrupting_visitor { bool found; interrupting_visitor() : found(false) {} template bool apply(Point const&, Strategy const&) { found = true; return false; } }; template void for_each_boundary_point(It first, It last, Visitor& visitor) const { if ( first == last ) return; std::size_t count = 1; It prev = first; ++first; for ( ; first != last ; ++first, ++prev ) { // the end of the equal points subrange if ( ! equals::equals_point_point(*first, *prev, m_strategy) ) { // odd count -> boundary if ( count % 2 != 0 ) { if (! visitor.apply(*prev, m_strategy)) { return; } } count = 1; } else { ++count; } } // odd count -> boundary if ( count % 2 != 0 ) { visitor.apply(*prev, m_strategy); } } private: MultiLinestring const& m_mls; Strategy const& m_strategy; mutable bool m_is_initialized; mutable bool m_has_interior; mutable bool m_has_boundary; typedef typename geometry::point_type::type point_type; mutable std::vector m_endpoints; }; struct topology_check_areal { static const char interior = '2'; static const char boundary = '1'; static bool has_interior() { return true; } static bool has_boundary() { return true; } }; template struct topology_check : topology_check_areal { topology_check(Ring const&, Strategy const&) {} }; template struct topology_check : topology_check_areal { topology_check(Polygon const&, Strategy const&) {} }; template struct topology_check : topology_check_areal { topology_check(MultiPolygon const&, Strategy const&) {} template static bool check_boundary_point(Point const& ) { return true; } }; }} // namespace detail::relate #endif // DOXYGEN_NO_DETAIL }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TOPOLOGY_CHECK_HPP