simplify_countries.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // (Unit) Test
  3. // Copyright (c) 2018 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 <boost/geometry.hpp>
  8. #include <geometry_test_common.hpp>
  9. #include <boost/foreach.hpp>
  10. #include <iomanip>
  11. #if defined(TEST_WITH_SVG)
  12. # include <boost/geometry/io/svg/svg_mapper.hpp>
  13. #endif
  14. template <typename MultiPolygon>
  15. std::string read_from_file(std::string const& filename)
  16. {
  17. MultiPolygon mp;
  18. std::ifstream in(filename.c_str());
  19. while (in.good())
  20. {
  21. std::string line;
  22. std::getline(in, line);
  23. if (! line.empty())
  24. {
  25. typename boost::range_value<MultiPolygon>::type pol;
  26. bg::read_wkt(line, pol);
  27. mp.push_back(pol);
  28. }
  29. }
  30. std::ostringstream out;
  31. if (! mp.empty())
  32. {
  33. out << std::fixed << std::setprecision(19) << bg::wkt(mp);
  34. }
  35. BOOST_CHECK(! out.str().empty());
  36. return out.str();
  37. }
  38. template <typename MultiPolygon>
  39. void test_one(std::string const& caseid, std::string const& wkt,
  40. double distance_in_meters,
  41. double expected_area_ratio, double expected_perimeter_ratio,
  42. std::size_t expected_polygon_count = 0,
  43. std::size_t expected_interior_count = 0,
  44. std::size_t expected_point_count = 0)
  45. {
  46. boost::ignore_unused(caseid);
  47. MultiPolygon geometry, simplified;
  48. bg::read_wkt(wkt, geometry);
  49. bg::correct(geometry);
  50. bg::simplify(geometry, simplified, distance_in_meters);
  51. double const area_ratio = bg::area(simplified) / bg::area(geometry);
  52. double const perimeter_ratio = bg::perimeter(simplified) / bg::perimeter(geometry);
  53. BOOST_CHECK_CLOSE(perimeter_ratio, expected_perimeter_ratio, 0.01);
  54. BOOST_CHECK_CLOSE(area_ratio, expected_area_ratio, 0.01);
  55. BOOST_CHECK_EQUAL(expected_polygon_count, boost::size(simplified));
  56. BOOST_CHECK_EQUAL(expected_interior_count, bg::num_interior_rings(simplified));
  57. BOOST_CHECK_EQUAL(expected_point_count, bg::num_points(simplified));
  58. // To add new tests, this is convenient and write the test itself:
  59. // std::cout << "test_one<mp>(\"" << caseid << "\", " << caseid
  60. // << ", " << distance_in_meters
  61. // << std::setprecision(6)
  62. // << ", " << area_ratio
  63. // << ", " << perimeter_ratio
  64. // << ", " << boost::size(simplified)
  65. // << ", " << bg::num_interior_rings(simplified)
  66. // << ", " << bg::num_points(simplified)
  67. // << ");"
  68. // << std::endl;
  69. #if defined(TEST_WITH_SVG)
  70. {
  71. typedef typename boost::range_value<MultiPolygon>::type polygon;
  72. std::ostringstream filename;
  73. filename << "simplify_" << caseid << "_" << distance_in_meters << ".svg";
  74. std::ofstream svg(filename.str().c_str());
  75. bg::svg_mapper
  76. <
  77. typename bg::point_type<MultiPolygon>::type
  78. > mapper(svg, 1200, 800);
  79. mapper.add(geometry);
  80. mapper.add(simplified);
  81. mapper.map(geometry, "fill-opacity:0.5;fill:rgb(153,204,0);"
  82. "stroke:rgb(153,204,0);stroke-width:1");
  83. BOOST_FOREACH(polygon const& pol, simplified)
  84. {
  85. mapper.map(pol,
  86. bg::area(pol) > 0 ? "fill:none;stroke:rgb(255,0,0);stroke-width:1"
  87. : "fill:none;stroke:rgb(255,0,255);stroke-width:1");
  88. }
  89. }
  90. #endif
  91. }
  92. template <bool Clockwise, typename P>
  93. void test_all()
  94. {
  95. typedef bg::model::polygon<P, Clockwise> polygon;
  96. typedef bg::model::multi_polygon<polygon> mp;
  97. // The unit test uses countries originally added for buffer unit test
  98. std::string base_folder = "buffer/data/";
  99. // Verify for Greece, Italy, Netherlands, Norway and UK
  100. std::string gr = read_from_file<mp>(base_folder + "gr.wkt");
  101. std::string it = read_from_file<mp>(base_folder + "it.wkt");
  102. std::string nl = read_from_file<mp>(base_folder + "nl.wkt");
  103. std::string no = read_from_file<mp>(base_folder + "no.wkt");
  104. std::string uk = read_from_file<mp>(base_folder + "uk.wkt");
  105. // Gradually simplify more aggresively.
  106. // Area ratio (first) can increase or decrease
  107. // Perimeter ratio (second) should decrease.
  108. // Polygons, interior rings, points should decrease
  109. test_one<mp>("gr", gr, 100, 0.999905, 0.999758, 68, 0, 2520);
  110. test_one<mp>("gr", gr, 200, 0.999773, 0.998865, 68, 0, 2019);
  111. test_one<mp>("gr", gr, 500, 0.999026, 0.995931, 68, 0, 1468);
  112. test_one<mp>("gr", gr, 1000, 0.997782, 0.991475, 68, 0, 1132);
  113. test_one<mp>("gr", gr, 2000, 0.994448, 0.9793, 65, 0, 854);
  114. test_one<mp>("gr", gr, 5000, 0.979743, 0.910266, 50, 0, 471);
  115. test_one<mp>("gr", gr, 10000, 0.968349, 0.778863, 28, 0, 245);
  116. test_one<mp>("gr", gr, 20000, 0.961943, 0.607009, 10, 0, 97); // Many islands disappear
  117. test_one<mp>("it", it, 100, 1.00001, 0.999813, 22, 1, 1783);
  118. test_one<mp>("it", it, 200, 1.00009, 0.9991, 22, 1, 1406);
  119. test_one<mp>("it", it, 500, 1.00019, 0.996848, 22, 1, 1011);
  120. test_one<mp>("it", it, 1000, 1.00041, 0.99294, 22, 1, 749);
  121. test_one<mp>("it", it, 2000, 1.00086, 0.985144, 22, 1, 546);
  122. test_one<mp>("it", it, 5000, 1.00147, 0.93927, 11, 1, 283);
  123. test_one<mp>("it", it, 10000, 1.01089, 0.882198, 4, 1, 153);
  124. test_one<mp>("it", it, 20000, 1.00893, 0.828774, 4, 0, 86); // San Marino disappears
  125. test_one<mp>("nl", nl, 100, 0.999896, 0.999804, 8, 0, 789);
  126. test_one<mp>("nl", nl, 200, 0.999733, 0.999095, 8, 0, 633);
  127. test_one<mp>("nl", nl, 500, 0.999423, 0.996313, 8, 0, 436);
  128. test_one<mp>("nl", nl, 1000, 0.997893, 0.991951, 8, 0, 331);
  129. test_one<mp>("nl", nl, 2000, 0.996129, 0.981998, 8, 0, 234);
  130. test_one<mp>("nl", nl, 5000, 0.986128, 0.896, 5, 0, 132);
  131. test_one<mp>("nl", nl, 10000, 0.973917, 0.832522, 4, 0, 75);
  132. test_one<mp>("nl", nl, 20000, 0.970675, 0.739275, 3, 0, 40);
  133. test_one<mp>("no", no, 100, 0.999966, 0.999975, 95, 0, 7650);
  134. test_one<mp>("no", no, 200, 0.999812, 0.999731, 95, 0, 6518);
  135. test_one<mp>("no", no, 500, 0.99929, 0.998092, 95, 0, 4728);
  136. test_one<mp>("no", no, 1000, 0.998473, 0.994075, 95, 0, 3524);
  137. test_one<mp>("no", no, 2000, 0.996674, 0.985863, 92, 0, 2576);
  138. test_one<mp>("no", no, 5000, 0.99098, 0.965689, 87, 0, 1667);
  139. test_one<mp>("no", no, 10000, 0.978207, 0.906525, 69, 0, 1059);
  140. test_one<mp>("no", no, 20000, 0.955223, 0.786546, 38, 0, 593);
  141. test_one<mp>("uk", uk, 100, 0.999942, 0.999878, 48, 0, 3208);
  142. test_one<mp>("uk", uk, 200, 0.999843, 0.999291, 48, 0, 2615);
  143. test_one<mp>("uk", uk, 500, 0.999522, 0.996888, 48, 0, 1885);
  144. test_one<mp>("uk", uk, 1000, 0.999027, 0.992306, 48, 0, 1396);
  145. test_one<mp>("uk", uk, 2000, 0.998074, 0.983839, 47, 0, 1032);
  146. test_one<mp>("uk", uk, 5000, 0.991901, 0.943496, 35, 0, 611);
  147. test_one<mp>("uk", uk, 10000, 0.990039, 0.871969, 23, 0, 359);
  148. test_one<mp>("uk", uk, 20000, 0.979171, 0.737577, 11, 0, 193);
  149. }
  150. int test_main(int, char* [])
  151. {
  152. test_all<true, bg::model::point<double, 2, bg::cs::cartesian> >();
  153. return 0;
  154. }