Imported Upstream version 1.72.0
[platform/upstream/boost.git] / libs / geometry / test / algorithms / buffer / test_buffer_svg.hpp
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 // Unit Test Helper
3
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)
8
9
10 #ifndef BOOST_GEOMETRY_TEST_BUFFER_SVG_HPP
11 #define BOOST_GEOMETRY_TEST_BUFFER_SVG_HPP
12
13 #include <fstream>
14 #include <sstream>
15
16 // Uncomment next lines if you want to have a zoomed view
17 //#define BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX
18
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)"
23 #  endif
24 #endif
25
26 #include <boost/foreach.hpp>
27 #include <boost/geometry/io/svg/svg_mapper.hpp>
28 #include <boost/geometry/algorithms/intersection.hpp>
29
30
31 inline char piece_type_char(bg::strategy::buffer::piece_type const& type)
32 {
33     using namespace bg::strategy::buffer;
34     switch(type)
35     {
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';
42         default : return '?';
43     }
44 }
45
46 template <typename SvgMapper, typename Box>
47 class svg_visitor
48 {
49 public :
50     svg_visitor(SvgMapper& mapper)
51         : m_mapper(mapper)
52         , m_zoom(false)
53     {
54         bg::assign_inverse(m_alternate_box);
55     }
56
57     void set_alternate_box(Box const& box)
58     {
59         m_alternate_box = box;
60         m_zoom = true;
61     }
62
63     template <typename PieceCollection>
64     inline void apply(PieceCollection const& collection, int phase)
65     {
66         // Comment next return if you want to see pieces, turns, etc.
67         return;
68
69         if(phase == 0)
70         {
71             map_pieces(collection.m_pieces, collection.offsetted_rings, true, true);
72         }
73         if (phase == 1)
74         {
75             map_turns(collection.m_turns, true, false);
76         }
77         if (phase == 2 && ! m_zoom)
78         {
79 //        map_traversed_rings(collection.traversed_rings);
80 //        map_offsetted_rings(collection.offsetted_rings);
81         }
82     }
83
84 private :
85     class si
86     {
87     private :
88         bg::segment_identifier m_id;
89
90     public :
91         inline si(bg::segment_identifier const& id)
92             : m_id(id)
93         {}
94
95         template <typename Char, typename Traits>
96         inline friend std::basic_ostream<Char, Traits>& operator<<(
97                 std::basic_ostream<Char, Traits>& os,
98                 si const& s)
99         {
100             os << s.m_id.multi_index << "." << s.m_id.segment_index;
101             return os;
102         }
103     };
104
105     template <typename Turns>
106     inline void map_turns(Turns const& turns, bool label_good_turns, bool label_wrong_turns)
107     {
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;
112
113         std::map<robust_point_type, int, bg::less<robust_point_type> > offsets;
114
115         for (typename boost::range_iterator<Turns const>::type it =
116             boost::begin(turns); it != boost::end(turns); ++it)
117         {
118             if (m_zoom && bg::disjoint(it->point, m_alternate_box))
119             {
120                 continue;
121             }
122
123             bool is_good = true;
124             char color = 'g';
125             std::string fill = "fill:rgb(0,255,0);";
126             switch(it->location)
127             {
128                 case bgdb::inside_buffer :
129                     fill = "fill:rgb(255,0,0);";
130                     color = 'r';
131                     is_good = false;
132                     break;
133                 case bgdb::location_discard :
134                     fill = "fill:rgb(0,0,255);";
135                     color = 'b';
136                     is_good = false;
137                     break;
138                 default:
139                     ; // to avoid "enumeration value not handled" warning
140             }
141             if (it->blocked())
142             {
143                 fill = "fill:rgb(128,128,128);";
144                 color = '-';
145                 is_good = false;
146             }
147
148             fill += "fill-opacity:0.7;";
149
150             m_mapper.map(it->point, fill, 4);
151
152             if ((label_good_turns && is_good) || (label_wrong_turns && ! is_good))
153             {
154                 std::ostringstream out;
155                 out << it->turn_index;
156                 if (it->cluster_id >= 0)
157                 {
158                    out << " ("  << it->cluster_id << ")";
159                 }
160                 out
161                     << " " << it->operations[0].piece_index << "/" << it->operations[1].piece_index
162                     << " " << si(it->operations[0].seg_id) << "/" << si(it->operations[1].seg_id)
163
164     //              If you want to see travel information
165                     << std::endl
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
169
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)
173
174                     << std::endl;
175                 out << " " << bg::method_char(it->method)
176                     << ":" << bg::operation_char(it->operations[0].operation)
177                     << "/" << bg::operation_char(it->operations[1].operation);
178                 out << " "
179                     << (it->count_on_offsetted > 0 ? "b" : "") // b: offsetted border
180                     << (it->count_within_near_offsetted > 0 ? "n" : "")
181                     << (it->count_within > 0 ? "w" : "")
182                     << (it->count_on_helper > 0 ? "h" : "")
183                     << (it->count_on_multi > 0 ? "m" : "")
184                     ;
185
186                 offsets[it->get_robust_point()] += 10;
187                 int offset = offsets[it->get_robust_point()];
188
189                 m_mapper.text(it->point, out.str(), "fill:rgb(0,0,0);font-family='Arial';font-size:9px;", 5, offset);
190
191                 offsets[it->get_robust_point()] += 25;
192             }
193         }
194     }
195
196     template <typename Pieces, typename OffsettedRings>
197     inline void map_pieces(Pieces const& pieces,
198                 OffsettedRings const& offsetted_rings,
199                 bool do_pieces, bool do_indices)
200     {
201         typedef typename boost::range_value<Pieces const>::type piece_type;
202         typedef typename boost::range_value<OffsettedRings const>::type ring_type;
203
204         for(typename boost::range_iterator<Pieces const>::type it = boost::begin(pieces);
205             it != boost::end(pieces);
206             ++it)
207         {
208             const piece_type& piece = *it;
209             bg::segment_identifier seg_id = piece.first_seg_id;
210             if (seg_id.segment_index < 0)
211             {
212                 continue;
213             }
214
215             ring_type corner;
216
217
218             ring_type const& ring = offsetted_rings[seg_id.multi_index];
219
220             std::copy(boost::begin(ring) + seg_id.segment_index,
221                     boost::begin(ring) + piece.last_segment_index,
222                     std::back_inserter(corner));
223             std::copy(boost::begin(piece.helper_points),
224                     boost::end(piece.helper_points),
225                     std::back_inserter(corner));
226
227             if (corner.empty())
228             {
229                 continue;
230             }
231 #if 0 // Does not compile (SVG is not enabled by default)
232             if (m_zoom && bg::disjoint(corner, m_alternate_box))
233             {
234                 continue;
235             }
236 #endif
237
238             if (m_zoom && do_pieces)
239             {
240                 try
241                 {
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);");
250                 }
251                 catch (...)
252                 {
253                     std::cerr << "Error for piece " << piece.index << std::endl;
254                 }
255             }
256             else if (do_pieces)
257             {
258                 std::string style = "opacity:0.3;stroke:rgb(0,0,0);stroke-width:1;";
259                 m_mapper.map(corner,
260                     piece.type == bg::strategy::buffer::buffered_segment
261                     ? style + "fill:rgb(255,128,0);"
262                     : style + "fill:rgb(255,0,0);");
263             }
264
265             if (do_indices)
266             {
267                 // Label starting piece_index / segment_index
268                 typedef typename bg::point_type<ring_type>::type point_type;
269
270                 std::ostringstream out;
271                 out << piece.index
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);
278
279                 if ((piece.type == bg::strategy::buffer::buffered_concave
280                      || piece.type == bg::strategy::buffer::buffered_flat_end)
281                     && corner.size() >= 2u)
282                 {
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);
285                 }
286                 m_mapper.text(label_point, out.str(), "fill:rgb(255,0,0);font-family='Arial';font-size:10px;", 5, 5);
287             }
288         }
289     }
290
291     template <typename TraversedRings>
292     inline void map_traversed_rings(TraversedRings const& traversed_rings)
293     {
294         for(typename boost::range_iterator<TraversedRings const>::type it
295                 = boost::begin(traversed_rings); it != boost::end(traversed_rings); ++it)
296         {
297             m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(0,255,0);stroke-width:2");
298         }
299     }
300
301     template <typename OffsettedRings>
302     inline void map_offsetted_rings(OffsettedRings const& offsetted_rings)
303     {
304         for(typename boost::range_iterator<OffsettedRings const>::type it
305                 = boost::begin(offsetted_rings); it != boost::end(offsetted_rings); ++it)
306         {
307             if (it->discarded())
308             {
309                 m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(255,0,0);stroke-width:2");
310             }
311             else
312             {
313                 m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(0,0,255);stroke-width:2");
314             }
315         }
316     }
317
318
319     SvgMapper& m_mapper;
320     Box m_alternate_box;
321     bool m_zoom;
322
323 };
324
325 template <typename Point>
326 class buffer_svg_mapper
327 {
328 public :
329
330     buffer_svg_mapper(std::string const& casename)
331         : m_casename(casename)
332         , m_zoom(false)
333     {
334         bg::assign_inverse(m_alternate_box);
335     }
336
337     template <typename Mapper, typename Visitor, typename Envelope>
338     void prepare(Mapper& mapper, Visitor& visitor, Envelope const& envelope, double box_buffer_distance)
339     {
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);
345
346         // Take care non-visible elements are skipped
347         visitor.set_alternate_box(alternate_box);
348         set_alternate_box(alternate_box);
349 #else
350         bg::model::box<Point> box = envelope;
351         bg::buffer(box, box, box_buffer_distance);
352         mapper.add(box);
353 #endif
354
355         boost::ignore_unused(visitor);
356     }
357
358     void set_alternate_box(bg::model::box<Point> const& box)
359     {
360         m_alternate_box = box;
361         m_zoom = true;
362     }
363
364     template <typename Mapper, typename Geometry, typename GeometryBuffer>
365     void map_input_output(Mapper& mapper, Geometry const& geometry,
366             GeometryBuffer const& buffered, bool negative)
367     {
368         bool const areal = boost::is_same
369             <
370                 typename bg::tag_cast
371                     <
372                         typename bg::tag<Geometry>::type,
373                         bg::areal_tag
374                     >::type, bg::areal_tag
375             >::type::value;
376
377         if (m_zoom)
378         {
379             map_io_zoomed(mapper, geometry, buffered, negative, areal);
380         }
381         else
382         {
383             map_io(mapper, geometry, buffered, negative, areal);
384         }
385     }
386
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)
389     {
390         typedef bg::detail::overlay::turn_info
391         <
392             Point,
393             typename bg::detail::segment_ratio_type<Point, RescalePolicy>::type
394         > turn_info;
395
396         std::vector<turn_info> turns;
397
398         bg::detail::self_get_turn_points::no_interrupt_policy policy;
399         bg::self_turns
400             <
401                 bg::detail::overlay::assign_null_policy
402             >(geometry, strategy, rescale_policy, turns, policy);
403
404         BOOST_FOREACH(turn_info const& turn, turns)
405         {
406             mapper.map(turn.point, "fill:rgb(255,128,0);stroke:rgb(0,0,100);stroke-width:1", 3);
407         }
408     }
409
410 private :
411
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)
415     {
416         // Map input geometry in green
417         if (areal)
418         {
419             mapper.map(geometry, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,64,0);stroke-width:2");
420         }
421         else
422         {
423             // TODO: clip input points/linestring
424             mapper.map(geometry, "opacity:0.5;stroke:rgb(0,128,0);stroke-width:10");
425         }
426
427         {
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";
432
433             mapper.map(buffered, style);
434         }
435     }
436
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)
440     {
441         // Map input geometry in green
442         if (areal)
443         {
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");
449         }
450         else
451         {
452             // TODO: clip input (multi)point/linestring
453             mapper.map(geometry, "opacity:0.5;stroke:rgb(0,128,0);stroke-width:10");
454         }
455
456         {
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";
461
462             try
463             {
464                 // Clip output multi-polygon with box
465                 GeometryBuffer clipped;
466                 bg::intersection(buffered, m_alternate_box, clipped);
467                 mapper.map(clipped, style);
468             }
469             catch (...)
470             {
471                 std::cout << "Error for buffered output " << m_casename << std::endl;
472             }
473         }
474     }
475
476     bg::model::box<Point> m_alternate_box;
477     bool m_zoom;
478     std::string m_casename;
479 };
480
481
482 #endif