overlay.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Unit Test
  3. // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands.
  4. // This file was modified by Oracle on 2017.
  5. // Modifications copyright (c) 2017, Oracle and/or its affiliates.
  6. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  7. // Use, modification and distribution is subject to the Boost Software License,
  8. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. #include <iostream>
  11. #include <iomanip>
  12. #include <fstream>
  13. #include <sstream>
  14. #include <string>
  15. #include <boost/type_traits/is_same.hpp>
  16. #if defined(TEST_WITH_SVG)
  17. # include <boost/geometry/io/svg/svg_mapper.hpp>
  18. #endif
  19. #include <geometry_test_common.hpp>
  20. #include <algorithms/check_validity.hpp>
  21. #include <boost/geometry.hpp>
  22. #include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
  23. #include <boost/geometry/geometries/geometries.hpp>
  24. //#include <boost/geometry/extensions/algorithms/inverse.hpp>
  25. #if defined(TEST_WITH_SVG)
  26. # include <boost/geometry/io/svg/svg_mapper.hpp>
  27. #endif
  28. #include "multi_overlay_cases.hpp"
  29. #if defined(TEST_WITH_SVG)
  30. template <typename Mapper>
  31. struct map_visitor
  32. {
  33. map_visitor(Mapper& mapper)
  34. : m_mapper(mapper)
  35. , m_traverse_seq(0)
  36. , m_do_output(true)
  37. {}
  38. void print(char const* header)
  39. {}
  40. template <typename Turns>
  41. void print(char const* header, Turns const& turns, int turn_index)
  42. {
  43. std::string style = "fill:rgb(0,0,0);font-family:Arial;font-size:6px";
  44. stream(turns, turns[turn_index], turns[turn_index].operations[0], header, style);
  45. }
  46. template <typename Turns>
  47. void print(char const* header, Turns const& turns, int turn_index, int op_index)
  48. {
  49. std::string style = "fill:rgb(0,0,0);font-family:Arial;font-size:6px";
  50. stream(turns, turns[turn_index], turns[turn_index].operations[op_index], header, style);
  51. }
  52. template <typename Turns>
  53. void visit_turns(int phase, Turns const& turns)
  54. {
  55. typedef typename boost::range_value<Turns>::type turn_type;
  56. int index = 0;
  57. BOOST_FOREACH(turn_type const& turn, turns)
  58. {
  59. switch (phase)
  60. {
  61. case 1 :
  62. m_mapper.map(turn.point, "fill:rgb(255,128,0);"
  63. "stroke:rgb(0,0,0);stroke-width:1", 3);
  64. break;
  65. case 11 :
  66. m_mapper.map(turn.point, "fill:rgb(92,255,0);" // Greenish
  67. "stroke:rgb(0,0,0);stroke-width:1", 3);
  68. break;
  69. case 21 :
  70. m_mapper.map(turn.point, "fill:rgb(0,128,255);" // Blueish
  71. "stroke:rgb(0,0,0);stroke-width:1", 3);
  72. break;
  73. case 3 :
  74. label_turn(index, turn);
  75. break;
  76. }
  77. index++;
  78. }
  79. }
  80. template <typename Turns, typename Turn, typename Operation>
  81. std::string stream_turn_index(Turns const& turns, Turn const& turn, Operation const& op)
  82. {
  83. std::ostringstream out;
  84. if (turn.cluster_id >= 0)
  85. {
  86. out << "cl=" << turn.cluster_id << " ";
  87. }
  88. // Because turn index is unknown here, and still useful for debugging,
  89. std::size_t index = 0;
  90. for (typename Turns::const_iterator it = turns.begin();
  91. it != turns.end(); ++it, ++index)
  92. {
  93. Turn const& t = *it;
  94. if (&t == &turn)
  95. {
  96. out << index;
  97. break;
  98. }
  99. }
  100. if (&op == &turn.operations[0]) { out << "[0]"; }
  101. if (&op == &turn.operations[1]) { out << "[1]"; }
  102. return out.str();
  103. }
  104. template <typename Clusters, typename Turns>
  105. void visit_clusters(Clusters const& clusters, Turns const& turns)
  106. {
  107. typedef typename boost::range_value<Turns>::type turn_type;
  108. int index = 0;
  109. BOOST_FOREACH(turn_type const& turn, turns)
  110. {
  111. if (turn.cluster_id >= 0)
  112. {
  113. std::cout << " TURN: " << index << " part of cluster " << turn.cluster_id << std::endl;
  114. }
  115. index++;
  116. }
  117. for (typename Clusters::const_iterator it = clusters.begin(); it != clusters.end(); ++it)
  118. {
  119. std::cout << " CLUSTER " << it->first << ": ";
  120. for (typename std::set<bg::signed_size_type>::const_iterator sit
  121. = it->second.turn_indices.begin();
  122. sit != it->second.turn_indices.end(); ++sit)
  123. {
  124. std::cout << " " << *sit;
  125. }
  126. std::cout << std::endl;
  127. }
  128. std::cout << std::endl;
  129. }
  130. template <typename Turns, typename Turn, typename Operation>
  131. void visit_traverse(Turns const& turns, Turn const& turn, Operation const& op, const std::string& header)
  132. {
  133. typedef typename boost::range_value<Turns>::type turn_type;
  134. if (! m_do_output)
  135. {
  136. return;
  137. }
  138. std::cout << "Visit turn " << stream_turn_index(turns, turn, op)
  139. << " " << bg::operation_char(turn.operations[0].operation)
  140. << bg::operation_char(turn.operations[1].operation)
  141. << " (" << bg::operation_char(op.operation) << ")"
  142. << " " << header << std::endl;
  143. // Uncomment for more detailed debug info in SVG on traversal
  144. std::string style
  145. = header == "Visit" ? "fill:rgb(80,80,80)" : "fill:rgb(0,0,0)";
  146. style += ";font-family:Arial;font-size:6px";
  147. stream(turns, turn, op, header.substr(0, 1), style);
  148. }
  149. template <typename Turns, typename Turn, typename Operation>
  150. void visit_traverse_reject(Turns const& turns, Turn const& turn, Operation const& op,
  151. bg::detail::overlay::traverse_error_type error)
  152. {
  153. if (! m_do_output)
  154. {
  155. return;
  156. }
  157. std::cout << "Reject turn " << stream_turn_index(turns, turn, op)
  158. << bg::operation_char(turn.operations[0].operation)
  159. << bg::operation_char(turn.operations[1].operation)
  160. << " (" << bg::operation_char(op.operation) << ")"
  161. << " " << bg::detail::overlay::traverse_error_string(error) << std::endl;
  162. //return;
  163. std::string style = "fill:rgb(255,0,0);font-family:Arial;font-size:7px";
  164. stream(turns, turn, op, bg::detail::overlay::traverse_error_string(error), style);
  165. m_do_output = false;
  166. }
  167. template <typename Turns, typename Turn, typename Operation>
  168. void visit_traverse_select_turn_from_cluster(Turns const& turns, Turn const& turn, Operation const& op)
  169. {
  170. std::cout << "Visit turn from cluster " << stream_turn_index(turns, turn, op)
  171. << " " << bg::operation_char(turn.operations[0].operation)
  172. << bg::operation_char(turn.operations[1].operation)
  173. << " (" << bg::operation_char(op.operation) << ")"
  174. << std::endl;
  175. return;
  176. }
  177. template <typename Turns, typename Turn, typename Operation>
  178. void stream(Turns const& turns, Turn const& turn, Operation const& op, const std::string& header, const std::string& style)
  179. {
  180. std::ostringstream out;
  181. out << m_traverse_seq++ << " " << header
  182. << " " << stream_turn_index(turns, turn, op);
  183. out << " " << bg::visited_char(op.visited);
  184. add_text(turn, out.str(), style);
  185. }
  186. template <typename Turn>
  187. bool label_operation(Turn const& turn, int index, std::ostream& os)
  188. {
  189. os << bg::operation_char(turn.operations[index].operation);
  190. bool result = false;
  191. if (! turn.discarded)
  192. {
  193. if (turn.operations[index].enriched.next_ip_index != -1)
  194. {
  195. os << "->" << turn.operations[index].enriched.next_ip_index;
  196. if (turn.operations[index].enriched.next_ip_index != -1)
  197. {
  198. result = true;
  199. }
  200. }
  201. else
  202. {
  203. os << "->" << turn.operations[index].enriched.travels_to_ip_index;
  204. if (turn.operations[index].enriched.travels_to_ip_index != -1)
  205. {
  206. result = true;
  207. }
  208. }
  209. os << " {" << turn.operations[index].enriched.region_id
  210. << (turn.operations[index].enriched.isolated ? " ISO" : "")
  211. << "}";
  212. if (! turn.operations[index].enriched.startable)
  213. {
  214. os << "$";
  215. }
  216. }
  217. return result;
  218. }
  219. template <typename Turn>
  220. void label_turn(int index, Turn const& turn)
  221. {
  222. std::ostringstream out;
  223. out << index << " ";
  224. if (turn.cluster_id != -1)
  225. {
  226. out << " c=" << turn.cluster_id << " ";
  227. }
  228. bool lab1 = label_operation(turn, 0, out);
  229. out << " / ";
  230. bool lab2 = label_operation(turn, 1, out);
  231. if (turn.discarded)
  232. {
  233. out << "!";
  234. }
  235. if (turn.has_colocated_both)
  236. {
  237. out << "+";
  238. }
  239. bool const self_turn = bg::detail::overlay::is_self_turn<bg::overlay_union>(turn);
  240. if (self_turn)
  241. {
  242. out << "@";
  243. }
  244. std::string font8 = "font-family:Arial;font-size:6px";
  245. std::string font6 = "font-family:Arial;font-size:4px";
  246. std::string style = "fill:rgb(0,0,255);" + font8;
  247. if (self_turn)
  248. {
  249. if (turn.discarded)
  250. {
  251. style = "fill:rgb(128,28,128);" + font6;
  252. }
  253. else
  254. {
  255. style = "fill:rgb(255,0,255);" + font8;
  256. }
  257. }
  258. else if (turn.discarded)
  259. {
  260. style = "fill:rgb(92,92,92);" + font6;
  261. }
  262. else if (turn.cluster_id != -1)
  263. {
  264. style = "fill:rgb(0,0,255);" + font8;
  265. }
  266. else if (! lab1 || ! lab2)
  267. {
  268. style = "fill:rgb(0,0,255);" + font6;
  269. }
  270. add_text(turn, out.str(), style);
  271. }
  272. template <typename Turn>
  273. void add_text(Turn const& turn, std::string const& text, std::string const& style)
  274. {
  275. int const margin = 5;
  276. int const lineheight = 6;
  277. double const half = 0.5;
  278. double const ten = 10;
  279. // Map characteristics
  280. // Create a rounded off point
  281. std::pair<int, int> p
  282. = std::make_pair(
  283. boost::numeric_cast<int>(half
  284. + ten * bg::get<0>(turn.point)),
  285. boost::numeric_cast<int>(half
  286. + ten * bg::get<1>(turn.point))
  287. );
  288. m_mapper.text(turn.point, text, style, margin, m_offsets[p], lineheight);
  289. m_offsets[p] += lineheight;
  290. }
  291. Mapper& m_mapper;
  292. std::map<std::pair<int, int>, int> m_offsets;
  293. int m_traverse_seq;
  294. bool m_do_output;
  295. };
  296. #endif
  297. template <typename Geometry, bg::overlay_type OverlayType>
  298. void test_overlay(std::string const& caseid,
  299. std::string const& wkt1, std::string const& wkt2,
  300. double expected_area,
  301. std::size_t expected_clip_count,
  302. std::size_t expected_hole_count)
  303. {
  304. Geometry g1;
  305. bg::read_wkt(wkt1, g1);
  306. Geometry g2;
  307. bg::read_wkt(wkt2, g2);
  308. // Reverse if necessary
  309. bg::correct(g1);
  310. bg::correct(g2);
  311. #if defined(TEST_WITH_SVG)
  312. bool const ccw = bg::point_order<Geometry>::value == bg::counterclockwise;
  313. bool const open = bg::closure<Geometry>::value == bg::open;
  314. std::ostringstream filename;
  315. filename << "overlay"
  316. << "_" << caseid
  317. << "_" << string_from_type<typename bg::coordinate_type<Geometry>::type>::name()
  318. << (ccw ? "_ccw" : "")
  319. << (open ? "_open" : "")
  320. #if defined(BOOST_GEOMETRY_USE_RESCALING)
  321. << "_rescaled"
  322. #endif
  323. << ".svg";
  324. std::ofstream svg(filename.str().c_str());
  325. typedef bg::svg_mapper<typename bg::point_type<Geometry>::type> svg_mapper;
  326. svg_mapper mapper(svg, 500, 500);
  327. mapper.add(g1);
  328. mapper.add(g2);
  329. // Input shapes in green (src=0) / blue (src=1)
  330. mapper.map(g1, "fill-opacity:0.5;fill:rgb(153,204,0);"
  331. "stroke:rgb(153,204,0);stroke-width:3");
  332. mapper.map(g2, "fill-opacity:0.3;fill:rgb(51,51,153);"
  333. "stroke:rgb(51,51,153);stroke-width:3");
  334. #endif
  335. typedef typename boost::range_value<Geometry>::type geometry_out;
  336. typedef bg::detail::overlay::overlay
  337. <
  338. Geometry, Geometry,
  339. bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value,
  340. OverlayType == bg::overlay_difference
  341. ? ! bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value
  342. : bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value,
  343. bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value,
  344. geometry_out,
  345. OverlayType
  346. > overlay;
  347. typedef typename bg::strategy::intersection::services::default_strategy
  348. <
  349. typename bg::cs_tag<Geometry>::type
  350. >::type strategy_type;
  351. strategy_type strategy;
  352. typedef typename bg::rescale_overlay_policy_type
  353. <
  354. Geometry,
  355. Geometry
  356. >::type rescale_policy_type;
  357. rescale_policy_type robust_policy
  358. = bg::get_rescale_policy<rescale_policy_type>(g1, g2);
  359. #if defined(TEST_WITH_SVG)
  360. map_visitor<svg_mapper> visitor(mapper);
  361. #else
  362. bg::detail::overlay::overlay_null_visitor visitor;
  363. #endif
  364. Geometry result;
  365. overlay::apply(g1, g2, robust_policy, std::back_inserter(result),
  366. strategy, visitor);
  367. std::string message;
  368. bool const valid = check_validity<Geometry>::apply(result, caseid, g1, g2, message);
  369. BOOST_CHECK_MESSAGE(valid,
  370. "overlay: " << caseid << " not valid: " << message
  371. << " type: " << (type_for_assert_message<Geometry, Geometry>()));
  372. BOOST_CHECK_CLOSE(bg::area(result), expected_area, 0.001);
  373. BOOST_CHECK_MESSAGE((bg::num_interior_rings(result) == expected_hole_count),
  374. caseid
  375. << " hole count: detected: " << bg::num_interior_rings(result)
  376. << " expected: " << expected_hole_count);
  377. BOOST_CHECK_MESSAGE((result.size() == expected_clip_count),
  378. caseid
  379. << " clip count: detected: " << result.size()
  380. << " expected: " << expected_clip_count);
  381. #if defined(TEST_WITH_SVG)
  382. mapper.map(result, "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);"
  383. "stroke:rgb(255,0,255);stroke-width:8");
  384. #endif
  385. }
  386. #define TEST_INTERSECTION(caseid, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_intersection>) \
  387. ( #caseid "_int", caseid[0], caseid[1], area, clips, holes)
  388. #define TEST_UNION(caseid, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_union>) \
  389. ( #caseid "_union", caseid[0], caseid[1], area, clips, holes)
  390. #define TEST_DIFFERENCE_A(caseid, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_difference>) \
  391. ( #caseid "_diff_a", caseid[0], caseid[1], area, clips, holes)
  392. #define TEST_DIFFERENCE_B(caseid, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_difference>) \
  393. ( #caseid "_diff_b", caseid[1], caseid[0], area, clips, holes)
  394. #define TEST_INTERSECTION_WITH(caseid, index1, index2, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_intersection>) \
  395. ( #caseid "_int_" #index1 "_" #index2, caseid[index1], caseid[index2], area, clips, holes)
  396. #define TEST_UNION_WITH(caseid, index1, index2, area, clips, holes) (test_overlay<multi_polygon, bg::overlay_union>) \
  397. ( #caseid "_union" #index1 "_" #index2, caseid[index1], caseid[index2], area, clips, holes)
  398. template <typename T, bool Clockwise>
  399. void test_all()
  400. {
  401. typedef bg::model::point<T, 2, bg::cs::cartesian> point_type;
  402. typedef bg::model::polygon<point_type, Clockwise> polygon;
  403. typedef bg::model::multi_polygon<polygon> multi_polygon;
  404. TEST_UNION(case_multi_simplex, 14.58, 1, 0);
  405. TEST_INTERSECTION(case_multi_simplex, 6.42, 2, 0);
  406. TEST_DIFFERENCE_A(case_multi_simplex, 5.58, 5, 0);
  407. TEST_DIFFERENCE_B(case_multi_simplex, 2.58, 4, 0);
  408. // Contains 5 clusters, needing immediate selection of next turn
  409. TEST_UNION_WITH(case_58_multi, 0, 3, 19.8333333, 2, 0);
  410. // Contains many clusters, needing to exclude u/u turns
  411. TEST_UNION(case_recursive_boxes_3, 56.5, 17, 6);
  412. // Contains 4 clusters, one of which having 4 turns
  413. TEST_UNION(case_recursive_boxes_7, 7.0, 2, 0);
  414. // Contains 5 clusters, needing immediate selection of next turn
  415. TEST_UNION(case_89_multi, 6.0, 1, 0);
  416. // Needs ux/next_turn_index==-1 to be filtered out
  417. TEST_INTERSECTION(case_77_multi, 9.0, 5, 0);
  418. TEST_UNION(case_101_multi, 22.25, 1, 3);
  419. TEST_INTERSECTION(case_101_multi, 4.75, 4, 0);
  420. TEST_INTERSECTION(case_recursive_boxes_11, 1.0, 2, 0);
  421. TEST_INTERSECTION(case_recursive_boxes_30, 6.0, 4, 0);
  422. TEST_UNION(case_recursive_boxes_4, 96.75, 1, 2);
  423. TEST_INTERSECTION_WITH(case_58_multi, 2, 6, 13.25, 1, 1);
  424. TEST_INTERSECTION_WITH(case_72_multi, 1, 2, 6.15, 3, 1);
  425. TEST_UNION(case_recursive_boxes_12, 6.0, 6, 0);
  426. TEST_UNION(case_recursive_boxes_13, 10.25, 3, 0);
  427. // std::cout
  428. // << " \""
  429. // << bg::inverse<multi_polygon>(case_65_multi[0], 1.0)
  430. // << "\"" << std::endl;
  431. }
  432. int test_main(int, char* [])
  433. {
  434. test_all<double, true>();
  435. // test_all<double, false>();
  436. return 0;
  437. }