]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/geometry/test/algorithms/buffer/test_buffer_svg.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / libs / geometry / test / algorithms / buffer / test_buffer_svg.hpp
CommitLineData
7c673cae
FG
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>
b32b8144 28#include <boost/geometry/algorithms/intersection.hpp>
7c673cae
FG
29
30
31inline 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
46template <typename SvgMapper, typename Box>
47class svg_visitor
48{
49public :
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
84private :
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
b32b8144
FG
166 << " nxt " << it->operations[0].enriched.get_next_turn_index()
167 << "/" << it->operations[1].enriched.get_next_turn_index()
7c673cae
FG
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
7c673cae 180 << (it->count_within_near_offsetted > 0 ? "n" : "")
7c673cae
FG
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 }
92f5a8d4 231#if 0 // Does not compile (SVG is not enabled by default)
7c673cae
FG
232 if (m_zoom && bg::disjoint(corner, m_alternate_box))
233 {
234 continue;
235 }
92f5a8d4 236#endif
7c673cae
FG
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;
b32b8144
FG
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;
7c673cae
FG
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
325template <typename Point>
326class buffer_svg_mapper
327{
328public :
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
b32b8144
FG
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)
7c673cae
FG
389 {
390 typedef bg::detail::overlay::turn_info
391 <
392 Point,
92f5a8d4 393 typename bg::detail::segment_ratio_type<Point, RescalePolicy>::type
7c673cae
FG
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
b32b8144 402 >(geometry, strategy, rescale_policy, turns, policy);
7c673cae
FG
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
410private :
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