]>
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 | ||
1e59de90 TL |
6 | // This file was modified by Oracle on 2016-2021. |
7 | // Modifications copyright (c) 2016-2021, Oracle and/or its affiliates. | |
7c673cae FG |
8 | // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle |
9 | ||
10 | // Use, modification and distribution is subject to the Boost Software License, | |
11 | // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at | |
12 | // http://www.boost.org/LICENSE_1_0.txt) | |
13 | ||
14 | #ifndef BOOST_GEOMETRY_TEST_INTERSECTION_HPP | |
15 | #define BOOST_GEOMETRY_TEST_INTERSECTION_HPP | |
16 | ||
17 | #include <fstream> | |
18 | #include <iomanip> | |
19 | ||
1e59de90 | 20 | #include <boost/range/value_type.hpp> |
7c673cae FG |
21 | #include <boost/variant/variant.hpp> |
22 | ||
23 | #include <boost/geometry/algorithms/intersection.hpp> | |
24 | #include <boost/geometry/algorithms/area.hpp> | |
25 | #include <boost/geometry/algorithms/correct.hpp> | |
26 | #include <boost/geometry/algorithms/is_valid.hpp> | |
27 | #include <boost/geometry/algorithms/length.hpp> | |
28 | #include <boost/geometry/algorithms/num_points.hpp> | |
29 | #include <boost/geometry/algorithms/num_interior_rings.hpp> | |
30 | ||
31 | #include <boost/geometry/geometries/geometries.hpp> | |
32 | ||
7c673cae FG |
33 | #include <boost/geometry/io/wkt/wkt.hpp> |
34 | ||
1e59de90 | 35 | #include <boost/geometry/strategies/strategies.hpp> |
7c673cae FG |
36 | |
37 | #if defined(TEST_WITH_SVG) | |
38 | # include <boost/geometry/io/svg/svg_mapper.hpp> | |
39 | #endif | |
40 | ||
41 | #include <geometry_test_common.hpp> | |
20effc67 TL |
42 | #include <count_set.hpp> |
43 | #include <expectation_limits.hpp> | |
92f5a8d4 | 44 | #include <algorithms/check_validity.hpp> |
7c673cae FG |
45 | #include "../setop_output_type.hpp" |
46 | ||
20effc67 | 47 | struct ut_settings : ut_base_settings |
7c673cae FG |
48 | { |
49 | double percentage; | |
7c673cae FG |
50 | bool debug; |
51 | ||
52 | explicit ut_settings(double p = 0.0001, bool tv = true) | |
20effc67 TL |
53 | : ut_base_settings(tv) |
54 | , percentage(p) | |
7c673cae FG |
55 | , debug(false) |
56 | {} | |
57 | ||
58 | }; | |
59 | ||
92f5a8d4 TL |
60 | template<typename IntersectionOutput, typename G1, typename G2> |
61 | void check_result(IntersectionOutput const& intersection_output, | |
7c673cae | 62 | std::string const& caseid, |
92f5a8d4 | 63 | G1 const& g1, G2 const& g2, |
20effc67 TL |
64 | const count_set& expected_count, const count_set& expected_hole_count, |
65 | int expected_point_count, expectation_limits const& expected_length_or_area, | |
7c673cae FG |
66 | ut_settings const& settings) |
67 | { | |
68 | typedef typename boost::range_value<IntersectionOutput>::type OutputType; | |
69 | bool const is_line = bg::geometry_id<OutputType>::type::value == 2; | |
70 | ||
71 | typename bg::default_area_result<G1>::type length_or_area = 0; | |
72 | int n = 0; | |
73 | std::size_t nholes = 0; | |
1e59de90 | 74 | for (auto it = intersection_output.begin(); it != intersection_output.end(); ++it) |
7c673cae | 75 | { |
20effc67 | 76 | if (! expected_count.empty()) |
7c673cae FG |
77 | { |
78 | // here n should rather be of type std::size_t, but expected_point_count | |
79 | // is set to -1 in some test cases so type int was left for now | |
80 | n += static_cast<int>(bg::num_points(*it, true)); | |
81 | } | |
82 | ||
20effc67 | 83 | if (! expected_hole_count.empty()) |
7c673cae FG |
84 | { |
85 | nholes += bg::num_interior_rings(*it); | |
86 | } | |
87 | ||
88 | // instead of specialization we check it run-time here | |
89 | length_or_area += is_line | |
90 | ? bg::length(*it) | |
91 | : bg::area(*it); | |
92 | ||
93 | if (settings.debug) | |
94 | { | |
95 | std::cout << std::setprecision(20) << bg::wkt(*it) << std::endl; | |
96 | } | |
b32b8144 | 97 | } |
7c673cae | 98 | |
20effc67 | 99 | if (settings.test_validity()) |
b32b8144 FG |
100 | { |
101 | std::string message; | |
92f5a8d4 TL |
102 | bool const valid = check_validity<IntersectionOutput> |
103 | ::apply(intersection_output, caseid, g1, g2, message); | |
104 | ||
b32b8144 FG |
105 | BOOST_CHECK_MESSAGE(valid, |
106 | "intersection: " << caseid << " not valid: " << message | |
107 | << " type: " << (type_for_assert_message<G1, G2>())); | |
7c673cae FG |
108 | } |
109 | ||
110 | #if ! defined(BOOST_GEOMETRY_NO_BOOST_TEST) | |
92f5a8d4 TL |
111 | #if defined(BOOST_GEOMETRY_USE_RESCALING) |
112 | // Without rescaling, point count might easily differ (which is no problem) | |
7c673cae FG |
113 | if (expected_point_count > 0) |
114 | { | |
115 | BOOST_CHECK_MESSAGE(bg::math::abs(n - expected_point_count) < 3, | |
116 | "intersection: " << caseid | |
117 | << " #points expected: " << expected_point_count | |
118 | << " detected: " << n | |
119 | << " type: " << (type_for_assert_message<G1, G2>()) | |
120 | ); | |
121 | } | |
122 | #endif | |
123 | ||
20effc67 | 124 | if (! expected_count.empty()) |
7c673cae | 125 | { |
20effc67 TL |
126 | BOOST_CHECK_MESSAGE(expected_count.has(intersection_output.size()), |
127 | "intersection: " << caseid | |
128 | << " #outputs expected: " << expected_count | |
129 | << " detected: " << intersection_output.size() | |
130 | << " type: " << (type_for_assert_message<G1, G2>()) | |
131 | ); | |
7c673cae FG |
132 | } |
133 | ||
20effc67 | 134 | if (! expected_hole_count.empty()) |
7c673cae | 135 | { |
20effc67 | 136 | BOOST_CHECK_MESSAGE(expected_hole_count.has(nholes), |
7c673cae | 137 | "intersection: " << caseid |
20effc67 | 138 | << " #holes expected: " << expected_hole_count |
7c673cae FG |
139 | << " detected: " << nholes |
140 | << " type: " << (type_for_assert_message<G1, G2>()) | |
141 | ); | |
142 | } | |
143 | ||
20effc67 TL |
144 | BOOST_CHECK_MESSAGE(expected_length_or_area.contains(length_or_area, settings.percentage), |
145 | "intersection: " << caseid << std::setprecision(20) | |
146 | << " #area expected: " << expected_length_or_area | |
147 | << " detected: " << length_or_area | |
148 | << " type: " << (type_for_assert_message<G1, G2>())); | |
7c673cae FG |
149 | #endif |
150 | ||
7c673cae FG |
151 | } |
152 | ||
153 | ||
154 | template <typename OutputType, typename CalculationType, typename G1, typename G2> | |
155 | typename bg::default_area_result<G1>::type test_intersection(std::string const& caseid, | |
156 | G1 const& g1, G2 const& g2, | |
20effc67 TL |
157 | const count_set& expected_count = count_set(), |
158 | const count_set& expected_hole_count = count_set(), | |
159 | int expected_point_count = 0, expectation_limits const& expected_length_or_area = 0, | |
7c673cae FG |
160 | ut_settings const& settings = ut_settings()) |
161 | { | |
162 | if (settings.debug) | |
163 | { | |
164 | std::cout << std::endl << "case " << caseid << std::endl; | |
165 | } | |
166 | ||
7c673cae FG |
167 | typedef typename setop_output_type<OutputType>::type result_type; |
168 | ||
b32b8144 FG |
169 | typedef typename bg::point_type<G1>::type point_type; |
170 | boost::ignore_unused<point_type>(); | |
171 | ||
172 | #if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE) | |
7c673cae FG |
173 | if (! settings.debug) |
174 | { | |
175 | // Check _inserter behaviour with stratey | |
b32b8144 | 176 | typedef typename bg::strategy::intersection::services::default_strategy |
7c673cae | 177 | < |
b32b8144 FG |
178 | typename bg::cs_tag<point_type>::type |
179 | >::type strategy_type; | |
7c673cae | 180 | result_type clip; |
b32b8144 | 181 | bg::detail::intersection::intersection_insert<OutputType>(g1, g2, std::back_inserter(clip), strategy_type()); |
7c673cae | 182 | } |
b32b8144 | 183 | #endif |
7c673cae FG |
184 | |
185 | typename bg::default_area_result<G1>::type length_or_area = 0; | |
186 | ||
187 | // Check normal behaviour | |
188 | result_type intersection_output; | |
189 | bg::intersection(g1, g2, intersection_output); | |
190 | ||
92f5a8d4 | 191 | check_result(intersection_output, caseid, g1, g2, expected_count, |
20effc67 | 192 | expected_hole_count, expected_point_count, expected_length_or_area, |
7c673cae FG |
193 | settings); |
194 | ||
b32b8144 | 195 | #if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE) |
7c673cae FG |
196 | // Check variant behaviour |
197 | intersection_output.clear(); | |
198 | bg::intersection(boost::variant<G1>(g1), g2, intersection_output); | |
199 | ||
92f5a8d4 | 200 | check_result(intersection_output, caseid, g1, g2, expected_count, |
20effc67 | 201 | expected_hole_count, expected_point_count, expected_length_or_area, |
7c673cae FG |
202 | settings); |
203 | ||
204 | intersection_output.clear(); | |
205 | bg::intersection(g1, boost::variant<G2>(g2), intersection_output); | |
206 | ||
92f5a8d4 | 207 | check_result(intersection_output, caseid, g1, g2, expected_count, |
20effc67 | 208 | expected_hole_count, expected_point_count, expected_length_or_area, |
7c673cae FG |
209 | settings); |
210 | ||
211 | intersection_output.clear(); | |
212 | bg::intersection(boost::variant<G1>(g1), boost::variant<G2>(g2), intersection_output); | |
213 | ||
92f5a8d4 | 214 | check_result(intersection_output, caseid, g1, g2, expected_count, |
20effc67 | 215 | expected_hole_count, expected_point_count, expected_length_or_area, |
7c673cae | 216 | settings); |
b32b8144 | 217 | #endif |
7c673cae FG |
218 | |
219 | #if defined(TEST_WITH_SVG) | |
220 | { | |
221 | bool const is_line = bg::geometry_id<OutputType>::type::value == 2; | |
222 | typedef typename bg::coordinate_type<G1>::type coordinate_type; | |
223 | ||
224 | bool const ccw = | |
225 | bg::point_order<G1>::value == bg::counterclockwise | |
226 | || bg::point_order<G2>::value == bg::counterclockwise; | |
227 | bool const open = | |
228 | bg::closure<G1>::value == bg::open | |
229 | || bg::closure<G2>::value == bg::open; | |
230 | ||
231 | std::ostringstream filename; | |
232 | filename << "intersection_" | |
233 | << caseid << "_" | |
234 | << string_from_type<coordinate_type>::name() | |
235 | << string_from_type<CalculationType>::name() | |
236 | << (ccw ? "_ccw" : "") | |
237 | << (open ? "_open" : "") | |
92f5a8d4 TL |
238 | #if defined(BOOST_GEOMETRY_USE_RESCALING) |
239 | << "_rescaled" | |
7c673cae FG |
240 | #endif |
241 | << ".svg"; | |
242 | ||
243 | std::ofstream svg(filename.str().c_str()); | |
244 | ||
245 | bg::svg_mapper<point_type> mapper(svg, 500, 500); | |
246 | ||
247 | mapper.add(g1); | |
248 | mapper.add(g2); | |
249 | ||
250 | mapper.map(g1, is_line | |
251 | ? "opacity:0.6;stroke:rgb(0,255,0);stroke-width:5" | |
252 | : "fill-opacity:0.5;fill:rgb(153,204,0);" | |
253 | "stroke:rgb(153,204,0);stroke-width:3"); | |
254 | mapper.map(g2, "fill-opacity:0.3;fill:rgb(51,51,153);" | |
255 | "stroke:rgb(51,51,153);stroke-width:3"); | |
256 | ||
257 | for (typename result_type::const_iterator it = intersection_output.begin(); | |
258 | it != intersection_output.end(); ++it) | |
259 | { | |
260 | mapper.map(*it, "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);" | |
261 | "stroke:rgb(255,0,255);stroke-width:8"); | |
262 | } | |
263 | } | |
264 | #endif | |
265 | ||
266 | ||
267 | if (settings.debug) | |
268 | { | |
269 | std::cout << "end case " << caseid << std::endl; | |
270 | } | |
271 | ||
272 | return length_or_area; | |
273 | } | |
274 | ||
275 | template <typename OutputType, typename CalculationType, typename G1, typename G2> | |
276 | typename bg::default_area_result<G1>::type test_intersection(std::string const& caseid, | |
277 | G1 const& g1, G2 const& g2, | |
20effc67 TL |
278 | const count_set& expected_count = count_set(), int expected_point_count = 0, |
279 | expectation_limits const& expected_length_or_area = 0, | |
7c673cae FG |
280 | ut_settings const& settings = ut_settings()) |
281 | { | |
282 | return test_intersection<OutputType, CalculationType>( | |
20effc67 | 283 | caseid, g1, g2, expected_count, count_set(), expected_point_count, |
7c673cae FG |
284 | expected_length_or_area, settings |
285 | ); | |
286 | } | |
287 | ||
20effc67 | 288 | // Version with expected hole count |
7c673cae FG |
289 | template <typename OutputType, typename G1, typename G2> |
290 | typename bg::default_area_result<G1>::type test_one(std::string const& caseid, | |
291 | std::string const& wkt1, std::string const& wkt2, | |
20effc67 TL |
292 | const count_set& expected_count, |
293 | const count_set& expected_hole_count, | |
294 | int expected_point_count, | |
295 | expectation_limits const& expected_length_or_area, | |
7c673cae FG |
296 | ut_settings const& settings = ut_settings()) |
297 | { | |
298 | G1 g1; | |
299 | bg::read_wkt(wkt1, g1); | |
300 | ||
301 | G2 g2; | |
302 | bg::read_wkt(wkt2, g2); | |
303 | ||
304 | // Reverse if necessary | |
305 | bg::correct(g1); | |
306 | bg::correct(g2); | |
307 | ||
308 | return test_intersection<OutputType, void>(caseid, g1, g2, | |
20effc67 | 309 | expected_count, expected_hole_count, expected_point_count, |
7c673cae FG |
310 | expected_length_or_area, settings); |
311 | } | |
312 | ||
20effc67 | 313 | // Version without expected hole count |
7c673cae FG |
314 | template <typename OutputType, typename G1, typename G2> |
315 | typename bg::default_area_result<G1>::type test_one(std::string const& caseid, | |
316 | std::string const& wkt1, std::string const& wkt2, | |
20effc67 TL |
317 | const count_set& expected_count, |
318 | int expected_point_count, | |
319 | expectation_limits const& expected_length_or_area, | |
7c673cae FG |
320 | ut_settings const& settings = ut_settings()) |
321 | { | |
322 | return test_one<OutputType, G1, G2>(caseid, wkt1, wkt2, | |
20effc67 | 323 | expected_count, count_set(), expected_point_count, |
7c673cae FG |
324 | expected_length_or_area, |
325 | settings); | |
326 | } | |
327 | ||
328 | template <typename OutputType, typename Areal, typename Linear> | |
329 | void test_one_lp(std::string const& caseid, | |
330 | std::string const& wkt_areal, std::string const& wkt_linear, | |
20effc67 TL |
331 | const count_set& expected_count = count_set(), int expected_point_count = 0, |
332 | expectation_limits const& expected_length = 0, | |
7c673cae FG |
333 | ut_settings const& settings = ut_settings()) |
334 | { | |
335 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
336 | std::cout << caseid << " -- start" << std::endl; | |
337 | #endif | |
338 | Areal areal; | |
339 | bg::read_wkt(wkt_areal, areal); | |
340 | bg::correct(areal); | |
341 | ||
342 | Linear linear; | |
343 | bg::read_wkt(wkt_linear, linear); | |
344 | ||
345 | test_intersection<OutputType, void>(caseid, areal, linear, | |
346 | expected_count, expected_point_count, | |
347 | expected_length, settings); | |
348 | ||
349 | // A linestring reversed should deliver exactly the same. | |
350 | bg::reverse(linear); | |
351 | ||
352 | test_intersection<OutputType, void>(caseid + "_rev", areal, linear, | |
353 | expected_count, expected_point_count, | |
354 | expected_length, settings); | |
355 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
356 | std::cout << caseid << " -- end" << std::endl; | |
357 | #endif | |
358 | } | |
359 | ||
360 | template <typename Geometry1, typename Geometry2> | |
361 | void test_point_output(std::string const& wkt1, std::string const& wkt2, unsigned int expected_count) | |
362 | { | |
363 | Geometry1 g1; | |
364 | bg::read_wkt(wkt1, g1); | |
365 | bg::correct(g1); | |
366 | ||
367 | Geometry2 g2; | |
368 | bg::read_wkt(wkt2, g2); | |
369 | bg::correct(g2); | |
370 | ||
371 | bg::model::multi_point<typename bg::point_type<Geometry1>::type> points; | |
372 | bg::intersection(g1, g2, points); | |
373 | BOOST_CHECK_EQUAL(points.size(), expected_count); | |
374 | } | |
375 | ||
376 | ||
377 | #endif |