diff options
Diffstat (limited to 'libs/geometry/test/algorithms/buffer/test_buffer.hpp')
-rw-r--r-- | libs/geometry/test/algorithms/buffer/test_buffer.hpp | 319 |
1 files changed, 237 insertions, 82 deletions
diff --git a/libs/geometry/test/algorithms/buffer/test_buffer.hpp b/libs/geometry/test/algorithms/buffer/test_buffer.hpp index 248519c58..6b0d91105 100644 --- a/libs/geometry/test/algorithms/buffer/test_buffer.hpp +++ b/libs/geometry/test/algorithms/buffer/test_buffer.hpp @@ -16,8 +16,19 @@ #if defined(TEST_WITH_SVG) #define BOOST_GEOMETRY_BUFFER_USE_HELPER_POINTS + +// Uncomment next lines if you want to have a zoomed view +// #define BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX + +// If possible define box before including this unit with the right view +#ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX +# ifndef BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX +# define BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX "BOX(0 0,100 100)" +# endif #endif +#endif // TEST_WITH_SVG + #include <boost/foreach.hpp> #include <geometry_test_common.hpp> @@ -26,31 +37,45 @@ #include <boost/geometry/algorithms/area.hpp> #include <boost/geometry/algorithms/buffer.hpp> #include <boost/geometry/algorithms/correct.hpp> +#include <boost/geometry/algorithms/disjoint.hpp> +#include <boost/geometry/algorithms/intersects.hpp> +#include <boost/geometry/algorithms/is_valid.hpp> #include <boost/geometry/algorithms/union.hpp> #include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp> #include <boost/geometry/geometries/geometries.hpp> #include <boost/geometry/strategies/strategies.hpp> -#include <boost/geometry/algorithms/disjoint.hpp> -#include <boost/geometry/algorithms/intersects.hpp> -#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp> - -#include <boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp> - #include <boost/geometry/strategies/buffer.hpp> - - #include <boost/geometry/io/wkt/wkt.hpp> +#include <boost/geometry/util/condition.hpp> + #if defined(TEST_WITH_SVG) #include <boost/geometry/io/svg/svg_mapper.hpp> + +inline char piece_type_char(bg::strategy::buffer::piece_type const& type) +{ + using namespace bg::strategy::buffer; + switch(type) + { + case buffered_segment : return 's'; + case buffered_join : return 'j'; + case buffered_round_end : return 'r'; + case buffered_flat_end : return 'f'; + case buffered_point : return 'p'; + case buffered_concave : return 'c'; + default : return '?'; + } +} + template <typename Geometry, typename Mapper, typename RescalePolicy> void post_map(Geometry const& geometry, Mapper& mapper, RescalePolicy const& rescale_policy) { @@ -75,9 +100,13 @@ void post_map(Geometry const& geometry, Mapper& mapper, RescalePolicy const& res } } -template <typename SvgMapper, typename Tag> +template <typename SvgMapper, typename Box, typename Tag> struct svg_visitor { +#ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX + Box m_alternate_box; +#endif + class si { private : @@ -118,6 +147,13 @@ struct svg_visitor for (typename boost::range_iterator<Turns const>::type it = boost::begin(turns); it != boost::end(turns); ++it) { +#ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX + if (bg::disjoint(it->point, m_alternate_box)) + { + continue; + } +#endif + bool is_good = true; char color = 'g'; std::string fill = "fill:rgb(0,255,0);"; @@ -128,7 +164,7 @@ struct svg_visitor color = 'r'; is_good = false; break; - case bgdb::inside_original : + case bgdb::location_discard : fill = "fill:rgb(0,0,255);"; color = 'b'; is_good = false; @@ -226,6 +262,12 @@ struct svg_visitor { continue; } +#ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX + if (bg::disjoint(corner, m_alternate_box)) + { + continue; + } +#endif if (do_pieces) { @@ -242,13 +284,15 @@ struct svg_visitor typedef typename bg::point_type<ring_type>::type point_type; std::ostringstream out; - out << piece.index << "/" << int(piece.type) << "/" << piece.first_seg_id.segment_index << ".." << piece.last_segment_index - 1; - point_type label_point = corner.front(); - int const mid_offset = piece.offsetted_count / 2 - 1; - if (mid_offset >= 0 && mid_offset + 1 < corner.size()) + out << piece.index << " (" << piece_type_char(piece.type) << ") " << piece.first_seg_id.segment_index << ".." << piece.last_segment_index - 1; + point_type label_point = bg::return_centroid<point_type>(corner); + + if ((piece.type == bg::strategy::buffer::buffered_concave + || piece.type == bg::strategy::buffer::buffered_flat_end) + && corner.size() >= 2u) { - bg::set<0>(label_point, (bg::get<0>(corner[mid_offset]) + bg::get<0>(corner[mid_offset + 1])) / 2.0); - bg::set<1>(label_point, (bg::get<1>(corner[mid_offset]) + bg::get<1>(corner[mid_offset + 1])) / 2.0); + bg::set<0>(label_point, (bg::get<0>(corner[0]) + bg::get<0>(corner[1])) / 2.0); + bg::set<1>(label_point, (bg::get<1>(corner[0]) + bg::get<1>(corner[1])) / 2.0); } m_mapper.text(label_point, out.str(), "fill:rgb(255,0,0);font-family='Arial';font-size:10px;", 5, 5); } @@ -285,16 +329,21 @@ struct svg_visitor template <typename PieceCollection> inline void apply(PieceCollection const& collection, int phase) { + // Comment next return if you want to see pieces, turns, etc. + return; + if(phase == 0) { map_pieces(collection.m_pieces, collection.offsetted_rings, true, true); map_turns(collection.m_turns, true, false); } +#if !defined(BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX) if (phase == 1) { // map_traversed_rings(collection.traversed_rings); // map_offsetted_rings(collection.offsetted_rings); } +#endif } }; @@ -302,7 +351,10 @@ struct svg_visitor //----------------------------------------------------------------------------- template <typename JoinStrategy> -struct JoinTestProperties { }; +struct JoinTestProperties +{ + static std::string name() { return "joinunknown"; } +}; template<> struct JoinTestProperties<boost::geometry::strategy::buffer::join_round> { @@ -362,12 +414,18 @@ template typename GeometryOut, typename JoinStrategy, typename EndStrategy, + typename DistanceStrategy, + typename SideStrategy, + typename PointStrategy, typename Geometry > void test_buffer(std::string const& caseid, Geometry const& geometry, - JoinStrategy const& join_strategy, EndStrategy const& end_strategy, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + DistanceStrategy const& distance_strategy, + SideStrategy const& side_strategy, + PointStrategy const& point_strategy, bool check_self_intersections, double expected_area, - double distance_left, double distance_right, double tolerance, std::size_t* self_ip_count) { @@ -387,19 +445,15 @@ void test_buffer(std::string const& caseid, Geometry const& geometry, : "" ; - if (distance_right < -998) - { - distance_right = distance_left; - } - bg::model::box<point_type> envelope; bg::envelope(geometry, envelope); std::string join_name = JoinTestProperties<JoinStrategy>::name(); std::string end_name = EndTestProperties<EndStrategy>::name(); - if (boost::is_same<tag, bg::point_tag>::value - || boost::is_same<tag, bg::multi_point_tag>::value) + if ( BOOST_GEOMETRY_CONDITION(( + boost::is_same<tag, bg::point_tag>::value + || boost::is_same<tag, bg::multi_point_tag>::value )) ) { join_name.clear(); } @@ -411,7 +465,7 @@ void test_buffer(std::string const& caseid, Geometry const& geometry, << string_from_type<coordinate_type>::name() << "_" << join_name << (end_name.empty() ? "" : "_") << end_name - << (distance_left < 0 && distance_right < 0 ? "_deflate" : "") + << (distance_strategy.negative() ? "_deflate" : "") << (bg::point_order<GeometryOut>::value == bg::counterclockwise ? "_ccw" : "") // << "_" << point_buffer_count ; @@ -426,32 +480,36 @@ void test_buffer(std::string const& caseid, Geometry const& geometry, typedef bg::svg_mapper<point_type> mapper_type; mapper_type mapper(svg, 1000, 1000); - { - double d = std::abs(distance_left) + std::abs(distance_right); + svg_visitor<mapper_type, bg::model::box<point_type>, tag> visitor(mapper); + +#ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX + // Create a zoomed-in view + bg::model::box<point_type> alternate_box; + bg::read_wkt(BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX, alternate_box); + mapper.add(alternate_box); + // Take care non-visible elements are skipped + visitor.m_alternate_box = alternate_box; +#else + + { bg::model::box<point_type> box = envelope; - bg::buffer(box, box, d * (join_name == "miter" ? 2.0 : 1.1)); + if (distance_strategy.negative()) + { + bg::buffer(box, box, 1.0); + } + else + { + bg::buffer(box, box, 1.1 * distance_strategy.max_distance(join_strategy, end_strategy)); + } mapper.add(box); } +#endif - svg_visitor<mapper_type, tag> visitor(mapper); #else bg::detail::buffer::visit_pieces_default_policy visitor; #endif - bg::strategy::buffer::distance_asymmetric - < - coordinate_type - > - distance_strategy(distance_left, distance_right); - - bg::strategy::buffer::side_straight side_strategy; - - // For (multi)points a buffer with 88 points is used for testing. - // More points will give a more precise result - expected area should be - // adapted then - bg::strategy::buffer::point_circle circle_strategy(88); - typedef typename bg::point_type<Geometry>::type point_type; typedef typename bg::rescale_policy_type<point_type>::type rescale_policy_type; @@ -462,7 +520,7 @@ void test_buffer(std::string const& caseid, Geometry const& geometry, rescale_policy_type rescale_policy = bg::get_rescale_policy<rescale_policy_type>(envelope); - std::vector<GeometryOut> buffered; + bg::model::multi_polygon<GeometryOut> buffered; bg::detail::buffer::buffer_inserter<GeometryOut>(geometry, std::back_inserter(buffered), @@ -470,52 +528,57 @@ void test_buffer(std::string const& caseid, Geometry const& geometry, side_strategy, join_strategy, end_strategy, - circle_strategy, + point_strategy, rescale_policy, visitor); - typename bg::default_area_result<GeometryOut>::type area = 0; - BOOST_FOREACH(GeometryOut const& polygon, buffered) - { - area += bg::area(polygon); - } + typename bg::default_area_result<GeometryOut>::type area = bg::area(buffered); //std::cout << caseid << " " << distance_left << std::endl; //std::cout << "INPUT: " << bg::wkt(geometry) << std::endl; //std::cout << "OUTPUT: " << area << std::endl; - //BOOST_FOREACH(GeometryOut const& polygon, buffered) - //{ - // std::cout << bg::wkt(polygon) << std::endl; - //} + //std::cout << bg::wkt(buffered) << std::endl; if (expected_area > -0.1) { + double const difference = area - expected_area; BOOST_CHECK_MESSAGE ( - bg::math::abs(area - expected_area) < tolerance, + bg::math::abs(difference) < tolerance, complete.str() << " not as expected. " << std::setprecision(18) - << " Expected: " << expected_area - << " Detected: " << area + << " Expected: " << expected_area + << " Detected: " << area + << " Diff: " << difference + << std::setprecision(3) + << " , " << 100.0 * (difference / expected_area) << "%" ); if (check_self_intersections) { // Be sure resulting polygon does not contain // self-intersections - BOOST_FOREACH(GeometryOut const& polygon, buffered) - { - BOOST_CHECK_MESSAGE - ( - ! bg::detail::overlay::has_self_intersections(polygon, - rescale_policy, false), - complete.str() << " output is self-intersecting. " - ); - } + BOOST_CHECK_MESSAGE + ( + ! bg::detail::overlay::has_self_intersections(buffered, + rescale_policy, false), + complete.str() << " output is self-intersecting. " + ); } } +#ifdef BOOST_GEOMETRY_BUFFER_TEST_IS_VALID + if (! bg::is_valid(buffered)) + { + std::cout + << "NOT VALID: " << complete.str() << std::endl + << std::fixed << std::setprecision(16) << bg::wkt(buffered) << std::endl; + } +// BOOST_CHECK_MESSAGE(bg::is_valid(buffered) == true, complete.str() << " is not valid"); +// BOOST_CHECK_MESSAGE(bg::intersects(buffered) == false, complete.str() << " intersects"); +#endif + #if defined(TEST_WITH_SVG) bool const areal = boost::is_same < @@ -525,30 +588,46 @@ void test_buffer(std::string const& caseid, Geometry const& geometry, // Map input geometry in green if (areal) { - mapper.map(geometry, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,128,0);stroke-width:2"); +#ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX_FOR_INPUT + // Assuming input is areal + bg::model::multi_polygon<GeometryOut> clipped; + bg::intersection(geometry, alternate_box, clipped); + mapper.map(clipped, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,64,0);stroke-width:2"); +#else + mapper.map(geometry, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,64,0);stroke-width:2"); +#endif } else { + // TODO: clip input points/linestring mapper.map(geometry, "opacity:0.5;stroke:rgb(0,128,0);stroke-width:10"); } - BOOST_FOREACH(GeometryOut const& polygon, buffered) { - mapper.map(polygon, "opacity:0.4;fill:rgb(255,255,128);stroke:rgb(0,0,0);stroke-width:3"); - post_map(polygon, mapper, rescale_policy); - } + // Map buffer in yellow (inflate) and with orange-dots (deflate) + std::string style = distance_strategy.negative() + ? "opacity:0.4;fill:rgb(255,255,192);stroke:rgb(255,128,0);stroke-width:3" + : "opacity:0.4;fill:rgb(255,255,128);stroke:rgb(0,0,0);stroke-width:3"; + +#ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX + // Clip output multi-polygon with box + bg::model::multi_polygon<GeometryOut> clipped; + bg::intersection(buffered, alternate_box, clipped); + mapper.map(clipped, style); +#else + mapper.map(buffered, style); #endif + } + post_map(buffered, mapper, rescale_policy); +#endif // TEST_WITH_SVG if (self_ip_count != NULL) { std::size_t count = 0; - BOOST_FOREACH(GeometryOut const& polygon, buffered) + if (bg::detail::overlay::has_self_intersections(buffered, + rescale_policy, false)) { - if (bg::detail::overlay::has_self_intersections(polygon, - rescale_policy, false)) - { - count += count_self_ips(polygon, rescale_policy); - } + count = count_self_ips(buffered, rescale_policy); } *self_ip_count += count; @@ -596,10 +675,43 @@ void test_one(std::string const& caseid, std::string const& wkt, << std::endl; #endif + + bg::strategy::buffer::side_straight side_strategy; + bg::strategy::buffer::point_circle circle_strategy(88); + + bg::strategy::buffer::distance_asymmetric + < + typename bg::coordinate_type<Geometry>::type + > distance_strategy(distance_left, + distance_right > -998 ? distance_right : distance_left); + test_buffer<GeometryOut> - (caseid, g, join_strategy, end_strategy, + (caseid, g, + join_strategy, end_strategy, + distance_strategy, side_strategy, circle_strategy, check_self_intersections, expected_area, - distance_left, distance_right, tolerance, NULL); + tolerance, NULL); + +#if !defined(BOOST_GEOMETRY_COMPILER_MODE_DEBUG) && defined(BOOST_GEOMETRY_COMPILER_MODE_RELEASE) + + // Also test symmetric distance strategy if right-distance is not specified + // (only in release mode) + if (bg::math::equals(distance_right, -999)) + { + bg::strategy::buffer::distance_symmetric + < + typename bg::coordinate_type<Geometry>::type + > sym_distance_strategy(distance_left); + + test_buffer<GeometryOut> + (caseid + "_sym", g, + join_strategy, end_strategy, + sym_distance_strategy, side_strategy, circle_strategy, + check_self_intersections, expected_area, + tolerance, NULL); + + } +#endif } // Version (currently for the Aimes test) counting self-ip's instead of checking @@ -622,10 +734,53 @@ void test_one(std::string const& caseid, std::string const& wkt, bg::read_wkt(wkt, g); bg::correct(g); - test_buffer<GeometryOut>(caseid, g, join_strategy, end_strategy, + bg::strategy::buffer::distance_asymmetric + < + typename bg::coordinate_type<Geometry>::type + > distance_strategy(distance_left, + distance_right > -998 ? distance_right : distance_left); + + bg::strategy::buffer::point_circle circle_strategy(88); + bg::strategy::buffer::side_straight side_strategy; + test_buffer<GeometryOut>(caseid, g, + join_strategy, end_strategy, + distance_strategy, side_strategy, circle_strategy, false, expected_area, - distance_left, distance_right, tolerance, &self_ip_count); + tolerance, &self_ip_count); } +template +< + typename Geometry, + typename GeometryOut, + typename JoinStrategy, + typename EndStrategy, + typename DistanceStrategy, + typename SideStrategy, + typename PointStrategy +> +void test_with_custom_strategies(std::string const& caseid, + std::string const& wkt, + JoinStrategy const& join_strategy, + EndStrategy const& end_strategy, + DistanceStrategy const& distance_strategy, + SideStrategy const& side_strategy, + PointStrategy const& point_strategy, + double expected_area, + double tolerance = 0.01) +{ + namespace bg = boost::geometry; + Geometry g; + bg::read_wkt(wkt, g); + bg::correct(g); + + test_buffer<GeometryOut> + (caseid, g, + join_strategy, end_strategy, + distance_strategy, side_strategy, point_strategy, + true, expected_area, tolerance, NULL); +} + + #endif |