]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/geometry/test/algorithms/buffer/test_buffer.hpp
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / libs / geometry / test / algorithms / buffer / test_buffer.hpp
CommitLineData
7c673cae
FG
1// Boost.Geometry (aka GGL, Generic Geometry Library)
2// Unit Test Helper
3
4// Copyright (c) 2010-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
15#ifndef BOOST_GEOMETRY_TEST_BUFFER_HPP
16#define BOOST_GEOMETRY_TEST_BUFFER_HPP
17
18#if defined(TEST_WITH_SVG)
19 // Define before including any buffer headerfile
20 #define BOOST_GEOMETRY_BUFFER_USE_HELPER_POINTS
21#endif
22
23#include <iostream>
24#include <fstream>
25#include <iomanip>
26
27#include <boost/foreach.hpp>
28#include <geometry_test_common.hpp>
29
30#include <boost/geometry/algorithms/envelope.hpp>
31#include <boost/geometry/algorithms/area.hpp>
32#include <boost/geometry/algorithms/buffer.hpp>
33#include <boost/geometry/algorithms/correct.hpp>
34#include <boost/geometry/algorithms/disjoint.hpp>
35#include <boost/geometry/algorithms/intersects.hpp>
36#include <boost/geometry/algorithms/is_empty.hpp>
37#include <boost/geometry/algorithms/is_valid.hpp>
38#include <boost/geometry/algorithms/num_interior_rings.hpp>
39#include <boost/geometry/algorithms/union.hpp>
40
41#include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
42#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp>
43
44#include <boost/geometry/geometries/geometries.hpp>
45
46#include <boost/geometry/strategies/strategies.hpp>
47
48#include <boost/geometry/strategies/buffer.hpp>
49
50#include <boost/geometry/io/wkt/wkt.hpp>
51
52#include <boost/geometry/util/condition.hpp>
53
54const double same_distance = -999;
55
56#if defined(TEST_WITH_SVG)
57# include <test_buffer_svg.hpp>
58# include <test_buffer_svg_per_turn.hpp>
59#endif
60
61//-----------------------------------------------------------------------------
62template <typename JoinStrategy>
63struct JoinTestProperties
64{
65 static std::string name() { return "joinunknown"; }
66};
67
68template<> struct JoinTestProperties<boost::geometry::strategy::buffer::join_round>
69{
70 static std::string name() { return "round"; }
71};
72
73template<> struct JoinTestProperties<boost::geometry::strategy::buffer::join_miter>
74{
75 static std::string name() { return "miter"; }
76};
77
78template<> struct JoinTestProperties<boost::geometry::strategy::buffer::join_round_by_divide>
79{
80 static std::string name() { return "divide"; }
81};
82
83
84//-----------------------------------------------------------------------------
85template <typename EndStrategy>
86struct EndTestProperties { };
87
88template<> struct EndTestProperties<boost::geometry::strategy::buffer::end_round>
89{
90 static std::string name() { return "round"; }
91};
92
93template<> struct EndTestProperties<boost::geometry::strategy::buffer::end_flat>
94{
95 static std::string name() { return "flat"; }
96};
97
98
99
100template <typename Geometry, typename RescalePolicy>
101std::size_t count_self_ips(Geometry const& geometry, RescalePolicy const& rescale_policy)
102{
103 typedef typename bg::point_type<Geometry>::type point_type;
104 typedef bg::detail::overlay::turn_info
105 <
106 point_type,
107 typename bg::segment_ratio_type<point_type, RescalePolicy>::type
108 > turn_info;
109
110 std::vector<turn_info> turns;
111
112 bg::detail::self_get_turn_points::no_interrupt_policy policy;
113 bg::self_turns
114 <
115 bg::detail::overlay::assign_null_policy
116 >(geometry, rescale_policy, turns, policy);
117
118 return turns.size();
119}
120
121template
122<
123 typename GeometryOut,
124 typename JoinStrategy,
125 typename EndStrategy,
126 typename DistanceStrategy,
127 typename SideStrategy,
128 typename PointStrategy,
129 typename Geometry
130>
131void test_buffer(std::string const& caseid, Geometry const& geometry,
132 JoinStrategy const& join_strategy,
133 EndStrategy const& end_strategy,
134 DistanceStrategy const& distance_strategy,
135 SideStrategy const& side_strategy,
136 PointStrategy const& point_strategy,
137 bool check_self_intersections,
138 int expected_count,
139 int expected_holes_count,
140 double expected_area,
141 double tolerance,
142 std::size_t* self_ip_count)
143{
144 namespace bg = boost::geometry;
145
146 typedef typename bg::coordinate_type<Geometry>::type coordinate_type;
147 typedef typename bg::point_type<Geometry>::type point_type;
148
149 typedef typename bg::tag<Geometry>::type tag;
150 // TODO use something different here:
151 std::string type = boost::is_same<tag, bg::polygon_tag>::value ? "poly"
152 : boost::is_same<tag, bg::linestring_tag>::value ? "line"
153 : boost::is_same<tag, bg::point_tag>::value ? "point"
154 : boost::is_same<tag, bg::multi_polygon_tag>::value ? "multipoly"
155 : boost::is_same<tag, bg::multi_linestring_tag>::value ? "multiline"
156 : boost::is_same<tag, bg::multi_point_tag>::value ? "multipoint"
157 : ""
158 ;
159
160 bg::model::box<point_type> envelope;
161 if (bg::is_empty(geometry))
162 {
163 bg::assign_values(envelope, 0, 0, 1, 1);
164 }
165 else
166 {
167 bg::envelope(geometry, envelope);
168 }
169
170 std::string join_name = JoinTestProperties<JoinStrategy>::name();
171 std::string end_name = EndTestProperties<EndStrategy>::name();
172
173 if ( BOOST_GEOMETRY_CONDITION((
174 boost::is_same<tag, bg::point_tag>::value
175 || boost::is_same<tag, bg::multi_point_tag>::value )) )
176 {
177 join_name.clear();
178 }
179
180 std::ostringstream complete;
181 complete
182 << type << "_"
183 << caseid << "_"
184 << string_from_type<coordinate_type>::name()
185 << "_" << join_name
186 << (end_name.empty() ? "" : "_") << end_name
187 << (distance_strategy.negative() ? "_deflate" : "")
188 << (bg::point_order<GeometryOut>::value == bg::counterclockwise ? "_ccw" : "")
189 // << "_" << point_buffer_count
190 ;
191
192 //std::cout << complete.str() << std::endl;
193
194#if defined(TEST_WITH_SVG_PER_TURN)
195 save_turns_visitor<point_type> visitor;
196#elif defined(TEST_WITH_SVG)
197
198 buffer_svg_mapper<point_type> buffer_mapper(complete.str());
199
200 std::ostringstream filename;
201 filename << "buffer_" << complete.str() << ".svg";
202 std::ofstream svg(filename.str().c_str());
203 typedef bg::svg_mapper<point_type> mapper_type;
204 mapper_type mapper(svg, 1000, 800);
205
206 svg_visitor<mapper_type, bg::model::box<point_type> > visitor(mapper);
207
208 buffer_mapper.prepare(mapper, visitor, envelope,
209 distance_strategy.negative()
210 ? 1.0
211 : 1.1 * distance_strategy.max_distance(join_strategy, end_strategy)
212 );
213#else
214 bg::detail::buffer::visit_pieces_default_policy visitor;
215#endif
216
217 typedef typename bg::point_type<Geometry>::type point_type;
218 typedef typename bg::rescale_policy_type<point_type>::type
219 rescale_policy_type;
220
221 // Enlarge the box to get a proper rescale policy
222 bg::buffer(envelope, envelope, distance_strategy.max_distance(join_strategy, end_strategy));
223
224 rescale_policy_type rescale_policy
225 = bg::get_rescale_policy<rescale_policy_type>(envelope);
226
227 bg::model::multi_polygon<GeometryOut> buffered;
228
229 bg::detail::buffer::buffer_inserter<GeometryOut>(geometry,
230 std::back_inserter(buffered),
231 distance_strategy,
232 side_strategy,
233 join_strategy,
234 end_strategy,
235 point_strategy,
236 rescale_policy,
237 visitor);
238
239#if defined(TEST_WITH_SVG)
240 buffer_mapper.map_input_output(mapper, geometry, buffered, distance_strategy.negative());
241#endif
242
243
244 typename bg::default_area_result<GeometryOut>::type area = bg::area(buffered);
245
246 //Uncomment to create simple CSV to compare/use in tests - adapt precision if necessary
247 //std::cout << complete.str() << "," << std::fixed << std::setprecision(0) << area << std::endl;
248 //return;
249
250 if (bg::is_empty(buffered) && bg::math::equals(expected_area, 0.0))
251 {
252 // As expected - don't get rescale policy for output (will be invalid)
253 return;
254 }
255
256 BOOST_CHECK_MESSAGE
257 (
258 ! bg::is_empty(buffered),
259 complete.str() << " output is empty (unexpected)."
260 );
261
262 bg::model::box<point_type> envelope_output;
263 bg::assign_values(envelope_output, 0, 0, 1, 1);
264 bg::envelope(buffered, envelope_output);
265 rescale_policy_type rescale_policy_output
266 = bg::get_rescale_policy<rescale_policy_type>(envelope_output);
267
268 // std::cout << caseid << std::endl;
269 // std::cout << "INPUT: " << bg::wkt(geometry) << std::endl;
270 // std::cout << "OUTPUT: " << area << std::endl;
271 // std::cout << "OUTPUT env: " << bg::wkt(envelope_output) << std::endl;
272 // std::cout << bg::wkt(buffered) << std::endl;
273
274 if (expected_count >= 0)
275 {
276 BOOST_CHECK_MESSAGE
277 (
278 int(buffered.size()) == expected_count,
279 "#outputs not as expected."
280 << " Expected: " << expected_count
281 << " Detected: " << buffered.size()
282 );
283 }
284
285 if (expected_holes_count >= 0)
286 {
287 std::size_t nholes = bg::num_interior_rings(buffered);
288 BOOST_CHECK_MESSAGE
289 (
290 int(nholes) == expected_holes_count,
291 "#holes not as expected."
292 << " Expected: " << expected_holes_count
293 << " Detected: " << nholes
294 );
295 }
296
297 if (expected_area > -0.1)
298 {
299 double const difference = area - expected_area;
300 BOOST_CHECK_MESSAGE
301 (
302 bg::math::abs(difference) < tolerance,
303 complete.str() << " not as expected. "
304 << std::setprecision(18)
305 << " Expected: " << expected_area
306 << " Detected: " << area
307 << " Diff: " << difference
308 << std::setprecision(3)
309 << " , " << 100.0 * (difference / expected_area) << "%"
310 );
311
312 if (check_self_intersections)
313 {
314
315 try
316 {
317 bool has_self_ips = bg::detail::overlay::has_self_intersections(
318 buffered, rescale_policy_output, false);
319 // Be sure resulting polygon does not contain
320 // self-intersections
321 BOOST_CHECK_MESSAGE
322 (
323 ! has_self_ips,
324 complete.str() << " output is self-intersecting. "
325 );
326 }
327 catch(...)
328 {
329 BOOST_CHECK_MESSAGE
330 (
331 false,
332 "Exception in checking self-intersections"
333 );
334 }
335 }
336 }
337
338#ifdef BOOST_GEOMETRY_BUFFER_TEST_IS_VALID
339 if (! bg::is_valid(buffered))
340 {
341 std::cout
342 << "NOT VALID: " << complete.str() << std::endl
343 << std::fixed << std::setprecision(16) << bg::wkt(buffered) << std::endl;
344 }
345// BOOST_CHECK_MESSAGE(bg::is_valid(buffered) == true, complete.str() << " is not valid");
346// BOOST_CHECK_MESSAGE(bg::intersects(buffered) == false, complete.str() << " intersects");
347#endif
348
349#if defined(TEST_WITH_SVG_PER_TURN)
350 {
351 // Create a per turn visitor to map per turn, and buffer again with it
352 per_turn_visitor<point_type> ptv(complete.str(), visitor.get_points());
353 bg::detail::buffer::buffer_inserter<GeometryOut>(geometry,
354 std::back_inserter(buffered),
355 distance_strategy,
356 side_strategy,
357 join_strategy,
358 end_strategy,
359 point_strategy,
360 rescale_policy,
361 ptv);
362 ptv.map_input_output(geometry, buffered, distance_strategy.negative());
363 // self_ips NYI here
364 }
365#elif defined(TEST_WITH_SVG)
366 buffer_mapper.map_self_ips(mapper, buffered, rescale_policy_output);
367#endif
368
369 // Check for self-intersections
370 if (self_ip_count != NULL)
371 {
372 std::size_t count = 0;
373 if (bg::detail::overlay::has_self_intersections(buffered,
374 rescale_policy_output, false))
375 {
376 count = count_self_ips(buffered, rescale_policy_output);
377 }
378
379 *self_ip_count += count;
380 if (count > 0)
381 {
382 std::cout << complete.str() << " " << count << std::endl;
383 }
384 }
385}
386
387template
388<
389 typename GeometryOut,
390 typename JoinStrategy,
391 typename EndStrategy,
392 typename DistanceStrategy,
393 typename SideStrategy,
394 typename PointStrategy,
395 typename Geometry
396>
397void test_buffer(std::string const& caseid, Geometry const& geometry,
398 JoinStrategy const& join_strategy,
399 EndStrategy const& end_strategy,
400 DistanceStrategy const& distance_strategy,
401 SideStrategy const& side_strategy,
402 PointStrategy const& point_strategy,
403 bool check_self_intersections,
404 double expected_area,
405 double tolerance,
406 std::size_t* self_ip_count)
407{
408 test_buffer<GeometryOut>(caseid, geometry,
409 join_strategy, end_strategy, distance_strategy, side_strategy, point_strategy,
410 check_self_intersections, -1, -1, expected_area, tolerance, self_ip_count);
411}
412
413#ifdef BOOST_GEOMETRY_CHECK_WITH_POSTGIS
414static int counter = 0;
415#endif
416
417template
418<
419 typename Geometry,
420 typename GeometryOut,
421 typename JoinStrategy,
422 typename EndStrategy
423>
424void test_one(std::string const& caseid, std::string const& wkt,
425 JoinStrategy const& join_strategy, EndStrategy const& end_strategy,
426 int expected_count, int expected_holes_count, double expected_area,
427 double distance_left, double distance_right = same_distance,
428 bool check_self_intersections = true,
429 double tolerance = 0.01)
430{
431 namespace bg = boost::geometry;
432 Geometry g;
433 bg::read_wkt(wkt, g);
434 bg::correct(g);
435
436
437#ifdef BOOST_GEOMETRY_CHECK_WITH_POSTGIS
438 std::cout
439 << (counter > 0 ? "union " : "")
440 << "select " << counter++
441 << ", '" << caseid << "' as caseid"
442 << ", ST_Area(ST_Buffer(ST_GeomFromText('" << wkt << "'), "
443 << distance_left
444 << ", 'endcap=" << end_name << " join=" << join_name << "'))"
445 << ", " << expected_area
446 << std::endl;
447#endif
448
449
450 bg::strategy::buffer::side_straight side_strategy;
451 bg::strategy::buffer::point_circle circle_strategy(88);
452
453 bg::strategy::buffer::distance_asymmetric
454 <
455 typename bg::coordinate_type<Geometry>::type
456 > distance_strategy(distance_left,
457 bg::math::equals(distance_right, same_distance)
458 ? distance_left : distance_right);
459
460 test_buffer<GeometryOut>
461 (caseid, g,
462 join_strategy, end_strategy,
463 distance_strategy, side_strategy, circle_strategy,
464 check_self_intersections,
465 expected_count, expected_holes_count, expected_area,
466 tolerance, NULL);
467
468#if !defined(BOOST_GEOMETRY_COMPILER_MODE_DEBUG) && defined(BOOST_GEOMETRY_COMPILER_MODE_RELEASE)
469
470 // Also test symmetric distance strategy if right-distance is not specified
471 // (only in release mode)
472 if (bg::math::equals(distance_right, same_distance))
473 {
474 bg::strategy::buffer::distance_symmetric
475 <
476 typename bg::coordinate_type<Geometry>::type
477 > sym_distance_strategy(distance_left);
478
479 test_buffer<GeometryOut>
480 (caseid + "_sym", g,
481 join_strategy, end_strategy,
482 sym_distance_strategy, side_strategy, circle_strategy,
483 check_self_intersections,
484 expected_count, expected_holes_count, expected_area,
485 tolerance, NULL);
486
487 }
488#endif
489}
490
491template
492<
493 typename Geometry,
494 typename GeometryOut,
495 typename JoinStrategy,
496 typename EndStrategy
497>
498void test_one(std::string const& caseid, std::string const& wkt,
499 JoinStrategy const& join_strategy, EndStrategy const& end_strategy,
500 double expected_area,
501 double distance_left, double distance_right = same_distance,
502 bool check_self_intersections = true,
503 double tolerance = 0.01)
504{
505 test_one<Geometry, GeometryOut>(caseid, wkt, join_strategy, end_strategy,
506 -1 ,-1, expected_area,
507 distance_left, distance_right, check_self_intersections, tolerance);
508}
509
510// Version (currently for the Aimes test) counting self-ip's instead of checking
511template
512<
513 typename Geometry,
514 typename GeometryOut,
515 typename JoinStrategy,
516 typename EndStrategy
517>
518void test_one(std::string const& caseid, std::string const& wkt,
519 JoinStrategy const& join_strategy, EndStrategy const& end_strategy,
520 double expected_area,
521 double distance_left, double distance_right,
522 std::size_t& self_ip_count,
523 double tolerance = 0.01)
524{
525 namespace bg = boost::geometry;
526 Geometry g;
527 bg::read_wkt(wkt, g);
528 bg::correct(g);
529
530 bg::strategy::buffer::distance_asymmetric
531 <
532 typename bg::coordinate_type<Geometry>::type
533 > distance_strategy(distance_left,
534 bg::math::equals(distance_right, same_distance)
535 ? distance_left : distance_right);
536
537 bg::strategy::buffer::point_circle circle_strategy(88);
538 bg::strategy::buffer::side_straight side_strategy;
539 test_buffer<GeometryOut>(caseid, g,
540 join_strategy, end_strategy,
541 distance_strategy, side_strategy, circle_strategy,
542 false, expected_area,
543 tolerance, &self_ip_count);
544}
545
546template
547<
548 typename Geometry,
549 typename GeometryOut,
550 typename JoinStrategy,
551 typename EndStrategy,
552 typename DistanceStrategy,
553 typename SideStrategy,
554 typename PointStrategy
555>
556void test_with_custom_strategies(std::string const& caseid,
557 std::string const& wkt,
558 JoinStrategy const& join_strategy,
559 EndStrategy const& end_strategy,
560 DistanceStrategy const& distance_strategy,
561 SideStrategy const& side_strategy,
562 PointStrategy const& point_strategy,
563 double expected_area,
564 double tolerance = 0.01)
565{
566 namespace bg = boost::geometry;
567 Geometry g;
568 bg::read_wkt(wkt, g);
569 bg::correct(g);
570
571 test_buffer<GeometryOut>
572 (caseid, g,
573 join_strategy, end_strategy,
574 distance_strategy, side_strategy, point_strategy,
575 true, expected_area, tolerance, NULL);
576}
577
578
579
580#endif