1 // Boost.Geometry (aka GGL, Generic Geometry Library)
4 // Copyright (c) 2010-2015 Barend Gehrels, Amsterdam, the Netherlands.
5 // Use, modification and distribution is subject to the Boost Software License,
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
10 #ifndef BOOST_GEOMETRY_TEST_BUFFER_SVG_HPP
11 #define BOOST_GEOMETRY_TEST_BUFFER_SVG_HPP
16 // Uncomment next lines if you want to have a zoomed view
17 //#define BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX
19 // If possible define box before including this unit with the right view
20 #ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX
21 # ifndef BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX
22 # define BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX "BOX(0 0,100 100)"
26 #include <boost/foreach.hpp>
27 #include <boost/geometry/io/svg/svg_mapper.hpp>
28 #include <boost/geometry/algorithms/intersection.hpp>
31 inline char piece_type_char(bg::strategy::buffer::piece_type const& type)
33 using namespace bg::strategy::buffer;
36 case buffered_segment : return 's';
37 case buffered_join : return 'j';
38 case buffered_round_end : return 'r';
39 case buffered_flat_end : return 'f';
40 case buffered_point : return 'p';
41 case buffered_concave : return 'c';
46 template <typename SvgMapper, typename Box>
50 svg_visitor(SvgMapper& mapper)
54 bg::assign_inverse(m_alternate_box);
57 void set_alternate_box(Box const& box)
59 m_alternate_box = box;
63 template <typename PieceCollection>
64 inline void apply(PieceCollection const& collection, int phase)
66 // Comment next return if you want to see pieces, turns, etc.
71 map_pieces(collection.m_pieces, collection.offsetted_rings, true, true);
75 map_turns(collection.m_turns, true, false);
77 if (phase == 2 && ! m_zoom)
79 // map_traversed_rings(collection.traversed_rings);
80 // map_offsetted_rings(collection.offsetted_rings);
88 bg::segment_identifier m_id;
91 inline si(bg::segment_identifier const& id)
95 template <typename Char, typename Traits>
96 inline friend std::basic_ostream<Char, Traits>& operator<<(
97 std::basic_ostream<Char, Traits>& os,
100 os << s.m_id.multi_index << "." << s.m_id.segment_index;
105 template <typename Turns>
106 inline void map_turns(Turns const& turns, bool label_good_turns, bool label_wrong_turns)
108 namespace bgdb = boost::geometry::detail::buffer;
109 typedef typename boost::range_value<Turns const>::type turn_type;
110 typedef typename turn_type::point_type point_type;
111 typedef typename turn_type::robust_point_type robust_point_type;
113 std::map<robust_point_type, int, bg::less<robust_point_type> > offsets;
115 for (typename boost::range_iterator<Turns const>::type it =
116 boost::begin(turns); it != boost::end(turns); ++it)
118 if (m_zoom && bg::disjoint(it->point, m_alternate_box))
125 std::string fill = "fill:rgb(0,255,0);";
128 case bgdb::inside_buffer :
129 fill = "fill:rgb(255,0,0);";
133 case bgdb::location_discard :
134 fill = "fill:rgb(0,0,255);";
139 ; // to avoid "enumeration value not handled" warning
143 fill = "fill:rgb(128,128,128);";
148 fill += "fill-opacity:0.7;";
150 m_mapper.map(it->point, fill, 4);
152 if ((label_good_turns && is_good) || (label_wrong_turns && ! is_good))
154 std::ostringstream out;
155 out << it->turn_index;
156 if (it->cluster_id >= 0)
158 out << " (" << it->cluster_id << ")";
161 << " " << it->operations[0].piece_index << "/" << it->operations[1].piece_index
162 << " " << si(it->operations[0].seg_id) << "/" << si(it->operations[1].seg_id)
164 // If you want to see travel information
166 << " nxt " << it->operations[0].enriched.get_next_turn_index()
167 << "/" << it->operations[1].enriched.get_next_turn_index()
168 //<< " frac " << it->operations[0].fraction
170 // If you want to see (robust)point coordinates (e.g. to find duplicates)
171 << std::endl << std::setprecision(16) << bg::wkt(it->point)
172 << std::endl << bg::wkt(it->robust_point)
175 out << " " << bg::method_char(it->method)
176 << ":" << bg::operation_char(it->operations[0].operation)
177 << "/" << bg::operation_char(it->operations[1].operation);
179 << (it->count_on_offsetted > 0 ? "b" : "") // b: offsetted border
180 #if ! defined(BOOST_GEOMETRY_BUFFER_USE_SIDE_OF_INTERSECTION)
181 << (it->count_within_near_offsetted > 0 ? "n" : "")
183 << (it->count_within > 0 ? "w" : "")
184 << (it->count_on_helper > 0 ? "h" : "")
185 << (it->count_on_multi > 0 ? "m" : "")
188 offsets[it->get_robust_point()] += 10;
189 int offset = offsets[it->get_robust_point()];
191 m_mapper.text(it->point, out.str(), "fill:rgb(0,0,0);font-family='Arial';font-size:9px;", 5, offset);
193 offsets[it->get_robust_point()] += 25;
198 template <typename Pieces, typename OffsettedRings>
199 inline void map_pieces(Pieces const& pieces,
200 OffsettedRings const& offsetted_rings,
201 bool do_pieces, bool do_indices)
203 typedef typename boost::range_value<Pieces const>::type piece_type;
204 typedef typename boost::range_value<OffsettedRings const>::type ring_type;
206 for(typename boost::range_iterator<Pieces const>::type it = boost::begin(pieces);
207 it != boost::end(pieces);
210 const piece_type& piece = *it;
211 bg::segment_identifier seg_id = piece.first_seg_id;
212 if (seg_id.segment_index < 0)
220 ring_type const& ring = offsetted_rings[seg_id.multi_index];
222 std::copy(boost::begin(ring) + seg_id.segment_index,
223 boost::begin(ring) + piece.last_segment_index,
224 std::back_inserter(corner));
225 std::copy(boost::begin(piece.helper_points),
226 boost::end(piece.helper_points),
227 std::back_inserter(corner));
233 if (m_zoom && bg::disjoint(corner, m_alternate_box))
238 if (m_zoom && do_pieces)
242 std::string style = "opacity:0.3;stroke:rgb(0,0,0);stroke-width:1;";
243 typedef typename bg::point_type<Box>::type point_type;
244 bg::model::multi_polygon<bg::model::polygon<point_type> > clipped;
245 bg::intersection(ring, m_alternate_box, clipped);
246 m_mapper.map(clipped,
247 piece.type == bg::strategy::buffer::buffered_segment
248 ? style + "fill:rgb(255,128,0);"
249 : style + "fill:rgb(255,0,0);");
253 std::cerr << "Error for piece " << piece.index << std::endl;
258 std::string style = "opacity:0.3;stroke:rgb(0,0,0);stroke-width:1;";
260 piece.type == bg::strategy::buffer::buffered_segment
261 ? style + "fill:rgb(255,128,0);"
262 : style + "fill:rgb(255,0,0);");
267 // Label starting piece_index / segment_index
268 typedef typename bg::point_type<ring_type>::type point_type;
270 std::ostringstream out;
272 << (piece.is_flat_start ? " FS" : "")
273 << (piece.is_flat_end ? " FE" : "")
274 << " (" << piece_type_char(piece.type) << ") "
275 << piece.first_seg_id.segment_index
276 << ".." << piece.last_segment_index - 1;
277 point_type label_point = bg::return_centroid<point_type>(corner);
279 if ((piece.type == bg::strategy::buffer::buffered_concave
280 || piece.type == bg::strategy::buffer::buffered_flat_end)
281 && corner.size() >= 2u)
283 bg::set<0>(label_point, (bg::get<0>(corner[0]) + bg::get<0>(corner[1])) / 2.0);
284 bg::set<1>(label_point, (bg::get<1>(corner[0]) + bg::get<1>(corner[1])) / 2.0);
286 m_mapper.text(label_point, out.str(), "fill:rgb(255,0,0);font-family='Arial';font-size:10px;", 5, 5);
291 template <typename TraversedRings>
292 inline void map_traversed_rings(TraversedRings const& traversed_rings)
294 for(typename boost::range_iterator<TraversedRings const>::type it
295 = boost::begin(traversed_rings); it != boost::end(traversed_rings); ++it)
297 m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(0,255,0);stroke-width:2");
301 template <typename OffsettedRings>
302 inline void map_offsetted_rings(OffsettedRings const& offsetted_rings)
304 for(typename boost::range_iterator<OffsettedRings const>::type it
305 = boost::begin(offsetted_rings); it != boost::end(offsetted_rings); ++it)
309 m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(255,0,0);stroke-width:2");
313 m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(0,0,255);stroke-width:2");
325 template <typename Point>
326 class buffer_svg_mapper
330 buffer_svg_mapper(std::string const& casename)
331 : m_casename(casename)
334 bg::assign_inverse(m_alternate_box);
337 template <typename Mapper, typename Visitor, typename Envelope>
338 void prepare(Mapper& mapper, Visitor& visitor, Envelope const& envelope, double box_buffer_distance)
340 #ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX
341 // Create a zoomed-in view
342 bg::model::box<Point> alternate_box;
343 bg::read_wkt(BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX, alternate_box);
344 mapper.add(alternate_box);
346 // Take care non-visible elements are skipped
347 visitor.set_alternate_box(alternate_box);
348 set_alternate_box(alternate_box);
350 bg::model::box<Point> box = envelope;
351 bg::buffer(box, box, box_buffer_distance);
355 boost::ignore_unused(visitor);
358 void set_alternate_box(bg::model::box<Point> const& box)
360 m_alternate_box = box;
364 template <typename Mapper, typename Geometry, typename GeometryBuffer>
365 void map_input_output(Mapper& mapper, Geometry const& geometry,
366 GeometryBuffer const& buffered, bool negative)
368 bool const areal = boost::is_same
370 typename bg::tag_cast
372 typename bg::tag<Geometry>::type,
374 >::type, bg::areal_tag
379 map_io_zoomed(mapper, geometry, buffered, negative, areal);
383 map_io(mapper, geometry, buffered, negative, areal);
387 template <typename Mapper, typename Geometry, typename Strategy, typename RescalePolicy>
388 void map_self_ips(Mapper& mapper, Geometry const& geometry, Strategy const& strategy, RescalePolicy const& rescale_policy)
390 typedef bg::detail::overlay::turn_info
393 typename bg::segment_ratio_type<Point, RescalePolicy>::type
396 std::vector<turn_info> turns;
398 bg::detail::self_get_turn_points::no_interrupt_policy policy;
401 bg::detail::overlay::assign_null_policy
402 >(geometry, strategy, rescale_policy, turns, policy);
404 BOOST_FOREACH(turn_info const& turn, turns)
406 mapper.map(turn.point, "fill:rgb(255,128,0);stroke:rgb(0,0,100);stroke-width:1", 3);
412 template <typename Mapper, typename Geometry, typename GeometryBuffer>
413 void map_io(Mapper& mapper, Geometry const& geometry,
414 GeometryBuffer const& buffered, bool negative, bool areal)
416 // Map input geometry in green
419 mapper.map(geometry, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,64,0);stroke-width:2");
423 // TODO: clip input points/linestring
424 mapper.map(geometry, "opacity:0.5;stroke:rgb(0,128,0);stroke-width:10");
428 // Map buffer in yellow (inflate) and with orange-dots (deflate)
429 std::string style = negative
430 ? "opacity:0.4;fill:rgb(255,255,192);stroke:rgb(255,128,0);stroke-width:3"
431 : "opacity:0.4;fill:rgb(255,255,128);stroke:rgb(0,0,0);stroke-width:3";
433 mapper.map(buffered, style);
437 template <typename Mapper, typename Geometry, typename GeometryBuffer>
438 void map_io_zoomed(Mapper& mapper, Geometry const& geometry,
439 GeometryBuffer const& buffered, bool negative, bool areal)
441 // Map input geometry in green
444 // Assuming input is areal
445 GeometryBuffer clipped;
446 // TODO: the next line does NOT compile for multi-point, TODO: implement that line
447 // bg::intersection(geometry, m_alternate_box, clipped);
448 mapper.map(clipped, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,64,0);stroke-width:2");
452 // TODO: clip input (multi)point/linestring
453 mapper.map(geometry, "opacity:0.5;stroke:rgb(0,128,0);stroke-width:10");
457 // Map buffer in yellow (inflate) and with orange-dots (deflate)
458 std::string style = negative
459 ? "opacity:0.4;fill:rgb(255,255,192);stroke:rgb(255,128,0);stroke-width:3"
460 : "opacity:0.4;fill:rgb(255,255,128);stroke:rgb(0,0,0);stroke-width:3";
464 // Clip output multi-polygon with box
465 GeometryBuffer clipped;
466 bg::intersection(buffered, m_alternate_box, clipped);
467 mapper.map(clipped, style);
471 std::cout << "Error for buffered output " << m_casename << std::endl;
476 bg::model::box<Point> m_alternate_box;
478 std::string m_casename;