9
3

test_difference.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Unit Test
  3. // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
  4. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
  5. // This file was modified by Oracle on 2016, 2017, 2019.
  6. // Modifications copyright (c) 2016-2019, Oracle and/or its affiliates.
  7. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  8. // Use, modification and distribution is subject to the Boost Software License,
  9. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  10. // http://www.boost.org/LICENSE_1_0.txt)
  11. #ifndef BOOST_GEOMETRY_TEST_DIFFERENCE_HPP
  12. #define BOOST_GEOMETRY_TEST_DIFFERENCE_HPP
  13. #include <fstream>
  14. #include <iomanip>
  15. #include <geometry_test_common.hpp>
  16. #include <algorithms/check_validity.hpp>
  17. #include "../setop_output_type.hpp"
  18. #include <boost/core/ignore_unused.hpp>
  19. #include <boost/foreach.hpp>
  20. #include <boost/range/algorithm/copy.hpp>
  21. #include <boost/geometry/algorithms/correct.hpp>
  22. #include <boost/geometry/algorithms/difference.hpp>
  23. #include <boost/geometry/algorithms/sym_difference.hpp>
  24. #include <boost/geometry/algorithms/area.hpp>
  25. #include <boost/geometry/algorithms/is_valid.hpp>
  26. #include <boost/geometry/algorithms/length.hpp>
  27. #include <boost/geometry/algorithms/num_points.hpp>
  28. #include <boost/geometry/algorithms/num_interior_rings.hpp>
  29. #include <boost/geometry/algorithms/remove_spikes.hpp>
  30. #include <boost/geometry/geometries/geometries.hpp>
  31. #include <boost/geometry/geometries/multi_point.hpp>
  32. #include <boost/geometry/geometries/multi_linestring.hpp>
  33. #include <boost/geometry/geometries/multi_polygon.hpp>
  34. #include <boost/geometry/strategies/strategies.hpp>
  35. #include <boost/geometry/io/wkt/wkt.hpp>
  36. #if defined(TEST_WITH_SVG)
  37. # define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER
  38. # define BOOST_GEOMETRY_DEBUG_IDENTIFIER
  39. # include <boost/geometry/io/svg/svg_mapper.hpp>
  40. # include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
  41. #endif
  42. struct ut_settings
  43. {
  44. double percentage;
  45. bool sym_difference;
  46. bool remove_spikes;
  47. bool test_validity;
  48. ut_settings()
  49. : percentage(0.0001)
  50. , sym_difference(true)
  51. , remove_spikes(false)
  52. , test_validity(true)
  53. {}
  54. };
  55. inline ut_settings tolerance(double percentage)
  56. {
  57. ut_settings result;
  58. result.percentage = percentage;
  59. return result;
  60. }
  61. template <typename Output, typename G1, typename G2>
  62. void difference_output(std::string const& caseid, G1 const& g1, G2 const& g2, Output const& output)
  63. {
  64. boost::ignore_unused(caseid, g1, g2, output);
  65. #if defined(TEST_WITH_SVG)
  66. {
  67. typedef typename bg::coordinate_type<G1>::type coordinate_type;
  68. typedef typename bg::point_type<G1>::type point_type;
  69. bool const ccw =
  70. bg::point_order<G1>::value == bg::counterclockwise
  71. || bg::point_order<G2>::value == bg::counterclockwise;
  72. bool const open =
  73. bg::closure<G1>::value == bg::open
  74. || bg::closure<G2>::value == bg::open;
  75. std::ostringstream filename;
  76. filename << "difference_"
  77. << caseid << "_"
  78. << string_from_type<coordinate_type>::name()
  79. << (ccw ? "_ccw" : "")
  80. << (open ? "_open" : "")
  81. #if defined(BOOST_GEOMETRY_USE_RESCALING)
  82. << "_rescaled"
  83. #endif
  84. << ".svg";
  85. std::ofstream svg(filename.str().c_str());
  86. bg::svg_mapper<point_type> mapper(svg, 500, 500);
  87. mapper.add(g1);
  88. mapper.add(g2);
  89. mapper.map(g1, "fill-opacity:0.3;fill:rgb(51,51,153);stroke:rgb(51,51,153);stroke-width:3");
  90. mapper.map(g2, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:3");
  91. for (typename Output::const_iterator it = output.begin(); it != output.end(); ++it)
  92. {
  93. mapper.map(*it,
  94. //sym ? "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,255,0);stroke:rgb(255,0,255);stroke-width:8" :
  95. "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);stroke:rgb(255,0,255);stroke-width:8");
  96. }
  97. }
  98. #endif
  99. }
  100. template <typename OutputType, typename G1, typename G2>
  101. std::string test_difference(std::string const& caseid, G1 const& g1, G2 const& g2,
  102. int expected_count, int expected_rings_count, int expected_point_count,
  103. double expected_area,
  104. bool sym,
  105. ut_settings const& settings)
  106. {
  107. typedef typename bg::coordinate_type<G1>::type coordinate_type;
  108. boost::ignore_unused<coordinate_type>();
  109. bg::model::multi_polygon<OutputType> result;
  110. if (sym)
  111. {
  112. bg::sym_difference(g1, g2, result);
  113. }
  114. else
  115. {
  116. bg::difference(g1, g2, result);
  117. }
  118. if (settings.remove_spikes)
  119. {
  120. bg::remove_spikes(result);
  121. }
  122. #if ! defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE)
  123. {
  124. bg::model::multi_polygon<OutputType> result_s;
  125. typedef typename bg::strategy::relate::services::default_strategy
  126. <
  127. G1, G2
  128. >::type strategy_type;
  129. if (sym)
  130. {
  131. bg::sym_difference(g1, g2, result_s, strategy_type());
  132. }
  133. else
  134. {
  135. bg::difference(g1, g2, result_s, strategy_type());
  136. }
  137. if (settings.remove_spikes)
  138. {
  139. bg::remove_spikes(result_s);
  140. }
  141. BOOST_CHECK_EQUAL(bg::num_points(result), bg::num_points(result_s));
  142. }
  143. #endif
  144. std::ostringstream return_string;
  145. return_string << bg::wkt(result);
  146. typename bg::default_area_result<G1>::type const area = bg::area(result);
  147. #if ! defined(BOOST_GEOMETRY_NO_BOOST_TEST)
  148. #if ! defined(BOOST_GEOMETRY_TEST_ALWAYS_CHECK_VALIDITY)
  149. if (settings.test_validity)
  150. #endif
  151. {
  152. // std::cout << bg::dsv(result) << std::endl;
  153. typedef bg::model::multi_polygon<OutputType> result_type;
  154. std::string message;
  155. bool const valid = check_validity<result_type>::apply(result, caseid, g1, g2, message);
  156. BOOST_CHECK_MESSAGE(valid,
  157. "difference: " << caseid << " not valid " << message
  158. << " type: " << (type_for_assert_message<G1, G2>()));
  159. }
  160. #endif
  161. difference_output(caseid, g1, g2, result);
  162. #if ! (defined(BOOST_GEOMETRY_TEST_ONLY_ONE_TYPE) \
  163. || defined(BOOST_GEOMETRY_DEBUG_ASSEMBLE))
  164. {
  165. // Test inserter functionality
  166. // Test if inserter returns output-iterator (using Boost.Range copy)
  167. typename setop_output_type<OutputType>::type
  168. inserted, array_with_one_empty_geometry;
  169. array_with_one_empty_geometry.push_back(OutputType());
  170. if (sym)
  171. {
  172. boost::copy(array_with_one_empty_geometry,
  173. bg::detail::sym_difference::sym_difference_insert<OutputType>
  174. (g1, g2, std::back_inserter(inserted)));
  175. }
  176. else
  177. {
  178. boost::copy(array_with_one_empty_geometry,
  179. bg::detail::difference::difference_insert<OutputType>(
  180. g1, g2, std::back_inserter(inserted)));
  181. }
  182. BOOST_CHECK_EQUAL(boost::size(result), boost::size(inserted) - 1);
  183. }
  184. #endif
  185. #if ! defined(BOOST_GEOMETRY_NO_BOOST_TEST)
  186. #if defined(BOOST_GEOMETRY_USE_RESCALING)
  187. if (expected_point_count >= 0)
  188. {
  189. std::size_t const n = bg::num_points(result);
  190. BOOST_CHECK_MESSAGE(bg::math::abs(int(n) - expected_point_count) < 3,
  191. "difference: " << caseid
  192. << " #points expected: " << expected_point_count
  193. << " detected: " << n
  194. << " type: " << (type_for_assert_message<G1, G2>())
  195. );
  196. }
  197. #endif
  198. if (expected_count >= 0)
  199. {
  200. BOOST_CHECK_MESSAGE(int(boost::size(result)) == expected_count,
  201. "difference: " << caseid
  202. << " #outputs expected: " << expected_count
  203. << " detected: " << result.size()
  204. << " type: " << (type_for_assert_message<G1, G2>())
  205. );
  206. }
  207. if (expected_rings_count >= 0)
  208. {
  209. int nrings = int(boost::size(result) + bg::num_interior_rings(result));
  210. BOOST_CHECK_MESSAGE(nrings == expected_rings_count,
  211. "difference: " << caseid
  212. << " #rings expected: " << expected_rings_count
  213. << " detected: " << nrings
  214. << " type: " << (type_for_assert_message<G1, G2>())
  215. );
  216. }
  217. if (expected_area > 0)
  218. {
  219. BOOST_CHECK_CLOSE(area, expected_area, settings.percentage);
  220. }
  221. else
  222. {
  223. // Compare 0 with 0 or a very small detected area
  224. BOOST_CHECK_LE(area, settings.percentage);
  225. }
  226. #endif
  227. return return_string.str();
  228. }
  229. template <typename OutputType, typename G1, typename G2>
  230. std::string test_difference(std::string const& caseid, G1 const& g1, G2 const& g2,
  231. int expected_count, int expected_point_count,
  232. double expected_area,
  233. bool sym,
  234. ut_settings const& settings)
  235. {
  236. return test_difference<OutputType>(caseid, g1, g2,
  237. expected_count, -1, expected_point_count, expected_area,
  238. sym, settings);
  239. }
  240. #ifdef BOOST_GEOMETRY_CHECK_WITH_POSTGIS
  241. static int counter = 0;
  242. #endif
  243. template <typename OutputType, typename G1, typename G2>
  244. std::string test_one(std::string const& caseid,
  245. std::string const& wkt1, std::string const& wkt2,
  246. int expected_count1,
  247. int expected_rings_count1,
  248. int expected_point_count1,
  249. double expected_area1,
  250. int expected_count2,
  251. int expected_rings_count2,
  252. int expected_point_count2,
  253. double expected_area2,
  254. int expected_count_s,
  255. int expected_rings_count_s,
  256. int expected_point_count_s,
  257. double expected_area_s,
  258. ut_settings const& settings = ut_settings())
  259. {
  260. G1 g1;
  261. bg::read_wkt(wkt1, g1);
  262. G2 g2;
  263. bg::read_wkt(wkt2, g2);
  264. bg::correct(g1);
  265. bg::correct(g2);
  266. std::string result = test_difference<OutputType>(caseid + "_a", g1, g2,
  267. expected_count1, expected_rings_count1, expected_point_count1,
  268. expected_area1, false, settings);
  269. #ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE
  270. return result;
  271. #endif
  272. test_difference<OutputType>(caseid + "_b", g2, g1,
  273. expected_count2, expected_rings_count2, expected_point_count2,
  274. expected_area2, false, settings);
  275. #if ! defined(BOOST_GEOMETRY_TEST_ALWAYS_CHECK_SYMDIFFERENCE)
  276. if (settings.sym_difference)
  277. #endif
  278. {
  279. test_difference<OutputType>(caseid + "_s", g1, g2,
  280. expected_count_s,
  281. expected_rings_count_s,
  282. expected_point_count_s,
  283. expected_area_s,
  284. true, settings);
  285. }
  286. return result;
  287. }
  288. template <typename OutputType, typename G1, typename G2>
  289. std::string test_one(std::string const& caseid,
  290. std::string const& wkt1, std::string const& wkt2,
  291. int expected_count1,
  292. int expected_rings_count1,
  293. int expected_point_count1,
  294. double expected_area1,
  295. int expected_count2,
  296. int expected_rings_count2,
  297. int expected_point_count2,
  298. double expected_area2,
  299. ut_settings const& settings = ut_settings())
  300. {
  301. return test_one<OutputType, G1, G2>(caseid, wkt1, wkt2,
  302. expected_count1, expected_rings_count1, expected_point_count1, expected_area1,
  303. expected_count2, expected_rings_count2, expected_point_count2, expected_area2,
  304. expected_count1 + expected_count2,
  305. expected_rings_count1 + expected_rings_count2,
  306. expected_point_count1 >= 0 && expected_point_count2 >= 0
  307. ? (expected_point_count1 + expected_point_count2) : -1,
  308. expected_area1 + expected_area2,
  309. settings);
  310. }
  311. template <typename OutputType, typename G1, typename G2>
  312. std::string test_one(std::string const& caseid,
  313. std::string const& wkt1, std::string const& wkt2,
  314. int expected_count1,
  315. int expected_point_count1,
  316. double expected_area1,
  317. int expected_count2,
  318. int expected_point_count2,
  319. double expected_area2,
  320. int expected_count_s,
  321. int expected_point_count_s,
  322. double expected_area_s,
  323. ut_settings const& settings = ut_settings())
  324. {
  325. return test_one<OutputType, G1, G2>(caseid, wkt1, wkt2,
  326. expected_count1, -1, expected_point_count1, expected_area1,
  327. expected_count2, -1, expected_point_count2, expected_area2,
  328. expected_count_s, -1, expected_point_count_s, expected_area_s,
  329. settings);
  330. }
  331. template <typename OutputType, typename G1, typename G2>
  332. std::string test_one(std::string const& caseid,
  333. std::string const& wkt1, std::string const& wkt2,
  334. int expected_count1,
  335. int expected_point_count1,
  336. double expected_area1,
  337. int expected_count2,
  338. int expected_point_count2,
  339. double expected_area2,
  340. ut_settings const& settings = ut_settings())
  341. {
  342. return test_one<OutputType, G1, G2>(caseid, wkt1, wkt2,
  343. expected_count1, expected_point_count1, expected_area1,
  344. expected_count2, expected_point_count2, expected_area2,
  345. expected_count1 + expected_count2,
  346. expected_point_count1 >= 0 && expected_point_count2 >= 0
  347. ? (expected_point_count1 + expected_point_count2) : -1,
  348. expected_area1 + expected_area2,
  349. settings);
  350. }
  351. template <typename OutputType, typename G1, typename G2>
  352. void test_one_lp(std::string const& caseid,
  353. std::string const& wkt1, std::string const& wkt2,
  354. std::size_t expected_count,
  355. int expected_point_count,
  356. double expected_length)
  357. {
  358. G1 g1;
  359. bg::read_wkt(wkt1, g1);
  360. G2 g2;
  361. bg::read_wkt(wkt2, g2);
  362. bg::correct(g1);
  363. bg::correct(g2);
  364. typedef typename setop_output_type<OutputType>::type result_type;
  365. result_type pieces;
  366. bg::difference(g1, g2, pieces);
  367. typename bg::default_length_result<G1>::type length = 0;
  368. std::size_t n = 0;
  369. std::size_t piece_count = 0;
  370. for (typename result_type::iterator it = pieces.begin();
  371. it != pieces.end();
  372. ++it)
  373. {
  374. if (expected_point_count >= 0)
  375. {
  376. n += bg::num_points(*it);
  377. }
  378. piece_count++;
  379. length += bg::length(*it);
  380. }
  381. BOOST_CHECK_MESSAGE(piece_count == expected_count,
  382. "difference: " << caseid
  383. << " #outputs expected: " << expected_count
  384. << " detected: " << pieces.size()
  385. );
  386. if (expected_point_count >= 0)
  387. {
  388. BOOST_CHECK_MESSAGE(n == std::size_t(expected_point_count),
  389. "difference: " << caseid
  390. << " #points expected: " << std::size_t(expected_point_count)
  391. << " detected: " << n
  392. << " type: " << (type_for_assert_message<G1, G2>())
  393. );
  394. }
  395. BOOST_CHECK_CLOSE(length, expected_length, 0.001);
  396. std::string lp = "lp_";
  397. difference_output(lp + caseid, g1, g2, pieces);
  398. }
  399. #endif