test_is_valid.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Unit Test
  3. // Copyright (c) 2014-2017, Oracle and/or its affiliates.
  4. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  5. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  6. // Licensed under the Boost Software License version 1.0.
  7. // http://www.boost.org/users/license.html
  8. #ifndef BOOST_GEOMETRY_TEST_IS_VALID_HPP
  9. #define BOOST_GEOMETRY_TEST_IS_VALID_HPP
  10. #include <iostream>
  11. #include <sstream>
  12. #include <string>
  13. #include <boost/core/ignore_unused.hpp>
  14. #include <boost/range.hpp>
  15. #include <boost/variant/variant.hpp>
  16. #include <boost/geometry/core/closure.hpp>
  17. #include <boost/geometry/core/exterior_ring.hpp>
  18. #include <boost/geometry/core/interior_rings.hpp>
  19. #include <boost/geometry/core/point_order.hpp>
  20. #include <boost/geometry/core/ring_type.hpp>
  21. #include <boost/geometry/core/tag.hpp>
  22. #include <boost/geometry/core/tags.hpp>
  23. #include <boost/geometry/geometries/point_xy.hpp>
  24. #include <boost/geometry/geometries/segment.hpp>
  25. #include <boost/geometry/geometries/box.hpp>
  26. #include <boost/geometry/geometries/linestring.hpp>
  27. #include <boost/geometry/geometries/ring.hpp>
  28. #include <boost/geometry/geometries/polygon.hpp>
  29. #include <boost/geometry/geometries/multi_point.hpp>
  30. #include <boost/geometry/geometries/multi_linestring.hpp>
  31. #include <boost/geometry/geometries/multi_polygon.hpp>
  32. #include <boost/geometry/strategies/strategies.hpp>
  33. #include <boost/geometry/io/wkt/wkt.hpp>
  34. #include <boost/geometry/algorithms/convert.hpp>
  35. #include <boost/geometry/algorithms/num_points.hpp>
  36. #include <boost/geometry/algorithms/is_valid.hpp>
  37. #include <boost/geometry/algorithms/detail/check_iterator_range.hpp>
  38. #include <from_wkt.hpp>
  39. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  40. #include "pretty_print_geometry.hpp"
  41. #endif
  42. namespace bg = ::boost::geometry;
  43. typedef bg::model::point<double, 2, bg::cs::cartesian> point_type;
  44. typedef bg::model::segment<point_type> segment_type;
  45. typedef bg::model::box<point_type> box_type;
  46. typedef bg::model::linestring<point_type> linestring_type;
  47. typedef bg::model::multi_linestring<linestring_type> multi_linestring_type;
  48. typedef bg::model::multi_point<point_type> multi_point_type;
  49. //----------------------------------------------------------------------------
  50. // returns true if a geometry can be converted to closed
  51. template
  52. <
  53. typename Geometry,
  54. typename Tag = typename bg::tag<Geometry>::type,
  55. bg::closure_selector Closure = bg::closure<Geometry>::value
  56. >
  57. struct is_convertible_to_closed
  58. {
  59. static inline bool apply(Geometry const&)
  60. {
  61. return false;
  62. }
  63. };
  64. template <typename Ring>
  65. struct is_convertible_to_closed<Ring, bg::ring_tag, bg::open>
  66. {
  67. static inline bool apply(Ring const& ring)
  68. {
  69. return boost::size(ring) > 0;
  70. }
  71. };
  72. template <typename Polygon>
  73. struct is_convertible_to_closed<Polygon, bg::polygon_tag, bg::open>
  74. {
  75. typedef typename bg::ring_type<Polygon>::type ring_type;
  76. template <typename InteriorRings>
  77. static inline
  78. bool apply_to_interior_rings(InteriorRings const& interior_rings)
  79. {
  80. return bg::detail::check_iterator_range
  81. <
  82. is_convertible_to_closed<ring_type>
  83. >::apply(boost::begin(interior_rings),
  84. boost::end(interior_rings));
  85. }
  86. static inline bool apply(Polygon const& polygon)
  87. {
  88. return boost::size(bg::exterior_ring(polygon)) > 0
  89. && apply_to_interior_rings(bg::interior_rings(polygon));
  90. }
  91. };
  92. template <typename MultiPolygon>
  93. struct is_convertible_to_closed<MultiPolygon, bg::multi_polygon_tag, bg::open>
  94. {
  95. typedef typename boost::range_value<MultiPolygon>::type polygon;
  96. static inline bool apply(MultiPolygon const& multi_polygon)
  97. {
  98. return bg::detail::check_iterator_range
  99. <
  100. is_convertible_to_closed<polygon>,
  101. false // do not allow empty multi-polygon
  102. >::apply(boost::begin(multi_polygon),
  103. boost::end(multi_polygon));
  104. }
  105. };
  106. //----------------------------------------------------------------------------
  107. // returns true if a geometry can be converted to cw
  108. template
  109. <
  110. typename Geometry,
  111. typename Tag = typename bg::tag<Geometry>::type,
  112. bg::order_selector Order = bg::point_order<Geometry>::value
  113. >
  114. struct is_convertible_to_cw
  115. {
  116. static inline bool apply(Geometry const&)
  117. {
  118. return bg::point_order<Geometry>::value == bg::counterclockwise;
  119. }
  120. };
  121. //----------------------------------------------------------------------------
  122. // returns true if geometry can be converted to polygon
  123. template
  124. <
  125. typename Geometry,
  126. typename Tag = typename bg::tag<Geometry>::type
  127. >
  128. struct is_convertible_to_polygon
  129. {
  130. typedef Geometry type;
  131. static bool const value = false;
  132. };
  133. template <typename Ring>
  134. struct is_convertible_to_polygon<Ring, bg::ring_tag>
  135. {
  136. typedef bg::model::polygon
  137. <
  138. typename bg::point_type<Ring>::type,
  139. bg::point_order<Ring>::value == bg::clockwise,
  140. bg::closure<Ring>::value == bg::closed
  141. > type;
  142. static bool const value = true;
  143. };
  144. //----------------------------------------------------------------------------
  145. // returns true if geometry can be converted to multi-polygon
  146. template
  147. <
  148. typename Geometry,
  149. typename Tag = typename bg::tag<Geometry>::type
  150. >
  151. struct is_convertible_to_multipolygon
  152. {
  153. typedef Geometry type;
  154. static bool const value = false;
  155. };
  156. template <typename Ring>
  157. struct is_convertible_to_multipolygon<Ring, bg::ring_tag>
  158. {
  159. typedef bg::model::multi_polygon
  160. <
  161. typename is_convertible_to_polygon<Ring>::type
  162. > type;
  163. static bool const value = true;
  164. };
  165. template <typename Polygon>
  166. struct is_convertible_to_multipolygon<Polygon, bg::polygon_tag>
  167. {
  168. typedef bg::model::multi_polygon<Polygon> type;
  169. static bool const value = true;
  170. };
  171. //----------------------------------------------------------------------------
  172. template <typename ValidityTester>
  173. struct validity_checker
  174. {
  175. template <typename Geometry>
  176. static inline bool apply(std::string const& case_id,
  177. Geometry const& geometry,
  178. bool expected_result,
  179. std::string& reason)
  180. {
  181. bool valid = ValidityTester::apply(geometry);
  182. std::string const reason_valid
  183. = bg::validity_failure_type_message(bg::no_failure);
  184. reason = ValidityTester::reason(geometry);
  185. std::string reason_short = reason.substr(0, reason_valid.length());
  186. BOOST_CHECK_MESSAGE(valid == expected_result,
  187. "case id: " << case_id
  188. << ", Expected: " << expected_result
  189. << ", detected: " << valid
  190. << "; wkt: " << bg::wkt(geometry));
  191. BOOST_CHECK_MESSAGE(reason != "",
  192. "case id (empty reason): " << case_id
  193. << ", Expected: " << valid
  194. << ", detected reason: " << reason
  195. << "; wkt: " << bg::wkt(geometry));
  196. BOOST_CHECK_MESSAGE((valid && reason == reason_valid)
  197. ||
  198. (! valid && reason != reason_valid)
  199. ||
  200. (! valid && reason_short != reason_valid),
  201. "case id (reason): " << case_id
  202. << ", Expected: " << valid
  203. << ", detected reason: " << reason
  204. << "; wkt: " << bg::wkt(geometry));
  205. return valid;
  206. }
  207. };
  208. //----------------------------------------------------------------------------
  209. struct default_validity_tester
  210. {
  211. template <typename Geometry>
  212. static inline bool apply(Geometry const& geometry)
  213. {
  214. return bg::is_valid(geometry);
  215. }
  216. template <typename Geometry>
  217. static inline std::string reason(Geometry const& geometry)
  218. {
  219. std::string message;
  220. bg::is_valid(geometry, message);
  221. return message;
  222. }
  223. };
  224. template <bool AllowSpikes>
  225. struct validity_tester_linear
  226. {
  227. template <typename Geometry>
  228. static inline bool apply(Geometry const& geometry)
  229. {
  230. bool const irrelevant = true;
  231. bg::is_valid_default_policy<irrelevant, AllowSpikes> visitor;
  232. return bg::is_valid(geometry, visitor, bg::default_strategy());
  233. }
  234. template <typename Geometry>
  235. static inline std::string reason(Geometry const& geometry)
  236. {
  237. bool const irrelevant = true;
  238. std::ostringstream oss;
  239. bg::failing_reason_policy<irrelevant, AllowSpikes> visitor(oss);
  240. bg::is_valid(geometry, visitor, bg::default_strategy());
  241. return oss.str();
  242. }
  243. };
  244. template <bool AllowDuplicates>
  245. struct validity_tester_areal
  246. {
  247. template <typename Geometry>
  248. static inline bool apply(Geometry const& geometry)
  249. {
  250. bg::is_valid_default_policy<AllowDuplicates> visitor;
  251. return bg::is_valid(geometry, visitor, bg::default_strategy());
  252. }
  253. template <typename Geometry>
  254. static inline std::string reason(Geometry const& geometry)
  255. {
  256. std::ostringstream oss;
  257. bg::failing_reason_policy<AllowDuplicates> visitor(oss);
  258. bg::is_valid(geometry, visitor, bg::default_strategy());
  259. return oss.str();
  260. }
  261. };
  262. template <bool AllowDuplicates>
  263. struct validity_tester_geo_areal
  264. {
  265. template <typename Geometry>
  266. static inline bool apply(Geometry const& geometry)
  267. {
  268. bg::is_valid_default_policy<AllowDuplicates> visitor;
  269. bg::strategy::intersection::geographic_segments<> s;
  270. return bg::is_valid(geometry, visitor, s);
  271. }
  272. template <typename Geometry>
  273. static inline std::string reason(Geometry const& geometry)
  274. {
  275. std::ostringstream oss;
  276. bg::failing_reason_policy<AllowDuplicates> visitor(oss);
  277. bg::strategy::intersection::geographic_segments<> s;
  278. bg::is_valid(geometry, visitor, s);
  279. return oss.str();
  280. }
  281. };
  282. //----------------------------------------------------------------------------
  283. template
  284. <
  285. typename ValidityTester,
  286. typename Geometry,
  287. typename ClosedGeometry = Geometry,
  288. typename CWGeometry = Geometry,
  289. typename CWClosedGeometry = Geometry,
  290. typename Tag = typename bg::tag<Geometry>::type
  291. >
  292. class test_valid
  293. {
  294. protected:
  295. template <typename G>
  296. static inline void base_test(std::string const& case_id,
  297. G const& g,
  298. bool expected_result)
  299. {
  300. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  301. std::cout << "=======" << std::endl;
  302. #endif
  303. std::string reason;
  304. bool valid = validity_checker
  305. <
  306. ValidityTester
  307. >::apply(case_id, g, expected_result, reason);
  308. boost::ignore_unused(valid);
  309. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  310. std::cout << "case id: " << case_id << ", Geometry: ";
  311. pretty_print_geometry<G>::apply(std::cout, g);
  312. std::cout << std::endl;
  313. std::cout << "wkt: " << bg::wkt(g) << std::endl;
  314. std::cout << std::boolalpha;
  315. std::cout << "is valid? " << valid << std::endl;
  316. std::cout << "expected result: " << expected_result << std::endl;
  317. std::cout << "reason: " << reason << std::endl;
  318. std::cout << "=======" << std::endl;
  319. std::cout << std::noboolalpha;
  320. #endif
  321. }
  322. public:
  323. static inline void apply(std::string const& case_id,
  324. Geometry const& geometry,
  325. bool expected_result)
  326. {
  327. std::stringstream sstr;
  328. sstr << case_id << "-original"; // which is: CCW open
  329. base_test(sstr.str(), geometry, expected_result);
  330. if ( is_convertible_to_closed<Geometry>::apply(geometry) )
  331. {
  332. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  333. std::cout << "...checking closed geometry..."
  334. << std::endl;
  335. #endif
  336. ClosedGeometry closed_geometry;
  337. bg::convert(geometry, closed_geometry);
  338. sstr.str("");
  339. sstr << case_id << "-2closed";
  340. base_test(sstr.str(), closed_geometry, expected_result);
  341. }
  342. if ( is_convertible_to_cw<Geometry>::apply(geometry) )
  343. {
  344. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  345. std::cout << "...checking cw open geometry..."
  346. << std::endl;
  347. #endif
  348. CWGeometry cw_geometry;
  349. bg::convert(geometry, cw_geometry);
  350. sstr.str("");
  351. sstr << case_id << "-2CW";
  352. base_test(sstr.str(), cw_geometry, expected_result);
  353. if ( is_convertible_to_closed<CWGeometry>::apply(cw_geometry) )
  354. {
  355. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  356. std::cout << "...checking cw closed geometry..."
  357. << std::endl;
  358. #endif
  359. CWClosedGeometry cw_closed_geometry;
  360. bg::convert(cw_geometry, cw_closed_geometry);
  361. sstr.str("");
  362. sstr << case_id << "-2CWclosed";
  363. base_test(sstr.str(), cw_closed_geometry, expected_result);
  364. }
  365. }
  366. if ( BOOST_GEOMETRY_CONDITION(is_convertible_to_polygon<Geometry>::value) )
  367. {
  368. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  369. std::cout << "...checking geometry converted to polygon..."
  370. << std::endl;
  371. #endif
  372. typename is_convertible_to_polygon<Geometry>::type polygon;
  373. bg::convert(geometry, polygon);
  374. sstr.str("");
  375. sstr << case_id << "-2Polygon";
  376. base_test(sstr.str(), polygon, expected_result);
  377. }
  378. if ( BOOST_GEOMETRY_CONDITION(is_convertible_to_multipolygon<Geometry>::value) )
  379. {
  380. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  381. std::cout << "...checking geometry converted to multi-polygon..."
  382. << std::endl;
  383. #endif
  384. typename is_convertible_to_multipolygon
  385. <
  386. Geometry
  387. >::type multipolygon;
  388. bg::convert(geometry, multipolygon);
  389. sstr.str("");
  390. sstr << case_id << "-2MultiPolygon";
  391. base_test(sstr.str(), multipolygon, expected_result);
  392. }
  393. #ifdef BOOST_GEOMETRY_TEST_DEBUG
  394. std::cout << std::endl << std::endl << std::endl;
  395. #endif
  396. }
  397. static inline void apply(std::string const& case_id,
  398. std::string const& wkt,
  399. bool expected_result)
  400. {
  401. apply(case_id, from_wkt<Geometry>(wkt), expected_result);
  402. }
  403. };
  404. //----------------------------------------------------------------------------
  405. template <typename VariantGeometry>
  406. class test_valid_variant
  407. : test_valid<default_validity_tester, VariantGeometry>
  408. {
  409. private:
  410. typedef test_valid<default_validity_tester, VariantGeometry> base_type;
  411. public:
  412. static inline void apply(std::string const& case_id,
  413. VariantGeometry const& vg,
  414. bool expected_result)
  415. {
  416. std::ostringstream oss;
  417. base_type::base_test(case_id, vg, expected_result);
  418. }
  419. };
  420. #endif // BOOST_GEOMETRY_TEST_IS_VALID_HPP