topology_check.hpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2014-2023, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  4. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  5. // Use, modification and distribution is subject to the Boost Software License,
  6. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  7. // http://www.boost.org/LICENSE_1_0.txt)
  8. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TOPOLOGY_CHECK_HPP
  9. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TOPOLOGY_CHECK_HPP
  10. #include <boost/range/size.hpp>
  11. #include <boost/geometry/algorithms/detail/equals/point_point.hpp>
  12. #include <boost/geometry/algorithms/not_implemented.hpp>
  13. #include <boost/geometry/policies/compare.hpp>
  14. #include <boost/geometry/util/has_nan_coordinate.hpp>
  15. #include <boost/geometry/util/range.hpp>
  16. namespace boost { namespace geometry {
  17. #ifndef DOXYGEN_NO_DETAIL
  18. namespace detail { namespace relate {
  19. // TODO: change the name for e.g. something with the word "exterior"
  20. template
  21. <
  22. typename Geometry,
  23. typename Strategy,
  24. typename Tag = typename geometry::tag<Geometry>::type
  25. >
  26. struct topology_check
  27. : not_implemented<Tag>
  28. {};
  29. //template <typename Point, typename Strategy>
  30. //struct topology_check<Point, Strategy, point_tag>
  31. //{
  32. // static const char interior = '0';
  33. // static const char boundary = 'F';
  34. //
  35. // static const bool has_interior = true;
  36. // static const bool has_boundary = false;
  37. //
  38. // topology_check(Point const&) {}
  39. // template <typename IgnoreBoundaryPoint>
  40. // topology_check(Point const&, IgnoreBoundaryPoint const&) {}
  41. //};
  42. template <typename Linestring, typename Strategy>
  43. struct topology_check<Linestring, Strategy, linestring_tag>
  44. {
  45. static const char interior = '1';
  46. static const char boundary = '0';
  47. topology_check(Linestring const& ls, Strategy const& strategy)
  48. : m_ls(ls)
  49. , m_strategy(strategy)
  50. , m_is_initialized(false)
  51. {}
  52. bool has_interior() const
  53. {
  54. init();
  55. return m_has_interior;
  56. }
  57. bool has_boundary() const
  58. {
  59. init();
  60. return m_has_boundary;
  61. }
  62. /*template <typename Point>
  63. bool check_boundary_point(Point const& point) const
  64. {
  65. init();
  66. return m_has_boundary
  67. && ( equals::equals_point_point(point, range::front(m_ls))
  68. || equals::equals_point_point(point, range::back(m_ls)) );
  69. }*/
  70. template <typename Visitor>
  71. void for_each_boundary_point(Visitor & visitor) const
  72. {
  73. init();
  74. if (m_has_boundary)
  75. {
  76. if (visitor.apply(range::front(m_ls), m_strategy))
  77. visitor.apply(range::back(m_ls), m_strategy);
  78. }
  79. }
  80. private:
  81. void init() const
  82. {
  83. if (m_is_initialized)
  84. return;
  85. std::size_t count = boost::size(m_ls);
  86. m_has_interior = count > 0;
  87. // NOTE: Linestring with all points equal is treated as 1d linear ring
  88. m_has_boundary = count > 1
  89. && ! detail::equals::equals_point_point(range::front(m_ls),
  90. range::back(m_ls),
  91. m_strategy);
  92. m_is_initialized = true;
  93. }
  94. Linestring const& m_ls;
  95. Strategy const& m_strategy;
  96. mutable bool m_is_initialized;
  97. mutable bool m_has_interior;
  98. mutable bool m_has_boundary;
  99. };
  100. template <typename MultiLinestring, typename Strategy>
  101. struct topology_check<MultiLinestring, Strategy, multi_linestring_tag>
  102. {
  103. static const char interior = '1';
  104. static const char boundary = '0';
  105. topology_check(MultiLinestring const& mls, Strategy const& strategy)
  106. : m_mls(mls)
  107. , m_strategy(strategy)
  108. , m_is_initialized(false)
  109. {}
  110. bool has_interior() const
  111. {
  112. init();
  113. return m_has_interior;
  114. }
  115. bool has_boundary() const
  116. {
  117. init();
  118. return m_has_boundary;
  119. }
  120. template <typename Point>
  121. bool check_boundary_point(Point const& point) const
  122. {
  123. init();
  124. if (! m_has_boundary)
  125. return false;
  126. std::size_t count = count_equal(m_endpoints.begin(), m_endpoints.end(), point);
  127. return count % 2 != 0; // odd count -> boundary
  128. }
  129. template <typename Visitor>
  130. void for_each_boundary_point(Visitor & visitor) const
  131. {
  132. init();
  133. if (m_has_boundary)
  134. {
  135. for_each_boundary_point(m_endpoints.begin(), m_endpoints.end(), visitor);
  136. }
  137. }
  138. private:
  139. typedef geometry::less<void, -1, Strategy> less_type;
  140. void init() const
  141. {
  142. if (m_is_initialized)
  143. {
  144. return;
  145. }
  146. m_endpoints.reserve(boost::size(m_mls) * 2);
  147. m_has_interior = false;
  148. for (auto it = boost::begin(m_mls); it != boost::end(m_mls); ++it)
  149. {
  150. auto const& ls = *it;
  151. std::size_t count = boost::size(ls);
  152. if (count > 0)
  153. {
  154. m_has_interior = true;
  155. }
  156. if (count > 1)
  157. {
  158. auto const& front_pt = range::front(ls);
  159. auto const& back_pt = range::back(ls);
  160. // don't store boundaries of linear rings, this doesn't change anything
  161. if (! equals::equals_point_point(front_pt, back_pt, m_strategy))
  162. {
  163. // do not add points containing NaN coordinates
  164. // because they cannot be reasonably compared, e.g. with MSVC
  165. // an assertion failure is reported in std::equal_range()
  166. // NOTE: currently ignoring_counter calling std::equal_range()
  167. // is not used anywhere in the code, still it's safer this way
  168. if (! geometry::has_nan_coordinate(front_pt))
  169. {
  170. m_endpoints.push_back(front_pt);
  171. }
  172. if (! geometry::has_nan_coordinate(back_pt))
  173. {
  174. m_endpoints.push_back(back_pt);
  175. }
  176. }
  177. }
  178. }
  179. m_has_boundary = false;
  180. if (! m_endpoints.empty())
  181. {
  182. std::sort(m_endpoints.begin(), m_endpoints.end(), less_type());
  183. m_has_boundary = find_odd_count(m_endpoints.begin(), m_endpoints.end());
  184. }
  185. m_is_initialized = true;
  186. }
  187. template <typename It, typename Point>
  188. static inline std::size_t count_equal(It first, It last, Point const& point)
  189. {
  190. std::pair<It, It> rng = std::equal_range(first, last, point, less_type());
  191. return (std::size_t)std::distance(rng.first, rng.second);
  192. }
  193. template <typename It>
  194. inline bool find_odd_count(It first, It last) const
  195. {
  196. interrupting_visitor visitor;
  197. for_each_boundary_point(first, last, visitor);
  198. return visitor.found;
  199. }
  200. struct interrupting_visitor
  201. {
  202. bool found;
  203. interrupting_visitor() : found(false) {}
  204. template <typename Point>
  205. bool apply(Point const&, Strategy const&)
  206. {
  207. found = true;
  208. return false;
  209. }
  210. };
  211. template <typename It, typename Visitor>
  212. void for_each_boundary_point(It first, It last, Visitor& visitor) const
  213. {
  214. if ( first == last )
  215. return;
  216. std::size_t count = 1;
  217. It prev = first;
  218. ++first;
  219. for ( ; first != last ; ++first, ++prev )
  220. {
  221. // the end of the equal points subrange
  222. if ( ! equals::equals_point_point(*first, *prev, m_strategy) )
  223. {
  224. // odd count -> boundary
  225. if ( count % 2 != 0 )
  226. {
  227. if (! visitor.apply(*prev, m_strategy))
  228. {
  229. return;
  230. }
  231. }
  232. count = 1;
  233. }
  234. else
  235. {
  236. ++count;
  237. }
  238. }
  239. // odd count -> boundary
  240. if ( count % 2 != 0 )
  241. {
  242. visitor.apply(*prev, m_strategy);
  243. }
  244. }
  245. private:
  246. MultiLinestring const& m_mls;
  247. Strategy const& m_strategy;
  248. mutable bool m_is_initialized;
  249. mutable bool m_has_interior;
  250. mutable bool m_has_boundary;
  251. typedef typename geometry::point_type<MultiLinestring>::type point_type;
  252. mutable std::vector<point_type> m_endpoints;
  253. };
  254. struct topology_check_areal
  255. {
  256. static const char interior = '2';
  257. static const char boundary = '1';
  258. static bool has_interior() { return true; }
  259. static bool has_boundary() { return true; }
  260. };
  261. template <typename Ring, typename Strategy>
  262. struct topology_check<Ring, Strategy, ring_tag>
  263. : topology_check_areal
  264. {
  265. topology_check(Ring const&, Strategy const&) {}
  266. };
  267. template <typename Polygon, typename Strategy>
  268. struct topology_check<Polygon, Strategy, polygon_tag>
  269. : topology_check_areal
  270. {
  271. topology_check(Polygon const&, Strategy const&) {}
  272. };
  273. template <typename MultiPolygon, typename Strategy>
  274. struct topology_check<MultiPolygon, Strategy, multi_polygon_tag>
  275. : topology_check_areal
  276. {
  277. topology_check(MultiPolygon const&, Strategy const&) {}
  278. template <typename Point>
  279. static bool check_boundary_point(Point const& ) { return true; }
  280. };
  281. }} // namespace detail::relate
  282. #endif // DOXYGEN_NO_DETAIL
  283. }} // namespace boost::geometry
  284. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TOPOLOGY_CHECK_HPP