]>
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 | ||
6 | // This file was modified by Oracle on 2015, 2016. | |
7 | // Modifications copyright (c) 2015-2016 Oracle and/or its affiliates. | |
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" | |
22 | ||
23 | #include <boost/core/ignore_unused.hpp> | |
24 | #include <boost/range/algorithm/copy.hpp> | |
25 | ||
26 | #include <boost/geometry/algorithms/union.hpp> | |
27 | ||
28 | #include <boost/geometry/algorithms/area.hpp> | |
29 | #include <boost/geometry/algorithms/correct.hpp> | |
30 | #include <boost/geometry/algorithms/is_empty.hpp> | |
31 | #include <boost/geometry/algorithms/length.hpp> | |
32 | #include <boost/geometry/algorithms/num_points.hpp> | |
33 | #include <boost/geometry/algorithms/is_valid.hpp> | |
34 | ||
35 | #include <boost/geometry/geometries/geometries.hpp> | |
36 | ||
37 | #include <boost/geometry/strategies/strategies.hpp> | |
38 | ||
39 | #include <boost/geometry/io/wkt/wkt.hpp> | |
40 | ||
41 | ||
42 | #if defined(TEST_WITH_SVG) | |
43 | # include <boost/geometry/io/svg/svg_mapper.hpp> | |
44 | #endif | |
45 | ||
46 | struct ut_settings | |
47 | { | |
48 | double percentage; | |
49 | bool test_validity; | |
50 | ||
51 | ut_settings() | |
52 | : percentage(0.001) | |
53 | , test_validity(true) | |
54 | {} | |
55 | ||
56 | }; | |
57 | ||
58 | #if defined(BOOST_GEOMETRY_TEST_CHECK_VALID_INPUT) | |
59 | template <typename Geometry> | |
60 | inline void check_input_validity(std::string const& caseid, int case_index, | |
61 | Geometry const& geometry) | |
62 | { | |
63 | std::string message; | |
64 | if (!bg::is_valid(geometry, message)) | |
65 | { | |
66 | std::cout << caseid << " Input [" | |
67 | << case_index << "] not valid" << std::endl | |
68 | << " (" << message << ")" << std::endl; | |
69 | } | |
70 | } | |
71 | #endif | |
72 | ||
73 | template <typename OutputType, typename G1, typename G2> | |
74 | void test_union(std::string const& caseid, G1 const& g1, G2 const& g2, | |
75 | int expected_count, int expected_hole_count, | |
76 | int expected_point_count, double expected_area, | |
77 | ut_settings const& settings) | |
78 | { | |
79 | typedef typename bg::coordinate_type<G1>::type coordinate_type; | |
80 | boost::ignore_unused<coordinate_type>(); | |
81 | boost::ignore_unused(expected_point_count); | |
82 | ||
83 | // Declare output (vector of rings or multi_polygon) | |
84 | typedef typename setop_output_type<OutputType>::type result_type; | |
85 | result_type clip; | |
86 | ||
87 | #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) | |
88 | std::cout << "*** UNION " << caseid << std::endl; | |
89 | #endif | |
90 | ||
91 | #if defined(BOOST_GEOMETRY_TEST_CHECK_VALID_INPUT) | |
92 | check_input_validity(caseid, 0, g1); | |
93 | check_input_validity(caseid, 1, g2); | |
94 | #endif | |
95 | ||
96 | bg::union_(g1, g2, clip); | |
97 | ||
98 | typename bg::default_area_result<OutputType>::type area = 0; | |
99 | std::size_t n = 0; | |
100 | std::size_t holes = 0; | |
101 | for (typename result_type::iterator it = clip.begin(); | |
102 | it != clip.end(); ++it) | |
103 | { | |
104 | area += bg::area(*it); | |
105 | holes += bg::num_interior_rings(*it); | |
106 | n += bg::num_points(*it, true); | |
107 | ||
108 | if (settings.test_validity) | |
109 | { | |
110 | // Check validity (currently on separate clips only) | |
111 | // std::cout << bg::dsv(*it) << std::endl; | |
112 | std::string message; | |
113 | bool const valid = bg::is_valid(*it, message); | |
114 | BOOST_CHECK_MESSAGE(valid, | |
115 | "union: " << caseid << " not valid " << message); | |
116 | } | |
117 | } | |
118 | ||
119 | ||
120 | #if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE) | |
121 | { | |
122 | // Test inserter functionality | |
123 | // Test if inserter returns output-iterator (using Boost.Range copy) | |
124 | result_type inserted, array_with_one_empty_geometry; | |
125 | array_with_one_empty_geometry.push_back(OutputType()); | |
126 | boost::copy(array_with_one_empty_geometry, bg::detail::union_::union_insert<OutputType>(g1, g2, std::back_inserter(inserted))); | |
127 | ||
128 | typename bg::default_area_result<OutputType>::type area_inserted = 0; | |
129 | int index = 0; | |
130 | for (typename result_type::iterator it = inserted.begin(); | |
131 | it != inserted.end(); | |
132 | ++it, ++index) | |
133 | { | |
134 | // Skip the empty polygon created above to avoid the empty_input_exception | |
135 | if (! bg::is_empty(*it)) | |
136 | { | |
137 | area_inserted += bg::area(*it); | |
138 | } | |
139 | } | |
140 | BOOST_CHECK_EQUAL(boost::size(clip), boost::size(inserted) - 1); | |
141 | BOOST_CHECK_CLOSE(area_inserted, expected_area, settings.percentage); | |
142 | } | |
143 | #endif | |
144 | ||
145 | ||
146 | ||
147 | #if defined(BOOST_GEOMETRY_DEBUG_ROBUSTNESS) | |
148 | std::cout << "*** case: " << caseid | |
149 | << " area: " << area | |
150 | << " points: " << n | |
151 | << " polygons: " << boost::size(clip) | |
152 | << " holes: " << holes | |
153 | << std::endl; | |
154 | #endif | |
155 | ||
156 | BOOST_CHECK_MESSAGE(expected_count < 0 || int(clip.size()) == expected_count, | |
157 | "union: " << caseid | |
158 | << " #clips expected: " << expected_count | |
159 | << " detected: " << clip.size() | |
160 | << " type: " << (type_for_assert_message<G1, G2>()) | |
161 | ); | |
162 | ||
163 | BOOST_CHECK_MESSAGE(expected_hole_count < 0 || int(holes) == expected_hole_count, | |
164 | "union: " << caseid | |
165 | << " #holes expected: " << expected_hole_count | |
166 | << " detected: " << holes | |
167 | << " type: " << (type_for_assert_message<G1, G2>()) | |
168 | ); | |
169 | ||
170 | #if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS) | |
171 | BOOST_CHECK_MESSAGE(expected_point_count < 0 || std::abs(int(n) - expected_point_count) < 3, | |
172 | "union: " << caseid | |
173 | << " #points expected: " << expected_point_count | |
174 | << " detected: " << n | |
175 | << " type: " << (type_for_assert_message<G1, G2>()) | |
176 | ); | |
177 | #endif | |
178 | ||
179 | BOOST_CHECK_CLOSE(area, expected_area, settings.percentage); | |
180 | ||
181 | #if defined(TEST_WITH_SVG) | |
182 | { | |
183 | bool const ccw = | |
184 | bg::point_order<G1>::value == bg::counterclockwise | |
185 | || bg::point_order<G2>::value == bg::counterclockwise; | |
186 | bool const open = | |
187 | bg::closure<G1>::value == bg::open | |
188 | || bg::closure<G2>::value == bg::open; | |
189 | ||
190 | std::ostringstream filename; | |
191 | filename << "union_" | |
192 | << caseid << "_" | |
193 | << string_from_type<coordinate_type>::name() | |
194 | << (ccw ? "_ccw" : "") | |
195 | << (open ? "_open" : "") | |
196 | #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) | |
197 | << "_no_rob" | |
198 | #endif | |
199 | << ".svg"; | |
200 | ||
201 | std::ofstream svg(filename.str().c_str()); | |
202 | ||
203 | bg::svg_mapper | |
204 | < | |
205 | typename bg::point_type<G2>::type | |
206 | > mapper(svg, 500, 500); | |
207 | mapper.add(g1); | |
208 | mapper.add(g2); | |
209 | ||
210 | mapper.map(g1, "fill-opacity:0.5;fill:rgb(153,204,0);" | |
211 | "stroke:rgb(153,204,0);stroke-width:3"); | |
212 | mapper.map(g2, "fill-opacity:0.3;fill:rgb(51,51,153);" | |
213 | "stroke:rgb(51,51,153);stroke-width:3"); | |
214 | //mapper.map(g1, "opacity:0.6;fill:rgb(0,0,255);stroke:rgb(0,0,0);stroke-width:1"); | |
215 | //mapper.map(g2, "opacity:0.6;fill:rgb(0,255,0);stroke:rgb(0,0,0);stroke-width:1"); | |
216 | ||
217 | for (typename result_type::const_iterator it = clip.begin(); | |
218 | it != clip.end(); ++it) | |
219 | { | |
220 | mapper.map(*it, "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);" | |
221 | "stroke:rgb(255,0,255);stroke-width:8"); | |
222 | //mapper.map(*it, "opacity:0.6;fill:none;stroke:rgb(255,0,0);stroke-width:5"); | |
223 | } | |
224 | } | |
225 | #endif | |
226 | } | |
227 | ||
228 | template <typename OutputType, typename G1, typename G2> | |
229 | void test_one(std::string const& caseid, | |
230 | std::string const& wkt1, std::string const& wkt2, | |
231 | int expected_count, int expected_hole_count, | |
232 | int expected_point_count, double expected_area, | |
233 | ut_settings const& settings = ut_settings()) | |
234 | { | |
235 | G1 g1; | |
236 | bg::read_wkt(wkt1, g1); | |
237 | ||
238 | G2 g2; | |
239 | bg::read_wkt(wkt2, g2); | |
240 | ||
241 | // Reverse/close if necessary (e.g. G1/G2 are ccw and/or open) | |
242 | bg::correct(g1); | |
243 | bg::correct(g2); | |
244 | ||
245 | test_union<OutputType>(caseid, g1, g2, | |
246 | expected_count, expected_hole_count, expected_point_count, | |
247 | expected_area, settings); | |
248 | } | |
249 | ||
250 | #endif |