general_intersection_precision.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // Boost.Geometry
  2. // Robustness Test
  3. // Copyright (c) 2019 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. #include <iostream>
  8. #include <iomanip>
  9. #include <fstream>
  10. #include <sstream>
  11. #include <string>
  12. #include <boost/type_traits/is_same.hpp>
  13. #include <geometry_test_common.hpp>
  14. #include <boost/geometry.hpp>
  15. #include <boost/geometry/geometries/geometries.hpp>
  16. // Basic case. Union should deliver 22.0
  17. static std::string case_a[2] =
  18. {
  19. "MULTIPOLYGON(((0 0,0 4,2 4,2 3,4 3,4 0,0 0)))",
  20. "MULTIPOLYGON(((2 7,4 7,4 3,2 3,2 7)))"
  21. };
  22. // Case with an interior ring. Union should deliver 73.0
  23. static std::string case_b[2] =
  24. {
  25. "MULTIPOLYGON(((0 0,0 4,2 4,2 3,4 3,4 0,0 0)))",
  26. "MULTIPOLYGON(((-1 -1,-1 8,8 8,8 -1,-1 -1),(2 7,2 3,4 3,4 7,2 7)))"
  27. };
  28. static std::string case_c[2] =
  29. {
  30. "MULTIPOLYGON(((0 0,0 4,2 4,2 3,4 3,4 0,0 0)))",
  31. "MULTIPOLYGON(((1 1,0 1,0 3,1 3,1 1)))"
  32. };
  33. template <bg::overlay_type OverlayType, typename Geometry>
  34. bool test_overlay(std::string const& caseid,
  35. Geometry const& g1, Geometry const& g2,
  36. double expected_area,
  37. bool do_output)
  38. {
  39. typedef typename boost::range_value<Geometry>::type geometry_out;
  40. typedef bg::detail::overlay::overlay
  41. <
  42. Geometry, Geometry,
  43. bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value,
  44. OverlayType == bg::overlay_difference
  45. ? ! bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value
  46. : bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value,
  47. bg::detail::overlay::do_reverse<bg::point_order<Geometry>::value>::value,
  48. geometry_out,
  49. OverlayType
  50. > overlay;
  51. typedef typename bg::strategy::intersection::services::default_strategy
  52. <
  53. typename bg::cs_tag<Geometry>::type
  54. >::type strategy_type;
  55. strategy_type strategy;
  56. typedef typename bg::rescale_overlay_policy_type
  57. <
  58. Geometry,
  59. Geometry
  60. >::type rescale_policy_type;
  61. rescale_policy_type robust_policy
  62. = bg::get_rescale_policy<rescale_policy_type>(g1, g2);
  63. Geometry result;
  64. bg::detail::overlay::overlay_null_visitor visitor;
  65. overlay::apply(g1, g2, robust_policy, std::back_inserter(result),
  66. strategy, visitor);
  67. const double detected_area = bg::area(result);
  68. if (std::fabs(detected_area - expected_area) > 0.01)
  69. {
  70. if (do_output)
  71. {
  72. std::cout << "ERROR: " << caseid << std::setprecision(18)
  73. << " detected=" << detected_area
  74. << " expected=" << expected_area << std::endl
  75. << " " << bg::wkt(g1) << std::endl
  76. << " " << bg::wkt(g2) << std::endl;
  77. }
  78. return false;
  79. }
  80. return true;
  81. }
  82. template <typename Ring>
  83. void update(Ring& ring, double x, double y, std::size_t index)
  84. {
  85. if (index >= ring.size())
  86. {
  87. return;
  88. }
  89. bg::set<0>(ring[index], bg::get<0>(ring[index]) + x);
  90. bg::set<1>(ring[index], bg::get<1>(ring[index]) + y);
  91. if (index == 0)
  92. {
  93. ring.back() = ring.front();
  94. }
  95. }
  96. template <bg::overlay_type OverlayType, typename MultiPolygon>
  97. std::size_t test_case(std::size_t& error_count,
  98. std::size_t case_index, std::size_t i, std::size_t j,
  99. std::size_t min_vertex_index, std::size_t max_vertex_index,
  100. double x, double y, double expectation,
  101. MultiPolygon const& poly1, MultiPolygon const& poly2,
  102. bool do_output)
  103. {
  104. std::size_t n = 0;
  105. for (std::size_t k = min_vertex_index; k <= max_vertex_index; k++, ++n)
  106. {
  107. MultiPolygon poly2_adapted = poly2;
  108. switch (case_index)
  109. {
  110. case 2 :
  111. update(bg::interior_rings(poly2_adapted.front()).front(), x, y, k);
  112. break;
  113. default :
  114. update(bg::exterior_ring(poly2_adapted.front()), x, y, k);
  115. break;
  116. }
  117. std::ostringstream out;
  118. out << "case_" << i << "_" << j << "_" << k;
  119. if (! test_overlay<OverlayType>(out.str(), poly1, poly2_adapted, expectation, do_output))
  120. {
  121. if (error_count == 0)
  122. {
  123. // First failure is always reported
  124. test_overlay<OverlayType>(out.str(), poly1, poly2_adapted, expectation, true);
  125. }
  126. error_count++;
  127. }
  128. }
  129. return n;
  130. }
  131. template <typename T, bool Clockwise, bg::overlay_type OverlayType>
  132. std::size_t test_all(std::size_t case_index, std::size_t min_vertex_index,
  133. std::size_t max_vertex_index,
  134. double expectation, bool do_output)
  135. {
  136. typedef bg::model::point<T, 2, bg::cs::cartesian> point_type;
  137. typedef bg::model::polygon<point_type, Clockwise> polygon;
  138. typedef bg::model::multi_polygon<polygon> multi_polygon;
  139. const std::string& first = case_a[0];
  140. const std::string& second
  141. = case_index == 1 ? case_a[1]
  142. : case_index == 2 ? case_b[1]
  143. : case_index == 3 ? case_c[1]
  144. : "";
  145. multi_polygon poly1;
  146. bg::read_wkt(first, poly1);
  147. multi_polygon poly2;
  148. bg::read_wkt(second, poly2);
  149. std::size_t error_count = 0;
  150. std::size_t n = 0;
  151. for (double factor = 1.0; factor > 1.0e-10; factor /= 10.0)
  152. {
  153. std::size_t i = 0;
  154. double const bound = 1.0e-5 * factor;
  155. double const step = 1.0e-6 * factor;
  156. for (double x = -bound; x <= bound; x += step, ++i)
  157. {
  158. std::size_t j = 0;
  159. for (double y = -bound; y <= bound; y += step, ++j, ++n)
  160. {
  161. n += test_case<OverlayType>(error_count,
  162. case_index, i, j,
  163. min_vertex_index, max_vertex_index,
  164. x, y, expectation,
  165. poly1, poly2, do_output);
  166. }
  167. }
  168. }
  169. std::cout << case_index
  170. << " #cases: " << n << " #errors: " << error_count << std::endl;
  171. BOOST_CHECK_EQUAL(error_count, 0u);
  172. return error_count;
  173. }
  174. int test_main(int argc, char** argv)
  175. {
  176. typedef double coordinate_type;
  177. const double factor = argc > 1 ? atof(argv[1]) : 2.0;
  178. const bool do_output = argc > 2 && atol(argv[2]) == 1;
  179. std::cout << std::endl << " -> TESTING "
  180. << string_from_type<coordinate_type>::name()
  181. << " " << factor
  182. << std::endl;
  183. test_all<coordinate_type, true, bg::overlay_union>(1, 0, 3, 22.0, do_output);
  184. test_all<coordinate_type, true, bg::overlay_union>(2, 0, 3, 73.0, do_output);
  185. test_all<coordinate_type, true, bg::overlay_intersection>(3, 1, 2, 2.0, do_output);
  186. test_all<coordinate_type, true, bg::overlay_union>(3, 1, 2, 14.0, do_output);
  187. return 0;
  188. }