// Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test // Copyright (c) 2010-2012 Barend Gehrels, Amsterdam, the Netherlands. // 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 //#define BOOST_GEOMETRY_DEBUG_WITH_MAPPER #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(TEST_WITH_SVG) # include #endif #if defined(TEST_WITH_SVG) #include template void post_map(Geometry const& geometry, Mapper& mapper, 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); BOOST_FOREACH(turn_info const& turn, turns) { mapper.map(turn.point, "fill:rgb(255,128,0);stroke:rgb(0,0,100);stroke-width:1", 3); } } #endif //----------------------------------------------------------------------------- template class JoinStrategy> struct JoinTestProperties { }; template<> struct JoinTestProperties { static std::string name() { return "round"; } static double tolerance() { return 0.1; } }; template<> struct JoinTestProperties { static std::string name() { return "miter"; } static double tolerance() { return 0.001; } }; template<> struct JoinTestProperties { static std::string name() { return "divide"; } static double tolerance() { return 0.1; } }; //----------------------------------------------------------------------------- template class EndStrategy> struct EndTestProperties { }; template<> struct EndTestProperties { static std::string name() { return "round"; } static double tolerance() { return 0.1; } }; template<> struct EndTestProperties { static std::string name() { return "flat"; } static double tolerance() { return 0.001; } }; template<> struct EndTestProperties { static std::string name() { return ""; } static double tolerance() { return 0.001; } }; template < typename GeometryOut, template class JoinStrategy, template class EndStrategy, typename Geometry > void test_buffer(std::string const& caseid, Geometry const& geometry, bool /*check*/, double expected_area, double distance_left, double distance_right) { namespace bg = boost::geometry; typedef typename bg::coordinate_type::type coordinate_type; typedef typename bg::point_type::type point_type; typedef bg::strategy::buffer::distance_asymmetric distance; typedef typename bg::ring_type::type ring_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" : "" ; typedef typename bg::point_type::type output_point_type; if (distance_right < -998) { distance_right = distance_left; } std::string join_name = JoinTestProperties::name(); std::string end_name = EndTestProperties::name(); if (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_left < 0 && distance_right < 0 ? "_deflate" : "") // << "_" << point_buffer_count ; //std::cout << complete.str() << std::endl; std::ostringstream filename; filename << "buffer_" << complete.str() << ".svg"; #if defined(TEST_WITH_SVG) std::ofstream svg(filename.str().c_str()); bg::svg_mapper mapper(svg, 1000, 1000); { bg::model::box box; bg::envelope(geometry, box); double d = std::abs(distance_left) + std::abs(distance_right); bg::buffer(box, box, d * (join_name == "miter" ? 2.0 : 1.1)); mapper.add(box); } #endif JoinStrategy < point_type, output_point_type > join_strategy; EndStrategy < point_type, output_point_type > end_strategy; bg::strategy::buffer::distance_asymmetric < coordinate_type > distance_strategy(distance_left, distance_right); typedef typename bg::point_type::type point_type; typedef typename bg::rescale_policy_type::type rescale_policy_type; rescale_policy_type rescale_policy = bg::get_rescale_policy(geometry); std::vector buffered; bg::buffer_inserter(geometry, std::back_inserter(buffered), distance_strategy, join_strategy, end_strategy, rescale_policy #ifdef BOOST_GEOMETRY_DEBUG_WITH_MAPPER , mapper #endif ); //// Remove duplicate point (this step should go automatically in the end) //BOOST_FOREACH(GeometryOut& polygon, buffered) //{ // bg::unique(polygon); //} typename bg::default_area_result::type area = 0; BOOST_FOREACH(GeometryOut const& polygon, buffered) { area += bg::area(polygon); } //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; //} if (expected_area > -0.1) { double tol = JoinTestProperties::tolerance() + EndTestProperties::tolerance(); if (expected_area < 1.0e-5) { tol /= 1.0e6; } typename bg::default_area_result::type tolerance = tol; BOOST_CHECK_MESSAGE ( bg::math::abs(area - expected_area) < tolerance, complete.str() << " not as expected. " << std::setprecision(18) << " Expected: " << expected_area << " Detected: " << area ); // 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. " ); } } #if defined(TEST_WITH_SVG) // Map input geometry in green mapper.map(geometry, "opacity:0.5;fill:rgb(0,128,0);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"); //mapper.map(polygon, "opacity:0.2;fill:none;stroke:rgb(255,0,0);stroke-width:3"); post_map(polygon, mapper, rescale_policy); } #endif } #ifdef BOOST_GEOMETRY_CHECK_WITH_POSTGIS static int counter = 0; #endif template < typename Geometry, template class JoinStrategy, template class EndStrategy, typename GeometryOut > void test_one(std::string const& caseid, std::string const& wkt, double expected_area, double distance_left, double distance_right = -999) { namespace bg = boost::geometry; Geometry g; bg::read_wkt(wkt, g); typedef typename bg::point_type::type point_type; #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 test_buffer (caseid, g, false, expected_area, distance_left, distance_right); } #endif