test_overlay_p_q.hpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Unit Test
  3. //
  4. // Copyright (c) 2009-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. #ifndef BOOST_GEOMETRY_TEST_OVERLAY_P_Q_HPP
  9. #define BOOST_GEOMETRY_TEST_OVERLAY_P_Q_HPP
  10. #include <iostream>
  11. #include <fstream>
  12. #include <sstream>
  13. #include <iomanip>
  14. #include <boost/typeof/typeof.hpp>
  15. //#define BOOST_GEOMETRY_ROBUSTNESS_USE_DIFFERENCE
  16. #include <geometry_test_common.hpp>
  17. // For mixing int/float
  18. #if defined(_MSC_VER)
  19. #pragma warning( disable : 4244 )
  20. #pragma warning( disable : 4267 )
  21. #endif
  22. #include <boost/geometry.hpp>
  23. #include <boost/geometry/geometries/geometries.hpp>
  24. #include <boost/geometry/geometries/point_xy.hpp>
  25. #include <boost/geometry/io/svg/svg_mapper.hpp>
  26. #include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
  27. #include <boost/geometry/algorithms/intersects.hpp>
  28. #include <boost/geometry/algorithms/is_valid.hpp>
  29. #include <boost/geometry/algorithms/touches.hpp>
  30. struct p_q_settings
  31. {
  32. bool svg;
  33. bool also_difference;
  34. bool validity;
  35. bool wkt;
  36. bool verify_area;
  37. double tolerance;
  38. p_q_settings()
  39. : svg(false)
  40. , also_difference(false)
  41. , validity(false)
  42. , wkt(false)
  43. , verify_area(false)
  44. , tolerance(1.0e-3) // since rescaling to integer the tolerance should be less. Was originally 1.0e-6
  45. {}
  46. };
  47. template <typename Geometry>
  48. inline typename bg::default_area_result<Geometry>::type p_q_area(Geometry const& g)
  49. {
  50. try
  51. {
  52. return bg::area(g);
  53. }
  54. catch(bg::empty_input_exception const&)
  55. {
  56. return 0;
  57. }
  58. }
  59. struct verify_area
  60. {
  61. template <typename Iterator>
  62. static inline bool check_ring(Iterator begin, Iterator end)
  63. {
  64. for (Iterator it = begin; it != end; ++it)
  65. {
  66. double const area = bg::area(*it);
  67. if (fabs(area) < 0.01)
  68. {
  69. return false;
  70. }
  71. }
  72. return true;
  73. }
  74. template <typename Interiors>
  75. static inline bool check_rings(Interiors const& rings)
  76. {
  77. return check_ring(boost::begin(rings), boost::end(rings));
  78. }
  79. template <typename Iterator>
  80. static inline bool check_polys(Iterator begin, Iterator end)
  81. {
  82. for (Iterator it = begin; it != end; ++it)
  83. {
  84. // If necessary, exterior_ring can be checked too
  85. if (! check_rings(bg::interior_rings(*it)))
  86. {
  87. return false;
  88. }
  89. }
  90. return true;
  91. }
  92. template <typename Geometry>
  93. static inline bool apply(Geometry const& g)
  94. {
  95. return check_polys(boost::begin(g), boost::end(g));
  96. }
  97. };
  98. template <typename OutputType, typename CalculationType, typename G1, typename G2>
  99. static bool test_overlay_p_q(std::string const& caseid,
  100. G1 const& p, G2 const& q,
  101. p_q_settings const& settings)
  102. {
  103. bool result = true;
  104. typedef typename bg::coordinate_type<G1>::type coordinate_type;
  105. typedef typename bg::point_type<G1>::type point_type;
  106. bg::model::multi_polygon<OutputType> out_i, out_u, out_d1, out_d2;
  107. CalculationType area_p = p_q_area(p);
  108. CalculationType area_q = p_q_area(q);
  109. CalculationType area_d1 = 0, area_d2 = 0;
  110. bg::intersection(p, q, out_i);
  111. CalculationType area_i = p_q_area(out_i);
  112. bg::union_(p, q, out_u);
  113. CalculationType area_u = p_q_area(out_u);
  114. double sum = (area_p + area_q) - area_u - area_i;
  115. bool wrong = std::abs(sum) > settings.tolerance;
  116. if (settings.also_difference)
  117. {
  118. bg::difference(p, q, out_d1);
  119. bg::difference(q, p, out_d2);
  120. area_d1 = p_q_area(out_d1);
  121. area_d2 = p_q_area(out_d2);
  122. double sum_d1 = (area_u - area_q) - area_d1;
  123. double sum_d2 = (area_u - area_p) - area_d2;
  124. bool wrong_d1 = std::abs(sum_d1) > settings.tolerance;
  125. bool wrong_d2 = std::abs(sum_d2) > settings.tolerance;
  126. if (wrong_d1 || wrong_d2)
  127. {
  128. wrong = true;
  129. }
  130. }
  131. if (settings.validity)
  132. {
  133. std::string message;
  134. if (! bg::is_valid(out_u, message))
  135. {
  136. std::cout << "Union is not valid: " << message << std::endl;
  137. wrong = true;
  138. }
  139. if (! bg::is_valid(out_i, message))
  140. {
  141. std::cout << "Intersection is not valid: " << message << std::endl;
  142. wrong = true;
  143. }
  144. if (settings.also_difference)
  145. {
  146. if (! bg::is_valid(out_d1, message))
  147. {
  148. std::cout << "Difference (p-q) is not valid: " << message << std::endl;
  149. wrong = true;
  150. }
  151. if (! bg::is_valid(out_d2, message))
  152. {
  153. std::cout << "Difference (q-p) is not valid: " << message << std::endl;
  154. wrong = true;
  155. }
  156. }
  157. if (settings.verify_area && ! verify_area::apply(out_u))
  158. {
  159. std::cout << "Union/interior area incorrect" << std::endl;
  160. wrong = true;
  161. }
  162. if (settings.verify_area && ! verify_area::apply(out_i))
  163. {
  164. std::cout << "Intersection/interior area incorrect" << std::endl;
  165. wrong = true;
  166. }
  167. }
  168. if (true)
  169. {
  170. if ((area_i > 0 && bg::touches(p, q))
  171. || (area_i <= 0 && bg::intersects(p, q) && ! bg::touches(p, q)))
  172. {
  173. std::cout << "Wrong 'touch'! "
  174. << " Intersection area: " << area_i
  175. << " Touch gives: " << std::boolalpha << bg::touches(p, q)
  176. << std::endl;
  177. wrong = true;
  178. }
  179. }
  180. bool svg = settings.svg;
  181. if (wrong || settings.wkt)
  182. {
  183. if (wrong)
  184. {
  185. result = false;
  186. svg = true;
  187. }
  188. bg::unique(out_i);
  189. bg::unique(out_u);
  190. std::cout
  191. << "type: " << string_from_type<CalculationType>::name()
  192. << " id: " << caseid
  193. << " area i: " << area_i
  194. << " area u: " << area_u
  195. << " area p: " << area_p
  196. << " area q: " << area_q
  197. << " sum: " << sum;
  198. if (settings.also_difference)
  199. {
  200. std::cout
  201. << " area d1: " << area_d1
  202. << " area d2: " << area_d2;
  203. }
  204. std::cout
  205. << std::endl
  206. << std::setprecision(9)
  207. << " p: " << bg::wkt(p) << std::endl
  208. << " q: " << bg::wkt(q) << std::endl
  209. << " i: " << bg::wkt(out_i) << std::endl
  210. << " u: " << bg::wkt(out_u) << std::endl
  211. ;
  212. }
  213. if(svg)
  214. {
  215. std::ostringstream filename;
  216. filename << "overlay_" << caseid << "_"
  217. << string_from_type<coordinate_type>::name()
  218. << string_from_type<CalculationType>::name()
  219. << ".svg";
  220. std::ofstream svg(filename.str().c_str());
  221. bg::svg_mapper<point_type> mapper(svg, 500, 500);
  222. mapper.add(p);
  223. mapper.add(q);
  224. // Input shapes in green/blue
  225. mapper.map(p, "fill-opacity:0.5;fill:rgb(153,204,0);"
  226. "stroke:rgb(153,204,0);stroke-width:3");
  227. mapper.map(q, "fill-opacity:0.3;fill:rgb(51,51,153);"
  228. "stroke:rgb(51,51,153);stroke-width:3");
  229. if (settings.also_difference)
  230. {
  231. for (BOOST_AUTO(it, out_d1.begin()); it != out_d1.end(); ++it)
  232. {
  233. mapper.map(*it,
  234. "opacity:0.8;fill:none;stroke:rgb(255,128,0);stroke-width:4;stroke-dasharray:1,7;stroke-linecap:round");
  235. }
  236. for (BOOST_AUTO(it, out_d2.begin()); it != out_d2.end(); ++it)
  237. {
  238. mapper.map(*it,
  239. "opacity:0.8;fill:none;stroke:rgb(255,0,255);stroke-width:4;stroke-dasharray:1,7;stroke-linecap:round");
  240. }
  241. }
  242. else
  243. {
  244. mapper.map(out_i, "fill-opacity:0.1;stroke-opacity:0.4;fill:rgb(255,0,128);"
  245. "stroke:rgb(255,0,0);stroke-width:4");
  246. mapper.map(out_u, "fill-opacity:0.1;stroke-opacity:0.4;fill:rgb(255,0,0);"
  247. "stroke:rgb(255,0,255);stroke-width:4");
  248. }
  249. }
  250. return result;
  251. }
  252. #endif // BOOST_GEOMETRY_TEST_OVERLAY_P_Q_HPP