test_buffer_svg.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Unit Test Helper
  3. // Copyright (c) 2010-2015 Barend Gehrels, Amsterdam, the Netherlands.
  4. // Use, modification and distribution is subject to the Boost Software License,
  5. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. #ifndef BOOST_GEOMETRY_TEST_BUFFER_SVG_HPP
  8. #define BOOST_GEOMETRY_TEST_BUFFER_SVG_HPP
  9. #include <fstream>
  10. #include <sstream>
  11. // Uncomment next lines if you want to have a zoomed view
  12. //#define BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX
  13. // If possible define box before including this unit with the right view
  14. #ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX
  15. # ifndef BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX
  16. # define BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX "BOX(0 0,100 100)"
  17. # endif
  18. #endif
  19. #include <boost/foreach.hpp>
  20. #include <boost/geometry/io/svg/svg_mapper.hpp>
  21. #include <boost/geometry/algorithms/intersection.hpp>
  22. inline char piece_type_char(bg::strategy::buffer::piece_type const& type)
  23. {
  24. using namespace bg::strategy::buffer;
  25. switch(type)
  26. {
  27. case buffered_segment : return 's';
  28. case buffered_join : return 'j';
  29. case buffered_round_end : return 'r';
  30. case buffered_flat_end : return 'f';
  31. case buffered_point : return 'p';
  32. case buffered_concave : return 'c';
  33. default : return '?';
  34. }
  35. }
  36. template <typename SvgMapper, typename Box>
  37. class svg_visitor
  38. {
  39. public :
  40. svg_visitor(SvgMapper& mapper)
  41. : m_mapper(mapper)
  42. , m_zoom(false)
  43. {
  44. bg::assign_inverse(m_alternate_box);
  45. }
  46. void set_alternate_box(Box const& box)
  47. {
  48. m_alternate_box = box;
  49. m_zoom = true;
  50. }
  51. template <typename PieceCollection>
  52. inline void apply(PieceCollection const& collection, int phase)
  53. {
  54. // Comment next return if you want to see pieces, turns, etc.
  55. return;
  56. if(phase == 0)
  57. {
  58. map_pieces(collection.m_pieces, collection.offsetted_rings, true, true);
  59. }
  60. if (phase == 1)
  61. {
  62. map_turns(collection.m_turns, true, false);
  63. }
  64. if (phase == 2 && ! m_zoom)
  65. {
  66. // map_traversed_rings(collection.traversed_rings);
  67. // map_offsetted_rings(collection.offsetted_rings);
  68. }
  69. }
  70. private :
  71. class si
  72. {
  73. private :
  74. bg::segment_identifier m_id;
  75. public :
  76. inline si(bg::segment_identifier const& id)
  77. : m_id(id)
  78. {}
  79. template <typename Char, typename Traits>
  80. inline friend std::basic_ostream<Char, Traits>& operator<<(
  81. std::basic_ostream<Char, Traits>& os,
  82. si const& s)
  83. {
  84. os << s.m_id.multi_index << "." << s.m_id.segment_index;
  85. return os;
  86. }
  87. };
  88. template <typename Turns>
  89. inline void map_turns(Turns const& turns, bool label_good_turns, bool label_wrong_turns)
  90. {
  91. namespace bgdb = boost::geometry::detail::buffer;
  92. typedef typename boost::range_value<Turns const>::type turn_type;
  93. typedef typename turn_type::point_type point_type;
  94. typedef typename turn_type::robust_point_type robust_point_type;
  95. std::map<robust_point_type, int, bg::less<robust_point_type> > offsets;
  96. for (typename boost::range_iterator<Turns const>::type it =
  97. boost::begin(turns); it != boost::end(turns); ++it)
  98. {
  99. if (m_zoom && bg::disjoint(it->point, m_alternate_box))
  100. {
  101. continue;
  102. }
  103. bool is_good = true;
  104. char color = 'g';
  105. std::string fill = "fill:rgb(0,255,0);";
  106. switch(it->location)
  107. {
  108. case bgdb::inside_buffer :
  109. fill = "fill:rgb(255,0,0);";
  110. color = 'r';
  111. is_good = false;
  112. break;
  113. case bgdb::location_discard :
  114. fill = "fill:rgb(0,0,255);";
  115. color = 'b';
  116. is_good = false;
  117. break;
  118. default:
  119. ; // to avoid "enumeration value not handled" warning
  120. }
  121. if (it->blocked())
  122. {
  123. fill = "fill:rgb(128,128,128);";
  124. color = '-';
  125. is_good = false;
  126. }
  127. fill += "fill-opacity:0.7;";
  128. m_mapper.map(it->point, fill, 4);
  129. if ((label_good_turns && is_good) || (label_wrong_turns && ! is_good))
  130. {
  131. std::ostringstream out;
  132. out << it->turn_index;
  133. if (it->cluster_id >= 0)
  134. {
  135. out << " (" << it->cluster_id << ")";
  136. }
  137. out
  138. << " " << it->operations[0].piece_index << "/" << it->operations[1].piece_index
  139. << " " << si(it->operations[0].seg_id) << "/" << si(it->operations[1].seg_id)
  140. // If you want to see travel information
  141. << std::endl
  142. << " nxt " << it->operations[0].enriched.get_next_turn_index()
  143. << "/" << it->operations[1].enriched.get_next_turn_index()
  144. //<< " frac " << it->operations[0].fraction
  145. // If you want to see (robust)point coordinates (e.g. to find duplicates)
  146. << std::endl << std::setprecision(16) << bg::wkt(it->point)
  147. << std::endl << bg::wkt(it->robust_point)
  148. << std::endl;
  149. out << " " << bg::method_char(it->method)
  150. << ":" << bg::operation_char(it->operations[0].operation)
  151. << "/" << bg::operation_char(it->operations[1].operation);
  152. out << " "
  153. << (it->count_on_offsetted > 0 ? "b" : "") // b: offsetted border
  154. << (it->count_within_near_offsetted > 0 ? "n" : "")
  155. << (it->count_within > 0 ? "w" : "")
  156. << (it->count_on_helper > 0 ? "h" : "")
  157. << (it->count_on_multi > 0 ? "m" : "")
  158. ;
  159. offsets[it->get_robust_point()] += 10;
  160. int offset = offsets[it->get_robust_point()];
  161. m_mapper.text(it->point, out.str(), "fill:rgb(0,0,0);font-family='Arial';font-size:9px;", 5, offset);
  162. offsets[it->get_robust_point()] += 25;
  163. }
  164. }
  165. }
  166. template <typename Pieces, typename OffsettedRings>
  167. inline void map_pieces(Pieces const& pieces,
  168. OffsettedRings const& offsetted_rings,
  169. bool do_pieces, bool do_indices)
  170. {
  171. typedef typename boost::range_value<Pieces const>::type piece_type;
  172. typedef typename boost::range_value<OffsettedRings const>::type ring_type;
  173. for(typename boost::range_iterator<Pieces const>::type it = boost::begin(pieces);
  174. it != boost::end(pieces);
  175. ++it)
  176. {
  177. const piece_type& piece = *it;
  178. bg::segment_identifier seg_id = piece.first_seg_id;
  179. if (seg_id.segment_index < 0)
  180. {
  181. continue;
  182. }
  183. ring_type corner;
  184. ring_type const& ring = offsetted_rings[seg_id.multi_index];
  185. std::copy(boost::begin(ring) + seg_id.segment_index,
  186. boost::begin(ring) + piece.last_segment_index,
  187. std::back_inserter(corner));
  188. std::copy(boost::begin(piece.helper_points),
  189. boost::end(piece.helper_points),
  190. std::back_inserter(corner));
  191. if (corner.empty())
  192. {
  193. continue;
  194. }
  195. #if 0 // Does not compile (SVG is not enabled by default)
  196. if (m_zoom && bg::disjoint(corner, m_alternate_box))
  197. {
  198. continue;
  199. }
  200. #endif
  201. if (m_zoom && do_pieces)
  202. {
  203. try
  204. {
  205. std::string style = "opacity:0.3;stroke:rgb(0,0,0);stroke-width:1;";
  206. typedef typename bg::point_type<Box>::type point_type;
  207. bg::model::multi_polygon<bg::model::polygon<point_type> > clipped;
  208. bg::intersection(ring, m_alternate_box, clipped);
  209. m_mapper.map(clipped,
  210. piece.type == bg::strategy::buffer::buffered_segment
  211. ? style + "fill:rgb(255,128,0);"
  212. : style + "fill:rgb(255,0,0);");
  213. }
  214. catch (...)
  215. {
  216. std::cerr << "Error for piece " << piece.index << std::endl;
  217. }
  218. }
  219. else if (do_pieces)
  220. {
  221. std::string style = "opacity:0.3;stroke:rgb(0,0,0);stroke-width:1;";
  222. m_mapper.map(corner,
  223. piece.type == bg::strategy::buffer::buffered_segment
  224. ? style + "fill:rgb(255,128,0);"
  225. : style + "fill:rgb(255,0,0);");
  226. }
  227. if (do_indices)
  228. {
  229. // Label starting piece_index / segment_index
  230. typedef typename bg::point_type<ring_type>::type point_type;
  231. std::ostringstream out;
  232. out << piece.index
  233. << (piece.is_flat_start ? " FS" : "")
  234. << (piece.is_flat_end ? " FE" : "")
  235. << " (" << piece_type_char(piece.type) << ") "
  236. << piece.first_seg_id.segment_index
  237. << ".." << piece.last_segment_index - 1;
  238. point_type label_point = bg::return_centroid<point_type>(corner);
  239. if ((piece.type == bg::strategy::buffer::buffered_concave
  240. || piece.type == bg::strategy::buffer::buffered_flat_end)
  241. && corner.size() >= 2u)
  242. {
  243. bg::set<0>(label_point, (bg::get<0>(corner[0]) + bg::get<0>(corner[1])) / 2.0);
  244. bg::set<1>(label_point, (bg::get<1>(corner[0]) + bg::get<1>(corner[1])) / 2.0);
  245. }
  246. m_mapper.text(label_point, out.str(), "fill:rgb(255,0,0);font-family='Arial';font-size:10px;", 5, 5);
  247. }
  248. }
  249. }
  250. template <typename TraversedRings>
  251. inline void map_traversed_rings(TraversedRings const& traversed_rings)
  252. {
  253. for(typename boost::range_iterator<TraversedRings const>::type it
  254. = boost::begin(traversed_rings); it != boost::end(traversed_rings); ++it)
  255. {
  256. m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(0,255,0);stroke-width:2");
  257. }
  258. }
  259. template <typename OffsettedRings>
  260. inline void map_offsetted_rings(OffsettedRings const& offsetted_rings)
  261. {
  262. for(typename boost::range_iterator<OffsettedRings const>::type it
  263. = boost::begin(offsetted_rings); it != boost::end(offsetted_rings); ++it)
  264. {
  265. if (it->discarded())
  266. {
  267. m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(255,0,0);stroke-width:2");
  268. }
  269. else
  270. {
  271. m_mapper.map(*it, "opacity:0.4;fill:none;stroke:rgb(0,0,255);stroke-width:2");
  272. }
  273. }
  274. }
  275. SvgMapper& m_mapper;
  276. Box m_alternate_box;
  277. bool m_zoom;
  278. };
  279. template <typename Point>
  280. class buffer_svg_mapper
  281. {
  282. public :
  283. buffer_svg_mapper(std::string const& casename)
  284. : m_casename(casename)
  285. , m_zoom(false)
  286. {
  287. bg::assign_inverse(m_alternate_box);
  288. }
  289. template <typename Mapper, typename Visitor, typename Envelope>
  290. void prepare(Mapper& mapper, Visitor& visitor, Envelope const& envelope, double box_buffer_distance)
  291. {
  292. #ifdef BOOST_GEOMETRY_BUFFER_TEST_SVG_USE_ALTERNATE_BOX
  293. // Create a zoomed-in view
  294. bg::model::box<Point> alternate_box;
  295. bg::read_wkt(BOOST_GEOMETRY_BUFFER_TEST_SVG_ALTERNATE_BOX, alternate_box);
  296. mapper.add(alternate_box);
  297. // Take care non-visible elements are skipped
  298. visitor.set_alternate_box(alternate_box);
  299. set_alternate_box(alternate_box);
  300. #else
  301. bg::model::box<Point> box = envelope;
  302. bg::buffer(box, box, box_buffer_distance);
  303. mapper.add(box);
  304. #endif
  305. boost::ignore_unused(visitor);
  306. }
  307. void set_alternate_box(bg::model::box<Point> const& box)
  308. {
  309. m_alternate_box = box;
  310. m_zoom = true;
  311. }
  312. template <typename Mapper, typename Geometry, typename GeometryBuffer>
  313. void map_input_output(Mapper& mapper, Geometry const& geometry,
  314. GeometryBuffer const& buffered, bool negative)
  315. {
  316. bool const areal = boost::is_same
  317. <
  318. typename bg::tag_cast
  319. <
  320. typename bg::tag<Geometry>::type,
  321. bg::areal_tag
  322. >::type, bg::areal_tag
  323. >::type::value;
  324. if (m_zoom)
  325. {
  326. map_io_zoomed(mapper, geometry, buffered, negative, areal);
  327. }
  328. else
  329. {
  330. map_io(mapper, geometry, buffered, negative, areal);
  331. }
  332. }
  333. template <typename Mapper, typename Geometry, typename Strategy, typename RescalePolicy>
  334. void map_self_ips(Mapper& mapper, Geometry const& geometry, Strategy const& strategy, RescalePolicy const& rescale_policy)
  335. {
  336. typedef bg::detail::overlay::turn_info
  337. <
  338. Point,
  339. typename bg::detail::segment_ratio_type<Point, RescalePolicy>::type
  340. > turn_info;
  341. std::vector<turn_info> turns;
  342. bg::detail::self_get_turn_points::no_interrupt_policy policy;
  343. bg::self_turns
  344. <
  345. bg::detail::overlay::assign_null_policy
  346. >(geometry, strategy, rescale_policy, turns, policy);
  347. BOOST_FOREACH(turn_info const& turn, turns)
  348. {
  349. mapper.map(turn.point, "fill:rgb(255,128,0);stroke:rgb(0,0,100);stroke-width:1", 3);
  350. }
  351. }
  352. private :
  353. template <typename Mapper, typename Geometry, typename GeometryBuffer>
  354. void map_io(Mapper& mapper, Geometry const& geometry,
  355. GeometryBuffer const& buffered, bool negative, bool areal)
  356. {
  357. // Map input geometry in green
  358. if (areal)
  359. {
  360. mapper.map(geometry, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,64,0);stroke-width:2");
  361. }
  362. else
  363. {
  364. // TODO: clip input points/linestring
  365. mapper.map(geometry, "opacity:0.5;stroke:rgb(0,128,0);stroke-width:10");
  366. }
  367. {
  368. // Map buffer in yellow (inflate) and with orange-dots (deflate)
  369. std::string style = negative
  370. ? "opacity:0.4;fill:rgb(255,255,192);stroke:rgb(255,128,0);stroke-width:3"
  371. : "opacity:0.4;fill:rgb(255,255,128);stroke:rgb(0,0,0);stroke-width:3";
  372. mapper.map(buffered, style);
  373. }
  374. }
  375. template <typename Mapper, typename Geometry, typename GeometryBuffer>
  376. void map_io_zoomed(Mapper& mapper, Geometry const& geometry,
  377. GeometryBuffer const& buffered, bool negative, bool areal)
  378. {
  379. // Map input geometry in green
  380. if (areal)
  381. {
  382. // Assuming input is areal
  383. GeometryBuffer clipped;
  384. // TODO: the next line does NOT compile for multi-point, TODO: implement that line
  385. // bg::intersection(geometry, m_alternate_box, clipped);
  386. mapper.map(clipped, "opacity:0.5;fill:rgb(0,128,0);stroke:rgb(0,64,0);stroke-width:2");
  387. }
  388. else
  389. {
  390. // TODO: clip input (multi)point/linestring
  391. mapper.map(geometry, "opacity:0.5;stroke:rgb(0,128,0);stroke-width:10");
  392. }
  393. {
  394. // Map buffer in yellow (inflate) and with orange-dots (deflate)
  395. std::string style = negative
  396. ? "opacity:0.4;fill:rgb(255,255,192);stroke:rgb(255,128,0);stroke-width:3"
  397. : "opacity:0.4;fill:rgb(255,255,128);stroke:rgb(0,0,0);stroke-width:3";
  398. try
  399. {
  400. // Clip output multi-polygon with box
  401. GeometryBuffer clipped;
  402. bg::intersection(buffered, m_alternate_box, clipped);
  403. mapper.map(clipped, style);
  404. }
  405. catch (...)
  406. {
  407. std::cout << "Error for buffered output " << m_casename << std::endl;
  408. }
  409. }
  410. }
  411. bg::model::box<Point> m_alternate_box;
  412. bool m_zoom;
  413. std::string m_casename;
  414. };
  415. #endif