• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
piece_type_char(bg::strategy::buffer::piece_type const & type)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 :
svg_visitor(SvgMapper & mapper)50     svg_visitor(SvgMapper& mapper)
51         : m_mapper(mapper)
52         , m_zoom(false)
53     {
54         bg::assign_inverse(m_alternate_box);
55     }
56 
set_alternate_box(Box const & box)57     void set_alternate_box(Box const& box)
58     {
59         m_alternate_box = box;
60         m_zoom = true;
61     }
62 
63     template <typename PieceCollection>
apply(PieceCollection const & collection,int phase)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 :
si(bg::segment_identifier const & id)91         inline si(bg::segment_identifier const& id)
92             : m_id(id)
93         {}
94 
95         template <typename Char, typename Traits>
operator <<(std::basic_ostream<Char,Traits> & os,si const & s)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>
map_turns(Turns const & turns,bool label_good_turns,bool label_wrong_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 
112         std::map<point_type, int, bg::less<point_type> > offsets;
113 
114         for (typename boost::range_iterator<Turns const>::type it =
115             boost::begin(turns); it != boost::end(turns); ++it)
116         {
117             if (m_zoom && bg::disjoint(it->point, m_alternate_box))
118             {
119                 continue;
120             }
121 
122             bool is_good = true;
123             char color = 'g';
124             std::string fill = "fill:rgb(0,255,0);";
125             if (! it->is_turn_traversable)
126             {
127                 fill = "fill:rgb(255,0,0);";
128                 color = 'r';
129                 is_good = false;
130             }
131             if (it->blocked())
132             {
133                 fill = "fill:rgb(128,128,128);";
134                 color = '-';
135                 is_good = false;
136             }
137 
138             fill += "fill-opacity:0.7;";
139 
140             m_mapper.map(it->point, fill, 4);
141 
142             if ((label_good_turns && is_good) || (label_wrong_turns && ! is_good))
143             {
144                 std::ostringstream out;
145                 out << it->turn_index;
146                 if (it->cluster_id >= 0)
147                 {
148                    out << " ("  << it->cluster_id << ")";
149                 }
150                 out
151                     << " " << it->operations[0].piece_index << "/" << it->operations[1].piece_index
152                     << " " << si(it->operations[0].seg_id) << "/" << si(it->operations[1].seg_id)
153 
154     //              If you want to see travel information
155                     << std::endl
156                     << " nxt " << it->operations[0].enriched.get_next_turn_index()
157                     << "/" << it->operations[1].enriched.get_next_turn_index()
158                     //<< " frac " << it->operations[0].fraction
159 
160     //                If you want to see point coordinates (e.g. to find duplicates)
161                     << std::endl << std::setprecision(16) << bg::wkt(it->point)
162 
163                     << std::endl;
164                 out << " " << bg::method_char(it->method)
165                     << ":" << bg::operation_char(it->operations[0].operation)
166                     << "/" << bg::operation_char(it->operations[1].operation);
167                 out << " "
168                     << (it->is_turn_traversable ? "" : "w")
169                     ;
170 
171                 offsets[it->point] += 10;
172                 int offset = offsets[it->point];
173 
174                 m_mapper.text(it->point, out.str(), "fill:rgb(0,0,0);font-family='Arial';font-size:9px;", 5, offset);
175 
176                 offsets[it->point] += 25;
177             }
178         }
179     }
180 
181     template <typename Pieces, typename OffsettedRings>
map_pieces(Pieces const & pieces,OffsettedRings const & offsetted_rings,bool do_pieces,bool do_indices)182     inline void map_pieces(Pieces const& pieces,
183                 OffsettedRings const& offsetted_rings,
184                 bool do_pieces, bool do_indices)
185     {
186         typedef typename boost::range_value<Pieces const>::type piece_type;
187         typedef typename boost::range_value<OffsettedRings const>::type ring_type;
188         typedef typename bg::point_type<ring_type>::type point_type;
189 
190         for(typename boost::range_iterator<Pieces const>::type it = boost::begin(pieces);
191             it != boost::end(pieces);
192             ++it)
193         {
194             const piece_type& piece = *it;
195             bg::segment_identifier seg_id = piece.first_seg_id;
196             if (seg_id.segment_index < 0)
197             {
198                 continue;
199             }
200 
201             ring_type const& ring = offsetted_rings[seg_id.multi_index];
202 
203 #if 0 // Does not compile (SVG is not enabled by default)
204             if (m_zoom && bg::disjoint(corner, m_alternate_box))
205             {
206                 continue;
207             }
208 #endif
209 
210             bg::model::ring<point_type> const& corner = piece.m_piece_border.get_full_ring();
211 
212             if (m_zoom && do_pieces)
213             {
214                 try
215                 {
216                     std::string style = "opacity:0.3;stroke:rgb(0,0,0);stroke-width:1;";
217                     typedef typename bg::point_type<Box>::type point_type;
218                     bg::model::multi_polygon<bg::model::polygon<point_type> > clipped;
219                     bg::intersection(ring, m_alternate_box, clipped);
220                     m_mapper.map(clipped,
221                         piece.type == bg::strategy::buffer::buffered_segment
222                         ? style + "fill:rgb(255,128,0);"
223                         : style + "fill:rgb(255,0,0);");
224                 }
225                 catch (...)
226                 {
227                     std::cerr << "Error for piece " << piece.index << std::endl;
228                 }
229             }
230             else if (do_pieces && ! corner.empty())
231             {
232                 std::string style = "opacity:0.3;stroke:rgb(0,0,0);stroke-width:1;";
233                 m_mapper.map(corner,
234                     piece.type == bg::strategy::buffer::buffered_segment
235                     ? style + "fill:rgb(255,128,0);"
236                     : style + "fill:rgb(255,0,0);");
237             }
238 
239             if (do_indices)
240             {
241                 // Label starting piece_index / segment_index
242 
243                 std::ostringstream out;
244                 out << piece.index
245                     << (piece.is_flat_start ? " FS" : "")
246                     << (piece.is_flat_end ? " FE" : "")
247                     << " (" << piece_type_char(piece.type) << ") "
248                     << piece.first_seg_id.segment_index
249                     << ".." << piece.beyond_last_segment_index - 1
250                        ;
251                 point_type label_point
252                         = corner.empty()
253                         ? piece.m_label_point
254                         : bg::return_centroid<point_type>(corner);
255 
256                 if ((piece.type == bg::strategy::buffer::buffered_concave
257                      || piece.type == bg::strategy::buffer::buffered_flat_end)
258                     && corner.size() >= 2u)
259                 {
260                     bg::set<0>(label_point, (bg::get<0>(corner[0]) + bg::get<0>(corner[1])) / 2.0);
261                     bg::set<1>(label_point, (bg::get<1>(corner[0]) + bg::get<1>(corner[1])) / 2.0);
262                 }
263                 m_mapper.text(label_point, out.str(), "fill:rgb(255,0,0);font-family='Arial';font-size:10px;", 5, 5);
264             }
265         }
266     }
267 
268     template <typename TraversedRings>
map_traversed_rings(TraversedRings const & traversed_rings)269     inline void map_traversed_rings(TraversedRings const& traversed_rings)
270     {
271         for(typename boost::range_iterator<TraversedRings const>::type it
272                 = boost::begin(traversed_rings); it != boost::end(traversed_rings); ++it)
273         {
274             m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(0,255,0);stroke-width:2");
275         }
276     }
277 
278     template <typename OffsettedRings>
map_offsetted_rings(OffsettedRings const & offsetted_rings)279     inline void map_offsetted_rings(OffsettedRings const& offsetted_rings)
280     {
281         for(typename boost::range_iterator<OffsettedRings const>::type it
282                 = boost::begin(offsetted_rings); it != boost::end(offsetted_rings); ++it)
283         {
284             if (it->discarded())
285             {
286                 m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(255,0,0);stroke-width:2");
287             }
288             else
289             {
290                 m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(0,0,255);stroke-width:2");
291             }
292         }
293     }
294 
295 
296     SvgMapper& m_mapper;
297     Box m_alternate_box;
298     bool m_zoom;
299 
300 };
301 
302 template <typename Point>
303 class buffer_svg_mapper
304 {
305 public :
306 
buffer_svg_mapper(std::string const & casename)307     buffer_svg_mapper(std::string const& casename)
308         : m_casename(casename)
309         , m_zoom(false)
310     {
311         bg::assign_inverse(m_alternate_box);
312     }
313 
314     template <typename Mapper, typename Visitor, typename Envelope>
prepare(Mapper & mapper,Visitor & visitor,Envelope const & envelope,double box_buffer_distance)315     void prepare(Mapper& mapper, Visitor& visitor, Envelope const& envelope, double box_buffer_distance)
316     {
317 #ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX
318         // Create a zoomed-in view
319         bg::model::box<Point> alternate_box;
320         bg::read_wkt(BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX, alternate_box);
321         mapper.add(alternate_box);
322 
323         // Take care non-visible elements are skipped
324         visitor.set_alternate_box(alternate_box);
325         set_alternate_box(alternate_box);
326 #else
327         bg::model::box<Point> box = envelope;
328         bg::buffer(box, box, box_buffer_distance);
329         mapper.add(box);
330 #endif
331 
332         boost::ignore_unused(visitor);
333     }
334 
set_alternate_box(bg::model::box<Point> const & box)335     void set_alternate_box(bg::model::box<Point> const& box)
336     {
337         m_alternate_box = box;
338         m_zoom = true;
339     }
340 
341     template <typename Mapper, typename Geometry, typename GeometryBuffer>
map_input_output(Mapper & mapper,Geometry const & geometry,GeometryBuffer const & buffered,bool negative)342     void map_input_output(Mapper& mapper, Geometry const& geometry,
343             GeometryBuffer const& buffered, bool negative)
344     {
345         bool const areal = boost::is_same
346             <
347                 typename bg::tag_cast
348                     <
349                         typename bg::tag<Geometry>::type,
350                         bg::areal_tag
351                     >::type, bg::areal_tag
352             >::type::value;
353 
354         if (m_zoom)
355         {
356             map_io_zoomed(mapper, geometry, buffered, negative, areal);
357         }
358         else
359         {
360             map_io(mapper, geometry, buffered, negative, areal);
361         }
362     }
363 
364     template <typename Mapper, typename Geometry, typename Strategy, typename RescalePolicy>
map_self_ips(Mapper & mapper,Geometry const & geometry,Strategy const & strategy,RescalePolicy const & rescale_policy)365     void map_self_ips(Mapper& mapper, Geometry const& geometry, Strategy const& strategy, RescalePolicy const& rescale_policy)
366     {
367         typedef bg::detail::overlay::turn_info
368         <
369             Point,
370             typename bg::detail::segment_ratio_type<Point, RescalePolicy>::type
371         > turn_info;
372 
373         std::vector<turn_info> turns;
374 
375         bg::detail::self_get_turn_points::no_interrupt_policy policy;
376         bg::self_turns
377             <
378                 bg::detail::overlay::assign_null_policy
379             >(geometry, strategy, rescale_policy, turns, policy);
380 
381         BOOST_FOREACH(turn_info const& turn, turns)
382         {
383             mapper.map(turn.point, "fill:rgb(255,128,0);stroke:rgb(0,0,100);stroke-width:1", 3);
384         }
385     }
386 
387 private :
388 
389     template <typename Mapper, typename Geometry, typename GeometryBuffer>
map_io(Mapper & mapper,Geometry const & geometry,GeometryBuffer const & buffered,bool negative,bool areal)390     void map_io(Mapper& mapper, Geometry const& geometry,
391             GeometryBuffer const& buffered, bool negative, bool areal)
392     {
393         // Map input geometry in green
394         if (areal)
395         {
396             mapper.map(geometry, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,64,0);stroke-width:2");
397         }
398         else
399         {
400             // TODO: clip input points/linestring
401             mapper.map(geometry, "opacity:0.5;stroke:rgb(0,128,0);stroke-width:10");
402         }
403 
404         {
405             // Map buffer in yellow (inflate) and with orange-dots (deflate)
406             std::string style = negative
407                 ? "opacity:0.4;fill:rgb(255,255,192);stroke:rgb(255,128,0);stroke-width:3"
408                 : "opacity:0.4;fill:rgb(255,255,128);stroke:rgb(0,0,0);stroke-width:3";
409 
410             mapper.map(buffered, style);
411         }
412     }
413 
414     template <typename Mapper, typename Geometry, typename GeometryBuffer>
map_io_zoomed(Mapper & mapper,Geometry const & geometry,GeometryBuffer const & buffered,bool negative,bool areal)415     void map_io_zoomed(Mapper& mapper, Geometry const& geometry,
416             GeometryBuffer const& buffered, bool negative, bool areal)
417     {
418         // Map input geometry in green
419         if (areal)
420         {
421             // Assuming input is areal
422             GeometryBuffer clipped;
423 // TODO: the next line does NOT compile for multi-point, TODO: implement that line
424 //            bg::intersection(geometry, m_alternate_box, clipped);
425             mapper.map(clipped, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,64,0);stroke-width:2");
426         }
427         else
428         {
429             // TODO: clip input (multi)point/linestring
430             mapper.map(geometry, "opacity:0.5;stroke:rgb(0,128,0);stroke-width:10");
431         }
432 
433         {
434             // Map buffer in yellow (inflate) and with orange-dots (deflate)
435             std::string style = negative
436                 ? "opacity:0.4;fill:rgb(255,255,192);stroke:rgb(255,128,0);stroke-width:3"
437                 : "opacity:0.4;fill:rgb(255,255,128);stroke:rgb(0,0,0);stroke-width:3";
438 
439             try
440             {
441                 // Clip output multi-polygon with box
442                 GeometryBuffer clipped;
443                 bg::intersection(buffered, m_alternate_box, clipped);
444                 mapper.map(clipped, style);
445             }
446             catch (...)
447             {
448                 std::cout << "Error for buffered output " << m_casename << std::endl;
449             }
450         }
451     }
452 
453     bg::model::box<Point> m_alternate_box;
454     bool m_zoom;
455     std::string m_casename;
456 };
457 
458 
459 #endif
460