1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 
3 // Copyright (c) 2016 Barend Gehrels, Amsterdam, the Netherlands.
4 
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 
9 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_AGGREGATE_OPERATIONS_HPP
10 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_AGGREGATE_OPERATIONS_HPP
11 
12 #include <set>
13 
14 #include <boost/geometry/algorithms/detail/overlay/sort_by_side.hpp>
15 
16 namespace boost { namespace geometry
17 {
18 
19 #ifndef DOXYGEN_NO_DETAIL
20 namespace detail { namespace overlay { namespace sort_by_side
21 {
22 
23 struct ring_with_direction
24 {
25     ring_identifier ring_id;
26     direction_type direction;
27 
28     std::size_t turn_index;
29     int operation_index;
30     operation_type operation;
31     signed_size_type region_id;
32     bool isolated;
33 
operator <boost::geometry::detail::overlay::sort_by_side::ring_with_direction34     inline bool operator<(ring_with_direction const& other) const
35     {
36         return this->ring_id != other.ring_id
37                 ? this->ring_id < other.ring_id
38                 : this->direction < other.direction;
39     }
40 
ring_with_directionboost::geometry::detail::overlay::sort_by_side::ring_with_direction41     ring_with_direction()
42         : direction(dir_unknown)
43         , turn_index(-1)
44         , operation_index(0)
45         , operation(operation_none)
46         , region_id(-1)
47         , isolated(false)
48     {}
49 };
50 
51 struct rank_with_rings
52 {
53     std::size_t rank;
54     std::set<ring_with_direction> rings;
55 
rank_with_ringsboost::geometry::detail::overlay::sort_by_side::rank_with_rings56     rank_with_rings()
57         : rank(0)
58     {
59     }
60 
all_equalboost::geometry::detail::overlay::sort_by_side::rank_with_rings61     inline bool all_equal(direction_type dir_type) const
62     {
63         for (std::set<ring_with_direction>::const_iterator it = rings.begin();
64              it != rings.end(); ++it)
65         {
66             if (it->direction != dir_type)
67             {
68                 return false;
69             }
70         }
71         return true;
72     }
73 
all_toboost::geometry::detail::overlay::sort_by_side::rank_with_rings74     inline bool all_to() const
75     {
76         return all_equal(sort_by_side::dir_to);
77     }
78 
all_fromboost::geometry::detail::overlay::sort_by_side::rank_with_rings79     inline bool all_from() const
80     {
81         return all_equal(sort_by_side::dir_from);
82     }
83 
has_onlyboost::geometry::detail::overlay::sort_by_side::rank_with_rings84     inline bool has_only(operation_type op) const
85     {
86         for (std::set<ring_with_direction>::const_iterator it = rings.begin();
87              it != rings.end(); ++it)
88         {
89             const ring_with_direction& rwd = *it;
90             if (rwd.operation != op)
91             {
92                 return false;
93             }
94         }
95         return true;
96     }
97 
98     //! Check if set has both op1 and op2, but no others
has_only_bothboost::geometry::detail::overlay::sort_by_side::rank_with_rings99     inline bool has_only_both(operation_type op1, operation_type op2) const
100     {
101         bool has1 = false;
102         bool has2 = false;
103         for (std::set<ring_with_direction>::const_iterator it = rings.begin();
104              it != rings.end(); ++it)
105         {
106             const ring_with_direction& rwd = *it;
107 
108             if (rwd.operation == op1) { has1 = true; }
109             else if (rwd.operation == op2) { has2 = true; }
110             else { return false; }
111         }
112         return has1 && has2;
113     }
114 
is_isolatedboost::geometry::detail::overlay::sort_by_side::rank_with_rings115     inline bool is_isolated() const
116     {
117         for (std::set<ring_with_direction>::const_iterator it = rings.begin();
118              it != rings.end(); ++it)
119         {
120             const ring_with_direction& rwd = *it;
121             if (! rwd.isolated)
122             {
123                 return false;
124             }
125         }
126         return true;
127     }
128 
has_unique_region_idboost::geometry::detail::overlay::sort_by_side::rank_with_rings129     inline bool has_unique_region_id() const
130     {
131         int region_id = -1;
132         for (std::set<ring_with_direction>::const_iterator it = rings.begin();
133              it != rings.end(); ++it)
134         {
135             const ring_with_direction& rwd = *it;
136             if (region_id == -1)
137             {
138                 region_id = rwd.region_id;
139             }
140             else if (rwd.region_id != region_id)
141             {
142                 return false;
143             }
144         }
145         return true;
146     }
147 
region_idboost::geometry::detail::overlay::sort_by_side::rank_with_rings148     inline int region_id() const
149     {
150         int region_id = -1;
151         for (std::set<ring_with_direction>::const_iterator it = rings.begin();
152              it != rings.end(); ++it)
153         {
154             const ring_with_direction& rwd = *it;
155             if (region_id == -1)
156             {
157                 region_id = rwd.region_id;
158             }
159             else if (rwd.region_id != region_id)
160             {
161                 return -1;
162             }
163         }
164         return region_id;
165     }
166 
167     template <typename Turns>
traversableboost::geometry::detail::overlay::sort_by_side::rank_with_rings168     inline bool traversable(Turns const& turns) const
169     {
170         typedef typename boost::range_value<Turns>::type turn_type;
171         typedef typename turn_type::turn_operation_type turn_operation_type;
172 
173         for (std::set<ring_with_direction>::const_iterator it = rings.begin();
174              it != rings.end(); ++it)
175         {
176             const ring_with_direction& rwd = *it;
177             turn_type const& turn = turns[rwd.turn_index];
178             turn_operation_type const& op = turn.operations[rwd.operation_index];
179 
180             // TODO: this is still necessary, but makes it order-dependent
181             // which should not be done.
182 
183             // This would obsolete the whole function and should be solved
184             // in a different way
185             if (op.visited.finalized() || op.visited.visited())
186             {
187                 return false;
188             }
189         }
190         return true;
191     }
192 
193 };
194 
195 template <typename Sbs, typename Turns>
aggregate_operations(Sbs const & sbs,std::vector<rank_with_rings> & aggregation,Turns const & turns,operation_type target_operation)196 inline void aggregate_operations(Sbs const& sbs, std::vector<rank_with_rings>& aggregation,
197                                  Turns const& turns,
198                                  operation_type target_operation)
199 {
200     typedef typename boost::range_value<Turns>::type turn_type;
201     typedef typename turn_type::turn_operation_type turn_operation_type;
202 
203     aggregation.clear();
204     for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++)
205     {
206         typename Sbs::rp const& ranked_point = sbs.m_ranked_points[i];
207 
208         turn_type const& turn = turns[ranked_point.turn_index];
209 
210         turn_operation_type const& op = turn.operations[ranked_point.operation_index];
211 
212         if (! ((target_operation == operation_union && ranked_point.rank == 0)
213                || op.operation == target_operation
214                || op.operation == operation_continue
215                || (op.operation == operation_blocked && ranked_point.direction == dir_from)))
216         {
217             // Always take rank 0 (because self-turns are blocked)
218             // Don't consider union/blocked (aggregate is only used for intersections)
219             // Blocked is allowed for from
220             continue;
221         }
222 
223         if (aggregation.empty() || aggregation.back().rank != ranked_point.rank)
224         {
225             rank_with_rings current;
226             current.rank = ranked_point.rank;
227             aggregation.push_back(current);
228         }
229 
230         ring_with_direction rwd;
231         segment_identifier const& sid = ranked_point.seg_id;
232 
233         rwd.ring_id = ring_identifier(sid.source_index, sid.multi_index, sid.ring_index);
234         rwd.direction = ranked_point.direction;
235         rwd.turn_index = ranked_point.turn_index;
236         rwd.operation_index = ranked_point.operation_index;
237         rwd.operation = op.operation;
238         rwd.region_id = op.enriched.region_id;
239         rwd.isolated = op.enriched.isolated;
240 
241         aggregation.back().rings.insert(rwd);
242     }
243 }
244 
245 
246 }}} // namespace detail::overlay::sort_by_side
247 #endif //DOXYGEN_NO_DETAIL
248 
249 
250 }} // namespace boost::geometry
251 
252 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_AGGREGATE_OPERATIONS_HPP
253