123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- // Boost.Geometry (aka GGL, Generic Geometry Library)
- // Unit Test Helper
- // Copyright (c) 2010-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)
- #ifndef BOOST_GEOMETRY_TEST_BUFFER_SVG_HPP
- #define BOOST_GEOMETRY_TEST_BUFFER_SVG_HPP
- #include <fstream>
- #include <sstream>
- // 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
- #include <boost/foreach.hpp>
- #include <boost/geometry/io/svg/svg_mapper.hpp>
- #include <boost/geometry/algorithms/intersection.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 SvgMapper, typename Box>
- class svg_visitor
- {
- public :
- svg_visitor(SvgMapper& mapper)
- : m_mapper(mapper)
- , m_zoom(false)
- {
- bg::assign_inverse(m_alternate_box);
- }
- void set_alternate_box(Box const& box)
- {
- m_alternate_box = box;
- m_zoom = true;
- }
- 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);
- }
- if (phase == 1)
- {
- map_turns(collection.m_turns, true, false);
- }
- if (phase == 2 && ! m_zoom)
- {
- // map_traversed_rings(collection.traversed_rings);
- // map_offsetted_rings(collection.offsetted_rings);
- }
- }
- private :
- class si
- {
- private :
- bg::segment_identifier m_id;
- public :
- inline si(bg::segment_identifier const& id)
- : m_id(id)
- {}
- template <typename Char, typename Traits>
- inline friend std::basic_ostream<Char, Traits>& operator<<(
- std::basic_ostream<Char, Traits>& os,
- si const& s)
- {
- os << s.m_id.multi_index << "." << s.m_id.segment_index;
- return os;
- }
- };
- template <typename Turns>
- inline void map_turns(Turns const& turns, bool label_good_turns, bool label_wrong_turns)
- {
- namespace bgdb = boost::geometry::detail::buffer;
- typedef typename boost::range_value<Turns const>::type turn_type;
- typedef typename turn_type::point_type point_type;
- typedef typename turn_type::robust_point_type robust_point_type;
- std::map<robust_point_type, int, bg::less<robust_point_type> > offsets;
- for (typename boost::range_iterator<Turns const>::type it =
- boost::begin(turns); it != boost::end(turns); ++it)
- {
- if (m_zoom && bg::disjoint(it->point, m_alternate_box))
- {
- continue;
- }
- bool is_good = true;
- char color = 'g';
- std::string fill = "fill:rgb(0,255,0);";
- switch(it->location)
- {
- case bgdb::inside_buffer :
- fill = "fill:rgb(255,0,0);";
- color = 'r';
- is_good = false;
- break;
- case bgdb::location_discard :
- fill = "fill:rgb(0,0,255);";
- color = 'b';
- is_good = false;
- break;
- default:
- ; // to avoid "enumeration value not handled" warning
- }
- if (it->blocked())
- {
- fill = "fill:rgb(128,128,128);";
- color = '-';
- is_good = false;
- }
- fill += "fill-opacity:0.7;";
- m_mapper.map(it->point, fill, 4);
- if ((label_good_turns && is_good) || (label_wrong_turns && ! is_good))
- {
- std::ostringstream out;
- out << it->turn_index;
- if (it->cluster_id >= 0)
- {
- out << " (" << it->cluster_id << ")";
- }
- out
- << " " << it->operations[0].piece_index << "/" << it->operations[1].piece_index
- << " " << si(it->operations[0].seg_id) << "/" << si(it->operations[1].seg_id)
- // If you want to see travel information
- << std::endl
- << " nxt " << it->operations[0].enriched.get_next_turn_index()
- << "/" << it->operations[1].enriched.get_next_turn_index()
- //<< " frac " << it->operations[0].fraction
- // If you want to see (robust)point coordinates (e.g. to find duplicates)
- << std::endl << std::setprecision(16) << bg::wkt(it->point)
- << std::endl << bg::wkt(it->robust_point)
- << std::endl;
- out << " " << bg::method_char(it->method)
- << ":" << bg::operation_char(it->operations[0].operation)
- << "/" << bg::operation_char(it->operations[1].operation);
- out << " "
- << (it->count_on_offsetted > 0 ? "b" : "") // b: offsetted border
- << (it->count_within_near_offsetted > 0 ? "n" : "")
- << (it->count_within > 0 ? "w" : "")
- << (it->count_on_helper > 0 ? "h" : "")
- << (it->count_on_multi > 0 ? "m" : "")
- ;
- offsets[it->get_robust_point()] += 10;
- int offset = offsets[it->get_robust_point()];
- m_mapper.text(it->point, out.str(), "fill:rgb(0,0,0);font-family='Arial';font-size:9px;", 5, offset);
- offsets[it->get_robust_point()] += 25;
- }
- }
- }
- template <typename Pieces, typename OffsettedRings>
- inline void map_pieces(Pieces const& pieces,
- OffsettedRings const& offsetted_rings,
- bool do_pieces, bool do_indices)
- {
- typedef typename boost::range_value<Pieces const>::type piece_type;
- typedef typename boost::range_value<OffsettedRings const>::type ring_type;
- for(typename boost::range_iterator<Pieces const>::type it = boost::begin(pieces);
- it != boost::end(pieces);
- ++it)
- {
- const piece_type& piece = *it;
- bg::segment_identifier seg_id = piece.first_seg_id;
- if (seg_id.segment_index < 0)
- {
- continue;
- }
- ring_type corner;
- ring_type const& ring = offsetted_rings[seg_id.multi_index];
- std::copy(boost::begin(ring) + seg_id.segment_index,
- boost::begin(ring) + piece.last_segment_index,
- std::back_inserter(corner));
- std::copy(boost::begin(piece.helper_points),
- boost::end(piece.helper_points),
- std::back_inserter(corner));
- if (corner.empty())
- {
- continue;
- }
- #if 0 // Does not compile (SVG is not enabled by default)
- if (m_zoom && bg::disjoint(corner, m_alternate_box))
- {
- continue;
- }
- #endif
- if (m_zoom && do_pieces)
- {
- try
- {
- std::string style = "opacity:0.3;stroke:rgb(0,0,0);stroke-width:1;";
- typedef typename bg::point_type<Box>::type point_type;
- bg::model::multi_polygon<bg::model::polygon<point_type> > clipped;
- bg::intersection(ring, m_alternate_box, clipped);
- m_mapper.map(clipped,
- piece.type == bg::strategy::buffer::buffered_segment
- ? style + "fill:rgb(255,128,0);"
- : style + "fill:rgb(255,0,0);");
- }
- catch (...)
- {
- std::cerr << "Error for piece " << piece.index << std::endl;
- }
- }
- else if (do_pieces)
- {
- std::string style = "opacity:0.3;stroke:rgb(0,0,0);stroke-width:1;";
- m_mapper.map(corner,
- piece.type == bg::strategy::buffer::buffered_segment
- ? style + "fill:rgb(255,128,0);"
- : style + "fill:rgb(255,0,0);");
- }
- if (do_indices)
- {
- // Label starting piece_index / segment_index
- typedef typename bg::point_type<ring_type>::type point_type;
- std::ostringstream out;
- out << piece.index
- << (piece.is_flat_start ? " FS" : "")
- << (piece.is_flat_end ? " FE" : "")
- << " (" << 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[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);
- }
- }
- }
- template <typename TraversedRings>
- inline void map_traversed_rings(TraversedRings const& traversed_rings)
- {
- for(typename boost::range_iterator<TraversedRings const>::type it
- = boost::begin(traversed_rings); it != boost::end(traversed_rings); ++it)
- {
- m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(0,255,0);stroke-width:2");
- }
- }
- template <typename OffsettedRings>
- inline void map_offsetted_rings(OffsettedRings const& offsetted_rings)
- {
- for(typename boost::range_iterator<OffsettedRings const>::type it
- = boost::begin(offsetted_rings); it != boost::end(offsetted_rings); ++it)
- {
- if (it->discarded())
- {
- m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(255,0,0);stroke-width:2");
- }
- else
- {
- m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(0,0,255);stroke-width:2");
- }
- }
- }
- SvgMapper& m_mapper;
- Box m_alternate_box;
- bool m_zoom;
- };
- template <typename Point>
- class buffer_svg_mapper
- {
- public :
- buffer_svg_mapper(std::string const& casename)
- : m_casename(casename)
- , m_zoom(false)
- {
- bg::assign_inverse(m_alternate_box);
- }
- template <typename Mapper, typename Visitor, typename Envelope>
- void prepare(Mapper& mapper, Visitor& visitor, Envelope const& envelope, double box_buffer_distance)
- {
- #ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX
- // Create a zoomed-in view
- bg::model::box<Point> 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.set_alternate_box(alternate_box);
- set_alternate_box(alternate_box);
- #else
- bg::model::box<Point> box = envelope;
- bg::buffer(box, box, box_buffer_distance);
- mapper.add(box);
- #endif
- boost::ignore_unused(visitor);
- }
- void set_alternate_box(bg::model::box<Point> const& box)
- {
- m_alternate_box = box;
- m_zoom = true;
- }
- template <typename Mapper, typename Geometry, typename GeometryBuffer>
- void map_input_output(Mapper& mapper, Geometry const& geometry,
- GeometryBuffer const& buffered, bool negative)
- {
- bool const areal = boost::is_same
- <
- typename bg::tag_cast
- <
- typename bg::tag<Geometry>::type,
- bg::areal_tag
- >::type, bg::areal_tag
- >::type::value;
- if (m_zoom)
- {
- map_io_zoomed(mapper, geometry, buffered, negative, areal);
- }
- else
- {
- map_io(mapper, geometry, buffered, negative, areal);
- }
- }
- template <typename Mapper, typename Geometry, typename Strategy, typename RescalePolicy>
- void map_self_ips(Mapper& mapper, Geometry const& geometry, Strategy const& strategy, RescalePolicy const& rescale_policy)
- {
- typedef bg::detail::overlay::turn_info
- <
- Point,
- typename bg::detail::segment_ratio_type<Point, RescalePolicy>::type
- > turn_info;
- std::vector<turn_info> turns;
- bg::detail::self_get_turn_points::no_interrupt_policy policy;
- bg::self_turns
- <
- bg::detail::overlay::assign_null_policy
- >(geometry, strategy, 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);
- }
- }
- private :
- template <typename Mapper, typename Geometry, typename GeometryBuffer>
- void map_io(Mapper& mapper, Geometry const& geometry,
- GeometryBuffer const& buffered, bool negative, bool areal)
- {
- // Map input geometry in green
- if (areal)
- {
- mapper.map(geometry, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,64,0);stroke-width:2");
- }
- else
- {
- // TODO: clip input points/linestring
- mapper.map(geometry, "opacity:0.5;stroke:rgb(0,128,0);stroke-width:10");
- }
- {
- // Map buffer in yellow (inflate) and with orange-dots (deflate)
- std::string style = 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";
- mapper.map(buffered, style);
- }
- }
- template <typename Mapper, typename Geometry, typename GeometryBuffer>
- void map_io_zoomed(Mapper& mapper, Geometry const& geometry,
- GeometryBuffer const& buffered, bool negative, bool areal)
- {
- // Map input geometry in green
- if (areal)
- {
- // Assuming input is areal
- GeometryBuffer clipped;
- // TODO: the next line does NOT compile for multi-point, TODO: implement that line
- // bg::intersection(geometry, m_alternate_box, clipped);
- mapper.map(clipped, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,64,0);stroke-width:2");
- }
- else
- {
- // TODO: clip input (multi)point/linestring
- mapper.map(geometry, "opacity:0.5;stroke:rgb(0,128,0);stroke-width:10");
- }
- {
- // Map buffer in yellow (inflate) and with orange-dots (deflate)
- std::string style = 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";
- try
- {
- // Clip output multi-polygon with box
- GeometryBuffer clipped;
- bg::intersection(buffered, m_alternate_box, clipped);
- mapper.map(clipped, style);
- }
- catch (...)
- {
- std::cout << "Error for buffered output " << m_casename << std::endl;
- }
- }
- }
- bg::model::box<Point> m_alternate_box;
- bool m_zoom;
- std::string m_casename;
- };
- #endif
|