// Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test Helper // Copyright (c) 2010-2015 Barend Gehrels, Amsterdam, the Netherlands. // This file was modified by Oracle on 2016. // Modifications copyright (c) 2016, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_GEOMETRY_TEST_BUFFER_HPP #define BOOST_GEOMETRY_TEST_BUFFER_HPP #if defined(TEST_WITH_SVG) // Define before including any buffer headerfile #define BOOST_GEOMETRY_BUFFER_USE_HELPER_POINTS #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const double same_distance = -999; #if defined(TEST_WITH_SVG) # include # include #endif //----------------------------------------------------------------------------- template struct JoinTestProperties { static std::string name() { return "joinunknown"; } }; template<> struct JoinTestProperties { static std::string name() { return "round"; } }; template<> struct JoinTestProperties { static std::string name() { return "miter"; } }; template<> struct JoinTestProperties { static std::string name() { return "divide"; } }; //----------------------------------------------------------------------------- template struct EndTestProperties { }; template<> struct EndTestProperties { static std::string name() { return "round"; } }; template<> struct EndTestProperties { static std::string name() { return "flat"; } }; template std::size_t count_self_ips(Geometry const& geometry, RescalePolicy const& rescale_policy) { typedef typename bg::point_type::type point_type; typedef bg::detail::overlay::turn_info < point_type, typename bg::segment_ratio_type::type > turn_info; std::vector turns; bg::detail::self_get_turn_points::no_interrupt_policy policy; bg::self_turns < bg::detail::overlay::assign_null_policy >(geometry, rescale_policy, turns, policy); return turns.size(); } 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, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, PointStrategy const& point_strategy, bool check_self_intersections, int expected_count, int expected_holes_count, double expected_area, double tolerance, std::size_t* self_ip_count) { namespace bg = boost::geometry; typedef typename bg::coordinate_type::type coordinate_type; typedef typename bg::point_type::type point_type; typedef typename bg::tag::type tag; // TODO use something different here: std::string type = boost::is_same::value ? "poly" : boost::is_same::value ? "line" : boost::is_same::value ? "point" : boost::is_same::value ? "multipoly" : boost::is_same::value ? "multiline" : boost::is_same::value ? "multipoint" : "" ; bg::model::box envelope; if (bg::is_empty(geometry)) { bg::assign_values(envelope, 0, 0, 1, 1); } else { bg::envelope(geometry, envelope); } std::string join_name = JoinTestProperties::name(); std::string end_name = EndTestProperties::name(); if ( BOOST_GEOMETRY_CONDITION(( boost::is_same::value || boost::is_same::value )) ) { join_name.clear(); } std::ostringstream complete; complete << type << "_" << caseid << "_" << string_from_type::name() << "_" << join_name << (end_name.empty() ? "" : "_") << end_name << (distance_strategy.negative() ? "_deflate" : "") << (bg::point_order::value == bg::counterclockwise ? "_ccw" : "") // << "_" << point_buffer_count ; //std::cout << complete.str() << std::endl; #if defined(TEST_WITH_SVG_PER_TURN) save_turns_visitor visitor; #elif defined(TEST_WITH_SVG) buffer_svg_mapper buffer_mapper(complete.str()); std::ostringstream filename; filename << "buffer_" << complete.str() << ".svg"; std::ofstream svg(filename.str().c_str()); typedef bg::svg_mapper mapper_type; mapper_type mapper(svg, 1000, 800); svg_visitor > visitor(mapper); buffer_mapper.prepare(mapper, visitor, envelope, distance_strategy.negative() ? 1.0 : 1.1 * distance_strategy.max_distance(join_strategy, end_strategy) ); #else bg::detail::buffer::visit_pieces_default_policy visitor; #endif typedef typename bg::point_type::type point_type; typedef typename bg::rescale_policy_type::type rescale_policy_type; // Enlarge the box to get a proper rescale policy bg::buffer(envelope, envelope, distance_strategy.max_distance(join_strategy, end_strategy)); rescale_policy_type rescale_policy = bg::get_rescale_policy(envelope); bg::model::multi_polygon buffered; bg::detail::buffer::buffer_inserter(geometry, std::back_inserter(buffered), distance_strategy, side_strategy, join_strategy, end_strategy, point_strategy, rescale_policy, visitor); #if defined(TEST_WITH_SVG) buffer_mapper.map_input_output(mapper, geometry, buffered, distance_strategy.negative()); #endif typename bg::default_area_result::type area = bg::area(buffered); //Uncomment to create simple CSV to compare/use in tests - adapt precision if necessary //std::cout << complete.str() << "," << std::fixed << std::setprecision(0) << area << std::endl; //return; if (bg::is_empty(buffered) && bg::math::equals(expected_area, 0.0)) { // As expected - don't get rescale policy for output (will be invalid) return; } BOOST_CHECK_MESSAGE ( ! bg::is_empty(buffered), complete.str() << " output is empty (unexpected)." ); bg::model::box envelope_output; bg::assign_values(envelope_output, 0, 0, 1, 1); bg::envelope(buffered, envelope_output); rescale_policy_type rescale_policy_output = bg::get_rescale_policy(envelope_output); // std::cout << caseid << std::endl; // std::cout << "INPUT: " << bg::wkt(geometry) << std::endl; // std::cout << "OUTPUT: " << area << std::endl; // std::cout << "OUTPUT env: " << bg::wkt(envelope_output) << std::endl; // std::cout << bg::wkt(buffered) << std::endl; if (expected_count >= 0) { BOOST_CHECK_MESSAGE ( int(buffered.size()) == expected_count, "#outputs not as expected." << " Expected: " << expected_count << " Detected: " << buffered.size() ); } if (expected_holes_count >= 0) { std::size_t nholes = bg::num_interior_rings(buffered); BOOST_CHECK_MESSAGE ( int(nholes) == expected_holes_count, "#holes not as expected." << " Expected: " << expected_holes_count << " Detected: " << nholes ); } if (expected_area > -0.1) { double const difference = area - expected_area; BOOST_CHECK_MESSAGE ( bg::math::abs(difference) < tolerance, complete.str() << " not as expected. " << std::setprecision(18) << " Expected: " << expected_area << " Detected: " << area << " Diff: " << difference << std::setprecision(3) << " , " << 100.0 * (difference / expected_area) << "%" ); if (check_self_intersections) { try { bool has_self_ips = bg::detail::overlay::has_self_intersections( buffered, rescale_policy_output, false); // Be sure resulting polygon does not contain // self-intersections BOOST_CHECK_MESSAGE ( ! has_self_ips, complete.str() << " output is self-intersecting. " ); } catch(...) { BOOST_CHECK_MESSAGE ( false, "Exception in checking self-intersections" ); } } } #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_PER_TURN) { // Create a per turn visitor to map per turn, and buffer again with it per_turn_visitor ptv(complete.str(), visitor.get_points()); bg::detail::buffer::buffer_inserter(geometry, std::back_inserter(buffered), distance_strategy, side_strategy, join_strategy, end_strategy, point_strategy, rescale_policy, ptv); ptv.map_input_output(geometry, buffered, distance_strategy.negative()); // self_ips NYI here } #elif defined(TEST_WITH_SVG) buffer_mapper.map_self_ips(mapper, buffered, rescale_policy_output); #endif // Check for self-intersections if (self_ip_count != NULL) { std::size_t count = 0; if (bg::detail::overlay::has_self_intersections(buffered, rescale_policy_output, false)) { count = count_self_ips(buffered, rescale_policy_output); } *self_ip_count += count; if (count > 0) { std::cout << complete.str() << " " << count << std::endl; } } } 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, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, PointStrategy const& point_strategy, bool check_self_intersections, double expected_area, double tolerance, std::size_t* self_ip_count) { test_buffer(caseid, geometry, join_strategy, end_strategy, distance_strategy, side_strategy, point_strategy, check_self_intersections, -1, -1, expected_area, tolerance, self_ip_count); } #ifdef BOOST_GEOMETRY_CHECK_WITH_POSTGIS static int counter = 0; #endif template < typename Geometry, typename GeometryOut, typename JoinStrategy, typename EndStrategy > void test_one(std::string const& caseid, std::string const& wkt, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, int expected_count, int expected_holes_count, double expected_area, double distance_left, double distance_right = same_distance, bool check_self_intersections = true, double tolerance = 0.01) { namespace bg = boost::geometry; Geometry g; bg::read_wkt(wkt, g); bg::correct(g); #ifdef BOOST_GEOMETRY_CHECK_WITH_POSTGIS std::cout << (counter > 0 ? "union " : "") << "select " << counter++ << ", '" << caseid << "' as caseid" << ", ST_Area(ST_Buffer(ST_GeomFromText('" << wkt << "'), " << distance_left << ", 'endcap=" << end_name << " join=" << join_name << "'))" << ", " << expected_area << 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::type > distance_strategy(distance_left, bg::math::equals(distance_right, same_distance) ? distance_left : distance_right); test_buffer (caseid, g, join_strategy, end_strategy, distance_strategy, side_strategy, circle_strategy, check_self_intersections, expected_count, expected_holes_count, expected_area, 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, same_distance)) { bg::strategy::buffer::distance_symmetric < typename bg::coordinate_type::type > sym_distance_strategy(distance_left); test_buffer (caseid + "_sym", g, join_strategy, end_strategy, sym_distance_strategy, side_strategy, circle_strategy, check_self_intersections, expected_count, expected_holes_count, expected_area, tolerance, NULL); } #endif } template < typename Geometry, typename GeometryOut, typename JoinStrategy, typename EndStrategy > void test_one(std::string const& caseid, std::string const& wkt, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, double expected_area, double distance_left, double distance_right = same_distance, bool check_self_intersections = true, double tolerance = 0.01) { test_one(caseid, wkt, join_strategy, end_strategy, -1 ,-1, expected_area, distance_left, distance_right, check_self_intersections, tolerance); } // Version (currently for the Aimes test) counting self-ip's instead of checking template < typename Geometry, typename GeometryOut, typename JoinStrategy, typename EndStrategy > void test_one(std::string const& caseid, std::string const& wkt, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, double expected_area, double distance_left, double distance_right, std::size_t& self_ip_count, double tolerance = 0.01) { namespace bg = boost::geometry; Geometry g; bg::read_wkt(wkt, g); bg::correct(g); bg::strategy::buffer::distance_asymmetric < typename bg::coordinate_type::type > distance_strategy(distance_left, bg::math::equals(distance_right, same_distance) ? distance_left : distance_right); bg::strategy::buffer::point_circle circle_strategy(88); bg::strategy::buffer::side_straight side_strategy; test_buffer(caseid, g, join_strategy, end_strategy, distance_strategy, side_strategy, circle_strategy, false, expected_area, 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 (caseid, g, join_strategy, end_strategy, distance_strategy, side_strategy, point_strategy, true, expected_area, tolerance, NULL); } #endif