1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
5 // This file was modified by Oracle on 2017.
6 // Modifications copyright (c) 2017 Oracle and/or its affiliates.
7 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
9 // Use, modification and distribution is subject to the Boost Software License,
10 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
11 // http://www.boost.org/LICENSE_1_0.txt)
13 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP
14 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP
16 #include <boost/range.hpp>
18 #include <boost/geometry/algorithms/area.hpp>
19 #include <boost/geometry/algorithms/envelope.hpp>
20 #include <boost/geometry/algorithms/expand.hpp>
21 #include <boost/geometry/algorithms/detail/partition.hpp>
22 #include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
23 #include <boost/geometry/algorithms/within.hpp>
25 #include <boost/geometry/geometries/box.hpp>
27 namespace boost { namespace geometry
31 #ifndef DOXYGEN_NO_DETAIL
32 namespace detail { namespace overlay
40 typename Geometry1, typename Geometry2,
41 typename RingCollection
43 static inline bool within_selected_input(Item const& item2, ring_identifier const& ring_id,
44 Geometry1 const& geometry1, Geometry2 const& geometry2,
45 RingCollection const& collection)
47 typedef typename geometry::tag<Geometry1>::type tag1;
48 typedef typename geometry::tag<Geometry2>::type tag2;
50 switch (ring_id.source_index)
53 return geometry::within(item2.point,
54 get_ring<tag1>::apply(ring_id, geometry1));
57 return geometry::within(item2.point,
58 get_ring<tag2>::apply(ring_id, geometry2));
61 return geometry::within(item2.point,
62 get_ring<void>::apply(ring_id, collection));
69 template <typename Point>
70 struct ring_info_helper
72 typedef typename geometry::default_area_result<Point>::type area_type;
77 model::box<Point> envelope;
79 inline ring_info_helper()
80 : real_area(0), abs_area(0)
83 inline ring_info_helper(ring_identifier i, area_type a)
84 : id(i), real_area(a), abs_area(geometry::math::abs(a))
89 struct ring_info_helper_get_box
91 template <typename Box, typename InputItem>
92 static inline void apply(Box& total, InputItem const& item)
94 geometry::expand(total, item.envelope);
98 struct ring_info_helper_ovelaps_box
100 template <typename Box, typename InputItem>
101 static inline bool apply(Box const& box, InputItem const& item)
103 return ! geometry::detail::disjoint::disjoint_box_box(box, item.envelope);
107 template <typename Geometry1, typename Geometry2, typename Collection, typename RingMap>
108 struct assign_visitor
110 typedef typename RingMap::mapped_type ring_info_type;
112 Geometry1 const& m_geometry1;
113 Geometry2 const& m_geometry2;
114 Collection const& m_collection;
116 bool m_check_for_orientation;
119 inline assign_visitor(Geometry1 const& g1, Geometry2 const& g2, Collection const& c,
120 RingMap& map, bool check)
125 , m_check_for_orientation(check)
128 template <typename Item>
129 inline void apply(Item const& outer, Item const& inner, bool first = true)
131 if (first && outer.abs_area < inner.abs_area)
133 // Apply with reversed arguments
134 apply(inner, outer, false);
138 if (m_check_for_orientation
139 || (math::larger(outer.real_area, 0)
140 && math::smaller(inner.real_area, 0)))
142 ring_info_type& inner_in_map = m_ring_map[inner.id];
144 if (geometry::within(inner_in_map.point, outer.envelope)
145 && within_selected_input(inner_in_map, outer.id, m_geometry1, m_geometry2, m_collection)
148 // Assign a parent if there was no earlier parent, or the newly
149 // found parent is smaller than the previous one
150 if (inner_in_map.parent.source_index == -1
151 || outer.abs_area < inner_in_map.parent_area)
153 inner_in_map.parent = outer.id;
154 inner_in_map.parent_area = outer.abs_area;
166 typename Geometry1, typename Geometry2,
167 typename RingCollection,
170 inline void assign_parents(Geometry1 const& geometry1,
171 Geometry2 const& geometry2,
172 RingCollection const& collection,
174 bool check_for_orientation = false)
176 typedef typename geometry::tag<Geometry1>::type tag1;
177 typedef typename geometry::tag<Geometry2>::type tag2;
179 typedef typename RingMap::mapped_type ring_info_type;
180 typedef typename ring_info_type::point_type point_type;
181 typedef model::box<point_type> box_type;
183 typedef typename RingMap::iterator map_iterator_type;
186 typedef ring_info_helper<point_type> helper;
187 typedef std::vector<helper> vector_type;
188 typedef typename boost::range_iterator<vector_type const>::type vector_iterator_type;
190 std::size_t count_total = ring_map.size();
191 std::size_t count_positive = 0;
192 std::size_t index_positive = 0; // only used if count_positive>0
193 std::size_t index = 0;
195 // Copy to vector (with new approach this might be obsolete as well, using the map directly)
196 vector_type vector(count_total);
198 for (map_iterator_type it = boost::begin(ring_map);
199 it != boost::end(ring_map); ++it, ++index)
201 vector[index] = helper(it->first, it->second.get_area());
202 helper& item = vector[index];
203 switch(it->first.source_index)
206 geometry::envelope(get_ring<tag1>::apply(it->first, geometry1),
210 geometry::envelope(get_ring<tag2>::apply(it->first, geometry2),
214 geometry::envelope(get_ring<void>::apply(it->first, collection),
218 if (item.real_area > 0)
221 index_positive = index;
225 if (! check_for_orientation)
227 if (count_positive == count_total)
229 // Optimization for only positive rings
230 // -> no assignment of parents or reversal necessary, ready here.
234 if (count_positive == 1)
236 // Optimization for one outer ring
237 // -> assign this as parent to all others (all interior rings)
238 // In unions, this is probably the most occuring case and gives
239 // a dramatic improvement (factor 5 for star_comb testcase)
240 ring_identifier id_of_positive = vector[index_positive].id;
241 ring_info_type& outer = ring_map[id_of_positive];
243 for (vector_iterator_type it = boost::begin(vector);
244 it != boost::end(vector); ++it, ++index)
246 if (index != index_positive)
248 ring_info_type& inner = ring_map[it->id];
249 inner.parent = id_of_positive;
250 outer.children.push_back(it->id);
259 Geometry1, Geometry2,
260 RingCollection, RingMap
261 > visitor(geometry1, geometry2, collection, ring_map, check_for_orientation);
266 >::apply(vector, visitor, ring_info_helper_get_box(),
267 ring_info_helper_ovelaps_box());
270 if (check_for_orientation)
272 for (map_iterator_type it = boost::begin(ring_map);
273 it != boost::end(ring_map); ++it)
275 if (geometry::math::equals(it->second.get_area(), 0))
277 it->second.discarded = true;
279 else if (it->second.parent.source_index >= 0
280 && math::larger(it->second.get_area(), 0))
282 const ring_info_type& parent = ring_map[it->second.parent];
284 if (math::larger(parent.area, 0))
286 // Discard positive inner ring with positive parent
287 it->second.discarded = true;
289 // Remove parent ID from any positive inner ring
290 it->second.parent.source_index = -1;
292 else if (it->second.parent.source_index < 0
293 && math::smaller(it->second.get_area(), 0))
295 // Reverse negative ring without parent
296 it->second.reversed = true;
302 for (map_iterator_type it = boost::begin(ring_map);
303 it != boost::end(ring_map); ++it)
305 if (it->second.parent.source_index >= 0)
307 ring_map[it->second.parent].children.push_back(it->first);
313 // Version for one geometry (called by buffer)
317 typename RingCollection,
320 inline void assign_parents(Geometry const& geometry,
321 RingCollection const& collection,
323 bool check_for_orientation)
325 // Call it with an empty geometry as second geometry (source_id == 1)
326 // (ring_map should be empty for source_id==1)
329 assign_parents(geometry, empty, collection, ring_map, check_for_orientation);
333 }} // namespace detail::overlay
334 #endif // DOXYGEN_NO_DETAIL
337 }} // namespace geometry
340 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP