// Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test // Copyright (c) 2015 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) #include #include #include #include #include #include #if defined(TEST_WITH_SVG) # include #endif #include #include #include #include //#include #if defined(TEST_WITH_SVG) # include #endif #include "multi_overlay_cases.hpp" #if defined(TEST_WITH_SVG) template struct map_visitor { map_visitor(Mapper& mapper) : m_mapper(mapper) , m_traverse_seq(0) {} template void visit_turns(int phase, Turns const& turns) { typedef typename boost::range_value::type turn_type; int index = 0; BOOST_FOREACH(turn_type const& turn, turns) { switch (phase) { case 1 : m_mapper.map(turn.point, "fill:rgb(255,128,0);" "stroke:rgb(0,0,0);stroke-width:1", 3); break; case 2 : label_turn(index, turn); break; } index++; } } template void visit_clusters(Clusters const& clusters, Turns const& turns) { typedef typename boost::range_value::type turn_type; int index = 0; BOOST_FOREACH(turn_type const& turn, turns) { if (turn.cluster_id >= 0) { std::cout << " TURN: " << index << " part of cluster " << turn.cluster_id << std::endl; } index++; } for (typename Clusters::const_iterator it = clusters.begin(); it != clusters.end(); ++it) { std::cout << " CLUSTER " << it->first << ": "; for (typename std::set::const_iterator sit = it->second.begin(); sit != it->second.end(); ++sit) { std::cout << " " << *sit; } std::cout << std::endl; } std::cout << std::endl; } template void visit_traverse(Turns const& turns, Turn const& turn, Operation const& op, const std::string& header) { // Uncomment for more detailed debug info in SVG on traversal std::string style = header == "Visit" ? "fill:rgb(80,80,80)" : "fill:rgb(0,0,0)"; style += ";font-family:Arial;font-size:8px"; stream(turns, turn, op, header.substr(0, 1), style); } template void visit_traverse_reject(Turns const& turns, Turn const& turn, Operation const& op, bg::detail::overlay::traverse_error_type error) { std::string style = "fill:rgb(255,0,0);font-family:Arial;font-size:8px"; stream(turns, turn, op, bg::detail::overlay::traverse_error_string(error), style); } template void stream(Turns const& turns, Turn const& turn, Operation const& op, const std::string& header, const std::string& style) { // Because turn index is unknown here, and still useful for debugging, // walk through turns to get it (turns is a deque, there is no .data() ) std::size_t index = 0; for (typename Turns::const_iterator it = turns.begin(); it != turns.end(); ++it, ++index) { Turn const& t = *it; if (&t == &turn) { break; } } std::ostringstream out; out << m_traverse_seq++ << " " << header << " " << index; if (&op == &turn.operations[0]) { out << "[0]"; } if (&op == &turn.operations[1]) { out << "[1]"; } out << " " << bg::visited_char(op.visited); add_text(turn, out.str(), style); } template bool label_operation(Turn const& turn, int index, std::ostream& os) { os << bg::operation_char(turn.operations[index].operation); bool result = false; if (! turn.discarded) { if (turn.operations[index].enriched.next_ip_index != -1) { os << "->" << turn.operations[index].enriched.next_ip_index; if (turn.operations[index].enriched.next_ip_index != -1) { result = true; } } else { os << "->" << turn.operations[index].enriched.travels_to_ip_index; if (turn.operations[index].enriched.travels_to_ip_index != -1) { result = true; } } } return result; } template void label_turn(int index, Turn const& turn) { std::ostringstream out; out << index << " "; if (turn.cluster_id != -1) { out << " c=" << turn.cluster_id << " "; } bool lab1 = label_operation(turn, 0, out); out << " / "; bool lab2 = label_operation(turn, 1, out); std::string style = "fill:rgb(0,0,0);font-family:Arial;font-size:8px"; if (turn.discarded) { style = "fill:rgb(92,92,92);font-family:Arial;font-size:6px"; } else if (turn.cluster_id != -1) { style = "fill:rgb(0,0,255);font-family:Arial;font-size:8px"; } else if (turn.colocated) { style = "fill:rgb(255,0,0);font-family:Arial;font-size:8px"; } else if (! lab1 || ! lab2) { style = "fill:rgb(0,0,255);font-family:Arial;font-size:6px"; } add_text(turn, out.str(), style); } template void add_text(Turn const& turn, std::string const& text, std::string const& style) { int const margin = 5; int const lineheight = 8; double const half = 0.5; double const ten = 10; // Map characteristics // Create a rounded off point std::pair p = std::make_pair( boost::numeric_cast(half + ten * bg::get<0>(turn.point)), boost::numeric_cast(half + ten * bg::get<1>(turn.point)) ); m_mapper.text(turn.point, text, style, margin, m_offsets[p], lineheight); m_offsets[p] += lineheight; } Mapper& m_mapper; std::map, int> m_offsets; int m_traverse_seq; }; #endif template void test_overlay(std::string const& caseid, std::string const& wkt1, std::string const& wkt2, double expected_area) { Geometry g1; bg::read_wkt(wkt1, g1); Geometry g2; bg::read_wkt(wkt2, g2); // Reverse if necessary bg::correct(g1); bg::correct(g2); #if defined(TEST_WITH_SVG) std::ostringstream filename; filename << "overlay" << "_" << caseid << "_" << string_from_type::type>::name() << ".svg"; std::ofstream svg(filename.str().c_str()); typedef bg::svg_mapper::type> svg_mapper; svg_mapper mapper(svg, 500, 500); mapper.add(g1); mapper.add(g2); // Input shapes in green (src=0) / blue (src=1) mapper.map(g1, "fill-opacity:0.5;fill:rgb(153,204,0);" "stroke:rgb(153,204,0);stroke-width:3"); mapper.map(g2, "fill-opacity:0.3;fill:rgb(51,51,153);" "stroke:rgb(51,51,153);stroke-width:3"); #endif typedef typename boost::range_value::type geometry_out; typedef bg::detail::overlay::overlay < Geometry, Geometry, false, OverlayType == bg::overlay_difference, false, geometry_out, OverlayType > overlay; typedef typename bg::rescale_overlay_policy_type < Geometry, Geometry >::type rescale_policy_type; rescale_policy_type robust_policy = bg::get_rescale_policy(g1, g2); typedef bg::strategy_intersection < typename bg::cs_tag::type, Geometry, Geometry, typename bg::point_type::type, rescale_policy_type > strategy; #if defined(TEST_WITH_SVG) map_visitor visitor(mapper); #else bg::detail::overlay::overlay_null_visitor visitor; #endif Geometry result; overlay::apply(g1, g2, robust_policy, std::back_inserter(result), strategy(), visitor); BOOST_CHECK_CLOSE(bg::area(result), expected_area, 0.001); #if defined(TEST_WITH_SVG) mapper.map(result, "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);" "stroke:rgb(255,0,255);stroke-width:8"); #endif } template void test_all() { typedef bg::model::point point_type; typedef bg::model::polygon polygon; typedef bg::model::multi_polygon < bg::model::polygon > multi_polygon; test_overlay ( "case_multi_simplex_union", case_multi_simplex[0], case_multi_simplex[1], 14.58 ); test_overlay ( "case_multi_simplex_intersection", case_multi_simplex[0], case_multi_simplex[1], 6.42 ); test_overlay ( "case_multi_simplex_diff_a", case_multi_simplex[0], case_multi_simplex[1], 5.58 ); test_overlay ( "case_multi_simplex_diff_b", case_multi_simplex[1], case_multi_simplex[0], 2.58 ); // Contains 5 clusters, needing immediate selection of next turn test_overlay ( "case_58_multi_0_3_union", case_58_multi[0], case_58_multi[3], 19.8333333 ); // Contains many clusters, needing to exclude u/u turns test_overlay ( "case_recursive_boxes_3_union", case_recursive_boxes_3[0], case_recursive_boxes_3[1], 56.5 ); // Contains 4 clusters, one of which having 4 turns test_overlay ( "case_recursive_boxes_7_union", case_recursive_boxes_7[0], case_recursive_boxes_7[1], 7.0 ); // Contains 5 clusters, needing immediate selection of next turn test_overlay ( "case_89_multi_union", case_89_multi[0], case_89_multi[1], 6.0 ); // Needs ux/next_turn_index==-1 to be filtered out test_overlay ( "case_77_multi_intersection", case_77_multi[0], case_77_multi[1], 9.0 ); test_overlay ( "case_101_multi_union", case_101_multi[0], case_101_multi[1], 22.25 ); test_overlay ( "case_101_multi_intersection", case_101_multi[0], case_101_multi[1], 4.75 ); test_overlay ( "case_recursive_boxes_11_intersection", case_recursive_boxes_11[0], case_recursive_boxes_11[1], 1.0 ); test_overlay ( test_overlay ( "case_recursive_boxes_4_union", case_recursive_boxes_4[0], case_recursive_boxes_4[1], 96.75 ); test_overlay ( "case_58_multi_b6_intersection", case_58_multi[6], case_58_multi[2], 13.25 ); "case_recursive_boxes_12_union", case_recursive_boxes_12[0], case_recursive_boxes_12[1], 6.0 ); // std::cout // << " \"" // << bg::inverse(case_65_multi[0], 1.0) // << "\"" << std::endl; } int test_main(int, char* []) { test_all(); return 0; }