]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // Boost.Geometry (aka GGL, Generic Geometry Library) |
2 | // Unit Test | |
3 | ||
4 | // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. | |
5 | ||
b32b8144 FG |
6 | // This file was modified by Oracle on 2015, 2016, 2017. |
7 | // Modifications copyright (c) 2015-2017 Oracle and/or its affiliates. | |
7c673cae FG |
8 | // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle |
9 | // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle | |
10 | ||
11 | // Use, modification and distribution is subject to the Boost Software License, | |
12 | // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | |
13 | // http://www.boost.org/LICENSE_1_0.txt) | |
14 | ||
15 | #ifndef BOOST_GEOMETRY_TEST_UNION_HPP | |
16 | #define BOOST_GEOMETRY_TEST_UNION_HPP | |
17 | ||
18 | #include <fstream> | |
19 | ||
20 | #include <geometry_test_common.hpp> | |
21 | #include "../setop_output_type.hpp" | |
b32b8144 | 22 | #include "../check_validity.hpp" |
7c673cae FG |
23 | |
24 | #include <boost/core/ignore_unused.hpp> | |
b32b8144 | 25 | #include <boost/foreach.hpp> |
7c673cae FG |
26 | #include <boost/range/algorithm/copy.hpp> |
27 | ||
28 | #include <boost/geometry/algorithms/union.hpp> | |
29 | ||
30 | #include <boost/geometry/algorithms/area.hpp> | |
31 | #include <boost/geometry/algorithms/correct.hpp> | |
32 | #include <boost/geometry/algorithms/is_empty.hpp> | |
33 | #include <boost/geometry/algorithms/length.hpp> | |
34 | #include <boost/geometry/algorithms/num_points.hpp> | |
35 | #include <boost/geometry/algorithms/is_valid.hpp> | |
36 | ||
37 | #include <boost/geometry/geometries/geometries.hpp> | |
38 | ||
39 | #include <boost/geometry/strategies/strategies.hpp> | |
40 | ||
41 | #include <boost/geometry/io/wkt/wkt.hpp> | |
42 | ||
43 | ||
44 | #if defined(TEST_WITH_SVG) | |
45 | # include <boost/geometry/io/svg/svg_mapper.hpp> | |
46 | #endif | |
47 | ||
48 | struct ut_settings | |
49 | { | |
50 | double percentage; | |
51 | bool test_validity; | |
52 | ||
53 | ut_settings() | |
54 | : percentage(0.001) | |
55 | , test_validity(true) | |
56 | {} | |
57 | ||
58 | }; | |
59 | ||
60 | #if defined(BOOST_GEOMETRY_TEST_CHECK_VALID_INPUT) | |
61 | template <typename Geometry> | |
62 | inline void check_input_validity(std::string const& caseid, int case_index, | |
63 | Geometry const& geometry) | |
64 | { | |
65 | std::string message; | |
66 | if (!bg::is_valid(geometry, message)) | |
67 | { | |
68 | std::cout << caseid << " Input [" | |
69 | << case_index << "] not valid" << std::endl | |
70 | << " (" << message << ")" << std::endl; | |
71 | } | |
72 | } | |
73 | #endif | |
74 | ||
b32b8144 FG |
75 | |
76 | ||
77 | template <typename Range> | |
78 | inline std::size_t num_points(Range const& rng, bool add_for_open = false) | |
79 | { | |
80 | std::size_t result = 0; | |
81 | for (typename boost::range_iterator<Range const>::type it = boost::begin(rng); | |
82 | it != boost::end(rng); ++it) | |
83 | { | |
84 | result += bg::num_points(*it, add_for_open); | |
85 | } | |
86 | return result; | |
87 | } | |
88 | ||
7c673cae FG |
89 | template <typename OutputType, typename G1, typename G2> |
90 | void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, | |
91 | int expected_count, int expected_hole_count, | |
92 | int expected_point_count, double expected_area, | |
93 | ut_settings const& settings) | |
94 | { | |
95 | typedef typename bg::coordinate_type<G1>::type coordinate_type; | |
96 | boost::ignore_unused<coordinate_type>(); | |
97 | boost::ignore_unused(expected_point_count); | |
98 | ||
99 | // Declare output (vector of rings or multi_polygon) | |
100 | typedef typename setop_output_type<OutputType>::type result_type; | |
101 | result_type clip; | |
102 | ||
103 | #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) | |
104 | std::cout << "*** UNION " << caseid << std::endl; | |
105 | #endif | |
106 | ||
107 | #if defined(BOOST_GEOMETRY_TEST_CHECK_VALID_INPUT) | |
108 | check_input_validity(caseid, 0, g1); | |
109 | check_input_validity(caseid, 1, g2); | |
110 | #endif | |
111 | ||
b32b8144 | 112 | // Check normal behaviour |
7c673cae FG |
113 | bg::union_(g1, g2, clip); |
114 | ||
b32b8144 FG |
115 | #if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE) |
116 | { | |
117 | // Check strategy passed explicitly | |
118 | result_type clip_s; | |
119 | typedef typename bg::strategy::intersection::services::default_strategy | |
120 | < | |
121 | typename bg::cs_tag<OutputType>::type | |
122 | >::type strategy_type; | |
123 | bg::union_(g1, g2, clip_s, strategy_type()); | |
124 | BOOST_CHECK_EQUAL(num_points(clip), num_points(clip_s)); | |
125 | } | |
126 | #endif | |
127 | ||
128 | if (settings.test_validity) | |
129 | { | |
130 | std::string message; | |
131 | bool const valid = check_validity<result_type>::apply(clip, message); | |
132 | BOOST_CHECK_MESSAGE(valid, | |
133 | "union: " << caseid << " not valid: " << message | |
134 | << " type: " << (type_for_assert_message<G1, G2>())); | |
135 | } | |
136 | ||
137 | ||
7c673cae FG |
138 | typename bg::default_area_result<OutputType>::type area = 0; |
139 | std::size_t n = 0; | |
140 | std::size_t holes = 0; | |
141 | for (typename result_type::iterator it = clip.begin(); | |
142 | it != clip.end(); ++it) | |
143 | { | |
144 | area += bg::area(*it); | |
145 | holes += bg::num_interior_rings(*it); | |
146 | n += bg::num_points(*it, true); | |
7c673cae FG |
147 | } |
148 | ||
149 | ||
150 | #if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE) | |
151 | { | |
152 | // Test inserter functionality | |
153 | // Test if inserter returns output-iterator (using Boost.Range copy) | |
154 | result_type inserted, array_with_one_empty_geometry; | |
155 | array_with_one_empty_geometry.push_back(OutputType()); | |
156 | boost::copy(array_with_one_empty_geometry, bg::detail::union_::union_insert<OutputType>(g1, g2, std::back_inserter(inserted))); | |
157 | ||
158 | typename bg::default_area_result<OutputType>::type area_inserted = 0; | |
159 | int index = 0; | |
160 | for (typename result_type::iterator it = inserted.begin(); | |
161 | it != inserted.end(); | |
162 | ++it, ++index) | |
163 | { | |
164 | // Skip the empty polygon created above to avoid the empty_input_exception | |
165 | if (! bg::is_empty(*it)) | |
166 | { | |
167 | area_inserted += bg::area(*it); | |
168 | } | |
169 | } | |
170 | BOOST_CHECK_EQUAL(boost::size(clip), boost::size(inserted) - 1); | |
171 | BOOST_CHECK_CLOSE(area_inserted, expected_area, settings.percentage); | |
172 | } | |
173 | #endif | |
174 | ||
175 | ||
176 | ||
177 | #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) | |
178 | std::cout << "*** case: " << caseid | |
179 | << " area: " << area | |
180 | << " points: " << n | |
181 | << " polygons: " << boost::size(clip) | |
182 | << " holes: " << holes | |
183 | << std::endl; | |
184 | #endif | |
185 | ||
186 | BOOST_CHECK_MESSAGE(expected_count < 0 || int(clip.size()) == expected_count, | |
187 | "union: " << caseid | |
188 | << " #clips expected: " << expected_count | |
189 | << " detected: " << clip.size() | |
190 | << " type: " << (type_for_assert_message<G1, G2>()) | |
191 | ); | |
192 | ||
193 | BOOST_CHECK_MESSAGE(expected_hole_count < 0 || int(holes) == expected_hole_count, | |
194 | "union: " << caseid | |
195 | << " #holes expected: " << expected_hole_count | |
196 | << " detected: " << holes | |
197 | << " type: " << (type_for_assert_message<G1, G2>()) | |
198 | ); | |
199 | ||
200 | #if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS) | |
201 | BOOST_CHECK_MESSAGE(expected_point_count < 0 || std::abs(int(n) - expected_point_count) < 3, | |
202 | "union: " << caseid | |
203 | << " #points expected: " << expected_point_count | |
204 | << " detected: " << n | |
205 | << " type: " << (type_for_assert_message<G1, G2>()) | |
206 | ); | |
207 | #endif | |
208 | ||
209 | BOOST_CHECK_CLOSE(area, expected_area, settings.percentage); | |
210 | ||
211 | #if defined(TEST_WITH_SVG) | |
212 | { | |
213 | bool const ccw = | |
214 | bg::point_order<G1>::value == bg::counterclockwise | |
215 | || bg::point_order<G2>::value == bg::counterclockwise; | |
216 | bool const open = | |
217 | bg::closure<G1>::value == bg::open | |
218 | || bg::closure<G2>::value == bg::open; | |
219 | ||
220 | std::ostringstream filename; | |
221 | filename << "union_" | |
222 | << caseid << "_" | |
223 | << string_from_type<coordinate_type>::name() | |
224 | << (ccw ? "_ccw" : "") | |
225 | << (open ? "_open" : "") | |
b32b8144 FG |
226 | #if defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS) |
227 | << "_self" | |
228 | #endif | |
7c673cae FG |
229 | #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) |
230 | << "_no_rob" | |
231 | #endif | |
232 | << ".svg"; | |
233 | ||
234 | std::ofstream svg(filename.str().c_str()); | |
235 | ||
236 | bg::svg_mapper | |
237 | < | |
238 | typename bg::point_type<G2>::type | |
239 | > mapper(svg, 500, 500); | |
240 | mapper.add(g1); | |
241 | mapper.add(g2); | |
242 | ||
243 | mapper.map(g1, "fill-opacity:0.5;fill:rgb(153,204,0);" | |
244 | "stroke:rgb(153,204,0);stroke-width:3"); | |
245 | mapper.map(g2, "fill-opacity:0.3;fill:rgb(51,51,153);" | |
246 | "stroke:rgb(51,51,153);stroke-width:3"); | |
247 | //mapper.map(g1, "opacity:0.6;fill:rgb(0,0,255);stroke:rgb(0,0,0);stroke-width:1"); | |
248 | //mapper.map(g2, "opacity:0.6;fill:rgb(0,255,0);stroke:rgb(0,0,0);stroke-width:1"); | |
249 | ||
250 | for (typename result_type::const_iterator it = clip.begin(); | |
251 | it != clip.end(); ++it) | |
252 | { | |
253 | mapper.map(*it, "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);" | |
254 | "stroke:rgb(255,0,255);stroke-width:8"); | |
255 | //mapper.map(*it, "opacity:0.6;fill:none;stroke:rgb(255,0,0);stroke-width:5"); | |
256 | } | |
257 | } | |
258 | #endif | |
259 | } | |
260 | ||
261 | template <typename OutputType, typename G1, typename G2> | |
262 | void test_one(std::string const& caseid, | |
263 | std::string const& wkt1, std::string const& wkt2, | |
264 | int expected_count, int expected_hole_count, | |
265 | int expected_point_count, double expected_area, | |
266 | ut_settings const& settings = ut_settings()) | |
267 | { | |
268 | G1 g1; | |
269 | bg::read_wkt(wkt1, g1); | |
270 | ||
271 | G2 g2; | |
272 | bg::read_wkt(wkt2, g2); | |
273 | ||
274 | // Reverse/close if necessary (e.g. G1/G2 are ccw and/or open) | |
275 | bg::correct(g1); | |
276 | bg::correct(g2); | |
277 | ||
278 | test_union<OutputType>(caseid, g1, g2, | |
279 | expected_count, expected_hole_count, expected_point_count, | |
280 | expected_area, settings); | |
281 | } | |
282 | ||
283 | #endif |