topology_check.hpp 9.4 KB

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