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