partition.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. //
  3. // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands.
  4. // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
  5. //
  6. // This file was modified by Oracle on 2017, 2018.
  7. // Modifications copyright (c) 2017-2018 Oracle and/or its affiliates.
  8. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  9. //
  10. // Use, modification and distribution is subject to the Boost Software License,
  11. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  12. // http://www.boost.org/LICENSE_1_0.txt)
  13. #include <geometry_test_common.hpp>
  14. #include <algorithms/test_overlay.hpp>
  15. #include <boost/geometry/geometry.hpp>
  16. #include <boost/geometry/geometries/multi_point.hpp>
  17. #include <boost/geometry/geometries/point_xy.hpp>
  18. #include <boost/geometry/geometries/register/point.hpp>
  19. #include <boost/geometry/algorithms/detail/partition.hpp>
  20. #include <boost/geometry/io/wkt/wkt.hpp>
  21. #if defined(TEST_WITH_SVG)
  22. # include <boost/geometry/io/svg/svg_mapper.hpp>
  23. #endif
  24. #include <boost/random/linear_congruential.hpp>
  25. #include <boost/random/uniform_int.hpp>
  26. #include <boost/random/uniform_real.hpp>
  27. #include <boost/random/variate_generator.hpp>
  28. template <typename Box>
  29. struct box_item
  30. {
  31. int id;
  32. Box box;
  33. box_item(int i = 0, std::string const& wkt = "")
  34. : id(i)
  35. {
  36. if (! wkt.empty())
  37. {
  38. bg::read_wkt(wkt, box);
  39. }
  40. }
  41. };
  42. struct get_box
  43. {
  44. template <typename Box, typename InputItem>
  45. static inline void apply(Box& total, InputItem const& item)
  46. {
  47. bg::expand(total, item.box);
  48. }
  49. };
  50. struct ovelaps_box
  51. {
  52. template <typename Box, typename InputItem>
  53. static inline bool apply(Box const& box, InputItem const& item)
  54. {
  55. typename bg::strategy::disjoint::services::default_strategy
  56. <
  57. Box, Box
  58. >::type strategy;
  59. return ! bg::detail::disjoint::disjoint_box_box(box, item.box, strategy);
  60. }
  61. };
  62. template <typename Box>
  63. struct box_visitor
  64. {
  65. int count;
  66. typename bg::default_area_result<Box>::type area;
  67. box_visitor()
  68. : count(0)
  69. , area(0)
  70. {}
  71. template <typename Item>
  72. inline bool apply(Item const& item1, Item const& item2)
  73. {
  74. if (bg::intersects(item1.box, item2.box))
  75. {
  76. Box b;
  77. bg::intersection(item1.box, item2.box, b);
  78. area += bg::area(b);
  79. count++;
  80. }
  81. return true;
  82. }
  83. };
  84. struct point_in_box_visitor
  85. {
  86. int count;
  87. point_in_box_visitor()
  88. : count(0)
  89. {}
  90. template <typename Point, typename BoxItem>
  91. inline bool apply(Point const& point, BoxItem const& box_item)
  92. {
  93. if (bg::within(point, box_item.box))
  94. {
  95. count++;
  96. }
  97. return true;
  98. }
  99. };
  100. struct reversed_point_in_box_visitor
  101. {
  102. int count;
  103. reversed_point_in_box_visitor()
  104. : count(0)
  105. {}
  106. template <typename BoxItem, typename Point>
  107. inline bool apply(BoxItem const& box_item, Point const& point)
  108. {
  109. if (bg::within(point, box_item.box))
  110. {
  111. count++;
  112. }
  113. return true;
  114. }
  115. };
  116. template <typename Box>
  117. void test_boxes(std::string const& wkt_box_list, double expected_area, int expected_count)
  118. {
  119. std::vector<std::string> wkt_boxes;
  120. boost::split(wkt_boxes, wkt_box_list, boost::is_any_of(";"), boost::token_compress_on);
  121. typedef box_item<Box> sample;
  122. std::vector<sample> boxes;
  123. int index = 1;
  124. BOOST_FOREACH(std::string const& wkt, wkt_boxes)
  125. {
  126. boxes.push_back(sample(index++, wkt));
  127. }
  128. box_visitor<Box> visitor;
  129. bg::partition
  130. <
  131. Box
  132. >::apply(boxes, visitor, get_box(), ovelaps_box(), 1);
  133. BOOST_CHECK_CLOSE(visitor.area, expected_area, 0.001);
  134. BOOST_CHECK_EQUAL(visitor.count, expected_count);
  135. }
  136. struct point_item
  137. {
  138. point_item()
  139. : id(0)
  140. {}
  141. int id;
  142. double x;
  143. double y;
  144. };
  145. BOOST_GEOMETRY_REGISTER_POINT_2D(point_item, double, cs::cartesian, x, y)
  146. struct get_point
  147. {
  148. template <typename Box, typename InputItem>
  149. static inline void apply(Box& total, InputItem const& item)
  150. {
  151. bg::expand(total, item);
  152. }
  153. };
  154. struct ovelaps_point
  155. {
  156. template <typename Box, typename InputItem>
  157. static inline bool apply(Box const& box, InputItem const& item)
  158. {
  159. return ! bg::disjoint(item, box);
  160. }
  161. };
  162. struct point_visitor
  163. {
  164. int count;
  165. point_visitor()
  166. : count(0)
  167. {}
  168. template <typename Item>
  169. inline bool apply(Item const& item1, Item const& item2)
  170. {
  171. if (bg::equals(item1, item2))
  172. {
  173. count++;
  174. }
  175. return true;
  176. }
  177. };
  178. void test_points(std::string const& wkt1, std::string const& wkt2, int expected_count)
  179. {
  180. bg::model::multi_point<point_item> mp1, mp2;
  181. bg::read_wkt(wkt1, mp1);
  182. bg::read_wkt(wkt2, mp2);
  183. int id = 1;
  184. BOOST_FOREACH(point_item& p, mp1)
  185. { p.id = id++; }
  186. id = 1;
  187. BOOST_FOREACH(point_item& p, mp2)
  188. { p.id = id++; }
  189. point_visitor visitor;
  190. bg::partition
  191. <
  192. bg::model::box<point_item>
  193. >::apply(mp1, mp2, visitor, get_point(), ovelaps_point(),
  194. get_point(), ovelaps_point(), 1);
  195. BOOST_CHECK_EQUAL(visitor.count, expected_count);
  196. }
  197. template <typename P>
  198. void test_all()
  199. {
  200. typedef bg::model::box<P> box;
  201. test_boxes<box>(
  202. // 1 2 3 4 5 6 7
  203. "box(0 0,1 1); box(0 0,2 2); box(9 9,10 10); box(8 8,9 9); box(4 4,6 6); box(2 4,6 8); box(7 1,8 2)",
  204. 5, // Area(Intersection(1,2)) + A(I(5,6))
  205. 3);
  206. test_boxes<box>(
  207. "box(0 0,10 10); box(4 4,6 6); box(3 3,7 7)",
  208. 4 + 16 + 4, // A(I(1,2)) + A(I(1,3)) + A(I(2,3))
  209. 3);
  210. test_boxes<box>(
  211. "box(0 2,10 3); box(3 1,4 5); box(7 1,8 5)",
  212. 1 + 1, // A(I(1,2)) + A(I(1,3))
  213. 2);
  214. test_points("multipoint((1 1))", "multipoint((1 1))", 1);
  215. test_points("multipoint((0 0),(1 1),(7 3),(10 10))", "multipoint((1 1),(2 2),(7 3))", 2);
  216. }
  217. //------------------- higher volumes
  218. #if defined(TEST_WITH_SVG)
  219. template <typename SvgMapper>
  220. struct svg_visitor
  221. {
  222. SvgMapper& m_mapper;
  223. svg_visitor(SvgMapper& mapper)
  224. : m_mapper(mapper)
  225. {}
  226. template <typename Box>
  227. inline void apply(Box const& box, int level)
  228. {
  229. /*
  230. std::string color("rgb(64,64,64)");
  231. switch(level)
  232. {
  233. case 0 : color = "rgb(255,0,0)"; break;
  234. case 1 : color = "rgb(0,255,0)"; break;
  235. case 2 : color = "rgb(0,0,255)"; break;
  236. case 3 : color = "rgb(255,255,0)"; break;
  237. case 4 : color = "rgb(255,0,255)"; break;
  238. case 5 : color = "rgb(0,255,255)"; break;
  239. case 6 : color = "rgb(255,128,0)"; break;
  240. case 7 : color = "rgb(0,128,255)"; break;
  241. }
  242. std::ostringstream style;
  243. style << "fill:none;stroke-width:" << (5.0 - level / 2.0) << ";stroke:" << color << ";";
  244. m_mapper.map(box, style.str());
  245. */
  246. m_mapper.map(box, "fill:none;stroke-width:2;stroke:rgb(0,0,0);");
  247. }
  248. };
  249. #endif
  250. template <typename Collection>
  251. void fill_points(Collection& collection, int seed, int size, int count)
  252. {
  253. typedef boost::minstd_rand base_generator_type;
  254. base_generator_type generator(seed);
  255. boost::uniform_int<> random_coordinate(0, size - 1);
  256. boost::variate_generator<base_generator_type&, boost::uniform_int<> >
  257. coordinate_generator(generator, random_coordinate);
  258. std::set<std::pair<int, int> > included;
  259. int n = 0;
  260. for (int i = 0; n < count && i < count*count; i++)
  261. {
  262. int x = coordinate_generator();
  263. int y = coordinate_generator();
  264. std::pair<int, int> pair = std::make_pair(x, y);
  265. if (included.find(pair) == included.end())
  266. {
  267. included.insert(pair);
  268. typename boost::range_value<Collection>::type item;
  269. item.x = x;
  270. item.y = y;
  271. collection.push_back(item);
  272. n++;
  273. }
  274. }
  275. }
  276. void test_many_points(int seed, int size, int count)
  277. {
  278. bg::model::multi_point<point_item> mp1, mp2;
  279. fill_points(mp1, seed, size, count);
  280. fill_points(mp2, seed * 2, size, count);
  281. // Test equality in quadratic loop
  282. int expected_count = 0;
  283. BOOST_FOREACH(point_item const& item1, mp1)
  284. {
  285. BOOST_FOREACH(point_item const& item2, mp2)
  286. {
  287. if (bg::equals(item1, item2))
  288. {
  289. expected_count++;
  290. }
  291. }
  292. }
  293. #if defined(TEST_WITH_SVG)
  294. std::ostringstream filename;
  295. filename << "partition" << seed << ".svg";
  296. std::ofstream svg(filename.str().c_str());
  297. bg::svg_mapper<point_item> mapper(svg, 800, 800);
  298. {
  299. point_item p;
  300. p.x = -1; p.y = -1; mapper.add(p);
  301. p.x = size + 1; p.y = size + 1; mapper.add(p);
  302. }
  303. typedef svg_visitor<bg::svg_mapper<point_item> > box_visitor_type;
  304. box_visitor_type box_visitor(mapper);
  305. #else
  306. typedef bg::detail::partition::visit_no_policy box_visitor_type;
  307. box_visitor_type box_visitor;
  308. #endif
  309. point_visitor visitor;
  310. bg::partition
  311. <
  312. bg::model::box<point_item>,
  313. bg::detail::partition::include_all_policy,
  314. bg::detail::partition::include_all_policy
  315. >::apply(mp1, mp2, visitor, get_point(), ovelaps_point(),
  316. get_point(), ovelaps_point(), 2, box_visitor);
  317. BOOST_CHECK_EQUAL(visitor.count, expected_count);
  318. #if defined(TEST_WITH_SVG)
  319. BOOST_FOREACH(point_item const& item, mp1)
  320. {
  321. mapper.map(item, "fill:rgb(255,128,0);stroke:rgb(0,0,100);stroke-width:1", 8);
  322. }
  323. BOOST_FOREACH(point_item const& item, mp2)
  324. {
  325. mapper.map(item, "fill:rgb(0,128,255);stroke:rgb(0,0,100);stroke-width:1", 4);
  326. }
  327. #endif
  328. }
  329. template <typename Collection>
  330. void fill_boxes(Collection& collection, int seed, int size, int count)
  331. {
  332. typedef boost::minstd_rand base_generator_type;
  333. base_generator_type generator(seed);
  334. boost::uniform_int<> random_coordinate(0, size * 10 - 1);
  335. boost::variate_generator<base_generator_type&, boost::uniform_int<> >
  336. coordinate_generator(generator, random_coordinate);
  337. int n = 0;
  338. for (int i = 0; n < count && i < count*count; i++)
  339. {
  340. int w = coordinate_generator() % 30;
  341. int h = coordinate_generator() % 30;
  342. if (w > 0 && h > 0)
  343. {
  344. int x = coordinate_generator();
  345. int y = coordinate_generator();
  346. if (x + w < size * 10 && y + h < size * 10)
  347. {
  348. typename boost::range_value<Collection>::type item(n+1);
  349. bg::assign_values(item.box, x / 10.0, y / 10.0, (x + w) / 10.0, (y + h) / 10.0);
  350. collection.push_back(item);
  351. n++;
  352. }
  353. }
  354. }
  355. }
  356. void test_many_boxes(int seed, int size, int count)
  357. {
  358. typedef bg::model::box<point_item> box_type;
  359. std::vector<box_item<box_type> > boxes;
  360. fill_boxes(boxes, seed, size, count);
  361. // Test equality in quadratic loop
  362. int expected_count = 0;
  363. double expected_area = 0.0;
  364. BOOST_FOREACH(box_item<box_type> const& item1, boxes)
  365. {
  366. BOOST_FOREACH(box_item<box_type> const& item2, boxes)
  367. {
  368. if (item1.id < item2.id)
  369. {
  370. if (bg::intersects(item1.box, item2.box))
  371. {
  372. box_type b;
  373. bg::intersection(item1.box, item2.box, b);
  374. expected_area += bg::area(b);
  375. expected_count++;
  376. }
  377. }
  378. }
  379. }
  380. #if defined(TEST_WITH_SVG)
  381. std::ostringstream filename;
  382. filename << "partition_box_" << seed << ".svg";
  383. std::ofstream svg(filename.str().c_str());
  384. bg::svg_mapper<point_item> mapper(svg, 800, 800);
  385. {
  386. point_item p;
  387. p.x = -1; p.y = -1; mapper.add(p);
  388. p.x = size + 1; p.y = size + 1; mapper.add(p);
  389. }
  390. BOOST_FOREACH(box_item<box_type> const& item, boxes)
  391. {
  392. mapper.map(item.box, "opacity:0.6;fill:rgb(50,50,210);stroke:rgb(0,0,0);stroke-width:1");
  393. }
  394. typedef svg_visitor<bg::svg_mapper<point_item> > partition_box_visitor_type;
  395. partition_box_visitor_type partition_box_visitor(mapper);
  396. #else
  397. typedef bg::detail::partition::visit_no_policy partition_box_visitor_type;
  398. partition_box_visitor_type partition_box_visitor;
  399. #endif
  400. box_visitor<box_type> visitor;
  401. bg::partition
  402. <
  403. box_type,
  404. bg::detail::partition::include_all_policy,
  405. bg::detail::partition::include_all_policy
  406. >::apply(boxes, visitor, get_box(), ovelaps_box(),
  407. 2, partition_box_visitor);
  408. BOOST_CHECK_EQUAL(visitor.count, expected_count);
  409. BOOST_CHECK_CLOSE(visitor.area, expected_area, 0.001);
  410. }
  411. void test_two_collections(int seed1, int seed2, int size, int count)
  412. {
  413. typedef bg::model::box<point_item> box_type;
  414. std::vector<box_item<box_type> > boxes1, boxes2;
  415. fill_boxes(boxes1, seed1, size, count);
  416. fill_boxes(boxes2, seed2, size, count);
  417. // Get expectations in quadratic loop
  418. int expected_count = 0;
  419. double expected_area = 0.0;
  420. BOOST_FOREACH(box_item<box_type> const& item1, boxes1)
  421. {
  422. BOOST_FOREACH(box_item<box_type> const& item2, boxes2)
  423. {
  424. if (bg::intersects(item1.box, item2.box))
  425. {
  426. box_type b;
  427. bg::intersection(item1.box, item2.box, b);
  428. expected_area += bg::area(b);
  429. expected_count++;
  430. }
  431. }
  432. }
  433. #if defined(TEST_WITH_SVG)
  434. std::ostringstream filename;
  435. filename << "partition_boxes_" << seed1 << "_" << seed2 << ".svg";
  436. std::ofstream svg(filename.str().c_str());
  437. bg::svg_mapper<point_item> mapper(svg, 800, 800);
  438. {
  439. point_item p;
  440. p.x = -1; p.y = -1; mapper.add(p);
  441. p.x = size + 1; p.y = size + 1; mapper.add(p);
  442. }
  443. BOOST_FOREACH(box_item<box_type> const& item, boxes1)
  444. {
  445. mapper.map(item.box, "opacity:0.6;fill:rgb(50,50,210);stroke:rgb(0,0,0);stroke-width:1");
  446. }
  447. BOOST_FOREACH(box_item<box_type> const& item, boxes2)
  448. {
  449. mapper.map(item.box, "opacity:0.6;fill:rgb(0,255,0);stroke:rgb(0,0,0);stroke-width:1");
  450. }
  451. typedef svg_visitor<bg::svg_mapper<point_item> > partition_box_visitor_type;
  452. partition_box_visitor_type partition_box_visitor(mapper);
  453. #else
  454. typedef bg::detail::partition::visit_no_policy partition_box_visitor_type;
  455. partition_box_visitor_type partition_box_visitor;
  456. #endif
  457. box_visitor<box_type> visitor;
  458. bg::partition
  459. <
  460. box_type,
  461. bg::detail::partition::include_all_policy,
  462. bg::detail::partition::include_all_policy
  463. >::apply(boxes1, boxes2, visitor, get_box(), ovelaps_box(),
  464. get_box(), ovelaps_box(), 2, partition_box_visitor);
  465. BOOST_CHECK_EQUAL(visitor.count, expected_count);
  466. BOOST_CHECK_CLOSE(visitor.area, expected_area, 0.001);
  467. }
  468. void test_heterogenuous_collections(int seed1, int seed2, int size, int count)
  469. {
  470. typedef bg::model::box<point_item> box_type;
  471. std::vector<point_item> points;
  472. std::vector<box_item<box_type> > boxes;
  473. fill_points(points, seed1, size, count);
  474. fill_boxes(boxes, seed2, size, count);
  475. // Get expectations in quadratic loop
  476. int expected_count = 0;
  477. BOOST_FOREACH(point_item const& point, points)
  478. {
  479. BOOST_FOREACH(box_item<box_type> const& box_item, boxes)
  480. {
  481. if (bg::within(point, box_item.box))
  482. {
  483. expected_count++;
  484. }
  485. }
  486. }
  487. #if defined(TEST_WITH_SVG)
  488. std::ostringstream filename;
  489. filename << "partition_heterogeneous_" << seed1 << "_" << seed2 << ".svg";
  490. std::ofstream svg(filename.str().c_str());
  491. bg::svg_mapper<point_item> mapper(svg, 800, 800);
  492. {
  493. point_item p;
  494. p.x = -1; p.y = -1; mapper.add(p);
  495. p.x = size + 1; p.y = size + 1; mapper.add(p);
  496. }
  497. BOOST_FOREACH(point_item const& point, points)
  498. {
  499. mapper.map(point, "fill:rgb(255,128,0);stroke:rgb(0,0,100);stroke-width:1", 8);
  500. }
  501. BOOST_FOREACH(box_item<box_type> const& item, boxes)
  502. {
  503. mapper.map(item.box, "opacity:0.6;fill:rgb(0,255,0);stroke:rgb(0,0,0);stroke-width:1");
  504. }
  505. typedef svg_visitor<bg::svg_mapper<point_item> > partition_box_visitor_type;
  506. partition_box_visitor_type partition_box_visitor(mapper);
  507. #else
  508. typedef bg::detail::partition::visit_no_policy partition_box_visitor_type;
  509. partition_box_visitor_type partition_box_visitor;
  510. #endif
  511. point_in_box_visitor visitor1;
  512. bg::partition
  513. <
  514. box_type,
  515. bg::detail::partition::include_all_policy,
  516. bg::detail::partition::include_all_policy
  517. >::apply(points, boxes, visitor1, get_point(), ovelaps_point(),
  518. get_box(), ovelaps_box(), 2, partition_box_visitor);
  519. reversed_point_in_box_visitor visitor2;
  520. bg::partition
  521. <
  522. box_type,
  523. bg::detail::partition::include_all_policy,
  524. bg::detail::partition::include_all_policy
  525. >::apply(boxes, points, visitor2, get_box(), ovelaps_box(),
  526. get_point(), ovelaps_point(), 2, partition_box_visitor);
  527. BOOST_CHECK_EQUAL(visitor1.count, expected_count);
  528. BOOST_CHECK_EQUAL(visitor2.count, expected_count);
  529. }
  530. int test_main( int , char* [] )
  531. {
  532. test_all<bg::model::d2::point_xy<double> >();
  533. test_many_points(12345, 20, 40);
  534. test_many_points(54321, 20, 60);
  535. test_many_points(67890, 20, 80);
  536. test_many_points(98765, 20, 100);
  537. for (int i = 1; i < 10; i++)
  538. {
  539. test_many_points(i, 30, i * 20);
  540. }
  541. test_many_boxes(12345, 20, 40);
  542. for (int i = 1; i < 10; i++)
  543. {
  544. test_many_boxes(i, 20, i * 10);
  545. }
  546. test_two_collections(12345, 54321, 20, 40);
  547. test_two_collections(67890, 98765, 20, 60);
  548. test_heterogenuous_collections(67890, 98765, 20, 60);
  549. return 0;
  550. }