1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2014-2017, Oracle and/or its affiliates.
5 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
7 // Use, modification and distribution is subject to the Boost Software License,
8 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
9 // http://www.boost.org/LICENSE_1_0.txt)
11 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TOPOLOGY_CHECK_HPP
12 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TOPOLOGY_CHECK_HPP
15 #include <boost/geometry/algorithms/detail/equals/point_point.hpp>
17 #include <boost/geometry/policies/compare.hpp>
19 #include <boost/geometry/util/has_nan_coordinate.hpp>
20 #include <boost/geometry/util/range.hpp>
23 namespace boost { namespace geometry {
25 #ifndef DOXYGEN_NO_DETAIL
26 namespace detail { namespace relate {
28 // TODO: change the name for e.g. something with the word "exterior"
30 template <typename Geometry,
31 typename Tag = typename geometry::tag<Geometry>::type>
33 : not_implemented<Tag>
36 //template <typename Point>
37 //struct topology_check<Point, point_tag>
39 // static const char interior = '0';
40 // static const char boundary = 'F';
42 // static const bool has_interior = true;
43 // static const bool has_boundary = false;
45 // topology_check(Point const&) {}
46 // template <typename IgnoreBoundaryPoint>
47 // topology_check(Point const&, IgnoreBoundaryPoint const&) {}
50 template <typename Linestring>
51 struct topology_check<Linestring, linestring_tag>
53 static const char interior = '1';
54 static const char boundary = '0';
56 topology_check(Linestring const& ls)
58 , m_is_initialized(false)
61 bool has_interior() const
64 return m_has_interior;
67 bool has_boundary() const
70 return m_has_boundary;
73 /*template <typename Point>
74 bool check_boundary_point(Point const& point) const
78 && ( equals::equals_point_point(point, range::front(m_ls))
79 || equals::equals_point_point(point, range::back(m_ls)) );
82 template <typename Visitor>
83 void for_each_boundary_point(Visitor & visitor) const
88 if (visitor.apply(range::front(m_ls)))
89 visitor.apply(range::back(m_ls));
99 std::size_t count = boost::size(m_ls);
100 m_has_interior = count > 0;
101 // NOTE: Linestring with all points equal is treated as 1d linear ring
102 m_has_boundary = count > 1
103 && ! detail::equals::equals_point_point(range::front(m_ls), range::back(m_ls));
105 m_is_initialized = true;
108 Linestring const& m_ls;
109 mutable bool m_is_initialized;
111 mutable bool m_has_interior;
112 mutable bool m_has_boundary;
115 template <typename MultiLinestring>
116 struct topology_check<MultiLinestring, multi_linestring_tag>
118 static const char interior = '1';
119 static const char boundary = '0';
121 topology_check(MultiLinestring const& mls)
123 , m_is_initialized(false)
126 bool has_interior() const
129 return m_has_interior;
132 bool has_boundary() const
135 return m_has_boundary;
138 template <typename Point>
139 bool check_boundary_point(Point const& point) const
143 if (! m_has_boundary)
146 std::size_t count = count_equal(m_endpoints.begin(), m_endpoints.end(), point);
148 return count % 2 != 0; // odd count -> boundary
151 template <typename Visitor>
152 void for_each_boundary_point(Visitor & visitor) const
157 for_each_boundary_point(m_endpoints.begin(), m_endpoints.end(), visitor);
164 if (m_is_initialized)
167 m_endpoints.reserve(boost::size(m_mls) * 2);
169 m_has_interior = false;
171 typedef typename boost::range_iterator<MultiLinestring const>::type ls_iterator;
172 for ( ls_iterator it = boost::begin(m_mls) ; it != boost::end(m_mls) ; ++it )
174 typename boost::range_reference<MultiLinestring const>::type
177 std::size_t count = boost::size(ls);
181 m_has_interior = true;
186 typedef typename boost::range_reference
188 typename boost::range_value<MultiLinestring const>::type const
189 >::type point_reference;
191 point_reference front_pt = range::front(ls);
192 point_reference back_pt = range::back(ls);
194 // don't store boundaries of linear rings, this doesn't change anything
195 if (! equals::equals_point_point(front_pt, back_pt))
197 // do not add points containing NaN coordinates
198 // because they cannot be reasonably compared, e.g. with MSVC
199 // an assertion failure is reported in std::equal_range()
200 // NOTE: currently ignoring_counter calling std::equal_range()
201 // is not used anywhere in the code, still it's safer this way
202 if (! geometry::has_nan_coordinate(front_pt))
204 m_endpoints.push_back(front_pt);
206 if (! geometry::has_nan_coordinate(back_pt))
208 m_endpoints.push_back(back_pt);
214 m_has_boundary = false;
216 if (! m_endpoints.empty() )
218 std::sort(m_endpoints.begin(), m_endpoints.end(), geometry::less<>());
219 m_has_boundary = find_odd_count(m_endpoints.begin(), m_endpoints.end());
222 m_is_initialized = true;
225 template <typename It, typename Point>
226 static inline std::size_t count_equal(It first, It last, Point const& point)
228 std::pair<It, It> rng = std::equal_range(first, last, point, geometry::less<>());
229 return (std::size_t)std::distance(rng.first, rng.second);
232 template <typename It>
233 static inline bool find_odd_count(It first, It last)
235 interrupting_visitor visitor;
236 for_each_boundary_point(first, last, visitor);
237 return visitor.found;
240 struct interrupting_visitor
243 interrupting_visitor() : found(false) {}
244 template <typename Point>
245 bool apply(Point const&)
252 template <typename It, typename Visitor>
253 static void for_each_boundary_point(It first, It last, Visitor& visitor)
258 std::size_t count = 1;
261 for ( ; first != last ; ++first, ++prev )
263 // the end of the equal points subrange
264 if ( ! equals::equals_point_point(*first, *prev) )
266 // odd count -> boundary
267 if ( count % 2 != 0 )
269 if (! visitor.apply(*prev))
283 // odd count -> boundary
284 if ( count % 2 != 0 )
286 visitor.apply(*prev);
291 MultiLinestring const& m_mls;
292 mutable bool m_is_initialized;
294 mutable bool m_has_interior;
295 mutable bool m_has_boundary;
297 typedef typename geometry::point_type<MultiLinestring>::type point_type;
298 mutable std::vector<point_type> m_endpoints;
301 template <typename Ring>
302 struct topology_check<Ring, ring_tag>
304 static const char interior = '2';
305 static const char boundary = '1';
307 topology_check(Ring const&) {}
309 static bool has_interior() { return true; }
310 static bool has_boundary() { return true; }
313 template <typename Polygon>
314 struct topology_check<Polygon, polygon_tag>
316 static const char interior = '2';
317 static const char boundary = '1';
319 topology_check(Polygon const&) {}
321 static bool has_interior() { return true; }
322 static bool has_boundary() { return true; }
325 template <typename MultiPolygon>
326 struct topology_check<MultiPolygon, multi_polygon_tag>
328 static const char interior = '2';
329 static const char boundary = '1';
331 topology_check(MultiPolygon const&) {}
333 static bool has_interior() { return true; }
334 static bool has_boundary() { return true; }
335 template <typename Point>
336 static bool check_boundary_point(Point const& ) { return true; }
339 }} // namespace detail::relate
340 #endif // DOXYGEN_NO_DETAIL
342 }} // namespace boost::geometry
344 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TOPOLOGY_CHECK_HPP