// Boost.Geometry // Unit Test // Copyright (c) 2024 Barend Gehrels, Amsterdam, the Netherlands. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include // Use another alias, on purpose it is not "bg::" here namespace bgeo = boost::geometry; // For convenience and to test current failures #if defined(BOOST_GEOMETRY_TEST_FAILURES) #define BG_IF_TEST_FAILURES true #else #define BG_IF_TEST_FAILURES false #endif struct ut_settings { explicit ut_settings() {} inline ut_settings& ignore_reverse() { test_reverse = BG_IF_TEST_FAILURES; return *this; } inline ut_settings& ignore_diff() { test_difference = BG_IF_TEST_FAILURES; return *this; } inline ut_settings& ignore_validity() { test_validity = BG_IF_TEST_FAILURES; return *this; } inline ut_settings& ignore_validity_union() { test_validity_union = BG_IF_TEST_FAILURES; return *this; } inline ut_settings& ignore_validity_intersection() { test_validity_intersection = BG_IF_TEST_FAILURES; return *this; } inline ut_settings& ignore_validity_diff() { test_validity_diff1 = BG_IF_TEST_FAILURES; test_validity_diff2 = BG_IF_TEST_FAILURES; test_validity_diff_sym = BG_IF_TEST_FAILURES; return *this; } inline ut_settings& ignore_validity_diff1() { test_validity_diff1 = BG_IF_TEST_FAILURES; return *this; } inline ut_settings& ignore_validity_diff2() { test_validity_diff2 = BG_IF_TEST_FAILURES; return *this; } inline ut_settings& ignore_validity_diff_sym() { test_validity_diff_sym = BG_IF_TEST_FAILURES; return *this; } inline ut_settings& set_epsilon(long double epsilon) { epsilon = epsilon; return *this; } bool test_reverse{true}; bool test_difference{true}; bool test_validity{true}; bool test_validity_union{true}; bool test_validity_intersection{true}; bool test_validity_diff1{true}; bool test_validity_diff2{true}; bool test_validity_diff_sym{true}; long double epsilon{1.0e-6}; }; template void test_detail(std::string const& name, std::string const& wkt1, std::string const& wkt2, ut_settings const& settings) { Geometry1 geometry1; Geometry2 geometry2; bgeo::read_wkt(wkt1, geometry1); bgeo::read_wkt(wkt2, geometry2); bgeo::correct(geometry1); bgeo::correct(geometry2); Multi result_intersection, result_union; Multi result_intersection_rev, result_union_rev; Multi diff1, diff2, sym_diff; bgeo::intersection(geometry1, geometry2, result_intersection); bgeo::intersection(geometry2, geometry1, result_intersection_rev); bgeo::union_(geometry1, geometry2, result_union); bgeo::union_(geometry2, geometry1, result_union_rev); bgeo::difference(geometry1, geometry2, diff1); bgeo::difference(geometry2, geometry1, diff2); bgeo::sym_difference(geometry1, geometry2, sym_diff); auto const area1 = bgeo::area(geometry1); auto const area2 = bgeo::area(geometry2); auto const area_union = bgeo::area(result_union); auto const area_intersection = bgeo::area(result_intersection); auto const area_diff1 = bgeo::area(diff1); auto const area_diff2 = bgeo::area(diff2); auto const area_sym_diff = bgeo::area(sym_diff); auto const balance = (area1 + area2) - area_union - area_intersection; auto const balance_d1 = (area_union - area2) - area_diff1; auto const balance_d2 = (area_union - area1) - area_diff2; auto const balance_sym = (area_diff1 + area_diff2) - area_sym_diff; auto const eps = settings.epsilon; BOOST_CHECK_MESSAGE(area_union + eps >= area1 && area_union + eps >= area2 , "Case: " << name << " wrong union " << area_union << " vs " << area1 << " and " << area2); BOOST_CHECK_MESSAGE(area_intersection - eps <= area1 && area_intersection - eps <= area2, "Case: " << name << " wrong intersection " << area_intersection << " vs " << area1 << " and " << area2); BOOST_CHECK_MESSAGE(bgeo::math::abs(balance) < eps, "Case: " << name << " wrong union or intersection " << balance); if (settings.test_difference) { BOOST_CHECK_MESSAGE(bgeo::math::abs(balance_d1) < eps, "Case: " << name << " wrong difference (a-b) " << balance_d1); BOOST_CHECK_MESSAGE(bgeo::math::abs(balance_d2) < eps, "Case: " << name << " wrong difference (b-a) " << balance_d2); BOOST_CHECK_MESSAGE(bgeo::math::abs(balance_sym) < eps, "Case: " << name << " wrong symmetric difference " << balance_sym); } if (settings.test_reverse) { BOOST_CHECK_MESSAGE(bgeo::math::abs(area_union - bgeo::area(result_union_rev)) < eps, "Case: " << name << " wrong union reversed: " << area_union << " != " << bgeo::area(result_union_rev)); BOOST_CHECK_MESSAGE(bgeo::math::abs(area_intersection - bgeo::area(result_intersection_rev)) < eps, "Case: " << name << " wrong intersection reversed: " << area_intersection << " != " << bgeo::area(result_intersection_rev)); } if (settings.test_validity) { BOOST_CHECK_MESSAGE(bgeo::is_valid(geometry1), "Case: " << name << " geometry1 is not valid"); BOOST_CHECK_MESSAGE(bgeo::is_valid(geometry2), "Case: " << name << " geometry2 is not valid"); if (settings.test_validity_union) { BOOST_CHECK_MESSAGE(bgeo::is_valid(result_union), "Case: " << name << " union is not valid"); } if (settings.test_validity_intersection) { BOOST_CHECK_MESSAGE(bgeo::is_valid(result_intersection), "Case: " << name << " intersection is not valid"); } if (settings.test_validity_diff1) { BOOST_CHECK_MESSAGE(bgeo::is_valid(diff1), "Case: " << name << " difference (a-b) is not valid"); } if (settings.test_validity_diff2) { BOOST_CHECK_MESSAGE(bgeo::is_valid(diff2), "Case: " << name << " difference (b-a) is not valid"); } if (settings.test_validity_diff_sym) { BOOST_CHECK_MESSAGE(bgeo::is_valid(sym_diff), "Case: " << name << " symmetric difference is not valid"); } } } template void test(std::string const& name, std::string const& wkt1, std::string const& wkt2, ut_settings const& settings) { using point_t = bgeo::model::point; using polygon_t = bgeo::model::polygon; using mp_t = bgeo::model::multi_polygon; constexpr auto multipolygon_tag = "MULTIPOLYGON"; bool const is_polygon1 = ! boost::icontains(wkt1, multipolygon_tag); bool const is_polygon2 = ! boost::icontains(wkt2, multipolygon_tag); if (is_polygon1 && is_polygon2) { test_detail(name, wkt1, wkt2, settings); } else if (! is_polygon1 && ! is_polygon2) { test_detail(name, wkt1, wkt2, settings); } else if (is_polygon1) { test_detail(name, wkt1, wkt2, settings); } else { test_detail(name, wkt1, wkt2, settings); } } void test_all(std::string const& name, std::string const& wkt1, std::string const& wkt2, ut_settings const& settings = ut_settings()) { test(name, wkt1, wkt2, settings); } #define TEST_CASE(caseid) (test_all(#caseid, caseid[0], caseid[1])) #define TEST_CASE_WITH(caseid, index1, index2, settings) (test_all(#caseid "_" #index1 "_" #index2, caseid[index1], caseid[index2], settings)) int test_main(int, char* []) { TEST_CASE(case_141_multi); TEST_CASE(case_142_multi); TEST_CASE(case_143_multi); TEST_CASE(case_144_multi); TEST_CASE(case_145_multi); TEST_CASE(case_146_multi); TEST_CASE(case_147_multi); TEST_CASE(case_148_multi); TEST_CASE(case_149_multi); TEST_CASE(case_150_multi); TEST_CASE(case_151_multi); TEST_CASE(issue_1221); TEST_CASE(issue_1222); TEST_CASE_WITH(issue_1226, 0, 1, ut_settings().ignore_validity_diff()); TEST_CASE(issue_1231); TEST_CASE_WITH(issue_1241, 0, 1, ut_settings().ignore_diff()); TEST_CASE(issue_1244); TEST_CASE_WITH(issue_1288, 0, 1, ut_settings().ignore_validity_diff()); TEST_CASE_WITH(issue_1288, 0, 2, ut_settings()); TEST_CASE(issue_1293); TEST_CASE(issue_1295); TEST_CASE(issue_1299); TEST_CASE(issue_1326); TEST_CASE(issue_1342_a); TEST_CASE(issue_1342_b); TEST_CASE(issue_1345_a); TEST_CASE(issue_1345_b); TEST_CASE_WITH(issue_1345_a, 1, 0, ut_settings()); TEST_CASE_WITH(issue_1345_b, 1, 0, ut_settings()); TEST_CASE(issue_1349); TEST_CASE(issue_1349_inverse); #if defined(BOOST_GEOMETRY_TEST_FAILURES) // Fails in nearly all operations TEST_CASE(issue_1363); #endif TEST_CASE(case_bitset_1); TEST_CASE(case_bitset_2); #if defined(BOOST_GEOMETRY_TEST_FAILURES) // Needs a fix in assign_parents TEST_CASE(case_bitset_3); #endif TEST_CASE(case_recursive_boxes_89); TEST_CASE(case_recursive_boxes_90); TEST_CASE(case_recursive_boxes_91); TEST_CASE(case_recursive_boxes_92); TEST_CASE(case_recursive_boxes_93); TEST_CASE(case_recursive_boxes_94); TEST_CASE(case_recursive_boxes_95); TEST_CASE(case_recursive_boxes_96); TEST_CASE(case_recursive_boxes_97); TEST_CASE(case_recursive_boxes_98); TEST_CASE(case_recursive_boxes_99); TEST_CASE_WITH(case_recursive_boxes_99, 2, 3, ut_settings()); #if defined(BOOST_GEOMETRY_TEST_FAILURES) // Fails in union or intersection, and in difference. Also the union is invalid. TEST_CASE_WITH(issue_1354, 0, 1, ut_settings().ignore_validity_union().ignore_diff()); #endif return 0; }