test_intersection.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Unit Test
  3. // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
  4. // This file was modified by Oracle on 2016, 2017.
  5. // Modifications copyright (c) 2016-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. #ifndef BOOST_GEOMETRY_TEST_INTERSECTION_HPP
  11. #define BOOST_GEOMETRY_TEST_INTERSECTION_HPP
  12. #include <fstream>
  13. #include <iomanip>
  14. #include <boost/foreach.hpp>
  15. #include <boost/variant/variant.hpp>
  16. #include <boost/geometry/algorithms/intersection.hpp>
  17. #include <boost/geometry/algorithms/area.hpp>
  18. #include <boost/geometry/algorithms/correct.hpp>
  19. #include <boost/geometry/algorithms/is_valid.hpp>
  20. #include <boost/geometry/algorithms/length.hpp>
  21. #include <boost/geometry/algorithms/num_points.hpp>
  22. #include <boost/geometry/algorithms/num_interior_rings.hpp>
  23. #include <boost/geometry/geometries/geometries.hpp>
  24. #include <boost/geometry/strategies/strategies.hpp>
  25. #include <boost/geometry/io/wkt/wkt.hpp>
  26. #if defined(TEST_WITH_SVG)
  27. # include <boost/geometry/io/svg/svg_mapper.hpp>
  28. #endif
  29. #include <geometry_test_common.hpp>
  30. #include <algorithms/check_validity.hpp>
  31. #include "../setop_output_type.hpp"
  32. struct ut_settings
  33. {
  34. double percentage;
  35. bool test_validity;
  36. bool debug;
  37. explicit ut_settings(double p = 0.0001, bool tv = true)
  38. : percentage(p)
  39. , test_validity(tv)
  40. , debug(false)
  41. {}
  42. };
  43. template<typename IntersectionOutput, typename G1, typename G2>
  44. void check_result(IntersectionOutput const& intersection_output,
  45. std::string const& caseid,
  46. G1 const& g1, G2 const& g2,
  47. std::size_t expected_count, std::size_t expected_holes_count,
  48. int expected_point_count, double expected_length_or_area,
  49. ut_settings const& settings)
  50. {
  51. typedef typename boost::range_value<IntersectionOutput>::type OutputType;
  52. bool const is_line = bg::geometry_id<OutputType>::type::value == 2;
  53. typename bg::default_area_result<G1>::type length_or_area = 0;
  54. int n = 0;
  55. std::size_t nholes = 0;
  56. for (typename IntersectionOutput::const_iterator it = intersection_output.begin();
  57. it != intersection_output.end();
  58. ++it)
  59. {
  60. if (expected_point_count > 0)
  61. {
  62. // here n should rather be of type std::size_t, but expected_point_count
  63. // is set to -1 in some test cases so type int was left for now
  64. n += static_cast<int>(bg::num_points(*it, true));
  65. }
  66. if (expected_holes_count > 0)
  67. {
  68. nholes += bg::num_interior_rings(*it);
  69. }
  70. // instead of specialization we check it run-time here
  71. length_or_area += is_line
  72. ? bg::length(*it)
  73. : bg::area(*it);
  74. if (settings.debug)
  75. {
  76. std::cout << std::setprecision(20) << bg::wkt(*it) << std::endl;
  77. }
  78. }
  79. #if ! defined(BOOST_GEOMETRY_TEST_ALWAYS_CHECK_VALIDITY)
  80. if (settings.test_validity)
  81. #endif
  82. {
  83. std::string message;
  84. bool const valid = check_validity<IntersectionOutput>
  85. ::apply(intersection_output, caseid, g1, g2, message);
  86. BOOST_CHECK_MESSAGE(valid,
  87. "intersection: " << caseid << " not valid: " << message
  88. << " type: " << (type_for_assert_message<G1, G2>()));
  89. }
  90. #if ! defined(BOOST_GEOMETRY_NO_BOOST_TEST)
  91. #if defined(BOOST_GEOMETRY_USE_RESCALING)
  92. // Without rescaling, point count might easily differ (which is no problem)
  93. if (expected_point_count > 0)
  94. {
  95. BOOST_CHECK_MESSAGE(bg::math::abs(n - expected_point_count) < 3,
  96. "intersection: " << caseid
  97. << " #points expected: " << expected_point_count
  98. << " detected: " << n
  99. << " type: " << (type_for_assert_message<G1, G2>())
  100. );
  101. }
  102. #endif
  103. if (expected_count > 0)
  104. {
  105. BOOST_CHECK_MESSAGE(intersection_output.size() == expected_count,
  106. "intersection: " << caseid
  107. << " #outputs expected: " << expected_count
  108. << " detected: " << intersection_output.size()
  109. << " type: " << (type_for_assert_message<G1, G2>())
  110. );
  111. }
  112. if (expected_holes_count > 0)
  113. {
  114. BOOST_CHECK_MESSAGE(nholes == expected_holes_count,
  115. "intersection: " << caseid
  116. << " #holes expected: " << expected_holes_count
  117. << " detected: " << nholes
  118. << " type: " << (type_for_assert_message<G1, G2>())
  119. );
  120. }
  121. double const detected_length_or_area = boost::numeric_cast<double>(length_or_area);
  122. if (settings.percentage > 0.0)
  123. {
  124. if (expected_length_or_area > 0)
  125. {
  126. BOOST_CHECK_CLOSE(detected_length_or_area, expected_length_or_area, settings.percentage);
  127. }
  128. else
  129. {
  130. // Compare 0 with 0 or a very small detected area
  131. BOOST_CHECK_LE(detected_length_or_area, settings.percentage);
  132. }
  133. }
  134. else
  135. {
  136. // In some cases (geos_2) the intersection is either 0, or a tiny rectangle,
  137. // depending on compiler/settings. That cannot be tested by CLOSE
  138. BOOST_CHECK_LE(detected_length_or_area, expected_length_or_area);
  139. }
  140. #endif
  141. }
  142. template <typename OutputType, typename CalculationType, typename G1, typename G2>
  143. typename bg::default_area_result<G1>::type test_intersection(std::string const& caseid,
  144. G1 const& g1, G2 const& g2,
  145. std::size_t expected_count = 0, std::size_t expected_holes_count = 0,
  146. int expected_point_count = 0, double expected_length_or_area = 0,
  147. ut_settings const& settings = ut_settings())
  148. {
  149. if (settings.debug)
  150. {
  151. std::cout << std::endl << "case " << caseid << std::endl;
  152. }
  153. typedef typename setop_output_type<OutputType>::type result_type;
  154. typedef typename bg::point_type<G1>::type point_type;
  155. boost::ignore_unused<point_type>();
  156. #if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE)
  157. if (! settings.debug)
  158. {
  159. // Check _inserter behaviour with stratey
  160. typedef typename bg::strategy::intersection::services::default_strategy
  161. <
  162. typename bg::cs_tag<point_type>::type
  163. >::type strategy_type;
  164. result_type clip;
  165. bg::detail::intersection::intersection_insert<OutputType>(g1, g2, std::back_inserter(clip), strategy_type());
  166. }
  167. #endif
  168. typename bg::default_area_result<G1>::type length_or_area = 0;
  169. // Check normal behaviour
  170. result_type intersection_output;
  171. bg::intersection(g1, g2, intersection_output);
  172. check_result(intersection_output, caseid, g1, g2, expected_count,
  173. expected_holes_count, expected_point_count, expected_length_or_area,
  174. settings);
  175. #if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE)
  176. // Check variant behaviour
  177. intersection_output.clear();
  178. bg::intersection(boost::variant<G1>(g1), g2, intersection_output);
  179. check_result(intersection_output, caseid, g1, g2, expected_count,
  180. expected_holes_count, expected_point_count, expected_length_or_area,
  181. settings);
  182. intersection_output.clear();
  183. bg::intersection(g1, boost::variant<G2>(g2), intersection_output);
  184. check_result(intersection_output, caseid, g1, g2, expected_count,
  185. expected_holes_count, expected_point_count, expected_length_or_area,
  186. settings);
  187. intersection_output.clear();
  188. bg::intersection(boost::variant<G1>(g1), boost::variant<G2>(g2), intersection_output);
  189. check_result(intersection_output, caseid, g1, g2, expected_count,
  190. expected_holes_count, expected_point_count, expected_length_or_area,
  191. settings);
  192. #endif
  193. #if defined(TEST_WITH_SVG)
  194. {
  195. bool const is_line = bg::geometry_id<OutputType>::type::value == 2;
  196. typedef typename bg::coordinate_type<G1>::type coordinate_type;
  197. bool const ccw =
  198. bg::point_order<G1>::value == bg::counterclockwise
  199. || bg::point_order<G2>::value == bg::counterclockwise;
  200. bool const open =
  201. bg::closure<G1>::value == bg::open
  202. || bg::closure<G2>::value == bg::open;
  203. std::ostringstream filename;
  204. filename << "intersection_"
  205. << caseid << "_"
  206. << string_from_type<coordinate_type>::name()
  207. << string_from_type<CalculationType>::name()
  208. << (ccw ? "_ccw" : "")
  209. << (open ? "_open" : "")
  210. #if defined(BOOST_GEOMETRY_USE_RESCALING)
  211. << "_rescaled"
  212. #endif
  213. << ".svg";
  214. std::ofstream svg(filename.str().c_str());
  215. bg::svg_mapper<point_type> mapper(svg, 500, 500);
  216. mapper.add(g1);
  217. mapper.add(g2);
  218. mapper.map(g1, is_line
  219. ? "opacity:0.6;stroke:rgb(0,255,0);stroke-width:5"
  220. : "fill-opacity:0.5;fill:rgb(153,204,0);"
  221. "stroke:rgb(153,204,0);stroke-width:3");
  222. mapper.map(g2, "fill-opacity:0.3;fill:rgb(51,51,153);"
  223. "stroke:rgb(51,51,153);stroke-width:3");
  224. for (typename result_type::const_iterator it = intersection_output.begin();
  225. it != intersection_output.end(); ++it)
  226. {
  227. mapper.map(*it, "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);"
  228. "stroke:rgb(255,0,255);stroke-width:8");
  229. }
  230. }
  231. #endif
  232. if (settings.debug)
  233. {
  234. std::cout << "end case " << caseid << std::endl;
  235. }
  236. return length_or_area;
  237. }
  238. template <typename OutputType, typename CalculationType, typename G1, typename G2>
  239. typename bg::default_area_result<G1>::type test_intersection(std::string const& caseid,
  240. G1 const& g1, G2 const& g2,
  241. std::size_t expected_count = 0, int expected_point_count = 0,
  242. double expected_length_or_area = 0,
  243. ut_settings const& settings = ut_settings())
  244. {
  245. return test_intersection<OutputType, CalculationType>(
  246. caseid, g1, g2, expected_count, 0, expected_point_count,
  247. expected_length_or_area, settings
  248. );
  249. }
  250. template <typename OutputType, typename G1, typename G2>
  251. typename bg::default_area_result<G1>::type test_one(std::string const& caseid,
  252. std::string const& wkt1, std::string const& wkt2,
  253. std::size_t expected_count = 0, std::size_t expected_holes_count = 0,
  254. int expected_point_count = 0, double expected_length_or_area = 0,
  255. ut_settings const& settings = ut_settings())
  256. {
  257. G1 g1;
  258. bg::read_wkt(wkt1, g1);
  259. G2 g2;
  260. bg::read_wkt(wkt2, g2);
  261. // Reverse if necessary
  262. bg::correct(g1);
  263. bg::correct(g2);
  264. return test_intersection<OutputType, void>(caseid, g1, g2,
  265. expected_count, expected_holes_count, expected_point_count,
  266. expected_length_or_area, settings);
  267. }
  268. template <typename OutputType, typename G1, typename G2>
  269. typename bg::default_area_result<G1>::type test_one(std::string const& caseid,
  270. std::string const& wkt1, std::string const& wkt2,
  271. std::size_t expected_count = 0, int expected_point_count = 0,
  272. double expected_length_or_area = 0,
  273. ut_settings const& settings = ut_settings())
  274. {
  275. return test_one<OutputType, G1, G2>(caseid, wkt1, wkt2,
  276. expected_count, 0, expected_point_count,
  277. expected_length_or_area,
  278. settings);
  279. }
  280. template <typename OutputType, typename Areal, typename Linear>
  281. void test_one_lp(std::string const& caseid,
  282. std::string const& wkt_areal, std::string const& wkt_linear,
  283. std::size_t expected_count = 0, int expected_point_count = 0,
  284. double expected_length = 0,
  285. ut_settings const& settings = ut_settings())
  286. {
  287. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  288. std::cout << caseid << " -- start" << std::endl;
  289. #endif
  290. Areal areal;
  291. bg::read_wkt(wkt_areal, areal);
  292. bg::correct(areal);
  293. Linear linear;
  294. bg::read_wkt(wkt_linear, linear);
  295. test_intersection<OutputType, void>(caseid, areal, linear,
  296. expected_count, expected_point_count,
  297. expected_length, settings);
  298. // A linestring reversed should deliver exactly the same.
  299. bg::reverse(linear);
  300. test_intersection<OutputType, void>(caseid + "_rev", areal, linear,
  301. expected_count, expected_point_count,
  302. expected_length, settings);
  303. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  304. std::cout << caseid << " -- end" << std::endl;
  305. #endif
  306. }
  307. template <typename Geometry1, typename Geometry2>
  308. void test_point_output(std::string const& wkt1, std::string const& wkt2, unsigned int expected_count)
  309. {
  310. Geometry1 g1;
  311. bg::read_wkt(wkt1, g1);
  312. bg::correct(g1);
  313. Geometry2 g2;
  314. bg::read_wkt(wkt2, g2);
  315. bg::correct(g2);
  316. bg::model::multi_point<typename bg::point_type<Geometry1>::type> points;
  317. bg::intersection(g1, g2, points);
  318. BOOST_CHECK_EQUAL(points.size(), expected_count);
  319. }
  320. #endif