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