From ae09052e7debe98ed813454d9ab01b0f7bddd73b Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 28 Jun 2017 22:27:03 +0200 Subject: [PATCH 001/188] [test] update administration after fix in is_valid. Most difference cases are valid now. --- .../set_operations/difference/difference.cpp | 24 +++++-------------- .../difference/difference_multi.cpp | 14 ++++------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/test/algorithms/set_operations/difference/difference.cpp b/test/algorithms/set_operations/difference/difference.cpp index 00983708f..d00e14129 100644 --- a/test/algorithms/set_operations/difference/difference.cpp +++ b/test/algorithms/set_operations/difference/difference.cpp @@ -42,9 +42,10 @@ clips3, -1, area1 + area2) #define TEST_DIFFERENCE_IGNORE(caseid, clips1, area1, clips2, area2, clips3) \ + { ut_settings ignore_validity; ignore_validity.test_validity = false; \ (test_one) \ ( #caseid, caseid[0], caseid[1], clips1, -1, area1, clips2, -1, area2, \ - clips3, -1, area1 + area2, ignore_validity) + clips3, -1, area1 + area2, ignore_validity); } template void test_all() @@ -55,9 +56,6 @@ void test_all() typedef typename bg::coordinate_type

::type ct; - ut_settings ignore_validity; - ignore_validity.test_validity = false; - ut_settings sym_settings; #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) sym_settings.sym_difference = false; @@ -211,8 +209,7 @@ void test_all() test_one("case_80", case_80[0], case_80[1], 1, 9, 44.5, - 1, 10, 84.5, - ignore_validity); + 1, 10, 84.5); #ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS // Fails, holes are not subtracted @@ -540,20 +537,16 @@ void test_all() TEST_DIFFERENCE(mysql_21965285, 1, 92.0, 1, 14.0, 1); - TEST_DIFFERENCE_IGNORE(mysql_23023665_1, 1, 92.0, 1, 142.5, 2); - TEST_DIFFERENCE_IGNORE(mysql_23023665_2, 1, 96.0, 1, 16.0, 2); - TEST_DIFFERENCE_IGNORE(mysql_23023665_3, 1, 225.0, 1, 66.0, 2); + TEST_DIFFERENCE(mysql_23023665_1, 1, 92.0, 1, 142.5, 2); + TEST_DIFFERENCE(mysql_23023665_2, 1, 96.0, 1, 16.0, 2); + TEST_DIFFERENCE(mysql_23023665_3, 1, 225.0, 1, 66.0, 2); #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_DIFFERENCE(mysql_23023665_5, 2, 165.23735, 2, 105.73735, 4); #else TEST_DIFFERENCE_IGNORE(mysql_23023665_5, 1, 165.23735, 2, 105.73735, 3); #endif -#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) TEST_DIFFERENCE(mysql_23023665_6, 2, 105.68756, 3, 10.18756, 5); -#else - TEST_DIFFERENCE_IGNORE(mysql_23023665_6, 2, 105.68756, 3, 10.18756, 5); -#endif #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_DIFFERENCE(mysql_23023665_13, 3, 99.74526, 3, 37.74526, 6); @@ -585,15 +578,10 @@ void test_specific() 2, 8, 489763.5, 1, 4, 6731652.0); - // Generates spikes, both a-b and b-a #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_DIFFERENCE(ticket_11676, 2, 2537992.5, 2, 294963.5, 3); #else - - ut_settings ignore_validity; - ignore_validity.test_validity = false; - TEST_DIFFERENCE_IGNORE(ticket_11676, 1, 2537992.5, 2, 294963.5, 2); #endif } diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index c11568838..be0ee294b 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -30,9 +30,10 @@ clips3, -1, area1 + area2) #define TEST_DIFFERENCE_IGNORE(caseid, clips1, area1, clips2, area2, clips3) \ + { ut_settings ignore_validity; ignore_validity.test_validity = false; \ (test_one) \ ( #caseid, caseid[0], caseid[1], clips1, -1, area1, clips2, -1, area2, \ - clips3, -1, area1 + area2, ignore_validity) + clips3, -1, area1 + area2, ignore_validity); } #define TEST_DIFFERENCE_WITH(index1, index2, caseid, clips1, area1, \ clips2, area2, clips3) \ @@ -46,9 +47,6 @@ template void test_areal() { - ut_settings ignore_validity; - ignore_validity.test_validity = false; - ut_settings sym_settings; #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) sym_settings.sym_difference = false; @@ -94,7 +92,7 @@ void test_areal() test_one("case_72_multi", case_72_multi[0], case_72_multi[1], - 3, 13, 1.65, 3, 17, 6.15, ignore_validity); + 3, 13, 1.65, 3, 17, 6.15); test_one("case_77_multi", case_77_multi[0], case_77_multi[1], @@ -178,11 +176,7 @@ void test_areal() tolerance(0.001)); // POSTGIS areas: 3.75893745345145, 2.5810000723917e-15 - test_one("bug_21155501", - bug_21155501[0], bug_21155501[1], - 1, 9, 3.758937, - 0, 0, 0.0, - ignore_validity); + TEST_DIFFERENCE_IGNORE(bug_21155501, 1, 3.758937, 0, 0.0, 1); #endif // Areas and #clips correspond with POSTGIS (except sym case) From 41a5570251a6dcae78c00f5fbf63c01d6fda9b2d Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 28 Jun 2017 22:35:06 +0200 Subject: [PATCH 002/188] [test] update test information on validity --- .../algorithms/set_operations/intersection/intersection.cpp | 3 +-- .../set_operations/intersection/intersection_multi.cpp | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/test/algorithms/set_operations/intersection/intersection.cpp b/test/algorithms/set_operations/intersection/intersection.cpp index 781fb03f1..10d23addd 100644 --- a/test/algorithms/set_operations/intersection/intersection.cpp +++ b/test/algorithms/set_operations/intersection/intersection.cpp @@ -48,8 +48,7 @@ BOOST_GEOMETRY_REGISTER_LINESTRING_TEMPLATED(std::vector) #if ! defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS) #define TEST_INTERSECTION_IGNORE(caseid, clips, points, area) \ - { ut_settings ignore_validity; \ - ignore_validity.test_validity = false; \ + { ut_settings ignore_validity; ignore_validity.test_validity = false; \ (test_one) \ ( #caseid, caseid[0], caseid[1], clips, points, area, ignore_validity); } #endif diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 10a6ff5d1..ef7e5226a 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -33,15 +33,13 @@ ( #caseid, caseid[0], caseid[1], clips, points, area) #define TEST_INTERSECTION_IGNORE(caseid, clips, points, area) \ + { ut_settings ignore_validity; ignore_validity.test_validity = false; \ (test_one) \ - ( #caseid, caseid[0], caseid[1], clips, points, area, ignore_validity) + ( #caseid, caseid[0], caseid[1], clips, points, area, ignore_validity); } template void test_areal() { - ut_settings ignore_validity; - ignore_validity.test_validity = false; - test_one("simplex_multi", case_multi_simplex[0], case_multi_simplex[1], 2, 12, 6.42); From 1026f9fd366b3e67235de3f9692cdcd8e263367a Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 2 Jul 2017 19:04:12 +0200 Subject: [PATCH 003/188] [strategies] Handle degenerated segments near poles in spherical and geographic intersection strategies. --- .../strategies/geographic/intersection.hpp | 139 +++++++++---- .../strategies/spherical/intersection.hpp | 182 +++++++++++++----- 2 files changed, 236 insertions(+), 85 deletions(-) diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp index 84acd149c..59a40f281 100644 --- a/include/boost/geometry/strategies/geographic/intersection.hpp +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -289,6 +289,8 @@ private: typedef typename select_calculation_type ::type calc_t; + static const calc_t c0 = 0; + // normalized spheroid srs::spheroid spheroid = normalized_spheroid(m_spheroid); @@ -325,31 +327,80 @@ private: // TODO: no need to call inverse formula if we know that the points are equal // distance can be set to 0 in this case and azimuth may be not calculated - bool const is_equal_a1_b1 = equals_point_point(a1, b1); - bool const is_equal_a2_b1 = equals_point_point(a2, b1); + bool is_equal_a1_b1 = equals_point_point(a1, b1); + bool is_equal_a2_b1 = equals_point_point(a2, b1); + bool degen_neq_coords = false; - inverse_result res_b1_b2 = inverse_dist_azi::apply(b1_lon, b1_lat, b2_lon, b2_lat, spheroid); - inverse_result res_b1_a1 = inverse_dist_azi::apply(b1_lon, b1_lat, a1_lon, a1_lat, spheroid); - inverse_result res_b1_a2 = inverse_dist_azi::apply(b1_lon, b1_lat, a2_lon, a2_lat, spheroid); - sides.set<0>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_b1_a1.azimuth, res_b1_b2.azimuth), - is_equal_a2_b1 ? 0 : formula::azimuth_side_value(res_b1_a2.azimuth, res_b1_b2.azimuth)); - if (sides.same<0>()) + inverse_result res_b1_b2, res_b1_a1, res_b1_a2; + if (! b_is_point) { - // Both points are at the same side of other segment, we can leave - return Policy::disjoint(); + res_b1_b2 = inverse_dist_azi::apply(b1_lon, b1_lat, b2_lon, b2_lat, spheroid); + if (math::equals(res_b1_b2.distance, c0)) + { + b_is_point = true; + degen_neq_coords = true; + } + else + { + res_b1_a1 = inverse_dist_azi::apply(b1_lon, b1_lat, a1_lon, a1_lat, spheroid); + if (math::equals(res_b1_a1.distance, c0)) + { + is_equal_a1_b1 = true; + } + res_b1_a2 = inverse_dist_azi::apply(b1_lon, b1_lat, a2_lon, a2_lat, spheroid); + if (math::equals(res_b1_a2.distance, c0)) + { + is_equal_a2_b1 = true; + } + sides.set<0>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_b1_a1.azimuth, res_b1_b2.azimuth), + is_equal_a2_b1 ? 0 : formula::azimuth_side_value(res_b1_a2.azimuth, res_b1_b2.azimuth)); + if (sides.same<0>()) + { + // Both points are at the same side of other segment, we can leave + return Policy::disjoint(); + } + } } - bool const is_equal_a1_b2 = equals_point_point(a1, b2); + bool is_equal_a1_b2 = equals_point_point(a1, b2); - inverse_result res_a1_a2 = inverse_dist_azi::apply(a1_lon, a1_lat, a2_lon, a2_lat, spheroid); - inverse_result res_a1_b1 = inverse_dist_azi::apply(a1_lon, a1_lat, b1_lon, b1_lat, spheroid); - inverse_result res_a1_b2 = inverse_dist_azi::apply(a1_lon, a1_lat, b2_lon, b2_lat, spheroid); - sides.set<1>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_a1_b1.azimuth, res_a1_a2.azimuth), - is_equal_a1_b2 ? 0 : formula::azimuth_side_value(res_a1_b2.azimuth, res_a1_a2.azimuth)); - if (sides.same<1>()) + inverse_result res_a1_a2, res_a1_b1, res_a1_b2; + if (! a_is_point) { - // Both points are at the same side of other segment, we can leave - return Policy::disjoint(); + res_a1_a2 = inverse_dist_azi::apply(a1_lon, a1_lat, a2_lon, a2_lat, spheroid); + if (math::equals(res_a1_a2.distance, c0)) + { + a_is_point = true; + degen_neq_coords = true; + } + else + { + res_a1_b1 = inverse_dist_azi::apply(a1_lon, a1_lat, b1_lon, b1_lat, spheroid); + if (math::equals(res_a1_b1.distance, c0)) + { + is_equal_a1_b1 = true; + } + res_a1_b2 = inverse_dist_azi::apply(a1_lon, a1_lat, b2_lon, b2_lat, spheroid); + if (math::equals(res_a1_b2.distance, c0)) + { + is_equal_a1_b2 = true; + } + sides.set<1>(is_equal_a1_b1 ? 0 : formula::azimuth_side_value(res_a1_b1.azimuth, res_a1_a2.azimuth), + is_equal_a1_b2 ? 0 : formula::azimuth_side_value(res_a1_b2.azimuth, res_a1_a2.azimuth)); + if (sides.same<1>()) + { + // Both points are at the same side of other segment, we can leave + return Policy::disjoint(); + } + } + } + + if(a_is_point && b_is_point) + { + return is_equal_a1_b2 + ? Policy::degenerate(a, true) + : Policy::disjoint() + ; } // NOTE: at this point the segments may still be disjoint @@ -379,11 +430,11 @@ private: { if (a_is_point) { - return collinear_one_degenerated(a, true, b1, b2, a1, a2, res_b1_b2, res_b1_a1, is_b_reversed); + return collinear_one_degenerated(a, true, b1, b2, a1, a2, res_b1_b2, res_b1_a1, res_b1_a2, is_b_reversed, degen_neq_coords); } else if (b_is_point) { - return collinear_one_degenerated(b, false, a1, a2, b1, b2, res_a1_a2, res_a1_b1, is_a_reversed); + return collinear_one_degenerated(b, false, a1, a2, b1, b2, res_a1_a2, res_a1_b1, res_a1_b2, is_a_reversed, degen_neq_coords); } else { @@ -392,16 +443,16 @@ private: // use shorter segment if (res_a1_a2.distance <= res_b1_b2.distance) { - calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b1, dist_a1_a2, dist_a1_b1); - calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b2, dist_a1_a2, dist_a1_b2); + calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b1, res_a1_b2, dist_a1_a2, dist_a1_b1); + calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b2, res_a1_b1, dist_a1_a2, dist_a1_b2); dist_b1_b2 = dist_a1_b2 - dist_a1_b1; dist_b1_a1 = -dist_a1_b1; dist_b1_a2 = dist_a1_a2 - dist_a1_b1; } else { - calculate_collinear_data(b1, b2, a1, a2, res_b1_b2, res_b1_a1, dist_b1_b2, dist_b1_a1); - calculate_collinear_data(b1, b2, a1, a2, res_b1_b2, res_b1_a2, dist_b1_b2, dist_b1_a2); + calculate_collinear_data(b1, b2, a1, a2, res_b1_b2, res_b1_a1, res_b1_a2, dist_b1_b2, dist_b1_a1); + calculate_collinear_data(b1, b2, a1, a2, res_b1_b2, res_b1_a2, res_b1_a1, dist_b1_b2, dist_b1_a2); dist_a1_a2 = dist_b1_a2 - dist_b1_a1; dist_a1_b1 = -dist_b1_a1; dist_a1_b2 = dist_b1_b2 - dist_b1_a1; @@ -549,11 +600,13 @@ private: Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2, ResultInverse const& res_a1_a2, - ResultInverse const& res_a1_bi, - bool is_other_reversed) + ResultInverse const& res_a1_b1, + ResultInverse const& res_a1_b2, + bool is_other_reversed, + bool degen_neq_coords) { CalcT dist_1_2, dist_1_o; - if (! calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_bi, dist_1_2, dist_1_o)) + if (! calculate_collinear_data(a1, a2, b1, b2, res_a1_a2, res_a1_b1, res_a1_b2, dist_1_2, dist_1_o, degen_neq_coords)) { return Policy::disjoint(); } @@ -574,13 +627,16 @@ private: static inline bool calculate_collinear_data(Point1 const& a1, Point1 const& a2, // in Point2 const& b1, Point2 const& b2, // in ResultInverse const& res_a1_a2, // in - ResultInverse const& res_a1_bi, // in - CalcT& dist_a1_a2, CalcT& dist_a1_bi) // out + ResultInverse const& res_a1_b1, // in + ResultInverse const& res_a1_b2, // in + CalcT& dist_a1_a2, // out + CalcT& dist_a1_bi, // out + bool degen_neq_coords = false) // in { dist_a1_a2 = res_a1_a2.distance; - dist_a1_bi = res_a1_bi.distance; - if (! same_direction(res_a1_bi.azimuth, res_a1_a2.azimuth)) + dist_a1_bi = res_a1_b1.distance; + if (! same_direction(res_a1_b1.azimuth, res_a1_a2.azimuth)) { dist_a1_bi = -dist_a1_bi; } @@ -598,6 +654,22 @@ private: return true; } + // check the other endpoint of a very short segment near the pole + if (degen_neq_coords) + { + static CalcT const c0 = 0; + if (math::equals(res_a1_b2.distance, c0)) + { + dist_a1_bi = 0; + return true; + } + else if (math::equals(dist_a1_a2 - res_a1_b2.distance, c0)) + { + dist_a1_bi = dist_a1_a2; + return true; + } + } + // or i1 is on b return segment_ratio(dist_a1_bi, dist_a1_a2).on_segment(); } @@ -825,8 +897,9 @@ private: static inline bool is_endpoint_equal(CalcT const& dist, P1 const& ai, P2 const& b1, P2 const& b2) { + static CalcT const c0 = 0; using geometry::detail::equals::equals_point_point; - return is_near(dist) && (equals_point_point(ai, b1) || equals_point_point(ai, b2)); + return is_near(dist) && (equals_point_point(ai, b1) || equals_point_point(ai, b2) || math::equals(dist, c0)); } template diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index 035961c0a..44b1cc62b 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -302,36 +302,64 @@ struct ecef_segments vec3d_t const b1v = calc_policy.template to_cart3d(b1); vec3d_t const b2v = calc_policy.template to_cart3d(b2); + bool degen_neq_coords = false; side_info sides; typename CalcPolicy::template plane plane2 = calc_policy.get_plane(b1v, b2v); - // not normalized normals, the same as in side strategy - sides.set<0>(plane2.side_value(a1v), plane2.side_value(a2v)); - if (sides.same<0>()) + calc_t dist_b1_b2 = 0; + if (! b_is_point) { - // Both points are at same side of other segment, we can leave - return Policy::disjoint(); + calculate_dist(b1v, b2v, plane2, dist_b1_b2); + if (math::equals(dist_b1_b2, c0)) + { + degen_neq_coords = true; + b_is_point = true; + dist_b1_b2 = 0; + } + else + { + // not normalized normals, the same as in side strategy + sides.set<0>(plane2.side_value(a1v), plane2.side_value(a2v)); + if (sides.same<0>()) + { + // Both points are at same side of other segment, we can leave + return Policy::disjoint(); + } + } } typename CalcPolicy::template plane plane1 = calc_policy.get_plane(a1v, a2v); - // not normalized normals, the same as in side strategy - sides.set<1>(plane1.side_value(b1v), plane1.side_value(b2v)); - if (sides.same<1>()) + calc_t dist_a1_a2 = 0; + if (! a_is_point) { - // Both points are at same side of other segment, we can leave - return Policy::disjoint(); + calculate_dist(a1v, a2v, plane1, dist_a1_a2); + if (math::equals(dist_a1_a2, c0)) + { + degen_neq_coords = true; + a_is_point = true; + dist_a1_a2 = 0; + } + else + { + // not normalized normals, the same as in side strategy + sides.set<1>(plane1.side_value(b1v), plane1.side_value(b2v)); + if (sides.same<1>()) + { + // Both points are at same side of other segment, we can leave + return Policy::disjoint(); + } + } } // NOTE: at this point the segments may still be disjoint - calc_t len1, len2; - + calc_t len1 = 0; // point or opposite sides of a sphere/spheroid, assume point - if (! detail::vec_normalize(plane1.normal, len1)) + if (! a_is_point && ! detail::vec_normalize(plane1.normal, len1)) { a_is_point = true; if (sides.get<0, 0>() == 0 || sides.get<0, 1>() == 0) @@ -340,7 +368,8 @@ struct ecef_segments } } - if (! detail::vec_normalize(plane2.normal, len2)) + calc_t len2 = 0; + if (! b_is_point && ! detail::vec_normalize(plane2.normal, len2)) { b_is_point = true; if (sides.get<1, 0>() == 0 || sides.get<1, 1>() == 0) @@ -404,30 +433,32 @@ struct ecef_segments { if (a_is_point) { - return collinear_one_degenerated(a, true, b1, b2, a1, a2, b1v, b2v, plane2, a1v); + return collinear_one_degenerated(a, true, b1, b2, a1, a2, b1v, b2v, + plane2, a1v, a2v, dist_b1_b2, degen_neq_coords); } else if (b_is_point) { // b2 used to be consistent with (degenerated) checks above (is it needed?) - return collinear_one_degenerated(b, false, a1, a2, b1, b2, a1v, a2v, plane1, b1v); + return collinear_one_degenerated(b, false, a1, a2, b1, b2, a1v, a2v, + plane1, b1v, b2v, dist_a1_a2, degen_neq_coords); } else { - calc_t dist_a1_a2, dist_a1_b1, dist_a1_b2; - calc_t dist_b1_b2, dist_b1_a1, dist_b1_a2; + calc_t dist_a1_b1, dist_a1_b2; + calc_t dist_b1_a1, dist_b1_a2; // use shorter segment if (len1 <= len2) { - calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b1v, dist_a1_a2, dist_a1_b1); - calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b2v, dist_a1_a2, dist_a1_b2); + calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b1v, b2v, dist_a1_a2, dist_a1_b1); + calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane1, b2v, b1v, dist_a1_a2, dist_a1_b2); dist_b1_b2 = dist_a1_b2 - dist_a1_b1; dist_b1_a1 = -dist_a1_b1; dist_b1_a2 = dist_a1_a2 - dist_a1_b1; } else { - calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a1v, dist_b1_b2, dist_b1_a1); - calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a2v, dist_b1_b2, dist_b1_a2); + calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a1v, a2v, dist_b1_b2, dist_b1_a1); + calculate_collinear_data(b1, b2, a1, a2, b1v, b2v, plane2, a2v, a1v, dist_b1_b2, dist_b1_a2); dist_a1_a2 = dist_b1_a2 - dist_b1_a1; dist_a1_b1 = -dist_b1_a1; dist_a1_b2 = dist_b1_b2 - dist_b1_a1; @@ -487,10 +518,11 @@ struct ecef_segments vec3d_t i1; intersection_point_flag ip_flag; - calc_t dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1; + calc_t dist_a1_i1, dist_b1_i1; if (calculate_ip_data(a1, a2, b1, b2, a1v, a2v, b1v, b2v, - plane1, plane2, calc_policy, sides, - i1, dist_a1_a2, dist_a1_i1, dist_b1_b2, dist_b1_i1, ip_flag)) + plane1, plane2, calc_policy, + sides, dist_a1_a2, dist_b1_b2, + i1, dist_a1_i1, dist_b1_i1, ip_flag)) { // intersects segment_intersection_info @@ -520,12 +552,14 @@ private: collinear_one_degenerated(Segment const& segment, bool degenerated_a, Point1 const& a1, Point1 const& a2, Point2 const& b1, Point2 const& b2, - Vec3d const& v1, Vec3d const& v2, + Vec3d const& a1v, Vec3d const& a2v, Plane const& plane, - Vec3d const& vother) + Vec3d const& b1v, Vec3d const& b2v, + CalcT const& dist_1_2, + bool degen_neq_coords) { - CalcT dist_1_2, dist_1_o; - return ! calculate_collinear_data(a1, a2, b1, b2, v1, v2, plane, vother, dist_1_2, dist_1_o) + CalcT dist_1_o; + return ! calculate_collinear_data(a1, a2, b1, b2, a1v, a2v, plane, b1v, b2v, dist_1_2, dist_1_o, degen_neq_coords) ? Policy::disjoint() : Policy::one_degenerate(segment, segment_ratio(dist_1_o, dist_1_2), degenerated_a); } @@ -536,11 +570,14 @@ private: Vec3d const& a1v, // in Vec3d const& a2v, // in Plane const& plane1, // in - Vec3d const& b1v_or_b2v, // in - CalcT& dist_a1_a2, CalcT& dist_a1_i1) // out + Vec3d const& b1v, // in + Vec3d const& b2v, // in + CalcT const& dist_a1_a2, // in + CalcT& dist_a1_i1, // out + bool degen_neq_coords = false) // in { // calculate dist_a1_a2 and dist_a1_i1 - calculate_dists(a1v, a2v, plane1, b1v_or_b2v, dist_a1_a2, dist_a1_i1); + calculate_dist(a1v, a2v, plane1, b1v, dist_a1_i1); // if i1 is close to a1 and b1 or b2 is equal to a1 if (is_endpoint_equal(dist_a1_i1, a1, b1, b2)) @@ -555,6 +592,26 @@ private: return true; } + // check the other endpoint of a very short segment near the pole + if (degen_neq_coords) + { + static CalcT const c0 = 0; + + CalcT dist_a1_i2 = 0; + calculate_dist(a1v, a2v, plane1, b2v, dist_a1_i2); + + if (math::equals(dist_a1_i2, c0)) + { + dist_a1_i1 = 0; + return true; + } + else if (math::equals(dist_a1_a2 - dist_a1_i2, c0)) + { + dist_a1_i1 = dist_a1_a2; + return true; + } + } + // or i1 is on b return segment_ratio(dist_a1_i1, dist_a1_a2).on_segment(); } @@ -568,15 +625,17 @@ private: Plane const& plane2, // in CalcPolicy const& calc_policy, // in side_info const& sides, // in - Vec3d & ip, // out - CalcT& dist_a1_a2, CalcT& dist_a1_ip, // out - CalcT& dist_b1_b2, CalcT& dist_b1_ip, // out - intersection_point_flag& ip_flag) // out + CalcT const& dist_a1_a2, // in + CalcT const& dist_b1_b2, // in + Vec3d & ip, // out + CalcT& dist_a1_ip, // out + CalcT& dist_b1_ip, // out + intersection_point_flag& ip_flag) // out { Vec3d ip1, ip2; calc_policy.intersection_points(plane1, plane2, ip1, ip2); - calculate_dists(a1v, a2v, plane1, ip1, dist_a1_a2, dist_a1_ip); + calculate_dist(a1v, a2v, plane1, ip1, dist_a1_ip); ip = ip1; // choose the opposite side of the globe if the distance is shorter @@ -602,7 +661,7 @@ private: return false; } - calculate_dists(b1v, b2v, plane2, ip, dist_b1_b2, dist_b1_ip); + calculate_dist(b1v, b2v, plane2, ip, dist_b1_ip); bool is_on_b = false, is_near_b1 = false, is_near_b2 = false; if (! is_potentially_crossing(dist_b1_b2, dist_b1_ip, is_on_b, is_near_b1, is_near_b2)) @@ -701,20 +760,26 @@ private: } template - static inline void calculate_dists(Vec3d const& a1v, // in - Vec3d const& a2v, // in - Plane const& plane1, // in - Vec3d const& i1, // in - CalcT& dist_a1_a2, // out - CalcT& dist_a1_i1) // out + static inline void calculate_dist(Vec3d const& a1v, // in + Vec3d const& a2v, // in + Plane const& plane1, // in + CalcT& dist_a1_a2) // out { - //CalcT const c0 = 0; - CalcT const c1 = 1; - CalcT const c2 = 2; - CalcT const c4 = 4; - - CalcT cos_a1_a2 = plane1.cos_angle_between(a1v, a2v); + static CalcT const c1 = 1; + CalcT const cos_a1_a2 = plane1.cos_angle_between(a1v, a2v); dist_a1_a2 = -cos_a1_a2 + c1; // [1, -1] -> [0, 2] representing [0, pi] + } + + template + static inline void calculate_dist(Vec3d const& a1v, // in + Vec3d const& /*a2v*/, // in + Plane const& plane1, // in + Vec3d const& i1, // in + CalcT& dist_a1_i1) // out + { + static CalcT const c1 = 1; + static CalcT const c2 = 2; + static CalcT const c4 = 4; bool is_forward = true; CalcT cos_a1_i1 = plane1.cos_angle_between(a1v, i1, is_forward); @@ -728,7 +793,19 @@ private: dist_a1_i1 += c4; // += 2pi } } - + /* + template + static inline void calculate_dists(Vec3d const& a1v, // in + Vec3d const& a2v, // in + Plane const& plane1, // in + Vec3d const& i1, // in + CalcT& dist_a1_a2, // out + CalcT& dist_a1_i1) // out + { + calculate_dist(a1v, a2v, plane1, dist_a1_a2); + calculate_dist(a1v, a2v, plane1, i1, dist_a1_i1); + } + */ // the dist of the ip on the other side of the sphere template static inline CalcT dist_of_i2(CalcT const& dist_a1_i1) @@ -769,8 +846,9 @@ private: static inline bool is_endpoint_equal(CalcT const& dist, P1 const& ai, P2 const& b1, P2 const& b2) { + static CalcT const c0 = 0; using geometry::detail::equals::equals_point_point; - return is_near(dist) && (equals_point_point(ai, b1) || equals_point_point(ai, b2)); + return is_near(dist) && (equals_point_point(ai, b1) || equals_point_point(ai, b2) || math::equals(dist, c0)); } template From 4122cab8221a734168605a35f818e0de775fb37b Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 4 Jul 2017 04:42:13 +0200 Subject: [PATCH 004/188] [correct] Allow passing area strategy into correct(). --- include/boost/geometry/algorithms/correct.hpp | 95 +++++++++++++------ .../algorithms/detail/multi_modify.hpp | 16 ++++ 2 files changed, 82 insertions(+), 29 deletions(-) diff --git a/include/boost/geometry/algorithms/correct.hpp b/include/boost/geometry/algorithms/correct.hpp index 5d3b6939a..a572d921d 100644 --- a/include/boost/geometry/algorithms/correct.hpp +++ b/include/boost/geometry/algorithms/correct.hpp @@ -5,6 +5,10 @@ // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -61,7 +65,8 @@ namespace detail { namespace correct template struct correct_nop { - static inline void apply(Geometry& ) + template + static inline void apply(Geometry& , Strategy const& ) {} }; @@ -104,8 +109,8 @@ struct correct_box_loop template struct correct_box { - - static inline void apply(Box& box) + template + static inline void apply(Box& box, Strategy const& ) { // Currently only for Cartesian coordinates // (or spherical without crossing dateline) @@ -119,18 +124,12 @@ struct correct_box // Close a ring, if not closed -template +template class Predicate> struct correct_ring { typedef typename point_type::type point_type; typedef typename coordinate_type::type coordinate_type; - typedef typename strategy::area::services::default_strategy - < - typename cs_tag::type, - point_type - >::type strategy_type; - typedef detail::area::ring_area < order_as_direction::value>::value, @@ -138,7 +137,8 @@ struct correct_ring > ring_area_type; - static inline void apply(Ring& r) + template + static inline void apply(Ring& r, Strategy const& strategy) { // Check close-ness if (boost::size(r) > 2) @@ -158,10 +158,10 @@ struct correct_ring } } // Check area - Predicate predicate; - typedef typename default_area_result::type area_result_type; - area_result_type const zero = area_result_type(); - if (predicate(ring_area_type::apply(r, strategy_type()), zero)) + typedef typename Strategy::return_type area_result_type; + Predicate predicate; + area_result_type const zero = 0; + if (predicate(ring_area_type::apply(r, strategy), zero)) { std::reverse(boost::begin(r), boost::end(r)); } @@ -174,15 +174,15 @@ template struct correct_polygon { typedef typename ring_type::type ring_type; - typedef typename default_area_result::type area_result_type; - - static inline void apply(Polygon& poly) + + template + static inline void apply(Polygon& poly, Strategy const& strategy) { correct_ring < ring_type, - std::less - >::apply(exterior_ring(poly)); + std::less + >::apply(exterior_ring(poly), strategy); typename interior_return_type::type rings = interior_rings(poly); @@ -192,8 +192,8 @@ struct correct_polygon correct_ring < ring_type, - std::greater - >::apply(*it); + std::greater + >::apply(*it, strategy); } } }; @@ -237,7 +237,7 @@ struct correct : detail::correct::correct_ring < Ring, - std::less::type> + std::less > {}; @@ -281,29 +281,36 @@ namespace resolve_variant { template struct correct { - static inline void apply(Geometry& geometry) + template + static inline void apply(Geometry& geometry, Strategy const& strategy) { concepts::check(); - dispatch::correct::apply(geometry); + dispatch::correct::apply(geometry, strategy); } }; template struct correct > { + template struct visitor: boost::static_visitor { + Strategy const& m_strategy; + + visitor(Strategy const& strategy): m_strategy(strategy) {} + template void operator()(Geometry& geometry) const { - correct::apply(geometry); + correct::apply(geometry, m_strategy); } }; + template static inline void - apply(boost::variant& geometry) + apply(boost::variant& geometry, Strategy const& strategy) { - boost::apply_visitor(visitor(), geometry); + boost::apply_visitor(visitor(strategy), geometry); } }; @@ -325,7 +332,37 @@ struct correct > template inline void correct(Geometry& geometry) { - resolve_variant::correct::apply(geometry); + typedef typename point_type::type point_type; + + typedef typename strategy::area::services::default_strategy + < + typename cs_tag::type, + point_type + >::type strategy_type; + + resolve_variant::correct::apply(geometry, strategy_type()); +} + +/*! +\brief Corrects a geometry +\details Corrects a geometry: all rings which are wrongly oriented with respect + to their expected orientation are reversed. To all rings which do not have a + closing point and are typed as they should have one, the first point is + appended. Also boxes can be corrected. +\ingroup correct +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Area} +\param geometry \param_geometry which will be corrected if necessary +\param strategy \param_strategy{area} + +\qbk{distinguish,with strategy} + +\qbk{[include reference/algorithms/correct.qbk]} +*/ +template +inline void correct(Geometry& geometry, Strategy const& strategy) +{ + resolve_variant::correct::apply(geometry, strategy); } #if defined(_MSC_VER) diff --git a/include/boost/geometry/algorithms/detail/multi_modify.hpp b/include/boost/geometry/algorithms/detail/multi_modify.hpp index f0b9ddd3e..23187f932 100644 --- a/include/boost/geometry/algorithms/detail/multi_modify.hpp +++ b/include/boost/geometry/algorithms/detail/multi_modify.hpp @@ -4,6 +4,10 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -40,6 +44,18 @@ struct multi_modify Policy::apply(*it); } } + + template + static inline void apply(MultiGeometry& multi, Strategy const& strategy) + { + typedef typename boost::range_iterator::type iterator_type; + for (iterator_type it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + Policy::apply(*it, strategy); + } + } }; From 8a12ebdb694aef6fc0d83f14c48db5d890f96051 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 4 Jul 2017 04:45:37 +0200 Subject: [PATCH 005/188] [buffer][is_valid][overlay] Use area strategy got from intersection strategy. --- .../buffer/buffered_piece_collection.hpp | 41 +++++++----- .../algorithms/detail/is_valid/ring.hpp | 5 +- .../detail/overlay/assign_parents.hpp | 16 +++-- .../algorithms/detail/overlay/overlay.hpp | 26 ++++++-- .../detail/overlay/ring_properties.hpp | 10 +-- .../detail/overlay/select_rings.hpp | 65 +++++++++++-------- 6 files changed, 103 insertions(+), 60 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index aa6302f39..4bb7a281f 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -142,10 +142,20 @@ struct buffered_piece_collection robust_point_type >::type robust_comparable_radius_type; - typedef typename strategy::side::services::default_strategy + typedef typename IntersectionStrategy::side_strategy_type side_strategy_type; + + typedef typename IntersectionStrategy::template area_strategy < - typename cs_tag::type - >::type side_strategy; + point_type + >::type area_strategy_type; + + typedef typename IntersectionStrategy::template area_strategy + < + robust_point_type + >::type robust_area_strategy_type; + + typedef typename area_strategy_type::return_type area_result_type; + typedef typename robust_area_strategy_type::return_type robust_area_result_type; typedef typename geometry::rescale_policy_type < @@ -306,7 +316,10 @@ struct buffered_piece_collection cluster_type m_clusters; - IntersectionStrategy const& m_intersection_strategy; + IntersectionStrategy m_intersection_strategy; + side_strategy_type m_side_strategy; + area_strategy_type m_area_strategy; + robust_area_strategy_type m_robust_area_strategy; RobustPolicy const& m_robust_policy; struct redundant_turn @@ -321,6 +334,9 @@ struct buffered_piece_collection RobustPolicy const& robust_policy) : m_first_piece_index(-1) , m_intersection_strategy(intersection_strategy) + , m_side_strategy(intersection_strategy.get_side_strategy()) + , m_area_strategy(intersection_strategy.template get_area_strategy()) + , m_robust_area_strategy(intersection_strategy.template get_area_strategy()) , m_robust_policy(robust_policy) {} @@ -699,7 +715,7 @@ struct buffered_piece_collection ++it) { piece& pc = *it; - if (geometry::area(pc.robust_ring) < 0) + if (geometry::area(pc.robust_ring, m_robust_area_strategy) < 0) { // Rings can be ccw: // - in a concave piece @@ -1220,14 +1236,9 @@ struct buffered_piece_collection inline void enrich() { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy_type; - enrich_intersection_points(m_turns, m_clusters, offsetted_rings, offsetted_rings, - m_robust_policy, side_strategy_type()); + m_robust_policy, m_side_strategy); } // Discards all rings which do have not-OK intersection points only. @@ -1314,7 +1325,7 @@ struct buffered_piece_collection buffered_ring& ring = *it; if (! ring.has_intersections() && boost::size(ring) > 0u - && geometry::area(ring) < 0) + && geometry::area(ring, m_area_strategy) < 0) { if (! point_coveredby_original(geometry::range::front(ring))) { @@ -1391,7 +1402,7 @@ struct buffered_piece_collection template inline OutputIterator assign(OutputIterator out) const { - typedef detail::overlay::ring_properties properties; + typedef detail::overlay::ring_properties properties; std::map selected; @@ -1407,7 +1418,7 @@ struct buffered_piece_collection if (! it->has_intersections() && ! it->is_untouched_outside_original) { - properties p = properties(*it); + properties p = properties(*it, m_area_strategy); if (p.valid) { ring_identifier id(0, index, -1); @@ -1423,7 +1434,7 @@ struct buffered_piece_collection it != boost::end(traversed_rings); ++it, ++index) { - properties p = properties(*it); + properties p = properties(*it, m_area_strategy); if (p.valid) { ring_identifier id(2, index, -1); diff --git a/include/boost/geometry/algorithms/detail/is_valid/ring.hpp b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp index 9ab68fdc4..996da7d96 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/ring.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp @@ -115,7 +115,10 @@ struct is_properly_oriented geometry::closure::value > ring_area_type; - typedef typename default_area_result::type area_result_type; + typedef typename Strategy::template area_strategy + < + point_type + >::type::return_type area_result_type; typename ring_area_predicate < diff --git a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp index e8bb2c1ac..78160f520 100644 --- a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -107,21 +107,19 @@ static inline bool within_selected_input(Item const& item2, } -template +template struct ring_info_helper { - typedef typename geometry::default_area_result::type area_type; - ring_identifier id; - area_type real_area; - area_type abs_area; + AreaType real_area; + AreaType abs_area; model::box envelope; inline ring_info_helper() : real_area(0), abs_area(0) {} - inline ring_info_helper(ring_identifier i, area_type a) + inline ring_info_helper(ring_identifier i, AreaType const& a) : id(i), real_area(a), abs_area(geometry::math::abs(a)) {} }; @@ -234,11 +232,15 @@ inline void assign_parents(Geometry1 const& geometry1, typedef typename RingMap::mapped_type ring_info_type; typedef typename ring_info_type::point_type point_type; typedef model::box box_type; + typedef typename Strategy::template area_strategy + < + point_type + >::type::return_type area_result_type; typedef typename RingMap::iterator map_iterator_type; { - typedef ring_info_helper helper; + typedef ring_info_helper helper; typedef std::vector vector_type; typedef typename boost::range_iterator::type vector_iterator_type; diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 90258cc84..10829abd4 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -197,7 +197,16 @@ inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, typename geometry::ring_type::type > ring_container_type; - typedef ring_properties::type> properties; + typedef typename geometry::point_type::type point_type1; + + typedef ring_properties + < + point_type1, + typename Strategy::template area_strategy + < + point_type1 + >::type::return_type + > properties; // Silence warning C4127: conditional expression is constant #if defined(_MSC_VER) @@ -309,7 +318,7 @@ std::cout << "get turns" << std::endl; #ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE std::cout << "enrich" << std::endl; #endif - typename Strategy::side_strategy_type side_strategy; + typename Strategy::side_strategy_type side_strategy = strategy.get_side_strategy(); cluster_type clusters; geometry::enrich_intersection_points(turns, @@ -341,10 +350,13 @@ std::cout << "traverse" << std::endl; std::map turn_info_per_ring; get_ring_turn_info(turn_info_per_ring, turns, clusters); + typedef typename Strategy::template area_strategy::type area_strategy_type; + typedef ring_properties - < - typename geometry::point_type::type - > properties; + < + point_type, + typename area_strategy_type::return_type + > properties; // Select all rings which are NOT touched by any intersection point std::map selected_ring_properties; @@ -353,13 +365,15 @@ std::cout << "traverse" << std::endl; // Add rings created during traversal { + area_strategy_type const area_strategy = strategy.template get_area_strategy(); + ring_identifier id(2, 0, -1); for (typename boost::range_iterator::type it = boost::begin(rings); it != boost::end(rings); ++it) { - selected_ring_properties[id] = properties(*it); + selected_ring_properties[id] = properties(*it, area_strategy); selected_ring_properties[id].reversed = ReverseOut; id.multi_index++; } diff --git a/include/boost/geometry/algorithms/detail/overlay/ring_properties.hpp b/include/boost/geometry/algorithms/detail/overlay/ring_properties.hpp index abade1fe1..7dbc5d5fa 100644 --- a/include/boost/geometry/algorithms/detail/overlay/ring_properties.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/ring_properties.hpp @@ -27,11 +27,11 @@ namespace boost { namespace geometry namespace detail { namespace overlay { -template +template struct ring_properties { typedef Point point_type; - typedef typename default_area_result::type area_type; + typedef AreaType area_type; bool valid; @@ -56,13 +56,13 @@ struct ring_properties , parent_area(-1) {} - template - inline ring_properties(RingOrBox const& ring_or_box) + template + inline ring_properties(RingOrBox const& ring_or_box, AreaStrategy const& strategy) : reversed(false) , discarded(false) , parent_area(-1) { - this->area = geometry::area(ring_or_box); + this->area = geometry::area(ring_or_box, strategy); valid = geometry::point_on_border(this->point, ring_or_box); } diff --git a/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp b/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp index 638bcf474..67a4f4bb7 100644 --- a/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp @@ -59,41 +59,45 @@ namespace dispatch template struct select_rings { - template + template static inline void apply(Box const& box, Geometry const& , - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { - ring_properties[id] = typename RingPropertyMap::mapped_type(box); + ring_properties[id] = typename RingPropertyMap::mapped_type(box, strategy); } - template + template static inline void apply(Box const& box, - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { - ring_properties[id] = typename RingPropertyMap::mapped_type(box); + ring_properties[id] = typename RingPropertyMap::mapped_type(box, strategy); } }; template struct select_rings { - template + template static inline void apply(Ring const& ring, Geometry const& , - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { if (boost::size(ring) > 0) { - ring_properties[id] = typename RingPropertyMap::mapped_type(ring); + ring_properties[id] = typename RingPropertyMap::mapped_type(ring, strategy); } } - template + template static inline void apply(Ring const& ring, - ring_identifier const& id, RingPropertyMap& ring_properties) + ring_identifier const& id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { if (boost::size(ring) > 0) { - ring_properties[id] = typename RingPropertyMap::mapped_type(ring); + ring_properties[id] = typename RingPropertyMap::mapped_type(ring, strategy); } } }; @@ -102,14 +106,15 @@ namespace dispatch template struct select_rings { - template + template static inline void apply(Polygon const& polygon, Geometry const& geometry, - ring_identifier id, RingPropertyMap& ring_properties) + ring_identifier id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { typedef typename geometry::ring_type::type ring_type; typedef select_rings per_ring; - per_ring::apply(exterior_ring(polygon), geometry, id, ring_properties); + per_ring::apply(exterior_ring(polygon), geometry, id, ring_properties, strategy); typename interior_return_type::type rings = interior_rings(polygon); @@ -117,18 +122,19 @@ namespace dispatch it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; - per_ring::apply(*it, geometry, id, ring_properties); + per_ring::apply(*it, geometry, id, ring_properties, strategy); } } - template + template static inline void apply(Polygon const& polygon, - ring_identifier id, RingPropertyMap& ring_properties) + ring_identifier id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { typedef typename geometry::ring_type::type ring_type; typedef select_rings per_ring; - per_ring::apply(exterior_ring(polygon), id, ring_properties); + per_ring::apply(exterior_ring(polygon), id, ring_properties, strategy); typename interior_return_type::type rings = interior_rings(polygon); @@ -136,7 +142,7 @@ namespace dispatch it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; - per_ring::apply(*it, id, ring_properties); + per_ring::apply(*it, id, ring_properties, strategy); } } }; @@ -144,9 +150,10 @@ namespace dispatch template struct select_rings { - template + template static inline void apply(Multi const& multi, Geometry const& geometry, - ring_identifier id, RingPropertyMap& ring_properties) + ring_identifier id, RingPropertyMap& ring_properties, + AreaStrategy const& strategy) { typedef typename boost::range_iterator < @@ -159,7 +166,7 @@ namespace dispatch for (iterator_type it = boost::begin(multi); it != boost::end(multi); ++it) { id.ring_index = -1; - per_polygon::apply(*it, geometry, id, ring_properties); + per_polygon::apply(*it, geometry, id, ring_properties, strategy); id.multi_index++; } } @@ -311,12 +318,16 @@ inline void select_rings(Geometry1 const& geometry1, Geometry2 const& geometry2, { typedef typename geometry::tag::type tag1; typedef typename geometry::tag::type tag2; + typedef typename geometry::point_type::type point1_type; + typedef typename geometry::point_type::type point2_type; RingPropertyMap all_ring_properties; dispatch::select_rings::apply(geometry1, geometry2, - ring_identifier(0, -1, -1), all_ring_properties); + ring_identifier(0, -1, -1), all_ring_properties, + strategy.template get_area_strategy()); dispatch::select_rings::apply(geometry2, geometry1, - ring_identifier(1, -1, -1), all_ring_properties); + ring_identifier(1, -1, -1), all_ring_properties, + strategy.template get_area_strategy()); update_ring_selection(geometry1, geometry2, turn_info_per_ring, all_ring_properties, selected_ring_properties, @@ -337,10 +348,12 @@ inline void select_rings(Geometry const& geometry, Strategy const& strategy) { typedef typename geometry::tag::type tag; + typedef typename geometry::point_type::type point_type; RingPropertyMap all_ring_properties; dispatch::select_rings::apply(geometry, - ring_identifier(0, -1, -1), all_ring_properties); + ring_identifier(0, -1, -1), all_ring_properties, + strategy.template get_area_strategy()); update_ring_selection(geometry, geometry, turn_info_per_ring, all_ring_properties, selected_ring_properties, From 47e5cf5c96b8b0c4b66ab44e3218c8a27f0bb5ee Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 4 Jul 2017 04:48:13 +0200 Subject: [PATCH 006/188] [extensions][dissolve] Use area strategy got from intersection strategy. --- .../geometry/extensions/algorithms/dissolve.hpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index a1f7e4e29..8ffb7cdfc 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -226,7 +226,13 @@ struct dissolve_ring_or_polygon std::map map; detail::overlay::get_ring_turn_info(map, turns, clusters); - typedef detail::overlay::ring_properties::type> properties; + typedef typename geometry::point_type::type point_type; + typedef typename Strategy::template area_strategy + < + point_type + >::type area_strategy_type; + typedef typename area_strategy_type::return_type area_result_type; + typedef detail::overlay::ring_properties properties; std::map selected; @@ -234,13 +240,15 @@ struct dissolve_ring_or_polygon // Add intersected rings { + area_strategy_type const area_strategy = strategy.template get_area_strategy(); + ring_identifier id(2, 0, -1); for (typename boost::range_iterator const>::type it = boost::begin(rings); it != boost::end(rings); ++it) { - selected[id] = properties(*it); + selected[id] = properties(*it, area_strategy); id.multi_index++; } } From 77d0195d21f45f61c977707192c85f4e07902084 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 4 Jul 2017 04:48:59 +0200 Subject: [PATCH 007/188] [test][overlay] Update select_rings test wrt recent changes in overlay. --- test/algorithms/overlay/select_rings.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/algorithms/overlay/select_rings.cpp b/test/algorithms/overlay/select_rings.cpp index b1ad67850..af43328f5 100644 --- a/test/algorithms/overlay/select_rings.cpp +++ b/test/algorithms/overlay/select_rings.cpp @@ -1,6 +1,11 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// // 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) @@ -36,7 +41,11 @@ template void test_geometry(std::string const& wkt1, std::string const& wkt2, RingIdVector const& expected_ids) { - typedef bg::detail::overlay::ring_properties::type> properties; + typedef bg::detail::overlay::ring_properties + < + typename bg::point_type::type, + double + > properties; Geometry1 geometry1; Geometry2 geometry2; From 7ad0c0b8ff2533b497a3ef3169aeb51f229c74d4 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Tue, 4 Jul 2017 09:38:49 +0300 Subject: [PATCH 008/188] Update topology_check.hpp --- .../boost/geometry/algorithms/detail/relate/topology_check.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/boost/geometry/algorithms/detail/relate/topology_check.hpp b/include/boost/geometry/algorithms/detail/relate/topology_check.hpp index 67cc554cf..654999d8f 100644 --- a/include/boost/geometry/algorithms/detail/relate/topology_check.hpp +++ b/include/boost/geometry/algorithms/detail/relate/topology_check.hpp @@ -165,6 +165,8 @@ private: m_endpoints.reserve(boost::size(m_mls) * 2); + m_has_interior = false; + typedef typename boost::range_iterator::type ls_iterator; for ( ls_iterator it = boost::begin(m_mls) ; it != boost::end(m_mls) ; ++it ) { From bcae6f1e81127ad02dca8effb5c539bf8a95ff46 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Tue, 4 Jul 2017 09:58:05 +0300 Subject: [PATCH 009/188] Update box_box.hpp --- .../geometry/algorithms/detail/disjoint/box_box.hpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp b/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp index f830f8161..b6f12618f 100644 --- a/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp +++ b/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp @@ -121,9 +121,18 @@ struct box_box // calculate positive longitude translation with b1_min as origin calc_t const diff_min = math::longitude_distance_unsigned(b1_min, b2_min); calc_t const b2_min_transl = b1_min + diff_min; // always right of b1_min + calc_t b2_max_transl = b2_min_transl - constants::period() + diff2;; - if (b2_min_transl > b1_max // b2_min right of b1_max - && b2_min_transl - constants::period() + diff2 < b1_min) // b2_max left of b1_min + // if the translation is too close then use the original point + // note that math::abs(b2_max_transl - b2_max) takes values very + // close to k*2*constants::period() for k=0,1,2,... + if (math::abs(b2_max_transl - b2_max) < constants::period() / 2) + { + b2_max_transl = b2_max; + } + + if (b2_min_transl > b1_max // b2_min right of b1_max + && b2_max_transl < b1_min) // b2_max left of b1_min { return true; } From fcc3c4e39d4150ab65d656c98894cdfeab94b666 Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Tue, 4 Jul 2017 10:24:13 +0300 Subject: [PATCH 010/188] [test] Add failing test case that now passing with box_box fix --- .../disjoint/disjoint_sph.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/algorithms/relational_operations/disjoint/disjoint_sph.cpp b/test/algorithms/relational_operations/disjoint/disjoint_sph.cpp index cee4c8d16..d9a21fdad 100644 --- a/test/algorithms/relational_operations/disjoint/disjoint_sph.cpp +++ b/test/algorithms/relational_operations/disjoint/disjoint_sph.cpp @@ -188,6 +188,21 @@ void test_linestring_linestring() test_geometry("LINESTRING(1 0,2 2,2 3)", "LINESTRING(0 0, 2 2, 3 2)", false); } +//https://svn.boost.org/trac10/ticket/13057 +template +void test_linestring_linestring_radians() +{ + typedef bg::model::linestring

ls; + + test_geometry("LINESTRING(0 -0.31415926535897897853,\ + 0.26179938779914918578 0,\ + -0.034906585039886556254 0.13962634015954622502,\ + -0.12217304763960294689 0.12217304763960294689)",\ + "LINESTRING(-0.034906585039886556254 0.13962634015954622502,\ + -0.26179938779914918578 0)", false); + +} + template void test_linestring_multi_linestring() { @@ -255,6 +270,9 @@ void test_all() test_linestring_multi_linestring

(); test_multi_linestring_multi_linestring

(); + test_linestring_linestring_radians > >(); + test_point_polygon

(); } From b6bfbe514a463661487e6ba3fd377faa00020c97 Mon Sep 17 00:00:00 2001 From: Vissarion Fisikopoulos Date: Tue, 4 Jul 2017 11:17:13 +0300 Subject: [PATCH 011/188] Update get_turn_info_helpers.hpp --- .../algorithms/detail/overlay/get_turn_info_helpers.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp index 5f2cb07fa..ab4ee87b9 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -45,7 +45,7 @@ struct side_calculator { inline side_calculator(Pi const& pi, Pj const& pj, Pk const& pk, Qi const& qi, Qj const& qj, Qk const& qk, - SideStrategy const& side_strategy) + SideStrategy side_strategy) : m_pi(pi), m_pj(pj), m_pk(pk) , m_qi(qi), m_qj(qj), m_qk(qk) , m_side_strategy(side_strategy) @@ -66,7 +66,7 @@ struct side_calculator Qj const& m_qj; Qk const& m_qk; - SideStrategy const& m_side_strategy; + SideStrategy m_side_strategy; }; template From a911872bf535171269c0c45eaeb25342143cd913 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 4 Jul 2017 15:58:05 +0200 Subject: [PATCH 012/188] [disjoint] Remove unwanted double ; --- include/boost/geometry/algorithms/detail/disjoint/box_box.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp b/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp index b6f12618f..87618939b 100644 --- a/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp +++ b/include/boost/geometry/algorithms/detail/disjoint/box_box.hpp @@ -121,7 +121,7 @@ struct box_box // calculate positive longitude translation with b1_min as origin calc_t const diff_min = math::longitude_distance_unsigned(b1_min, b2_min); calc_t const b2_min_transl = b1_min + diff_min; // always right of b1_min - calc_t b2_max_transl = b2_min_transl - constants::period() + diff2;; + calc_t b2_max_transl = b2_min_transl - constants::period() + diff2; // if the translation is too close then use the original point // note that math::abs(b2_max_transl - b2_max) takes values very From a2168a97d98565d2808b14f580e120182a25aaed Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 4 Jul 2017 16:03:54 +0200 Subject: [PATCH 013/188] [overlay] Add missing const&. --- .../algorithms/detail/overlay/get_turn_info_helpers.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp index ab4ee87b9..f8247cd24 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -45,7 +45,7 @@ struct side_calculator { inline side_calculator(Pi const& pi, Pj const& pj, Pk const& pk, Qi const& qi, Qj const& qj, Qk const& qk, - SideStrategy side_strategy) + SideStrategy const& side_strategy) : m_pi(pi), m_pj(pj), m_pk(pk) , m_qi(qi), m_qj(qj), m_qk(qk) , m_side_strategy(side_strategy) From 4ccd4e43cedde83aa2e679ccdc5f83c068530660 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 5 Jul 2017 13:06:15 +0200 Subject: [PATCH 014/188] [is_valid] add extra info to turn to determine validity Including unit test adaptions. Results are verified visually with SVG --- .../detail/overlay/get_turn_info.hpp | 14 + .../algorithms/detail/overlay/turn_info.hpp | 2 + test/algorithms/overlay/get_turn_info.cpp | 260 +++++++++--------- 3 files changed, 149 insertions(+), 127 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp index 08bc34218..895952c8f 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -190,6 +190,7 @@ struct touch_interior : public base_turn_handler // Q turns left on the right side of P (test "MR3") // Both directions for "intersection" both(ti, operation_intersection); + ti.touch_only = true; } else if (side_qi_p == 1 && side_qk_p == 1 && side_qk_q == -1) { @@ -197,6 +198,7 @@ struct touch_interior : public base_turn_handler // Union: take both operation // Intersection: skip both(ti, operation_union); + ti.touch_only = true; } else if (side_qi_p == side_qk_p && side_qi_p == side_qk_q) { @@ -207,6 +209,7 @@ struct touch_interior : public base_turn_handler unsigned int index = side_qk_q == 1 ? index_q : index_p; ti.operations[index].operation = operation_union; ti.operations[1 - index].operation = operation_intersection; + ti.touch_only = true; } else if (side_qk_p == 0) { @@ -346,6 +349,7 @@ struct touch : public base_turn_handler if (side_pk_q2 == -side_qk_q) { ui_else_iu(! q_turns_left, ti); + ti.touch_only = true; return; } @@ -358,6 +362,10 @@ struct touch : public base_turn_handler { ti.operations[1].operation = operation_blocked; } + else + { + ti.touch_only = true; + } //block_second(block_q, ti); return; } @@ -373,6 +381,10 @@ struct touch : public base_turn_handler : side_qi_p1 == 1 || side_qk_p1 == 1 ? operation_union : operation_intersection; + if (! block_q) + { + ti.touch_only = true; + } return; } @@ -400,6 +412,7 @@ struct touch : public base_turn_handler if (side_pk_q1 == side_qk_p1) { uu_else_ii(right_to_left, ti); + ti.touch_only = true; return; } } @@ -418,6 +431,7 @@ struct touch : public base_turn_handler if (side_pk_q2 == side_qk_p1) { ui_else_iu(right_to_left, ti); + ti.touch_only = true; return; } } diff --git a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp index 06bf1a293..3a4c2e94a 100644 --- a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -89,6 +89,7 @@ struct turn_info Point point; method_type method; + bool touch_only; // True in case of method touch(interior) and lines do not cross signed_size_type cluster_id; // For multiple turns on same location, >= 0. Else -1 bool discarded; @@ -101,6 +102,7 @@ struct turn_info inline turn_info() : method(method_none) + , touch_only(false) , cluster_id(-1) , discarded(false) , colocated_ii(false) diff --git a/test/algorithms/overlay/get_turn_info.cpp b/test/algorithms/overlay/get_turn_info.cpp index 24fc5ac74..6310e248e 100644 --- a/test/algorithms/overlay/get_turn_info.cpp +++ b/test/algorithms/overlay/get_turn_info.cpp @@ -36,6 +36,7 @@ void test_with_point(std::string const& caseid, T pi_x, T pi_y, T pj_x, T pj_y, T pk_x, T pk_y, T qi_x, T qi_y, T qj_x, T qj_y, T qk_x, T qk_y, bg::detail::overlay::method_type expected_method, + bool expected_touch_only, T ip_x, T ip_y, std::string const& expected, T ip_x2, T ip_y2) @@ -100,6 +101,10 @@ void test_with_point(std::string const& caseid, if (! info.empty()) { BOOST_CHECK_EQUAL(info[0].method, expected_method); + BOOST_CHECK_MESSAGE(info[0].touch_only == expected_touch_only, + caseid + << " detected: " << info[0].touch_only + << " expected: " << expected_touch_only); BOOST_CHECK_CLOSE(bg::get<0>(info[0].point), ip_x, 0.001); BOOST_CHECK_CLOSE(bg::get<1>(info[0].point), ip_y, 0.001); @@ -164,6 +169,8 @@ void test_with_point(std::string const& caseid, op.clear(); op += bg::method_char(it->method); + op += ' '; + op += (it->touch_only ? 'o' : '*'); if (info.size() != 1) { op += ch; @@ -183,6 +190,7 @@ void test_both(std::string const& caseid, T qi_x, T qi_y, T qj_x, T qj_y, T qk_x, T qk_y, bg::detail::overlay::method_type method = bg::detail::overlay::method_none, + bool expected_touch_only = false, T ip_x = -1, T ip_y = -1, std::string const& expected = "", T ip_x2 = -1, T ip_y2 = -1) @@ -190,9 +198,7 @@ void test_both(std::string const& caseid, test_with_point(caseid, pi_x, pi_y, pj_x, pj_y, pk_x, pk_y, qi_x, qi_y, qj_x, qj_y, qk_x, qk_y, - method, ip_x, ip_y, expected, ip_x2, ip_y2); - - //return; + method, expected_touch_only, ip_x, ip_y, expected, ip_x2, ip_y2); std::string reversed(expected.rbegin(), expected.rend()); @@ -205,7 +211,7 @@ void test_both(std::string const& caseid, test_with_point(caseid + "_r", qi_x, qi_y, qj_x, qj_y, qk_x, qk_y, // q pi_x, pi_y, pj_x, pj_y, pk_x, pk_y, // p - method, ip_x, ip_y, reversed, ip_x2, ip_y2); + method, expected_touch_only, ip_x, ip_y, reversed, ip_x2, ip_y2); } @@ -223,27 +229,27 @@ void test_all() test_both("il1", 5, 1, 5, 6, 7, 8, // p 3, 3, 7, 5, 8, 3, // q - method_crosses, 5, 4, "ui"); + method_crosses, false, 5, 4, "ui"); test_both("il2", 5, 1, 5, 6, 7, 8, // p 3, 5, 7, 5, 3, 3, // q - method_crosses, 5, 5, "ui"); + method_crosses, false, 5, 5, "ui"); test_both("il3", 5, 1, 5, 6, 7, 8, // p 3, 3, 7, 5, 3, 5, // q - method_crosses, 5, 4, "ui"); + method_crosses, false, 5, 4, "ui"); test_both("il4", 5, 1, 5, 6, 7, 8, // p 3, 3, 7, 5, 4, 8, // q - method_crosses, 5, 4, "ui"); + method_crosses, false, 5, 4, "ui"); test_both("ir1", 5, 1, 5, 6, 7, 8, // p 7, 5, 3, 3, 2, 5, // q - method_crosses, 5, 4, "iu"); + method_crosses, false, 5, 4, "iu"); // ------------------------------------------------------------------------ @@ -252,52 +258,52 @@ void test_all() test_both("ml1", 5, 1, 5, 6, 7, 8, // p 3, 3, 5, 4, 7, 3, // q - method_touch_interior, 5, 4, "ui"); + method_touch_interior, false, 5, 4, "ui"); test_both("ml2", 5, 1, 5, 6, 7, 8, // p 3, 3, 5, 4, 3, 6, // q - method_touch_interior, 5, 4, "iu"); + method_touch_interior, true, 5, 4, "iu"); test_both("ml3", 5, 1, 5, 6, 7, 8, // p 3, 6, 5, 4, 3, 3, // q - method_touch_interior, 5, 4, "uu"); + method_touch_interior, true, 5, 4, "uu"); test_both("mr1", 5, 1, 5, 6, 7, 8, // p 7, 3, 5, 4, 3, 3, // q - method_touch_interior, 5, 4, "iu"); + method_touch_interior, false, 5, 4, "iu"); test_both("mr2", 5, 1, 5, 6, 7, 8, // p 7, 3, 5, 4, 7, 6, // q - method_touch_interior, 5, 4, "ui"); + method_touch_interior, true, 5, 4, "ui"); test_both("mr3", 5, 1, 5, 6, 7, 8, // p 7, 6, 5, 4, 7, 3, // q - method_touch_interior, 5, 4, "ii"); + method_touch_interior, true, 5, 4, "ii"); test_both("mcl", 5, 1, 5, 6, 7, 8, // p 3, 2, 5, 3, 5, 5, // q - method_touch_interior, 5, 3, "cc"); + method_touch_interior, false, 5, 3, "cc"); test_both("mcr", 5, 1, 5, 6, 7, 8, // p 7, 2, 5, 3, 5, 5, // q - method_touch_interior, 5, 3, "cc"); + method_touch_interior, false, 5, 3, "cc"); test_both("mclo", 5, 1, 5, 6, 7, 8, // p 3, 4, 5, 5, 5, 3, // q - method_touch_interior, 5, 5, "ux"); + method_touch_interior, false, 5, 5, "ux"); test_both("mcro", 5, 1, 5, 6, 7, 8, // p 7, 4, 5, 5, 5, 3, // q - method_touch_interior, 5, 5, "ix"); + method_touch_interior, false, 5, 5, "ix"); // ------------------------------------------------------------------------ // COLLINEAR @@ -305,53 +311,53 @@ void test_all() test_both("cll1", 5, 1, 5, 6, 3, 8, // p 5, 5, 5, 7, 3, 8, // q - method_collinear, 5, 6, "ui"); + method_collinear, false, 5, 6, "ui"); test_both("cll2", 5, 1, 5, 6, 3, 8, // p 5, 3, 5, 5, 3, 6, // q - method_collinear, 5, 5, "iu"); + method_collinear, false, 5, 5, "iu"); test_both("clr1", 5, 1, 5, 6, 3, 8, // p 5, 5, 5, 7, 6, 8, // q - method_collinear, 5, 6, "ui"); + method_collinear, false, 5, 6, "ui"); test_both("clr2", 5, 1, 5, 6, 3, 8, // p 5, 3, 5, 5, 6, 6, // q - method_collinear, 5, 5, "ui"); + method_collinear, false, 5, 5, "ui"); test_both("crl1", 5, 1, 5, 6, 7, 8, // p 5, 5, 5, 7, 3, 8, // q - method_collinear, 5, 6, "iu"); + method_collinear, false, 5, 6, "iu"); test_both("crl2", 5, 1, 5, 6, 7, 8, // p 5, 3, 5, 5, 3, 6, // q - method_collinear, 5, 5, "iu"); + method_collinear, false, 5, 5, "iu"); test_both("crr1", 5, 1, 5, 6, 7, 8, // p 5, 5, 5, 7, 6, 8, // q - method_collinear, 5, 6, "iu"); + method_collinear, false, 5, 6, "iu"); test_both("crr2", 5, 1, 5, 6, 7, 8, // p 5, 3, 5, 5, 6, 6, // q - method_collinear, 5, 5, "ui"); + method_collinear, false, 5, 5, "ui"); // The next two cases are changed (BSG 2013-09-24), they contain turn info (#buffer_rt_g) // In new approach they are changed back (BSG 2013-10-20) test_both("ccx1", 5, 1, 5, 6, 5, 8, // p 5, 5, 5, 7, 3, 8, // q - method_collinear, 5, 6, "cc"); // "iu"); + method_collinear, false, 5, 6, "cc"); // "iu"); test_both("cxc1", 5, 1, 5, 6, 7, 8, // p 5, 3, 5, 5, 5, 7, // q - method_collinear, 5, 5, "cc"); // "iu"); + method_collinear, false, 5, 5, "cc"); // "iu"); // Bug in case #54 of "overlay_cases.hpp" test_both("c_bug1", 5, 0, 2, 0, 2, 2, // p 4, 0, 1, 0, 1, 2, // q - method_collinear, 2, 0, "iu"); + method_collinear, false, 2, 0, "iu"); // ------------------------------------------------------------------------ @@ -361,66 +367,66 @@ void test_all() test_both("clo1", 5, 2, 5, 6, 3, 8, // p 5, 7, 5, 5, 3, 3, // q - method_collinear, 5, 6, "ixxu", 5, 5); + method_collinear, false, 5, 6, "ixxu", 5, 5); test_both("clo2", 5, 2, 5, 6, 3, 8, // p 5, 7, 5, 5, 5, 2, // q - method_collinear, 5, 6, "ix"); + method_collinear, false, 5, 6, "ix"); // actually "xxix", xx is skipped everywhere test_both("clo3", 5, 2, 5, 6, 3, 8, // p 5, 7, 5, 5, 7, 3, // q - method_collinear, 5, 6, "ixxi", 5, 5); + method_collinear, false, 5, 6, "ixxi", 5, 5); test_both("cco1", 5, 2, 5, 6, 5, 8, // p 5, 7, 5, 5, 3, 3, // q - method_collinear, 5, 5, "xu"); // "xuxx" + method_collinear, false, 5, 5, "xu"); // "xuxx" test_both("cco2", 5, 2, 5, 6, 5, 8, // p 5, 7, 5, 5, 5, 2); // q "xxxx" test_both("cco3", 5, 2, 5, 6, 5, 8, // p 5, 7, 5, 5, 7, 3, // q - method_collinear, 5, 5, "xi"); // "xixx" + method_collinear, false, 5, 5, "xi"); // "xixx" test_both("cro1", 5, 2, 5, 6, 7, 8, // p 5, 7, 5, 5, 3, 3, // q - method_collinear, 5, 6, "uxxu", 5, 5); + method_collinear, false, 5, 6, "uxxu", 5, 5); test_both("cro2", 5, 2, 5, 6, 7, 8, // p 5, 7, 5, 5, 5, 2, // q - method_collinear, 5, 6, "ux"); // "xxux" + method_collinear, false, 5, 6, "ux"); // "xxux" test_both("cro3", 5, 2, 5, 6, 7, 8, // p 5, 7, 5, 5, 7, 3, // q - method_collinear, 5, 6, "uxxi", 5, 5); + method_collinear, false, 5, 6, "uxxi", 5, 5); test_both("cxo1", 5, 2, 5, 6, 3, 8, // p 5, 5, 5, 3, 3, 1, // q - method_collinear, 5, 3, "xu"); + method_collinear, false, 5, 3, "xu"); test_both("cxo2", 5, 2, 5, 6, 3, 8, // p 5, 5, 5, 3, 5, 0); // q "xx" test_both("cxo3", 5, 2, 5, 6, 3, 8, // p 5, 5, 5, 3, 7, 1, // q - method_collinear, 5, 3, "xi"); + method_collinear, false, 5, 3, "xi"); test_both("cxo4", 5, 2, 5, 6, 3, 8, // p 5, 7, 5, 1, 3, 0, // q - method_collinear, 5, 6, "ix"); + method_collinear, false, 5, 6, "ix"); test_both("cxo5", 5, 2, 5, 6, 5, 8, // p 5, 7, 5, 1, 3, 0); // q "xx" test_both("cxo6", 5, 2, 5, 6, 7, 8, // p 5, 7, 5, 1, 3, 0, // q - method_collinear, 5, 6, "ux"); + method_collinear, false, 5, 6, "ux"); // Verify @@ -441,61 +447,61 @@ void test_all() test_both("blr1", 5, 1, 5, 6, 4, 4, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); test_both("blr2", 5, 1, 5, 6, 1, 4, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("blr3", 5, 1, 5, 6, 3, 6, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "iu"); + method_touch, false, 5, 6, "iu"); test_both("blr4", 5, 1, 5, 6, 1, 8, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "xu"); + method_touch, false, 5, 6, "xu"); test_both("blr5", 5, 1, 5, 6, 4, 8, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); test_both("blr6", 5, 1, 5, 6, 6, 4, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); test_both("blr7", 5, 1, 5, 6, 3, 6, // p 3, 7, 5, 6, 5, 3, // q - method_touch, 5, 6, "ix"); + method_touch, false, 5, 6, "ix"); test_both("blr8", 5, 1, 5, 6, 3, 6, // p 3, 6, 5, 6, 5, 3, // q - method_touch, 5, 6, "xx"); + method_touch, false, 5, 6, "xx"); test_both("blr9", 5, 1, 5, 6, 3, 6, // p 3, 5, 5, 6, 5, 3, // q - method_touch, 5, 6, "ux"); + method_touch, false, 5, 6, "ux"); // Variants test_both("blr7-a", 5, 1, 5, 6, 3, 6, // p 5, 8, 5, 6, 5, 3, // q - method_touch, 5, 6, "ix"); + method_touch, false, 5, 6, "ix"); test_both("blr7-b", // in fact NOT "both-left" 5, 1, 5, 6, 3, 6, // p 6, 8, 5, 6, 5, 3, // q - method_touch, 5, 6, "ix"); + method_touch, false, 5, 6, "ix"); // To check if "collinear-check" on other side // does not apply to this side test_both("blr6-c1", 5, 1, 5, 6, 7, 5, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); test_both("blr6-c2", 5, 1, 5, 6, 7, 7, // p 3, 7, 5, 6, 3, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); @@ -503,75 +509,75 @@ void test_all() test_both("brr1", 5, 1, 5, 6, 6, 4, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); test_both("brr2", 5, 1, 5, 6, 9, 4, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "xu"); + method_touch, false, 5, 6, "xu"); test_both("brr3", 5, 1, 5, 6, 7, 6, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "iu"); + method_touch, false, 5, 6, "iu"); test_both("brr4", 5, 1, 5, 6, 9, 8, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("brr5", 5, 1, 5, 6, 6, 8, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); test_both("brr6", 5, 1, 5, 6, 4, 4, // p 7, 5, 5, 6, 7, 7, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); // Both right, Q turns left test_both("brl1", 5, 1, 5, 6, 6, 4, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); test_both("brl2", 5, 1, 5, 6, 9, 4, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("brl3", 5, 1, 5, 6, 7, 6, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("brl4", 5, 1, 5, 6, 9, 8, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "xi"); + method_touch, false, 5, 6, "xi"); test_both("brl5", 5, 1, 5, 6, 6, 8, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); test_both("brl6", 5, 1, 5, 6, 4, 4, // p 7, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); test_both("brl7", 5, 1, 5, 6, 7, 6, // p 7, 7, 5, 6, 5, 3, // q - method_touch, 5, 6, "ux"); + method_touch, false, 5, 6, "ux"); test_both("brl8", 5, 1, 5, 6, 7, 6, // p 7, 6, 5, 6, 5, 3, // q - method_touch, 5, 6, "xx"); + method_touch, false, 5, 6, "xx"); test_both("brl9", 5, 1, 5, 6, 7, 6, // p 7, 5, 5, 6, 5, 3, // q - method_touch, 5, 6, "ix"); + method_touch, false, 5, 6, "ix"); // Variants test_both("brl7-a", 5, 1, 5, 6, 7, 6, // p 5, 8, 5, 6, 5, 3, // q - method_touch, 5, 6, "ux"); + method_touch, false, 5, 6, "ux"); test_both("brl7-b", // in fact NOT "both right" 5, 1, 5, 6, 7, 6, // p 4, 8, 5, 6, 5, 3, // q - method_touch, 5, 6, "ux"); + method_touch, false, 5, 6, "ux"); @@ -579,251 +585,251 @@ void test_all() test_both("bll1", 5, 1, 5, 6, 4, 4, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); test_both("bll2", 5, 1, 5, 6, 1, 4, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "xi"); + method_touch, false, 5, 6, "xi"); test_both("bll3", 5, 1, 5, 6, 3, 6, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("bll4", 5, 1, 5, 6, 1, 8, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("bll5", 5, 1, 5, 6, 4, 8, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); test_both("bll6", 5, 1, 5, 6, 6, 4, // p 3, 5, 5, 6, 3, 7, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); // TOUCH - COLLINEAR + one side // Collinear/left, Q turns right test_both("t-clr1", 5, 1, 5, 6, 4, 4, // p 5, 8, 5, 6, 3, 5, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); test_both("t-clr2", 5, 1, 5, 6, 1, 4, // p 5, 8, 5, 6, 3, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("t-clr3", 5, 1, 5, 6, 3, 6, // p 5, 8, 5, 6, 3, 5, // q - method_touch, 5, 6, "iu"); + method_touch, false, 5, 6, "iu"); test_both("t-clr4", 5, 1, 5, 6, 5, 8, // p 5, 8, 5, 6, 3, 5, // q - method_touch, 5, 6, "xu"); + method_touch, false, 5, 6, "xu"); // 5 n.a. test_both("t-clr6", 5, 1, 5, 6, 6, 4, // p 5, 8, 5, 6, 3, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); // Collinear/right, Q turns right test_both("t-crr1", 5, 1, 5, 6, 6, 4, // p 7, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); test_both("t-crr2", 5, 1, 5, 6, 9, 4, // p 7, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "xu"); + method_touch, false, 5, 6, "xu"); test_both("t-crr3", 5, 1, 5, 6, 7, 6, // p 7, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "iu"); + method_touch, false, 5, 6, "iu"); test_both("t-crr4", 5, 1, 5, 6, 5, 9, // p 7, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); // 5 n.a. test_both("t-crr6", 5, 1, 5, 6, 4, 4, // p 7, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); // Collinear/right, Q turns left test_both("t-crl1", 5, 1, 5, 6, 6, 4, // p 5, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); test_both("t-crl2", 5, 1, 5, 6, 9, 4, // p 5, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("t-crl3", 5, 1, 5, 6, 7, 6, // p 5, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("t-crl4", 5, 1, 5, 6, 5, 8, // p 5, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "xi"); + method_touch, false, 5, 6, "xi"); // 5 n.a. test_both("t-crl6", 5, 1, 5, 6, 4, 4, // p 5, 7, 5, 6, 7, 5, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); // Collinear/left, Q turns left test_both("t-cll1", 5, 1, 5, 6, 4, 4, // p 3, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); test_both("t-cll2", 5, 1, 5, 6, 1, 4, // p 3, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "xi"); + method_touch, false, 5, 6, "xi"); test_both("t-cll3", 5, 1, 5, 6, 3, 6, // p 3, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("t-cll4", 5, 1, 5, 6, 5, 9, // p 3, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); // 5 n.a. test_both("t-cll6", 5, 1, 5, 6, 6, 4, // p 3, 5, 5, 6, 5, 8, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); // Left to right test_both("lr1", 5, 1, 5, 6, 3, 3, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ii"); + method_touch, true, 5, 6, "ii"); test_both("lr2", 5, 1, 5, 6, 1, 5, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "xi"); + method_touch, false, 5, 6, "xi"); test_both("lr3", 5, 1, 5, 6, 4, 8, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("lr4", 5, 1, 5, 6, 9, 5, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("lr5", 5, 1, 5, 6, 7, 3, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "iu"); + method_touch, true, 5, 6, "iu"); // otherwise case more thoroughly test_both("lr3a", 5, 1, 5, 6, 1, 6, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("lr3b", 5, 1, 5, 6, 5, 10, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("lr3c", 5, 1, 5, 6, 8, 9, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("lr3d", 5, 1, 5, 6, 9, 7, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); test_both("lr3e", 5, 1, 5, 6, 9, 6, // p 1, 5, 5, 6, 9, 5, // q - method_touch, 5, 6, "ui"); + method_touch, false, 5, 6, "ui"); // Right to left test_both("rl1", 5, 1, 5, 6, 3, 3, // p 9, 5, 5, 6, 1, 5, // q - method_touch, 5, 6, "ui"); + method_touch, true, 5, 6, "ui"); test_both("rl2", 5, 1, 5, 6, 1, 5, // p 9, 5, 5, 6, 1, 5, // q - method_touch, 5, 6, "cc"); + method_touch, false, 5, 6, "cc"); test_both("rl3", 5, 1, 5, 6, 4, 8, // p 9, 5, 5, 6, 1, 5, // q - method_touch, 5, 6, "iu"); + method_touch, false, 5, 6, "iu"); test_both("rl4", 5, 1, 5, 6, 9, 5, // p 9, 5, 5, 6, 1, 5, // q - method_touch, 5, 6, "xu"); + method_touch, false, 5, 6, "xu"); test_both("rl5", 5, 1, 5, 6, 7, 3, // p 9, 5, 5, 6, 1, 5, // q - method_touch, 5, 6, "uu"); + method_touch, true, 5, 6, "uu"); // Equal (p1/q1 are equal) test_both("ebl1", 5, 1, 5, 6, 3, 4, // p 5, 1, 5, 6, 3, 8, // q - method_equal, 5, 6, "ui"); + method_equal, false, 5, 6, "ui"); test_both("ebl2", 5, 1, 5, 6, 3, 8, // p 5, 1, 5, 6, 3, 4, // q - method_equal, 5, 6, "iu"); + method_equal, false, 5, 6, "iu"); test_both("ebl3", 5, 1, 5, 6, 3, 8, // p 5, 1, 5, 6, 3, 8, // q - method_equal, 5, 6, "cc"); + method_equal, false, 5, 6, "cc"); test_both("ebl3-c1", 5, 1, 5, 6, 10, 1, // p 5, 1, 5, 6, 3, 8, // q - method_equal, 5, 6, "iu"); + method_equal, false, 5, 6, "iu"); test_both("ebr1", 5, 1, 5, 6, 7, 4, // p 5, 1, 5, 6, 7, 8, // q - method_equal, 5, 6, "iu"); + method_equal, false, 5, 6, "iu"); test_both("ebr2", 5, 1, 5, 6, 7, 8, // p 5, 1, 5, 6, 7, 4, // q - method_equal, 5, 6, "ui"); + method_equal, false, 5, 6, "ui"); test_both("ebr3", 5, 1, 5, 6, 7, 8, // p 5, 1, 5, 6, 7, 8, // q - method_equal, 5, 6, "cc"); + method_equal, false, 5, 6, "cc"); test_both("ebr3-c1", 5, 1, 5, 6, 0, 1, // p 5, 1, 5, 6, 7, 8, // q - method_equal, 5, 6, "ui"); + method_equal, false, 5, 6, "ui"); test_both("elr1", 5, 1, 5, 6, 7, 8, // p 5, 1, 5, 6, 3, 8, // q - method_equal, 5, 6, "iu"); + method_equal, false, 5, 6, "iu"); test_both("elr2", 5, 1, 5, 6, 3, 8, // p 5, 1, 5, 6, 7, 8, // q - method_equal, 5, 6, "ui"); + method_equal, false, 5, 6, "ui"); test_both("ec1", 5, 1, 5, 6, 5, 8, // p 5, 1, 5, 6, 5, 8, // q - method_equal, 5, 6, "cc"); + method_equal, false, 5, 6, "cc"); test_both("ec2", 5, 1, 5, 6, 5, 8, // p 5, 1, 5, 6, 5, 7, // q - method_equal, 5, 6, "cc"); + method_equal, false, 5, 6, "cc"); test_both("snl-1", 0, 3, 2, 3, 4, 3, // p 4, 3, 2, 3, 0, 3, // q - method_touch, 2, 3, "xx"); + method_touch, false, 2, 3, "xx"); // BSG 2012-05-26 to be decided what's the problem here and what it tests... // Anyway, test results are not filled out. //test_both("issue_buffer_mill", // 5.1983614873206241 , 6.7259025813913107 , 5.0499999999999998 , 6.4291796067500622 , 5.1983614873206241 , 6.7259025813913107, // p // 5.0499999999999998 , 6.4291796067500622 , 5.0499999999999998 , 6.4291796067500622 , 5.1983614873206241 , 6.7259025813913107, // q - // method_collinear, 2, 0, "tt"); + // method_collinear, false, 2, 0, "tt"); } From 866acb0dcf26bc16abfb7070e3b6c165c188862f Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 5 Jul 2017 14:20:16 +0200 Subject: [PATCH 015/188] [doc] Update 1.65 release notes. --- doc/release_notes.qbk | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 61ba87250..c014969c7 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -22,9 +22,16 @@ [heading Boost 1.65] [/=================] +[*Improvements] + +* Add correct() overload taking area strategy +* Add missing relational operations (covered_by, crosses, equals, etc.) for MultiPoint/AnyGeometry + [*Solved issues] * [@https://svn.boost.org/trac/boost/ticket/12410 12410] Lack of support of geographic coordinate system in correct() +* [@https://svn.boost.org/trac/boost/ticket/13040 13040] Wrong result of non-cartesian intersection strategies for segments near poles. +* [@https://svn.boost.org/trac/boost/ticket/13057 13057] Wrong result of intersects() for linestrings caused by numerical issue in disjoint() for boxes. [*Bugfixes] @@ -32,6 +39,9 @@ * Fix union which could produce invalid results (for some cases, this needs to define BOOST_GEOMETRY_INCLUDE_SELF_TURNS * Fix intersection (idem), but some cases are still not yet valid * Fix difference (idem), but some cases are still not yet valid +* Fix propagation of area strategy into the internals of various algorithms from intersection strategy +* Fix uninitialized variable in relate and reference to temporary in overlay +* Fix error in disjoint for geographic Segment/Box [/=================] [heading Boost 1.64] From b592017cb15635e653c602ad0c3b2f3ac832c817 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 5 Jul 2017 14:31:31 +0200 Subject: [PATCH 016/188] [doc] Add missing correct section. --- doc/reference.qbk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/reference.qbk b/doc/reference.qbk index e84115ece..699712399 100644 --- a/doc/reference.qbk +++ b/doc/reference.qbk @@ -89,7 +89,10 @@ [include generated/clear.qbk] [include generated/convert.qbk] [include generated/convex_hull.qbk] + +[section:correct correct] [include generated/correct.qbk] +[endsect] [section:covered_by covered_by] [include generated/covered_by.qbk] From de8273ab92d9501a834ab80a055be25c59b90b09 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 5 Jul 2017 14:37:29 +0200 Subject: [PATCH 017/188] [is_valid] fix, using touch_only information, and checking border_point instead of touching point --- .../detail/is_valid/is_acceptable_turn.hpp | 53 ++------------- .../detail/is_valid/multipolygon.hpp | 9 ++- .../algorithms/detail/is_valid/polygon.hpp | 19 +++++- .../algorithms/detail/point_on_border.hpp | 68 ++++++++++--------- 4 files changed, 64 insertions(+), 85 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp b/include/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp index bac541219..fccc0ffdb 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp @@ -125,34 +125,6 @@ private: typedef typename boost::range_value::type polygon; typedef is_acceptable_turn base; - template - static inline - bool check_int_ext(Operation const& op1, - detail::overlay::operation_type optype1, - Operation const& op2, - detail::overlay::operation_type optype2) - { - // u/i is acceptable for touch of interior ring with another exterior ring - // (but only if there is a colocated uu-turn of its exterior, TODO) - return op1.seg_id.ring_index == -1 - && op2.seg_id.ring_index >= 0 - && op1.operation == optype1 - && op2.operation == optype2; - } - - template - static inline - bool check_int_int(Operation const& op1, - Operation const& op2, - detail::overlay::operation_type optype) - { - // i/i is acceptable for touching interior/interior rings - return op1.seg_id.ring_index >= 0 - && op2.seg_id.ring_index >= 0 - && op1.operation == optype - && op2.operation == optype; - } - public: template static inline bool apply(Turn const& turn) @@ -172,26 +144,11 @@ public: return true; } - if (turn.method != method_touch) - { - return false; - } - - operation_type const reverse_op - = op == operation_union - ? operation_intersection - : operation_union; - - if ( check_int_int(turn.operations[0], turn.operations[1], reverse_op) - || check_int_ext(turn.operations[0], reverse_op, - turn.operations[1], op) - || check_int_ext(turn.operations[1], reverse_op, - turn.operations[0], op)) - { - return true; - } - - return false; + // Turn is acceptable only in case of a touch(interior) and both lines + // (polygons) do not cross + return (turn.method == method_touch + || turn.method == method_touch_interior) + && turn.touch_only; } }; diff --git a/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp index caacda8a3..ed24b1381 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp @@ -89,12 +89,15 @@ private: { boost::ignore_unused(visitor); - // collect all polygons that have turns + // collect all polygons that have crossing turns std::set multi_indices; for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit) { - multi_indices.insert(tit->operations[0].seg_id.multi_index); - multi_indices.insert(tit->operations[1].seg_id.multi_index); + if (! tit->touch_only) + { + multi_indices.insert(tit->operations[0].seg_id.multi_index); + multi_indices.insert(tit->operations[1].seg_id.multi_index); + } } typedef geometry::model::box::type> box_type; diff --git a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp index 7c8a5d58c..5c6229b79 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/polygon.hpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -216,14 +217,26 @@ protected: , m_strategy(strategy) {} + template + inline bool is_within(Item const& first, Item const& second) + { + typename point_type::type point; + typedef detail::point_on_border::point_on_range pob; + + // TODO: this should check for a point on the interior, instead + // of on border. Or it should check using the overlap function. + + return pob::apply(point, points_begin(first), points_end(first)) + && geometry::within(point, second, m_strategy); + } + template inline bool apply(partition_item const& item1, partition_item const& item2) { if (! items_overlap - && (geometry::within(*points_begin(*item1.get()), *item2.get(), m_strategy) - || geometry::within(*points_begin(*item2.get()), *item1.get(), m_strategy)) - ) + && (is_within(*item1.get(), *item2.get()) + || is_within(*item2.get(), *item1.get()))) { items_overlap = true; return false; // interrupt diff --git a/include/boost/geometry/algorithms/detail/point_on_border.hpp b/include/boost/geometry/algorithms/detail/point_on_border.hpp index a5b03d842..831081aa6 100644 --- a/include/boost/geometry/algorithms/detail/point_on_border.hpp +++ b/include/boost/geometry/algorithms/detail/point_on_border.hpp @@ -84,43 +84,49 @@ struct midpoint_helper template struct point_on_range { + // Version with iterator + template + static inline bool apply(Point& point, Iterator begin, Iterator end) + { + Iterator it = begin; + if (it == end) + { + return false; + } + + if (! Midpoint) + { + geometry::detail::conversion::convert_point_to_point(*it, point); + return true; + } + + Iterator prev = it++; + + // Go to next non-duplicate point + while (it != end + && detail::equals::equals_point_point(*it, *prev)) + { + prev = it++; + } + if (it != end) + { + return midpoint_helper + < + Point, + 0, dimension::value + >::apply(point, *prev, *it); + } + return false; + } + + // Version with range template static inline bool apply(Point& point, Range const& range) { typedef typename geometry::cs_tag::type cs_tag; BOOST_STATIC_ASSERT((! Midpoint || boost::is_same::value)); - const std::size_t n = boost::size(range); - if (Midpoint && n > 1) - { - typedef typename boost::range_iterator - < - Range const - >::type iterator; - - iterator it = boost::begin(range); - iterator prev = it++; - while (it != boost::end(range) - && detail::equals::equals_point_point(*it, *prev)) - { - prev = it++; - } - if (it != boost::end(range)) - { - return midpoint_helper - < - Point, - 0, dimension::value - >::apply(point, *prev, *it); - } - } - - if (n > 0) - { - geometry::detail::conversion::convert_point_to_point(*boost::begin(range), point); - return true; - } - return false; + return apply(point, boost::begin(range), boost::end(range)); } }; From 59ea116d8c71f0b5b121dcd2a2ae63b07dd81130 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 5 Jul 2017 14:40:21 +0200 Subject: [PATCH 018/188] [test] intersection, check validity of whole output instead of its separate polygons --- .../set_operations/check_validity.hpp | 50 +++++++++++++++++++ .../intersection/test_intersection.hpp | 33 +++++++----- .../set_operations/union/test_union.hpp | 37 +------------- 3 files changed, 71 insertions(+), 49 deletions(-) create mode 100644 test/algorithms/set_operations/check_validity.hpp diff --git a/test/algorithms/set_operations/check_validity.hpp b/test/algorithms/set_operations/check_validity.hpp new file mode 100644 index 000000000..3e81538dc --- /dev/null +++ b/test/algorithms/set_operations/check_validity.hpp @@ -0,0 +1,50 @@ +// Boost.Geometry + +// Copyright (c) 2017 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) + +#ifndef BOOST_GEOMETRY_TEST_SETOP_CHECK_VALIDITY_HPP +#define BOOST_GEOMETRY_TEST_SETOP_CHECK_VALIDITY_HPP + +#include + +#include + +template +< + typename Geometry, + typename Tag = typename bg::tag::type +> +struct check_validity +{ + static inline + bool apply(Geometry const& geometry, std::string& message) + { + return bg::is_valid(geometry, message); + } +}; + +// Specialization for vector of (e.g. rings) +template +struct check_validity +{ + static inline + bool apply(Geometry const& geometry, std::string& message) + { + typedef typename boost::range_value::type single_type; + BOOST_FOREACH(single_type const& element, geometry) + { + if (! bg::is_valid(element, message)) + { + return false; + } + } + return true; + } +}; + + +#endif // BOOST_GEOMETRY_TEST_SETOP_CHECK_VALIDITY_HPP diff --git a/test/algorithms/set_operations/intersection/test_intersection.hpp b/test/algorithms/set_operations/intersection/test_intersection.hpp index 8c8f68aa2..4afe42f5b 100644 --- a/test/algorithms/set_operations/intersection/test_intersection.hpp +++ b/test/algorithms/set_operations/intersection/test_intersection.hpp @@ -41,6 +41,7 @@ #include #include "../setop_output_type.hpp" +#include "../check_validity.hpp" struct ut_settings { @@ -56,7 +57,13 @@ struct ut_settings }; -template +template +< + typename G1, + typename G2, + typename ResultType, + typename IntersectionOutput +> typename bg::default_area_result::type check_result( IntersectionOutput const& intersection_output, @@ -96,15 +103,15 @@ check_result( { std::cout << std::setprecision(20) << bg::wkt(*it) << std::endl; } + } - if (settings.test_validity) - { - std::string message; - bool const valid = bg::is_valid(*it, message); - BOOST_CHECK_MESSAGE(valid, - "intersection: " << caseid << " not valid: " << message - << " type: " << (type_for_assert_message())); - } + if (settings.test_validity) + { + std::string message; + bool const valid = check_validity::apply(intersection_output, message); + BOOST_CHECK_MESSAGE(valid, + "intersection: " << caseid << " not valid: " << message + << " type: " << (type_for_assert_message())); } #if ! defined(BOOST_GEOMETRY_NO_BOOST_TEST) @@ -194,7 +201,7 @@ typename bg::default_area_result::type test_intersection(std::string const& result_type intersection_output; bg::intersection(g1, g2, intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); @@ -203,21 +210,21 @@ typename bg::default_area_result::type test_intersection(std::string const& intersection_output.clear(); bg::intersection(boost::variant(g1), g2, intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); intersection_output.clear(); bg::intersection(g1, boost::variant(g2), intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); intersection_output.clear(); bg::intersection(boost::variant(g1), boost::variant(g2), intersection_output); - check_result(intersection_output, caseid, expected_count, + check_result(intersection_output, caseid, expected_count, expected_holes_count, expected_point_count, expected_length_or_area, settings); #endif diff --git a/test/algorithms/set_operations/union/test_union.hpp b/test/algorithms/set_operations/union/test_union.hpp index 9b32292c5..352521eb2 100644 --- a/test/algorithms/set_operations/union/test_union.hpp +++ b/test/algorithms/set_operations/union/test_union.hpp @@ -19,6 +19,7 @@ #include #include "../setop_output_type.hpp" +#include "../check_validity.hpp" #include #include @@ -71,42 +72,6 @@ inline void check_input_validity(std::string const& caseid, int case_index, } #endif -template -< - typename Geometry, - typename Tag = typename bg::tag::type -> -struct check_validity -{ - static inline - bool apply(Geometry const& geometry, std::string& message) - { - if (! bg::is_valid(geometry, message)) - { - std::cout << bg::wkt(geometry) << std::endl; - } - return bg::is_valid(geometry, message); - } -}; - -// Specialization for vector of (e.g. rings) -template -struct check_validity -{ - static inline - bool apply(Geometry const& geometry, std::string& message) - { - typedef typename boost::range_value::type single_type; - BOOST_FOREACH(single_type const& element, geometry) - { - if (! bg::is_valid(element, message)) - { - return false; - } - } - return true; - } -}; template From 2d827cbf3398fd69a34dcf65061278d671e6fd94 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 5 Jul 2017 14:41:35 +0200 Subject: [PATCH 019/188] [doc] Fix ticket number in 1.65 release notes. --- doc/release_notes.qbk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index c014969c7..930c6dc7d 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -30,7 +30,7 @@ [*Solved issues] * [@https://svn.boost.org/trac/boost/ticket/12410 12410] Lack of support of geographic coordinate system in correct() -* [@https://svn.boost.org/trac/boost/ticket/13040 13040] Wrong result of non-cartesian intersection strategies for segments near poles. +* [@https://svn.boost.org/trac/boost/ticket/13035 13035] Wrong result of non-cartesian intersection strategies for segments near poles. * [@https://svn.boost.org/trac/boost/ticket/13057 13057] Wrong result of intersects() for linestrings caused by numerical issue in disjoint() for boxes. [*Bugfixes] From 32cc0c1d748f0341e932c11b77231b04eb055573 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 5 Jul 2017 14:45:05 +0200 Subject: [PATCH 020/188] [test] Add new testcase for is_valid --- test/algorithms/is_valid.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/algorithms/is_valid.cpp b/test/algorithms/is_valid.cpp index 1fc356f64..ed2669f76 100644 --- a/test/algorithms/is_valid.cpp +++ b/test/algorithms/is_valid.cpp @@ -963,6 +963,12 @@ inline void test_open_multipolygons() "MULTIPOLYGON(((10 5,5 10,0 5,5 0),(10 5,5 4,4 6)),((10 5,10 0,20 0,20 10,10 10),(10 5,18 8,15 3)))", true); + // Two polygons, one inside interior of other one, touching all at same point (0,0) + test::apply + ("mpg23", + "MULTIPOLYGON(((0 0,10 0,10 10,0 10),(0 0,1 9,9 9,9 1)),((0 0,8 2,8 8,2 8)))", + true); + // MySQL report 12.06.2015 { std::string wkt = "MULTIPOLYGON(" From 114bca9f555b5f279c852c3457438f706c4889ea Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 5 Jul 2017 14:47:04 +0200 Subject: [PATCH 021/188] [test] remove redundant booleans --- test/algorithms/is_valid.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/algorithms/is_valid.cpp b/test/algorithms/is_valid.cpp index ed2669f76..b683b4c21 100644 --- a/test/algorithms/is_valid.cpp +++ b/test/algorithms/is_valid.cpp @@ -1192,11 +1192,8 @@ inline void test_open_multipolygons() BOOST_AUTO_TEST_CASE( test_is_valid_multipolygon ) { - bool const allow_duplicates = true; - bool const do_not_allow_duplicates = !allow_duplicates; - - test_open_multipolygons(); - test_open_multipolygons(); + test_open_multipolygons(); + test_open_multipolygons(); } From bcbcbf8a745f7262b76fc579ce56b773093ea68a Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 5 Jul 2017 16:44:25 +0200 Subject: [PATCH 022/188] [test] with corrected validity, update testcases --- .../set_operations/difference/difference.cpp | 18 ++++++++++++++---- .../difference/difference_multi.cpp | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/test/algorithms/set_operations/difference/difference.cpp b/test/algorithms/set_operations/difference/difference.cpp index d00e14129..e1a811676 100644 --- a/test/algorithms/set_operations/difference/difference.cpp +++ b/test/algorithms/set_operations/difference/difference.cpp @@ -41,11 +41,13 @@ ( #caseid, caseid[0], caseid[1], clips1, -1, area1, clips2, -1, area2, \ clips3, -1, area1 + area2) +#if !defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS) #define TEST_DIFFERENCE_IGNORE(caseid, clips1, area1, clips2, area2, clips3) \ { ut_settings ignore_validity; ignore_validity.test_validity = false; \ (test_one) \ ( #caseid, caseid[0], caseid[1], clips1, -1, area1, clips2, -1, area2, \ clips3, -1, area1 + area2, ignore_validity); } +#endif template void test_all() @@ -368,10 +370,18 @@ void test_all() } #if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS) - test_one("ggl_list_20110820_christophe", - ggl_list_20110820_christophe[0], ggl_list_20110820_christophe[1], - 1, -1, 2.8570121719168924, - 1, -1, 64.498061986388564); + { + // symmetric difference is not valid due to robustness issue, it has + // two turns (touch_only) and a midpoint is located in other polygon + ut_settings ignore_validity; + ignore_validity.test_validity = false; + + test_one("ggl_list_20110820_christophe", + ggl_list_20110820_christophe[0], ggl_list_20110820_christophe[1], + 1, -1, 2.8570121719168924, + 1, -1, 64.498061986388564, + ignore_validity); + } #endif test_one("ggl_list_20120717_volker", diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index be0ee294b..f275078ac 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -106,7 +106,7 @@ void test_areal() TEST_DIFFERENCE(case_123_multi, 1, 0.25, 2, 0.625, 3); TEST_DIFFERENCE(case_124_multi, 1, 0.25, 2, 0.4375, 3); - TEST_DIFFERENCE_IGNORE(case_125_multi, 1, 0.25, 2, 0.400, 3); + TEST_DIFFERENCE(case_125_multi, 1, 0.25, 2, 0.400, 3); // A should have 3 clips, B should have 5 clips #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS From 654e7393e736342f56672928f64c70af62c97ac1 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 5 Jul 2017 17:12:01 +0200 Subject: [PATCH 023/188] [doc] update release notes --- doc/release_notes.qbk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 930c6dc7d..aca0f84d1 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -36,7 +36,8 @@ [*Bugfixes] * Fix is_valid which could return false for multipolygons where an interior ring touches another interior ring -* Fix union which could produce invalid results (for some cases, this needs to define BOOST_GEOMETRY_INCLUDE_SELF_TURNS +* Fix is_valid which could return false for multipolygons where a polygon was located in an interior ring, all touching each other +* Fix union which could produce invalid results (for some cases, this needs to define BOOST_GEOMETRY_INCLUDE_SELF_TURNS) * Fix intersection (idem), but some cases are still not yet valid * Fix difference (idem), but some cases are still not yet valid * Fix propagation of area strategy into the internals of various algorithms from intersection strategy From 91f0d84d9aeaca814a1b57e811e07b0aa3f5fd20 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 5 Jul 2017 18:16:00 +0200 Subject: [PATCH 024/188] [test] is_valid_failure: fix failure type of mpg09 --- test/algorithms/is_valid_failure.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/algorithms/is_valid_failure.cpp b/test/algorithms/is_valid_failure.cpp index 7d2ea1d27..cbdd29180 100644 --- a/test/algorithms/is_valid_failure.cpp +++ b/test/algorithms/is_valid_failure.cpp @@ -848,10 +848,9 @@ void test_open_multipolygons() bg::failure_self_intersections); // one polygon inside another and boundaries touching - // TODO: return the failure value failure_intersecting_interiors test::apply("mpg09", "MULTIPOLYGON(((0 0,10 0,10 10,0 10)),((0 0,9 1,9 2)))", - bg::failure_self_intersections); + bg::failure_intersecting_interiors); // one polygon inside another and boundaries not touching test::apply("mpg10", From 1b71880b4f2c5d98c76fadaf9fb892fff4ceb5c3 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 8 Jul 2017 12:45:24 +0200 Subject: [PATCH 025/188] [intersection] fix intersection pattern where it accidentally took last one instead of penultimate --- .../detail/overlay/traversal_intersection_patterns.hpp | 4 ++-- test/algorithms/overlay/multi_overlay_cases.hpp | 8 ++++++++ .../set_operations/difference/difference_multi.cpp | 1 + .../set_operations/intersection/intersection_multi.cpp | 1 + test/algorithms/set_operations/union/union_multi.cpp | 1 + 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp index 12279d762..ca35c423c 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp @@ -249,7 +249,7 @@ inline bool intersection_pattern_common_interior4(std::size_t& selected_rank, //Rank 1 {13[0] (s:0, r:0, m:0) i T rgn: 2 ISO ->15} {11[1] (s:1, r:1, m:0) i T rgn: 2 ISO ->15} //Rank 2 {13[0] (s:0, r:0, m:0) i F rgn: 2 ISO} {11[1] (s:1, r:1, m:0) i F rgn: 2 ISO} - // LEAVING (in two different directions, take last one) + // LEAVING (in two different directions, take penultimate one) //Rank 3 {10[1] (s:1, m:0) i T rgn: 1 ISO ->0} //Rank 4 {11[0] (s:0, m:0) i T rgn: 1 ISO ->12} @@ -292,7 +292,7 @@ inline bool intersection_pattern_common_interior4(std::size_t& selected_rank, // Check if pairs 1,2 (and possibly 3,4 and 5,6 etc) satisfy if (check_pairs(aggregation, incoming.region_id(), 1, n - 3)) { - selected_rank = n - 1; + selected_rank = n - 2; return true; } return false; diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index bd94b43a4..8f6f4556a 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1080,6 +1080,14 @@ static std::string case_recursive_boxes_60[2] = "MULTIPOLYGON(((2 3,1 2,1 4,0 4,0 5,3 5,3 3,2 3)),((1 2,3 2,3 3,4 4,5 4,5 3,4 3,3.5 2.5,4 2,4 0,0 0,0 1,1 1,1 2)),((4 2,5 3,5 2,4 2)))", }; + +static std::string case_recursive_boxes_61[2] = +{ + // Needs pattern 4 for intersections + "MULTIPOLYGON(((2 0,2 1,1 0,0 0,0 1,1 2,0 2,0 4,1 4,1 5,1.5 4.5,2 5,4 5,4.5 4.5,5 5,5 2,4.5 1.5,5 1,5 0,2 0),(3 1,3 2,2 2,2 1,3 1),(4 5,3.5 4.5,4 4,4 5)),((0 4,0 5,1 5,0 4)))", + "MULTIPOLYGON(((3 5,5 5,5 0,2 0,2 1,1 1,1 0,0 0,0 2,1 2,1 3,0 3,0 5,3 5),(3 1,3 2,2 2,2 1,3 1)))", +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index f275078ac..3507971d1 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -355,6 +355,7 @@ void test_areal() #else TEST_DIFFERENCE_IGNORE(case_recursive_boxes_60, 5, 5.25, 5, 5.25, 8); #endif + TEST_DIFFERENCE(case_recursive_boxes_61, 2, 1.5, 6, 2.0, 7); test_one("mysql_21965285_b", mysql_21965285_b[0], diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index ef7e5226a..4dd39e834 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -305,6 +305,7 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_58, 1, -1, 0.25); TEST_INTERSECTION(case_recursive_boxes_59, 8, -1, 8.25); TEST_INTERSECTION(case_recursive_boxes_60, 8, -1, 10.0); + TEST_INTERSECTION(case_recursive_boxes_61, 2, -1, 20.0); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index a3b1c2ac1..491fc7dd3 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -382,6 +382,7 @@ void test_areal() #endif TEST_UNION(case_recursive_boxes_60, 3, 0, -1, 20.5); + TEST_UNION(case_recursive_boxes_61, 1, 1, -1, 23.5); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 07389472a1bebdd39ce633b5da86d4e53096c583 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Sat, 8 Jul 2017 12:48:05 +0200 Subject: [PATCH 026/188] [test] small changes in test, moving settings --- .../difference/difference_multi.cpp | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 3507971d1..1533125f7 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -47,11 +47,6 @@ template void test_areal() { - ut_settings sym_settings; -#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) - sym_settings.sym_difference = false; -#endif - test_one("simplex_multi", case_multi_simplex[0], case_multi_simplex[1], 5, 21, 5.58, 4, 17, 2.58); @@ -357,12 +352,18 @@ void test_areal() #endif TEST_DIFFERENCE(case_recursive_boxes_61, 2, 1.5, 6, 2.0, 7); - test_one("mysql_21965285_b", - mysql_21965285_b[0], - mysql_21965285_b[1], - 2, -1, 183.71376870369406, - 2, -1, 131.21376870369406, - sym_settings); + { + ut_settings sym_settings; + #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + sym_settings.sym_difference = false; + #endif + test_one("mysql_21965285_b", + mysql_21965285_b[0], + mysql_21965285_b[1], + 2, -1, 183.71376870369406, + 2, -1, 131.21376870369406, + sym_settings); + } } @@ -384,11 +385,9 @@ void test_specific_areal() // Spikes in a-b and b-a, failure in symmetric difference ut_settings settings; +#if !defined(BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS) settings.sym_difference = false; settings.test_validity = false; -#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS - settings.test_validity = true; - settings.sym_difference = true; #endif TEST_DIFFERENCE_WITH(0, 1, ticket_11674, 3, 9105781.5, 5, 119059.5, -1); From 9acd4dcbba3a80edcc46458de1938d72ac03e55e Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 12 Jul 2017 09:56:23 +0200 Subject: [PATCH 027/188] [overlay] dont excluded colocated self-turns from check within other geometry --- .../algorithms/detail/overlay/handle_self_turns.hpp | 8 ++------ test/algorithms/overlay/multi_overlay_cases.hpp | 7 +++++++ .../set_operations/difference/difference_multi.cpp | 4 ++++ .../set_operations/intersection/intersection_multi.cpp | 1 + test/algorithms/set_operations/union/union_multi.cpp | 1 + 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp index 39c55db75..2debd48c6 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp @@ -52,9 +52,7 @@ struct discard_closed_turns { turn_type& turn = *it; - if (turn.cluster_id >= 0 - || turn.discarded - || ! is_self_turn(turn)) + if (turn.discarded || ! is_self_turn(turn)) { continue; } @@ -89,9 +87,7 @@ struct discard_self_intersection_turns { turn_type& turn = *it; - if (turn.cluster_id >= 0 - || turn.discarded - || ! is_self_turn(turn)) + if (turn.discarded || ! is_self_turn(turn)) { continue; } diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 8f6f4556a..436a24723 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1088,6 +1088,13 @@ static std::string case_recursive_boxes_61[2] = "MULTIPOLYGON(((3 5,5 5,5 0,2 0,2 1,1 1,1 0,0 0,0 2,1 2,1 3,0 3,0 5,3 5),(3 1,3 2,2 2,2 1,3 1)))", }; +static std::string case_recursive_boxes_62[2] = +{ + // Needs discarding self-intersections within other geometry + "MULTIPOLYGON(((2.5 3.5,3 4,3 3,4 3,4 2,5 2,5 0,1 0,1 1,2 1,3 2,2 2,2 3,1 3,2 4,2.5 3.5),(3 1,3 0,4 1,3 1)),((1 0,0 0,0 1,1 0)),((2 4,2 5,3 4,2 4)),((2 4,1 4,1 5,2 4)),((3 4,3 5,4 5,4 4,3 4)),((0 1,0 3,0.5 2.5,1 3,1 1,0 1)),((1 3,0 3,0 4,1 4,1 3)),((4 4,5 4,5 3,4 3,4 4)))", + "MULTIPOLYGON(((2 0,1 0,1 1,2 2,2 1,4 1,3 0,2 0)),((2 2,1 2,1 1,0.5 0.5,1 0,0 0,0 5,4 5,4 4,3 3,4 3,4 2,2 2),(1 3,1 4,0 4,1 3)),((4 2,5 1,4 1,4 2)))", +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 1533125f7..79c72e1f6 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -351,6 +351,10 @@ void test_areal() TEST_DIFFERENCE_IGNORE(case_recursive_boxes_60, 5, 5.25, 5, 5.25, 8); #endif TEST_DIFFERENCE(case_recursive_boxes_61, 2, 1.5, 6, 2.0, 7); +#if defined(BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS) + // Misses one triangle + TEST_DIFFERENCE(case_recursive_boxes_62, 5, 5.0, 11, 5.75, 12); +#endif { ut_settings sym_settings; diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 4dd39e834..1fa558894 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -306,6 +306,7 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_59, 8, -1, 8.25); TEST_INTERSECTION(case_recursive_boxes_60, 8, -1, 10.0); TEST_INTERSECTION(case_recursive_boxes_61, 2, -1, 20.0); + TEST_INTERSECTION(case_recursive_boxes_62, 9, -1, 10.5); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 491fc7dd3..ee6d9c29c 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -383,6 +383,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_60, 3, 0, -1, 20.5); TEST_UNION(case_recursive_boxes_61, 1, 1, -1, 23.5); + TEST_UNION(case_recursive_boxes_62, 2, 3, -1, 21.25); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 8b33899ec7b9b09322f442d8aef6c4bb342b7442 Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Thu, 13 Jul 2017 09:59:10 +0300 Subject: [PATCH 028/188] [distance] Point-segment distance formula, strategy and tests --- .../detail/distance/point_to_geometry.hpp | 2 +- .../formulas/distance_point_segment.hpp | 288 ++++++++++++++ .../boost/geometry/strategies/distance.hpp | 3 - .../geographic/distance_cross_track.hpp | 339 ++++++++++++++++ .../strategies/geographic/parameters.hpp | 51 +++ .../boost/geometry/strategies/strategies.hpp | 1 + test/algorithms/distance/Jamfile.v2 | 1 + .../distance/distance_brute_force.hpp | 22 +- .../algorithms/distance/distance_geo_pl_l.cpp | 370 +++++++++++++++++ .../distance/test_distance_geo_common.hpp | 371 ++++++++++++++++++ 10 files changed, 1433 insertions(+), 15 deletions(-) create mode 100644 include/boost/geometry/formulas/distance_point_segment.hpp create mode 100644 include/boost/geometry/strategies/geographic/distance_cross_track.hpp create mode 100644 test/algorithms/distance/distance_geo_pl_l.cpp create mode 100644 test/algorithms/distance/test_distance_geo_common.hpp diff --git a/include/boost/geometry/algorithms/detail/distance/point_to_geometry.hpp b/include/boost/geometry/algorithms/detail/distance/point_to_geometry.hpp index ab5de3d9b..9596799cb 100644 --- a/include/boost/geometry/algorithms/detail/distance/point_to_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/distance/point_to_geometry.hpp @@ -391,7 +391,7 @@ struct distance template struct distance < - Point, Polygon, Strategy, point_tag, polygon_tag, + Point, Polygon, Strategy, point_tag, polygon_tag, strategy_tag_distance_point_segment, false > : detail::distance::point_to_polygon < diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp new file mode 100644 index 000000000..6363aac8b --- /dev/null +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -0,0 +1,288 @@ +// Boost.Geometry + +// Copyright (c) 2016-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// 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) + +#ifndef BOOST_GEOMETRY_FORMULAS_CROSS_TRACK_GEO_HPP +#define BOOST_GEOMETRY_FORMULAS_CROSS_TRACK_GEO_HPP + +#include +#include + +namespace boost { namespace geometry { namespace formula +{ + +template +< + typename CT, + typename Units, + typename Inverse_type_azimuth, + typename Inverse_type_distance, + typename Direct_type +> +class cross_track_geo_formula{ + +public: + + CT static inline apply(CT lon1, CT lat1, //p1 + CT lon2, CT lat2, //p2 + CT lon3, CT lat3) //query point p3 + { + + int print = 0; + + // Constants + geometry::srs::spheroid spheroid; + CT const f = flattening(spheroid); + CT const pi = math::pi(); + CT const half_pi = math::pi() / CT(2); + geometry::srs::sphere sphere; + CT const earth_radius = sphere.get_radius<1>(); + //CT const earth_radius = 6372795; + //CT const earth_radius = 6378137; + + // Convert to radians + lon1 = math::as_radian(lon1); + lat1 = math::as_radian(lat1); + lon2 = math::as_radian(lon2); + lat2 = math::as_radian(lat2); + lon3 = math::as_radian(lon3); + lat3 = math::as_radian(lat3); + + if (lon1 > lon2) + { + std::swap(lon1, lon2); + std::swap(lat1, lat2); + } + + //segment on equator + if (math::equals(lat1, 0) && math::equals(lat2, 0)) + { + if (lon3 <= lon1) + { + return Inverse_type_distance::apply(lon1, lat1, lon3, lat3, spheroid).distance; + } + if (lon3 >= lon2) + { + return Inverse_type_distance::apply(lon2, lat2, lon3, lat3, spheroid).distance; + } + return Inverse_type_distance::apply(lon3, lat1, lon3, lat3, spheroid).distance; + } + + //segment on meridian + if (math::equals(lon1, lon2)) + { + if (lat1 > lat2) + { + std::swap(lat1, lat2); + } + if (lat3 <= lat1) + { + return Inverse_type_distance::apply(lon1, lat1, lon3, lat3, spheroid).distance; + } + if (lat3 >= lat2) + { + return Inverse_type_distance::apply(lon2, lat2, lon3, lat3, spheroid).distance; + } + return Inverse_type_distance::apply(lon1, lat3, lon3, lat3, spheroid).distance; + } + + // Easy cases + if (lon3 <= lon1 && lat3 <= lat1) + { + return Inverse_type_distance::apply(lon1, lat1, lon3, lat3, spheroid).distance; + } + if (lon3 >= lon2 && lat3 <= lat2) + { + return Inverse_type_distance::apply(lon2, lat2, lon3, lat3, spheroid).distance; + } + + // Compute a1 (GEO) + CT a1 = Inverse_type_azimuth::apply(lon1, lat1, lon2, lat2, spheroid).azimuth; + CT a13 = Inverse_type_azimuth::apply(lon1, lat1, lon3, lat3, spheroid).azimuth; + + CT a312 = a1 - a13; + + if (print) + { + std::cout << "a1=" << a1 * math::r2d() << std::endl; + std::cout << "a13=" << a13 * math::r2d() << std::endl; + std::cout << "a312=" << a312 * math::r2d() << std::endl; + } + if (a312 > half_pi) + { + // projection of p3 on geodesic spanned by segment (p1,p2) fall + // outside of segment on the side of p1 + return Inverse_type_distance::apply(lon1, lat1, lon3, lat3, spheroid).distance; + } + + CT a2 = pi + Inverse_type_azimuth::apply(lon2, lat2, lon1, lat1, spheroid).azimuth; + CT a23 = Inverse_type_azimuth::apply(lon2, lat2, lon3, lat3, spheroid).azimuth; + + CT a321 = a2 - a23; + + if (print) + { + std::cout << "a2=" << a2 * math::r2d() << std::endl; + std::cout << "a23=" << a13 * math::r2d() << std::endl; + std::cout << "a321=" << a321 * math::r2d() << std::endl; + } + if (a321 < half_pi) + { + // projection of p3 on geodesic spanned by segment (p1,p2) fall + // outside of segment on the side of p2 + return Inverse_type_distance::apply(lon2, lat2, lon3, lat3, spheroid).distance; + } + + // Guess s14 (SPHERICAL) + typedef geometry::model::point > point; + + CT bet1 = atan((1 - f) * tan(lon1)); + CT bet2 = atan((1 - f) * tan(lon2)); + CT bet3 = atan((1 - f) * tan(lon3)); + point p1 = point(bet1, lat1); + point p2 = point(bet2, lat2); + point p3 = point(bet3, lat3); + + geometry::strategy::distance::cross_track cross_track(earth_radius); + CT s34 = cross_track.apply(p3, p1, p2); + + geometry::strategy::distance::haversine str(earth_radius); + CT s13 = str.apply(p1, p3); + /* + // Closest point is one of the segment's endpoints + if (s34 == s13) + { + return Inverse_type_distance::apply(lon1, lat1, lon3, lat3, spheroid).distance; + } + CT s23 = str.apply(p2, p3); + if (s34 == s23) + { + return Inverse_type_distance::apply(lon2, lat2, lon3, lat3, spheroid).distance; + } +*/ + CT s14 = acos( cos(s13/earth_radius) / cos(s34/earth_radius)) * earth_radius; + + if (print) + { + std::cout << "s34=" << s34 << std::endl; + std::cout << "s13=" << s13 << std::endl; + std::cout << "s14=" << s14 << std::endl; + std::cout << "===============" << std::endl; + } + + // Update s14 (using Newton method) + CT prev_distance = 0; + geometry::formula::result_direct res14; + geometry::formula::result_inverse res34; + + for (int i=0; i<20; ++i) + { + // Solve the direct problem to find p4 (GEO) + res14 = Direct_type::apply(lon1, lat1, s14, a1, spheroid); + + // Solve an inverse problem to find g4 + // g4 is the angle between segment (p1,p2) and segment (p3,p4) that meet on p4 (GEO) + + CT a4 = Inverse_type_azimuth::apply(res14.lon2, res14.lat2, lon2, lat2, spheroid).azimuth; + res34 = Inverse_type_distance::apply(res14.lon2, res14.lat2, lon3, lat3, spheroid); + CT g4 = res34.azimuth - a4; + + // Normalize g4 + CT delta_g4; + + if (g4 < 0 && g4 < -pi)//close to -270 + { + delta_g4 = g4 + 1.5 * pi; + } + else if (g4 > 0 && g4 > pi)//close to 270 + { + delta_g4 = - g4 + 1.5 * pi; + } + else if (g4 < 0 && g4 > -pi)//close to -90 + { + delta_g4 = -g4 - pi/2; + } + else //close to 90 + { + delta_g4 = g4 - pi/2; + } + + CT M43 = res34.geodesic_scale; // cos(s14/earth_radius) is the spherical limit + CT m34 = res34.reduced_length; + CT der = M43 / m34; + s14 = s14 - delta_g4 / der; + + //Print + if (print) + { + std::cout << "p4=" << res14.lon2 * math::r2d() << + "," << res14.lat2 * math::r2d() << std::endl; + std::cout << "delta_g4=" << delta_g4 << std::endl; + std::cout << "g4=" << g4 * math::r2d() << std::endl; + std::cout << "der=" << der << std::endl; + std::cout << "new_s14=" << s14 << std::endl; + std::cout << "dist =" << res34.distance << std::endl; + std::cout << "---------end of step " << i << std::endl<< std::endl; + } + + if (g4 == math::pi()/2 || (res34.distance >= prev_distance && prev_distance!=0) || delta_g4 == 0) + { + //std::cout << "stop" << std::endl; + return prev_distance; + } + prev_distance = res34.distance; + + } + + ////////////////////////////TESTING + + std::cout << "dist =" << res34.distance << std::endl; + + point p4(res14.lon2, res14.lat2); + CT s34_sph = str.apply(p4, p3); + + std::cout << "s34(sph) =" << s34_sph << std::endl; + std::cout << "s34(geo) =" + << Inverse_type_distance::apply(get<0>(p4), get<1>(p4), lon3, lat3, spheroid).distance + << ", p4=(" << get<0>(p4) * math::r2d() << "," + << get<1>(p4) * math::r2d() << ")" + << std::endl; + + CT s31 = Inverse_type_distance::apply(lon3, lat3, lon1, lat1, spheroid).distance; + CT s32 = Inverse_type_distance::apply(lon3, lat3, lon2, lat2, spheroid).distance; + + CT a4 = Inverse_type_azimuth::apply(get<0>(p4), get<1>(p4), lon2, lat2, spheroid).azimuth; + geometry::formula::result_direct res4 = Direct_type::apply(get<0>(p4), get<1>(p4), .04, a4, spheroid); + CT p4_plus = Inverse_type_distance::apply(res4.lon2, res4.lat2, lon3, lat3, spheroid).distance; + + geometry::formula::result_direct res1 = Direct_type::apply(lon1, lat1, s14-.04, a1, spheroid); + CT p4_minus = Inverse_type_distance::apply(res1.lon2, res1.lat2, lon3, lat3, spheroid).distance; + + std::cout << "s31=" << s31 << "\ns32=" << s32 + << "\np4_plus=" << p4_plus << ", p4=(" << res4.lon2 * math::r2d() << "," << res4.lat2 * math::r2d() << ")" + << "\np4_minus=" << p4_minus << ", p4=(" << res1.lon2 * math::r2d() << "," << res1.lat2 * math::r2d() << ")" + << std::endl; + + if (res34.distance <= p4_plus && res34.distance <= p4_minus) + { + std::cout << "Closest point computed" << std::endl << std::endl; + } + else + { + std::cout << "There is a closer point nearby!" << std::endl; + } + + CT min_dist_endpoints = std::min(s31,s32); + return std::min(res34.distance, min_dist_endpoints); + } + +}; + +}}} // namespace boost::geometry::formula +#endif // BOOST_GEOMETRY_FORMULAS_CROSS_TRACK_GEO_HPP diff --git a/include/boost/geometry/strategies/distance.hpp b/include/boost/geometry/strategies/distance.hpp index 98ccb8202..0d5a9e6dd 100644 --- a/include/boost/geometry/strategies/distance.hpp +++ b/include/boost/geometry/strategies/distance.hpp @@ -65,9 +65,6 @@ template struct get_comparable template struct result_from_distance {}; - - - // Default strategy diff --git a/include/boost/geometry/strategies/geographic/distance_cross_track.hpp b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp new file mode 100644 index 000000000..b339f8f45 --- /dev/null +++ b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp @@ -0,0 +1,339 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2016-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// 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) + +#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_CROSS_TRACK_HPP +#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_CROSS_TRACK_HPP + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK +# include +#endif + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace distance +{ + + +/*! +\brief Strategy functor for distance point to segment calculation +\ingroup strategies +\details Class which calculates the distance of a point to a segment, for points on a sphere or globe +\see http://williams.best.vwh.net/avform.htm +\tparam CalculationType \tparam_calculation +\tparam Strategy underlying point-point distance strategy +\qbk{ +[heading See also] +[link geometry.reference.algorithms.distance.distance_3_with_strategy distance (with strategy)] +} +*/ +template +< + typename FormulaPolicy = strategy::andoyer, + typename Spheroid = srs::spheroid, + typename CalculationType = void +> +class cross_track_geo +{ +public : + template + struct return_type + : promote_floating_point + < + typename select_calculation_type + < + Point, + PointOfSegment, + CalculationType + >::type + > + {}; + + inline cross_track_geo() + {} + + typedef geographic + < + FormulaPolicy, Spheroid, CalculationType + > DistanceStrategy; + + /* + template + inline T comparable_to_distance(T& a) const + { + return a; + } +*/ + template + inline typename return_type::type + apply(Point const& p, PointOfSegment const& sp1, PointOfSegment const& sp2) const + { + typedef typename coordinate_system::type::units units_type; + + typedef typename return_type::type CT; + + typedef typename FormulaPolicy::template inverse + < + CT, + false, + true, + false, + false, + false + > inverse_type_azimuth; + + typedef typename FormulaPolicy::template inverse + < + CT, + true, + true, + false, + true, + true + > inverse_type_distance; + + typedef typename FormulaPolicy::template direct + < + CT, + true, + false, + false, + false + > direct_type; + + return geometry::formula::cross_track_geo_formula + < + CT, + units_type, + inverse_type_azimuth, + inverse_type_distance, + direct_type + >::apply(get<0>(sp1), get<1>(sp1), + get<0>(sp2), get<1>(sp2), + get<0>(p), get<1>(p)); + } + + //TODO: apply a more general strategy getter + inline DistanceStrategy get_distance_strategy() const + { + return DistanceStrategy(); + } + +}; + + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +namespace services +{ + +//tags +template +struct tag > +{ + typedef strategy_tag_distance_point_segment type; +}; + +template +< + typename FormulaPolicy, + typename Spheroid +> +struct tag > +{ + typedef strategy_tag_distance_point_segment type; +}; + +template +< + typename FormulaPolicy, + typename Spheroid, + typename CalculationType +> +struct tag > +{ + typedef strategy_tag_distance_point_segment type; +}; + + +//return types +template +struct return_type, P, PS> + : cross_track_geo::template return_type +{}; + +template +< + typename FormulaPolicy, + typename Spheroid, + typename P, + typename PS +> +struct return_type, P, PS> + : cross_track_geo::template return_type +{}; + +template +< + typename FormulaPolicy, + typename Spheroid, + typename CalculationType, + typename P, + typename PS +> +struct return_type, P, PS> + : cross_track_geo::template return_type +{}; + +//comparable types +template +< + typename FormulaPolicy, + typename Spheroid, + typename CalculationType +> +struct comparable_type > +{ + typedef cross_track_geo + < + FormulaPolicy, Spheroid, CalculationType + > type; +}; + +template +< + typename FormulaPolicy, + typename Spheroid, + typename CalculationType +> +struct get_comparable > +{ + typedef typename comparable_type + < + cross_track_geo + >::type comparable_type; +public : + static inline comparable_type + apply(cross_track_geo const& strategy) + { + return comparable_type(); + } +}; + + +template +< + typename FormulaPolicy, + typename P, + typename PS +> +struct result_from_distance, P, PS> +{ +private : + typedef typename cross_track_geo + < + FormulaPolicy + >::template return_type::type return_type; +public : + template + static inline return_type + apply(cross_track_geo const& , T const& distance) + { + return distance; + } +}; + +/* +template +< + typename FormulaPolicy, + typename P, + typename PS +> +struct result_from_comparable + < + cross_track_geo, P, PS + > +{ +private : + typedef cross_track_geo strategy_type; +// typedef typename return_type::type return_type; +public : + template + static inline T apply(strategy_type const& strategy, + T const& comparable_distance) + { + //T c = T(2.0) * asin(math::sqrt(a)); + //return c * strategy.radius(); + return comparable_distance; + } +}; +*/ + +template +struct default_strategy + < + point_tag, segment_tag, Point, PointOfSegment, + geographic_tag, geographic_tag + > +{ + typedef cross_track_geo<> type; +}; + + +template +struct default_strategy + < + segment_tag, point_tag, PointOfSegment, Point, + geographic_tag, geographic_tag + > +{ + typedef typename default_strategy + < + point_tag, segment_tag, Point, PointOfSegment, + geographic_tag, geographic_tag + >::type type; +}; + +} // namespace services +#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + +}} // namespace strategy::distance + +}} // namespace boost::geometry +#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_DISTANCE_CROSS_TRACK_HPP diff --git a/include/boost/geometry/strategies/geographic/parameters.hpp b/include/boost/geometry/strategies/geographic/parameters.hpp index 5638db50f..c70c636f0 100644 --- a/include/boost/geometry/strategies/geographic/parameters.hpp +++ b/include/boost/geometry/strategies/geographic/parameters.hpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -24,6 +26,23 @@ namespace boost { namespace geometry { namespace strategy struct andoyer { + //TODO: this should be replaced by an andoyer direct formula + template + < + typename CT, + bool EnableCoordinates = true, + bool EnableReverseAzimuth = false, + bool EnableReducedLength = false, + bool EnableGeodesicScale = false + > + struct direct + : formula::thomas_direct + < + CT, EnableCoordinates, EnableReverseAzimuth, + EnableReducedLength, EnableGeodesicScale + > + {}; + template < typename CT, @@ -45,6 +64,22 @@ struct andoyer struct thomas { + template + < + typename CT, + bool EnableCoordinates = true, + bool EnableReverseAzimuth = false, + bool EnableReducedLength = false, + bool EnableGeodesicScale = false + > + struct direct + : formula::thomas_direct + < + CT, EnableCoordinates, EnableReverseAzimuth, + EnableReducedLength, EnableGeodesicScale + > + {}; + template < typename CT, @@ -66,6 +101,22 @@ struct thomas struct vincenty { + template + < + typename CT, + bool EnableCoordinates = true, + bool EnableReverseAzimuth = false, + bool EnableReducedLength = false, + bool EnableGeodesicScale = false + > + struct direct + : formula::vincenty_direct + < + CT, EnableCoordinates, EnableReverseAzimuth, + EnableReducedLength, EnableGeodesicScale + > + {}; + template < typename CT, diff --git a/include/boost/geometry/strategies/strategies.hpp b/include/boost/geometry/strategies/strategies.hpp index 27a025c7c..05895ddf5 100644 --- a/include/boost/geometry/strategies/strategies.hpp +++ b/include/boost/geometry/strategies/strategies.hpp @@ -83,6 +83,7 @@ #include #include #include +#include #include #include #include diff --git a/test/algorithms/distance/Jamfile.v2 b/test/algorithms/distance/Jamfile.v2 index 656555a57..e19750a66 100644 --- a/test/algorithms/distance/Jamfile.v2 +++ b/test/algorithms/distance/Jamfile.v2 @@ -26,4 +26,5 @@ test-suite boost-geometry-algorithms-distance [ run distance_se_pl_l.cpp : : : : algorithms_distance_se_pl_l ] [ run distance_se_pl_pl.cpp : : : : algorithms_distance_se_pl_pl ] [ run distance_se_point_box.cpp : : : : algorithms_distance_se_point_box ] + [ run distance_geo_pl_l.cpp : : : : algorithms_distance_geo_pl_l ] ; diff --git a/test/algorithms/distance/distance_brute_force.hpp b/test/algorithms/distance/distance_brute_force.hpp index b62c09105..7992ffcae 100644 --- a/test/algorithms/distance/distance_brute_force.hpp +++ b/test/algorithms/distance/distance_brute_force.hpp @@ -170,7 +170,7 @@ struct distance_brute_force }; -template +template < typename Point1, typename Point2, @@ -184,7 +184,7 @@ struct distance_brute_force {}; -template +template < typename Point, typename Segment, @@ -198,7 +198,7 @@ struct distance_brute_force {}; -template +template < typename Point, typename Box, @@ -226,7 +226,7 @@ struct distance_brute_force {}; -template +template < typename Point, typename Linear, @@ -258,7 +258,7 @@ struct distance_brute_force }; -template +template < typename Point, typename MultiPoint, @@ -286,7 +286,7 @@ struct distance_brute_force } }; -template +template < typename MultiPoint1, typename MultiPoint2, @@ -320,7 +320,7 @@ struct distance_brute_force }; -template +template < typename MultiPoint, typename Linear, @@ -354,7 +354,7 @@ struct distance_brute_force }; -template +template < typename Linear, typename MultiPoint, @@ -384,7 +384,7 @@ struct distance_brute_force }; -template +template < typename MultiPoint, typename Segment, @@ -413,7 +413,7 @@ struct distance_brute_force }; -template +template < typename Linear, typename Segment, @@ -445,7 +445,7 @@ struct distance_brute_force }; -template +template < typename Linear1, typename Linear2, diff --git a/test/algorithms/distance/distance_geo_pl_l.cpp b/test/algorithms/distance/distance_geo_pl_l.cpp new file mode 100644 index 000000000..f30665de8 --- /dev/null +++ b/test/algorithms/distance/distance_geo_pl_l.cpp @@ -0,0 +1,370 @@ + +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2016-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#include + +#ifndef BOOST_TEST_MODULE +#define BOOST_TEST_MODULE test_distance_geographic_pl_l +#endif + +#include +#include + +#include "test_distance_geo_common.hpp" + +typedef bg::cs::geographic cs_type; +typedef bg::model::point point_type; +typedef bg::model::segment segment_type; +typedef bg::model::multi_point multi_point_type; +typedef bg::model::segment segment_type; +typedef bg::model::linestring linestring_type; +typedef bg::model::multi_linestring multi_linestring_type; + +namespace services = bg::strategy::distance::services; +typedef bg::default_distance_result::type return_type; + +//typedef bg::strategy::distance::haversine point_point_strategy; +//typedef bg::strategy::distance::cross_track<> point_segment_strategy; + +typedef bg::srs::spheroid stype; + +// Strategies for point-point distance + +typedef bg::strategy::distance::andoyer andoyer_pp; +typedef bg::strategy::distance::thomas thomas_pp; +typedef bg::strategy::distance::vincenty vincenty_pp; + +// Strategies for point-segment distance + +typedef bg::strategy::distance::cross_track_geo + andoyer_strategy; + +typedef bg::strategy::distance::cross_track_geo + thomas_strategy; + +typedef bg::strategy::distance::cross_track_geo + vincenty_strategy; + +//=========================================================================== + +template +inline bg::default_distance_result::type +pp_distance(std::string const& wkt1, + std::string const& wkt2, + Strategy const& strategy) +{ + point_type p1, p2; + bg::read_wkt(wkt1, p1); + bg::read_wkt(wkt2, p2); + return bg::distance(p1, p2, strategy); +} + +template +inline bg::default_comparable_distance_result::type +pp_comparable_distance(std::string const& wkt1, + std::string const& wkt2, + Strategy const&) +{ + point_type p1, p2; + bg::read_wkt(wkt1, p1); + bg::read_wkt(wkt2, p2); + return bg::comparable_distance(p1, p2); +} + +template +inline bg::default_distance_result::type +ps_distance(std::string const& wkt1, + std::string const& wkt2, + Strategy const& strategy) +{ + point_type p; + segment_type s; + bg::read_wkt(wkt1, p); + bg::read_wkt(wkt2, s); + return bg::distance(p, s, strategy); +} + +template +inline bg::default_comparable_distance_result::type +ps_comparable_distance(std::string const& wkt1, + std::string const& wkt2, + Strategy const& strategy) +{ + point_type p; + segment_type s; + bg::read_wkt(wkt1, p); + bg::read_wkt(wkt2, s); + return bg::comparable_distance(p, s, strategy); +} + +template +T to_comparable(Strategy const& strategy, T const& distance) +{ + namespace services = bg::strategy::distance::services; + + typedef typename services::comparable_type + < + Strategy + >::type comparable_strategy; + + typedef typename services::result_from_distance + < + comparable_strategy, + point_type, + bg::point_type::type + > get_comparable_distance; + + comparable_strategy cstrategy = services::get_comparable + < + Strategy + >::apply(strategy); + + return get_comparable_distance::apply(cstrategy, distance); +} + +//=========================================================================== + +template +void test_distance_point_segment(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ + +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "point/segment distance tests" << std::endl; +#endif + typedef test_distance_of_geometries tester; + + tester::apply("p-s-01", + "POINT(0 0)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(0 0)", "POINT(2 0)", strategy_pp), + strategy_ps); + tester::apply("p-s-02", + "POINT(2.5 3)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(2.5 3)", "POINT(2.49777 0)", strategy_pp), + strategy_ps); + tester::apply("p-s-03", + "POINT(2 0)", + "SEGMENT(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-s-04", + "POINT(3 0)", + "SEGMENT(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-s-06", + "POINT(3.5 3)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(3 0)", "POINT(3.5 3)", strategy_pp), + strategy_ps); + tester::apply("p-s-07", + "POINT(15 80)", + "SEGMENT(10 15,30 15)", + 7204174.8264546748, + strategy_ps); + tester::apply("p-s-08", + "POINT(15 10)", + "SEGMENT(10 15,30 15)", + 571412.71247283253, + strategy_ps); + tester::apply("p-s-09", + "POINT(5 10)", + "SEGMENT(10 15,30 15)", + pp_distance("POINT(5 10)", "POINT(10 15)", strategy_pp), + strategy_ps); + tester::apply("p-s-10", + "POINT(35 10)", + "SEGMENT(10 15,30 15)", + pp_distance("POINT(35 10)", "POINT(30 15)", strategy_pp), + strategy_ps); + tester::apply("p-s-11", + "POINT(5 10)", + "SEGMENT(30 15,10 15)", + pp_distance("POINT(5 10)", "POINT(10 15)", strategy_pp), + strategy_ps); + tester::apply("p-s-12", + "POINT(35 10)", + "SEGMENT(30 15,10 15)", + pp_distance("POINT(35 10)", "POINT(30 15)", strategy_pp), + strategy_ps); + + tester::apply("p-s-right-up", + "POINT(3.5 3)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(3 2)", "POINT(3.5 3)", strategy_pp), + strategy_ps); + + tester::apply("p-s-left-up", + "POINT(1.5 3)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2 2)", "POINT(1.5 3)", strategy_pp), + strategy_ps); + + tester::apply("p-s-up-1", + "POINT(2 3)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2.0003 2)", "POINT(2 3)", strategy_pp), + strategy_ps); + + tester::apply("p-s-up-2", + "POINT(3 3)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2.9997 2)", "POINT(3 3)", strategy_pp), + strategy_ps); + + tester::apply("p-s-right-down", + "POINT(3.5 1)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(3 2)", "POINT(3.5 1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-left-down", + "POINT(1.5 1)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2 2)", "POINT(1.5 1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-down-1", + "POINT(2 1)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2.0003 2)", "POINT(2 1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-down-2", + "POINT(3 1)", + "SEGMENT(2 2,3 2)", + pp_distance("POINT(2.9997 2)", "POINT(3 1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-south", + "POINT(3 -1)", + "SEGMENT(2 -2,3 -2)", + pp_distance("POINT(2.9997 -2)", "POINT(3 -1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-antimeridian-1", + "POINT(3 1)", + "SEGMENT(220 2,3 2)", + pp_distance("POINT(3 2)", "POINT(3 1)", strategy_pp), + strategy_ps); + + tester::apply("p-s-antimeridian-2", + "POINT(220 1)", + "SEGMENT(220 2,3 2)", + pp_distance("POINT(220 2)", "POINT(220 1)", strategy_pp), + strategy_ps); + + // equator special case + tester::apply("p-s-eq1", + "POINT(2.5 0)", + "SEGMENT(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-s-eq2", + "POINT(2.5 2)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(2.5 0)", "POINT(2.5 2)", strategy_pp), + strategy_ps); + tester::apply("p-s-eq3", + "POINT(2 2)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(2 0)", "POINT(2 2)", strategy_pp), + strategy_ps); + tester::apply("p-s-eq4", + "POINT(3 2)", + "SEGMENT(2 0,3 0)", + pp_distance("POINT(3 0)", "POINT(3 2)", strategy_pp), + strategy_ps); + + // meridian special case + tester::apply("p-s-mer1", + "POINT(2.5 2)", + "SEGMENT(2 2,2 4)", + pp_distance("POINT(2.5 2)", "POINT(2 2)", strategy_pp), + strategy_ps); + tester::apply("p-s-mer2", + "POINT(2.5 3)", + "SEGMENT(2 2,2 4)", + pp_distance("POINT(2.5 3)", "POINT(2 3)", strategy_pp), + strategy_ps); + tester::apply("p-s-mer3", + "POINT(2.5 5)", + "SEGMENT(2 2,2 4)", + pp_distance("POINT(2.5 5)", "POINT(2 4)", strategy_pp), + strategy_ps); + + + //Nearly antipodal points not treated correctly in BG so far +/* + // very small distances to segment + tester::apply("p-s-07", + "POINT(90 1e-3)", + "SEGMENT(0.5 0,175.5 0)", + 110.57427582147321, + //1e-3 * d2r * 6335439.3272858784, + //to_comparable(strategy, 1e-3 * d2r * strategy.radius()), + strategy_ps); + tester::apply("p-s-08", + "POINT(90 1e-4)", + "SEGMENT(0.5 0,175.5 0)", + 6176.4469462110519, + //1e-4 * d2r * 6335439.3272858784, + //1e-4 * d2r * strategy.radius(), + //to_comparable(strategy, 1e-4 * d2r * strategy.radius()), + strategy_ps); + tester::apply("p-s-09", + "POINT(90 1e-5)", + "SEGMENT(0.5 0,175.5 0)", + 4917.5121192812849, + //1e-5 * d2r * strategy.radius(), + //to_comparable(strategy, 1e-5 * d2r * strategy.radius()), + strategy_ps); + tester::apply("p-s-10", + "POINT(90 1e-6)", + "SEGMENT(0.5 0,175.5 0)", + 4997.0354014473833, + //1e-6 * d2r * strategy.radius(), + //to_comparable(strategy, 1e-6 * d2r * strategy.radius()), + strategy_ps); + tester::apply("p-s-11", + "POINT(90 1e-7)", + "SEGMENT(0.5 0,175.5 0)", + 109854.20732250962, + //1e-7 * d2r * strategy.radius(), + //to_comparable(strategy, 1e-7 * d2r * strategy.radius()), + strategy_ps); + tester::apply("p-s-12", + "POINT(90 1e-8)", + "SEGMENT(0.5 0,175.5 0)", + 1372504.9365672076, + //1e-8 * d2r * strategy.radius(), + //to_comparable(strategy, 1e-8 * d2r * strategy.radius()), + strategy_ps); +*/ +} + +//=========================================================================== +//=========================================================================== +//=========================================================================== + +BOOST_AUTO_TEST_CASE( test_all_point_segment ) +{ + //test_distance_point_segment(andoyer_strategy()); + //test_distance_point_segment(thomas_strategy()); + test_distance_point_segment(vincenty_pp(), vincenty_strategy()); + test_distance_point_segment(thomas_pp(), thomas_strategy()); + test_distance_point_segment(andoyer_pp(), andoyer_strategy()); + //test_distance_point_segment(point_segment_strategy(earth_radius_km)); + //test_distance_point_segment(point_segment_strategy(earth_radius_miles)); +} diff --git a/test/algorithms/distance/test_distance_geo_common.hpp b/test/algorithms/distance/test_distance_geo_common.hpp new file mode 100644 index 000000000..5a820dc90 --- /dev/null +++ b/test/algorithms/distance/test_distance_geo_common.hpp @@ -0,0 +1,371 @@ + +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2016-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_TEST_DISTANCE_GEO_COMMON_HPP +#define BOOST_GEOMETRY_TEST_DISTANCE_GEO_COMMON_HPP + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "distance_brute_force.hpp" + +namespace bg = ::boost::geometry; + +//======================================================================== + + +template +struct check_equal +{ + template + struct equal_to + { + static inline void apply(Value const& x, Value const& y) + { + BOOST_CHECK(x == y); + } + }; + + template + struct equal_to + { + static inline void apply(double x, double y) + { + BOOST_CHECK_CLOSE(x, y, 0.001); + } + }; + + template + static inline void apply(std::string const& /*case_id*/, + std::string const& /*subcase_id*/, + Geometry1 const& /*geometry1*/, + Geometry2 const& /*geometry2*/, + T const& detected, + T const& expected) + { + equal_to::apply(expected, detected); + /* + TODO: + Ideally we would want the following, but it does not work well + approximate equality test. + BOOST_CHECK_MESSAGE(equal_to::apply(expected, detected), + "case ID: " << case_id << "-" << subcase_id << "; " + << "G1: " << bg::wkt(geometry1) + << " - " + << "G2: " << bg::wkt(geometry2) + << " -> Detected: " << detected + << "; Expected: " << expected); + */ + } +}; + +//======================================================================== + +template +< + typename Geometry1, typename Geometry2, + int id1 = bg::geometry_id::value, + int id2 = bg::geometry_id::value +> +struct test_distance_of_geometries + : public test_distance_of_geometries +{}; + + +template +struct test_distance_of_geometries +{ + template + < + typename DistanceType, + typename ComparableDistanceType, + typename Strategy + > + static inline + void apply(std::string const& case_id, + std::string const& wkt1, + std::string const& wkt2, + DistanceType const& expected_distance, + ComparableDistanceType const& expected_comparable_distance, + Strategy const& strategy, + bool test_reversed = true) + { + Geometry1 geometry1 = from_wkt(wkt1); + Geometry2 geometry2 = from_wkt(wkt2); + + apply(case_id, geometry1, geometry2, + expected_distance, expected_comparable_distance, + strategy, test_reversed); + } + + template + static inline + void apply(std::string const& case_id, + std::string const& wkt1, + std::string const& wkt2, + DistanceType const& expected_distance, + Strategy const& strategy, + bool test_reversed = true) + { + Geometry1 geometry1 = from_wkt(wkt1); + Geometry2 geometry2 = from_wkt(wkt2); + + apply(case_id, geometry1, geometry2, + expected_distance, expected_distance, + strategy, test_reversed); + } + + + template + < + typename DistanceType, + typename ComparableDistanceType, + typename Strategy + > + static inline + void apply(std::string const& case_id, + Geometry1 const& geometry1, + Geometry2 const& geometry2, + DistanceType const& expected_distance, + ComparableDistanceType const& expected_comparable_distance, + Strategy const& strategy, + bool test_reversed = true) + { +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "case ID: " << case_id << "; " + << "G1: " << bg::wkt(geometry1) + << " - " + << "G2: " << bg::wkt(geometry2) + << std::endl; +#endif + namespace services = bg::strategy::distance::services; + + using bg::unit_test::distance_brute_force; + + typedef typename bg::default_distance_result + < + Geometry1, Geometry2 + >::type default_distance_result; + + typedef typename services::return_type + < + Strategy, Geometry1, Geometry2 + >::type distance_result_from_strategy; + + static const bool same_regular = boost::is_same + < + default_distance_result, + distance_result_from_strategy + >::type::value; + + BOOST_CHECK(same_regular); +/* + typedef typename bg::default_comparable_distance_result + < + Geometry1, Geometry2 + >::type default_comparable_distance_result; + typedef typename services::return_type + < + typename services::comparable_type::type, + Geometry1, + Geometry2 + >::type comparable_distance_result_from_strategy; + static const bool same_comparable = boost::is_same + < + default_comparable_distance_result, + comparable_distance_result_from_strategy + >::type::value; + BOOST_CHECK( same_comparable ); +*/ + // check distance with passed strategy + distance_result_from_strategy dist = + bg::distance(geometry1, geometry2, strategy); + + check_equal + < + distance_result_from_strategy + >::apply(case_id, "a", geometry1, geometry2, + dist, expected_distance); + + // check against the comparable distance computed in a + // brute-force manner + default_distance_result dist_brute_force + = distance_brute_force(geometry1, geometry2, strategy); + + check_equal + < + default_distance_result + >::apply(case_id, "b", geometry1, geometry2, + dist_brute_force, expected_distance); + +/* + // check comparable distance with passed strategy + comparable_distance_result_from_strategy cdist = + bg::comparable_distance(geometry1, geometry2, strategy); + check_equal + < + default_comparable_distance_result + >::apply(case_id, "c", geometry1, geometry2, + cdist, expected_comparable_distance); + // check against the comparable distance computed in a + // brute-force manner + default_comparable_distance_result cdist_brute_force + = distance_brute_force(geometry1, + geometry2, + services::get_comparable + < + Strategy + >::apply(strategy)); + check_equal + < + default_comparable_distance_result + >::apply(case_id, "d", geometry1, geometry2, + cdist_brute_force, expected_comparable_distance); +*/ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << string_from_type::type>::name() + << string_from_type::type>::name() + << " -> " + << string_from_type::name() + //<< string_from_type::name() + << std::endl; + //std::cout << "strategy radius: " << strategy.radius() << std::endl; + std::cout << "expected distance = " + << expected_distance << " ; " + << "expected comp. distance = " + // << expected_comparable_distance + << std::endl; + std::cout << "distance = " + << dist << " ; " + << "comp. distance = " + //<< cdist + << std::endl; + + if ( !test_reversed ) + { + std::cout << std::endl; + } +#endif + + if ( test_reversed ) + { + // check distance with given strategy + dist = bg::distance(geometry2, geometry1, strategy); + + check_equal + < + default_distance_result + >::apply(case_id, "ra", geometry2, geometry1, + dist, expected_distance); +/* + // check comparable distance with given strategy + cdist = bg::comparable_distance(geometry2, geometry1, strategy); + check_equal + < + default_comparable_distance_result + >::apply(case_id, "rc", geometry2, geometry1, + cdist, expected_comparable_distance); +*/ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "distance[reversed args] = " + << dist << " ; " + << "comp. distance[reversed args] = " + //<< cdist + << std::endl; + std::cout << std::endl; +#endif + } + + } +}; + + +//======================================================================== + + +template +void test_empty_input(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Strategy const& strategy) +{ + try + { + bg::distance(geometry1, geometry2); + } + catch(bg::empty_input_exception const& ) + { + return; + } + BOOST_CHECK_MESSAGE(false, + "A empty_input_exception should have been thrown"); + + try + { + bg::distance(geometry2, geometry1); + } + catch(bg::empty_input_exception const& ) + { + return; + } + BOOST_CHECK_MESSAGE(false, + "A empty_input_exception should have been thrown"); + + try + { + bg::distance(geometry1, geometry2, strategy); + } + catch(bg::empty_input_exception const& ) + { + return; + } + BOOST_CHECK_MESSAGE(false, + "A empty_input_exception should have been thrown"); + + try + { + bg::distance(geometry2, geometry1, strategy); + } + catch(bg::empty_input_exception const& ) + { + return; + } + BOOST_CHECK_MESSAGE(false, + "A empty_input_exception should have been thrown"); +} + +#endif // BOOST_GEOMETRY_TEST_DISTANCE_GEO_COMMON_HPP From 36232a3cdb4e040baf15fbe10ed44054852312ee Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Thu, 13 Jul 2017 12:52:52 +0300 Subject: [PATCH 029/188] [distance] Spheroid passed as parameter, more general strategy getter for distance strategy --- .../formulas/distance_point_segment.hpp | 13 ++- .../geographic/distance_cross_track.hpp | 79 ++++++------------- .../strategies/geographic/intersection.hpp | 6 +- .../algorithms/distance/distance_geo_pl_l.cpp | 6 +- .../distance/test_distance_geo_common.hpp | 1 - 5 files changed, 35 insertions(+), 70 deletions(-) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index 6363aac8b..a6ce5c77f 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -25,26 +25,25 @@ template typename Inverse_type_distance, typename Direct_type > -class cross_track_geo_formula{ +class distance_point_segment{ public: + template CT static inline apply(CT lon1, CT lat1, //p1 CT lon2, CT lat2, //p2 - CT lon3, CT lat3) //query point p3 + CT lon3, CT lat3, //query point p3 + Spheroid const& spheroid, + CT earth_radius = + geometry::srs::sphere().get_radius<1>()) { int print = 0; // Constants - geometry::srs::spheroid spheroid; CT const f = flattening(spheroid); CT const pi = math::pi(); CT const half_pi = math::pi() / CT(2); - geometry::srs::sphere sphere; - CT const earth_radius = sphere.get_radius<1>(); - //CT const earth_radius = 6372795; - //CT const earth_radius = 6378137; // Convert to radians lon1 = math::as_radian(lon1); diff --git a/include/boost/geometry/strategies/geographic/distance_cross_track.hpp b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp index b339f8f45..64009ee6a 100644 --- a/include/boost/geometry/strategies/geographic/distance_cross_track.hpp +++ b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp @@ -49,18 +49,15 @@ namespace boost { namespace geometry namespace strategy { namespace distance { - /*! -\brief Strategy functor for distance point to segment calculation +\brief Strategy functor for distance point to segment calculation on ellipsoid \ingroup strategies -\details Class which calculates the distance of a point to a segment, for points on a sphere or globe -\see http://williams.best.vwh.net/avform.htm +\details Class which calculates the distance of a point to a segment, for points +on the ellipsoid +\see https://arxiv.org/abs/1102.1215 +\tparam FormulaPolicy underlying point-point distance strategy +\tparam Spheroid is the spheroidal model used \tparam CalculationType \tparam_calculation -\tparam Strategy underlying point-point distance strategy -\qbk{ -[heading See also] -[link geometry.reference.algorithms.distance.distance_3_with_strategy distance (with strategy)] -} */ template < @@ -84,21 +81,21 @@ public : > {}; - inline cross_track_geo() + struct distance_strategy + { + typedef geographic type; + }; + + inline typename distance_strategy::type get_distance_strategy() const + { + typedef typename distance_strategy::type distance_type; + return distance_type(m_spheroid); + } + + explicit cross_track_geo(Spheroid const& spheroid = Spheroid()) + : m_spheroid(spheroid) {} - typedef geographic - < - FormulaPolicy, Spheroid, CalculationType - > DistanceStrategy; - - /* - template - inline T comparable_to_distance(T& a) const - { - return a; - } -*/ template inline typename return_type::type apply(Point const& p, PointOfSegment const& sp1, PointOfSegment const& sp2) const @@ -136,7 +133,7 @@ public : false > direct_type; - return geometry::formula::cross_track_geo_formula + return geometry::formula::distance_point_segment < CT, units_type, @@ -145,14 +142,12 @@ public : direct_type >::apply(get<0>(sp1), get<1>(sp1), get<0>(sp2), get<1>(sp2), - get<0>(p), get<1>(p)); + get<0>(p), get<1>(p), + m_spheroid); } - //TODO: apply a more general strategy getter - inline DistanceStrategy get_distance_strategy() const - { - return DistanceStrategy(); - } +private : + Spheroid m_spheroid; }; @@ -278,32 +273,6 @@ public : } }; -/* -template -< - typename FormulaPolicy, - typename P, - typename PS -> -struct result_from_comparable - < - cross_track_geo, P, PS - > -{ -private : - typedef cross_track_geo strategy_type; -// typedef typename return_type::type return_type; -public : - template - static inline T apply(strategy_type const& strategy, - T const& comparable_distance) - { - //T c = T(2.0) * asin(math::sqrt(a)); - //return c * strategy.radius(); - return comparable_distance; - } -}; -*/ template struct default_strategy diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp index 84acd149c..f4c2c1436 100644 --- a/include/boost/geometry/strategies/geographic/intersection.hpp +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -118,7 +118,6 @@ struct geographic_segments return strategy_type(m_spheroid); } - template struct distance_strategy { typedef distance::geographic @@ -129,10 +128,9 @@ struct geographic_segments > type; }; - template - inline typename distance_strategy::type get_distance_strategy() const + inline typename distance_strategy::type get_distance_strategy() const { - typedef typename distance_strategy::type strategy_type; + typedef typename distance_strategy::type strategy_type; return strategy_type(m_spheroid); } diff --git a/test/algorithms/distance/distance_geo_pl_l.cpp b/test/algorithms/distance/distance_geo_pl_l.cpp index f30665de8..83858f64d 100644 --- a/test/algorithms/distance/distance_geo_pl_l.cpp +++ b/test/algorithms/distance/distance_geo_pl_l.cpp @@ -44,13 +44,13 @@ typedef bg::strategy::distance::vincenty vincenty_pp; // Strategies for point-segment distance -typedef bg::strategy::distance::cross_track_geo +typedef bg::strategy::distance::cross_track_geo andoyer_strategy; -typedef bg::strategy::distance::cross_track_geo +typedef bg::strategy::distance::cross_track_geo thomas_strategy; -typedef bg::strategy::distance::cross_track_geo +typedef bg::strategy::distance::cross_track_geo vincenty_strategy; //=========================================================================== diff --git a/test/algorithms/distance/test_distance_geo_common.hpp b/test/algorithms/distance/test_distance_geo_common.hpp index 5a820dc90..1f831d447 100644 --- a/test/algorithms/distance/test_distance_geo_common.hpp +++ b/test/algorithms/distance/test_distance_geo_common.hpp @@ -1,4 +1,3 @@ - // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test From 7dcba58943b28f6e1ac852c17828d254da2bcec1 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 14 Jul 2017 16:13:38 +0200 Subject: [PATCH 030/188] [algorithms] Use user-defined side strategy in various algorithms ... instead of using the default one created internally. It affects buffer, is_simple, is_valid, is_convex, point_on_surface, remove spikes and various overlay algorithms. --- .../detail/buffer/buffer_inserter.hpp | 221 ++++++++++-------- .../buffer/buffered_piece_collection.hpp | 2 +- .../algorithms/detail/extreme_points.hpp | 93 +++++--- .../algorithms/detail/get_left_turns.hpp | 40 ++-- .../algorithms/detail/is_simple/linear.hpp | 44 +++- .../algorithms/detail/is_valid/has_spikes.hpp | 15 +- .../algorithms/detail/is_valid/linear.hpp | 36 +-- .../algorithms/detail/is_valid/ring.hpp | 2 +- .../algorithms/detail/occupation_info.hpp | 19 +- .../overlay/append_no_dups_or_spikes.hpp | 13 +- .../detail/overlay/copy_segments.hpp | 33 ++- .../overlay/enrich_intersection_points.hpp | 21 +- .../algorithms/detail/overlay/follow.hpp | 20 +- .../detail/overlay/follow_linear_linear.hpp | 41 ++-- .../detail/overlay/get_relative_order.hpp | 32 +-- .../detail/overlay/handle_colocations.hpp | 15 +- .../detail/overlay/less_by_segment_ratio.hpp | 24 +- .../detail/overlay/linear_linear.hpp | 10 +- .../detail/overlay/sort_by_side.hpp | 32 +-- .../algorithms/detail/overlay/traversal.hpp | 17 +- .../detail/overlay/traversal_ring_creator.hpp | 22 +- .../detail/point_is_spike_or_equal.hpp | 100 ++++++-- .../boost/geometry/algorithms/is_convex.hpp | 92 ++++++-- .../geometry/algorithms/point_on_surface.hpp | 56 ++++- .../geometry/algorithms/remove_spikes.hpp | 112 ++++++--- 25 files changed, 743 insertions(+), 369 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index 029053dda..a149f1dd4 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -122,7 +122,8 @@ struct buffer_range typename DistanceStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void add_join(Collection& collection, @@ -133,18 +134,19 @@ struct buffer_range Point const& input, output_point_type const& perp1, output_point_type const& perp2, - strategy::buffer::buffer_side_selector side, + geometry::strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, - RobustPolicy const& ) + RobustPolicy const& , + Strategy const& strategy) // side strategy { output_point_type intersection_point; geometry::assign_zero(intersection_point); - strategy::buffer::join_selector join - = get_join_type(penultimate_input, previous_input, input); - if (join == strategy::buffer::join_convex) + geometry::strategy::buffer::join_selector join + = get_join_type(penultimate_input, previous_input, input, strategy); + if (join == geometry::strategy::buffer::join_convex) { // Calculate the intersection-point formed by the two sides. // It might be that the two sides are not convex, but continue @@ -157,23 +159,23 @@ struct buffer_range switch(join) { - case strategy::buffer::join_continue : + case geometry::strategy::buffer::join_continue : // No join, we get two consecutive sides break; - case strategy::buffer::join_concave : + case geometry::strategy::buffer::join_concave : { std::vector range_out; range_out.push_back(prev_perp2); range_out.push_back(previous_input); - collection.add_piece(strategy::buffer::buffered_concave, previous_input, range_out); + collection.add_piece(geometry::strategy::buffer::buffered_concave, previous_input, range_out); range_out.clear(); range_out.push_back(previous_input); range_out.push_back(perp1); - collection.add_piece(strategy::buffer::buffered_concave, previous_input, range_out); + collection.add_piece(geometry::strategy::buffer::buffered_concave, previous_input, range_out); } break; - case strategy::buffer::join_spike : + case geometry::strategy::buffer::join_spike : { // For linestrings, only add spike at one side to avoid // duplicates @@ -183,7 +185,7 @@ struct buffer_range collection.set_current_ring_concave(); } break; - case strategy::buffer::join_convex : + case geometry::strategy::buffer::join_convex : { // The corner is convex, we create a join // TODO (future) - avoid a separate vector, add the piece directly @@ -193,7 +195,7 @@ struct buffer_range distance.apply(previous_input, input, side), range_out)) { - collection.add_piece(strategy::buffer::buffered_join, + collection.add_piece(geometry::strategy::buffer::buffered_join, previous_input, range_out); } } @@ -201,27 +203,24 @@ struct buffer_range } } - static inline strategy::buffer::join_selector get_join_type( + template + static inline geometry::strategy::buffer::join_selector get_join_type( output_point_type const& p0, output_point_type const& p1, - output_point_type const& p2) + output_point_type const& p2, + Strategy const& strategy) // side strategy { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy; - - int const side = side_strategy::apply(p0, p1, p2); - return side == -1 ? strategy::buffer::join_convex - : side == 1 ? strategy::buffer::join_concave + int const side = strategy.apply(p0, p1, p2); + return side == -1 ? geometry::strategy::buffer::join_convex + : side == 1 ? geometry::strategy::buffer::join_concave : parallel_continue ( get<0>(p2) - get<0>(p1), get<1>(p2) - get<1>(p1), get<0>(p1) - get<0>(p0), get<1>(p1) - get<1>(p0) - ) ? strategy::buffer::join_continue - : strategy::buffer::join_spike; + ) ? geometry::strategy::buffer::join_continue + : geometry::strategy::buffer::join_spike; } template @@ -232,16 +231,18 @@ struct buffer_range typename SideStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code iterate(Collection& collection, + static inline geometry::strategy::buffer::result_code iterate(Collection& collection, Iterator begin, Iterator end, - strategy::buffer::buffer_side_selector side, + geometry::strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, RobustPolicy const& robust_policy, + Strategy const& strategy, // side strategy output_point_type& first_p1, output_point_type& first_p2, output_point_type& last_p1, @@ -273,7 +274,7 @@ struct buffer_range * pup: penultimate_point */ - strategy::buffer::result_code result = strategy::buffer::result_no_output; + geometry::strategy::buffer::result_code result = geometry::strategy::buffer::result_no_output; bool first = true; Iterator it = begin; @@ -284,25 +285,25 @@ struct buffer_range for (Iterator prev = it++; it != end; ++it) { generated_side.clear(); - strategy::buffer::result_code error_code + geometry::strategy::buffer::result_code error_code = side_strategy.apply(*prev, *it, side, distance_strategy, generated_side); - if (error_code == strategy::buffer::result_no_output) + if (error_code == geometry::strategy::buffer::result_no_output) { // Because input is simplified, this is improbable, // but it can happen for degenerate geometries // Further handling of this side is skipped continue; } - else if (error_code == strategy::buffer::result_error_numerical) + else if (error_code == geometry::strategy::buffer::result_error_numerical) { return error_code; } BOOST_GEOMETRY_ASSERT(! generated_side.empty()); - result = strategy::buffer::result_normal; + result = geometry::strategy::buffer::result_normal; if (! first) { @@ -312,7 +313,7 @@ struct buffer_range *it, generated_side.front(), generated_side.back(), side, distance_strategy, join_strategy, end_strategy, - robust_policy); + robust_policy, strategy); } collection.add_side_piece(*prev, *it, generated_side, first); @@ -350,7 +351,8 @@ struct buffer_multi typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply(Multi const& multi, Collection& collection, @@ -359,7 +361,8 @@ struct buffer_multi JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { for (typename boost::range_iterator::type it = boost::begin(multi); @@ -369,7 +372,7 @@ struct buffer_multi Policy::apply(*it, collection, distance_strategy, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); } } }; @@ -396,9 +399,9 @@ inline void buffer_point(Point const& point, Collection& collection, collection.start_new_ring(); std::vector range_out; point_strategy.apply(point, distance_strategy, range_out); - collection.add_piece(strategy::buffer::buffered_point, range_out, false); + collection.add_piece(geometry::strategy::buffer::buffered_point, range_out, false); collection.set_piece_center(point); - collection.finish_ring(strategy::buffer::result_normal); + collection.finish_ring(geometry::strategy::buffer::result_normal); } @@ -436,7 +439,8 @@ struct buffer_inserter typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply(Point const& point, Collection& collection, DistanceStrategy const& distance_strategy, @@ -444,7 +448,8 @@ struct buffer_inserter JoinStrategy const& , EndStrategy const& , PointStrategy const& point_strategy, - RobustPolicy const& ) + RobustPolicy const& , + Strategy const& ) // side strategy { detail::buffer::buffer_point < @@ -472,29 +477,32 @@ struct buffer_inserter_ring typename SideStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code iterate(Collection& collection, + static inline geometry::strategy::buffer::result_code iterate(Collection& collection, Iterator begin, Iterator end, - strategy::buffer::buffer_side_selector side, + geometry::strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { output_point_type first_p1, first_p2, last_p1, last_p2; typedef detail::buffer::buffer_range buffer_range; - strategy::buffer::result_code result + geometry::strategy::buffer::result_code result = buffer_range::iterate(collection, begin, end, side, - distance_strategy, side_strategy, join_strategy, end_strategy, robust_policy, + distance_strategy, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1, first_p2, last_p1, last_p2); // Generate closing join - if (result == strategy::buffer::result_normal) + if (result == geometry::strategy::buffer::result_normal) { buffer_range::add_join(collection, *(end - 2), @@ -502,7 +510,7 @@ struct buffer_inserter_ring *(begin + 1), first_p1, first_p2, side, distance_strategy, join_strategy, end_strategy, - robust_policy); + robust_policy, strategy); } // Buffer is closed automatically by last closing corner @@ -517,21 +525,23 @@ struct buffer_inserter_ring typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code apply(RingInput const& ring, + static inline geometry::strategy::buffer::result_code apply(RingInput const& ring, Collection& collection, DistanceStrategy const& distance, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { RingInput simplified; detail::buffer::simplify_input(ring, distance, simplified); - strategy::buffer::result_code code = strategy::buffer::result_no_output; + geometry::strategy::buffer::result_code code = geometry::strategy::buffer::result_no_output; std::size_t n = boost::size(simplified); std::size_t const min_points = core_detail::closure::minimum_ring_size @@ -546,18 +556,20 @@ struct buffer_inserter_ring { // Walk backwards (rings will be reversed afterwards) code = iterate(collection, boost::rbegin(view), boost::rend(view), - strategy::buffer::buffer_side_right, - distance, side_strategy, join_strategy, end_strategy, robust_policy); + geometry::strategy::buffer::buffer_side_right, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy); } else { code = iterate(collection, boost::begin(view), boost::end(view), - strategy::buffer::buffer_side_left, - distance, side_strategy, join_strategy, end_strategy, robust_policy); + geometry::strategy::buffer::buffer_side_left, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy); } } - if (code == strategy::buffer::result_no_output && n >= 1) + if (code == geometry::strategy::buffer::result_no_output && n >= 1) { // Use point_strategy to buffer degenerated ring detail::buffer::buffer_point @@ -586,23 +598,25 @@ struct buffer_inserter typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code apply(RingInput const& ring, + static inline geometry::strategy::buffer::result_code apply(RingInput const& ring, Collection& collection, DistanceStrategy const& distance, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { collection.start_new_ring(); - strategy::buffer::result_code const code + geometry::strategy::buffer::result_code const code = buffer_inserter_ring::apply(ring, collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); collection.finish_ring(code); return code; } @@ -627,16 +641,18 @@ struct buffer_inserter typename SideStrategy, typename JoinStrategy, typename EndStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code iterate(Collection& collection, + static inline geometry::strategy::buffer::result_code iterate(Collection& collection, Iterator begin, Iterator end, - strategy::buffer::buffer_side_selector side, + geometry::strategy::buffer::buffer_side_selector side, DistanceStrategy const& distance_strategy, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, RobustPolicy const& robust_policy, + Strategy const& strategy, // side strategy output_point_type& first_p1) { input_point_type const& ultimate_point = *(end - 1); @@ -647,18 +663,18 @@ struct buffer_inserter // we have it already from the first phase (left). // But for the first pass, we have to generate it output_point_type reverse_p1; - if (side == strategy::buffer::buffer_side_right) + if (side == geometry::strategy::buffer::buffer_side_right) { reverse_p1 = first_p1; } else { std::vector generated_side; - strategy::buffer::result_code code + geometry::strategy::buffer::result_code code = side_strategy.apply(ultimate_point, penultimate_point, - strategy::buffer::buffer_side_right, + geometry::strategy::buffer::buffer_side_right, distance_strategy, generated_side); - if (code != strategy::buffer::result_normal) + if (code != geometry::strategy::buffer::result_normal) { // No output or numerical error return code; @@ -668,16 +684,18 @@ struct buffer_inserter output_point_type first_p2, last_p1, last_p2; - strategy::buffer::result_code result + geometry::strategy::buffer::result_code result = detail::buffer::buffer_range::iterate(collection, begin, end, side, - distance_strategy, side_strategy, join_strategy, end_strategy, robust_policy, + distance_strategy, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1, first_p2, last_p1, last_p2); - if (result == strategy::buffer::result_normal) + if (result == geometry::strategy::buffer::result_normal) { std::vector range_out; - end_strategy.apply(penultimate_point, last_p2, ultimate_point, reverse_p1, side, distance_strategy, range_out); + end_strategy.apply(penultimate_point, last_p2, ultimate_point, reverse_p1, + side, distance_strategy, range_out); collection.add_endcap(end_strategy, range_out, ultimate_point); } return result; @@ -691,20 +709,23 @@ struct buffer_inserter typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > - static inline strategy::buffer::result_code apply(Linestring const& linestring, Collection& collection, + static inline geometry::strategy::buffer::result_code apply(Linestring const& linestring, + Collection& collection, DistanceStrategy const& distance, SideStrategy const& side_strategy, JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { Linestring simplified; detail::buffer::simplify_input(linestring, distance, simplified); - strategy::buffer::result_code code = strategy::buffer::result_no_output; + geometry::strategy::buffer::result_code code = geometry::strategy::buffer::result_no_output; std::size_t n = boost::size(simplified); if (n > 1) { @@ -712,21 +733,23 @@ struct buffer_inserter output_point_type first_p1; code = iterate(collection, boost::begin(simplified), boost::end(simplified), - strategy::buffer::buffer_side_left, - distance, side_strategy, join_strategy, end_strategy, robust_policy, + geometry::strategy::buffer::buffer_side_left, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1); - if (code == strategy::buffer::result_normal) + if (code == geometry::strategy::buffer::result_normal) { code = iterate(collection, boost::rbegin(simplified), boost::rend(simplified), - strategy::buffer::buffer_side_right, - distance, side_strategy, join_strategy, end_strategy, robust_policy, + geometry::strategy::buffer::buffer_side_right, + distance, side_strategy, join_strategy, end_strategy, + robust_policy, strategy, first_p1); } collection.finish_ring(code); } - if (code == strategy::buffer::result_no_output && n >= 1) + if (code == geometry::strategy::buffer::result_no_output && n >= 1) { // Use point_strategy to buffer degenerated linestring detail::buffer::buffer_point @@ -763,7 +786,8 @@ private: typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void iterate(Iterator begin, Iterator end, @@ -774,15 +798,16 @@ private: EndStrategy const& end_strategy, PointStrategy const& point_strategy, RobustPolicy const& robust_policy, + Strategy const& strategy, // side strategy bool is_interior) { for (Iterator it = begin; it != end; ++it) { collection.start_new_ring(); - strategy::buffer::result_code const code + geometry::strategy::buffer::result_code const code = policy::apply(*it, collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); collection.finish_ring(code, is_interior); } @@ -797,7 +822,8 @@ private: typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply_interior_rings(InteriorRings const& interior_rings, @@ -807,12 +833,13 @@ private: JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { iterate(boost::begin(interior_rings), boost::end(interior_rings), collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy, true); + robust_policy, strategy, true); } public: @@ -824,7 +851,8 @@ public: typename JoinStrategy, typename EndStrategy, typename PointStrategy, - typename RobustPolicy + typename RobustPolicy, + typename Strategy > static inline void apply(PolygonInput const& polygon, Collection& collection, @@ -833,16 +861,17 @@ public: JoinStrategy const& join_strategy, EndStrategy const& end_strategy, PointStrategy const& point_strategy, - RobustPolicy const& robust_policy) + RobustPolicy const& robust_policy, + Strategy const& strategy) // side strategy { { collection.start_new_ring(); - strategy::buffer::result_code const code + geometry::strategy::buffer::result_code const code = policy::apply(exterior_ring(polygon), collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); collection.finish_ring(code, false, geometry::num_interior_rings(polygon) > 0u); @@ -851,7 +880,7 @@ public: apply_interior_rings(interior_rings(polygon), collection, distance, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, strategy); } }; @@ -945,7 +974,7 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator >::apply(geometry_input, collection, distance_strategy, side_strategy, join_strategy, end_strategy, point_strategy, - robust_policy); + robust_policy, intersection_strategy.get_side_strategy()); collection.get_turns(); collection.classify_turns(linear); diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index 4bb7a281f..c0d906fe6 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -494,7 +494,7 @@ struct buffered_piece_collection for (typename occupation_map_type::iterator it = occupation_map.begin(); it != occupation_map.end(); ++it) { - it->second.get_left_turns(it->first, m_turns); + it->second.get_left_turns(it->first, m_turns, m_side_strategy); } } diff --git a/include/boost/geometry/algorithms/detail/extreme_points.hpp b/include/boost/geometry/algorithms/detail/extreme_points.hpp index 65795cd05..61e984ee3 100644 --- a/include/boost/geometry/algorithms/detail/extreme_points.hpp +++ b/include/boost/geometry/algorithms/detail/extreme_points.hpp @@ -5,6 +5,11 @@ // Copyright (c) 2009-2013 Mateusz Loskot, London, UK. // Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -117,12 +122,6 @@ struct extreme_points_on_ring typedef typename boost::range_iterator::type range_iterator; typedef typename geometry::point_type::type point_type; - typedef typename geometry::strategy::side::services::default_strategy - < - typename geometry::cs_tag::type - >::type side_strategy; - - template static inline bool extend(CirclingIterator& it, std::size_t n, @@ -214,10 +213,11 @@ struct extreme_points_on_ring return true; } - template + template static inline void get_intruders(Ring const& ring, CirclingIterator left, CirclingIterator right, Extremes const& extremes, - Intruders& intruders) + Intruders& intruders, + SideStrategy const& strategy) { if (boost::size(extremes) < 3) { @@ -238,8 +238,8 @@ struct extreme_points_on_ring if (coordinate > min_value && other_coordinate > other_min && other_coordinate < other_max) { int const factor = geometry::point_order::value == geometry::clockwise ? 1 : -1; - int const first_side = side_strategy::apply(*right, extremes.front(), *(extremes.begin() + 1)) * factor; - int const last_side = side_strategy::apply(*right, *(extremes.rbegin() + 1), extremes.back()) * factor; + int const first_side = strategy.apply(*right, extremes.front(), *(extremes.begin() + 1)) * factor; + int const last_side = strategy.apply(*right, *(extremes.rbegin() + 1), extremes.back()) * factor; // If not lying left from any of the extemes side if (first_side != 1 && last_side != 1) @@ -263,10 +263,11 @@ struct extreme_points_on_ring } } - template + template static inline void get_intruders(Ring const& ring, Extremes const& extremes, - Intruders& intruders) + Intruders& intruders, + SideStrategy const& strategy) { std::size_t const n = boost::size(ring); if (n >= 3) @@ -275,12 +276,12 @@ struct extreme_points_on_ring geometry::ever_circling_range_iterator right(ring); ++right; - get_intruders(ring, left, right, extremes, intruders); + get_intruders(ring, left, right, extremes, intruders, strategy); } } - template - static inline bool right_turn(Ring const& ring, Iterator it) + template + static inline bool right_turn(Ring const& ring, Iterator it, SideStrategy const& strategy) { typename std::iterator_traits::difference_type const index = std::distance(boost::begin(ring), it); @@ -295,8 +296,8 @@ struct extreme_points_on_ring } int const factor = geometry::point_order::value == geometry::clockwise ? 1 : -1; - int const first_side = side_strategy::apply(*(right - 1), *right, *left) * factor; - int const last_side = side_strategy::apply(*left, *(left + 1), *right) * factor; + int const first_side = strategy.apply(*(right - 1), *right, *left) * factor; + int const last_side = strategy.apply(*left, *(left + 1), *right) * factor; //std::cout << "Candidate at " << geometry::wkt(*it) << " first=" << first_side << " last=" << last_side << std::endl; @@ -306,8 +307,11 @@ struct extreme_points_on_ring // Gets the extreme segments (top point plus neighbouring points), plus intruders, if any, on the same ring - template - static inline bool apply(Ring const& ring, Extremes& extremes, Intruders& intruders) + template + static inline bool apply(Ring const& ring, + Extremes& extremes, + Intruders& intruders, + SideStrategy const& strategy) { std::size_t const n = boost::size(ring); if (n < 3) @@ -321,7 +325,7 @@ struct extreme_points_on_ring compare smaller; for (range_iterator it = max_it + 1; it != boost::end(ring); ++it) { - if (smaller(*max_it, *it) && right_turn(ring, it)) + if (smaller(*max_it, *it) && right_turn(ring, it, strategy)) { max_it = it; } @@ -365,7 +369,7 @@ struct extreme_points_on_ring std::copy(points.begin(), points.end(), std::back_inserter(extremes)); - get_intruders(ring, left, right, extremes, intruders); + get_intruders(ring, left, right, extremes, intruders, strategy); return true; } @@ -403,8 +407,9 @@ struct extreme_points template struct extreme_points { - template - static inline bool apply(Polygon const& polygon, Extremes& extremes, Intruders& intruders) + template + static inline bool apply(Polygon const& polygon, Extremes& extremes, Intruders& intruders, + SideStrategy const& strategy) { typedef typename geometry::ring_type::type ring_type; typedef detail::extreme_points::extreme_points_on_ring @@ -412,7 +417,8 @@ struct extreme_points ring_type, Dimension > ring_implementation; - if (! ring_implementation::apply(geometry::exterior_ring(polygon), extremes, intruders)) + if (! ring_implementation::apply(geometry::exterior_ring(polygon), + extremes, intruders, strategy)) { return false; } @@ -423,7 +429,7 @@ struct extreme_points for (typename detail::interior_iterator::type it = boost::begin(rings); it != boost::end(rings); ++it) { - ring_implementation::get_intruders(*it, extremes, intruders); + ring_implementation::get_intruders(*it, extremes, intruders, strategy); } return true; @@ -433,8 +439,9 @@ struct extreme_points template struct extreme_points { - template - static inline bool apply(Box const& box, Extremes& extremes, Intruders& ) + template + static inline bool apply(Box const& box, Extremes& extremes, Intruders& , + SideStrategy const& ) { extremes.resize(4); geometry::detail::assign_box_corners_oriented(box, extremes); @@ -446,8 +453,9 @@ struct extreme_points template struct extreme_points { - template - static inline bool apply(Box const& box, Extremes& extremes, Intruders& ) + template + static inline bool apply(Box const& box, Extremes& extremes, Intruders& , + SideStrategy const& ) { extremes.resize(4); geometry::detail::assign_box_corners_oriented(box, extremes); @@ -460,8 +468,9 @@ struct extreme_points template struct extreme_points { - template - static inline bool apply(MultiPolygon const& multi, Extremes& extremes, Intruders& intruders) + template + static inline bool apply(MultiPolygon const& multi, Extremes& extremes, + Intruders& intruders, SideStrategy const& strategy) { // Get one for the very first polygon, that is (for the moment) enough. // It is not guaranteed the "extreme" then, but for the current purpose @@ -473,7 +482,7 @@ struct extreme_points typename boost::range_value::type, Dimension, polygon_tag - >::apply(*boost::begin(multi), extremes, intruders); + >::apply(*boost::begin(multi), extremes, intruders, strategy); } return false; @@ -489,8 +498,18 @@ struct extreme_points for Edge=0 in dimension 0, the right side) \note We could specify a strategy (less/greater) to get bottom/left side too. However, until now we don't need that. */ -template -inline bool extreme_points(Geometry const& geometry, Extremes& extremes, Intruders& intruders) +template +< + std::size_t Edge, + typename Geometry, + typename Extremes, + typename Intruders, + typename SideStrategy +> +inline bool extreme_points(Geometry const& geometry, + Extremes& extremes, + Intruders& intruders, + SideStrategy const& strategy) { concepts::check(); @@ -509,7 +528,11 @@ inline bool extreme_points(Geometry const& geometry, Extremes& extremes, Intrude const >(); - return dispatch::extreme_points::apply(geometry, extremes, intruders); + return dispatch::extreme_points + < + Geometry, + Edge + >::apply(geometry, extremes, intruders, strategy); } diff --git a/include/boost/geometry/algorithms/detail/get_left_turns.hpp b/include/boost/geometry/algorithms/detail/get_left_turns.hpp index 95ab98c23..e9f6a5085 100644 --- a/include/boost/geometry/algorithms/detail/get_left_turns.hpp +++ b/include/boost/geometry/algorithms/detail/get_left_turns.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -60,17 +65,14 @@ inline int squared_length(Vector const& vector) } -template +template struct angle_less { typedef Point vector_type; - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy_type; - angle_less(Point const& origin) + angle_less(Point const& origin, SideStrategy const& strategy) : m_origin(origin) + , m_strategy(strategy) {} template @@ -89,8 +91,7 @@ struct angle_less return quadrant_p < quadrant_q; } // Same quadrant, check if p is located left of q - int const side = side_strategy_type::apply(m_origin, q.point, - p.point); + int const side = m_strategy.apply(m_origin, q.point, p.point); if (side != 0) { return side == 1; @@ -114,19 +115,17 @@ struct angle_less private: Point m_origin; + SideStrategy m_strategy; }; -template +template struct angle_equal_to { typedef Point vector_type; - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy_type; - - inline angle_equal_to(Point const& origin) + + inline angle_equal_to(Point const& origin, SideStrategy const& strategy) : m_origin(origin) + , m_strategy(strategy) {} template @@ -143,13 +142,13 @@ struct angle_equal_to return false; } // Same quadrant, check if p/q are collinear - int const side = side_strategy_type::apply(m_origin, q.point, - p.point); + int const side = m_strategy.apply(m_origin, q.point, p.point); return side == 0; } private: Point m_origin; + SideStrategy m_strategy; }; template @@ -193,13 +192,14 @@ inline void get_left_turns(AngleCollection const& sorted_angles, //! Returns the number of clusters -template -inline std::size_t assign_cluster_indices(AngleCollection& sorted, Point const& origin) +template +inline std::size_t assign_cluster_indices(AngleCollection& sorted, Point const& origin, + SideStrategy const& strategy) { // Assign same cluster_index for all turns in same direction BOOST_GEOMETRY_ASSERT(boost::size(sorted) >= 4u); - angle_equal_to comparator(origin); + angle_equal_to comparator(origin, strategy); typename boost::range_iterator::type it = sorted.begin(); std::size_t cluster_index = 0; diff --git a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp index 5852ed071..5acf56c5b 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp @@ -236,7 +236,9 @@ inline bool has_self_intersections(Linear const& linear, Strategy const& strateg template struct is_simple_linestring { - static inline bool apply(Linestring const& linestring) + template + static inline bool apply(Linestring const& linestring, + Strategy const& strategy) { simplicity_failure_policy policy; return ! boost::empty(linestring) @@ -247,7 +249,7 @@ struct is_simple_linestring && ! detail::is_valid::has_spikes < Linestring, closed - >::apply(linestring, policy); + >::apply(linestring, policy, strategy.get_side_strategy()); } }; @@ -258,7 +260,10 @@ struct is_simple_linestring static inline bool apply(Linestring const& linestring, Strategy const& strategy) { - return is_simple_linestring::apply(linestring) + return is_simple_linestring + < + Linestring, false + >::apply(linestring, strategy) && ! has_self_intersections(linestring, strategy); } }; @@ -267,23 +272,44 @@ struct is_simple_linestring template struct is_simple_multilinestring { +private: + template + struct per_linestring + { + per_linestring(Strategy const& strategy) + : m_strategy(strategy) + {} + + template + inline bool apply(Linestring const& linestring) const + { + return detail::is_simple::is_simple_linestring + < + Linestring, + false // do not compute self-intersections + >::apply(linestring, m_strategy); + } + + Strategy const& m_strategy; + }; + +public: template static inline bool apply(MultiLinestring const& multilinestring, Strategy const& strategy) { + typedef per_linestring per_ls; + // check each of the linestrings for simplicity // but do not compute self-intersections yet; these will be // computed for the entire multilinestring if ( ! detail::check_iterator_range < - is_simple_linestring - < - typename boost::range_value::type, - false // do not compute self-intersections - >, + per_ls, // do not compute self-intersections true // allow empty multilinestring >::apply(boost::begin(multilinestring), - boost::end(multilinestring)) + boost::end(multilinestring), + per_ls(strategy)) ) { return false; diff --git a/include/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp b/include/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp index aa90e52db..96efec79c 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -91,8 +92,9 @@ struct has_spikes return std::find_if(second, last, not_equal(*first)); } - template - static inline bool apply(Range const& range, VisitPolicy& visitor) + template + static inline bool apply(Range const& range, VisitPolicy& visitor, + SideStrategy const& strategy) { boost::ignore_unused(visitor); @@ -124,9 +126,8 @@ struct has_spikes while (next != boost::end(view)) { - if ( geometry::detail::point_is_spike_or_equal(*prev, - *next, - *cur) ) + if ( geometry::detail::point_is_spike_or_equal(*prev, *next, *cur, + strategy) ) { return ! visitor.template apply(is_linear, *cur); @@ -146,7 +147,7 @@ struct has_spikes boost::rend(view)); iterator next = find_different_from_first(cur, boost::end(view)); - if (detail::point_is_spike_or_equal(*prev, *next, *cur)) + if (detail::point_is_spike_or_equal(*prev, *next, *cur, strategy)) { return ! visitor.template apply(is_linear, *cur); diff --git a/include/boost/geometry/algorithms/detail/is_valid/linear.hpp b/include/boost/geometry/algorithms/detail/is_valid/linear.hpp index 6bc6b86cf..39cb36ef5 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/linear.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/linear.hpp @@ -43,9 +43,10 @@ namespace detail { namespace is_valid template struct is_valid_linestring { - template + template static inline bool apply(Linestring const& linestring, - VisitPolicy& visitor) + VisitPolicy& visitor, + Strategy const& strategy) { if (has_invalid_coordinate::apply(linestring, visitor)) { @@ -75,15 +76,12 @@ struct is_valid_linestring { return visitor.template apply(); } - return ! has_spikes::apply(linestring, visitor); - } - template - static inline bool apply(Linestring const& linestring, - VisitPolicy& visitor, - Strategy const&) - { - return apply(linestring, visitor); + return ! has_spikes + < + Linestring, closed + >::apply(linestring, visitor, + strategy.get_side_strategy()); } }; @@ -132,10 +130,13 @@ class is_valid > { private: - template + template struct per_linestring { - per_linestring(VisitPolicy& policy) : m_policy(policy) {} + per_linestring(VisitPolicy& policy, Strategy const& strategy) + : m_policy(policy) + , m_strategy(strategy) + {} template inline bool apply(Linestring const& linestring) const @@ -143,17 +144,18 @@ private: return detail::is_valid::is_valid_linestring < Linestring - >::apply(linestring, m_policy); + >::apply(linestring, m_policy, m_strategy); } VisitPolicy& m_policy; + Strategy const& m_strategy; }; public: template static inline bool apply(MultiLinestring const& multilinestring, VisitPolicy& visitor, - Strategy const&) + Strategy const& strategy) { if (BOOST_GEOMETRY_CONDITION( AllowEmptyMultiGeometries && boost::empty(multilinestring))) @@ -161,13 +163,15 @@ public: return visitor.template apply(); } + typedef per_linestring per_ls; + return detail::check_iterator_range < - per_linestring, + per_ls, false // do not check for empty multilinestring (done above) >::apply(boost::begin(multilinestring), boost::end(multilinestring), - per_linestring(visitor)); + per_ls(visitor, strategy)); } }; diff --git a/include/boost/geometry/algorithms/detail/is_valid/ring.hpp b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp index 996da7d96..0b9595043 100644 --- a/include/boost/geometry/algorithms/detail/is_valid/ring.hpp +++ b/include/boost/geometry/algorithms/detail/is_valid/ring.hpp @@ -198,7 +198,7 @@ struct is_valid_ring return is_topologically_closed::apply(ring, visitor) && ! has_duplicates::apply(ring, visitor) - && ! has_spikes::apply(ring, visitor) + && ! has_spikes::apply(ring, visitor, strategy.get_side_strategy()) && (! CheckSelfIntersections || has_valid_self_turns::apply(ring, visitor, strategy)) && is_properly_oriented::apply(ring, visitor, strategy); diff --git a/include/boost/geometry/algorithms/detail/occupation_info.hpp b/include/boost/geometry/algorithms/detail/occupation_info.hpp index 4048d59d7..fc74f0cc7 100644 --- a/include/boost/geometry/algorithms/detail/occupation_info.hpp +++ b/include/boost/geometry/algorithms/detail/occupation_info.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2012-2014 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -99,12 +104,18 @@ public : } } - template - inline void get_left_turns(RobustPoint const& origin, Turns& turns) + template + inline void get_left_turns(RobustPoint const& origin, Turns& turns, + SideStrategy const& strategy) { + typedef detail::left_turns::angle_less + < + typename AngleInfo::point_type, + SideStrategy + > angle_less; + // Sort on angle - std::sort(m_angles.begin(), m_angles.end(), - detail::left_turns::angle_less(origin)); + std::sort(m_angles.begin(), m_angles.end(), angle_less(origin, strategy)); // Group same-angled elements std::size_t cluster_size = detail::left_turns::assign_cluster_indices(m_angles, origin); diff --git a/include/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp b/include/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp index 03c06c28d..fb7384079 100644 --- a/include/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -63,8 +63,9 @@ inline bool points_equal_or_close(Point1 const& point1, } -template +template inline void append_no_dups_or_spikes(Range& range, Point const& point, + SideStrategy const& strategy, RobustPolicy const& robust_policy) { #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION @@ -92,6 +93,7 @@ inline void append_no_dups_or_spikes(Range& range, Point const& point, && point_is_spike_or_equal(point, *(boost::end(range) - 3), *(boost::end(range) - 2), + strategy, robust_policy)) { // Use the Concept/traits, so resize and append again @@ -100,8 +102,9 @@ inline void append_no_dups_or_spikes(Range& range, Point const& point, } } -template +template inline void clean_closing_dups_and_spikes(Range& range, + SideStrategy const& strategy, RobustPolicy const& robust_policy) { std::size_t const minsize @@ -135,7 +138,7 @@ inline void clean_closing_dups_and_spikes(Range& range, // Check if closing point is a spike (this is so if the second point is // considered as a spike w.r.t. the last segment) - if (point_is_spike_or_equal(*second, *ultimate, *first, robust_policy)) + if (point_is_spike_or_equal(*second, *ultimate, *first, strategy, robust_policy)) { range::erase(range, first); if (BOOST_GEOMETRY_CONDITION(closed)) diff --git a/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp b/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp index a056d4fa3..c6f540a97 100644 --- a/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -57,12 +57,14 @@ struct copy_segments_ring < typename Ring, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(Ring const& ring, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -109,7 +111,7 @@ struct copy_segments_ring for (signed_size_type i = 0; i < count; ++i, ++it) { - detail::overlay::append_no_dups_or_spikes(current_output, *it, robust_policy); + detail::overlay::append_no_dups_or_spikes(current_output, *it, strategy, robust_policy); } } }; @@ -119,20 +121,23 @@ class copy_segments_linestring { private: // remove spikes - template + template static inline void append_to_output(RangeOut& current_output, Point const& point, + SideStrategy const& strategy, RobustPolicy const& robust_policy, boost::true_type const&) { detail::overlay::append_no_dups_or_spikes(current_output, point, + strategy, robust_policy); } // keep spikes - template + template static inline void append_to_output(RangeOut& current_output, Point const& point, + SideStrategy const&, RobustPolicy const&, boost::false_type const&) { @@ -144,12 +149,14 @@ public: < typename LineString, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(LineString const& ls, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -170,7 +177,7 @@ public: for (signed_size_type i = 0; i < count; ++i, ++it) { - append_to_output(current_output, *it, robust_policy, + append_to_output(current_output, *it, strategy, robust_policy, boost::integral_constant()); } } @@ -183,12 +190,14 @@ struct copy_segments_polygon < typename Polygon, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(Polygon const& polygon, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -199,6 +208,7 @@ struct copy_segments_polygon ? geometry::exterior_ring(polygon) : range::at(geometry::interior_rings(polygon), seg_id.ring_index), seg_id, to_index, + strategy, robust_policy, current_output ); @@ -213,12 +223,14 @@ struct copy_segments_box < typename Box, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(Box const& box, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -239,7 +251,7 @@ struct copy_segments_box for (signed_size_type i = 0; i < count; i++, index++) { detail::overlay::append_no_dups_or_spikes(current_output, - bp[index % 5], robust_policy); + bp[index % 5], strategy, robust_policy); } } @@ -253,12 +265,14 @@ struct copy_segments_multi < typename MultiGeometry, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > static inline void apply(MultiGeometry const& multi_geometry, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& current_output) { @@ -272,6 +286,7 @@ struct copy_segments_multi // Call the single-version Policy::apply(range::at(multi_geometry, seg_id.multi_index), seg_id, to_index, + strategy, robust_policy, current_output); } @@ -341,12 +356,14 @@ template bool Reverse, typename Geometry, typename SegmentIdentifier, + typename SideStrategy, typename RobustPolicy, typename RangeOut > inline void copy_segments(Geometry const& geometry, SegmentIdentifier const& seg_id, signed_size_type to_index, + SideStrategy const& strategy, RobustPolicy const& robust_policy, RangeOut& range_out) { @@ -356,7 +373,7 @@ inline void copy_segments(Geometry const& geometry, < typename tag::type, Reverse - >::apply(geometry, seg_id, to_index, robust_policy, range_out); + >::apply(geometry, seg_id, to_index, strategy, robust_policy, range_out); } diff --git a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index c7b464cfa..47225328d 100644 --- a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -59,7 +64,7 @@ template typename Turns, typename Geometry1, typename Geometry2, typename RobustPolicy, - typename Strategy + typename SideStrategy > inline void enrich_sort(Operations& operations, Turns const& turns, @@ -67,7 +72,7 @@ inline void enrich_sort(Operations& operations, Geometry1 const& geometry1, Geometry2 const& geometry2, RobustPolicy const& robust_policy, - Strategy const& /*strategy*/) + SideStrategy const& strategy) { std::sort(boost::begin(operations), boost::end(operations), @@ -77,8 +82,9 @@ inline void enrich_sort(Operations& operations, typename boost::range_value::type, Geometry1, Geometry2, RobustPolicy, + SideStrategy, Reverse1, Reverse2 - >(turns, for_operation, geometry1, geometry2, robust_policy)); + >(turns, for_operation, geometry1, geometry2, robust_policy, strategy)); } @@ -279,7 +285,7 @@ inline void calculate_remaining_distance(Turns& turns) \tparam Clusters type of cluster container \tparam Geometry1 \tparam_geometry \tparam Geometry2 \tparam_geometry -\tparam Strategy side strategy type +\tparam SideStrategy side strategy type \param turns container containing intersection points \param clusters container containing clusters \param geometry1 \param_geometry @@ -295,13 +301,13 @@ template typename Clusters, typename Geometry1, typename Geometry2, typename RobustPolicy, - typename Strategy + typename SideStrategy > inline void enrich_intersection_points(Turns& turns, Clusters& clusters, Geometry1 const& geometry1, Geometry2 const& geometry2, RobustPolicy const& robust_policy, - Strategy const& strategy) + SideStrategy const& strategy) { static const detail::overlay::operation_type target_operation = detail::overlay::operation_from_overlay::value; @@ -424,7 +430,8 @@ inline void enrich_intersection_points(Turns& turns, Reverse1, Reverse2, OverlayType - >(clusters, turns, target_operation, geometry1, geometry2); + >(clusters, turns, target_operation, + geometry1, geometry2, strategy); detail::overlay::cleanup_clusters(turns, clusters); } diff --git a/include/boost/geometry/algorithms/detail/overlay/follow.hpp b/include/boost/geometry/algorithms/detail/overlay/follow.hpp index e2de429af..589e12cc2 100644 --- a/include/boost/geometry/algorithms/detail/overlay/follow.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/follow.hpp @@ -157,6 +157,7 @@ struct action_selector typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void enter(LineStringOut& current_piece, @@ -164,6 +165,7 @@ struct action_selector segment_identifier& segment_id, signed_size_type , Point const& point, Operation const& operation, + SideStrategy const& , RobustPolicy const& , OutputIterator& ) { @@ -180,6 +182,7 @@ struct action_selector typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void leave(LineStringOut& current_piece, @@ -187,6 +190,7 @@ struct action_selector segment_identifier& segment_id, signed_size_type index, Point const& point, Operation const& , + SideStrategy const& strategy, RobustPolicy const& robust_policy, OutputIterator& out) { @@ -195,7 +199,7 @@ struct action_selector detail::copy_segments::copy_segments_linestring < false, RemoveSpikes - >::apply(linestring, segment_id, index, robust_policy, current_piece); + >::apply(linestring, segment_id, index, strategy, robust_policy, current_piece); detail::overlay::append_no_duplicates(current_piece, point); if (::boost::size(current_piece) > 1) { @@ -254,6 +258,7 @@ struct action_selector typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void enter(LineStringOut& current_piece, @@ -261,11 +266,12 @@ struct action_selector segment_identifier& segment_id, signed_size_type index, Point const& point, Operation const& operation, + SideStrategy const& strategy, RobustPolicy const& robust_policy, OutputIterator& out) { normal_action::leave(current_piece, linestring, segment_id, index, - point, operation, robust_policy, out); + point, operation, strategy, robust_policy, out); } template @@ -275,6 +281,7 @@ struct action_selector typename LineString, typename Point, typename Operation, + typename SideStrategy, typename RobustPolicy > static inline void leave(LineStringOut& current_piece, @@ -282,11 +289,12 @@ struct action_selector segment_identifier& segment_id, signed_size_type index, Point const& point, Operation const& operation, + SideStrategy const& strategy, RobustPolicy const& robust_policy, OutputIterator& out) { normal_action::enter(current_piece, linestring, segment_id, index, - point, operation, robust_policy, out); + point, operation, strategy, robust_policy, out); } template @@ -456,7 +464,7 @@ public : entered = true; action::enter(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, - robust_policy, + strategy, robust_policy, out); } else if (following::is_leaving(*it, *iit, entered, first, linestring, polygon, pt_in_poly_strategy)) @@ -466,7 +474,7 @@ public : entered = false; action::leave(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, - robust_policy, + strategy, robust_policy, out); } first = false; @@ -480,7 +488,7 @@ public : >::apply(linestring, current_segment_id, static_cast(boost::size(linestring) - 1), - robust_policy, + strategy, robust_policy, current_piece); } diff --git a/include/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp b/include/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp index c249ff57f..2a374bf0b 100644 --- a/include/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp @@ -2,12 +2,14 @@ // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland. -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html -// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_LINEAR_LINEAR_HPP @@ -183,7 +185,8 @@ protected: typename TurnIterator, typename TurnOperationIterator, typename SegmentIdentifier, - typename OutputIterator + typename OutputIterator, + typename SideStrategy > static inline OutputIterator process_turn(TurnIterator it, @@ -193,7 +196,8 @@ protected: Linestring const& linestring, LinestringOut& current_piece, SegmentIdentifier& current_segment_id, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { // We don't rescale linear/linear detail::no_rescale_policy robust_policy; @@ -208,7 +212,7 @@ protected: action::enter(current_piece, linestring, current_segment_id, op_it->seg_id.segment_index, - it->point, *op_it, robust_policy, oit); + it->point, *op_it, strategy, robust_policy, oit); } ++enter_count; } @@ -223,7 +227,7 @@ protected: action::leave(current_piece, linestring, current_segment_id, op_it->seg_id.segment_index, - it->point, *op_it, robust_policy, oit); + it->point, *op_it, strategy, robust_policy, oit); } } else if ( FollowIsolatedPoints @@ -249,14 +253,16 @@ protected: template < typename SegmentIdentifier, - typename OutputIterator + typename OutputIterator, + typename SideStrategy > static inline OutputIterator process_end(bool entered, Linestring const& linestring, SegmentIdentifier const& current_segment_id, LinestringOut& current_piece, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { if ( action::is_entered(entered) ) { @@ -269,6 +275,7 @@ protected: >::apply(linestring, current_segment_id, static_cast(boost::size(linestring) - 1), + strategy, robust_policy, current_piece); } @@ -283,11 +290,12 @@ protected: } public: - template + template static inline OutputIterator apply(Linestring const& linestring, Linear const&, TurnIterator first, TurnIterator beyond, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { // Iterate through all intersection points (they are // ordered along the each line) @@ -304,7 +312,8 @@ public: entered, enter_count, linestring, current_piece, current_segment_id, - oit); + oit, + strategy); } #if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) @@ -318,7 +327,8 @@ public: return process_end(entered, linestring, current_segment_id, current_piece, - oit); + oit, + strategy); } }; @@ -413,11 +423,12 @@ protected: }; public: - template + template static inline OutputIterator apply(MultiLinestring const& multilinestring, Linear const& linear, TurnIterator first, TurnIterator beyond, - OutputIterator oit) + OutputIterator oit, + SideStrategy const& strategy) { BOOST_GEOMETRY_ASSERT( first != beyond ); @@ -447,7 +458,7 @@ public: has_other_multi_id(current_multi_id)); oit = Base::apply(*(ls_first + current_multi_id), - linear, per_ls_current, per_ls_next, oit); + linear, per_ls_current, per_ls_next, oit, strategy); signed_size_type next_multi_id = -1; linestring_iterator ls_next = ls_beyond; diff --git a/include/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp b/include/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp index ea9aa29f1..2eec6af66 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -31,20 +36,15 @@ namespace detail { namespace overlay but we still need to know which comes first. Therefore, it is useful that using sides we are able to discover this. */ -template struct get_relative_order { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type strategy; - - template + template static inline int value_via_product(Point const& ti, Point const& tj, - Point const& ui, Point const& uj, int factor) + Point const& ui, Point const& uj, int factor, + SideStrategy const& strategy) { - int const side_ti_u = strategy::apply(ti, tj, ui); - int const side_tj_u = strategy::apply(ti, tj, uj); + int const side_ti_u = strategy.apply(ti, tj, ui); + int const side_tj_u = strategy.apply(ti, tj, uj); #ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER std::cout << (factor == 1 ? " r//s " : " s//r ") @@ -57,13 +57,15 @@ struct get_relative_order } + template static inline int apply( Point1 const& pi, Point1 const& pj, Point1 const& ri, Point1 const& rj, - Point1 const& si, Point1 const& sj) + Point1 const& si, Point1 const& sj, + SideStrategy const& strategy) { - int const side_ri_p = strategy::apply(pi, pj, ri); - int const side_si_p = strategy::apply(pi, pj, si); + int const side_ri_p = strategy.apply(pi, pj, ri); + int const side_si_p = strategy.apply(pi, pj, si); #ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER int const side_rj_p = strategy::apply(pi, pj, rj); @@ -72,10 +74,10 @@ struct get_relative_order std::cout << " s//p: " << side_si_p << " / " << side_sj_p; #endif - int value = value_via_product(si, sj, ri, rj, 1); + int value = value_via_product(si, sj, ri, rj, 1, strategy); if (value == 0) { - value = value_via_product(ri, rj, si, sj, -1); + value = value_via_product(ri, rj, si, sj, -1, strategy); } int const order = side_ri_p * side_ri_p * side_si_p * value; diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index 9c0f190eb..f3311b34e 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -697,11 +702,13 @@ template typename Turns, typename Clusters, typename Geometry1, - typename Geometry2 + typename Geometry2, + typename SideStrategy > inline void gather_cluster_properties(Clusters& clusters, Turns& turns, operation_type for_operation, - Geometry1 const& geometry1, Geometry2 const& geometry2) + Geometry1 const& geometry1, Geometry2 const& geometry2, + SideStrategy const& strategy) { typedef typename boost::range_value::type turn_type; typedef typename turn_type::point_type point_type; @@ -711,7 +718,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, // right side typedef sort_by_side::side_sorter < - Reverse1, Reverse2, OverlayType, point_type, std::less + Reverse1, Reverse2, OverlayType, point_type, SideStrategy, std::less > sbs_type; for (typename Clusters::iterator mit = clusters.begin(); @@ -724,7 +731,7 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, continue; } - sbs_type sbs; + sbs_type sbs(strategy); point_type turn_point; // should be all the same for all turns in cluster bool first = true; diff --git a/include/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp b/include/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp index 21868a293..dd30635ee 100644 --- a/include/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/less_by_segment_ratio.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -60,6 +65,7 @@ template typename Indexed, typename Geometry1, typename Geometry2, typename RobustPolicy, + typename SideStrategy, bool Reverse1, bool Reverse2 > struct less_by_segment_ratio @@ -68,12 +74,14 @@ struct less_by_segment_ratio , operation_type for_operation , Geometry1 const& geometry1 , Geometry2 const& geometry2 - , RobustPolicy const& robust_policy) + , RobustPolicy const& robust_policy + , SideStrategy const& strategy) : m_turns(turns) , m_for_operation(for_operation) , m_geometry1(geometry1) , m_geometry2(geometry2) , m_robust_policy(robust_policy) + , m_strategy(strategy) { } @@ -84,6 +92,7 @@ private : Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; RobustPolicy const& m_robust_policy; + SideStrategy const& m_strategy; typedef typename geometry::point_type::type point_type; @@ -108,13 +117,8 @@ private : *right.other_seg_id, si, sj); - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type strategy; - - int const side_rj_p = strategy::apply(pi, pj, rj); - int const side_sj_p = strategy::apply(pi, pj, sj); + int const side_rj_p = m_strategy.apply(pi, pj, rj); + int const side_sj_p = m_strategy.apply(pi, pj, sj); // Put the one turning left (1; right == -1) as last if (side_rj_p != side_sj_p) @@ -122,8 +126,8 @@ private : return side_rj_p < side_sj_p; } - int const side_sj_r = strategy::apply(ri, rj, sj); - int const side_rj_s = strategy::apply(si, sj, rj); + int const side_sj_r = m_strategy.apply(ri, rj, sj); + int const side_rj_s = m_strategy.apply(si, sj, rj); // If they both turn left: the most left as last // If they both turn right: this is not relevant, but take also here most left diff --git a/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp b/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp index a74bb33ba..21d079d95 100644 --- a/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/linear_linear.hpp @@ -194,13 +194,15 @@ protected: typename Turns, typename LinearGeometry1, typename LinearGeometry2, - typename OutputIterator + typename OutputIterator, + typename IntersectionStrategy > static inline OutputIterator sort_and_follow_turns(Turns& turns, LinearGeometry1 const& linear1, LinearGeometry2 const& linear2, - OutputIterator oit) + OutputIterator oit, + IntersectionStrategy const& strategy) { // remove turns that have no added value turns::filter_continue_turns @@ -228,7 +230,7 @@ protected: FollowIsolatedPoints, !EnableFilterContinueTurns || OverlayType == overlay_intersection >::apply(linear1, linear2, boost::begin(turns), boost::end(turns), - oit); + oit, strategy.get_side_strategy()); } public: @@ -277,7 +279,7 @@ public: OverlayType, EnableFollowIsolatedPoints && OverlayType == overlay_intersection - >(turns, linear1, linear2, oit); + >(turns, linear1, linear2, oit, strategy); } }; diff --git a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp index a2f7691c3..5ad2e41b1 100644 --- a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -18,7 +23,6 @@ #include #include #include -#include namespace boost { namespace geometry { @@ -106,17 +110,13 @@ struct less_false } }; -template +template struct less_by_side { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side; - - less_by_side(const Point& p1, const Point& p2) + less_by_side(const Point& p1, const Point& p2, SideStrategy const& strategy) : m_p1(p1) , m_p2(p2) + , m_strategy(strategy) {} template @@ -125,8 +125,8 @@ struct less_by_side LessOnSame on_same; Compare compare; - int const side_first = side::apply(m_p1, m_p2, first.point); - int const side_second = side::apply(m_p1, m_p2, second.point); + int const side_first = m_strategy.apply(m_p1, m_p2, first.point); + int const side_second = m_strategy.apply(m_p1, m_p2, second.point); if (side_first == 0 && side_second == 0) { @@ -166,7 +166,7 @@ struct less_by_side // They are both left, both right, and/or both collinear (with each other and/or with p1,p2) // Check mutual side - int const side_second_wrt_first = side::apply(m_p2, first.point, second.point); + int const side_second_wrt_first = m_strategy.apply(m_p2, first.point, second.point); if (side_second_wrt_first == 0) { @@ -184,6 +184,7 @@ struct less_by_side private : Point m_p1, m_p2; + SideStrategy const& m_strategy; }; // Sorts vectors in counter clockwise order (by default) @@ -193,6 +194,7 @@ template bool Reverse2, overlay_type OverlayType, typename Point, + typename SideStrategy, typename Compare > struct side_sorter @@ -223,9 +225,10 @@ private : }; public : - side_sorter() + side_sorter(SideStrategy const& strategy) : m_origin_count(0) , m_origin_segment_distance(0) + , m_strategy(strategy) {} template @@ -309,8 +312,8 @@ public : // to give colinear points // Sort by side and assign rank - less_by_side less_unique(m_origin, turn_point); - less_by_side less_non_unique(m_origin, turn_point); + less_by_side less_unique(m_origin, turn_point, m_strategy); + less_by_side less_non_unique(m_origin, turn_point, m_strategy); std::sort(m_ranked_points.begin(), m_ranked_points.end(), less_unique); @@ -425,6 +428,7 @@ public : Point m_origin; std::size_t m_origin_count; int m_origin_segment_distance; + SideStrategy m_strategy; private : diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index ccb5d0e1f..69d62b788 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -94,6 +99,7 @@ template typename Turns, typename Clusters, typename RobustPolicy, + typename SideStrategy, typename Visitor > struct traversal @@ -108,17 +114,19 @@ struct traversal typedef sort_by_side::side_sorter < Reverse1, Reverse2, OverlayType, - point_type, side_compare_type + point_type, SideStrategy, side_compare_type > sbs_type; inline traversal(Geometry1 const& geometry1, Geometry2 const& geometry2, Turns& turns, Clusters const& clusters, - RobustPolicy const& robust_policy, Visitor& visitor) + RobustPolicy const& robust_policy, SideStrategy const& strategy, + Visitor& visitor) : m_geometry1(geometry1) , m_geometry2(geometry2) , m_turns(turns) , m_clusters(clusters) , m_robust_policy(robust_policy) + , m_strategy(strategy) , m_visitor(visitor) { } @@ -587,7 +595,7 @@ struct traversal cluster_info const& cinfo = mit->second; std::set const& ids = cinfo.turn_indices; - sbs_type sbs; + sbs_type sbs(m_strategy); for (typename std::set::const_iterator sit = ids.begin(); sit != ids.end(); ++sit) @@ -633,7 +641,7 @@ struct traversal turn_type const& current_turn, segment_identifier const& previous_seg_id) { - sbs_type sbs; + sbs_type sbs(m_strategy); // Add this turn to the sort-by-side sorter for (int i = 0; i < 2; i++) @@ -825,6 +833,7 @@ private : Turns& m_turns; Clusters const& m_clusters; RobustPolicy const& m_robust_policy; + SideStrategy m_strategy; Visitor& m_visitor; }; diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp index 6a346423e..af643a822 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp @@ -49,9 +49,13 @@ template > struct traversal_ring_creator { - typedef traversal - traversal_type; + typedef traversal + < + Reverse1, Reverse2, OverlayType, + Geometry1, Geometry2, Turns, Clusters, + RobustPolicy, typename IntersectionStrategy::side_strategy_type, + Visitor + > traversal_type; typedef typename boost::range_value::type turn_type; typedef typename turn_type::turn_operation_type turn_operation_type; @@ -63,7 +67,9 @@ struct traversal_ring_creator Turns& turns, Clusters const& clusters, IntersectionStrategy const& intersection_strategy, RobustPolicy const& robust_policy, Visitor& visitor) - : m_trav(geometry1, geometry2, turns, clusters, robust_policy,visitor) + : m_trav(geometry1, geometry2, turns, clusters, + robust_policy, intersection_strategy.get_side_strategy(), + visitor) , m_geometry1(geometry1) , m_geometry2(geometry2) , m_turns(turns) @@ -103,12 +109,14 @@ struct traversal_ring_creator { geometry::copy_segments(m_geometry1, previous_op.seg_id, to_vertex_index, + m_intersection_strategy.get_side_strategy(), m_robust_policy, current_ring); } else { geometry::copy_segments(m_geometry2, previous_op.seg_id, to_vertex_index, + m_intersection_strategy.get_side_strategy(), m_robust_policy, current_ring); } } @@ -152,6 +160,7 @@ struct traversal_ring_creator turn_type& current_turn = m_turns[turn_index]; turn_operation_type& op = current_turn.operations[op_index]; detail::overlay::append_no_dups_or_spikes(current_ring, current_turn.point, + m_intersection_strategy.get_side_strategy(), m_robust_policy); // Register the visit @@ -169,6 +178,7 @@ struct traversal_ring_creator turn_operation_type& start_op = m_turns[start_turn_index].operations[start_op_index]; detail::overlay::append_no_dups_or_spikes(ring, start_turn.point, + m_intersection_strategy.get_side_strategy(), m_robust_policy); signed_size_type current_turn_index = start_turn_index; @@ -271,7 +281,9 @@ struct traversal_ring_creator if (geometry::num_points(ring) >= min_num_points) { - clean_closing_dups_and_spikes(ring, m_robust_policy); + clean_closing_dups_and_spikes(ring, + m_intersection_strategy.get_side_strategy(), + m_robust_policy); rings.push_back(ring); m_trav.finalize_visit_info(); diff --git a/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp b/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp index 607ba8153..9a48ec8b1 100644 --- a/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp +++ b/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp @@ -19,10 +19,13 @@ #include #include +#include #include #include #include #include +#include +#include namespace boost { namespace geometry { @@ -32,6 +35,65 @@ namespace boost { namespace geometry namespace detail { +template ::type> +struct collinear_point_is_spike_or_equal +{ +/*}; + +template +struct collinear_point_is_spike_or_equal +{*/ + template + static inline bool apply(Point1 const& last_point, + Point2 const& segment_a, + Point3 const& segment_b) + { + // Check if segment is equal + int const sgn_x1 = sign_of_difference<0>(last_point, segment_b); + int const sgn_y1 = sign_of_difference<1>(last_point, segment_b); + if (sgn_x1 == 0 && sgn_y1 == 0) + { + return true; + } + + // Check if segment moves forward + int const sgn_x2 = sign_of_difference<0>(segment_b, segment_a); + int const sgn_y2 = sign_of_difference<1>(segment_b, segment_a); + + return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; + } +}; +/* +template +struct collinear_point_is_spike_or_equal +{ + template + static inline bool apply(Point1 const& last_point, + Point2 const& segment_a, + Point3 const& segment_b) + { + // Check if segment is equal + + bool const is_equal_x1 = math::equals(geometry::get<0>(last_point), geometry::get<0>(segment_b)); + bool const is_equal_y1 = math::equals(geometry::get<1>(last_point), geometry::get<1>(segment_b)); + if (is_equal_x1 && is_equal_y1) + { + return true; + } + + typedef typename select_coordinate_type::type coord_t1; + coord_t1 diff_x1 = longitude_distance_signed<> + typename coordinate_system::type::units + + + // Check if segment moves forward + int const sgn_x2 = sign_of_difference<0>(segment_b, segment_a); + int const sgn_y2 = sign_of_difference<1>(segment_b, segment_a); + + return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; + } +};*/ + // Checks if a point ("last_point") causes a spike w.r.t. // the specified two other points (segment_a, segment_b) // @@ -42,33 +104,22 @@ namespace detail // So specify last point first, then (a,b) // The segment's orientation does matter: if lp is to the right of b // no spike is reported -template +template +< + typename Point1, typename Point2, typename Point3, + typename SideStrategy +> static inline bool point_is_spike_or_equal(Point1 const& last_point, Point2 const& segment_a, - Point3 const& segment_b) + Point3 const& segment_b, + SideStrategy const& strategy) { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy; - - int const side = side_strategy::apply(last_point, segment_a, segment_b); + int const side = strategy.apply(segment_a, segment_b, last_point); if (side == 0) { // Last point is collinear w.r.t previous segment. - // Check if it is equal - int const sgn_x1 = sign_of_difference<0>(last_point, segment_b); - int const sgn_y1 = sign_of_difference<1>(last_point, segment_b); - if (sgn_x1 == 0 && sgn_y1 == 0) - { - return true; - } - - // Check if it moves forward - int const sgn_x2 = sign_of_difference<0>(segment_b, segment_a); - int const sgn_y2 = sign_of_difference<1>(segment_b, segment_a); - - return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; + return collinear_point_is_spike_or_equal + ::apply(last_point, segment_a, segment_b); } return false; } @@ -78,14 +129,16 @@ template typename Point1, typename Point2, typename Point3, + typename SideStrategy, typename RobustPolicy > static inline bool point_is_spike_or_equal(Point1 const& last_point, Point2 const& segment_a, Point3 const& segment_b, + SideStrategy const& strategy, RobustPolicy const& robust_policy) { - if (point_is_spike_or_equal(last_point, segment_a, segment_b)) + if (point_is_spike_or_equal(last_point, segment_a, segment_b, strategy)) { return true; } @@ -111,7 +164,8 @@ static inline bool point_is_spike_or_equal(Point1 const& last_point, ( last_point_rob, segment_a_rob, - segment_b_rob + segment_b_rob, + strategy ); } diff --git a/include/boost/geometry/algorithms/is_convex.hpp b/include/boost/geometry/algorithms/is_convex.hpp index 8feb48db6..4a9251b27 100644 --- a/include/boost/geometry/algorithms/is_convex.hpp +++ b/include/boost/geometry/algorithms/is_convex.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -10,17 +15,23 @@ #define BOOST_GEOMETRY_ALGORITHMS_IS_CONVEX_HPP +#include +#include +#include + +#include #include #include #include #include #include -#include +#include #include +#include #include -#include #include + namespace boost { namespace geometry { @@ -31,15 +42,9 @@ namespace detail { namespace is_convex struct ring_is_convex { - template - static inline bool apply(Ring const& ring) + template + static inline bool apply(Ring const& ring, SideStrategy const& strategy) { - typedef typename geometry::point_type::type point_type; - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy_type; - std::size_t n = boost::size(ring); if (boost::size(ring) < core_detail::closure::minimum_ring_size < @@ -86,7 +91,7 @@ struct ring_is_convex // iterator for (std::size_t i = 0; i < n; i++) { - int const side = side_strategy_type::apply(*previous, *current, *next); + int const side = strategy.apply(*previous, *current, *next); if (side == 1) { // Next is on the left side of clockwise ring: @@ -129,7 +134,8 @@ struct is_convex : not_implemented template struct is_convex { - static inline bool apply(Box const& ) + template + static inline bool apply(Box const& , Strategy const& ) { // Any box is convex (TODO: consider spherical boxes) return true; @@ -144,13 +150,71 @@ struct is_convex : detail::is_convex::ring_is_convex } // namespace dispatch #endif // DOXYGEN_NO_DISPATCH -// TODO: variants +namespace resolve_variant { + +template +struct is_convex +{ + template + static bool apply(Geometry const& geometry, Strategy const& strategy) + { + concepts::check(); + return dispatch::is_convex::apply(geometry, strategy); + } + + static bool apply(Geometry const& geometry, geometry::default_strategy const&) + { + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + + return apply(geometry, side_strategy()); + } +}; + +template +struct is_convex > +{ + template + struct visitor: boost::static_visitor + { + Strategy const& m_strategy; + + visitor(Strategy const& strategy) : m_strategy(strategy) {} + + template + bool operator()(Geometry const& geometry) const + { + return is_convex::apply(geometry, m_strategy); + } + }; + + template + static inline bool apply(boost::variant const& geometry, + Strategy const& strategy) + { + return boost::apply_visitor(visitor(strategy), geometry); + } +}; + +} // namespace resolve_variant // TODO: documentation / qbk template inline bool is_convex(Geometry const& geometry) { - return dispatch::is_convex::apply(geometry); + return resolve_variant::is_convex + < + Geometry + >::apply(geometry, geometry::default_strategy()); +} + +// TODO: documentation / qbk +template +inline bool is_convex(Geometry const& geometry, Strategy const& strategy) +{ + return resolve_variant::is_convex::apply(geometry, strategy); } diff --git a/include/boost/geometry/algorithms/point_on_surface.hpp b/include/boost/geometry/algorithms/point_on_surface.hpp index e9041f937..3f4d0f4af 100644 --- a/include/boost/geometry/algorithms/point_on_surface.hpp +++ b/include/boost/geometry/algorithms/point_on_surface.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2013 Mateusz Loskot, London, UK. // Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -33,6 +33,7 @@ #include #include +#include namespace boost { namespace geometry @@ -241,8 +242,9 @@ inline void replace_extremes_for_self_tangencies(Extremes& extremes, Intruders& extremes = triangle; } -template -inline bool calculate_point_on_surface(Geometry const& geometry, Point& point) +template +inline bool calculate_point_on_surface(Geometry const& geometry, Point& point, + SideStrategy const& strategy) { typedef typename geometry::point_type::type point_type; typedef typename geometry::coordinate_type::type coordinate_type; @@ -250,7 +252,7 @@ inline bool calculate_point_on_surface(Geometry const& geometry, Point& point) typedef std::vector > intruders_type; intruders_type intruders; - geometry::extreme_points(geometry, extremes, intruders); + geometry::extreme_points(geometry, extremes, intruders, strategy); if (extremes.size() < 3) { @@ -291,21 +293,57 @@ inline bool calculate_point_on_surface(Geometry const& geometry, Point& point) \tparam Geometry geometry type. This also defines the type of the output point \param geometry Geometry to take point from \param point Point to assign +\param strategy side strategy */ -template -inline void point_on_surface(Geometry const& geometry, Point & point) +template +inline void point_on_surface(Geometry const& geometry, Point & point, + SideStrategy const& strategy) { concepts::check(); concepts::check(); // First try in Y-direction (which should always succeed for valid polygons) - if (! detail::point_on_surface::calculate_point_on_surface<1>(geometry, point)) + if (! detail::point_on_surface::calculate_point_on_surface<1>(geometry, point, strategy)) { // For invalid polygons, we might try X-direction - detail::point_on_surface::calculate_point_on_surface<0>(geometry, point); + detail::point_on_surface::calculate_point_on_surface<0>(geometry, point, strategy); } } +/*! +\brief Assigns a Point guaranteed to lie on the surface of the Geometry +\tparam Geometry geometry type. This also defines the type of the output point +\param geometry Geometry to take point from +\param point Point to assign + */ +template +inline void point_on_surface(Geometry const& geometry, Point & point) +{ + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type strategy_type; + + point_on_surface(geometry, point, strategy_type()); +} + + +/*! +\brief Returns point guaranteed to lie on the surface of the Geometry +\tparam Geometry geometry type. This also defines the type of the output point +\param geometry Geometry to take point from +\param strategy side strategy +\return The Point guaranteed to lie on the surface of the Geometry + */ +template +inline typename geometry::point_type::type +return_point_on_surface(Geometry const& geometry, SideStrategy const& strategy) +{ + typename geometry::point_type::type result; + geometry::point_on_surface(geometry, result, strategy); + return result; +} + /*! \brief Returns point guaranteed to lie on the surface of the Geometry \tparam Geometry geometry type. This also defines the type of the output point diff --git a/include/boost/geometry/algorithms/remove_spikes.hpp b/include/boost/geometry/algorithms/remove_spikes.hpp index caa7fed9b..196b20c6a 100644 --- a/include/boost/geometry/algorithms/remove_spikes.hpp +++ b/include/boost/geometry/algorithms/remove_spikes.hpp @@ -5,6 +5,11 @@ // Copyright (c) 2009-2013 Mateusz Loskot, London, UK. // Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -34,6 +39,8 @@ #include #include +#include + #include @@ -59,20 +66,13 @@ namespace detail { namespace remove_spikes { -template struct range_remove_spikes { - typedef typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type side_strategy; - - typedef typename coordinate_type::type coordinate_type; - typedef typename point_type::type point_type; - - - static inline void apply(Range& range) + template + static inline void apply(Range& range, SideStrategy const& strategy) { + typedef typename point_type::type point_type; + std::size_t n = boost::size(range); std::size_t const min_num_points = core_detail::closure::minimum_ring_size < @@ -91,7 +91,10 @@ struct range_remove_spikes cleaned.push_back(*it); while(cleaned.size() >= 3 - && detail::point_is_spike_or_equal(cleaned.back(), *(cleaned.end() - 3), *(cleaned.end() - 2))) + && detail::point_is_spike_or_equal(cleaned.back(), + *(cleaned.end() - 3), + *(cleaned.end() - 2), + strategy)) { // Remove pen-ultimate point causing the spike (or which was equal) cleaned.erase(cleaned.end() - 2); @@ -110,13 +113,21 @@ struct range_remove_spikes found = false; // Check for spike in first point int const penultimate = 2; - while(cleaned.size() >= 3 && detail::point_is_spike_or_equal(cleaned.front(), *(cleaned.end() - penultimate), cleaned.back())) + while(cleaned.size() >= 3 + && detail::point_is_spike_or_equal(cleaned.front(), + *(cleaned.end() - penultimate), + cleaned.back(), + strategy)) { cleaned.pop_back(); found = true; } // Check for spike in second point - while(cleaned.size() >= 3 && detail::point_is_spike_or_equal(*(cleaned.begin() + 1), cleaned.back(), cleaned.front())) + while(cleaned.size() >= 3 + && detail::point_is_spike_or_equal(*(cleaned.begin() + 1), + cleaned.back(), + cleaned.front(), + strategy)) { cleaned.pop_front(); found = true; @@ -144,15 +155,13 @@ struct range_remove_spikes }; -template struct polygon_remove_spikes { - static inline void apply(Polygon& polygon) + template + static inline void apply(Polygon& polygon, SideStrategy const& strategy) { - typedef typename geometry::ring_type::type ring_type; - - typedef range_remove_spikes per_range; - per_range::apply(exterior_ring(polygon)); + typedef range_remove_spikes per_range; + per_range::apply(exterior_ring(polygon), strategy); typename interior_return_type::type rings = interior_rings(polygon); @@ -160,23 +169,24 @@ struct polygon_remove_spikes for (typename detail::interior_iterator::type it = boost::begin(rings); it != boost::end(rings); ++it) { - per_range::apply(*it); + per_range::apply(*it, strategy); } } }; -template +template struct multi_remove_spikes { - static inline void apply(MultiGeometry& multi) + template + static inline void apply(MultiGeometry& multi, SideStrategy const& strategy) { for (typename boost::range_iterator::type it = boost::begin(multi); it != boost::end(multi); ++it) { - SingleVersion::apply(*it); + SingleVersion::apply(*it, strategy); } } }; @@ -199,21 +209,22 @@ template > struct remove_spikes { - static inline void apply(Geometry&) + template + static inline void apply(Geometry&, SideStrategy const&) {} }; template struct remove_spikes - : detail::remove_spikes::range_remove_spikes + : detail::remove_spikes::range_remove_spikes {}; template struct remove_spikes - : detail::remove_spikes::polygon_remove_spikes + : detail::remove_spikes::polygon_remove_spikes {}; @@ -221,11 +232,7 @@ template struct remove_spikes : detail::remove_spikes::multi_remove_spikes < - MultiPolygon, detail::remove_spikes::polygon_remove_spikes - < - typename boost::range_value::type - > > {}; @@ -239,28 +246,46 @@ namespace resolve_variant { template struct remove_spikes { - static void apply(Geometry& geometry) + template + static void apply(Geometry& geometry, Strategy const& strategy) { concepts::check(); - dispatch::remove_spikes::apply(geometry); + dispatch::remove_spikes::apply(geometry, strategy); + } + + static void apply(Geometry& geometry, geometry::default_strategy const&) + { + typedef typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type side_strategy; + + apply(geometry, side_strategy()); } }; template struct remove_spikes > { + template struct visitor: boost::static_visitor { + Strategy const& m_strategy; + + visitor(Strategy const& strategy) : m_strategy(strategy) {} + template void operator()(Geometry& geometry) const { - remove_spikes::apply(geometry); + remove_spikes::apply(geometry, m_strategy); } }; - static inline void apply(boost::variant& geometry) + template + static inline void apply(boost::variant& geometry, + Strategy const& strategy) { - boost::apply_visitor(visitor(), geometry); + boost::apply_visitor(visitor(strategy), geometry); } }; @@ -275,7 +300,20 @@ struct remove_spikes > template inline void remove_spikes(Geometry& geometry) { - resolve_variant::remove_spikes::apply(geometry); + resolve_variant::remove_spikes::apply(geometry, geometry::default_strategy()); +} + +/*! + \ingroup remove_spikes + \tparam Geometry geometry type + \tparam Strategy side strategy type + \param geometry the geometry to make remove_spikes + \param strategy the side strategy used by the algorithm +*/ +template +inline void remove_spikes(Geometry& geometry, Strategy const& strategy) +{ + resolve_variant::remove_spikes::apply(geometry, strategy); } From 1dad2eb97cec09e5be3caadf438abd73b64e5a0c Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 14 Jul 2017 16:19:07 +0200 Subject: [PATCH 031/188] [test][overlay] Update cases wrt recent changes (user-defined strategy). --- test/algorithms/overlay/relative_order.cpp | 10 +++++++++- test/algorithms/overlay/sort_by_side.cpp | 12 +++++++----- test/algorithms/overlay/sort_by_side_basic.cpp | 7 ++++--- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/test/algorithms/overlay/relative_order.cpp b/test/algorithms/overlay/relative_order.cpp index e26f39066..8d2a5b47a 100644 --- a/test/algorithms/overlay/relative_order.cpp +++ b/test/algorithms/overlay/relative_order.cpp @@ -31,6 +31,9 @@ # include #endif +#include +#include + template void test_with_point(std::string const& /*caseid*/, @@ -46,7 +49,12 @@ void test_with_point(std::string const& /*caseid*/, P si = bg::make

(si_x, si_y); P sj = bg::make

(sj_x, sj_y); - int order = bg::detail::overlay::get_relative_order

::apply(pi, pj, ri, rj, si, sj); + typedef typename bg::strategy::side::services::default_strategy + < + typename bg::cs_tag

::type + >::type strategy_type; + + int order = bg::detail::overlay::get_relative_order::apply(pi, pj, ri, rj, si, sj, strategy_type()); BOOST_CHECK_EQUAL(order, expected_order); diff --git a/test/algorithms/overlay/sort_by_side.cpp b/test/algorithms/overlay/sort_by_side.cpp index 5635d46f6..b311dccc7 100644 --- a/test/algorithms/overlay/sort_by_side.cpp +++ b/test/algorithms/overlay/sort_by_side.cpp @@ -49,12 +49,14 @@ template typename Turns, typename Clusters, typename Geometry1, - typename Geometry2 + typename Geometry2, + typename SideStrategy > std::vector test_gather_cluster_properties(std::string const& case_id, Clusters& clusters, Turns& turns, bg::detail::overlay::operation_type for_operation, - Geometry1 const& geometry1, Geometry2 const& geometry2) + Geometry1 const& geometry1, Geometry2 const& geometry2, + SideStrategy const& strategy) { using namespace boost::geometry; using namespace boost::geometry::detail::overlay; @@ -69,7 +71,7 @@ std::vector test_gather_cluster_properties(std::string const& case_ // right side typedef sort_by_side::side_sorter < - Reverse1, Reverse2, OverlayType, point_type, std::less + Reverse1, Reverse2, OverlayType, point_type, SideStrategy, std::less > sbs_type; for (typename Clusters::iterator mit = clusters.begin(); @@ -82,7 +84,7 @@ std::vector test_gather_cluster_properties(std::string const& case_ return result; } - sbs_type sbs; + sbs_type sbs(strategy); point_type turn_point; // should be all the same for all turns in cluster bool first = true; @@ -165,7 +167,7 @@ std::vector apply_overlay(std::string const& case_id, // Gather cluster properties, with test option return test_gather_cluster_properties(case_id, clusters, turns, bg::detail::overlay::operation_from_overlay::value, - geometry1, geometry2); + geometry1, geometry2, strategy.get_side_strategy()); } diff --git a/test/algorithms/overlay/sort_by_side_basic.cpp b/test/algorithms/overlay/sort_by_side_basic.cpp index 877de7c50..15c85c285 100644 --- a/test/algorithms/overlay/sort_by_side_basic.cpp +++ b/test/algorithms/overlay/sort_by_side_basic.cpp @@ -89,13 +89,14 @@ std::vector apply_get_turns(std::string const& case_id, // Define sorter, sorting counter-clockwise such that polygons are on the // right side + typedef typename Strategy::side_strategy_type side_strategy; typedef bg::detail::overlay::sort_by_side::side_sorter < false, false, overlay_union, - point_type, std::less + point_type, side_strategy, std::less > sbs_type; - sbs_type sbs; + sbs_type sbs(strategy.get_side_strategy()); std::cout << "Case: " << case_id << std::endl; @@ -187,7 +188,7 @@ std::vector apply_get_turns(std::string const& case_id, } else { - BOOST_CHECK_MESSAGE(right_count[rank] == ranked_point.count_right, + BOOST_CHECK_MESSAGE(right_count[rank] == int(ranked_point.count_right), " caseid=" << case_id << " ranks: conflict in right_count=" << ranked_point.count_right << " vs " << right_count[rank]); From b8453a3caf9ab12e381297f941a1a3180bb00fa6 Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Tue, 18 Jul 2017 13:05:15 +0300 Subject: [PATCH 032/188] [distance] [test] Clean code in distance test and point segment formula --- .../formulas/distance_point_segment.hpp | 200 +++++++++++------- .../boost/geometry/strategies/distance.hpp | 3 + .../geographic/distance_cross_track.hpp | 4 +- .../algorithms/distance/distance_geo_pl_l.cpp | 58 ----- .../distance/test_distance_geo_common.hpp | 99 +-------- 5 files changed, 128 insertions(+), 236 deletions(-) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index a6ce5c77f..a1b60993a 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -11,9 +11,12 @@ #ifndef BOOST_GEOMETRY_FORMULAS_CROSS_TRACK_GEO_HPP #define BOOST_GEOMETRY_FORMULAS_CROSS_TRACK_GEO_HPP -#include #include +#ifndef BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS +#define BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS 100 +#endif + namespace boost { namespace geometry { namespace formula { @@ -23,27 +26,59 @@ template typename Units, typename Inverse_type_azimuth, typename Inverse_type_distance, - typename Direct_type + typename Direct_type, + bool EnableClosestPoint = false > class distance_point_segment{ public: + struct result_distance_point_segment + { + result_distance_point_segment() + : distance(0) + , closest_point_lon(0) + , closest_point_lat(0) + {} + + CT distance; + CT closest_point_lon; + CT closest_point_lat; + }; + template - CT static inline apply(CT lon1, CT lat1, //p1 - CT lon2, CT lat2, //p2 - CT lon3, CT lat3, //query point p3 - Spheroid const& spheroid, - CT earth_radius = + result_distance_point_segment + static inline non_iterative_case(CT lon1, CT lat1, //p1 + CT lon2, CT lat2, //p2 + Spheroid const& spheroid) + { + result_distance_point_segment result; + result.distance = Inverse_type_distance::apply(lon1, lat1, + lon2, lat2, + spheroid).distance; + if (EnableClosestPoint) + { + result.closest_point_lon = lon1; + result.closest_point_lat = lat1; + } + return result; + } + + template + result_distance_point_segment + static inline apply(CT lon1, CT lat1, //p1 + CT lon2, CT lat2, //p2 + CT lon3, CT lat3, //query point p3 + Spheroid const& spheroid, + CT earth_radius = geometry::srs::sphere().get_radius<1>()) { - - int print = 0; + result_distance_point_segment result; // Constants CT const f = flattening(spheroid); CT const pi = math::pi(); - CT const half_pi = math::pi() / CT(2); + CT const half_pi = pi / CT(2); // Convert to radians lon1 = math::as_radian(lon1); @@ -64,13 +99,13 @@ public: { if (lon3 <= lon1) { - return Inverse_type_distance::apply(lon1, lat1, lon3, lat3, spheroid).distance; + return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); } if (lon3 >= lon2) { - return Inverse_type_distance::apply(lon2, lat2, lon3, lat3, spheroid).distance; + return non_iterative_case(lon2, lat2, lon3, lat3, spheroid); } - return Inverse_type_distance::apply(lon3, lat1, lon3, lat3, spheroid).distance; + return non_iterative_case(lon3, lat1, lon3, lat3, spheroid); } //segment on meridian @@ -82,23 +117,23 @@ public: } if (lat3 <= lat1) { - return Inverse_type_distance::apply(lon1, lat1, lon3, lat3, spheroid).distance; + return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); } if (lat3 >= lat2) { - return Inverse_type_distance::apply(lon2, lat2, lon3, lat3, spheroid).distance; + return non_iterative_case(lon2, lat2, lon3, lat3, spheroid); } - return Inverse_type_distance::apply(lon1, lat3, lon3, lat3, spheroid).distance; + return non_iterative_case(lon1, lat3, lon3, lat3, spheroid); } // Easy cases if (lon3 <= lon1 && lat3 <= lat1) { - return Inverse_type_distance::apply(lon1, lat1, lon3, lat3, spheroid).distance; + return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); } if (lon3 >= lon2 && lat3 <= lat2) { - return Inverse_type_distance::apply(lon2, lat2, lon3, lat3, spheroid).distance; + return non_iterative_case(lon2, lat2, lon3, lat3, spheroid); } // Compute a1 (GEO) @@ -107,17 +142,11 @@ public: CT a312 = a1 - a13; - if (print) - { - std::cout << "a1=" << a1 * math::r2d() << std::endl; - std::cout << "a13=" << a13 * math::r2d() << std::endl; - std::cout << "a312=" << a312 * math::r2d() << std::endl; - } if (a312 > half_pi) { // projection of p3 on geodesic spanned by segment (p1,p2) fall // outside of segment on the side of p1 - return Inverse_type_distance::apply(lon1, lat1, lon3, lat3, spheroid).distance; + return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); } CT a2 = pi + Inverse_type_azimuth::apply(lon2, lat2, lon1, lat1, spheroid).azimuth; @@ -125,17 +154,25 @@ public: CT a321 = a2 - a23; - if (print) - { - std::cout << "a2=" << a2 * math::r2d() << std::endl; - std::cout << "a23=" << a13 * math::r2d() << std::endl; - std::cout << "a321=" << a321 * math::r2d() << std::endl; - } +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "segment=(" << lon1 * math::r2d(); + std::cout << "," << lat1 * math::r2d(); + std::cout << "),(" << lon2 * math::r2d(); + std::cout << "," << lat2 * math::r2d(); + std::cout << ")\np=(" << lon3 * math::r2d(); + std::cout << "," << lat3 * math::r2d(); + std::cout << ")\na1=" << a1 * math::r2d() << std::endl; + std::cout << "a13=" << a13 * math::r2d() << std::endl; + std::cout << "a312=" << a312 * math::r2d() << std::endl; + std::cout << "a2=" << a2 * math::r2d() << std::endl; + std::cout << "a23=" << a13 * math::r2d() << std::endl; + std::cout << "a321=" << a321 * math::r2d() << std::endl; +#endif if (a321 < half_pi) { // projection of p3 on geodesic spanned by segment (p1,p2) fall // outside of segment on the side of p2 - return Inverse_type_distance::apply(lon2, lat2, lon3, lat3, spheroid).distance; + return non_iterative_case(lon2, lat2, lon3, lat3, spheroid); } // Guess s14 (SPHERICAL) @@ -153,35 +190,27 @@ public: geometry::strategy::distance::haversine str(earth_radius); CT s13 = str.apply(p1, p3); - /* - // Closest point is one of the segment's endpoints - if (s34 == s13) - { - return Inverse_type_distance::apply(lon1, lat1, lon3, lat3, spheroid).distance; - } - CT s23 = str.apply(p2, p3); - if (s34 == s23) - { - return Inverse_type_distance::apply(lon2, lat2, lon3, lat3, spheroid).distance; - } -*/ - CT s14 = acos( cos(s13/earth_radius) / cos(s34/earth_radius)) * earth_radius; + CT s14 = acos( cos(s13/earth_radius) / cos(s34/earth_radius) ) * earth_radius; - if (print) - { - std::cout << "s34=" << s34 << std::endl; - std::cout << "s13=" << s13 << std::endl; - std::cout << "s14=" << s14 << std::endl; - std::cout << "===============" << std::endl; - } +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "s34=" << s34 << std::endl; + std::cout << "s13=" << s13 << std::endl; + std::cout << "s14=" << s14 << std::endl; + std::cout << "===============" << std::endl; +#endif // Update s14 (using Newton method) CT prev_distance = 0; geometry::formula::result_direct res14; geometry::formula::result_inverse res34; - for (int i=0; i<20; ++i) - { + int counter = 0; // robustness + CT g4; + CT delta_g4; + + do{ + prev_distance = res34.distance; + // Solve the direct problem to find p4 (GEO) res14 = Direct_type::apply(lon1, lat1, s14, a1, spheroid); @@ -190,11 +219,9 @@ public: CT a4 = Inverse_type_azimuth::apply(res14.lon2, res14.lat2, lon2, lat2, spheroid).azimuth; res34 = Inverse_type_distance::apply(res14.lon2, res14.lat2, lon3, lat3, spheroid); - CT g4 = res34.azimuth - a4; + g4 = res34.azimuth - a4; // Normalize g4 - CT delta_g4; - if (g4 < 0 && g4 < -pi)//close to -270 { delta_g4 = g4 + 1.5 * pi; @@ -217,31 +244,44 @@ public: CT der = M43 / m34; s14 = s14 - delta_g4 / der; - //Print - if (print) +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "p4=" << res14.lon2 * math::r2d() << + "," << res14.lat2 * math::r2d() << std::endl; + std::cout << "delta_g4=" << delta_g4 << std::endl; + std::cout << "g4=" << g4 * math::r2d() << std::endl; + std::cout << "der=" << der << std::endl; + std::cout << "new_s14=" << s14 << std::endl; + std::cout << std::setprecision(16) << "dist =" << res34.distance << std::endl; + std::cout << "---------end of step " << counter << std::endl<< std::endl; +#endif + result.distance = prev_distance; + +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + if (g4 == half_pi) { - std::cout << "p4=" << res14.lon2 * math::r2d() << - "," << res14.lat2 * math::r2d() << std::endl; - std::cout << "delta_g4=" << delta_g4 << std::endl; - std::cout << "g4=" << g4 * math::r2d() << std::endl; - std::cout << "der=" << der << std::endl; - std::cout << "new_s14=" << s14 << std::endl; - std::cout << "dist =" << res34.distance << std::endl; - std::cout << "---------end of step " << i << std::endl<< std::endl; + std::cout << "Stop msg: g4 == half_pi" << std::endl; } - - if (g4 == math::pi()/2 || (res34.distance >= prev_distance && prev_distance!=0) || delta_g4 == 0) + if (res34.distance >= prev_distance && prev_distance != 0) { - //std::cout << "stop" << std::endl; - return prev_distance; + std::cout << "Stop msg: res34.distance >= prev_distance" << std::endl; } - prev_distance = res34.distance; + if (delta_g4 == 0) + { + std::cout << "Stop msg: delta_g4 == 0" << std::endl; + } + if (counter == 19) + { + std::cout << "Stop msg: counter" << std::endl; + } +#endif - } + } while (g4 != half_pi + && (prev_distance > res34.distance || prev_distance == 0) + && delta_g4 != 0 + && ++counter < BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS ) ; - ////////////////////////////TESTING - - std::cout << "dist =" << res34.distance << std::endl; +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "distance=" << res34.distance << std::endl; point p4(res14.lon2, res14.lat2); CT s34_sph = str.apply(p4, p3); @@ -270,15 +310,15 @@ public: if (res34.distance <= p4_plus && res34.distance <= p4_minus) { - std::cout << "Closest point computed" << std::endl << std::endl; + std::cout << "Closest point computed" << std::endl; } else { - std::cout << "There is a closer point nearby!" << std::endl; + std::cout << "There is a closer point nearby" << std::endl; } +#endif - CT min_dist_endpoints = std::min(s31,s32); - return std::min(res34.distance, min_dist_endpoints); + return result; } }; diff --git a/include/boost/geometry/strategies/distance.hpp b/include/boost/geometry/strategies/distance.hpp index 0d5a9e6dd..98ccb8202 100644 --- a/include/boost/geometry/strategies/distance.hpp +++ b/include/boost/geometry/strategies/distance.hpp @@ -65,6 +65,9 @@ template struct get_comparable template struct result_from_distance {}; + + + // Default strategy diff --git a/include/boost/geometry/strategies/geographic/distance_cross_track.hpp b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp index 64009ee6a..60ebd54fd 100644 --- a/include/boost/geometry/strategies/geographic/distance_cross_track.hpp +++ b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp @@ -133,7 +133,7 @@ public : false > direct_type; - return geometry::formula::distance_point_segment + return (geometry::formula::distance_point_segment < CT, units_type, @@ -143,7 +143,7 @@ public : >::apply(get<0>(sp1), get<1>(sp1), get<0>(sp2), get<1>(sp2), get<0>(p), get<1>(p), - m_spheroid); + m_spheroid)).distance; } private : diff --git a/test/algorithms/distance/distance_geo_pl_l.cpp b/test/algorithms/distance/distance_geo_pl_l.cpp index 83858f64d..58cdb3b8b 100644 --- a/test/algorithms/distance/distance_geo_pl_l.cpp +++ b/test/algorithms/distance/distance_geo_pl_l.cpp @@ -1,4 +1,3 @@ - // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test @@ -31,9 +30,6 @@ typedef bg::model::multi_linestring multi_linestring_type; namespace services = bg::strategy::distance::services; typedef bg::default_distance_result::type return_type; -//typedef bg::strategy::distance::haversine point_point_strategy; -//typedef bg::strategy::distance::cross_track<> point_segment_strategy; - typedef bg::srs::spheroid stype; // Strategies for point-point distance @@ -67,18 +63,6 @@ pp_distance(std::string const& wkt1, return bg::distance(p1, p2, strategy); } -template -inline bg::default_comparable_distance_result::type -pp_comparable_distance(std::string const& wkt1, - std::string const& wkt2, - Strategy const&) -{ - point_type p1, p2; - bg::read_wkt(wkt1, p1); - bg::read_wkt(wkt2, p2); - return bg::comparable_distance(p1, p2); -} - template inline bg::default_distance_result::type ps_distance(std::string const& wkt1, @@ -92,44 +76,6 @@ ps_distance(std::string const& wkt1, return bg::distance(p, s, strategy); } -template -inline bg::default_comparable_distance_result::type -ps_comparable_distance(std::string const& wkt1, - std::string const& wkt2, - Strategy const& strategy) -{ - point_type p; - segment_type s; - bg::read_wkt(wkt1, p); - bg::read_wkt(wkt2, s); - return bg::comparable_distance(p, s, strategy); -} - -template -T to_comparable(Strategy const& strategy, T const& distance) -{ - namespace services = bg::strategy::distance::services; - - typedef typename services::comparable_type - < - Strategy - >::type comparable_strategy; - - typedef typename services::result_from_distance - < - comparable_strategy, - point_type, - bg::point_type::type - > get_comparable_distance; - - comparable_strategy cstrategy = services::get_comparable - < - Strategy - >::apply(strategy); - - return get_comparable_distance::apply(cstrategy, distance); -} - //=========================================================================== template @@ -360,11 +306,7 @@ void test_distance_point_segment(Strategy_pp const& strategy_pp, BOOST_AUTO_TEST_CASE( test_all_point_segment ) { - //test_distance_point_segment(andoyer_strategy()); - //test_distance_point_segment(thomas_strategy()); test_distance_point_segment(vincenty_pp(), vincenty_strategy()); test_distance_point_segment(thomas_pp(), thomas_strategy()); test_distance_point_segment(andoyer_pp(), andoyer_strategy()); - //test_distance_point_segment(point_segment_strategy(earth_radius_km)); - //test_distance_point_segment(point_segment_strategy(earth_radius_miles)); } diff --git a/test/algorithms/distance/test_distance_geo_common.hpp b/test/algorithms/distance/test_distance_geo_common.hpp index 1f831d447..f1a06e0a2 100644 --- a/test/algorithms/distance/test_distance_geo_common.hpp +++ b/test/algorithms/distance/test_distance_geo_common.hpp @@ -34,7 +34,6 @@ #include #include -#include #include @@ -78,18 +77,6 @@ struct check_equal T const& expected) { equal_to::apply(expected, detected); - /* - TODO: - Ideally we would want the following, but it does not work well - approximate equality test. - BOOST_CHECK_MESSAGE(equal_to::apply(expected, detected), - "case ID: " << case_id << "-" << subcase_id << "; " - << "G1: " << bg::wkt(geometry1) - << " - " - << "G2: " << bg::wkt(geometry2) - << " -> Detected: " << detected - << "; Expected: " << expected); - */ } }; @@ -109,29 +96,6 @@ struct test_distance_of_geometries template struct test_distance_of_geometries { - template - < - typename DistanceType, - typename ComparableDistanceType, - typename Strategy - > - static inline - void apply(std::string const& case_id, - std::string const& wkt1, - std::string const& wkt2, - DistanceType const& expected_distance, - ComparableDistanceType const& expected_comparable_distance, - Strategy const& strategy, - bool test_reversed = true) - { - Geometry1 geometry1 = from_wkt(wkt1); - Geometry2 geometry2 = from_wkt(wkt2); - - apply(case_id, geometry1, geometry2, - expected_distance, expected_comparable_distance, - strategy, test_reversed); - } - template static inline void apply(std::string const& case_id, @@ -145,7 +109,7 @@ struct test_distance_of_geometries Geometry2 geometry2 = from_wkt(wkt2); apply(case_id, geometry1, geometry2, - expected_distance, expected_distance, + expected_distance, strategy, test_reversed); } @@ -153,7 +117,6 @@ struct test_distance_of_geometries template < typename DistanceType, - typename ComparableDistanceType, typename Strategy > static inline @@ -161,7 +124,6 @@ struct test_distance_of_geometries Geometry1 const& geometry1, Geometry2 const& geometry2, DistanceType const& expected_distance, - ComparableDistanceType const& expected_comparable_distance, Strategy const& strategy, bool test_reversed = true) { @@ -193,24 +155,7 @@ struct test_distance_of_geometries >::type::value; BOOST_CHECK(same_regular); -/* - typedef typename bg::default_comparable_distance_result - < - Geometry1, Geometry2 - >::type default_comparable_distance_result; - typedef typename services::return_type - < - typename services::comparable_type::type, - Geometry1, - Geometry2 - >::type comparable_distance_result_from_strategy; - static const bool same_comparable = boost::is_same - < - default_comparable_distance_result, - comparable_distance_result_from_strategy - >::type::value; - BOOST_CHECK( same_comparable ); -*/ + // check distance with passed strategy distance_result_from_strategy dist = bg::distance(geometry1, geometry2, strategy); @@ -232,47 +177,17 @@ struct test_distance_of_geometries >::apply(case_id, "b", geometry1, geometry2, dist_brute_force, expected_distance); -/* - // check comparable distance with passed strategy - comparable_distance_result_from_strategy cdist = - bg::comparable_distance(geometry1, geometry2, strategy); - check_equal - < - default_comparable_distance_result - >::apply(case_id, "c", geometry1, geometry2, - cdist, expected_comparable_distance); - // check against the comparable distance computed in a - // brute-force manner - default_comparable_distance_result cdist_brute_force - = distance_brute_force(geometry1, - geometry2, - services::get_comparable - < - Strategy - >::apply(strategy)); - check_equal - < - default_comparable_distance_result - >::apply(case_id, "d", geometry1, geometry2, - cdist_brute_force, expected_comparable_distance); -*/ #ifdef BOOST_GEOMETRY_TEST_DEBUG std::cout << string_from_type::type>::name() << string_from_type::type>::name() << " -> " << string_from_type::name() - //<< string_from_type::name() << std::endl; - //std::cout << "strategy radius: " << strategy.radius() << std::endl; std::cout << "expected distance = " << expected_distance << " ; " - << "expected comp. distance = " - // << expected_comparable_distance << std::endl; std::cout << "distance = " << dist << " ; " - << "comp. distance = " - //<< cdist << std::endl; if ( !test_reversed ) @@ -291,15 +206,7 @@ struct test_distance_of_geometries default_distance_result >::apply(case_id, "ra", geometry2, geometry1, dist, expected_distance); -/* - // check comparable distance with given strategy - cdist = bg::comparable_distance(geometry2, geometry1, strategy); - check_equal - < - default_comparable_distance_result - >::apply(case_id, "rc", geometry2, geometry1, - cdist, expected_comparable_distance); -*/ + #ifdef BOOST_GEOMETRY_TEST_DEBUG std::cout << "distance[reversed args] = " << dist << " ; " From ca2ab199cf196fd5c0ea0568a59a148a5011c063 Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Tue, 18 Jul 2017 14:00:10 +0300 Subject: [PATCH 033/188] [formula] Documentation for distance point segment --- .../boost/geometry/formulas/distance_point_segment.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index a1b60993a..83f443ab2 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -17,6 +17,14 @@ #define BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS 100 #endif +/*! +\brief Algorithm to compute the distance between a segment and a point using + direct and inverse geodesic problems as subroutines. The algorithm + approximates the distance by an iterative Newton method. +\see C.F.F.Karney - Geodesics on an ellipsoid of revolution, + https://arxiv.org/abs/1102.1215 +*/ + namespace boost { namespace geometry { namespace formula { From 77c54c50d9976e1592dc0d4f6937218af6ba3e98 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 18 Jul 2017 14:38:19 +0200 Subject: [PATCH 034/188] [algorithms] Fix direction_code for very close coordinates and use it in point_is_spike_or_equal to fix it too. --- .../algorithms/detail/direction_code.hpp | 78 ++++++++++----- .../detail/point_is_spike_or_equal.hpp | 94 ++++++------------- 2 files changed, 85 insertions(+), 87 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/direction_code.hpp b/include/boost/geometry/algorithms/detail/direction_code.hpp index 26d53ab4e..fa6c04409 100644 --- a/include/boost/geometry/algorithms/detail/direction_code.hpp +++ b/include/boost/geometry/algorithms/detail/direction_code.hpp @@ -2,10 +2,11 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015 Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017 Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -17,14 +18,19 @@ #include #include +#include + namespace boost { namespace geometry { + #ifndef DOXYGEN_NO_DETAIL namespace detail { + +// TODO: remove template inline int sign_of_difference(Point1 const& point1, Point2 const& point2) { @@ -37,6 +43,50 @@ inline int sign_of_difference(Point1 const& point1, Point2 const& point2) } +template ::type> +struct direction_code_impl +{ + template + static inline int apply(Point1 const& segment_a, Point1 const& segment_b, + Point2 const& p) + { + typedef typename geometry::select_coordinate_type + < + Point1, Point2 + >::type calc_t; + + if ( (math::equals(geometry::get<0>(segment_b), geometry::get<0>(segment_a)) + && math::equals(geometry::get<1>(segment_b), geometry::get<1>(segment_a))) + || (math::equals(geometry::get<0>(segment_b), geometry::get<0>(p)) + && math::equals(geometry::get<1>(segment_b), geometry::get<1>(p))) ) + { + return 0; + } + + calc_t x1 = geometry::get<0>(segment_b) - geometry::get<0>(segment_a); + calc_t y1 = geometry::get<1>(segment_b) - geometry::get<1>(segment_a); + calc_t x2 = geometry::get<0>(segment_b) - geometry::get<0>(p); + calc_t y2 = geometry::get<1>(segment_b) - geometry::get<1>(p); + + calc_t ax = (std::min)(math::abs(x1), math::abs(x2)); + calc_t ay = (std::min)(math::abs(y1), math::abs(y2)); + + int s1 = 0, s2 = 0; + if (ax >= ay) + { + s1 = x1 > 0 ? 1 : -1; + s2 = x2 > 0 ? 1 : -1; + } + else + { + s1 = y1 > 0 ? 1 : -1; + s2 = y2 > 0 ? 1 : -1; + } + + return s1 == s2 ? -1 : 1; + } +}; + // Gives sense of direction for point p, collinear w.r.t. segment (a,b) // Returns -1 if p goes backward w.r.t (a,b), so goes from b in direction of a // Returns 1 if p goes forward, so extends (a,b) @@ -44,28 +94,9 @@ inline int sign_of_difference(Point1 const& point1, Point2 const& point2) // Note that it does not do any collinearity test, that should be done before template inline int direction_code(Point1 const& segment_a, Point1 const& segment_b, - const Point2& p) + Point2 const& p) { - // Suppose segment = (4 3,4 4) and p =(4 2) - // Then sign_a1 = 1 and sign_p1 = 1 -> goes backward -> return -1 - - int const sign_a0 = sign_of_difference<0>(segment_b, segment_a); - int const sign_a1 = sign_of_difference<1>(segment_b, segment_a); - - if (sign_a0 == 0 && sign_a1 == 0) - { - return 0; - } - - int const sign_p0 = sign_of_difference<0>(segment_b, p); - int const sign_p1 = sign_of_difference<1>(segment_b, p); - - if (sign_p0 == 0 && sign_p1 == 0) - { - return 0; - } - - return sign_a0 == sign_p0 && sign_a1 == sign_p1 ? -1 : 1; + return direction_code_impl::apply(segment_a, segment_b, p); } @@ -73,7 +104,6 @@ inline int direction_code(Point1 const& segment_a, Point1 const& segment_b, #endif //DOXYGEN_NO_DETAIL - }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DIRECITON_CODE_HPP diff --git a/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp b/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp index 9a48ec8b1..b8ea5e30e 100644 --- a/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp +++ b/include/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp @@ -5,10 +5,11 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2013-2015 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015 Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017 Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -24,8 +25,7 @@ #include #include #include -#include -#include + namespace boost { namespace geometry { @@ -35,64 +35,25 @@ namespace boost { namespace geometry namespace detail { -template ::type> -struct collinear_point_is_spike_or_equal +template +inline bool collinear_point_is_spike_or_equal(Point1 const& last_point, + Point2 const& segment_a, + Point3 const& segment_b) { -/*}; - -template -struct collinear_point_is_spike_or_equal -{*/ - template - static inline bool apply(Point1 const& last_point, - Point2 const& segment_a, - Point3 const& segment_b) + // Check if segment is equal + int const sgn_x1 = sign_of_difference<0>(last_point, segment_b); + int const sgn_y1 = sign_of_difference<1>(last_point, segment_b); + if (sgn_x1 == 0 && sgn_y1 == 0) { - // Check if segment is equal - int const sgn_x1 = sign_of_difference<0>(last_point, segment_b); - int const sgn_y1 = sign_of_difference<1>(last_point, segment_b); - if (sgn_x1 == 0 && sgn_y1 == 0) - { - return true; - } - - // Check if segment moves forward - int const sgn_x2 = sign_of_difference<0>(segment_b, segment_a); - int const sgn_y2 = sign_of_difference<1>(segment_b, segment_a); - - return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; + return true; } -}; -/* -template -struct collinear_point_is_spike_or_equal -{ - template - static inline bool apply(Point1 const& last_point, - Point2 const& segment_a, - Point3 const& segment_b) - { - // Check if segment is equal - - bool const is_equal_x1 = math::equals(geometry::get<0>(last_point), geometry::get<0>(segment_b)); - bool const is_equal_y1 = math::equals(geometry::get<1>(last_point), geometry::get<1>(segment_b)); - if (is_equal_x1 && is_equal_y1) - { - return true; - } - typedef typename select_coordinate_type::type coord_t1; - coord_t1 diff_x1 = longitude_distance_signed<> - typename coordinate_system::type::units - + // Check if segment moves forward + int const sgn_x2 = sign_of_difference<0>(segment_b, segment_a); + int const sgn_y2 = sign_of_difference<1>(segment_b, segment_a); - // Check if segment moves forward - int const sgn_x2 = sign_of_difference<0>(segment_b, segment_a); - int const sgn_y2 = sign_of_difference<1>(segment_b, segment_a); - - return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; - } -};*/ + return sgn_x1 != sgn_x2 || sgn_y1 != sgn_y2; +} // Checks if a point ("last_point") causes a spike w.r.t. // the specified two other points (segment_a, segment_b) @@ -109,17 +70,24 @@ template typename Point1, typename Point2, typename Point3, typename SideStrategy > -static inline bool point_is_spike_or_equal(Point1 const& last_point, - Point2 const& segment_a, - Point3 const& segment_b, - SideStrategy const& strategy) +static inline bool point_is_spike_or_equal(Point1 const& last_point, // prev | back + Point2 const& segment_a, // next | back - 2 + Point3 const& segment_b, // curr | back - 1 | spike's vertex + SideStrategy const& strategy) { int const side = strategy.apply(segment_a, segment_b, last_point); if (side == 0) { // Last point is collinear w.r.t previous segment. - return collinear_point_is_spike_or_equal - ::apply(last_point, segment_a, segment_b); +#ifdef BOOST_GEOMETRY_ENABLE_POINT_IS_SPIKE_OR_EQUAL_TEST + bool r1 = collinear_point_is_spike_or_equal(last_point, segment_a, segment_b); + bool r2 = direction_code(segment_a, segment_b, last_point) < 1; + if (r1 != r2) + std::cout << "spike detection failure with: " << r1 << " " << r2 << std::endl; + return r2; +#else + return direction_code(segment_a, segment_b, last_point) < 1; +#endif } return false; } From 8e100074e2b42f427767e02649a0f61a476d19d5 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 18 Jul 2017 14:39:03 +0200 Subject: [PATCH 035/188] [test][union] Update test case according to the recent fix. --- test/algorithms/set_operations/union/union.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/algorithms/set_operations/union/union.cpp b/test/algorithms/set_operations/union/union.cpp index d36c842f4..a0e13f82d 100644 --- a/test/algorithms/set_operations/union/union.cpp +++ b/test/algorithms/set_operations/union/union.cpp @@ -398,9 +398,9 @@ void test_areal() // Robustness issues, followed out buffer-robustness-tests, test them also reverse #if ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS) test_one("buffer_rt_f", buffer_rt_f[0], buffer_rt_f[1], - 1, 0, if_typed(18, 23), 4.60853); + 1, 0, 23, 4.60853); test_one("buffer_rt_f_rev", buffer_rt_f[1], buffer_rt_f[0], - 1, 0, if_typed(18, 23), 4.60853); + 1, 0, 23, 4.60853); test_one("buffer_rt_g", buffer_rt_g[0], buffer_rt_g[1], 1, 0, if_typed(18, 17), 16.571); test_one("buffer_rt_g_rev", buffer_rt_g[1], buffer_rt_g[0], From 497e0556392f0617347b4bc908dc530d267b5782 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 19 Jul 2017 14:02:59 +0200 Subject: [PATCH 036/188] [overlay] don't check startable for determining has_traversed_turn, and also check for turns both going in opposite direction (so uu for int) This fixes case_recursive_boxes_63 (but not for difference yet) --- .../boost/geometry/algorithms/detail/overlay/overlay.hpp | 6 ++++-- test/algorithms/overlay/multi_overlay_cases.hpp | 7 +++++++ .../set_operations/difference/difference_multi.cpp | 6 ++++++ .../set_operations/intersection/intersection_multi.cpp | 2 ++ test/algorithms/set_operations/union/union_multi.cpp | 1 + 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 10829abd4..31851292b 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -150,7 +150,7 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C op_it->seg_id.ring_index ); - if (traversed || is_closed || ! op_it->enriched.startable) + if (traversed || is_closed) { turn_info_map[ring_id].has_traversed_turn = true; } @@ -171,7 +171,9 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C // For intersection, any uu - but not if it is a self-turn turn_info_map[ring_id].has_traversed_turn = true; } - else if (colocated_opp && ! colocated_target) + else if (colocated_opp + && ! colocated_target + && ! turn.both(opposite_operation)) { // For union, a turn colocated with ii and NOT with uu/ux // For intersection v.v. diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 436a24723..80e91b264 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1095,6 +1095,13 @@ static std::string case_recursive_boxes_62[2] = "MULTIPOLYGON(((2 0,1 0,1 1,2 2,2 1,4 1,3 0,2 0)),((2 2,1 2,1 1,0.5 0.5,1 0,0 0,0 5,4 5,4 4,3 3,4 3,4 2,2 2),(1 3,1 4,0 4,1 3)),((4 2,5 1,4 1,4 2)))", }; +static std::string case_recursive_boxes_63[2] = +{ + // Derived from 62, needs not excluding startable points for checking rings for traversals + "MULTIPOLYGON(((2 0,1 0,1 1,2 2,2 1,4 1,3 0,2 0)),((2 2,1 2,1 1,0.5 0.5,1 0,0 0,0 5,4 5,4 4,3 3,4 3,4 2,2 2),(1 3,1 4,0 4,1 3)),((4 2,5 1,4 1,4 2)))", + "MULTIPOLYGON(((-1 -1, 6 -1, 6 6, -1 6, -1 -1), (0 0, 0 1, 0 3, 0 4, 1 4, 1 5, 2 4, 2 5, 3 4, 3 5, 4 5, 4 4, 5 4, 5 3, 4 3, 4 2, 5 2, 5 0, 3 0, 1 0, 0 0)),((2.5 3.5, 3 4, 2 4, 2.5 3.5)), ((3 3, 4 3, 4 4, 3 4, 3 3)), ((1 3, 2 4, 1 4, 1 3)), ((0.5 2.5, 1 3, 0 3, 0.5 2.5)), ((1 1, 2 1, 3 2, 2 2, 2 3, 1 3, 1 1)), ((3 0, 4 1, 3 1, 3 0)), ((1 0, 1 1, 0 1, 1 0)))", +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 79c72e1f6..e6111884c 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -356,6 +356,12 @@ void test_areal() TEST_DIFFERENCE(case_recursive_boxes_62, 5, 5.0, 11, 5.75, 12); #endif +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_63, 9, 10.5, 5, 27.75, 4); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_63, 6, 10.5, 5, 27.75, 2); +#endif + { ut_settings sym_settings; #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 1fa558894..892db5591 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -308,6 +308,8 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_61, 2, -1, 20.0); TEST_INTERSECTION(case_recursive_boxes_62, 9, -1, 10.5); + TEST_INTERSECTION(case_recursive_boxes_63, 11, -1, 5.75); + test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], 2, 10, 6.0); // Area from SQL Server diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index ee6d9c29c..d21df48be 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -384,6 +384,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_60, 3, 0, -1, 20.5); TEST_UNION(case_recursive_boxes_61, 1, 1, -1, 23.5); TEST_UNION(case_recursive_boxes_62, 2, 3, -1, 21.25); + TEST_UNION(case_recursive_boxes_63, 2, 3, -1, 44.0); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 174ef5e38c352a2a6d34893d3c59bba3d1b5945b Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 19 Jul 2017 18:59:55 +0200 Subject: [PATCH 037/188] [overlay] fix cases where two arcs leave a cluster in same direction, one longer as the other. Remaining distance needs to be considered. Including testcase case_recursive_boxes_64 which is fixed by this issue. --- .../geometry/algorithms/detail/overlay/traversal.hpp | 12 ++++++++++-- test/algorithms/overlay/multi_overlay_cases.hpp | 7 +++++++ .../set_operations/difference/difference_multi.cpp | 1 + .../intersection/intersection_multi.cpp | 5 +++++ test/algorithms/set_operations/union/union_multi.cpp | 1 + 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index ccb5d0e1f..5ef26f656 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -537,6 +537,9 @@ struct traversal if (selected_rank > 0) { + typename turn_operation_type::comparable_distance_type + min_remaining_distance = 0; + std::size_t selected_index = sbs.m_ranked_points.size(); for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++) { @@ -554,8 +557,13 @@ struct traversal continue; } - // Take the last turn from this rank - selected_index = i; + // Take turn with the smallest remaining distance + if (selected_index == sbs.m_ranked_points.size() + || ranked_op.remaining_distance < min_remaining_distance) + { + selected_index = i; + min_remaining_distance = ranked_op.remaining_distance; + } } } diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 80e91b264..8508854ac 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1102,6 +1102,13 @@ static std::string case_recursive_boxes_63[2] = "MULTIPOLYGON(((-1 -1, 6 -1, 6 6, -1 6, -1 -1), (0 0, 0 1, 0 3, 0 4, 1 4, 1 5, 2 4, 2 5, 3 4, 3 5, 4 5, 4 4, 5 4, 5 3, 4 3, 4 2, 5 2, 5 0, 3 0, 1 0, 0 0)),((2.5 3.5, 3 4, 2 4, 2.5 3.5)), ((3 3, 4 3, 4 4, 3 4, 3 3)), ((1 3, 2 4, 1 4, 1 3)), ((0.5 2.5, 1 3, 0 3, 0.5 2.5)), ((1 1, 2 1, 3 2, 2 2, 2 3, 1 3, 1 1)), ((3 0, 4 1, 3 1, 3 0)), ((1 0, 1 1, 0 1, 1 0)))", }; +static std::string case_recursive_boxes_64[2] = +{ + // Needs considering remaining_distance in clusters + "MULTIPOLYGON(((3 4,3 5,5 5,5 4,4 4,3 3,3.5 2.5,4 3,5 3,5 1,4 0,0 0,0 5,2 5,2 4,3 4),(2 2,3 2,3 3,2 2),(2 3,2 4,1 4,2 3),(2 0,2 1,1 1,2 0),(1 2,0 2,0.5 1.5,1 2)))", + "MULTIPOLYGON(((3 5,5 5,5 0,0 0,1 1,0 1,0 3,1 3,1 4,1.5 3.5,2 4,2 5,3 5),(2 2,1 2,2 1,2 2),(2 2,2.5 1.5,3 2,2 2),(4 4,4.5 3.5,5 4,4 4),(4 1,3 1,4 0,4 1)),((2 5,0 3,0 5,2 5)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index e6111884c..d9f2e13cf 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -361,6 +361,7 @@ void test_areal() #else TEST_DIFFERENCE_IGNORE(case_recursive_boxes_63, 6, 10.5, 5, 27.75, 2); #endif + TEST_DIFFERENCE(case_recursive_boxes_64, 6, 2.75, 7, 4.5, 11); { ut_settings sym_settings; diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 892db5591..20b23504a 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -309,6 +309,11 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_62, 9, -1, 10.5); TEST_INTERSECTION(case_recursive_boxes_63, 11, -1, 5.75); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_INTERSECTION(case_recursive_boxes_64, 5, -1, 17.25); +#else + TEST_INTERSECTION_IGNORE(case_recursive_boxes_64, 3, -1, 17.25); +#endif test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index d21df48be..0e64e7afe 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -385,6 +385,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_61, 1, 1, -1, 23.5); TEST_UNION(case_recursive_boxes_62, 2, 3, -1, 21.25); TEST_UNION(case_recursive_boxes_63, 2, 3, -1, 44.0); + TEST_UNION(case_recursive_boxes_64, 1, 2, -1, 24.5); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 982d4936fa4dc0a616664160cbed8944ee76ea4c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 19 Jul 2017 19:00:41 +0200 Subject: [PATCH 038/188] [traverse] remove unused function --- .../algorithms/detail/overlay/traversal.hpp | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index 5ef26f656..7e89d7ffb 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -422,36 +422,6 @@ struct traversal return false; } - - inline bool all_operations_of_type(sort_by_side::rank_with_rings const& rwr, - operation_type op_type, - sort_by_side::direction_type dir) const - { - typedef std::set::const_iterator sit_type; - for (sit_type it = rwr.rings.begin(); it != rwr.rings.end(); ++it) - { - sort_by_side::ring_with_direction const& rwd = *it; - if (rwd.direction != dir) - { - return false; - } - turn_type const& turn = m_turns[rwd.turn_index]; - if (! turn.both(op_type)) - { - return false; - } - - // Check if this is not yet taken - turn_operation_type const& op = turn.operations[rwd.operation_index]; - if (op.visited.finalized()) - { - return false; - } - - } - return true; - } - inline bool analyze_cluster_intersection(signed_size_type& turn_index, int& op_index, sbs_type const& sbs) const { From 4082167be864f3b0e5460332524c692d824aac20 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 19 Jul 2017 19:03:24 +0200 Subject: [PATCH 039/188] [traverse] added local typedef and comments to aggregate_operations --- .../detail/overlay/aggregate_operations.hpp | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp index 106ecaad0..3c88e9be6 100644 --- a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp @@ -50,8 +50,12 @@ struct ring_with_direction struct rank_with_rings { + // Define a set having a ring, with its direction (from/to). Each ring + // arrive at / leaves a cluster only once. TODO: this is not true for + // invalid ring. The rank needs to be considered too. + typedef std::set container_type; std::size_t rank; - std::set rings; + container_type rings; rank_with_rings() : rank(0) @@ -60,7 +64,7 @@ struct rank_with_rings inline bool all_equal(direction_type dir_type) const { - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { if (it->direction != dir_type) @@ -83,7 +87,7 @@ struct rank_with_rings inline bool has_only(operation_type op) const { - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; @@ -100,7 +104,7 @@ struct rank_with_rings { bool has1 = false; bool has2 = false; - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; @@ -114,7 +118,7 @@ struct rank_with_rings inline bool is_isolated() const { - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; @@ -129,7 +133,7 @@ struct rank_with_rings inline bool has_unique_region_id() const { int region_id = -1; - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; @@ -148,7 +152,7 @@ struct rank_with_rings inline int region_id() const { int region_id = -1; - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; @@ -170,7 +174,7 @@ struct rank_with_rings typedef typename boost::range_value::type turn_type; typedef typename turn_type::turn_operation_type turn_operation_type; - for (std::set::const_iterator it = rings.begin(); + for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { const ring_with_direction& rwd = *it; From 0cd5f968cc5b19d6db6dbccb81c1d7b30933a05a Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 19 Jul 2017 19:05:07 +0200 Subject: [PATCH 040/188] [traverse] belongs to previous commit, remaining distance needs also to be calculated for ii turns etc, for clusters. --- .../algorithms/detail/overlay/enrich_intersection_points.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index c7b464cfa..9634d0820 100644 --- a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -239,10 +239,6 @@ inline void calculate_remaining_distance(Turns& turns) ++it) { turn_type& turn = *it; - if (! turn.both(detail::overlay::operation_continue)) - { - continue; - } op_type& op0 = turn.operations[0]; op_type& op1 = turn.operations[1]; From 6519f7a41131d9e31892e7531b36fa3f1889fbeb Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 20 Jul 2017 18:27:11 +0200 Subject: [PATCH 041/188] [strategies] Fix geographic area strategy for sphere (a==b), error being NaN result. --- .../geometry/strategies/geographic/area.hpp | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/include/boost/geometry/strategies/geographic/area.hpp b/include/boost/geometry/strategies/geographic/area.hpp index e1d3b09b5..44dc2e694 100644 --- a/include/boost/geometry/strategies/geographic/area.hpp +++ b/include/boost/geometry/strategies/geographic/area.hpp @@ -83,7 +83,7 @@ protected : CT const m_e2; // squared eccentricity CT const m_ep2; // squared second eccentricity CT const m_ep; // second eccentricity - CT const m_c2; // authalic radius + CT const m_c2; // squared authalic radius inline spheroid_constants(Spheroid const& spheroid) : m_spheroid(spheroid) @@ -92,12 +92,27 @@ protected : * (CT(2.0) - CT(formula::flattening(spheroid)))) , m_ep2(m_e2 / (CT(1.0) - m_e2)) , m_ep(math::sqrt(m_ep2)) - , m_c2((m_a2 / CT(2.0)) + - ((math::sqr(get_radius<2>(spheroid)) * boost::math::atanh(math::sqrt(m_e2))) - / (CT(2.0) * math::sqrt(m_e2)))) + , m_c2(authalic_radius(spheroid, m_a2, m_e2)) {} }; + static inline CT authalic_radius(Spheroid const& sph, CT const& a2, CT const& e2) + { + CT const c0 = 0; + + if (math::equals(e2, c0)) + { + return a2; + } + + CT const sqrt_e2 = math::sqrt(e2); + CT const c2 = 2; + + return (a2 / c2) + + ((math::sqr(get_radius<2>(sph)) * boost::math::atanh(sqrt_e2)) + / (c2 * sqrt_e2)); + } + struct area_sums { CT m_excess_sum; From 4f7e479d06fecd9c80831abf74b78749ad63dfee Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 20 Jul 2017 18:28:54 +0200 Subject: [PATCH 042/188] [test][area] Add test case passing sphere into geographic area strategy. --- test/algorithms/area/area_sph_geo.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/algorithms/area/area_sph_geo.cpp b/test/algorithms/area/area_sph_geo.cpp index 70185c1d8..9cde3b45a 100644 --- a/test/algorithms/area/area_sph_geo.cpp +++ b/test/algorithms/area/area_sph_geo.cpp @@ -389,6 +389,21 @@ void test_spherical_geo() // for select geography::STGeomFromText('POLYGON((4.892 52.373,4.23 52.08, // 4.479 51.930,5.119 52.093,4.892 52.373))',4326).STArea()/1000000.0 } + + { + bg::model::polygon geometry_sph; + std::string wkt = "POLYGON((0 0, 5 0, 5 5, 0 5, 0 0))"; + bg::read_wkt(wkt, geometry_sph); + + area = bg::area(geometry_sph, bg::strategy::area::spherical(6371228.0)); + BOOST_CHECK_CLOSE(area, 308932296103.83051, 0.0001); + + bg::model::polygon geometry_geo; + bg::read_wkt(wkt, geometry_geo); + + area = bg::area(geometry_geo, bg::strategy::area::geographic(bg::srs::spheroid(6371228.0, 6371228.0))); + BOOST_CHECK_CLOSE(area, 308932296103.82574, 0.001); + } } int test_main(int, char* []) From 925b715f4d60407baa8bd7cec840bc7a0d9f2208 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 21 Jul 2017 03:36:15 +0200 Subject: [PATCH 043/188] [algorithms] direction_code(): Add support for non-cartesian CSes. --- .../algorithms/detail/direction_code.hpp | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/include/boost/geometry/algorithms/detail/direction_code.hpp b/include/boost/geometry/algorithms/detail/direction_code.hpp index fa6c04409..c5c522110 100644 --- a/include/boost/geometry/algorithms/detail/direction_code.hpp +++ b/include/boost/geometry/algorithms/detail/direction_code.hpp @@ -19,6 +19,9 @@ #include #include #include +#include + +#include namespace boost { namespace geometry @@ -45,6 +48,12 @@ inline int sign_of_difference(Point1 const& point1, Point2 const& point2) template ::type> struct direction_code_impl +{ + BOOST_MPL_ASSERT_MSG((false), NOT_IMPLEMENTED_FOR_THIS_CS, (CSTag)); +}; + +template +struct direction_code_impl { template static inline int apply(Point1 const& segment_a, Point1 const& segment_b, @@ -87,6 +96,144 @@ struct direction_code_impl } }; +template +struct direction_code_impl +{ + template + static inline int apply(Point1 const& segment_a, Point1 const& segment_b, + Point2 const& p) + { + typedef typename coordinate_type::type coord1_t; + typedef typename coordinate_type::type coord2_t; + typedef typename coordinate_system::type::units units_t; + typedef typename coordinate_system::type::units units2_t; + BOOST_MPL_ASSERT_MSG((boost::is_same::value), + NOT_IMPLEMENTED_FOR_DIFFERENT_UNITS, + (units_t, units2_t)); + + typedef typename geometry::select_coordinate_type ::type calc_t; + typedef math::detail::constants_on_spheroid constants1; + typedef math::detail::constants_on_spheroid constants2; + typedef math::detail::constants_on_spheroid constants; + + coord1_t const a0 = geometry::get<0>(segment_a); + coord1_t const a1 = geometry::get<1>(segment_a); + coord1_t const b0 = geometry::get<0>(segment_b); + coord1_t const b1 = geometry::get<1>(segment_b); + coord2_t const p0 = geometry::get<0>(p); + coord2_t const p1 = geometry::get<1>(p); + coord1_t const pi_half1 = constants1::max_latitude(); + coord2_t const pi_half2 = constants2::max_latitude(); + calc_t const pi = constants::half_period(); + calc_t const pi_half = constants::max_latitude(); + calc_t const c0 = 0; + + if ( (math::equals(b0, a0) && math::equals(b1, a1)) + || (math::equals(b0, p0) && math::equals(b1, p1)) ) + { + return 0; + } + + bool const is_a_pole = math::equals(pi_half1, math::abs(a1)); + bool const is_b_pole = math::equals(pi_half1, math::abs(b1)); + bool const is_p_pole = math::equals(pi_half2, math::abs(p1)); + + if ( is_b_pole && ((is_a_pole && math::sign(b1) == math::sign(a1)) + || (is_p_pole && math::sign(b1) == math::sign(p1))) ) + { + return 0; + } + + // NOTE: as opposed to the implementation for cartesian CS + // here point b is the origin + + calc_t const dlon1 = math::longitude_distance_signed(b0, a0); + calc_t const dlon2 = math::longitude_distance_signed(b0, p0); + + bool is_antilon1 = false, is_antilon2 = false; + calc_t const dlat1 = latitude_distance_signed(b1, a1, dlon1, pi, is_antilon1); + calc_t const dlat2 = latitude_distance_signed(b1, p1, dlon2, pi, is_antilon2); + + calc_t mx = is_a_pole || is_b_pole || is_p_pole ? + c0 : + (std::min)(is_antilon1 ? c0 : math::abs(dlon1), + is_antilon2 ? c0 : math::abs(dlon2)); + calc_t my = (std::min)(math::abs(dlat1), + math::abs(dlat2)); + + int s1 = 0, s2 = 0; + if (mx >= my) + { + s1 = dlon1 > 0 ? 1 : -1; + s2 = dlon2 > 0 ? 1 : -1; + } + else + { + s1 = dlat1 > 0 ? 1 : -1; + s2 = dlat2 > 0 ? 1 : -1; + } + + return s1 == s2 ? -1 : 1; + } + + template + static inline T latitude_distance_signed(T const& lat1, T const& lat2, T const& lon_ds, T const& pi, bool & is_antilon) + { + T const c0 = 0; + + T res = lat2 - lat1; + + is_antilon = math::equals(math::abs(lon_ds), pi); + if (is_antilon) + { + res = lat2 + lat1; + if (res >= c0) + res = pi - res; + else + res = -pi - res; + } + + return res; + } +}; + +template +struct direction_code_impl +{ + template + static inline int apply(Point1 segment_a, Point1 segment_b, + Point2 p) + { + typedef math::detail::constants_on_spheroid + < + typename coordinate_type::type, + typename coordinate_system::type::units + > constants1; + typedef math::detail::constants_on_spheroid + < + typename coordinate_type::type, + typename coordinate_system::type::units + > constants2; + + geometry::set<1>(segment_a, + constants1::max_latitude() - geometry::get<1>(segment_a)); + geometry::set<1>(segment_b, + constants1::max_latitude() - geometry::get<1>(segment_b)); + geometry::set<1>(p, + constants2::max_latitude() - geometry::get<1>(p)); + + return direction_code_impl + < + Point, spherical_equatorial_tag + >::apply(segment_a, segment_b, p); + } +}; + +template +struct direction_code_impl + : direction_code_impl +{}; + // Gives sense of direction for point p, collinear w.r.t. segment (a,b) // Returns -1 if p goes backward w.r.t (a,b), so goes from b in direction of a // Returns 1 if p goes forward, so extends (a,b) From d25fe47a93e775258078638e543daebc1c6a8481 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 26 Jul 2017 14:26:44 +0200 Subject: [PATCH 044/188] [overlay] fix case which was not taking correct rank --- .../algorithms/detail/overlay/traversal.hpp | 1 + .../traversal_intersection_patterns.hpp | 83 +++++++++++++++++++ .../overlay/multi_overlay_cases.hpp | 7 ++ 3 files changed, 91 insertions(+) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index 7e89d7ffb..932c0dd48 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -437,6 +437,7 @@ struct traversal || intersection_pattern_common_interior2(selected_rank, aggregation) || intersection_pattern_common_interior3(selected_rank, aggregation) || intersection_pattern_common_interior4(selected_rank, aggregation) + || intersection_pattern_common_interior5(selected_rank, aggregation) ; if (! detected) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp index ca35c423c..2e7b36f6f 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp @@ -298,6 +298,89 @@ inline bool intersection_pattern_common_interior4(std::size_t& selected_rank, return false; } +inline bool intersection_pattern_common_interior5(std::size_t& selected_rank, + std::vector const& aggregation) +{ + // Pattern: isolated regions + + // See #case_recursive_boxes_65 + + // INCOMING: + // Rank 0 {19[0] (s:0, r:2, m:0) i F rgn: 4 ISO} + + // Rank 1 {19[1] (s:1, m:0) i T rgn: 1 ISO FIN ->18 (2.5)} + // Rank 2 {21[1] (s:1, m:2) i F rgn: 1 ISO} + // Rank 3 {21[1] (s:1, m:2) i T rgn: 1 ISO ->17 (2)} + // Rank 4 {19[1] (s:1, m:0) i F rgn: 1 ISO FIN} + + // LEAVING (take this one): + // Rank 5 {19[0] (s:0, r:2, m:0) i T rgn: 4 ISO ->22 (1)} + + std::size_t const n = aggregation.size(); + if (n < 3) + { + return false; + } + + sort_by_side::rank_with_rings const& incoming = aggregation.front(); + sort_by_side::rank_with_rings const& outgoing = aggregation.back(); + + bool const incoming_ok = + incoming.all_from() + && incoming.has_unique_region_id() + && incoming.is_isolated(); + + if (! incoming_ok) + { + return false; + } + + const int incoming_region_id = incoming.region_id(); + + bool const outgoing_ok = + outgoing.all_to() + && outgoing.has_unique_region_id() + && outgoing.is_isolated() + && outgoing.region_id() == incoming_region_id; + + if (! outgoing_ok) + { + return false; + } + + selected_rank = n - 1; + bool other_region = true; + + // Assumed is that other regions go (T) and come back (F) + for (std::size_t i = 1; i < n - 1; i++) + { + sort_by_side::rank_with_rings const& rwr = aggregation[i]; + if (! rwr.has_unique_region_id() || ! rwr.is_isolated()) + { + return false; + } + const int region_id = rwr.region_id(); + if (other_region && region_id != incoming_region_id) + { + // OK + } + else if (other_region && region_id == incoming_region_id) + { + // OK, next phase (same region as incoming region) + selected_rank = i; + other_region = false; + } + else if (! other_region && region_id != incoming_region_id) + { + // After that the region is the same is incoming, it should + // stay like that + return false; + } + } + + return true; +} + }} // namespace detail::overlay #endif // DOXYGEN_NO_DETAIL diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 8508854ac..3bc8e3200 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1109,6 +1109,13 @@ static std::string case_recursive_boxes_64[2] = "MULTIPOLYGON(((3 5,5 5,5 0,0 0,1 1,0 1,0 3,1 3,1 4,1.5 3.5,2 4,2 5,3 5),(2 2,1 2,2 1,2 2),(2 2,2.5 1.5,3 2,2 2),(4 4,4.5 3.5,5 4,4 4),(4 1,3 1,4 0,4 1)),((2 5,0 3,0 5,2 5)))" }; +static std::string case_recursive_boxes_65[2] = +{ + // Misses large hole in intersection + "MULTIPOLYGON(((3 5,3.5 4.5,4 5,5 5,5 1,4 0,3 0,3 1,2 0,0 0,0 4,1 4,1 5,3 5),(2 4,2 3,3 4,2 4),(2 1,2 2,1.5 1.5,2 1),(3 3,4 2,4 3,3 3)))", + "MULTIPOLYGON(((3 5,4 5,4 4,5 5,5 2,4 2,3.5 1.5,5 0,1 0,1 1,0 1,0 5,3 5),(2 2,3 2,4 3,1 3,1 2,2 2)),((1 0,0 0,0 1,1 0)),((4 1,4 2,5 1,4 1)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", From 664c1aa10a0e849642e326efe8e3a15162709386 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 26 Jul 2017 14:33:26 +0200 Subject: [PATCH 045/188] [test] add test cases for last fix --- .../set_operations/difference/difference_multi.cpp | 7 +++++++ .../set_operations/intersection/intersection_multi.cpp | 1 + test/algorithms/set_operations/union/union_multi.cpp | 1 + 3 files changed, 9 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index d9f2e13cf..9dcbc6f36 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -361,8 +361,15 @@ void test_areal() #else TEST_DIFFERENCE_IGNORE(case_recursive_boxes_63, 6, 10.5, 5, 27.75, 2); #endif + TEST_DIFFERENCE(case_recursive_boxes_64, 6, 2.75, 7, 4.5, 11); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_65, 6, 4.25, 7, 3.0, 13); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_65, 4, 4.25, 7, 3.0, 11); +#endif + { ut_settings sym_settings; #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 20b23504a..d49f07eac 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -314,6 +314,7 @@ void test_areal() #else TEST_INTERSECTION_IGNORE(case_recursive_boxes_64, 3, -1, 17.25); #endif + TEST_INTERSECTION_IGNORE(case_recursive_boxes_65, 3, -1, 17.25); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 0e64e7afe..f05715f69 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -386,6 +386,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_62, 2, 3, -1, 21.25); TEST_UNION(case_recursive_boxes_63, 2, 3, -1, 44.0); TEST_UNION(case_recursive_boxes_64, 1, 2, -1, 24.5); + TEST_UNION(case_recursive_boxes_65, 1, 1, -1, 24.5); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From e7463b35f013e3a3b3ead03e9e65d6b96a8adafb Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 27 Jul 2017 03:31:35 +0200 Subject: [PATCH 046/188] [buffer][overlay] Fix get_turns/secionalize/partition for non-cartesian CS. - Handle longitudes differently in section_functions preceding() and exceeding(). - Calculate directions in sectionalize differently for longitudes. - Update buffer and get_turns due to the above changes. --- .../detail/buffer/get_piece_turns.hpp | 29 +++--- .../algorithms/detail/overlay/get_turns.hpp | 8 +- .../detail/sections/section_functions.hpp | 96 ++++++++++++++++--- .../detail/sections/sectionalize.hpp | 70 ++++++++++++-- 4 files changed, 166 insertions(+), 37 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp index 94dfddd44..5c012e715 100644 --- a/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp @@ -127,26 +127,31 @@ class piece_turn_visitor template inline void move_begin_iterator(Iterator& it_begin, Iterator it_beyond, - signed_size_type& index, int dir, Box const& other_bounding_box) + signed_size_type& index, int dir, + Box const& this_bounding_box, + Box const& other_bounding_box) { for(; it_begin != it_beyond && it_begin + 1 != it_beyond && detail::section::preceding(dir, *(it_begin + 1), - other_bounding_box, m_robust_policy); + this_bounding_box, + other_bounding_box, + m_robust_policy); ++it_begin, index++) {} } template inline void move_end_iterator(Iterator it_begin, Iterator& it_beyond, - int dir, Box const& other_bounding_box) + int dir, Box const& this_bounding_box, + Box const& other_bounding_box) { while (it_beyond != it_begin && it_beyond - 1 != it_begin && it_beyond - 2 != it_begin) { if (detail::section::exceeding(dir, *(it_beyond - 2), - other_bounding_box, m_robust_policy)) + this_bounding_box, other_bounding_box, m_robust_policy)) { --it_beyond; } @@ -192,23 +197,23 @@ class piece_turn_visitor // Set begin/end of monotonic ranges, in both x/y directions signed_size_type index1 = sec1_first_index; move_begin_iterator<0>(it1_first, it1_beyond, index1, - section1.directions[0], section2.bounding_box); + section1.directions[0], section1.bounding_box, section2.bounding_box); move_end_iterator<0>(it1_first, it1_beyond, - section1.directions[0], section2.bounding_box); + section1.directions[0], section1.bounding_box, section2.bounding_box); move_begin_iterator<1>(it1_first, it1_beyond, index1, - section1.directions[1], section2.bounding_box); + section1.directions[1], section1.bounding_box, section2.bounding_box); move_end_iterator<1>(it1_first, it1_beyond, - section1.directions[1], section2.bounding_box); + section1.directions[1], section1.bounding_box, section2.bounding_box); signed_size_type index2 = sec2_first_index; move_begin_iterator<0>(it2_first, it2_beyond, index2, - section2.directions[0], section1.bounding_box); + section2.directions[0], section2.bounding_box, section1.bounding_box); move_end_iterator<0>(it2_first, it2_beyond, - section2.directions[0], section1.bounding_box); + section2.directions[0], section2.bounding_box, section1.bounding_box); move_begin_iterator<1>(it2_first, it2_beyond, index2, - section2.directions[1], section1.bounding_box); + section2.directions[1], section2.bounding_box, section1.bounding_box); move_end_iterator<1>(it2_first, it2_beyond, - section2.directions[1], section1.bounding_box); + section2.directions[1], section2.bounding_box, section1.bounding_box); turn_type the_model; the_model.operations[0].piece_index = piece1.index; diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp index 6b6458dd7..f88dfe842 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -233,7 +233,7 @@ public : // section 2: [--------------] // section 1: |----|---|---|---|---| for (prev1 = it1++, next1++; - it1 != end1 && ! detail::section::exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); + it1 != end1 && ! detail::section::exceeding<0>(dir1, *prev1, sec1.bounding_box, sec2.bounding_box, robust_policy); ++prev1, ++it1, ++index1, ++next1, ++ndi1) { ever_circling_iterator nd_next1( @@ -251,7 +251,7 @@ public : next2++; for (prev2 = it2++, next2++; - it2 != end2 && ! detail::section::exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); + it2 != end2 && ! detail::section::exceeding<0>(dir2, *prev2, sec2.bounding_box, sec1.bounding_box, robust_policy); ++prev2, ++it2, ++index2, ++next2, ++ndi2) { bool skip = same_source; @@ -359,7 +359,7 @@ private : // skips to the begin-point, we loose the index or have to recalculate it) // So we mimic it here template - static inline void get_start_point_iterator(Section & section, + static inline void get_start_point_iterator(Section const& section, Range const& range, typename boost::range_iterator::type& it, typename boost::range_iterator::type& prev, @@ -373,7 +373,7 @@ private : // Mimic section-iterator: // Skip to point such that section interects other box prev = it++; - for(; it != end && detail::section::preceding<0>(dir, *it, other_bounding_box, robust_policy); + for(; it != end && detail::section::preceding<0>(dir, *it, section.bounding_box, other_bounding_box, robust_policy); prev = it++, index++, ndi++) {} // Go back one step because we want to start completely preceding diff --git a/include/boost/geometry/algorithms/detail/sections/section_functions.hpp b/include/boost/geometry/algorithms/detail/sections/section_functions.hpp index 7bc5c0804..67df3060c 100644 --- a/include/boost/geometry/algorithms/detail/sections/section_functions.hpp +++ b/include/boost/geometry/algorithms/detail/sections/section_functions.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -27,6 +27,74 @@ namespace boost { namespace geometry namespace detail { namespace section { +template +< + std::size_t Dimension, + typename Geometry, + typename CastedCSTag = typename tag_cast + < + typename cs_tag::type, + spherical_tag + >::type +> +struct preceding_check +{ + template + static inline bool apply(int dir, Point const& point, Box const& /*point_box*/, Box const& other_box) + { + return (dir == 1 && get(point) < get(other_box)) + || (dir == -1 && get(point) > get(other_box)); + } +}; + +template +struct preceding_check<0, Geometry, spherical_tag> +{ + template + static inline bool apply(int dir, Point const& point, Box const& point_box, Box const& other_box) + { + typedef typename select_coordinate_type + < + Point, Box + >::type calc_t; + typedef typename coordinate_system::type::units units_t; + + calc_t const c0 = 0; + + if (dir == 1) + { + calc_t const diff_min = math::longitude_distance_signed + < + units_t, calc_t + >(get(other_box), get<0>(point)); + + calc_t const diff_min_min = math::longitude_distance_signed + < + units_t, calc_t + >(get(other_box), get(point_box)); + + return diff_min < c0 && diff_min_min <= c0 && diff_min_min <= diff_min; + } + else if (dir == -1) + { + calc_t const diff_max = math::longitude_distance_signed + < + units_t, calc_t + >(get(other_box), get<0>(point)); + + calc_t const diff_max_max = math::longitude_distance_signed + < + units_t, calc_t + >(get(other_box), get(point_box)); + + return diff_max > c0 && diff_max_max >= c0 && diff_max <= diff_max_max; + } + + return false; + } +}; + + template < std::size_t Dimension, @@ -34,14 +102,15 @@ template typename RobustBox, typename RobustPolicy > -static inline bool preceding(int dir, Point const& point, - RobustBox const& robust_box, - RobustPolicy const& robust_policy) +static inline bool preceding(int dir, + Point const& point, + RobustBox const& point_robust_box, + RobustBox const& other_robust_box, + RobustPolicy const& robust_policy) { typename geometry::robust_point_type::type robust_point; geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get(robust_point) < get(robust_box)) - || (dir == -1 && get(robust_point) > get(robust_box)); + return preceding_check::apply(dir, robust_point, point_robust_box, other_robust_box); } template @@ -51,14 +120,13 @@ template typename RobustBox, typename RobustPolicy > -static inline bool exceeding(int dir, Point const& point, - RobustBox const& robust_box, - RobustPolicy const& robust_policy) +static inline bool exceeding(int dir, + Point const& point, + RobustBox const& point_robust_box, + RobustBox const& other_robust_box, + RobustPolicy const& robust_policy) { - typename geometry::robust_point_type::type robust_point; - geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get(robust_point) > get(robust_box)) - || (dir == -1 && get(robust_point) < get(robust_box)); + return preceding(-dir, point, point_robust_box, other_robust_box, robust_policy); } diff --git a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp index ff56acec4..b4441d6be 100644 --- a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp +++ b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2013, 2014, 2015. -// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015, 2017. +// Modifications copyright (c) 2013-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -136,11 +136,21 @@ struct sections : std::vector > namespace detail { namespace sectionalize { +// NOTE: This utility will NOT work for latitudes, dimension 1 in spherical +// and geographic coordinate system because in these coordinate systems +// e.g. a segment on northern hemisphere may go towards greater latitude +// and then towards lesser latitude. template < + typename Point, typename DimensionVector, std::size_t Index, - std::size_t Count + std::size_t Count, + typename CastedCSTag = typename tag_cast + < + typename cs_tag::type, + spherical_tag + >::type > struct get_direction_loop { @@ -161,21 +171,67 @@ struct get_direction_loop get_direction_loop < + Point, DimensionVector, Index + 1, - Count + Count, + CastedCSTag >::apply(seg, directions); } }; -template -struct get_direction_loop +template +< + typename Point, + typename DimensionVector, + std::size_t Count +> +struct get_direction_loop +{ + typedef typename boost::mpl::at_c::type dimension; + + template + static inline void apply(Segment const& seg, + int directions[Count]) + { + typedef typename coordinate_type::type coordinate_type; + typedef typename coordinate_system::type::units units_t; + + coordinate_type const diff = math::longitude_distance_signed + < + units_t, coordinate_type + >(geometry::get<0, 0>(seg), + geometry::get<1, 0>(seg)); + + coordinate_type zero = coordinate_type(); + directions[0] = diff > zero ? 1 : diff < zero ? -1 : 0; + + get_direction_loop + < + Point, + DimensionVector, + 1, + Count, + spherical_tag + >::apply(seg, directions); + } +}; + +template +< + typename Point, + typename DimensionVector, + std::size_t Count, + typename CastedCSTag +> +struct get_direction_loop { template static inline void apply(Segment const&, int [Count]) {} }; + //! Copy one static array to another template struct copy_loop @@ -410,7 +466,7 @@ struct sectionalize_part int direction_classes[dimension_count] = {0}; get_direction_loop < - DimensionVector, 0, dimension_count + Point, DimensionVector, 0, dimension_count >::apply(robust_segment, direction_classes); // if "dir" == 0 for all point-dimensions, it is duplicate. From ea8c93478fd64727b0adf2ffa0a8ccac155d1763 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 27 Jul 2017 03:40:21 +0200 Subject: [PATCH 047/188] [test][is_simple] Add test for geographic CS. --- test/algorithms/Jamfile.v2 | 1 + test/algorithms/is_simple.cpp | 96 +---------------------- test/algorithms/is_simple_geo.cpp | 59 ++++++++++++++ test/algorithms/test_is_simple.hpp | 121 +++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 95 deletions(-) create mode 100644 test/algorithms/is_simple_geo.cpp create mode 100644 test/algorithms/test_is_simple.hpp diff --git a/test/algorithms/Jamfile.v2 b/test/algorithms/Jamfile.v2 index cb01090ef..70d9688b0 100644 --- a/test/algorithms/Jamfile.v2 +++ b/test/algorithms/Jamfile.v2 @@ -33,6 +33,7 @@ test-suite boost-geometry-algorithms [ run is_convex.cpp : : : : algorithms_is_convex ] [ run is_empty.cpp : : : : algorithms_is_empty ] [ run is_simple.cpp : : : : algorithms_is_simple ] + [ run is_simple_geo.cpp : : : : algorithms_is_simple_geo ] [ run is_valid.cpp : : : : algorithms_is_valid ] [ run is_valid_failure.cpp : : : : algorithms_is_valid_failure ] [ run make.cpp : : : : algorithms_make ] diff --git a/test/algorithms/is_simple.cpp b/test/algorithms/is_simple.cpp index a61ba6216..41cef5ed5 100644 --- a/test/algorithms/is_simple.cpp +++ b/test/algorithms/is_simple.cpp @@ -13,36 +13,7 @@ #define BOOST_TEST_MODULE test_is_simple #endif -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include - -#ifdef BOOST_GEOMETRY_TEST_DEBUG -#include "pretty_print_geometry.hpp" -#endif +#include "test_is_simple.hpp" namespace bg = ::boost::geometry; @@ -61,71 +32,6 @@ typedef bg::model::multi_polygon multi_polygon_type; typedef bg::model::box box_type; -//---------------------------------------------------------------------------- - - -template -void test_simple(Geometry const& geometry, bool expected_result, - bool check_validity = true) -{ -#ifdef BOOST_GEOMETRY_TEST_DEBUG - std::cout << "=======" << std::endl; -#endif - - bool simple = bg::is_simple(geometry); - - BOOST_ASSERT( ! check_validity || bg::is_valid(geometry) ); - BOOST_CHECK_MESSAGE( simple == expected_result, - "Expected: " << expected_result - << " detected: " << simple - << " wkt: " << bg::wkt(geometry) ); - - typedef typename bg::strategy::intersection::services::default_strategy - < - CSTag - >::type strategy_type; - - bool simple_s = bg::is_simple(geometry, strategy_type()); - - BOOST_CHECK_EQUAL(simple, simple_s); - -#ifdef BOOST_GEOMETRY_TEST_DEBUG - std::cout << "Geometry: "; - pretty_print_geometry::apply(std::cout, geometry); - std::cout << std::endl; - std::cout << std::boolalpha; - std::cout << "is simple: " << simple << std::endl; - std::cout << "expected result: " << expected_result << std::endl; - std::cout << "=======" << std::endl; - std::cout << std::endl << std::endl; - std::cout << std::noboolalpha; -#endif -} - - -template -void test_simple(Geometry const& geometry, - bool expected_result, - bool check_validity = true) -{ - typedef typename bg::cs_tag::type cs_tag; - test_simple(geometry, expected_result, check_validity); -} - -template -void test_simple(boost::variant const& variant_geometry, - bool expected_result, - bool check_validity = true) -{ - typedef typename bg::cs_tag::type cs_tag; - test_simple(variant_geometry, expected_result, check_validity); -} - - - -//---------------------------------------------------------------------------- - - BOOST_AUTO_TEST_CASE( test_is_simple_point ) { #ifdef BOOST_GEOMETRY_TEST_DEBUG diff --git a/test/algorithms/is_simple_geo.cpp b/test/algorithms/is_simple_geo.cpp new file mode 100644 index 000000000..30a01a30c --- /dev/null +++ b/test/algorithms/is_simple_geo.cpp @@ -0,0 +1,59 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2014-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_TEST_MODULE +#define BOOST_TEST_MODULE test_is_simple_geo +#endif + +#include "test_is_simple.hpp" + + +typedef bg::model::point > point_type; +typedef bg::model::segment segment_type; +typedef bg::model::linestring linestring_type; +typedef bg::model::multi_linestring multi_linestring_type; +// ccw open and closed polygons +typedef bg::model::polygon open_ccw_polygon_type; +typedef bg::model::polygon closed_ccw_polygon_type; +// multi-geometries +typedef bg::model::multi_point multi_point_type; +typedef bg::model::multi_polygon multi_polygon_type; +// box +typedef bg::model::box box_type; + + +BOOST_AUTO_TEST_CASE( test_is_simple_geo_linestring ) +{ + typedef linestring_type G; + + bg::strategy::intersection::geographic_segments<> s; + + test_simple_s(from_wkt("LINESTRING(0 0, -90 0, 90 0)"), s, true); + test_simple_s(from_wkt("LINESTRING(0 90, -90 0, 90 0)"), s, false); + test_simple_s(from_wkt("LINESTRING(0 90, -90 50, 90 0)"), s, false); + test_simple_s(from_wkt("LINESTRING(0 90, -90 -50, 90 0)"), s, true); + + test_simple_s(from_wkt("LINESTRING(35 0, 110 36, 159 0, 82 30)"), s, false); + test_simple_s(from_wkt("LINESTRING(135 0, -150 36, -101 0, -178 30)"), s, false); + test_simple_s(from_wkt("LINESTRING(45 0, 120 36, 169 0, 92 30)"), s, false); + test_simple_s(from_wkt("LINESTRING(179 0, -179 1, -179 0, 179 1)"), s, false); +} + +BOOST_AUTO_TEST_CASE( test_is_simple_geo_multilinestring ) +{ + typedef multi_linestring_type G; + + bg::strategy::intersection::geographic_segments<> s; + + test_simple_s(from_wkt("MULTILINESTRING((35 0, 110 36),(159 0, 82 30))"), s, false); + test_simple_s(from_wkt("MULTILINESTRING((135 0, -150 36),(-101 0, -178 30))"), s, false); + test_simple_s(from_wkt("MULTILINESTRING((45 0, 120 36),(169 0, 92 30))"), s, false); + test_simple_s(from_wkt("MULTILINESTRING((179 0, -179 1),(-179 0, 179 1))"), s, false); +} diff --git a/test/algorithms/test_is_simple.hpp b/test/algorithms/test_is_simple.hpp new file mode 100644 index 000000000..d4162d04b --- /dev/null +++ b/test/algorithms/test_is_simple.hpp @@ -0,0 +1,121 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2014-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +#ifdef BOOST_GEOMETRY_TEST_DEBUG +#include "pretty_print_geometry.hpp" +#endif + +namespace bg = ::boost::geometry; + +template +void test_simple_s(Geometry const& geometry, + Strategy const& strategy, + bool expected_result, + bool check_validity = true) +{ + bool simple = bg::is_simple(geometry, strategy); + bool valid = ! check_validity || bg::is_valid(geometry, strategy); + + BOOST_CHECK_MESSAGE( valid == true, + "Expected valid geometry, " + << " wkt: " << bg::wkt(geometry) ); + + BOOST_CHECK_MESSAGE( simple == expected_result, + "Expected: " << expected_result + << " detected: " << simple + << " wkt: " << bg::wkt(geometry) ); +} + +template +void test_simple(Geometry const& geometry, bool expected_result, + bool check_validity = true) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "=======" << std::endl; +#endif + + bool simple = bg::is_simple(geometry); + bool valid = ! check_validity || bg::is_valid(geometry); + + BOOST_CHECK_MESSAGE( valid == true, + "Expected valid geometry, " + << " wkt: " << bg::wkt(geometry) ); + + BOOST_CHECK_MESSAGE( simple == expected_result, + "Expected: " << expected_result + << " detected: " << simple + << " wkt: " << bg::wkt(geometry) ); + + typedef typename bg::strategy::intersection::services::default_strategy + < + CSTag + >::type strategy_type; + + test_simple_s(geometry, strategy_type(), expected_result, check_validity); + +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "Geometry: "; + pretty_print_geometry::apply(std::cout, geometry); + std::cout << std::endl; + std::cout << std::boolalpha; + std::cout << "is simple: " << simple << std::endl; + std::cout << "expected result: " << expected_result << std::endl; + std::cout << "=======" << std::endl; + std::cout << std::endl << std::endl; + std::cout << std::noboolalpha; +#endif +} + +template +void test_simple(Geometry const& geometry, + bool expected_result, + bool check_validity = true) +{ + typedef typename bg::cs_tag::type cs_tag; + test_simple(geometry, expected_result, check_validity); +} + +template +void test_simple(boost::variant const& variant_geometry, + bool expected_result, + bool check_validity = true) +{ + typedef typename bg::cs_tag::type cs_tag; + test_simple(variant_geometry, expected_result, check_validity); +} From a35d0f756de77a708d958037105e71244cb850dc Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 30 Jul 2017 04:45:56 +0200 Subject: [PATCH 048/188] [test][buffer] Add MultiPoint test case (5 equal Points). --- test/algorithms/buffer/buffer_multi_point.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/algorithms/buffer/buffer_multi_point.cpp b/test/algorithms/buffer/buffer_multi_point.cpp index fb3b61fb6..3d0e94a07 100644 --- a/test/algorithms/buffer/buffer_multi_point.cpp +++ b/test/algorithms/buffer/buffer_multi_point.cpp @@ -24,6 +24,8 @@ static std::string const grid_a = "MULTIPOINT(5 0,6 0,7 0, 5 1,7 1, 0 13,8 13) static std::string const mysql_report_2015_02_25_1 = "MULTIPOINT(-9 19,9 -6,-4 4,16 -14,-3 16,14 9)"; static std::string const mysql_report_2015_02_25_2 = "MULTIPOINT(-2 11,-15 3,6 4,-14 0,20 -7,-17 -1)"; +static std::string const mysql_report_3 = "MULTIPOINT(0 0,0 0,0 0,0 0,0 0)"; + template void test_all() { @@ -78,6 +80,18 @@ void test_all() mysql_report_2015_02_25_1, join, end_flat, distance_strategy(6051788), side_strategy, bg::strategy::buffer::point_circle(800), 115057490003226.125, 1.0); + + { + multi_point_type g; + bg::read_wkt(mysql_report_3, g); + test_buffer("mysql_report_3", g, + bg::strategy::buffer::join_round(36), + bg::strategy::buffer::end_round(36), + distance_strategy(1), + side_strategy, + bg::strategy::buffer::point_circle(36), + true, 1, 0, 3.12566719800474635, 1.0, NULL); + } } template From 56b5d7bf8af4e6fe4f418eaa3dce5138977ca53e Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 31 Jul 2017 12:14:40 +0200 Subject: [PATCH 049/188] [overlay] fix bug where self-turn was not startable, and therefore an interior ring was missing --- .../algorithms/detail/overlay/handle_colocations.hpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index 9c0f190eb..8cc2880f1 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -751,7 +751,9 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, cinfo.open_count = sbs.open_count(for_operation); - // Unset the startable flag for all 'closed' zones + // Unset the startable flag for all 'closed' zones. This does not + // apply for self-turns, because those counts are not from both + // polygons for (std::size_t i = 0; i < sbs.m_ranked_points.size(); i++) { const typename sbs_type::rp& ranked = sbs.m_ranked_points[i]; @@ -773,6 +775,13 @@ inline void gather_cluster_properties(Clusters& clusters, Turns& turns, op.enriched.rank = ranked.rank; op.enriched.zone = ranked.zone; + if (OverlayType != overlay_difference + && is_self_turn(turn)) + { + // Difference needs the self-turns, TODO: investigate + continue; + } + if ((for_operation == operation_union && ranked.count_left != 0) || (for_operation == operation_intersection From 0938983c5e461a723acef230adfd2fa9bbba1a6c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 31 Jul 2017 12:16:26 +0200 Subject: [PATCH 050/188] [test] add testcase 66 for startable self-turns --- test/algorithms/overlay/multi_overlay_cases.hpp | 7 +++++++ .../set_operations/difference/difference_multi.cpp | 2 ++ .../set_operations/intersection/intersection_multi.cpp | 6 ++++++ test/algorithms/set_operations/union/union_multi.cpp | 1 + 4 files changed, 16 insertions(+) diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 3bc8e3200..5e820cd17 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1116,6 +1116,13 @@ static std::string case_recursive_boxes_65[2] = "MULTIPOLYGON(((3 5,4 5,4 4,5 5,5 2,4 2,3.5 1.5,5 0,1 0,1 1,0 1,0 5,3 5),(2 2,3 2,4 3,1 3,1 2,2 2)),((1 0,0 0,0 1,1 0)),((4 1,4 2,5 1,4 1)))" }; +static std::string case_recursive_boxes_66[2] = +{ + // Needs self-turns startable, at least not determined using count left/right for self-turns + "MULTIPOLYGON(((1 0,0 0,1 1,0 1,0 4,1 4,1 5,3 5,3 4,4 5,5 5,5 0,1 0),(3 3,4 2,4 3,3 3),(3 1,4 1,4 2,3 2,3 1),(3 3,3 4,2 3,3 3),(3 4,3.5 3.5,4 4,3 4)))", + "MULTIPOLYGON(((2 0,0 0,0 1,1 1,1 2,0 2,0 5,1 5,2 4,2 5,5 5,5 0,2 0),(3 1,3 2,2.5 1.5,3 1),(2 3,1 2,2 1,2 2,3 2,4 3,2 3),(0 3,0.5 2.5,1 3,1 4,0 3)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 9dcbc6f36..e17865445 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -370,6 +370,8 @@ void test_areal() TEST_DIFFERENCE_IGNORE(case_recursive_boxes_65, 4, 4.25, 7, 3.0, 11); #endif + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_66, 5, 4.75, 7, 4.0, 9); + { ut_settings sym_settings; #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index d49f07eac..9d0b33aa3 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -316,6 +316,12 @@ void test_areal() #endif TEST_INTERSECTION_IGNORE(case_recursive_boxes_65, 3, -1, 17.25); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_INTERSECTION(case_recursive_boxes_66, 4, -1, 16.0); +#else + TEST_INTERSECTION_IGNORE(case_recursive_boxes_66, 2, -1, 16.0); +#endif + test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], 2, 10, 6.0); // Area from SQL Server diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index f05715f69..517c51e1d 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -387,6 +387,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_63, 2, 3, -1, 44.0); TEST_UNION(case_recursive_boxes_64, 1, 2, -1, 24.5); TEST_UNION(case_recursive_boxes_65, 1, 1, -1, 24.5); + TEST_UNION(case_recursive_boxes_66, 1, 1, -1, 24.75); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From e7ae7454b7546dbcc6ccaf9b5cad4ece1b783d71 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 1 Aug 2017 01:41:16 +0200 Subject: [PATCH 051/188] [doc][index] Remove images of queries using higher-level geometries. --- doc/index/rtree/query.qbk | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/index/rtree/query.qbk b/doc/index/rtree/query.qbk index 75f40ec29..a2fa49594 100644 --- a/doc/index/rtree/query.qbk +++ b/doc/index/rtree/query.qbk @@ -69,14 +69,19 @@ Examples of some basic queries may be found in the tables below. The query regio ] [table -[[intersects(Ring)] [intersects(Polygon)] [intersects(MultiPolygon)] [intersects(Segment)] [intersects(Linestring)]] -[[[$img/index/rtree/intersects_ring.png]] [[$img/index/rtree/intersects_poly.png]] [[$img/index/rtree/intersects_mpoly.png]] [[$img/index/rtree/intersects_segment.png]] [[$img/index/rtree/intersects_linestring.png]]] +[[intersects(Segment)] [intersects(Box)] [disjoint(Box)] [intersects(Box)] [disjoint(Box)]] +[[[$img/index/rtree/intersects_segment.png]] [[$img/index/rtree/rtree_pt_intersects_box.png]] [[$img/index/rtree/rtree_pt_disjoint_box.png]] [[$img/index/rtree/rtree_seg_intersects_box.png]] [[$img/index/rtree/rtree_seg_disjoint_box.png]]] ] -[table +[/table +[[intersects(Ring)] [intersects(Polygon)] [intersects(MultiPolygon)] [intersects(Segment)] [intersects(Linestring)]] +[[[$img/index/rtree/intersects_ring.png]] [[$img/index/rtree/intersects_poly.png]] [[$img/index/rtree/intersects_mpoly.png]] [[$img/index/rtree/intersects_segment.png]] [[$img/index/rtree/intersects_linestring.png]]] +/] + +[/table [[intersects(Box)] [disjoint(Box)] [intersects(Box)] [disjoint(Box)]] [[[$img/index/rtree/rtree_pt_intersects_box.png]] [[$img/index/rtree/rtree_pt_disjoint_box.png]] [[$img/index/rtree/rtree_seg_intersects_box.png]] [[$img/index/rtree/rtree_seg_disjoint_box.png]]] -] +/] Spatial predicates are generated by functions defined in `boost::geometry::index` namespace. From 98b8821be6788f54912313f9f2def8be9129d08f Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 2 Aug 2017 02:52:02 +0200 Subject: [PATCH 052/188] [sectionalize] Add full namespace to function call (mingw workaround). --- .../geometry/algorithms/detail/sections/sectionalize.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp index b4441d6be..f1d8e7d23 100644 --- a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp +++ b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -985,9 +985,12 @@ inline void sectionalize(Geometry const& geometry, typename cs_tag::type >::type envelope_strategy_type; - sectionalize(geometry, robust_policy, sections, - envelope_strategy_type(), - source_index, max_count); + boost::geometry::sectionalize + < + Reverse, DimensionVector + >(geometry, robust_policy, sections, + envelope_strategy_type(), + source_index, max_count); } }} // namespace boost::geometry From 012afcb0ef6d9d71741fc62558529c1fa6344461 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 2 Aug 2017 04:03:19 +0200 Subject: [PATCH 053/188] [doc] Update 1.65 release notes. --- doc/release_notes.qbk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index aca0f84d1..24f1e2377 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -25,6 +25,7 @@ [*Improvements] * Add correct() overload taking area strategy +* Add is_convex() overload taking side strategy * Add missing relational operations (covered_by, crosses, equals, etc.) for MultiPoint/AnyGeometry [*Solved issues] @@ -43,6 +44,7 @@ * Fix propagation of area strategy into the internals of various algorithms from intersection strategy * Fix uninitialized variable in relate and reference to temporary in overlay * Fix error in disjoint for geographic Segment/Box +* Fix handling of non-cartesian geometries in various algorithms [/=================] [heading Boost 1.64] From fdca32a6e5d6f624a53b0f8b1190390edd42531e Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 5 Aug 2017 01:16:16 +0200 Subject: [PATCH 054/188] [compare] Support non-cartesian CSes in less<>, greater<> and equal_to<>. Improve consistency with single-Dimension version and equals(Pt, Pt). Always use math::equals when checking equality of coordinates, also in single-Dimension versions. Check antimeridians and poles also when only longitudes are compared (At poles all longitudes are considered equal). Add template argument defaulting to void and specialization defining operator comparing objects of 2 different types (after C++14). Remove Strategy template parameter which only allowed to pass standard predicates (e.g. std::less) so was unusable and this design was forcing unnecessary equal_to call when math::equals() failed. Implement compare strategies the same way how other strategies are implemented. Add compare:: cartesian and spherical strategies and compare::services::default_strategy traits. --- include/boost/geometry/policies/compare.hpp | 276 ++++++--------- include/boost/geometry/strategies/compare.hpp | 284 +++++++++------- .../geometry/strategies/spherical/compare.hpp | 321 ++++++++++++++++++ .../strategies/spherical/compare_circular.hpp | 152 --------- .../boost/geometry/strategies/strategies.hpp | 2 +- 5 files changed, 599 insertions(+), 436 deletions(-) create mode 100644 include/boost/geometry/strategies/spherical/compare.hpp delete mode 100644 include/boost/geometry/strategies/spherical/compare_circular.hpp diff --git a/include/boost/geometry/policies/compare.hpp b/include/boost/geometry/policies/compare.hpp index 2e952d3e1..7f32b7ab3 100644 --- a/include/boost/geometry/policies/compare.hpp +++ b/include/boost/geometry/policies/compare.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -20,170 +25,56 @@ namespace boost { namespace geometry { -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace compare -{ - - -template -< - int Direction, - typename Point, - typename Strategy, - std::size_t Dimension, - std::size_t DimensionCount -> -struct compare_loop -{ - typedef typename strategy::compare::detail::select_strategy - < - Strategy, Direction, Point, Dimension - >::type compare_type; - - typedef typename geometry::coordinate_type::type coordinate_type; - - static inline bool apply(Point const& left, Point const& right) - { - coordinate_type const& cleft = geometry::get(left); - coordinate_type const& cright = geometry::get(right); - - if (geometry::math::equals(cleft, cright)) - { - return compare_loop - < - Direction, Point, Strategy, - Dimension + 1, DimensionCount - >::apply(left, right); - } - else - { - compare_type compare; - return compare(cleft, cright); - } - } -}; - -template -< - int Direction, - typename Point, - typename Strategy, - std::size_t DimensionCount -> -struct compare_loop -{ - static inline bool apply(Point const&, Point const&) - { - // On coming here, points are equal. Return true if - // direction = 0 (equal), false if -1/1 (greater/less) - return Direction == 0; - } -}; - - -template -struct compare_in_all_dimensions -{ - inline bool operator()(Point const& left, Point const& right) const - { - return detail::compare::compare_loop - < - Direction, Point, Strategy, - 0, geometry::dimension::type::value - >::apply(left, right); - } -}; - - -template -class compare_in_one_dimension -{ - Strategy compare; - -public : - inline bool operator()(Point const& left, Point const& right) const - { - typedef typename geometry::coordinate_type::type coordinate_type; - - coordinate_type const& cleft = get(left); - coordinate_type const& cright = get(right); - return compare(cleft, cright); - } -}; - -}} // namespace detail::compare - -#endif - -#ifndef DOXYGEN_NO_DISPATCH -namespace dispatch -{ - -template -< - int Direction, - typename Point, - typename Strategy, - int Dimension -> -struct compare_geometries - : detail::compare::compare_in_one_dimension - < - Point, - typename strategy::compare::detail::select_strategy - < - Strategy, Direction, Point, Dimension - >::type, - Dimension - > -{}; - - -// Specialization with -1: compare in all dimensions -template -struct compare_geometries - : detail::compare::compare_in_all_dimensions -{}; - - - -} // namespace dispatch -#endif // DOXYGEN_NO_DISPATCH - - /*! \brief Less functor, to sort points in ascending order. \ingroup compare \details This functor compares points and orders them on x, then on y, then on z coordinate. -\tparam Geometry the geometry +\tparam Point the geometry \tparam Dimension the dimension to sort on, defaults to -1, indicating ALL dimensions. That's to say, first on x, on equal x-es then on y, etc. If a dimension is specified, only that dimension is considered -\tparam Strategy underlying coordinate comparing functor, - defaults to the default comparison strategies - related to the point coordinate system. If specified, the specified - strategy is used. This can e.g. be std::less. */ template < - typename Point, - int Dimension = -1, - typename Strategy = strategy::compare::default_strategy + typename Point = void, + int Dimension = -1 > struct less - : dispatch::compare_geometries - < - 1, // indicates ascending - Point, - Strategy, - Dimension - > { typedef Point first_argument_type; typedef Point second_argument_type; typedef bool result_type; + + inline bool operator()(Point const& left, Point const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::less, + Point, Point, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } +}; + +template +struct less +{ + template + inline bool operator()(Point1 const& left, Point2 const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::less, + Point1, Point2, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } }; @@ -195,19 +86,44 @@ struct less */ template < - typename Point, - int Dimension = -1, - typename Strategy = strategy::compare::default_strategy + typename Point = void, + int Dimension = -1 > struct greater - : dispatch::compare_geometries - < - -1, // indicates descending - Point, - Strategy, - Dimension - > -{}; +{ + typedef Point first_argument_type; + typedef Point second_argument_type; + typedef bool result_type; + + bool operator()(Point const& left, Point const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::greater, + Point, Point, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } +}; + +template +struct greater +{ + template + bool operator()(Point1 const& left, Point2 const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::greater, + Point1, Point2, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } +}; /*! @@ -217,23 +133,47 @@ struct greater \tparam Dimension the dimension to compare on, defaults to -1, indicating ALL dimensions. If a dimension is specified, only that dimension is considered -\tparam Strategy underlying coordinate comparing functor */ template < typename Point, - int Dimension = -1, - typename Strategy = strategy::compare::default_strategy + int Dimension = -1 > struct equal_to - : dispatch::compare_geometries - < - 0, - Point, - Strategy, - Dimension - > -{}; +{ + typedef Point first_argument_type; + typedef Point second_argument_type; + typedef bool result_type; + + bool operator()(Point const& left, Point const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::equal_to, + Point, Point, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } +}; + +template +struct equal_to +{ + template + bool operator()(Point1 const& left, Point2 const& right) const + { + typedef typename strategy::compare::services::default_strategy + < + strategy::compare::equal_to, + Point1, Point2, + Dimension + >::type strategy_type; + + return strategy_type::apply(left, right); + } +}; }} // namespace boost::geometry diff --git a/include/boost/geometry/strategies/compare.hpp b/include/boost/geometry/strategies/compare.hpp index 295831922..b196b75ec 100644 --- a/include/boost/geometry/strategies/compare.hpp +++ b/include/boost/geometry/strategies/compare.hpp @@ -4,6 +4,11 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -15,155 +20,204 @@ #ifndef BOOST_GEOMETRY_STRATEGIES_COMPARE_HPP #define BOOST_GEOMETRY_STRATEGIES_COMPARE_HPP + #include #include -#include +#include +#include +#include #include #include +#include -#include +#include namespace boost { namespace geometry { -/*! - \brief Traits class binding a comparing strategy to a coordinate system - \ingroup util - \tparam Tag tag of coordinate system of point-type - \tparam Direction direction to compare on: 1 for less (-> ascending order) - and -1 for greater (-> descending order) - \tparam Point point-type - \tparam CoordinateSystem coordinate sytem of point - \tparam Dimension: the dimension to compare on -*/ -template -< - typename Tag, - int Direction, - typename Point, - typename CoordinateSystem, - std::size_t Dimension -> -struct strategy_compare -{ - typedef strategy::not_implemented type; -}; - - -#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS - -// For compare we add defaults specializations, -// because they defaultly redirect to std::less / greater / equal_to -template -< - typename Tag, - typename Point, - typename CoordinateSystem, - std::size_t Dimension -> -struct strategy_compare -{ - typedef std::less::type> type; -}; - - -template -< - typename Tag, - typename Point, - typename CoordinateSystem, - std::size_t Dimension -> -struct strategy_compare -{ - typedef std::greater::type> type; -}; - - -template -< - typename Tag, - typename Point, - typename CoordinateSystem, - std::size_t Dimension -> -struct strategy_compare -{ - typedef std::equal_to::type> type; -}; - - -#endif - - namespace strategy { namespace compare { -/*! - \brief Default strategy, indicates the default strategy for comparisons - \details The default strategy for comparisons defer in most cases - to std::less (for ascending) and std::greater (for descending). - However, if a spherical coordinate system is used, and comparison - is done on longitude, it will take another strategy handling circular -*/ -struct default_strategy {}; +struct less +{ + template + static inline bool apply(T1 const& l, T2 const& r) + { + return l < r; + } +}; + +struct greater +{ + template + static inline bool apply(T1 const& l, T2 const& r) + { + return l > r; + } +}; + +struct equal_to +{ + template + static inline bool apply(T1 const& , T2 const& ) + { + return false; + } +}; #ifndef DOXYGEN_NO_DETAIL namespace detail { -template -struct is_default : boost::false_type -{}; - -template <> -struct is_default : boost::true_type -{}; - - -/*! - \brief Meta-function to select strategy - \details If "default_strategy" is specified, it will take the - traits-registered class for the specified coordinate system. - If another strategy is explicitly specified, it takes that one. -*/ template < - typename Strategy, - int Direction, - typename Point, - std::size_t Dimension + typename ComparePolicy, + std::size_t Dimension, + std::size_t DimensionCount > -struct select_strategy +struct compare_loop { - typedef typename - boost::mpl::if_ - < - is_default, - typename strategy_compare - < - typename cs_tag::type, - Direction, - Point, - typename coordinate_system::type, - Dimension - >::type, - Strategy - >::type type; + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + typename geometry::coordinate_type::type const& + cleft = geometry::get(left); + typename geometry::coordinate_type::type const& + cright = geometry::get(right); + + if (math::equals(cleft, cright)) + { + return compare_loop + < + ComparePolicy, + Dimension + 1, DimensionCount + >::apply(left, right); + } + else + { + return ComparePolicy::apply(cleft, cright); + } + } +}; + +template +< + typename ComparePolicy, + std::size_t DimensionCount +> +struct compare_loop +{ + template + static inline bool apply(Point1 const& , Point2 const& ) + { + // On coming here, points are equal. + // Return false for less/greater. + return false; + } +}; + +template +< + std::size_t DimensionCount +> +struct compare_loop +{ + template + static inline bool apply(Point1 const& , Point2 const& ) + { + // On coming here, points are equal. + // Return true for equal_to. + return true; + } }; } // namespace detail #endif // DOXYGEN_NO_DETAIL -}} // namespace strategy::compare +template +< + typename ComparePolicy, + int Dimension = -1 +> +struct cartesian +{ + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::compare_loop + < + ComparePolicy, Dimension, Dimension + 1 + >::apply(left, right); + } +}; + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +template +< + typename ComparePolicy +> +struct cartesian +{ + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::compare_loop + < + ComparePolicy, + 0, + boost::mpl::min + < + geometry::dimension, + geometry::dimension + >::type::value + >::apply(left, right); + } +}; +#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + +namespace services +{ + + +template +< + typename ComparePolicy, + typename Point1, + typename Point2 = Point1, + int Dimension = -1, + typename CSTag1 = typename cs_tag::type, + typename CSTag2 = typename cs_tag::type +> +struct default_strategy +{ + BOOST_MPL_ASSERT_MSG + ( + false, + NOT_IMPLEMENTED_FOR_THESE_TYPES, + (types) + ); +}; + + +template +struct default_strategy +{ + typedef compare::cartesian type; +}; + + +} // namespace services + + +}} // namespace strategy compare }} // namespace boost::geometry diff --git a/include/boost/geometry/strategies/spherical/compare.hpp b/include/boost/geometry/strategies/spherical/compare.hpp new file mode 100644 index 000000000..26163f740 --- /dev/null +++ b/include/boost/geometry/strategies/spherical/compare.hpp @@ -0,0 +1,321 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// 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) + +#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_HPP +#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_HPP + + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +namespace strategy { namespace compare +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template +static inline typename geometry::coordinate_type

::type +get(P const& p, boost::true_type /*same units*/) +{ + return geometry::get(p); +} + +template +static inline typename geometry::coordinate_type

::type +get(P const& p, boost::false_type /*different units*/) +{ + return geometry::get_as_radian(p); +} + +template +< + typename ComparePolicy, + typename Point1, + typename Point2, + std::size_t DimensionCount +> +struct spherical_latitude +{ + typedef typename geometry::coordinate_type::type coordinate1_type; + typedef typename geometry::coordinate_system::type::units units1_type; + typedef typename geometry::coordinate_type::type coordinate2_type; + typedef typename geometry::coordinate_system::type::units units2_type; + typedef typename boost::is_same::type same_units_type; + + template + static inline bool apply(Point1 const& left, Point2 const& right, + T1 const& l1, T2 const& r1) + { + // latitudes equal + if (math::equals(l1, r1)) + { + return compare::detail::compare_loop + < + ComparePolicy, 2, DimensionCount + >::apply(left, right); + } + else + { + return ComparePolicy::apply(l1, r1); + } + } + + static inline bool apply(Point1 const& left, Point2 const& right) + { + coordinate1_type const& l1 = compare::detail::get<1>(left, same_units_type()); + coordinate2_type const& r1 = compare::detail::get<1>(right, same_units_type()); + + return apply(left, right, l1, r1); + } +}; + +template +< + typename ComparePolicy, + typename Point1, + typename Point2 +> +struct spherical_latitude +{ + template + static inline bool apply(Point1 const& left, Point2 const& right, + T1 const& , T2 const& ) + { + return apply(left, right); + } + + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::compare_loop + < + ComparePolicy, 1, 1 + >::apply(left, right); + } +}; + +template +< + typename ComparePolicy, + typename Point1, + typename Point2, + std::size_t DimensionCount +> +struct spherical_longitude +{ + typedef typename geometry::coordinate_type::type coordinate1_type; + typedef typename geometry::coordinate_system::type::units units1_type; + typedef typename geometry::coordinate_type::type coordinate2_type; + typedef typename geometry::coordinate_system::type::units units2_type; + typedef typename boost::is_same::type same_units_type; + typedef typename boost::mpl::if_::type units_type; + + static const bool is_equatorial = ! boost::is_same + < + typename geometry::cs_tag::type, + geometry::spherical_polar_tag + >::value; + + static inline bool are_both_at_antimeridian(coordinate1_type const& l0, + coordinate2_type const& r0, + bool & is_left_at, + bool & is_right_at) + { + is_left_at = math::is_longitude_antimeridian(l0); + is_right_at = math::is_longitude_antimeridian(r0); + return is_left_at && is_right_at; + } + + static inline bool apply(Point1 const& left, Point2 const& right) + { + // if units are different the coordinates are in radians + coordinate1_type const& l0 = compare::detail::get<0>(left, same_units_type()); + coordinate2_type const& r0 = compare::detail::get<0>(right, same_units_type()); + coordinate1_type const& l1 = compare::detail::get<1>(left, same_units_type()); + coordinate2_type const& r1 = compare::detail::get<1>(right, same_units_type()); + + bool is_left_at_antimeridian = false; + bool is_right_at_antimeridian = false; + + // longitudes equal + if (math::equals(l0, r0) + // both at antimeridian + || are_both_at_antimeridian(l0, r0, is_left_at_antimeridian, is_right_at_antimeridian) + // both at pole + || (math::equals(l1, r1) + && math::is_latitude_pole(l1))) + { + return spherical_latitude + < + ComparePolicy, Point1, Point2, DimensionCount + >::apply(left, right, l1, r1); + } + // if left is at antimeridian and right is not at antimeridian + // then left is greater than right + else if (is_left_at_antimeridian) + { + // less/equal_to -> false, greater -> true + return ComparePolicy::apply(1, 0); + } + // if right is at antimeridian and left is not at antimeridian + // then left is lesser than right + else if (is_right_at_antimeridian) + { + // less -> true, equal_to/greater -> false + return ComparePolicy::apply(0, 1); + } + else + { + return ComparePolicy::apply(l0, r0); + } + } +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +/*! +\brief Compare strategy for spherical coordinates +\ingroup strategies +\tparam Point point-type +\tparam Dimension dimension +*/ +template +< + typename ComparePolicy, + int Dimension = -1 +> +struct spherical + : cartesian +{}; + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +// all dimensions starting from longitude +template +struct spherical +{ + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::spherical_longitude + < + ComparePolicy, + Point1, + Point2, + boost::mpl::min + < + geometry::dimension, + geometry::dimension + >::type::value + >::apply(left, right); + } +}; + +// only longitudes (and latitudes to check poles) +template +struct spherical +{ + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::spherical_longitude + < + ComparePolicy, Point1, Point2, 1 + >::apply(left, right); + } +}; + +// only latitudes +template +struct spherical +{ + template + static inline bool apply(Point1 const& left, Point2 const& right) + { + return compare::detail::spherical_latitude + < + ComparePolicy, Point1, Point2, 2 + >::apply(left, right); + } +}; + +#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + + +namespace services +{ + + +template +struct default_strategy + < + ComparePolicy, Point1, Point2, Dimension, + spherical_polar_tag, spherical_polar_tag + > +{ + typedef compare::spherical type; +}; + +template +struct default_strategy + < + ComparePolicy, Point1, Point2, Dimension, + spherical_equatorial_tag, spherical_equatorial_tag + > +{ + typedef compare::spherical type; +}; + +template +struct default_strategy + < + ComparePolicy, Point1, Point2, Dimension, + geographic_tag, geographic_tag + > +{ + typedef compare::spherical type; +}; + + +} // namespace services + + +}} // namespace strategy::compare + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_HPP diff --git a/include/boost/geometry/strategies/spherical/compare_circular.hpp b/include/boost/geometry/strategies/spherical/compare_circular.hpp deleted file mode 100644 index 2f890dfd8..000000000 --- a/include/boost/geometry/strategies/spherical/compare_circular.hpp +++ /dev/null @@ -1,152 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) - -// Copyright (c) 2007-2012 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) - -#ifndef BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_SPHERICAL_HPP -#define BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_SPHERICAL_HPP - -#include - -#include -#include -#include -#include - - -namespace boost { namespace geometry -{ - - -namespace strategy { namespace compare -{ - - -#ifndef DOXYGEN_NO_DETAIL -namespace detail -{ - -template -struct shift -{ -}; - -template <> -struct shift -{ - static inline double full() { return 360.0; } - static inline double half() { return 180.0; } -}; - -template <> -struct shift -{ - static inline double full() { return 2.0 * boost::math::constants::pi(); } - static inline double half() { return boost::math::constants::pi(); } -}; - -} // namespace detail -#endif - -/*! -\brief Compare (in one direction) strategy for spherical coordinates -\ingroup strategies -\tparam Point point-type -\tparam Dimension dimension -*/ -template -struct circular_comparator -{ - static inline CoordinateType put_in_range(CoordinateType const& c, - double min_border, double max_border) - { - CoordinateType value = c; - while (value < min_border) - { - value += detail::shift::full(); - } - while (value > max_border) - { - value -= detail::shift::full(); - } - return value; - } - - inline bool operator()(CoordinateType const& c1, CoordinateType const& c2) const - { - Compare compare; - - // Check situation that one of them is e.g. std::numeric_limits. - static const double full = detail::shift::full(); - double mx = 10.0 * full; - if (c1 < -mx || c1 > mx || c2 < -mx || c2 > mx) - { - // do normal comparison, using circular is not useful - return compare(c1, c2); - } - - static const double half = full / 2.0; - CoordinateType v1 = put_in_range(c1, -half, half); - CoordinateType v2 = put_in_range(c2, -half, half); - - // Two coordinates on a circle are - // at max <= half a circle away from each other. - // So if it is more, shift origin. - CoordinateType diff = geometry::math::abs(v1 - v2); - if (diff > half) - { - v1 = put_in_range(v1, 0, full); - v2 = put_in_range(v2, 0, full); - } - - return compare(v1, v2); - } -}; - -}} // namespace strategy::compare - -#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS - -// Specialize for the longitude (dim 0) -template -< - typename Point, - template class CoordinateSystem, - typename Units -> -struct strategy_compare, 0> -{ - typedef typename coordinate_type::type coordinate_type; - typedef strategy::compare::circular_comparator - < - coordinate_type, - Units, - std::less - > type; -}; - -template -< - typename Point, - template class CoordinateSystem, - typename Units -> -struct strategy_compare, 0> -{ - typedef typename coordinate_type::type coordinate_type; - typedef strategy::compare::circular_comparator - < - coordinate_type, - Units, - std::greater - > type; -}; - -#endif // DOXYGEN_NO_STRATEGY_SPECIALIZATIONS - -}} // namespace boost::geometry - -#endif // BOOST_GEOMETRY_STRATEGIES_SPHERICAL_COMPARE_SPHERICAL_HPP diff --git a/include/boost/geometry/strategies/strategies.hpp b/include/boost/geometry/strategies/strategies.hpp index 27a025c7c..97c602323 100644 --- a/include/boost/geometry/strategies/strategies.hpp +++ b/include/boost/geometry/strategies/strategies.hpp @@ -73,7 +73,7 @@ #include #include #include -#include +#include #include #include #include From 3ba45ba8ccb5d330d86e9596a5771e948d1d28cb Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 5 Aug 2017 01:41:55 +0200 Subject: [PATCH 055/188] [math][detail] Support spherical_polar CS in normalization utils. Also add some helper functions (checking poles and antimeridians). --- .../geometry/algorithms/detail/normalize.hpp | 27 +++++- .../normalize_spheroidal_box_coordinates.hpp | 36 +++++++- .../util/normalize_spheroidal_coordinates.hpp | 87 ++++++++++++++++++- 3 files changed, 144 insertions(+), 6 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/normalize.hpp b/include/boost/geometry/algorithms/detail/normalize.hpp index 913fe324b..7a761d42b 100644 --- a/include/boost/geometry/algorithms/detail/normalize.hpp +++ b/include/boost/geometry/algorithms/detail/normalize.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -120,7 +121,7 @@ struct assign_loop<1, DimensionCount> }; -template +template struct normalize_point { static inline void apply(PointIn const& point_in, PointOut& point_out) @@ -133,6 +134,7 @@ struct normalize_point math::normalize_spheroidal_coordinates < typename coordinate_system::type::units, + IsEquatorial, in_coordinate_type >(longitude, latitude); @@ -144,7 +146,7 @@ struct normalize_point }; -template +template class normalize_box { template @@ -193,6 +195,7 @@ public: math::normalize_spheroidal_box_coordinates < typename coordinate_system::type::units, + IsEquatorial, in_coordinate_type >(lon_min, lat_min, lon_max, lat_max); @@ -234,6 +237,15 @@ struct normalize {}; +template +struct normalize + < + PointIn, PointOut, point_tag, point_tag, + spherical_polar_tag, spherical_polar_tag + > : detail::normalization::normalize_point +{}; + + template struct normalize < @@ -251,6 +263,15 @@ struct normalize {}; +template +struct normalize + < + BoxIn, BoxOut, box_tag, box_tag, + spherical_polar_tag, spherical_polar_tag + > : detail::normalization::normalize_box +{}; + + template struct normalize < diff --git a/include/boost/geometry/util/normalize_spheroidal_box_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_box_coordinates.hpp index eb947bb09..288dab20d 100644 --- a/include/boost/geometry/util/normalize_spheroidal_box_coordinates.hpp +++ b/include/boost/geometry/util/normalize_spheroidal_box_coordinates.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -26,7 +27,7 @@ namespace detail { -template +template class normalize_spheroidal_box_coordinates { private: @@ -50,6 +51,9 @@ public: normalize::apply(longitude1, latitude1, false); normalize::apply(longitude2, latitude2, false); + latitude_convert_if_polar::apply(latitude1); + latitude_convert_if_polar::apply(latitude2); + if (math::equals(latitude1, constants::min_latitude()) && math::equals(latitude2, constants::min_latitude())) { @@ -76,6 +80,9 @@ public: longitude2 += constants::period(); } + latitude_convert_if_polar::apply(latitude1); + latitude_convert_if_polar::apply(latitude2); + #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE BOOST_GEOMETRY_ASSERT(! math::larger(latitude1, latitude2)); BOOST_GEOMETRY_ASSERT(! math::smaller(latitude1, constants::min_latitude())); @@ -126,6 +133,18 @@ inline void normalize_spheroidal_box_coordinates(CoordinateType& longitude1, >::apply(longitude1, latitude1, longitude2, latitude2); } +template +inline void normalize_spheroidal_box_coordinates(CoordinateType& longitude1, + CoordinateType& latitude1, + CoordinateType& longitude2, + CoordinateType& latitude2) +{ + detail::normalize_spheroidal_box_coordinates + < + Units, CoordinateType, IsEquatorial + >::apply(longitude1, latitude1, longitude2, latitude2); +} + /*! \brief Short utility to normalize the coordinates of a box on a spheroid \tparam Units The units of the coordindate system in the spheroid @@ -151,6 +170,19 @@ inline void normalize_spheroidal_box_coordinates(CoordinateType& longitude1, >::apply(longitude1, latitude1, longitude2, latitude2, band); } +template +inline void normalize_spheroidal_box_coordinates(CoordinateType& longitude1, + CoordinateType& latitude1, + CoordinateType& longitude2, + CoordinateType& latitude2, + bool band) +{ + detail::normalize_spheroidal_box_coordinates + < + Units, CoordinateType, IsEquatorial + >::apply(longitude1, latitude1, longitude2, latitude2, band); +} + } // namespace math diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp index 19d4d33d2..1ab7d9010 100644 --- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp +++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015-2016, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -99,7 +99,78 @@ struct constants_on_spheroid }; +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + template +inline CoordinateType latitude_convert_ep(CoordinateType const& lat) +{ + typedef math::detail::constants_on_spheroid + < + CoordinateType, + Units + > constants; + + return constants::max_latitude() - lat; +} + + +template +static bool is_latitude_pole(T const& lat) +{ + typedef math::detail::constants_on_spheroid + < + T, + Units + > constants; + + return math::equals(math::abs(IsEquatorial + ? lat + : math::latitude_convert_ep(lat)), + constants::max_latitude()); + +} + + +template +static bool is_longitude_antimeridian(T const& lon) +{ + typedef math::detail::constants_on_spheroid + < + T, + Units + > constants; + + return math::equals(math::abs(lon), constants::half_period()); + +} + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + + +template +struct latitude_convert_if_polar +{ + template + static inline void apply(T & lat) {} +}; + +template +struct latitude_convert_if_polar +{ + template + static inline void apply(T & lat) + { + lat = latitude_convert_ep(lat); + } +}; + + +template class normalize_spheroidal_coordinates { typedef constants_on_spheroid constants; @@ -145,6 +216,8 @@ public: CoordinateType& latitude, bool normalize_poles = true) { + latitude_convert_if_polar::apply(latitude); + #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE // normalize latitude if (math::larger(latitude, constants::half_period())) @@ -183,6 +256,8 @@ public: } } + latitude_convert_if_polar::apply(latitude); + #ifdef BOOST_GEOMETRY_NORMALIZE_LATITUDE BOOST_GEOMETRY_ASSERT(! math::larger(constants::min_latitude(), latitude)); BOOST_GEOMETRY_ASSERT(! math::larger(latitude, constants::max_latitude())); @@ -216,6 +291,15 @@ inline void normalize_spheroidal_coordinates(CoordinateType& longitude, >::apply(longitude, latitude); } +template +inline void normalize_spheroidal_coordinates(CoordinateType& longitude, + CoordinateType& latitude) +{ + detail::normalize_spheroidal_coordinates + < + Units, CoordinateType, IsEquatorial + >::apply(longitude, latitude); +} /*! \brief Short utility to normalize the longitude on a spheroid. @@ -316,6 +400,7 @@ inline CoordinateType longitude_interval_distance_signed(CoordinateType const& l : c0; } + } // namespace math From e3e06f7e96d57806a5c094a1aca0497dce8cfc79 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 5 Aug 2017 01:45:32 +0200 Subject: [PATCH 056/188] [envelope][expand] Stop using bg::less<>. bg::less<> now use math::equals() consistently also in single-Dimension version, so shouldn't be used for expanding a Box. Implement expand(Box, Pt) for spherical polar. Remove compare "strategies" (Predicates) template parameters from expand detail and dispatch templates. --- .../algorithms/detail/envelope/multipoint.hpp | 5 +- .../detail/envelope/range_of_boxes.hpp | 7 +- .../algorithms/detail/envelope/segment.hpp | 2 - .../geometry/algorithms/detail/expand/box.hpp | 23 ++--- .../algorithms/detail/expand/indexed.hpp | 42 ++------ .../algorithms/detail/expand/point.hpp | 96 +++++++------------ .../algorithms/detail/expand/segment.hpp | 23 ++--- .../geometry/algorithms/dispatch/expand.hpp | 7 +- .../extensions/nsphere/algorithms/expand.hpp | 33 ++----- 9 files changed, 73 insertions(+), 165 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/envelope/multipoint.hpp b/include/boost/geometry/algorithms/detail/envelope/multipoint.hpp index efee4701c..eef756379 100644 --- a/include/boost/geometry/algorithms/detail/envelope/multipoint.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/multipoint.hpp @@ -1,9 +1,10 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015-2016, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -336,8 +337,6 @@ public: { detail::expand::point_loop < - strategy::compare::default_strategy, - strategy::compare::default_strategy, 2, dimension::value >::apply(mbr, *it, strategy); } diff --git a/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp b/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp index f61fc422d..7be97e59e 100644 --- a/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp @@ -1,9 +1,10 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015-2016, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -194,8 +195,6 @@ struct envelope_range_of_boxes_by_expansion { detail::expand::indexed_loop < - strategy::compare::default_strategy, - strategy::compare::default_strategy, min_corner, Dimension, DimensionCount @@ -203,8 +202,6 @@ struct envelope_range_of_boxes_by_expansion detail::expand::indexed_loop < - strategy::compare::default_strategy, - strategy::compare::default_strategy, max_corner, Dimension, DimensionCount diff --git a/include/boost/geometry/algorithms/detail/envelope/segment.hpp b/include/boost/geometry/algorithms/detail/envelope/segment.hpp index 7e3719496..3843244b5 100644 --- a/include/boost/geometry/algorithms/detail/envelope/segment.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/segment.hpp @@ -383,8 +383,6 @@ struct envelope_one_segment envelope_one_point::apply(p1, mbr, strategy); detail::expand::point_loop < - strategy::compare::default_strategy, - strategy::compare::default_strategy, Dimension, DimensionCount >::apply(mbr, p2, strategy); diff --git a/include/boost/geometry/algorithms/detail/expand/box.hpp b/include/boost/geometry/algorithms/detail/expand/box.hpp index 3edb23f5a..450f66533 100644 --- a/include/boost/geometry/algorithms/detail/expand/box.hpp +++ b/include/boost/geometry/algorithms/detail/expand/box.hpp @@ -5,11 +5,12 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Samuel Debionne, Grenoble, France. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -73,18 +74,16 @@ namespace dispatch template < typename BoxOut, typename BoxIn, - typename StrategyLess, typename StrategyGreater, typename CSTagOut, typename CSTag > struct expand < BoxOut, BoxIn, - StrategyLess, StrategyGreater, box_tag, box_tag, CSTagOut, CSTag > : detail::expand::expand_indexed < - 0, dimension::value, StrategyLess, StrategyGreater + 0, dimension::value > { BOOST_MPL_ASSERT_MSG((boost::is_same::value), @@ -92,29 +91,19 @@ struct expand (types())); }; -template -< - typename BoxOut, typename BoxIn, - typename StrategyLess, typename StrategyGreater -> +template struct expand < BoxOut, BoxIn, - StrategyLess, StrategyGreater, box_tag, box_tag, spherical_equatorial_tag, spherical_equatorial_tag > : detail::expand::box_on_spheroid {}; -template -< - typename BoxOut, typename BoxIn, - typename StrategyLess, typename StrategyGreater -> +template struct expand < BoxOut, BoxIn, - StrategyLess, StrategyGreater, box_tag, box_tag, geographic_tag, geographic_tag > : detail::expand::box_on_spheroid diff --git a/include/boost/geometry/algorithms/detail/expand/indexed.hpp b/include/boost/geometry/algorithms/detail/expand/indexed.hpp index 28cf0e2e4..e268c03de 100644 --- a/include/boost/geometry/algorithms/detail/expand/indexed.hpp +++ b/include/boost/geometry/algorithms/detail/expand/indexed.hpp @@ -5,11 +5,12 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Samuel Debionne, Grenoble, France. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -22,15 +23,13 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EXPAND_INDEXED_HPP #include +#include #include #include #include -#include -#include - #include @@ -44,7 +43,6 @@ namespace detail { namespace expand template < - typename StrategyLess, typename StrategyGreater, std::size_t Index, std::size_t Dimension, std::size_t DimensionCount > @@ -53,27 +51,17 @@ struct indexed_loop template static inline void apply(Box& box, Geometry const& source, Strategy const& strategy) { - typedef typename strategy::compare::detail::select_strategy - < - StrategyLess, 1, Box, Dimension - >::type less_type; - - typedef typename strategy::compare::detail::select_strategy - < - StrategyGreater, -1, Box, Dimension - >::type greater_type; - typedef typename select_coordinate_type < Box, Geometry >::type coordinate_type; - less_type less; - greater_type greater; - coordinate_type const coord = get(source); + std::less const less; + std::greater const greater; + if (less(coord, get(box))) { set(box, coord); @@ -86,21 +74,15 @@ struct indexed_loop indexed_loop < - StrategyLess, StrategyGreater, Index, Dimension + 1, DimensionCount >::apply(box, source, strategy); } }; -template -< - typename StrategyLess, typename StrategyGreater, - std::size_t Index, std::size_t DimensionCount -> +template struct indexed_loop < - StrategyLess, StrategyGreater, Index, DimensionCount, DimensionCount > { @@ -111,11 +93,7 @@ struct indexed_loop // Changes a box such that the other box is also contained by the box -template -< - std::size_t Dimension, std::size_t DimensionCount, - typename StrategyLess, typename StrategyGreater -> +template struct expand_indexed { template @@ -125,13 +103,11 @@ struct expand_indexed { indexed_loop < - StrategyLess, StrategyGreater, 0, Dimension, DimensionCount >::apply(box, geometry, strategy); indexed_loop < - StrategyLess, StrategyGreater, 1, Dimension, DimensionCount >::apply(box, geometry, strategy); } diff --git a/include/boost/geometry/algorithms/detail/expand/point.hpp b/include/boost/geometry/algorithms/detail/expand/point.hpp index f0cbd1db0..a09db51af 100644 --- a/include/boost/geometry/algorithms/detail/expand/point.hpp +++ b/include/boost/geometry/algorithms/detail/expand/point.hpp @@ -5,11 +5,12 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Samuel Debionne, Grenoble, France. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -23,6 +24,7 @@ #include #include +#include #include #include @@ -36,9 +38,6 @@ #include #include -#include -#include - #include #include @@ -53,33 +52,19 @@ namespace detail { namespace expand { -template -< - typename StrategyLess, typename StrategyGreater, - std::size_t Dimension, std::size_t DimensionCount -> +template struct point_loop { template static inline void apply(Box& box, Point const& source, Strategy const& strategy) { - typedef typename strategy::compare::detail::select_strategy - < - StrategyLess, 1, Point, Dimension - >::type less_type; - - typedef typename strategy::compare::detail::select_strategy - < - StrategyGreater, -1, Point, Dimension - >::type greater_type; - typedef typename select_coordinate_type < Point, Box >::type coordinate_type; - less_type less; - greater_type greater; + std::less const less; + std::greater const greater; coordinate_type const coord = get(source); @@ -93,37 +78,21 @@ struct point_loop set(box, coord); } - point_loop - < - StrategyLess, StrategyGreater, Dimension + 1, DimensionCount - >::apply(box, source, strategy); + point_loop::apply(box, source, strategy); } }; -template -< - typename StrategyLess, - typename StrategyGreater, - std::size_t DimensionCount -> -struct point_loop - < - StrategyLess, StrategyGreater, DimensionCount, DimensionCount - > +template +struct point_loop { template static inline void apply(Box&, Point const&, Strategy const&) {} }; -// implementation for the spherical equatorial and geographic coordinate systems -template -< - typename StrategyLess, - typename StrategyGreater, - std::size_t DimensionCount -> +// implementation for the spherical and geographic coordinate systems +template struct point_loop_on_spheroid { template @@ -133,11 +102,12 @@ struct point_loop_on_spheroid { typedef typename point_type::type box_point_type; typedef typename coordinate_type::type box_coordinate_type; + typedef typename coordinate_system::type::units units_type; typedef math::detail::constants_on_spheroid < box_coordinate_type, - typename coordinate_system::type::units + units_type > constants; // normalize input point and input box @@ -157,7 +127,7 @@ struct point_loop_on_spheroid b_lon_max = geometry::get(box), b_lat_max = geometry::get(box); - if (math::equals(math::abs(p_lat), constants::max_latitude())) + if (math::is_latitude_pole(p_lat)) { // the point of expansion is the either the north or the // south pole; the only important coordinate here is the @@ -169,7 +139,7 @@ struct point_loop_on_spheroid } if (math::equals(b_lat_min, b_lat_max) - && math::equals(math::abs(b_lat_min), constants::max_latitude())) + && math::is_latitude_pole(b_lat_min)) { // the box degenerates to either the north or the south pole; // the only important coordinate here is the pole's latitude, @@ -228,7 +198,7 @@ struct point_loop_on_spheroid point_loop < - StrategyLess, StrategyGreater, 2, DimensionCount + 2, DimensionCount >::apply(box, point, strategy); } }; @@ -246,18 +216,16 @@ namespace dispatch template < typename BoxOut, typename Point, - typename StrategyLess, typename StrategyGreater, typename CSTagOut, typename CSTag > struct expand < BoxOut, Point, - StrategyLess, StrategyGreater, box_tag, point_tag, CSTagOut, CSTag > : detail::expand::point_loop < - StrategyLess, StrategyGreater, 0, dimension::value + 0, dimension::value > { BOOST_MPL_ASSERT_MSG((boost::is_same::value), @@ -265,37 +233,43 @@ struct expand (types())); }; -template -< - typename BoxOut, typename Point, - typename StrategyLess, typename StrategyGreater -> +template struct expand < BoxOut, Point, - StrategyLess, StrategyGreater, box_tag, point_tag, spherical_equatorial_tag, spherical_equatorial_tag > : detail::expand::point_loop_on_spheroid < - StrategyLess, StrategyGreater, dimension::value + dimension::value + > +{}; + +template +struct expand + < + BoxOut, Point, + box_tag, point_tag, + spherical_polar_tag, spherical_polar_tag + > : detail::expand::point_loop_on_spheroid + < + dimension::value, + false > {}; template < - typename BoxOut, typename Point, - typename StrategyLess, typename StrategyGreater + typename BoxOut, typename Point > struct expand < BoxOut, Point, - StrategyLess, StrategyGreater, box_tag, point_tag, geographic_tag, geographic_tag > : detail::expand::point_loop_on_spheroid < - StrategyLess, StrategyGreater, dimension::value + dimension::value > {}; diff --git a/include/boost/geometry/algorithms/detail/expand/segment.hpp b/include/boost/geometry/algorithms/detail/expand/segment.hpp index 0570e944d..d229138a8 100644 --- a/include/boost/geometry/algorithms/detail/expand/segment.hpp +++ b/include/boost/geometry/algorithms/detail/expand/segment.hpp @@ -5,11 +5,12 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Samuel Debionne, Grenoble, France. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -77,18 +78,16 @@ namespace dispatch template < typename Box, typename Segment, - typename StrategyLess, typename StrategyGreater, typename CSTagOut, typename CSTag > struct expand < Box, Segment, - StrategyLess, StrategyGreater, box_tag, segment_tag, CSTagOut, CSTag > : detail::expand::expand_indexed < - 0, dimension::value, StrategyLess, StrategyGreater + 0, dimension::value > { BOOST_MPL_ASSERT_MSG((boost::is_same::value), @@ -96,29 +95,19 @@ struct expand (types())); }; -template -< - typename Box, typename Segment, - typename StrategyLess, typename StrategyGreater -> +template struct expand < Box, Segment, - StrategyLess, StrategyGreater, box_tag, segment_tag, spherical_equatorial_tag, spherical_equatorial_tag > : detail::expand::segment {}; -template -< - typename Box, typename Segment, - typename StrategyLess, typename StrategyGreater -> +template struct expand < Box, Segment, - StrategyLess, StrategyGreater, box_tag, segment_tag, geographic_tag, geographic_tag > : detail::expand::segment diff --git a/include/boost/geometry/algorithms/dispatch/expand.hpp b/include/boost/geometry/algorithms/dispatch/expand.hpp index 2c23d6a1c..c7a769648 100644 --- a/include/boost/geometry/algorithms/dispatch/expand.hpp +++ b/include/boost/geometry/algorithms/dispatch/expand.hpp @@ -5,10 +5,11 @@ // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. // Copyright (c) 2014-2015 Samuel Debionne, Grenoble, France. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -39,8 +40,6 @@ namespace dispatch template < typename GeometryOut, typename Geometry, - typename StrategyLess = strategy::compare::default_strategy, - typename StrategyGreater = strategy::compare::default_strategy, typename TagOut = typename tag::type, typename Tag = typename tag::type, typename CSTagOut = typename cs_tag::type, diff --git a/include/boost/geometry/extensions/nsphere/algorithms/expand.hpp b/include/boost/geometry/extensions/nsphere/algorithms/expand.hpp index a87d5cf03..fdf14dbe9 100644 --- a/include/boost/geometry/extensions/nsphere/algorithms/expand.hpp +++ b/include/boost/geometry/extensions/nsphere/algorithms/expand.hpp @@ -5,6 +5,11 @@ // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -17,6 +22,7 @@ #include +#include #include @@ -26,9 +32,6 @@ #include -#include -#include - namespace boost { namespace geometry { @@ -40,7 +43,6 @@ namespace detail { namespace expand template < - typename StrategyLess, typename StrategyGreater, std::size_t Dimension, std::size_t DimensionCount > struct nsphere_loop @@ -48,20 +50,10 @@ struct nsphere_loop template static inline void apply(Box& box, NSphere const& source) { - typedef typename strategy::compare::detail::select_strategy - < - StrategyLess, 1, NSphere, Dimension - >::type less_type; - - typedef typename strategy::compare::detail::select_strategy - < - StrategyGreater, -1, NSphere, Dimension - >::type greater_type; - typedef typename select_coordinate_type::type coordinate_type; - less_type less; - greater_type greater; + std::less const less; + std::greater const greater; coordinate_type const min_coord = get(source) - get_radius<0>(source); coordinate_type const max_coord = get(source) + get_radius<0>(source); @@ -78,7 +70,6 @@ struct nsphere_loop nsphere_loop < - StrategyLess, StrategyGreater, Dimension + 1, DimensionCount >::apply(box, source); } @@ -87,12 +78,10 @@ struct nsphere_loop template < - typename StrategyLess, typename StrategyGreater, std::size_t DimensionCount > struct nsphere_loop < - StrategyLess, StrategyGreater, DimensionCount, DimensionCount > { @@ -112,13 +101,11 @@ namespace dispatch // Box + Nsphere -> new box containing also nsphere template < - typename BoxOut, typename NSphere, - typename StrategyLess, typename StrategyGreater + typename BoxOut, typename NSphere > -struct expand +struct expand : detail::expand::nsphere_loop < - StrategyLess, StrategyGreater, 0, dimension::type::value > {}; From 7104c112b16a52b2f4528128cfa1d2260b361d2f Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 5 Aug 2017 01:54:27 +0200 Subject: [PATCH 057/188] [algorithms] Remove relate::less<> and replace it with geometry::less<>. --- .../detail/disjoint/multipoint_geometry.hpp | 9 +- .../detail/overlay/pointlike_linear.hpp | 1 - .../detail/overlay/pointlike_pointlike.hpp | 21 +++-- .../algorithms/detail/relate/less.hpp | 83 ------------------- .../detail/relate/multi_point_geometry.hpp | 4 +- .../algorithms/detail/relate/point_point.hpp | 9 +- .../detail/relate/topology_check.hpp | 7 +- .../algorithms/detail/within/multi_point.hpp | 7 +- 8 files changed, 34 insertions(+), 107 deletions(-) delete mode 100644 include/boost/geometry/algorithms/detail/relate/less.hpp diff --git a/include/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp b/include/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp index f8d3e3c59..d7aa9089e 100644 --- a/include/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp @@ -36,10 +36,11 @@ #include #include #include -#include #include +#include + namespace boost { namespace geometry { @@ -56,10 +57,10 @@ class multipoint_multipoint private: template class unary_disjoint_predicate - : detail::relate::less + : geometry::less<> { private: - typedef detail::relate::less base_type; + typedef geometry::less<> base_type; public: unary_disjoint_predicate(Iterator first, Iterator last) @@ -90,7 +91,7 @@ public: std::vector points1(boost::begin(multipoint1), boost::end(multipoint1)); - std::sort(points1.begin(), points1.end(), detail::relate::less()); + std::sort(points1.begin(), points1.end(), geometry::less<>()); typedef unary_disjoint_predicate < diff --git a/include/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp b/include/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp index 0a7c3bc46..6f8fdd32b 100644 --- a/include/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/pointlike_linear.hpp @@ -32,7 +32,6 @@ #include #include -#include #include #include #include diff --git a/include/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp b/include/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp index 438a37787..88aedecf8 100644 --- a/include/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp @@ -1,12 +1,13 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014-2015, Oracle and/or its affiliates. +// Copyright (c) 2014-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html -// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle - #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_POINTLIKE_POINTLIKE_HPP @@ -24,10 +25,11 @@ #include #include -#include #include #include +#include + namespace boost { namespace geometry { @@ -264,17 +266,20 @@ struct multipoint_multipoint_point >::apply(multipoint2, multipoint1, robust_policy, oit, strategy); } - std::vector::type> - points2(boost::begin(multipoint2), boost::end(multipoint2)); + typedef typename boost::range_value::type point2_type; - std::sort(points2.begin(), points2.end(), detail::relate::less()); + std::vector points2(boost::begin(multipoint2), + boost::end(multipoint2)); + + geometry::less<> const less = geometry::less<>(); + std::sort(points2.begin(), points2.end(), less); for (typename boost::range_iterator::type it1 = boost::begin(multipoint1); it1 != boost::end(multipoint1); ++it1) { bool found = std::binary_search(points2.begin(), points2.end(), - *it1, detail::relate::less()); + *it1, less); action_selector_pl_pl < diff --git a/include/boost/geometry/algorithms/detail/relate/less.hpp b/include/boost/geometry/algorithms/detail/relate/less.hpp deleted file mode 100644 index 462fcc35f..000000000 --- a/include/boost/geometry/algorithms/detail/relate/less.hpp +++ /dev/null @@ -1,83 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) - -// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. - -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014, Oracle and/or its affiliates. - -// 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) - -// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle - -#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LESS_HPP -#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LESS_HPP - -#include -#include -#include - -namespace boost { namespace geometry -{ - -#ifndef DOXYGEN_NO_DISPATCH -namespace detail_dispatch { namespace relate { - -// TODO: Integrate it with geometry::less? - -template ::value> -struct less -{ - static inline bool apply(Point1 const& left, Point2 const& right) - { - typename geometry::coordinate_type::type - cleft = geometry::get(left); - typename geometry::coordinate_type::type - cright = geometry::get(right); - - if ( geometry::math::equals(cleft, cright) ) - { - return less::apply(left, right); - } - else - { - return cleft < cright; - } - } -}; - -template -struct less -{ - static inline bool apply(Point1 const&, Point2 const&) - { - return false; - } -}; - -}} // namespace detail_dispatch::relate - -#endif - -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace relate { - -struct less -{ - template - inline bool operator()(Point1 const& point1, Point2 const& point2) const - { - return detail_dispatch::relate::less::apply(point1, point2); - } -}; - -}} // namespace detail::relate -#endif // DOXYGEN_NO_DETAIL - -}} // namespace boost::geometry - -#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LESS_HPP diff --git a/include/boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp b/include/boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp index 47c6963b8..8d5f21555 100644 --- a/include/boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/relate/multi_point_geometry.hpp @@ -121,7 +121,7 @@ struct multi_point_geometry_eb template bool apply(Point const& boundary_point) { - if (! std::binary_search(m_points.begin(), m_points.end(), boundary_point, relate::less())) + if (! std::binary_search(m_points.begin(), m_points.end(), boundary_point, geometry::less<>())) { m_boundary_found = true; return false; @@ -143,7 +143,7 @@ struct multi_point_geometry_eb typedef typename boost::range_value::type point_type; typedef std::vector points_type; points_type points(boost::begin(multi_point), boost::end(multi_point)); - std::sort(points.begin(), points.end(), relate::less()); + std::sort(points.begin(), points.end(), geometry::less<>()); boundary_visitor visitor(points); tc.for_each_boundary_point(visitor); diff --git a/include/boost/geometry/algorithms/detail/relate/point_point.hpp b/include/boost/geometry/algorithms/detail/relate/point_point.hpp index 68d8be031..e0bed72ba 100644 --- a/include/boost/geometry/algorithms/detail/relate/point_point.hpp +++ b/include/boost/geometry/algorithms/detail/relate/point_point.hpp @@ -21,9 +21,10 @@ #include #include -#include #include +#include + namespace boost { namespace geometry { @@ -213,7 +214,9 @@ struct multipoint_multipoint // sort points from the 1 MPt typedef typename geometry::point_type::type point_type; std::vector points(boost::begin(sorted_mpt), boost::end(sorted_mpt)); - std::sort(points.begin(), points.end(), less()); + + geometry::less<> const less = geometry::less<>(); + std::sort(points.begin(), points.end(), less); bool found_inside = false; bool found_outside = false; @@ -224,7 +227,7 @@ struct multipoint_multipoint it != boost::end(iterated_mpt) ; ++it ) { bool ii = - std::binary_search(points.begin(), points.end(), *it, less()); + std::binary_search(points.begin(), points.end(), *it, less); if ( ii ) found_inside = true; else diff --git a/include/boost/geometry/algorithms/detail/relate/topology_check.hpp b/include/boost/geometry/algorithms/detail/relate/topology_check.hpp index 654999d8f..810466ec0 100644 --- a/include/boost/geometry/algorithms/detail/relate/topology_check.hpp +++ b/include/boost/geometry/algorithms/detail/relate/topology_check.hpp @@ -13,7 +13,8 @@ #include -#include + +#include #include #include @@ -214,7 +215,7 @@ private: if (! m_endpoints.empty() ) { - std::sort(m_endpoints.begin(), m_endpoints.end(), relate::less()); + std::sort(m_endpoints.begin(), m_endpoints.end(), geometry::less<>()); m_has_boundary = find_odd_count(m_endpoints.begin(), m_endpoints.end()); } @@ -224,7 +225,7 @@ private: template static inline std::size_t count_equal(It first, It last, Point const& point) { - std::pair rng = std::equal_range(first, last, point, relate::less()); + std::pair rng = std::equal_range(first, last, point, geometry::less<>()); return (std::size_t)std::distance(rng.first, rng.second); } diff --git a/include/boost/geometry/algorithms/detail/within/multi_point.hpp b/include/boost/geometry/algorithms/detail/within/multi_point.hpp index 7e85f3338..359853f6a 100644 --- a/include/boost/geometry/algorithms/detail/within/multi_point.hpp +++ b/include/boost/geometry/algorithms/detail/within/multi_point.hpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -33,6 +32,8 @@ #include +#include + #include #include @@ -63,7 +64,7 @@ struct multi_point_point } }; -// NOTE: currently the strategy is ignored, math::equals() is used inside relate::less +// NOTE: currently the strategy is ignored, math::equals() is used inside geometry::less<> struct multi_point_multi_point { template @@ -73,7 +74,7 @@ struct multi_point_multi_point { typedef typename boost::range_value::type point2_type; - relate::less const less = relate::less(); + geometry::less<> const less = geometry::less<>(); std::vector points2(boost::begin(multi_point2), boost::end(multi_point2)); std::sort(points2.begin(), points2.end(), less); From 197d39621851edc74b23dedcd45170e98f4acd61 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 5 Aug 2017 01:55:25 +0200 Subject: [PATCH 058/188] [strategies] In cartesian side strategy use cartesian compare strategy directly instead of relate::less<>. --- .../strategies/cartesian/side_by_triangle.hpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/include/boost/geometry/strategies/cartesian/side_by_triangle.hpp b/include/boost/geometry/strategies/cartesian/side_by_triangle.hpp index 4d1d97520..91bbee7bb 100644 --- a/include/boost/geometry/strategies/cartesian/side_by_triangle.hpp +++ b/include/boost/geometry/strategies/cartesian/side_by_triangle.hpp @@ -4,10 +4,11 @@ // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -29,9 +30,9 @@ #include #include +#include #include -#include #include @@ -182,10 +183,11 @@ public : // arguments, we cyclically permute them so that the first // argument is always the lexicographically smallest point. - geometry::detail::relate::less less; - if (less(p, p1)) + typedef compare::cartesian less; + + if (less::apply(p, p1)) { - if (less(p, p2)) + if (less::apply(p, p2)) { // p is the lexicographically smallest return side_value(p, p1, p2, epsp); @@ -197,7 +199,7 @@ public : } } - if (less(p1, p2)) + if (less::apply(p1, p2)) { // p1 is the lexicographically smallest return side_value(p1, p2, p, epsp); From 8e53f02acbed0a91511ab2177b99f98af16dde97 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 5 Aug 2017 01:56:28 +0200 Subject: [PATCH 059/188] [test] Add/update test cases after improvements in compare. --- test/algorithms/envelope_expand/expand.cpp | 27 ++++++++++---- .../test_envelope_expand_on_spheroid.hpp | 37 ++++++++++++++++++- test/algorithms/is_simple_geo.cpp | 29 +++++++++++++++ test/policies/compare.cpp | 26 ++++++++++--- 4 files changed, 104 insertions(+), 15 deletions(-) diff --git a/test/algorithms/envelope_expand/expand.cpp b/test/algorithms/envelope_expand/expand.cpp index b3cf67ba3..9ed205027 100644 --- a/test/algorithms/envelope_expand/expand.cpp +++ b/test/algorithms/envelope_expand/expand.cpp @@ -5,6 +5,11 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2017. +// Modifications copyright (c) 2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -95,7 +100,11 @@ void test_2d() template void test_spherical_degree() { - bg::model::box b = bg::make_inverse >(); + // it doesn't work with normalization of input enabled + //bg::model::box b = bg::make_inverse >(); + Point p; + bg::read_wkt("POINT(179.73 71.56)", p); + bg::model::box b(p, p); test_expand(b, "POINT(179.73 71.56)", "(179.73,71.56),(179.73,71.56)"); @@ -104,17 +113,19 @@ void test_spherical_degree() // It detects that this point is lying RIGHT of the others, // and then it "expands" it. - // It might be argued that "181.22" is displayed instead. However, they are - // the same. test_expand(b, "POINT(-178.78 70.78)", - "(177.47,70.78),(-178.78,71.56)"); + "(177.47,70.78),(181.22,71.56)"); } template void test_spherical_radian() { - bg::model::box b = bg::make_inverse >(); + // it doesn't work with normalization of input enabled + //bg::model::box b = bg::make_inverse >(); + Point p; + bg::read_wkt("POINT(3.128 1.249)", p); + bg::model::box b(p, p); test_expand(b, "POINT(3.128 1.249)", "(3.128,1.249),(3.128,1.249)"); @@ -123,10 +134,8 @@ void test_spherical_radian() // It detects that this point is lying RIGHT of the others, // and then it "expands" it. - // It might be argued that "181.22" is displayed instead. However, they are - // the same. test_expand(b, "POINT(-3.121 1.235)", - "(3.097,1.235),(-3.121,1.249)"); + "(3.097,1.235),(3.16219,1.249)"); } int test_main(int, char* []) @@ -141,6 +150,8 @@ int test_main(int, char* []) test_spherical_degree > >(); test_spherical_radian > >(); + test_spherical_degree > >(); + test_spherical_radian > >(); #if defined(HAVE_TTMATH) diff --git a/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp b/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp index ba04c85ef..e604552e5 100644 --- a/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp +++ b/test/algorithms/envelope_expand/test_envelope_expand_on_spheroid.hpp @@ -1,9 +1,10 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2015, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -110,6 +111,40 @@ struct other_system_info > } }; +template <> +struct other_system_info > +{ + typedef bg::degree units; + typedef bg::cs::spherical type; + + static inline double convert(double value) + { + return value * bg::math::r2d(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::r2d(); + } +}; + +template <> +struct other_system_info > +{ + typedef bg::radian units; + typedef bg::cs::spherical type; + + static inline double convert(double value) + { + return value * bg::math::d2r(); + } + + static inline rng convert(rng const& value) + { + return value * bg::math::d2r(); + } +}; + template <> struct other_system_info > { diff --git a/test/algorithms/is_simple_geo.cpp b/test/algorithms/is_simple_geo.cpp index 30a01a30c..ebd740d05 100644 --- a/test/algorithms/is_simple_geo.cpp +++ b/test/algorithms/is_simple_geo.cpp @@ -29,6 +29,19 @@ typedef bg::model::multi_polygon multi_polygon_type; typedef bg::model::box box_type; +BOOST_AUTO_TEST_CASE( test_is_simple_geo_multipoint ) +{ + typedef multi_point_type G; + + bg::strategy::intersection::geographic_segments<> s; + + test_simple_s(from_wkt("MULTIPOINT(0 90, 0 90)"), s, false); + test_simple_s(from_wkt("MULTIPOINT(0 90, 1 90)"), s, false); + test_simple_s(from_wkt("MULTIPOINT(0 -90, 0 -90)"), s, false); + test_simple_s(from_wkt("MULTIPOINT(0 -90, 1 -90)"), s, false); + test_simple_s(from_wkt("MULTIPOINT(0 80, 1 80)"), s, true); +} + BOOST_AUTO_TEST_CASE( test_is_simple_geo_linestring ) { typedef linestring_type G; @@ -40,6 +53,18 @@ BOOST_AUTO_TEST_CASE( test_is_simple_geo_linestring ) test_simple_s(from_wkt("LINESTRING(0 90, -90 50, 90 0)"), s, false); test_simple_s(from_wkt("LINESTRING(0 90, -90 -50, 90 0)"), s, true); + // invalid linestrings + test_simple_s(from_wkt("LINESTRING(0 90, 0 90)"), s, false, false); + test_simple_s(from_wkt("LINESTRING(0 -90, 0 -90)"), s, false, false); + test_simple_s(from_wkt("LINESTRING(0 90, 1 90)"), s, false, false); + test_simple_s(from_wkt("LINESTRING(0 -90, 1 -90)"), s, false, false); + + // FAILING + //test_simple_s(from_wkt("LINESTRING(0 90, 0 80, 1 80, 0 90)"), s, false); + //test_simple_s(from_wkt("LINESTRING(0 -90, 0 -80, 1 -80, 0 -90)"), s, false); + //test_simple_s(from_wkt("LINESTRING(0 90, 0 80, 1 80, 1 90)"), s, false); + //test_simple_s(from_wkt("LINESTRING(0 -90, 0 -80, 1 -80, 1 -90)"), s, false); + test_simple_s(from_wkt("LINESTRING(35 0, 110 36, 159 0, 82 30)"), s, false); test_simple_s(from_wkt("LINESTRING(135 0, -150 36, -101 0, -178 30)"), s, false); test_simple_s(from_wkt("LINESTRING(45 0, 120 36, 169 0, 92 30)"), s, false); @@ -52,6 +77,10 @@ BOOST_AUTO_TEST_CASE( test_is_simple_geo_multilinestring ) bg::strategy::intersection::geographic_segments<> s; + // FAILING + //test_simple_s(from_wkt("MULTILINESTRING((0 90, 0 80),(1 90, 1 80))"), s, false); + //test_simple_s(from_wkt("MULTILINESTRING((0 -90, 0 -80),(1 -90, 1 -80))"), s, false); + test_simple_s(from_wkt("MULTILINESTRING((35 0, 110 36),(159 0, 82 30))"), s, false); test_simple_s(from_wkt("MULTILINESTRING((135 0, -150 36),(-101 0, -178 30))"), s, false); test_simple_s(from_wkt("MULTILINESTRING((45 0, 120 36),(169 0, 92 30))"), s, false); diff --git a/test/policies/compare.cpp b/test/policies/compare.cpp index 3e4b1b021..b9d0be01a 100644 --- a/test/policies/compare.cpp +++ b/test/policies/compare.cpp @@ -3,6 +3,10 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle 2017. +// Modifications copyright (c) 2017, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // 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) @@ -203,26 +207,34 @@ void test_spherical() typedef typename bg::coordinate_type

::type ct; std::vector

v; + v.push_back(bg::make

( 180.00, 70.56)); v.push_back(bg::make

( 179.73, 71.56)); // east v.push_back(bg::make

( 177.47, 71.23)); // less east - v.push_back(bg::make

(-178.78, 70.78)); // further east, = west, this is the most right point + v.push_back(bg::make

(-178.78, 72.78)); // further east, = west, this is the most left point + v.push_back(bg::make

(-180.00, 73.12)); // Sort on coordinates in order x,y,z std::sort(v.begin(), v.end(), bg::less

()); std::string s = coordinates(v); - BOOST_CHECK_EQUAL(s, "(177.47, 71.23)(179.73, 71.56)(-178.78, 70.78)"); + BOOST_CHECK_EQUAL(s, "(-178.78, 72.78)(177.47, 71.23)(179.73, 71.56)(180, 70.56)(-180, 73.12)"); + + // Sort ascending on only y-coordinate + std::sort(v.begin(), v.end(), bg::less()); + s = coordinates(v); + BOOST_CHECK_EQUAL(s, "(180, 70.56)(177.47, 71.23)(179.73, 71.56)(-178.78, 72.78)(-180, 73.12)"); // Sort ascending on only x-coordinate std::sort(v.begin(), v.end(), bg::less()); s = coordinates(v); - BOOST_CHECK_EQUAL(s, "(177.47, 71.23)(179.73, 71.56)(-178.78, 70.78)"); + BOOST_CHECK((s == "(-178.78, 72.78)(177.47, 71.23)(179.73, 71.56)(180, 70.56)(-180, 73.12)" + || s == "(-178.78, 72.78)(177.47, 71.23)(179.73, 71.56)(-180, 73.12)(180, 70.56)")); // Sort ascending on only x-coordinate, but override with std-comparison, // (so this is the normal sorting behaviour that would have been used // if it would not have been spherical) - std::sort(v.begin(), v.end(), bg::less >()); - s = coordinates(v); - BOOST_CHECK_EQUAL(s, "(-178.78, 70.78)(177.47, 71.23)(179.73, 71.56)"); + //std::sort(v.begin(), v.end(), bg::less >()); + //s = coordinates(v); + //BOOST_CHECK_EQUAL(s, "(-178.78, 70.78)(177.47, 71.23)(179.73, 71.56)"); } @@ -237,6 +249,8 @@ int test_main(int, char* []) test_2d_sort >(); test_spherical > >(); + test_spherical > >(); + test_spherical > >(); return 0; } From 42902826bea0b9cd3fd09295c8bc398b13f2e332 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 6 Aug 2017 19:35:54 +0200 Subject: [PATCH 060/188] [envelope][expand] Add support for spherical polar Boxes and Segments. --- .../algorithms/detail/envelope/box.hpp | 15 +++-- .../algorithms/detail/envelope/point.hpp | 13 +++- .../detail/envelope/range_of_boxes.hpp | 8 ++- .../algorithms/detail/envelope/segment.hpp | 62 ++++++++++++++++--- .../geometry/algorithms/detail/expand/box.hpp | 30 +++++++-- .../algorithms/detail/expand/point.hpp | 20 ++++-- .../algorithms/detail/expand/segment.hpp | 31 ++++++++-- .../geometry/geometries/helper_geometry.hpp | 15 ++++- .../util/normalize_spheroidal_coordinates.hpp | 52 ++++++++++++++-- 9 files changed, 206 insertions(+), 40 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/envelope/box.hpp b/include/boost/geometry/algorithms/detail/envelope/box.hpp index 795f51392..33b43da25 100644 --- a/include/boost/geometry/algorithms/detail/envelope/box.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/box.hpp @@ -4,11 +4,12 @@ // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -146,12 +147,18 @@ namespace dispatch { -template -struct envelope +template +struct envelope : detail::envelope::envelope_box {}; +template +struct envelope + : detail::envelope::envelope_box_on_spheroid +{}; + + template struct envelope : detail::envelope::envelope_box_on_spheroid diff --git a/include/boost/geometry/algorithms/detail/envelope/point.hpp b/include/boost/geometry/algorithms/detail/envelope/point.hpp index ee0559bf5..86e73f3aa 100644 --- a/include/boost/geometry/algorithms/detail/envelope/point.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/point.hpp @@ -4,11 +4,12 @@ // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2015, 2016. +// This file was modified by Oracle on 2015, 2016, 2017. // Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at @@ -102,12 +103,18 @@ namespace dispatch { -template -struct envelope +template +struct envelope : detail::envelope::envelope_one_point<0, dimension::value> {}; +template +struct envelope + : detail::envelope::envelope_point_on_spheroid +{}; + + template struct envelope : detail::envelope::envelope_point_on_spheroid diff --git a/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp b/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp index 7be97e59e..9b9e2f85d 100644 --- a/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/range_of_boxes.hpp @@ -240,9 +240,15 @@ struct envelope_range_of_boxes RangeOfBoxes const >::type iterator_type; + static const bool is_equatorial = ! boost::is_same + < + typename cs_tag::type, + spherical_polar_tag + >::value; + typedef math::detail::constants_on_spheroid < - coordinate_type, units_type + coordinate_type, units_type, is_equatorial > constants; typedef longitude_interval interval_type; diff --git a/include/boost/geometry/algorithms/detail/envelope/segment.hpp b/include/boost/geometry/algorithms/detail/envelope/segment.hpp index 3843244b5..f635fa611 100644 --- a/include/boost/geometry/algorithms/detail/envelope/segment.hpp +++ b/include/boost/geometry/algorithms/detail/envelope/segment.hpp @@ -80,6 +80,35 @@ struct envelope_segment_call_vertex_latitude } }; +template +struct envelope_segment_convert_polar +{ + template + static inline void pre(T & , T & ) {} + + template + static inline void post(T & , T & ) {} +}; + +template +struct envelope_segment_convert_polar +{ + template + static inline void pre(T & lat1, T & lat2) + { + lat1 = math::latitude_convert_ep(lat1); + lat2 = math::latitude_convert_ep(lat2); + } + + template + static inline void post(T & lat1, T & lat2) + { + lat1 = math::latitude_convert_ep(lat1); + lat2 = math::latitude_convert_ep(lat2); + std::swap(lat1, lat2); + } +}; + template class envelope_segment_impl { @@ -266,29 +295,29 @@ private: Box, box_coordinate_type, Units >::type helper_box_type; - helper_box_type radian_mbr; + helper_box_type helper_mbr; geometry::set < min_corner, 0 - >(radian_mbr, boost::numeric_cast(lon1)); + >(helper_mbr, boost::numeric_cast(lon1)); geometry::set < min_corner, 1 - >(radian_mbr, boost::numeric_cast(lat1)); + >(helper_mbr, boost::numeric_cast(lat1)); geometry::set < max_corner, 0 - >(radian_mbr, boost::numeric_cast(lon2)); + >(helper_mbr, boost::numeric_cast(lon2)); geometry::set < max_corner, 1 - >(radian_mbr, boost::numeric_cast(lat2)); + >(helper_mbr, boost::numeric_cast(lat2)); - transform_units(radian_mbr, mbr); + transform_units(helper_mbr, mbr); } @@ -347,7 +376,14 @@ public: Box& mbr, Strategy const& strategy) { + typedef envelope_segment_convert_polar::type> convert_polar; + + convert_polar::pre(lat1, lat2); + apply(lon1, lat1, lon2, lat2, strategy); + + convert_polar::post(lat1, lat2); + create_box(lon1, lat1, lon2, lat2, mbr); } @@ -366,7 +402,14 @@ public: Strategy const& strategy, CalculationType alp1) { + typedef envelope_segment_convert_polar::type> convert_polar; + + convert_polar::pre(lat1, lat2); + apply(lon1, lat1, lon2, lat2, strategy, alp1); + + convert_polar::post(lat1, lat2); + create_box(lon1, lat1, lon2, lat2, mbr); } }; @@ -407,13 +450,14 @@ struct envelope_segment envelope_one_segment<2, DimensionCount>::apply(p1, p2, mbr, strategy); } - template - static inline void apply(Segment const& segment, Box& mbr) + template + static inline void apply(Segment const& segment, Box& mbr, + Strategy const& strategy) { typename point_type::type p[2]; detail::assign_point_from_index<0>(segment, p[0]); detail::assign_point_from_index<1>(segment, p[1]); - apply(p[0], p[1], mbr); + apply(p[0], p[1], mbr, strategy); } }; diff --git a/include/boost/geometry/algorithms/detail/expand/box.hpp b/include/boost/geometry/algorithms/detail/expand/box.hpp index 450f66533..485f4d25e 100644 --- a/include/boost/geometry/algorithms/detail/expand/box.hpp +++ b/include/boost/geometry/algorithms/detail/expand/box.hpp @@ -81,16 +81,25 @@ struct expand BoxOut, BoxIn, box_tag, box_tag, CSTagOut, CSTag - > : detail::expand::expand_indexed - < - 0, dimension::value - > + > { - BOOST_MPL_ASSERT_MSG((boost::is_same::value), - COORDINATE_SYSTEMS_MUST_BE_THE_SAME, + BOOST_MPL_ASSERT_MSG((false), + NOT_IMPLEMENTED_FOR_THESE_COORDINATE_SYSTEMS, (types())); }; +template +struct expand + < + BoxOut, BoxIn, + box_tag, box_tag, + cartesian_tag, cartesian_tag + > : detail::expand::expand_indexed + < + 0, dimension::value + > +{}; + template struct expand < @@ -100,6 +109,15 @@ struct expand > : detail::expand::box_on_spheroid {}; +template +struct expand + < + BoxOut, BoxIn, + box_tag, box_tag, + spherical_polar_tag, spherical_polar_tag + > : detail::expand::box_on_spheroid +{}; + template struct expand < diff --git a/include/boost/geometry/algorithms/detail/expand/point.hpp b/include/boost/geometry/algorithms/detail/expand/point.hpp index a09db51af..58e104675 100644 --- a/include/boost/geometry/algorithms/detail/expand/point.hpp +++ b/include/boost/geometry/algorithms/detail/expand/point.hpp @@ -223,15 +223,25 @@ struct expand BoxOut, Point, box_tag, point_tag, CSTagOut, CSTag + > +{ + BOOST_MPL_ASSERT_MSG((false), + NOT_IMPLEMENTED_FOR_THESE_COORDINATE_SYSTEMS, + (types())); +}; + + +template +struct expand + < + BoxOut, Point, + box_tag, point_tag, + cartesian_tag, cartesian_tag > : detail::expand::point_loop < 0, dimension::value > -{ - BOOST_MPL_ASSERT_MSG((boost::is_same::value), - COORDINATE_SYSTEMS_MUST_BE_THE_SAME, - (types())); -}; +{}; template struct expand diff --git a/include/boost/geometry/algorithms/detail/expand/segment.hpp b/include/boost/geometry/algorithms/detail/expand/segment.hpp index d229138a8..dddd3d2c7 100644 --- a/include/boost/geometry/algorithms/detail/expand/segment.hpp +++ b/include/boost/geometry/algorithms/detail/expand/segment.hpp @@ -85,15 +85,36 @@ struct expand Box, Segment, box_tag, segment_tag, CSTagOut, CSTag + > +{ + BOOST_MPL_ASSERT_MSG((false), + NOT_IMPLEMENTED_FOR_THESE_COORDINATE_SYSTEMS, + (types())); +}; + +template +< + typename Box, typename Segment +> +struct expand + < + Box, Segment, + box_tag, segment_tag, + cartesian_tag, cartesian_tag > : detail::expand::expand_indexed < 0, dimension::value > -{ - BOOST_MPL_ASSERT_MSG((boost::is_same::value), - COORDINATE_SYSTEMS_MUST_BE_THE_SAME, - (types())); -}; +{}; + +template +struct expand + < + Box, Segment, + box_tag, segment_tag, + spherical_polar_tag, spherical_polar_tag + > : detail::expand::segment +{}; template struct expand diff --git a/include/boost/geometry/geometries/helper_geometry.hpp b/include/boost/geometry/geometries/helper_geometry.hpp index 9cf14a911..f3102fee9 100644 --- a/include/boost/geometry/geometries/helper_geometry.hpp +++ b/include/boost/geometry/geometries/helper_geometry.hpp @@ -1,8 +1,9 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2015, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -10,6 +11,8 @@ #ifndef BOOST_GEOMETRY_GEOMETRIES_HELPER_GEOMETRY_HPP #define BOOST_GEOMETRY_GEOMETRIES_HELPER_GEOMETRY_HPP +#include + #include #include #include @@ -52,6 +55,14 @@ struct default_units template struct cs_tag_to_coordinate_system +{ + BOOST_MPL_ASSERT_MSG((false), + NOT_IMPLEMENTED_FOR_THIS_COORDINATE_SYSTEM, + (types)); +}; + +template +struct cs_tag_to_coordinate_system { typedef cs::cartesian type; }; @@ -63,7 +74,7 @@ struct cs_tag_to_coordinate_system }; template -struct cs_tag_to_coordinate_system +struct cs_tag_to_coordinate_system { typedef cs::spherical type; }; diff --git a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp index 1ab7d9010..eaa686ed1 100644 --- a/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp +++ b/include/boost/geometry/util/normalize_spheroidal_coordinates.hpp @@ -26,8 +26,8 @@ namespace math namespace detail { - -template +// CoordinateType, radian, true +template struct constants_on_spheroid { static inline CoordinateType period() @@ -40,6 +40,13 @@ struct constants_on_spheroid return math::pi(); } + static inline CoordinateType quarter_period() + { + static CoordinateType const + pi_half = math::pi() / CoordinateType(2); + return pi_half; + } + static inline CoordinateType min_longitude() { static CoordinateType const minus_pi = -math::pi(); @@ -65,7 +72,22 @@ struct constants_on_spheroid }; template -struct constants_on_spheroid +struct constants_on_spheroid + : constants_on_spheroid +{ + static inline CoordinateType min_latitude() + { + return CoordinateType(0); + } + + static inline CoordinateType max_latitude() + { + return math::pi(); + } +}; + +template +struct constants_on_spheroid { static inline CoordinateType period() { @@ -77,6 +99,11 @@ struct constants_on_spheroid return CoordinateType(180.0); } + static inline CoordinateType quarter_period() + { + return CoordinateType(90.0); + } + static inline CoordinateType min_longitude() { return CoordinateType(-180.0); @@ -98,6 +125,21 @@ struct constants_on_spheroid } }; +template +struct constants_on_spheroid + : constants_on_spheroid +{ + static inline CoordinateType min_latitude() + { + return CoordinateType(0); + } + + static inline CoordinateType max_latitude() + { + return CoordinateType(180.0); + } +}; + } // namespace detail #endif // DOXYGEN_NO_DETAIL @@ -112,7 +154,7 @@ inline CoordinateType latitude_convert_ep(CoordinateType const& lat) Units > constants; - return constants::max_latitude() - lat; + return constants::quarter_period() - lat; } @@ -128,7 +170,7 @@ static bool is_latitude_pole(T const& lat) return math::equals(math::abs(IsEquatorial ? lat : math::latitude_convert_ep(lat)), - constants::max_latitude()); + constants::quarter_period()); } From 84b41b5a9cb49a9025b951b6d5537cbdc6fab367 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 6 Aug 2017 19:36:34 +0200 Subject: [PATCH 061/188] [test][envelope][expand] Add test cases for spherical polar Boxes and Segments. --- .../envelope_expand/envelope_on_spheroid.cpp | 55 ++++++++++++++++++- .../envelope_expand/expand_on_spheroid.cpp | 23 +++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp index 278a44dd9..d84f8936d 100644 --- a/test/algorithms/envelope_expand/envelope_on_spheroid.cpp +++ b/test/algorithms/envelope_expand/envelope_on_spheroid.cpp @@ -805,6 +805,32 @@ BOOST_AUTO_TEST_CASE( envelope_segment_sphere ) 1-heps, 1, 1, 2); } +BOOST_AUTO_TEST_CASE( envelope_segment_spherical_polar ) +{ + typedef bg::cs::spherical coordinate_system_type; + typedef bg::model::point P; + typedef bg::model::segment

G; + typedef bg::model::box

B; + typedef test_envelope_on_sphere_or_spheroid tester; + + tester::apply("s01", + from_wkt("SEGMENT(10 10,40 40)"), + 10, 10, 40, 40); + + tester::apply("s02", + from_wkt("SEGMENT(10 80,40 80)"), + 10, 90 - 10.345270046149988, 40, 80); + + tester::apply("s03", + from_wkt("SEGMENT(160 80,-170 80)"), + 160, 90 - 10.34527004614999, 190, 80); + + // segment ending at the north pole + tester::apply("s05", + from_wkt("SEGMENT(40 45,80 0)"), + 40, 0, 40, 45); +} + BOOST_AUTO_TEST_CASE( envelope_segment_spheroid ) { @@ -1667,8 +1693,7 @@ BOOST_AUTO_TEST_CASE( envelope_multipoint_with_height ) template void test_envelope_box() { - typedef bg::cs::spherical_equatorial coordinate_system_type; - typedef bg::model::point P; + typedef bg::model::point P; typedef bg::model::box

G; typedef bg::model::box

B; typedef test_envelope_on_sphere_or_spheroid tester; @@ -1857,10 +1882,36 @@ void test_envelope_box() tester::apply("b100", G(P(1-heps, 1-heps), P(1, 1)), 1-heps, 1-heps, 1, 1); } +template +void test_envelope_box_polar() +{ + typedef bg::model::point P; + typedef bg::model::box

G; + typedef bg::model::box

B; + typedef test_envelope_on_sphere_or_spheroid tester; + + tester::apply("b01", + from_wkt("BOX(10 10,20 20)"), + 10, 10, 20, 20); + + tester::apply("b02a", + from_wkt("BOX(170 10,-170 20)"), + 170, 10, 190, 20); + + tester::apply("b10b", + from_wkt("BOX(0 0,10 180)"), + 0, 0, 10, 180); + + tester::apply("b16a", + from_wkt("BOX(170 40,-170 180)"), + 170, 40, 190, 180); +} + BOOST_AUTO_TEST_CASE( envelope_box ) { test_envelope_box >(); test_envelope_box >(); + test_envelope_box_polar >(); } diff --git a/test/algorithms/envelope_expand/expand_on_spheroid.cpp b/test/algorithms/envelope_expand/expand_on_spheroid.cpp index 1c2436f41..1c535aa87 100644 --- a/test/algorithms/envelope_expand/expand_on_spheroid.cpp +++ b/test/algorithms/envelope_expand/expand_on_spheroid.cpp @@ -1,10 +1,11 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2015-2016, Oracle and/or its affiliates. +// Copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Licensed under the Boost Software License version 1.0. // http://www.boost.org/users/license.html @@ -704,6 +705,26 @@ BOOST_AUTO_TEST_CASE( expand_segment_sphere ) 10, -90, 100, 45); } +BOOST_AUTO_TEST_CASE( expand_segment_spherical_polar ) +{ + typedef bg::cs::spherical coordinate_system_type; + typedef bg::model::point point_type; + typedef bg::model::box B; + typedef bg::model::segment G; + typedef test_expand_on_spheroid tester; + + tester::apply("s02", + from_wkt("BOX(20 20,50 50)"), + from_wkt("SEGMENT(10 10,40 20)"), + 10, 10, 50, 50); + + // segment ending at the north pole + tester::apply("s04", + from_wkt("BOX(5 15,50 50)"), + from_wkt("SEGMENT(40 45,80 0)"), + 5, 0, 50, 50); +} + BOOST_AUTO_TEST_CASE( expand_segment_spheroid ) { typedef bg::cs::geographic coordinate_system_type; From bfd0456f247bc68ccd65910aef2bf0b82b76e767 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 9 Aug 2017 20:39:26 +0200 Subject: [PATCH 062/188] [test][compare] Comment out unused type. --- test/policies/compare.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/policies/compare.cpp b/test/policies/compare.cpp index b9d0be01a..60c4021d5 100644 --- a/test/policies/compare.cpp +++ b/test/policies/compare.cpp @@ -204,7 +204,7 @@ void test_2d_sort() template void test_spherical() { - typedef typename bg::coordinate_type

::type ct; + //typedef typename bg::coordinate_type

::type ct; std::vector

v; v.push_back(bg::make

( 180.00, 70.56)); From 6f17140ecc749664981c1dacfad913d53862bb80 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 11 Aug 2017 20:08:57 +0200 Subject: [PATCH 063/188] [test][is_valid] Add test of geographic polygon. --- test/algorithms/Jamfile.v2 | 3 ++- test/algorithms/is_valid_geo.cpp | 38 +++++++++++++++++++++++++++++++ test/algorithms/test_is_valid.hpp | 24 +++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 test/algorithms/is_valid_geo.cpp diff --git a/test/algorithms/Jamfile.v2 b/test/algorithms/Jamfile.v2 index 70d9688b0..6797b4147 100644 --- a/test/algorithms/Jamfile.v2 +++ b/test/algorithms/Jamfile.v2 @@ -4,7 +4,7 @@ # Copyright (c) 2008-2015 Bruno Lalande, Paris, France. # Copyright (c) 2009-2015 Mateusz Loskot, London, UK. # -# This file was modified by Oracle on 2014, 2015, 2016,2017. +# This file was modified by Oracle on 2014, 2015, 2016, 2017. # Modifications copyright (c) 2014-2017, Oracle and/or its affiliates. # # Contributed and/or modified by Vissarion Fisikopoulos, on behalf of Oracle @@ -36,6 +36,7 @@ test-suite boost-geometry-algorithms [ run is_simple_geo.cpp : : : : algorithms_is_simple_geo ] [ run is_valid.cpp : : : : algorithms_is_valid ] [ run is_valid_failure.cpp : : : : algorithms_is_valid_failure ] + [ run is_valid_geo.cpp : : : : algorithms_is_valid_geo ] [ run make.cpp : : : : algorithms_make ] [ run maximum_gap.cpp : : : : algorithms_maximum_gap ] [ run num_geometries.cpp : : : : algorithms_num_geometries ] diff --git a/test/algorithms/is_valid_geo.cpp b/test/algorithms/is_valid_geo.cpp new file mode 100644 index 000000000..e2a54146b --- /dev/null +++ b/test/algorithms/is_valid_geo.cpp @@ -0,0 +1,38 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) +// Unit Test + +// Copyright (c) 2014-2017, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_TEST_MODULE +#define BOOST_TEST_MODULE test_is_valid_geo +#endif + +#include +#include + +#include + +#include "test_is_valid.hpp" + +#include + +#include +#include +#include + +BOOST_AUTO_TEST_CASE( test_is_valid_geo_polygon ) +{ + typedef bg::model::point > pt; + typedef bg::model::polygon G; + + typedef validity_tester_geo_areal tester; + typedef test_valid test; + + test::apply("p01", "POLYGON((-1 -1, 1 -1, 1 1, -1 1, -1 -1),(-0.5 -0.5, -0.5 0.5, 0.0 0.0, -0.5 -0.5),(0.0 0.0, 0.5 0.5, 0.5 -0.5, 0.0 0.0))", true); +} diff --git a/test/algorithms/test_is_valid.hpp b/test/algorithms/test_is_valid.hpp index 2776327d9..c230eca19 100644 --- a/test/algorithms/test_is_valid.hpp +++ b/test/algorithms/test_is_valid.hpp @@ -325,6 +325,30 @@ struct validity_tester_areal }; +template +struct validity_tester_geo_areal +{ + template + static inline bool apply(Geometry const& geometry) + { + bg::is_valid_default_policy visitor; + bg::strategy::intersection::geographic_segments<> s; + return bg::is_valid(geometry, visitor, s); + } + + template + static inline std::string reason(Geometry const& geometry) + { + std::ostringstream oss; + bg::failing_reason_policy visitor(oss); + bg::strategy::intersection::geographic_segments<> s; + bg::is_valid(geometry, visitor, s); + return oss.str(); + } + +}; + + //---------------------------------------------------------------------------- From 11ce2ac02ed47a92e2b1601933f88e2071d8a2f8 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 14 Aug 2017 17:39:18 +0200 Subject: [PATCH 064/188] [algorithms] Fix direction_code different Point types handling and unused variable warning. --- .../algorithms/detail/direction_code.hpp | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/direction_code.hpp b/include/boost/geometry/algorithms/detail/direction_code.hpp index c5c522110..3a7d3d878 100644 --- a/include/boost/geometry/algorithms/detail/direction_code.hpp +++ b/include/boost/geometry/algorithms/detail/direction_code.hpp @@ -114,7 +114,9 @@ struct direction_code_impl typedef typename geometry::select_coordinate_type ::type calc_t; typedef math::detail::constants_on_spheroid constants1; typedef math::detail::constants_on_spheroid constants2; - typedef math::detail::constants_on_spheroid constants; + static coord1_t const pi_half1 = constants1::max_latitude(); + static coord2_t const pi_half2 = constants2::max_latitude(); + static calc_t const c0 = 0; coord1_t const a0 = geometry::get<0>(segment_a); coord1_t const a1 = geometry::get<1>(segment_a); @@ -122,11 +124,6 @@ struct direction_code_impl coord1_t const b1 = geometry::get<1>(segment_b); coord2_t const p0 = geometry::get<0>(p); coord2_t const p1 = geometry::get<1>(p); - coord1_t const pi_half1 = constants1::max_latitude(); - coord2_t const pi_half2 = constants2::max_latitude(); - calc_t const pi = constants::half_period(); - calc_t const pi_half = constants::max_latitude(); - calc_t const c0 = 0; if ( (math::equals(b0, a0) && math::equals(b1, a1)) || (math::equals(b0, p0) && math::equals(b1, p1)) ) @@ -147,12 +144,12 @@ struct direction_code_impl // NOTE: as opposed to the implementation for cartesian CS // here point b is the origin - calc_t const dlon1 = math::longitude_distance_signed(b0, a0); - calc_t const dlon2 = math::longitude_distance_signed(b0, p0); + calc_t const dlon1 = math::longitude_distance_signed(b0, a0); + calc_t const dlon2 = math::longitude_distance_signed(b0, p0); bool is_antilon1 = false, is_antilon2 = false; - calc_t const dlat1 = latitude_distance_signed(b1, a1, dlon1, pi, is_antilon1); - calc_t const dlat2 = latitude_distance_signed(b1, p1, dlon2, pi, is_antilon2); + calc_t const dlat1 = latitude_distance_signed(b1, a1, dlon1, is_antilon1); + calc_t const dlat2 = latitude_distance_signed(b1, p1, dlon2, is_antilon2); calc_t mx = is_a_pole || is_b_pole || is_p_pole ? c0 : @@ -176,10 +173,12 @@ struct direction_code_impl return s1 == s2 ? -1 : 1; } - template - static inline T latitude_distance_signed(T const& lat1, T const& lat2, T const& lon_ds, T const& pi, bool & is_antilon) + template + static inline T latitude_distance_signed(T const& lat1, T const& lat2, T const& lon_ds, bool & is_antilon) { - T const c0 = 0; + typedef math::detail::constants_on_spheroid constants; + static T const pi = constants::half_period(); + static T const c0 = 0; T res = lat2 - lat1; From 4e99afb9d0fef6ed551da5b9e6270c815c285955 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 16 Aug 2017 16:25:25 +0200 Subject: [PATCH 065/188] [overlay] revise implementation to set traversed - now set in traversal itself, which makes code in overlay.hpp a bit less complex --- .../algorithms/detail/overlay/overlay.hpp | 76 +++++++------------ .../detail/overlay/select_rings.hpp | 6 +- .../algorithms/detail/overlay/traversal.hpp | 15 +++- .../detail/overlay/traversal_ring_creator.hpp | 8 +- .../algorithms/detail/overlay/traverse.hpp | 10 ++- 5 files changed, 58 insertions(+), 57 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 31851292b..799b52e09 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -99,34 +99,26 @@ template inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, Clusters const& clusters) { typedef typename boost::range_value::type turn_type; + typedef typename turn_type::turn_operation_type turn_operation_type; typedef typename turn_type::container_type container_type; static const operation_type target_operation = operation_from_overlay::value; static const operation_type opposite_operation - = target_operation == operation_union ? operation_intersection : operation_union; + = target_operation == operation_union + ? operation_intersection + : operation_union; - signed_size_type turn_index = 0; for (typename boost::range_iterator::type it = boost::begin(turns); it != boost::end(turns); - ++it, turn_index++) + ++it) { - typename boost::range_value::type const& turn = *it; + turn_type const& turn = *it; + bool const both_opposite = turn.both(opposite_operation); bool const colocated_target = target_operation == operation_union ? turn.colocated_uu : turn.colocated_ii; - bool const colocated_opp = target_operation == operation_union - ? turn.colocated_ii : turn.colocated_uu; - bool const both_opposite = turn.both(opposite_operation); - - bool const traversed - = turn.operations[0].visited.finalized() - || turn.operations[0].visited.rejected() - || turn.operations[1].visited.finalized() - || turn.operations[1].visited.rejected() - || turn.both(operation_blocked) - || turn.combination(opposite_operation, operation_blocked); bool is_closed = false; if (turn.cluster_id >= 0 && target_operation == operation_union) @@ -143,47 +135,35 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C op_it != boost::end(turn.operations); ++op_it) { - ring_identifier const ring_id - ( - op_it->seg_id.source_index, - op_it->seg_id.multi_index, - op_it->seg_id.ring_index - ); + turn_operation_type const& op = *op_it; - if (traversed || is_closed) - { - turn_info_map[ring_id].has_traversed_turn = true; - } - else if (both_opposite && colocated_target) - { - // For union: ii, colocated with a uu - // For example, two interior rings touch where two exterior rings also touch. - // The interior rings are not yet traversed, and should be taken from the input + // Block, for example, i for union and u for intersection + // Don't block self-uu for intersection + // Don't block self-ii for union + // Don't block i/u if there is an ii too, for a union + bool opposite_op = op.operation == opposite_operation + && ! colocated_target + && ! (both_opposite + && is_self_turn(turn)); - // For intersection: uu, colocated with an ii - // unless it is two interior inner rings colocated with a uu + if (opposite_op + || is_closed + || turn.both(operation_blocked) + || turn.combination(opposite_operation, operation_blocked)) + { + ring_identifier const ring_id + ( + op.seg_id.source_index, + op.seg_id.multi_index, + op.seg_id.ring_index + ); - // So don't set has_traversed_turn here - } - else if (both_opposite && ! is_self_turn(turn)) - { - // For union, mark any ring with a ii turn as traversed - // For intersection, any uu - but not if it is a self-turn - turn_info_map[ring_id].has_traversed_turn = true; - } - else if (colocated_opp - && ! colocated_target - && ! turn.both(opposite_operation)) - { - // For union, a turn colocated with ii and NOT with uu/ux - // For intersection v.v. - turn_info_map[ring_id].has_traversed_turn = true; + turn_info_map[ring_id].has_blocked_turn = true; } } } } - template < typename GeometryOut, overlay_type OverlayType, bool ReverseOut, diff --git a/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp b/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp index 67a4f4bb7..262ba748a 100644 --- a/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp @@ -41,10 +41,12 @@ namespace detail { namespace overlay struct ring_turn_info { bool has_traversed_turn; + bool has_blocked_turn; bool within_other; ring_turn_info() : has_traversed_turn(false) + , has_blocked_turn(false) , within_other(false) {} }; @@ -265,9 +267,9 @@ inline void update_ring_selection(Geometry1 const& geometry1, info = tcit->second; // Copy by value } - if (info.has_traversed_turn) + if (info.has_traversed_turn || info.has_blocked_turn) { - // This turn is traversed (or blocked), + // This turn is traversed or blocked, // don't include the original ring continue; } diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index 932c0dd48..27727aee1 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -123,7 +123,8 @@ struct traversal { } - inline void finalize_visit_info() + template + inline void finalize_visit_info(TurnInfoMap& turn_info_map) { for (typename boost::range_iterator::type it = boost::begin(m_turns); @@ -134,6 +135,18 @@ struct traversal for (int i = 0; i < 2; i++) { turn_operation_type& op = turn.operations[i]; + if (op.visited.visited() + || op.visited.started() + || op.visited.finished() ) + { + ring_identifier const ring_id + ( + op.seg_id.source_index, + op.seg_id.multi_index, + op.seg_id.ring_index + ); + turn_info_map[ring_id].has_traversed_turn = true; + } op.visited.finalize(); } } diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp index 6a346423e..ef50d73be 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp @@ -41,6 +41,7 @@ template typename Geometry1, typename Geometry2, typename Turns, + typename TurnInfoMap, typename Clusters, typename IntersectionStrategy, typename RobustPolicy, @@ -60,13 +61,15 @@ struct traversal_ring_creator = operation_from_overlay::value; inline traversal_ring_creator(Geometry1 const& geometry1, Geometry2 const& geometry2, - Turns& turns, Clusters const& clusters, + Turns& turns, TurnInfoMap& turn_info_map, + Clusters const& clusters, IntersectionStrategy const& intersection_strategy, RobustPolicy const& robust_policy, Visitor& visitor) : m_trav(geometry1, geometry2, turns, clusters, robust_policy,visitor) , m_geometry1(geometry1) , m_geometry2(geometry2) , m_turns(turns) + , m_turn_info_map(turn_info_map) , m_clusters(clusters) , m_intersection_strategy(intersection_strategy) , m_robust_policy(robust_policy) @@ -274,7 +277,7 @@ struct traversal_ring_creator clean_closing_dups_and_spikes(ring, m_robust_policy); rings.push_back(ring); - m_trav.finalize_visit_info(); + m_trav.finalize_visit_info(m_turn_info_map); finalized_ring_size++; } } @@ -335,6 +338,7 @@ private: Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; Turns& m_turns; + TurnInfoMap& m_turn_info_map; // contains turn-info information per ring Clusters const& m_clusters; IntersectionStrategy const& m_intersection_strategy; RobustPolicy const& m_robust_policy; diff --git a/include/boost/geometry/algorithms/detail/overlay/traverse.hpp b/include/boost/geometry/algorithms/detail/overlay/traverse.hpp index 058f6c945..2ec71850b 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traverse.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traverse.hpp @@ -62,14 +62,16 @@ public : typename RobustPolicy, typename Turns, typename Rings, - typename Visitor, - typename Clusters + typename TurnInfoMap, + typename Clusters, + typename Visitor > static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, IntersectionStrategy const& intersection_strategy, RobustPolicy const& robust_policy, Turns& turns, Rings& rings, + TurnInfoMap& turn_info_map, Clusters& clusters, Visitor& visitor) { @@ -89,11 +91,11 @@ public : < Reverse1, Reverse2, OverlayType, Geometry1, Geometry2, - Turns, Clusters, + Turns, TurnInfoMap, Clusters, IntersectionStrategy, RobustPolicy, Visitor, Backtrack - > trav(geometry1, geometry2, turns, clusters, + > trav(geometry1, geometry2, turns, turn_info_map, clusters, intersection_strategy, robust_policy, visitor); std::size_t finalized_ring_size = boost::size(rings); From ff918e4776c5f92c83615b040fa9a8a66b789317 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 16 Aug 2017 16:25:50 +0200 Subject: [PATCH 066/188] [test] dont ignore validity for case 65 --- .../set_operations/intersection/intersection_multi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 9d0b33aa3..c3717ef0c 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -314,7 +314,7 @@ void test_areal() #else TEST_INTERSECTION_IGNORE(case_recursive_boxes_64, 3, -1, 17.25); #endif - TEST_INTERSECTION_IGNORE(case_recursive_boxes_65, 3, -1, 17.25); + TEST_INTERSECTION(case_recursive_boxes_65, 3, -1, 17.25); #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_INTERSECTION(case_recursive_boxes_66, 4, -1, 16.0); From 9f4682403e5c6391360e23be424e227c09e9ab0f Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 22 Aug 2017 00:28:08 +0200 Subject: [PATCH 067/188] [strategies] Fix winding strategy for specific cases in spherical and geographic. Implement as separate strategies per CS: cartesian_winding, spherical_winding and geographic_winding. The last one taking Geodesic FormulaPolicy and Spheroid (interface consistent with other geographic strategies). Leave agnostic winding strategy for backward compatibility, sort of because SideStrategy parameter was removed so it's compatible with Boost 1.63 and below. Internally using one of the above strategies. --- .../agnostic/point_in_poly_winding.hpp | 1030 +++++++++++------ 1 file changed, 698 insertions(+), 332 deletions(-) diff --git a/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp b/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp index 0a797ac0f..557413f10 100644 --- a/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp +++ b/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include namespace boost { namespace geometry @@ -34,292 +37,16 @@ namespace boost { namespace geometry namespace strategy { namespace within { -// 1 deg or pi/180 rad -template ::type> -struct winding_small_angle -{ - typedef typename coordinate_system::type cs_t; - typedef math::detail::constants_on_spheroid - < - CalculationType, - typename cs_t::units - > constants; - - static inline CalculationType apply() - { - return constants::half_period() / CalculationType(180); - } -}; - - -// Fix for https://svn.boost.org/trac/boost/ticket/9628 -// For floating point coordinates, the coordinate of a point is compared -// with the segment's points using some EPS. If the coordinates are "equal" -// the sides are calculated. Therefore we can treat a segment as a long areal -// geometry having some width. There is a small ~triangular area somewhere -// between the segment's effective area and a segment's line used in sides -// calculation where the segment is on the one side of the line but on the -// other side of a segment (due to the width). -// Below picture assuming D = 1, if D = 0 horiz<->vert, E<->N, RIGHT<->UP. -// For the s1 of a segment going NE the real side is RIGHT but the point may -// be detected as LEFT, like this: -// RIGHT -// ___-----> -// ^ O Pt __ __ -// EPS __ __ -// v__ __ BUT DETECTED AS LEFT OF THIS LINE -// _____7 -// _____/ -// _____/ -// In the code below actually D = 0, so segments are nearly-vertical -// Called when the point is on the same level as one of the segment's points -// but the point is not aligned with a vertical segment -template -struct winding_side_equal -{ - typedef typename strategy::side::services::default_strategy - < - CSTag - >::type strategy_side_type; - - template - static inline int apply(Point const& point, - PointOfSegment const& se, - int count) - { - typedef typename coordinate_type::type scoord_t; - typedef typename coordinate_system::type::units units_t; - - if (math::equals(get<1>(point), get<1>(se))) - return 0; - - // Create a horizontal segment intersecting the original segment's endpoint - // equal to the point, with the derived direction (E/W). - PointOfSegment ss1, ss2; - set<1>(ss1, get<1>(se)); - set<0>(ss1, get<0>(se)); - set<1>(ss2, get<1>(se)); - scoord_t ss20 = get<0>(se); - if (count > 0) - { - ss20 += winding_small_angle::apply(); - } - else - { - ss20 -= winding_small_angle::apply(); - } - math::normalize_longitude(ss20); - set<0>(ss2, ss20); - - // Check the side using this vertical segment - return strategy_side_type::apply(ss1, ss2, point); - } -}; -// The optimization for cartesian -template <> -struct winding_side_equal -{ - template - static inline int apply(Point const& point, - PointOfSegment const& se, - int count) - { - // NOTE: for D=0 the signs would be reversed - return math::equals(get<1>(point), get<1>(se)) ? - 0 : - get<1>(point) < get<1>(se) ? - // assuming count is equal to 1 or -1 - -count : // ( count > 0 ? -1 : 1) : - count; // ( count > 0 ? 1 : -1) ; - } -}; - - -template ::type> -struct winding_check_touch -{ - typedef CalculationType calc_t; - typedef typename coordinate_system::type::units units_t; - typedef math::detail::constants_on_spheroid constants; - - template - static inline int apply(Point const& point, - PointOfSegment const& seg1, - PointOfSegment const& seg2, - State& state, - bool& eq1, - bool& eq2) - { - calc_t const pi = constants::half_period(); - calc_t const pi2 = pi / calc_t(2); - - calc_t const px = get<0>(point); - calc_t const s1x = get<0>(seg1); - calc_t const s2x = get<0>(seg2); - calc_t const py = get<1>(point); - calc_t const s1y = get<1>(seg1); - calc_t const s2y = get<1>(seg2); - - // NOTE: lat in {-90, 90} and arbitrary lon - // it doesn't matter what lon it is if it's a pole - // so e.g. if one of the segment endpoints is a pole - // then only the other lon matters - - bool eq1_strict = math::equals(s1x, px); - bool eq2_strict = math::equals(s2x, px); - - eq1 = eq1_strict // lon strictly equal to s1 - || math::equals(s1y, pi2) || math::equals(s1y, -pi2); // s1 is pole - eq2 = eq2_strict // lon strictly equal to s2 - || math::equals(s2y, pi2) || math::equals(s2y, -pi2); // s2 is pole - - // segment overlapping pole - calc_t s1x_anti = s1x + constants::half_period(); - math::normalize_longitude(s1x_anti); - bool antipodal = math::equals(s2x, s1x_anti); - if (antipodal) - { - eq1 = eq2 = eq1 || eq2; - - // segment overlapping pole and point is pole - if (math::equals(py, pi2) || math::equals(py, -pi2)) - { - eq1 = eq2 = true; - } - } - - // Both equal p -> segment vertical - // The only thing which has to be done is check if point is ON segment - if (eq1 && eq2) - { - // segment endpoints on the same sides of the globe - if (! antipodal - // p's lat between segment endpoints' lats - ? (s1y <= py && s2y >= py) || (s2y <= py && s1y >= py) - // going through north or south pole? - : (pi - s1y - s2y <= pi - ? (eq1_strict && s1y <= py) || (eq2_strict && s2y <= py) // north - || math::equals(py, pi2) // point on north pole - : (eq1_strict && s1y >= py) || (eq2_strict && s2y >= py)) // south - || math::equals(py, -pi2) // point on south pole - ) - { - state.m_touches = true; - } - return true; - } - return false; - } -}; -// The optimization for cartesian -template -struct winding_check_touch -{ - typedef CalculationType calc_t; - - template - static inline bool apply(Point const& point, - PointOfSegment const& seg1, - PointOfSegment const& seg2, - State& state, - bool& eq1, - bool& eq2) - { - calc_t const px = get<0>(point); - calc_t const s1x = get<0>(seg1); - calc_t const s2x = get<0>(seg2); - - eq1 = math::equals(s1x, px); - eq2 = math::equals(s2x, px); - - // Both equal p -> segment vertical - // The only thing which has to be done is check if point is ON segment - if (eq1 && eq2) - { - calc_t const py = get<1>(point); - calc_t const s1y = get<1>(seg1); - calc_t const s2y = get<1>(seg2); - if ((s1y <= py && s2y >= py) || (s2y <= py && s1y >= py)) - { - state.m_touches = true; - } - return true; - } - return false; - } -}; - - -// Called if point is not aligned with a vertical segment -template ::type> -struct winding_calculate_count -{ - typedef CalculationType calc_t; - typedef typename coordinate_system::type::units units_t; - - static inline bool greater(calc_t const& l, calc_t const& r) - { - calc_t diff = l - r; - math::normalize_longitude(diff); - return diff > calc_t(0); - } - - static inline int apply(calc_t const& p, - calc_t const& s1, calc_t const& s2, - bool eq1, bool eq2) - { - // Probably could be optimized by avoiding normalization for some comparisons - // e.g. s1 > p could be calculated from p > s1 - - // If both segment endpoints were poles below checks wouldn't be enough - // but this means that either both are the same or that they are N/S poles - // and therefore the segment is not valid. - // If needed (eq1 && eq2 ? 0) could be returned - - return - eq1 ? (greater(s2, p) ? 1 : -1) // Point on level s1, E/W depending on s2 - : eq2 ? (greater(s1, p) ? -1 : 1) // idem - : greater(p, s1) && greater(s2, p) ? 2 // Point between s1 -> s2 --> E - : greater(p, s2) && greater(s1, p) ? -2 // Point between s2 -> s1 --> W - : 0; - } -}; -// The optimization for cartesian -template -struct winding_calculate_count -{ - typedef CalculationType calc_t; - - static inline int apply(calc_t const& p, - calc_t const& s1, calc_t const& s2, - bool eq1, bool eq2) - { - return - eq1 ? (s2 > p ? 1 : -1) // Point on level s1, E/W depending on s2 - : eq2 ? (s1 > p ? -1 : 1) // idem - : s1 < p && s2 > p ? 2 // Point between s1 -> s2 --> E - : s2 < p && s1 > p ? -2 // Point between s2 -> s1 --> W - : 0; - } -}; - /*! -\brief Within detection using winding rule +\brief Within detection using winding rule in cartesian coordinate system. \ingroup strategies \tparam Point \tparam_point \tparam PointOfSegment \tparam_segment_point -\tparam SideStrategy Side strategy \tparam CalculationType \tparam_calculation \author Barend Gehrels \note The implementation is inspired by terralib http://www.terralib.org (LGPL) \note but totally revised afterwards, especially for cases on segments -\note Only dependant on "side", -> agnostic, suitable for spherical/latlong \qbk{ [heading See also] @@ -330,14 +57,12 @@ template < typename Point, typename PointOfSegment = Point, - typename SideStrategy = typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type, typename CalculationType = void > -class winding +class cartesian_winding { + typedef side::side_by_triangle side_strategy_type; + typedef typename select_calculation_type < Point, @@ -357,10 +82,7 @@ class winding } public : - friend class winding; - - template - friend struct winding_check_touch; + friend class cartesian_winding; inline counter() : m_count(0) @@ -369,23 +91,231 @@ class winding }; - static inline int check_segment(Point const& point, - PointOfSegment const& seg1, PointOfSegment const& seg2, - counter& state, bool& eq1, bool& eq2) +public: + typedef typename side_strategy_type::envelope_strategy_type envelope_strategy_type; + + static inline envelope_strategy_type get_envelope_strategy() { - if (winding_check_touch - ::apply(point, seg1, seg2, state, eq1, eq2)) + return side_strategy_type::get_envelope_strategy(); + } + + typedef typename side_strategy_type::disjoint_strategy_type disjoint_strategy_type; + + static inline disjoint_strategy_type get_disjoint_strategy() + { + return side_strategy_type::get_disjoint_strategy(); + } + + // Typedefs and static methods to fulfill the concept + typedef Point point_type; + typedef PointOfSegment segment_point_type; + typedef counter state_type; + + static inline bool apply(Point const& point, + PointOfSegment const& s1, PointOfSegment const& s2, + counter& state) + { + bool eq1 = false; + bool eq2 = false; + + int count = check_segment(point, s1, s2, state, eq1, eq2); + if (count != 0) + { + int side = 0; + if (count == 1 || count == -1) + { + side = side_equal(point, eq1 ? s1 : s2, count); + } + else // count == 2 || count == -2 + { + // 1 left, -1 right + side = side_strategy_type::apply(s1, s2, point); + } + + if (side == 0) + { + // Point is lying on segment + state.m_touches = true; + state.m_count = 0; + return false; + } + + // Side is NEG for right, POS for left. + // The count is -2 for left, 2 for right (or -1/1) + // Side positive thus means RIGHT and LEFTSIDE or LEFT and RIGHTSIDE + // See accompagnying figure (TODO) + if (side * count > 0) + { + state.m_count += count; + } + } + return ! state.m_touches; + } + + static inline int result(counter const& state) + { + return state.code(); + } + +private: + static inline int check_segment(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, bool& eq2) + { + if (check_touch(point, seg1, seg2, state, eq1, eq2)) { return 0; } + return calculate_count(point, seg1, seg2, eq1, eq2); + } + + static inline bool check_touch(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, bool& eq2) + { + calculation_type const px = get<0>(point); + calculation_type const s1x = get<0>(seg1); + calculation_type const s2x = get<0>(seg2); + + eq1 = math::equals(s1x, px); + eq2 = math::equals(s2x, px); + + // Both equal p -> segment vertical + // The only thing which has to be done is check if point is ON segment + if (eq1 && eq2) + { + calculation_type const py = get<1>(point); + calculation_type const s1y = get<1>(seg1); + calculation_type const s2y = get<1>(seg2); + if ((s1y <= py && s2y >= py) || (s2y <= py && s1y >= py)) + { + state.m_touches = true; + } + return true; + } + return false; + } + + static inline int calculate_count(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + bool eq1, bool eq2) + { calculation_type const p = get<0>(point); calculation_type const s1 = get<0>(seg1); calculation_type const s2 = get<0>(seg2); - return winding_calculate_count - ::apply(p, s1, s2, eq1, eq2); + + return eq1 ? (s2 > p ? 1 : -1) // Point on level s1, E/W depending on s2 + : eq2 ? (s1 > p ? -1 : 1) // idem + : s1 < p && s2 > p ? 2 // Point between s1 -> s2 --> E + : s2 < p && s1 > p ? -2 // Point between s2 -> s1 --> W + : 0; } + static inline int side_equal(Point const& point, + PointOfSegment const& se, + int count) + { + // NOTE: for D=0 the signs would be reversed + return math::equals(get<1>(point), get<1>(se)) ? + 0 : + get<1>(point) < get<1>(se) ? + // assuming count is equal to 1 or -1 + -count : // ( count > 0 ? -1 : 1) : + count; // ( count > 0 ? 1 : -1) ; + } +}; + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template +< + typename Point, + typename PointOfSegment = Point, + typename SideStrategy = typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type, + typename CalculationType = void +> +class spherical_winding_base +{ + typedef typename select_calculation_type + < + Point, + PointOfSegment, + CalculationType + >::type calculation_type; + + typedef typename coordinate_system::type::units units_t; + typedef math::detail::constants_on_spheroid constants; + + /*! subclass to keep state */ + class counter + { + int m_count; + //int m_count_n; + int m_count_s; + int m_raw_count; + int m_raw_count_anti; + bool m_touches; + + inline int code() const + { + if (m_touches) + { + return 0; + } + + if (m_raw_count != 0 && m_raw_count_anti != 0) + { + if (m_raw_count > 0) // right, wrap around south pole + { + return (m_count + m_count_s) == 0 ? -1 : 1; + } + else // left, wrap around north pole + { + //return (m_count + m_count_n) == 0 ? -1 : 1; + // m_count_n is 0 + return m_count == 0 ? -1 : 1; + } + } + + return m_count == 0 ? -1 : 1; + } + + public : + friend class spherical_winding_base; + + inline counter() + : m_count(0) + //, m_count_n(0) + , m_count_s(0) + , m_raw_count(0) + , m_raw_count_anti(0) + , m_touches(false) + {} + + }; + + struct count_info + { + explicit count_info(int c = 0, bool ia = false) + : count(c) + , is_anti(ia) + {} + + int count; + bool is_anti; + }; public: typedef typename SideStrategy::envelope_strategy_type envelope_strategy_type; @@ -402,11 +332,12 @@ public: return m_side_strategy.get_disjoint_strategy(); } - winding() + spherical_winding_base() {} - explicit winding(SideStrategy const& side_strategy) - : m_side_strategy(side_strategy) + template + explicit spherical_winding_base(Model const& model) + : m_side_strategy(model) {} // Typedefs and static methods to fulfill the concept @@ -418,41 +349,73 @@ public: PointOfSegment const& s1, PointOfSegment const& s2, counter& state) const { - typedef typename cs_tag::type cs_t; - bool eq1 = false; bool eq2 = false; - boost::ignore_unused(eq2); + bool s_antipodal = false; - int count = check_segment(point, s1, s2, state, eq1, eq2); - if (count != 0) + count_info ci = check_segment(point, s1, s2, state, eq1, eq2, s_antipodal); + if (ci.count != 0) { - int side = 0; - if (count == 1 || count == -1) + if (! ci.is_anti) { - side = winding_side_equal::apply(point, eq1 ? s1 : s2, count); - } - else // count == 2 || count == -2 - { - // 1 left, -1 right - side = m_side_strategy.apply(s1, s2, point); - } - - if (side == 0) - { - // Point is lying on segment - state.m_touches = true; - state.m_count = 0; - return false; - } + int side = 0; + if (ci.count == 1 || ci.count == -1) + { + side = side_equal(point, eq1 ? s1 : s2, ci, s1, s2); + } + else // count == 2 || count == -2 + { + if (! s_antipodal) + { + // 1 left, -1 right + side = m_side_strategy.apply(s1, s2, point); + } + else + { + calculation_type const pi = constants::half_period(); + calculation_type const s1_lat = get<1>(s1); + calculation_type const s2_lat = get<1>(s2); - // Side is NEG for right, POS for left. - // The count is -2 for down, 2 for up (or -1/1) - // Side positive thus means UP and LEFTSIDE or DOWN and RIGHTSIDE - // See accompagnying figure (TODO) - if (side * count > 0) + side = math::sign(ci.count) + * (pi - s1_lat - s2_lat <= pi // segment goes through north pole + ? -1 // going right all points will be on right side + : 1); // going right all points will be on left side + } + } + + if (side == 0) + { + // Point is lying on segment + state.m_touches = true; + state.m_count = 0; + return false; + } + + // Side is NEG for right, POS for left. + // The count is -2 for left, 2 for right (or -1/1) + // Side positive thus means RIGHT and LEFTSIDE or LEFT and RIGHTSIDE + // See accompagnying figure (TODO) + if (side * ci.count > 0) + { + state.m_count += ci.count; + } + + state.m_raw_count += ci.count; + } + else { - state.m_count += count; + // Count negated because the segment is on the other side of the globe + // so it is reversed to match this side of the globe + + // Assuming geometry wraps around north pole, for segments on the other side of the globe + // the point will always be RIGHT+RIGHTSIDE or LEFT+LEFTSIDE, so side*-count always < 0 + //state.m_count_n -= 0; + + // Assuming geometry wraps around south pole, for segments on the other side of the globe + // the point will always be RIGHT+LEFTSIDE or LEFT+RIGHTSIDE, so side*-count always > 0 + state.m_count_s -= ci.count; + + state.m_raw_count_anti -= ci.count; } } return ! state.m_touches; @@ -464,10 +427,413 @@ public: } private: + + static inline count_info check_segment(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, bool& eq2, bool& s_antipodal) + { + if (check_touch(point, seg1, seg2, state, eq1, eq2, s_antipodal)) + { + return count_info(0, false); + } + + return calculate_count(point, seg1, seg2, eq1, eq2, s_antipodal); + } + + static inline int check_touch(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, + bool& eq2, + bool& s_antipodal) + { + calculation_type const c0 = 0; + calculation_type const c2 = 2; + calculation_type const pi = constants::half_period(); + calculation_type const half_pi = pi / c2; + + calculation_type const p_lon = get<0>(point); + calculation_type const s1_lon = get<0>(seg1); + calculation_type const s2_lon = get<0>(seg2); + calculation_type const p_lat = get<1>(point); + calculation_type const s1_lat = get<1>(seg1); + calculation_type const s2_lat = get<1>(seg2); + + // NOTE: lat in {-90, 90} and arbitrary lon + // it doesn't matter what lon it is if it's a pole + // so e.g. if one of the segment endpoints is a pole + // then only the other lon matters + + bool eq1_strict = longitudes_equal(s1_lon, p_lon); + bool eq2_strict = longitudes_equal(s2_lon, p_lon); + bool eq1_anti = false; + bool eq2_anti = false; + + calculation_type const anti_p_lon = p_lon + (p_lon <= c0 ? pi : -pi); + + eq1 = eq1_strict // lon strictly equal to s1 + || (eq1_anti = longitudes_equal(s1_lon, anti_p_lon)) // anti-lon strictly equal to s1 + || math::equals(math::abs(s1_lat), half_pi); // s1 is pole + eq2 = eq2_strict // lon strictly equal to s2 + || (eq2_anti = longitudes_equal(s2_lon, anti_p_lon)) // anti-lon strictly equal to s2 + || math::equals(math::abs(s2_lat), half_pi); // s2 is pole + + // segment overlapping pole + calculation_type const s_lon_diff = math::longitude_distance_signed(s1_lon, s2_lon); + s_antipodal = math::equals(s_lon_diff, pi); + if (s_antipodal) + { + eq1 = eq2 = eq1 || eq2; + + // segment overlapping pole and point is pole + if (math::equals(math::abs(p_lat), half_pi)) + { + eq1 = eq2 = true; + } + } + + // Both equal p -> segment vertical + // The only thing which has to be done is check if point is ON segment + if (eq1 && eq2) + { + // segment endpoints on the same sides of the globe + if (! s_antipodal) + { + // p's lat between segment endpoints' lats + if ( (s1_lat <= p_lat && s2_lat >= p_lat) || (s2_lat <= p_lat && s1_lat >= p_lat) ) + { + if (!eq1_anti || !eq2_anti) + { + state.m_touches = true; + } + } + } + else + { + // going through north or south pole? + if (pi - s1_lat - s2_lat <= pi) + { + if ( (eq1_strict && s1_lat <= p_lat) || (eq2_strict && s2_lat <= p_lat) // north + || math::equals(p_lat, half_pi) ) // point on north pole + { + state.m_touches = true; + } + else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, -half_pi) ) // point on south pole + { + return false; + } + } + else // south pole + { + if ( (eq1_strict && s1_lat >= p_lat) || (eq2_strict && s2_lat >= p_lat) // south + || math::equals(p_lat, -half_pi) ) // point on south pole + { + state.m_touches = true; + } + else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, half_pi) ) // point on north pole + { + return false; + } + } + } + + return true; + } + + return false; + } + + // Called if point is not aligned with a vertical segment + static inline count_info calculate_count(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + bool eq1, bool eq2, bool s_antipodal) + { + // If both segment endpoints were poles below checks wouldn't be enough + // but this means that either both are the same or that they are N/S poles + // and therefore the segment is not valid. + // If needed (eq1 && eq2 ? 0) could be returned + + calculation_type const c0 = 0; + calculation_type const c2 = 2; + calculation_type const pi = constants::half_period(); + calculation_type const half_pi = pi / c2; + + calculation_type const p = get<0>(point); + calculation_type const s1 = get<0>(seg1); + calculation_type const s2 = get<0>(seg2); + + calculation_type const s1_p = math::longitude_distance_signed(s1, p); + + if (s_antipodal) + { + return count_info(s1_p < c0 ? -2 : 2, false); // choose W/E + } + + calculation_type const s1_s2 = math::longitude_distance_signed(s1, s2); + + if (eq1 || eq2) // Point on level s1 or s2 + { + return count_info(s1_s2 < c0 ? -1 : 1, // choose W/E + longitudes_equal(p + pi, (eq1 ? s1 : s2))); + } + + // Point between s1 and s2 + if ( math::sign(s1_p) == math::sign(s1_s2) + && math::abs(s1_p) < math::abs(s1_s2) ) + { + return count_info(s1_s2 < c0 ? -2 : 2, false); // choose W/E + } + + calculation_type const s1_p_anti = math::longitude_distance_signed(s1, p + pi); + + // Anti-Point between s1 and s2 + if ( math::sign(s1_p_anti) == math::sign(s1_s2) + && math::abs(s1_p_anti) < math::abs(s1_s2) ) + { + return count_info(s1_s2 < c0 ? -2 : 2, true); // choose W/E + } + + return count_info(0, false); + } + + + // Fix for https://svn.boost.org/trac/boost/ticket/9628 + // For floating point coordinates, the coordinate of a point is compared + // with the segment's points using some EPS. If the coordinates are "equal" + // the sides are calculated. Therefore we can treat a segment as a long areal + // geometry having some width. There is a small ~triangular area somewhere + // between the segment's effective area and a segment's line used in sides + // calculation where the segment is on the one side of the line but on the + // other side of a segment (due to the width). + // Below picture assuming D = 1, if D = 0 horiz<->vert, E<->N, RIGHT<->UP. + // For the s1 of a segment going NE the real side is RIGHT but the point may + // be detected as LEFT, like this: + // RIGHT + // ___-----> + // ^ O Pt __ __ + // EPS __ __ + // v__ __ BUT DETECTED AS LEFT OF THIS LINE + // _____7 + // _____/ + // _____/ + // In the code below actually D = 0, so segments are nearly-vertical + // Called when the point is on the same level as one of the segment's points + // but the point is not aligned with a vertical segment + inline int side_equal(Point const& point, + PointOfSegment const& se, + count_info const& ci, + PointOfSegment const& s1, PointOfSegment const& s2) const + { + typedef typename coordinate_type::type scoord_t; + typedef typename coordinate_system::type::units units_t; + + if (math::equals(get<1>(point), get<1>(se))) + { + return 0; + } + + // Create a horizontal segment intersecting the original segment's endpoint + // equal to the point, with the derived direction (E/W). + PointOfSegment ss1, ss2; + set<1>(ss1, get<1>(se)); + set<0>(ss1, get<0>(se)); + set<1>(ss2, get<1>(se)); + scoord_t ss20 = get<0>(se); + if (ci.count > 0) + { + ss20 += small_angle(); + } + else + { + ss20 -= small_angle(); + } + math::normalize_longitude(ss20); + set<0>(ss2, ss20); + + // Check the side using this vertical segment + return m_side_strategy.apply(ss1, ss2, point); + } + + // 1 deg or pi/180 rad + static inline calculation_type small_angle() + { + return constants::half_period() / calculation_type(180); + }; + + static inline bool longitudes_equal(calculation_type const& lon1, calculation_type const& lon2) + { + return math::equals( + math::longitude_distance_signed(lon1, lon2), + calculation_type(0)); + } + SideStrategy m_side_strategy; }; +template +< + typename Point, + typename PointOfSegment, + typename CalculationType, + typename CSTag = typename tag_cast + < + typename cs_tag::type, + spherical_tag + >::type +> +struct winding_base_type +{ + BOOST_MPL_ASSERT_MSG(false, + NOT_IMPLEMENTED_FOR_THIS_COORDINATE_SYSTEM, + (CSTag)); +}; + +template +struct winding_base_type +{ + typedef within::cartesian_winding type; +}; + +template +struct winding_base_type +{ + typedef within::detail::spherical_winding_base + < + Point, + PointOfSegment, + typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type, + CalculationType + > type; +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +/*! +\brief Within detection using winding rule in spherical coordinate system. +\ingroup strategies +\tparam Point \tparam_point +\tparam PointOfSegment \tparam_segment_point +\tparam CalculationType \tparam_calculation + +\qbk{ +[heading See also] +[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] +} + */ +template +< + typename Point, + typename PointOfSegment = Point, + typename CalculationType = void +> +class spherical_winding + : public within::detail::spherical_winding_base + < + Point, + PointOfSegment, + side::spherical_side_formula, + CalculationType + > +{}; + + +/*! +\brief Within detection using winding rule in geographic coordinate system. +\ingroup strategies +\tparam Point \tparam_point +\tparam PointOfSegment \tparam_segment_point +\tparam FormulaPolicy Geodesic formula policy +\tparam Spheroid Spheroid model +\tparam CalculationType \tparam_calculation + +\qbk{ +[heading See also] +[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] +} + */ +template +< + typename Point, + typename PointOfSegment = Point, + typename FormulaPolicy = strategy::andoyer, + typename Spheroid = srs::spheroid, + typename CalculationType = void +> +class geographic_winding + : public within::detail::spherical_winding_base + < + Point, + PointOfSegment, + side::geographic, + CalculationType + > +{ + typedef within::detail::spherical_winding_base + < + Point, + PointOfSegment, + side::geographic, + CalculationType + > base_t; + +public: + geographic_winding() + {} + + explicit geographic_winding(Spheroid const& model) + : base_t(model) + {} +}; + + +/*! +\brief Within detection using winding rule. Side strategy used internally is + choosen based on Point's coordinate system. +\ingroup strategies +\tparam Point \tparam_point +\tparam PointOfSegment \tparam_segment_point +\tparam CalculationType \tparam_calculation + +\qbk{ +[heading See also] +[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] +} + */ +template +< + typename Point, + typename PointOfSegment = Point, + typename CalculationType = void +> +class winding + : public within::detail::winding_base_type::type +{ + typedef typename within::detail::winding_base_type + < + Point, PointOfSegment, CalculationType + >::type base_t; + +public: + winding() {} + + template + explicit winding(Model const& model) + : base_t(model) + {} +}; + + #ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS namespace services @@ -476,7 +842,7 @@ namespace services template struct default_strategy { - typedef winding + typedef cartesian_winding < typename geometry::point_type::type, typename geometry::point_type::type @@ -486,7 +852,7 @@ struct default_strategy struct default_strategy { - typedef winding + typedef within::detail::spherical_winding_base < typename geometry::point_type::type, typename geometry::point_type::type @@ -496,7 +862,7 @@ struct default_strategy struct default_strategy { - typedef winding + typedef cartesian_winding < typename geometry::point_type::type, typename geometry::point_type::type @@ -506,7 +872,7 @@ struct default_strategy struct default_strategy { - typedef winding + typedef within::detail::spherical_winding_base < typename geometry::point_type::type, typename geometry::point_type::type @@ -528,7 +894,7 @@ namespace strategy { namespace covered_by { namespace services template struct default_strategy { - typedef within::winding + typedef within::cartesian_winding < typename geometry::point_type::type, typename geometry::point_type::type @@ -538,7 +904,7 @@ struct default_strategy struct default_strategy { - typedef within::winding + typedef within::detail::spherical_winding_base < typename geometry::point_type::type, typename geometry::point_type::type @@ -548,7 +914,7 @@ struct default_strategy struct default_strategy { - typedef within::winding + typedef within::cartesian_winding < typename geometry::point_type::type, typename geometry::point_type::type @@ -558,7 +924,7 @@ struct default_strategy struct default_strategy { - typedef within::winding + typedef within::detail::spherical_winding_base < typename geometry::point_type::type, typename geometry::point_type::type From 74f13547bea8b76a918eeb5d5980f2dbb09f7c09 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 22 Aug 2017 00:33:39 +0200 Subject: [PATCH 068/188] [strategies] Update intersection and within strategies after change in winding strategy. --- .../boost/geometry/strategies/agnostic/point_in_point.hpp | 2 +- .../boost/geometry/strategies/cartesian/intersection.hpp | 3 +-- .../boost/geometry/strategies/geographic/intersection.hpp | 7 ++++--- .../boost/geometry/strategies/spherical/intersection.hpp | 3 +-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/boost/geometry/strategies/agnostic/point_in_point.hpp b/include/boost/geometry/strategies/agnostic/point_in_point.hpp index 1a9274149..d4692766c 100644 --- a/include/boost/geometry/strategies/agnostic/point_in_point.hpp +++ b/include/boost/geometry/strategies/agnostic/point_in_point.hpp @@ -32,7 +32,7 @@ struct point_in_point { static inline bool apply(Point1 const& point1, Point2 const& point2) { - return detail::equals::equals_point_point(point1, point2); + return geometry::detail::equals::equals_point_point(point1, point2); } }; diff --git a/include/boost/geometry/strategies/cartesian/intersection.hpp b/include/boost/geometry/strategies/cartesian/intersection.hpp index 50e903885..867c8320d 100644 --- a/include/boost/geometry/strategies/cartesian/intersection.hpp +++ b/include/boost/geometry/strategies/cartesian/intersection.hpp @@ -81,11 +81,10 @@ struct cartesian_segments template struct point_in_geometry_strategy { - typedef strategy::within::winding + typedef strategy::within::cartesian_winding < typename point_type::type, typename point_type::type, - side_strategy_type, CalculationType > type; }; diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp index 59a40f281..668d4b922 100644 --- a/include/boost/geometry/strategies/geographic/intersection.hpp +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -78,11 +78,12 @@ struct geographic_segments template struct point_in_geometry_strategy { - typedef strategy::within::winding + typedef strategy::within::geographic_winding < typename point_type::type, typename point_type::type, - side_strategy_type, + FormulaPolicy, + Spheroid, CalculationType > type; }; @@ -95,7 +96,7 @@ struct geographic_segments < Geometry1, Geometry2 >::type strategy_type; - return strategy_type(get_side_strategy()); + return strategy_type(m_spheroid); } template diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index 44b1cc62b..5080729d9 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -94,11 +94,10 @@ struct ecef_segments template struct point_in_geometry_strategy { - typedef strategy::within::winding + typedef strategy::within::spherical_winding < typename point_type::type, typename point_type::type, - side_strategy_type, CalculationType > type; }; From 3011d4aa7ea2486c1d6255a73bcfe148a8a22c0c Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 22 Aug 2017 00:35:14 +0200 Subject: [PATCH 069/188] [test][covered_by][within] Add spherical and geographic cases. --- .../covered_by/covered_by_sph.cpp | 51 ++++++- .../covered_by/covered_by_sph_geo.cpp | 30 ++++ .../covered_by/test_covered_by.hpp | 14 ++ .../within/within_pointlike_geometry.cpp | 138 +++++++++++++----- 4 files changed, 193 insertions(+), 40 deletions(-) diff --git a/test/algorithms/relational_operations/covered_by/covered_by_sph.cpp b/test/algorithms/relational_operations/covered_by/covered_by_sph.cpp index 710742fd1..a80fb2390 100644 --- a/test/algorithms/relational_operations/covered_by/covered_by_sph.cpp +++ b/test/algorithms/relational_operations/covered_by/covered_by_sph.cpp @@ -229,8 +229,11 @@ void test_point_polygon() "POLYGON((30 0,30 30,90 30, 90 0, 30 0))", false); // extended - test_geometry("POINT(0 -90)", - "POLYGON((0 -80,90 -80, -180 -80, -90 -80, 0 -80))", + test_geometry("POINT(0 90)", + "POLYGON((0 80, 0 81, -90 80, -180 80, 90 80, 0 80))", + true); + test_geometry("POINT(0 90)", + "POLYGON((0 80, -90 80, -90 81, -180 80, 90 80, 0 80))", true); test_geometry("POINT(0 89)", "POLYGON((0 80,-90 80, -180 80, 90 80, 0 80))", @@ -238,6 +241,50 @@ void test_point_polygon() test_geometry("POINT(-180 89)", "POLYGON((0 80,-90 80, -180 80, 90 80, 0 80))", true); + test_geometry("POINT(0 -90)", + "POLYGON((0 -80,90 -80, -180 -80, -90 -80, 0 -80))", + true); + test_geometry("POINT(0 -89)", + "POLYGON((0 -80,90 -80, -180 -80, -90 -80, 0 -80))", + true); + test_geometry("POINT(1 -90)", + "POLYGON((0 -80,90 -80, -180 -80, -90 -80, 0 -80))", + true); + test_geometry("POINT(1 -89)", + "POLYGON((0 -80,90 -80, -180 -80, -90 -80, 0 -80))", + true); + test_geometry("POINT(1 90)", + "POLYGON((0 80,-90 80, -180 80, 90 80, 0 80))", + true); + test_geometry("POINT(1 90)", + "POLYGON((0 80,-90 80, -180 80, 90 80, 0 80))", + true); + + + + // MySQL report 08.2017 + test_geometry("POINT(-179 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + false); + // extended + test_geometry("POINT(179 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + false); + test_geometry("POINT(180 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + false); + test_geometry("POINT(-179 0)", + "POLYGON((-10 -10, -10 10, 10 10, 10 -10, -10 10))", + false); + test_geometry("POINT(179 0)", + "POLYGON((-10 -10, -10 10, 10 10, 10 -10, -10 10))", + false); + test_geometry("POINT(-179 0)", + "POLYGON((0 0, 0 1, 1 0, 0 -1, 0 0))", + false); + test_geometry("POINT(179 0)", + "POLYGON((0 0, 0 1, 1 0, 0 -1, 0 0))", + false); } diff --git a/test/algorithms/relational_operations/covered_by/covered_by_sph_geo.cpp b/test/algorithms/relational_operations/covered_by/covered_by_sph_geo.cpp index b46b6f4e9..6c8d5853b 100644 --- a/test/algorithms/relational_operations/covered_by/covered_by_sph_geo.cpp +++ b/test/algorithms/relational_operations/covered_by/covered_by_sph_geo.cpp @@ -79,12 +79,42 @@ void test_box_box() test_geometry("BOX(-179.9 1, -177.872408 1)", "BOX(179.08882 0, 182.127592 2)", true); } +template +void test_point_polygon() +{ + typename boost::mpl::if_ + < + boost::is_same::type, bg::geographic_tag>, + bg::strategy::within::geographic_winding

, + bg::strategy::within::spherical_winding

+ >::type s; + + typedef bg::model::polygon

poly; + + // MySQL report 08.2017 + test_geometry("POINT(-179 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + false); + test_geometry("POINT(-179 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + false, + s); + + test_geometry("POINT(1 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + true); + test_geometry("POINT(1 0)", + "POLYGON((0 0, 0 2, 2 0, 0 -2, 0 0))", + true, + s); +} template void test_cs() { test_point_box

(); test_box_box

(); + test_point_polygon

(); } diff --git a/test/algorithms/relational_operations/covered_by/test_covered_by.hpp b/test/algorithms/relational_operations/covered_by/test_covered_by.hpp index 4622e80ec..e22ed03fd 100644 --- a/test/algorithms/relational_operations/covered_by/test_covered_by.hpp +++ b/test/algorithms/relational_operations/covered_by/test_covered_by.hpp @@ -88,6 +88,20 @@ void test_geometry(std::string const& wkt1, check_geometry(v1, v2, wkt1, wkt2, expected, no_strategy()); } +template +void test_geometry(std::string const& wkt1, + std::string const& wkt2, + bool expected, + Strategy const& strategy) +{ + Geometry1 geometry1; + Geometry2 geometry2; + bg::read_wkt(wkt1, geometry1); + bg::read_wkt(wkt2, geometry2); + + check_geometry(geometry1, geometry2, wkt1, wkt2, expected, strategy); +} + /* template diff --git a/test/algorithms/relational_operations/within/within_pointlike_geometry.cpp b/test/algorithms/relational_operations/within/within_pointlike_geometry.cpp index 3a00bb41a..523e183c0 100644 --- a/test/algorithms/relational_operations/within/within_pointlike_geometry.cpp +++ b/test/algorithms/relational_operations/within/within_pointlike_geometry.cpp @@ -154,6 +154,22 @@ void test_spherical_geographic() { bg::model::polygon wrangel; + typename boost::mpl::if_ + < + boost::is_same::type, bg::geographic_tag>, + bg::strategy::within::geographic_winding, + bg::strategy::within::spherical_winding + >::type ws; + + typename boost::mpl::if_ + < + boost::is_same::type, bg::geographic_tag>, + bg::strategy::side::geographic<>, + bg::strategy::side::spherical_side_formula<> + >::type ss; + + boost::ignore_unused(ws, ss); + // SQL Server check (no geography::STWithin, so check with intersection trick) /* @@ -189,9 +205,9 @@ void test_spherical_geographic() bg::model::polygon triangle; bg::read_wkt("POLYGON((5 52,9 53,7 50,5 52))", triangle); - BOOST_CHECK_EQUAL(bg::within(Point(7, 52.5), triangle), true); - BOOST_CHECK_EQUAL(bg::within(Point(8.0, 51.5), triangle), false); - BOOST_CHECK_EQUAL(bg::within(Point(6.0, 51.0), triangle), false); + BOOST_CHECK_EQUAL(bg::within(Point(7, 52.5), triangle, ws), true); + BOOST_CHECK_EQUAL(bg::within(Point(8.0, 51.5), triangle, ws), false); + BOOST_CHECK_EQUAL(bg::within(Point(6.0, 51.0), triangle, ws), false); // northern hemisphere { @@ -199,11 +215,10 @@ void test_spherical_geographic() bg::read_wkt("POLYGON((10 50,30 50,30 40,10 40, 10 50))", poly_n); Point pt_n1(20, 50.00001); Point pt_n2(20, 40.00001); - typedef bg::strategy::side::spherical_side_formula<> ssf; - BOOST_CHECK_EQUAL(ssf::apply(poly_n.outer()[0], poly_n.outer()[1], pt_n1), -1); // right of segment - BOOST_CHECK_EQUAL(ssf::apply(poly_n.outer()[2], poly_n.outer()[3], pt_n2), 1); // left of segment - BOOST_CHECK_EQUAL(bg::within(pt_n1, poly_n), true); - BOOST_CHECK_EQUAL(bg::within(pt_n2, poly_n), false); + BOOST_CHECK_EQUAL(ss.apply(poly_n.outer()[0], poly_n.outer()[1], pt_n1), -1); // right of segment + BOOST_CHECK_EQUAL(ss.apply(poly_n.outer()[2], poly_n.outer()[3], pt_n2), 1); // left of segment + BOOST_CHECK_EQUAL(bg::within(pt_n1, poly_n, ws), true); + BOOST_CHECK_EQUAL(bg::within(pt_n2, poly_n, ws), false); } // southern hemisphere { @@ -211,11 +226,10 @@ void test_spherical_geographic() bg::read_wkt("POLYGON((10 -40,30 -40,30 -50,10 -50, 10 -40))", poly_s); Point pt_s1(20, -40.00001); Point pt_s2(20, -50.00001); - typedef bg::strategy::side::spherical_side_formula<> ssf; - BOOST_CHECK_EQUAL(ssf::apply(poly_s.outer()[0], poly_s.outer()[1], pt_s1), 1); // left of segment - BOOST_CHECK_EQUAL(ssf::apply(poly_s.outer()[2], poly_s.outer()[3], pt_s2), -1); // right of segment - BOOST_CHECK_EQUAL(bg::within(pt_s1, poly_s), false); - BOOST_CHECK_EQUAL(bg::within(pt_s2, poly_s), true); + BOOST_CHECK_EQUAL(ss.apply(poly_s.outer()[0], poly_s.outer()[1], pt_s1), 1); // left of segment + BOOST_CHECK_EQUAL(ss.apply(poly_s.outer()[2], poly_s.outer()[3], pt_s2), -1); // right of segment + BOOST_CHECK_EQUAL(bg::within(pt_s1, poly_s, ws), false); + BOOST_CHECK_EQUAL(bg::within(pt_s2, poly_s, ws), true); } // crossing antimeridian, northern hemisphere { @@ -229,14 +243,14 @@ void test_spherical_geographic() Point pt_n22(-180, 40.00001); Point pt_n23(179, 40.00001); Point pt_n24(-179, 40.00001); - BOOST_CHECK_EQUAL(bg::within(pt_n11, poly_n), true); - BOOST_CHECK_EQUAL(bg::within(pt_n12, poly_n), true); - BOOST_CHECK_EQUAL(bg::within(pt_n13, poly_n), true); - BOOST_CHECK_EQUAL(bg::within(pt_n14, poly_n), true); - BOOST_CHECK_EQUAL(bg::within(pt_n21, poly_n), false); - BOOST_CHECK_EQUAL(bg::within(pt_n22, poly_n), false); - BOOST_CHECK_EQUAL(bg::within(pt_n23, poly_n), false); - BOOST_CHECK_EQUAL(bg::within(pt_n24, poly_n), false); + BOOST_CHECK_EQUAL(bg::within(pt_n11, poly_n, ws), true); + BOOST_CHECK_EQUAL(bg::within(pt_n12, poly_n, ws), true); + BOOST_CHECK_EQUAL(bg::within(pt_n13, poly_n, ws), true); + BOOST_CHECK_EQUAL(bg::within(pt_n14, poly_n, ws), true); + BOOST_CHECK_EQUAL(bg::within(pt_n21, poly_n, ws), false); + BOOST_CHECK_EQUAL(bg::within(pt_n22, poly_n, ws), false); + BOOST_CHECK_EQUAL(bg::within(pt_n23, poly_n, ws), false); + BOOST_CHECK_EQUAL(bg::within(pt_n24, poly_n, ws), false); } // TODO: Move to covered_by tests @@ -246,22 +260,22 @@ void test_spherical_geographic() bg::model::polygon poly_n1; bg::read_wkt("POLYGON((-90 80,90 80,90 70,-90 70, -90 80))", poly_n1); // Points on segment - BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 85), poly_n1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 85), poly_n1), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 85), poly_n1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 85), poly_n1, ws), true); // Points on pole - BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 90), poly_n1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(0, 90), poly_n1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(45, 90), poly_n1), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 90), poly_n1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(0, 90), poly_n1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(45, 90), poly_n1, ws), true); } // Segment going through pole { bg::model::polygon poly_n2; bg::read_wkt("POLYGON((-90 80,90 70,0 70,-90 80))", poly_n2); // Points on segment - BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 85), poly_n2), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 75), poly_n2), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 85), poly_n2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(90, 75), poly_n2, ws), true); // Points outside but on the same level as segment - BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 75), poly_n2), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, 75), poly_n2, ws), false); } // Possibly invalid, 2-segment polygon with segment going through pole /*{ @@ -279,27 +293,75 @@ void test_spherical_geographic() { bg::model::polygon poly_n3; bg::read_wkt("POLYGON((45 90,45 80,0 80,45 90))", poly_n3); - BOOST_CHECK_EQUAL(bg::covered_by(Point(0, 85), poly_n3), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(45, 85), poly_n3), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(0, 85), poly_n3, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(45, 85), poly_n3, ws), true); } // Segment going through pole { bg::model::polygon poly_s1; bg::read_wkt("POLYGON((-90 -80,-90 -70,90 -70,90 -80,-90 -80))", poly_s1); // Points on segment - BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, -85), poly_s1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(90, -85), poly_s1), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(-90, -85), poly_s1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(90, -85), poly_s1, ws), true); // Points on pole - BOOST_CHECK_EQUAL(bg::covered_by(Point(90, -90), poly_s1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(0, -90), poly_s1), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(45, -90), poly_s1), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(90, -90), poly_s1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(0, -90), poly_s1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(45, -90), poly_s1, ws), true); } // Segment endpoints on pole with arbitrary longitudes { bg::model::polygon poly_s2; bg::read_wkt("POLYGON((45 -90,0 -80,45 -80,45 -90))", poly_s2); - BOOST_CHECK_EQUAL(bg::covered_by(Point(0, -85), poly_s2), true); - BOOST_CHECK_EQUAL(bg::covered_by(Point(45, -85), poly_s2), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(0, -85), poly_s2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(45, -85), poly_s2, ws), true); + } + // Polygon covering nearly half of the globe but no poles + { + bg::model::polygon poly_h1; + bg::read_wkt("POLYGON((170 0, 170 -80,10 -80,0 -80,0 -20,10 -20,10 20,0 20,0 80,10 80,170 80,170 0))", poly_h1); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 90), poly_h1, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 85), poly_h1, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 50), poly_h1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 0), poly_h1, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -50), poly_h1, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -85), poly_h1, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -90), poly_h1, ws), false); + } + // Polygon covering more than half of the globe with both holes + { + bg::model::polygon poly_h2; + bg::read_wkt("POLYGON((180 0, 180 -80,0 -80,10 -80,10 -20,0 -20,0 20,10 20,10 80,0 80,180 80,180 0))", poly_h2); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 90), poly_h2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 85), poly_h2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 50), poly_h2, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 0), poly_h2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -50), poly_h2, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -85), poly_h2, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -90), poly_h2, ws), true); + } + // Polygon covering around half of the globe covering south pole + { + bg::model::polygon poly_h3; + bg::read_wkt("POLYGON((180 0, 180 -80,0 -80,0 -20,10 -20,10 20,0 20,0 80,10 80,170 80,180 0))", poly_h3); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 90), poly_h3, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 85), poly_h3, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 50), poly_h3, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 0), poly_h3, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -50), poly_h3, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -85), poly_h3, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -90), poly_h3, ws), true); + } + // Polygon covering around half of the globe covering north pole + { + bg::model::polygon poly_h4; + bg::read_wkt("POLYGON((180 0, 170 -80,10 -80,10 -20,0 -20,0 20,10 20,10 80,0 80,180 80,180 0))", poly_h4); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 90), poly_h4, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 85), poly_h4, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 50), poly_h4, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, 0), poly_h4, ws), true); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -50), poly_h4, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -85), poly_h4, ws), false); + BOOST_CHECK_EQUAL(bg::covered_by(Point(5, -90), poly_h4, ws), false); } } From 95ffd9efbcc605bfe18d21b90bb9a779b173bcae Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 22 Aug 2017 01:11:51 +0200 Subject: [PATCH 070/188] [strategies] Relocate CS-specific winding strategies. --- .../agnostic/point_in_poly_winding.hpp | 827 +----------------- .../strategies/cartesian/intersection.hpp | 2 +- .../cartesian/point_in_poly_winding.hpp | 296 +++++++ .../strategies/geographic/intersection.hpp | 1 + .../geographic/point_in_poly_winding.hpp | 80 ++ .../strategies/spherical/intersection.hpp | 2 +- .../spherical/point_in_poly_winding.hpp | 583 ++++++++++++ .../boost/geometry/strategies/strategies.hpp | 5 +- 8 files changed, 976 insertions(+), 820 deletions(-) create mode 100644 include/boost/geometry/strategies/cartesian/point_in_poly_winding.hpp create mode 100644 include/boost/geometry/strategies/geographic/point_in_poly_winding.hpp create mode 100644 include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp diff --git a/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp b/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp index 557413f10..774294b57 100644 --- a/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp +++ b/include/boost/geometry/strategies/agnostic/point_in_poly_winding.hpp @@ -18,17 +18,15 @@ #define BOOST_GEOMETRY_STRATEGY_AGNOSTIC_POINT_IN_POLY_WINDING_HPP -#include +#include -#include -#include +#include +#include +#include +#include #include -#include -#include -#include -#include -#include +#include namespace boost { namespace geometry @@ -38,642 +36,10 @@ namespace strategy { namespace within { -/*! -\brief Within detection using winding rule in cartesian coordinate system. -\ingroup strategies -\tparam Point \tparam_point -\tparam PointOfSegment \tparam_segment_point -\tparam CalculationType \tparam_calculation -\author Barend Gehrels -\note The implementation is inspired by terralib http://www.terralib.org (LGPL) -\note but totally revised afterwards, especially for cases on segments - -\qbk{ -[heading See also] -[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] -} - */ -template -< - typename Point, - typename PointOfSegment = Point, - typename CalculationType = void -> -class cartesian_winding -{ - typedef side::side_by_triangle side_strategy_type; - - typedef typename select_calculation_type - < - Point, - PointOfSegment, - CalculationType - >::type calculation_type; - - /*! subclass to keep state */ - class counter - { - int m_count; - bool m_touches; - - inline int code() const - { - return m_touches ? 0 : m_count == 0 ? -1 : 1; - } - - public : - friend class cartesian_winding; - - inline counter() - : m_count(0) - , m_touches(false) - {} - - }; - -public: - typedef typename side_strategy_type::envelope_strategy_type envelope_strategy_type; - - static inline envelope_strategy_type get_envelope_strategy() - { - return side_strategy_type::get_envelope_strategy(); - } - - typedef typename side_strategy_type::disjoint_strategy_type disjoint_strategy_type; - - static inline disjoint_strategy_type get_disjoint_strategy() - { - return side_strategy_type::get_disjoint_strategy(); - } - - // Typedefs and static methods to fulfill the concept - typedef Point point_type; - typedef PointOfSegment segment_point_type; - typedef counter state_type; - - static inline bool apply(Point const& point, - PointOfSegment const& s1, PointOfSegment const& s2, - counter& state) - { - bool eq1 = false; - bool eq2 = false; - - int count = check_segment(point, s1, s2, state, eq1, eq2); - if (count != 0) - { - int side = 0; - if (count == 1 || count == -1) - { - side = side_equal(point, eq1 ? s1 : s2, count); - } - else // count == 2 || count == -2 - { - // 1 left, -1 right - side = side_strategy_type::apply(s1, s2, point); - } - - if (side == 0) - { - // Point is lying on segment - state.m_touches = true; - state.m_count = 0; - return false; - } - - // Side is NEG for right, POS for left. - // The count is -2 for left, 2 for right (or -1/1) - // Side positive thus means RIGHT and LEFTSIDE or LEFT and RIGHTSIDE - // See accompagnying figure (TODO) - if (side * count > 0) - { - state.m_count += count; - } - } - return ! state.m_touches; - } - - static inline int result(counter const& state) - { - return state.code(); - } - -private: - static inline int check_segment(Point const& point, - PointOfSegment const& seg1, - PointOfSegment const& seg2, - counter& state, - bool& eq1, bool& eq2) - { - if (check_touch(point, seg1, seg2, state, eq1, eq2)) - { - return 0; - } - - return calculate_count(point, seg1, seg2, eq1, eq2); - } - - static inline bool check_touch(Point const& point, - PointOfSegment const& seg1, - PointOfSegment const& seg2, - counter& state, - bool& eq1, bool& eq2) - { - calculation_type const px = get<0>(point); - calculation_type const s1x = get<0>(seg1); - calculation_type const s2x = get<0>(seg2); - - eq1 = math::equals(s1x, px); - eq2 = math::equals(s2x, px); - - // Both equal p -> segment vertical - // The only thing which has to be done is check if point is ON segment - if (eq1 && eq2) - { - calculation_type const py = get<1>(point); - calculation_type const s1y = get<1>(seg1); - calculation_type const s2y = get<1>(seg2); - if ((s1y <= py && s2y >= py) || (s2y <= py && s1y >= py)) - { - state.m_touches = true; - } - return true; - } - return false; - } - - static inline int calculate_count(Point const& point, - PointOfSegment const& seg1, - PointOfSegment const& seg2, - bool eq1, bool eq2) - { - calculation_type const p = get<0>(point); - calculation_type const s1 = get<0>(seg1); - calculation_type const s2 = get<0>(seg2); - - return eq1 ? (s2 > p ? 1 : -1) // Point on level s1, E/W depending on s2 - : eq2 ? (s1 > p ? -1 : 1) // idem - : s1 < p && s2 > p ? 2 // Point between s1 -> s2 --> E - : s2 < p && s1 > p ? -2 // Point between s2 -> s1 --> W - : 0; - } - - static inline int side_equal(Point const& point, - PointOfSegment const& se, - int count) - { - // NOTE: for D=0 the signs would be reversed - return math::equals(get<1>(point), get<1>(se)) ? - 0 : - get<1>(point) < get<1>(se) ? - // assuming count is equal to 1 or -1 - -count : // ( count > 0 ? -1 : 1) : - count; // ( count > 0 ? 1 : -1) ; - } -}; - - #ifndef DOXYGEN_NO_DETAIL namespace detail { -template -< - typename Point, - typename PointOfSegment = Point, - typename SideStrategy = typename strategy::side::services::default_strategy - < - typename cs_tag::type - >::type, - typename CalculationType = void -> -class spherical_winding_base -{ - typedef typename select_calculation_type - < - Point, - PointOfSegment, - CalculationType - >::type calculation_type; - - typedef typename coordinate_system::type::units units_t; - typedef math::detail::constants_on_spheroid constants; - - /*! subclass to keep state */ - class counter - { - int m_count; - //int m_count_n; - int m_count_s; - int m_raw_count; - int m_raw_count_anti; - bool m_touches; - - inline int code() const - { - if (m_touches) - { - return 0; - } - - if (m_raw_count != 0 && m_raw_count_anti != 0) - { - if (m_raw_count > 0) // right, wrap around south pole - { - return (m_count + m_count_s) == 0 ? -1 : 1; - } - else // left, wrap around north pole - { - //return (m_count + m_count_n) == 0 ? -1 : 1; - // m_count_n is 0 - return m_count == 0 ? -1 : 1; - } - } - - return m_count == 0 ? -1 : 1; - } - - public : - friend class spherical_winding_base; - - inline counter() - : m_count(0) - //, m_count_n(0) - , m_count_s(0) - , m_raw_count(0) - , m_raw_count_anti(0) - , m_touches(false) - {} - - }; - - struct count_info - { - explicit count_info(int c = 0, bool ia = false) - : count(c) - , is_anti(ia) - {} - - int count; - bool is_anti; - }; - -public: - typedef typename SideStrategy::envelope_strategy_type envelope_strategy_type; - - inline envelope_strategy_type get_envelope_strategy() const - { - return m_side_strategy.get_envelope_strategy(); - } - - typedef typename SideStrategy::disjoint_strategy_type disjoint_strategy_type; - - inline disjoint_strategy_type get_disjoint_strategy() const - { - return m_side_strategy.get_disjoint_strategy(); - } - - spherical_winding_base() - {} - - template - explicit spherical_winding_base(Model const& model) - : m_side_strategy(model) - {} - - // Typedefs and static methods to fulfill the concept - typedef Point point_type; - typedef PointOfSegment segment_point_type; - typedef counter state_type; - - inline bool apply(Point const& point, - PointOfSegment const& s1, PointOfSegment const& s2, - counter& state) const - { - bool eq1 = false; - bool eq2 = false; - bool s_antipodal = false; - - count_info ci = check_segment(point, s1, s2, state, eq1, eq2, s_antipodal); - if (ci.count != 0) - { - if (! ci.is_anti) - { - int side = 0; - if (ci.count == 1 || ci.count == -1) - { - side = side_equal(point, eq1 ? s1 : s2, ci, s1, s2); - } - else // count == 2 || count == -2 - { - if (! s_antipodal) - { - // 1 left, -1 right - side = m_side_strategy.apply(s1, s2, point); - } - else - { - calculation_type const pi = constants::half_period(); - calculation_type const s1_lat = get<1>(s1); - calculation_type const s2_lat = get<1>(s2); - - side = math::sign(ci.count) - * (pi - s1_lat - s2_lat <= pi // segment goes through north pole - ? -1 // going right all points will be on right side - : 1); // going right all points will be on left side - } - } - - if (side == 0) - { - // Point is lying on segment - state.m_touches = true; - state.m_count = 0; - return false; - } - - // Side is NEG for right, POS for left. - // The count is -2 for left, 2 for right (or -1/1) - // Side positive thus means RIGHT and LEFTSIDE or LEFT and RIGHTSIDE - // See accompagnying figure (TODO) - if (side * ci.count > 0) - { - state.m_count += ci.count; - } - - state.m_raw_count += ci.count; - } - else - { - // Count negated because the segment is on the other side of the globe - // so it is reversed to match this side of the globe - - // Assuming geometry wraps around north pole, for segments on the other side of the globe - // the point will always be RIGHT+RIGHTSIDE or LEFT+LEFTSIDE, so side*-count always < 0 - //state.m_count_n -= 0; - - // Assuming geometry wraps around south pole, for segments on the other side of the globe - // the point will always be RIGHT+LEFTSIDE or LEFT+RIGHTSIDE, so side*-count always > 0 - state.m_count_s -= ci.count; - - state.m_raw_count_anti -= ci.count; - } - } - return ! state.m_touches; - } - - static inline int result(counter const& state) - { - return state.code(); - } - -private: - - static inline count_info check_segment(Point const& point, - PointOfSegment const& seg1, - PointOfSegment const& seg2, - counter& state, - bool& eq1, bool& eq2, bool& s_antipodal) - { - if (check_touch(point, seg1, seg2, state, eq1, eq2, s_antipodal)) - { - return count_info(0, false); - } - - return calculate_count(point, seg1, seg2, eq1, eq2, s_antipodal); - } - - static inline int check_touch(Point const& point, - PointOfSegment const& seg1, - PointOfSegment const& seg2, - counter& state, - bool& eq1, - bool& eq2, - bool& s_antipodal) - { - calculation_type const c0 = 0; - calculation_type const c2 = 2; - calculation_type const pi = constants::half_period(); - calculation_type const half_pi = pi / c2; - - calculation_type const p_lon = get<0>(point); - calculation_type const s1_lon = get<0>(seg1); - calculation_type const s2_lon = get<0>(seg2); - calculation_type const p_lat = get<1>(point); - calculation_type const s1_lat = get<1>(seg1); - calculation_type const s2_lat = get<1>(seg2); - - // NOTE: lat in {-90, 90} and arbitrary lon - // it doesn't matter what lon it is if it's a pole - // so e.g. if one of the segment endpoints is a pole - // then only the other lon matters - - bool eq1_strict = longitudes_equal(s1_lon, p_lon); - bool eq2_strict = longitudes_equal(s2_lon, p_lon); - bool eq1_anti = false; - bool eq2_anti = false; - - calculation_type const anti_p_lon = p_lon + (p_lon <= c0 ? pi : -pi); - - eq1 = eq1_strict // lon strictly equal to s1 - || (eq1_anti = longitudes_equal(s1_lon, anti_p_lon)) // anti-lon strictly equal to s1 - || math::equals(math::abs(s1_lat), half_pi); // s1 is pole - eq2 = eq2_strict // lon strictly equal to s2 - || (eq2_anti = longitudes_equal(s2_lon, anti_p_lon)) // anti-lon strictly equal to s2 - || math::equals(math::abs(s2_lat), half_pi); // s2 is pole - - // segment overlapping pole - calculation_type const s_lon_diff = math::longitude_distance_signed(s1_lon, s2_lon); - s_antipodal = math::equals(s_lon_diff, pi); - if (s_antipodal) - { - eq1 = eq2 = eq1 || eq2; - - // segment overlapping pole and point is pole - if (math::equals(math::abs(p_lat), half_pi)) - { - eq1 = eq2 = true; - } - } - - // Both equal p -> segment vertical - // The only thing which has to be done is check if point is ON segment - if (eq1 && eq2) - { - // segment endpoints on the same sides of the globe - if (! s_antipodal) - { - // p's lat between segment endpoints' lats - if ( (s1_lat <= p_lat && s2_lat >= p_lat) || (s2_lat <= p_lat && s1_lat >= p_lat) ) - { - if (!eq1_anti || !eq2_anti) - { - state.m_touches = true; - } - } - } - else - { - // going through north or south pole? - if (pi - s1_lat - s2_lat <= pi) - { - if ( (eq1_strict && s1_lat <= p_lat) || (eq2_strict && s2_lat <= p_lat) // north - || math::equals(p_lat, half_pi) ) // point on north pole - { - state.m_touches = true; - } - else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, -half_pi) ) // point on south pole - { - return false; - } - } - else // south pole - { - if ( (eq1_strict && s1_lat >= p_lat) || (eq2_strict && s2_lat >= p_lat) // south - || math::equals(p_lat, -half_pi) ) // point on south pole - { - state.m_touches = true; - } - else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, half_pi) ) // point on north pole - { - return false; - } - } - } - - return true; - } - - return false; - } - - // Called if point is not aligned with a vertical segment - static inline count_info calculate_count(Point const& point, - PointOfSegment const& seg1, - PointOfSegment const& seg2, - bool eq1, bool eq2, bool s_antipodal) - { - // If both segment endpoints were poles below checks wouldn't be enough - // but this means that either both are the same or that they are N/S poles - // and therefore the segment is not valid. - // If needed (eq1 && eq2 ? 0) could be returned - - calculation_type const c0 = 0; - calculation_type const c2 = 2; - calculation_type const pi = constants::half_period(); - calculation_type const half_pi = pi / c2; - - calculation_type const p = get<0>(point); - calculation_type const s1 = get<0>(seg1); - calculation_type const s2 = get<0>(seg2); - - calculation_type const s1_p = math::longitude_distance_signed(s1, p); - - if (s_antipodal) - { - return count_info(s1_p < c0 ? -2 : 2, false); // choose W/E - } - - calculation_type const s1_s2 = math::longitude_distance_signed(s1, s2); - - if (eq1 || eq2) // Point on level s1 or s2 - { - return count_info(s1_s2 < c0 ? -1 : 1, // choose W/E - longitudes_equal(p + pi, (eq1 ? s1 : s2))); - } - - // Point between s1 and s2 - if ( math::sign(s1_p) == math::sign(s1_s2) - && math::abs(s1_p) < math::abs(s1_s2) ) - { - return count_info(s1_s2 < c0 ? -2 : 2, false); // choose W/E - } - - calculation_type const s1_p_anti = math::longitude_distance_signed(s1, p + pi); - - // Anti-Point between s1 and s2 - if ( math::sign(s1_p_anti) == math::sign(s1_s2) - && math::abs(s1_p_anti) < math::abs(s1_s2) ) - { - return count_info(s1_s2 < c0 ? -2 : 2, true); // choose W/E - } - - return count_info(0, false); - } - - - // Fix for https://svn.boost.org/trac/boost/ticket/9628 - // For floating point coordinates, the coordinate of a point is compared - // with the segment's points using some EPS. If the coordinates are "equal" - // the sides are calculated. Therefore we can treat a segment as a long areal - // geometry having some width. There is a small ~triangular area somewhere - // between the segment's effective area and a segment's line used in sides - // calculation where the segment is on the one side of the line but on the - // other side of a segment (due to the width). - // Below picture assuming D = 1, if D = 0 horiz<->vert, E<->N, RIGHT<->UP. - // For the s1 of a segment going NE the real side is RIGHT but the point may - // be detected as LEFT, like this: - // RIGHT - // ___-----> - // ^ O Pt __ __ - // EPS __ __ - // v__ __ BUT DETECTED AS LEFT OF THIS LINE - // _____7 - // _____/ - // _____/ - // In the code below actually D = 0, so segments are nearly-vertical - // Called when the point is on the same level as one of the segment's points - // but the point is not aligned with a vertical segment - inline int side_equal(Point const& point, - PointOfSegment const& se, - count_info const& ci, - PointOfSegment const& s1, PointOfSegment const& s2) const - { - typedef typename coordinate_type::type scoord_t; - typedef typename coordinate_system::type::units units_t; - - if (math::equals(get<1>(point), get<1>(se))) - { - return 0; - } - - // Create a horizontal segment intersecting the original segment's endpoint - // equal to the point, with the derived direction (E/W). - PointOfSegment ss1, ss2; - set<1>(ss1, get<1>(se)); - set<0>(ss1, get<0>(se)); - set<1>(ss2, get<1>(se)); - scoord_t ss20 = get<0>(se); - if (ci.count > 0) - { - ss20 += small_angle(); - } - else - { - ss20 -= small_angle(); - } - math::normalize_longitude(ss20); - set<0>(ss2, ss20); - - // Check the side using this vertical segment - return m_side_strategy.apply(ss1, ss2, point); - } - - // 1 deg or pi/180 rad - static inline calculation_type small_angle() - { - return constants::half_period() / calculation_type(180); - }; - - static inline bool longitudes_equal(calculation_type const& lon1, calculation_type const& lon2) - { - return math::equals( - math::longitude_distance_signed(lon1, lon2), - calculation_type(0)); - } - - SideStrategy m_side_strategy; -}; - template < @@ -719,84 +85,6 @@ struct winding_base_type #endif // DOXYGEN_NO_DETAIL -/*! -\brief Within detection using winding rule in spherical coordinate system. -\ingroup strategies -\tparam Point \tparam_point -\tparam PointOfSegment \tparam_segment_point -\tparam CalculationType \tparam_calculation - -\qbk{ -[heading See also] -[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] -} - */ -template -< - typename Point, - typename PointOfSegment = Point, - typename CalculationType = void -> -class spherical_winding - : public within::detail::spherical_winding_base - < - Point, - PointOfSegment, - side::spherical_side_formula, - CalculationType - > -{}; - - -/*! -\brief Within detection using winding rule in geographic coordinate system. -\ingroup strategies -\tparam Point \tparam_point -\tparam PointOfSegment \tparam_segment_point -\tparam FormulaPolicy Geodesic formula policy -\tparam Spheroid Spheroid model -\tparam CalculationType \tparam_calculation - -\qbk{ -[heading See also] -[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] -} - */ -template -< - typename Point, - typename PointOfSegment = Point, - typename FormulaPolicy = strategy::andoyer, - typename Spheroid = srs::spheroid, - typename CalculationType = void -> -class geographic_winding - : public within::detail::spherical_winding_base - < - Point, - PointOfSegment, - side::geographic, - CalculationType - > -{ - typedef within::detail::spherical_winding_base - < - Point, - PointOfSegment, - side::geographic, - CalculationType - > base_t; - -public: - geographic_winding() - {} - - explicit geographic_winding(Spheroid const& model) - : base_t(model) - {} -}; - - /*! \brief Within detection using winding rule. Side strategy used internally is choosen based on Point's coordinate system. @@ -817,7 +105,10 @@ template typename CalculationType = void > class winding - : public within::detail::winding_base_type::type + : public within::detail::winding_base_type + < + Point, PointOfSegment, CalculationType + >::type { typedef typename within::detail::winding_base_type < @@ -834,107 +125,9 @@ public: }; -#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS - -namespace services -{ - -template -struct default_strategy -{ - typedef cartesian_winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef within::detail::spherical_winding_base - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef cartesian_winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef within::detail::spherical_winding_base - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -} // namespace services - -#endif - - }} // namespace strategy::within -#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS -namespace strategy { namespace covered_by { namespace services -{ - -template -struct default_strategy -{ - typedef within::cartesian_winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef within::detail::spherical_winding_base - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef within::cartesian_winding - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -template -struct default_strategy -{ - typedef within::detail::spherical_winding_base - < - typename geometry::point_type::type, - typename geometry::point_type::type - > type; -}; - -}}} // namespace strategy::covered_by::services -#endif - - }} // namespace boost::geometry diff --git a/include/boost/geometry/strategies/cartesian/intersection.hpp b/include/boost/geometry/strategies/cartesian/intersection.hpp index 867c8320d..233bb50b6 100644 --- a/include/boost/geometry/strategies/cartesian/intersection.hpp +++ b/include/boost/geometry/strategies/cartesian/intersection.hpp @@ -33,10 +33,10 @@ #include #include -#include #include #include #include +#include #include #include #include diff --git a/include/boost/geometry/strategies/cartesian/point_in_poly_winding.hpp b/include/boost/geometry/strategies/cartesian/point_in_poly_winding.hpp new file mode 100644 index 000000000..c41bc9b83 --- /dev/null +++ b/include/boost/geometry/strategies/cartesian/point_in_poly_winding.hpp @@ -0,0 +1,296 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013, 2014, 2016, 2017. +// Modifications copyright (c) 2013-2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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) + +#ifndef BOOST_GEOMETRY_STRATEGY_CARTESIAN_POINT_IN_POLY_WINDING_HPP +#define BOOST_GEOMETRY_STRATEGY_CARTESIAN_POINT_IN_POLY_WINDING_HPP + + +#include + +#include +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace within +{ + + +/*! +\brief Within detection using winding rule in cartesian coordinate system. +\ingroup strategies +\tparam Point \tparam_point +\tparam PointOfSegment \tparam_segment_point +\tparam CalculationType \tparam_calculation +\author Barend Gehrels +\note The implementation is inspired by terralib http://www.terralib.org (LGPL) +\note but totally revised afterwards, especially for cases on segments + +\qbk{ +[heading See also] +[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] +} + */ +template +< + typename Point, + typename PointOfSegment = Point, + typename CalculationType = void +> +class cartesian_winding +{ + typedef side::side_by_triangle side_strategy_type; + + typedef typename select_calculation_type + < + Point, + PointOfSegment, + CalculationType + >::type calculation_type; + + /*! subclass to keep state */ + class counter + { + int m_count; + bool m_touches; + + inline int code() const + { + return m_touches ? 0 : m_count == 0 ? -1 : 1; + } + + public : + friend class cartesian_winding; + + inline counter() + : m_count(0) + , m_touches(false) + {} + + }; + +public: + typedef typename side_strategy_type::envelope_strategy_type envelope_strategy_type; + + static inline envelope_strategy_type get_envelope_strategy() + { + return side_strategy_type::get_envelope_strategy(); + } + + typedef typename side_strategy_type::disjoint_strategy_type disjoint_strategy_type; + + static inline disjoint_strategy_type get_disjoint_strategy() + { + return side_strategy_type::get_disjoint_strategy(); + } + + // Typedefs and static methods to fulfill the concept + typedef Point point_type; + typedef PointOfSegment segment_point_type; + typedef counter state_type; + + static inline bool apply(Point const& point, + PointOfSegment const& s1, PointOfSegment const& s2, + counter& state) + { + bool eq1 = false; + bool eq2 = false; + + int count = check_segment(point, s1, s2, state, eq1, eq2); + if (count != 0) + { + int side = 0; + if (count == 1 || count == -1) + { + side = side_equal(point, eq1 ? s1 : s2, count); + } + else // count == 2 || count == -2 + { + // 1 left, -1 right + side = side_strategy_type::apply(s1, s2, point); + } + + if (side == 0) + { + // Point is lying on segment + state.m_touches = true; + state.m_count = 0; + return false; + } + + // Side is NEG for right, POS for left. + // The count is -2 for left, 2 for right (or -1/1) + // Side positive thus means RIGHT and LEFTSIDE or LEFT and RIGHTSIDE + // See accompagnying figure (TODO) + if (side * count > 0) + { + state.m_count += count; + } + } + return ! state.m_touches; + } + + static inline int result(counter const& state) + { + return state.code(); + } + +private: + static inline int check_segment(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, bool& eq2) + { + if (check_touch(point, seg1, seg2, state, eq1, eq2)) + { + return 0; + } + + return calculate_count(point, seg1, seg2, eq1, eq2); + } + + static inline bool check_touch(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, bool& eq2) + { + calculation_type const px = get<0>(point); + calculation_type const s1x = get<0>(seg1); + calculation_type const s2x = get<0>(seg2); + + eq1 = math::equals(s1x, px); + eq2 = math::equals(s2x, px); + + // Both equal p -> segment vertical + // The only thing which has to be done is check if point is ON segment + if (eq1 && eq2) + { + calculation_type const py = get<1>(point); + calculation_type const s1y = get<1>(seg1); + calculation_type const s2y = get<1>(seg2); + if ((s1y <= py && s2y >= py) || (s2y <= py && s1y >= py)) + { + state.m_touches = true; + } + return true; + } + return false; + } + + static inline int calculate_count(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + bool eq1, bool eq2) + { + calculation_type const p = get<0>(point); + calculation_type const s1 = get<0>(seg1); + calculation_type const s2 = get<0>(seg2); + + return eq1 ? (s2 > p ? 1 : -1) // Point on level s1, E/W depending on s2 + : eq2 ? (s1 > p ? -1 : 1) // idem + : s1 < p && s2 > p ? 2 // Point between s1 -> s2 --> E + : s2 < p && s1 > p ? -2 // Point between s2 -> s1 --> W + : 0; + } + + static inline int side_equal(Point const& point, + PointOfSegment const& se, + int count) + { + // NOTE: for D=0 the signs would be reversed + return math::equals(get<1>(point), get<1>(se)) ? + 0 : + get<1>(point) < get<1>(se) ? + // assuming count is equal to 1 or -1 + -count : // ( count > 0 ? -1 : 1) : + count; // ( count > 0 ? 1 : -1) ; + } +}; + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + +namespace services +{ + +template +struct default_strategy +{ + typedef cartesian_winding + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +template +struct default_strategy +{ + typedef cartesian_winding + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +} // namespace services + +#endif + + +}} // namespace strategy::within + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +namespace strategy { namespace covered_by { namespace services +{ + +template +struct default_strategy +{ + typedef within::cartesian_winding + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +template +struct default_strategy +{ + typedef within::cartesian_winding + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +}}} // namespace strategy::covered_by::services +#endif + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGY_CARTESIAN_POINT_IN_POLY_WINDING_HPP diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp index 668d4b922..489c7deb9 100644 --- a/include/boost/geometry/strategies/geographic/intersection.hpp +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/geometry/strategies/geographic/point_in_poly_winding.hpp b/include/boost/geometry/strategies/geographic/point_in_poly_winding.hpp new file mode 100644 index 000000000..95a196147 --- /dev/null +++ b/include/boost/geometry/strategies/geographic/point_in_poly_winding.hpp @@ -0,0 +1,80 @@ +// Boost.Geometry + +// Copyright (c) 2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// 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) + +#ifndef BOOST_GEOMETRY_STRATEGY_GEOGRAPHIC_POINT_IN_POLY_WINDING_HPP +#define BOOST_GEOMETRY_STRATEGY_GEOGRAPHIC_POINT_IN_POLY_WINDING_HPP + + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace within +{ + + +/*! +\brief Within detection using winding rule in geographic coordinate system. +\ingroup strategies +\tparam Point \tparam_point +\tparam PointOfSegment \tparam_segment_point +\tparam FormulaPolicy Geodesic formula policy +\tparam Spheroid Spheroid model +\tparam CalculationType \tparam_calculation + +\qbk{ +[heading See also] +[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] +} + */ +template +< + typename Point, + typename PointOfSegment = Point, + typename FormulaPolicy = strategy::andoyer, + typename Spheroid = srs::spheroid, + typename CalculationType = void +> +class geographic_winding + : public within::detail::spherical_winding_base + < + Point, + PointOfSegment, + side::geographic, + CalculationType + > +{ + typedef within::detail::spherical_winding_base + < + Point, + PointOfSegment, + side::geographic, + CalculationType + > base_t; + +public: + geographic_winding() + {} + + explicit geographic_winding(Spheroid const& model) + : base_t(model) + {} +}; + + +}} // namespace strategy::within + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGY_GEOGRAPHIC_POINT_IN_POLY_WINDING_HPP diff --git a/include/boost/geometry/strategies/spherical/intersection.hpp b/include/boost/geometry/strategies/spherical/intersection.hpp index 5080729d9..b5ae878b8 100644 --- a/include/boost/geometry/strategies/spherical/intersection.hpp +++ b/include/boost/geometry/strategies/spherical/intersection.hpp @@ -33,7 +33,6 @@ #include -#include #include #include #include @@ -42,6 +41,7 @@ #include #include #include +#include #include #include diff --git a/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp b/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp new file mode 100644 index 000000000..fddbad95f --- /dev/null +++ b/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp @@ -0,0 +1,583 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. + +// This file was modified by Oracle on 2013, 2014, 2016, 2017. +// Modifications copyright (c) 2013-2017 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, 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) + +#ifndef BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP +#define BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP + + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace within +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template +< + typename Point, + typename PointOfSegment = Point, + typename SideStrategy = typename strategy::side::services::default_strategy + < + typename cs_tag::type + >::type, + typename CalculationType = void +> +class spherical_winding_base +{ + typedef typename select_calculation_type + < + Point, + PointOfSegment, + CalculationType + >::type calculation_type; + + typedef typename coordinate_system::type::units units_t; + typedef math::detail::constants_on_spheroid constants; + + /*! subclass to keep state */ + class counter + { + int m_count; + //int m_count_n; + int m_count_s; + int m_raw_count; + int m_raw_count_anti; + bool m_touches; + + inline int code() const + { + if (m_touches) + { + return 0; + } + + if (m_raw_count != 0 && m_raw_count_anti != 0) + { + if (m_raw_count > 0) // right, wrap around south pole + { + return (m_count + m_count_s) == 0 ? -1 : 1; + } + else // left, wrap around north pole + { + //return (m_count + m_count_n) == 0 ? -1 : 1; + // m_count_n is 0 + return m_count == 0 ? -1 : 1; + } + } + + return m_count == 0 ? -1 : 1; + } + + public : + friend class spherical_winding_base; + + inline counter() + : m_count(0) + //, m_count_n(0) + , m_count_s(0) + , m_raw_count(0) + , m_raw_count_anti(0) + , m_touches(false) + {} + + }; + + struct count_info + { + explicit count_info(int c = 0, bool ia = false) + : count(c) + , is_anti(ia) + {} + + int count; + bool is_anti; + }; + +public: + typedef typename SideStrategy::envelope_strategy_type envelope_strategy_type; + + inline envelope_strategy_type get_envelope_strategy() const + { + return m_side_strategy.get_envelope_strategy(); + } + + typedef typename SideStrategy::disjoint_strategy_type disjoint_strategy_type; + + inline disjoint_strategy_type get_disjoint_strategy() const + { + return m_side_strategy.get_disjoint_strategy(); + } + + spherical_winding_base() + {} + + template + explicit spherical_winding_base(Model const& model) + : m_side_strategy(model) + {} + + // Typedefs and static methods to fulfill the concept + typedef Point point_type; + typedef PointOfSegment segment_point_type; + typedef counter state_type; + + inline bool apply(Point const& point, + PointOfSegment const& s1, PointOfSegment const& s2, + counter& state) const + { + bool eq1 = false; + bool eq2 = false; + bool s_antipodal = false; + + count_info ci = check_segment(point, s1, s2, state, eq1, eq2, s_antipodal); + if (ci.count != 0) + { + if (! ci.is_anti) + { + int side = 0; + if (ci.count == 1 || ci.count == -1) + { + side = side_equal(point, eq1 ? s1 : s2, ci, s1, s2); + } + else // count == 2 || count == -2 + { + if (! s_antipodal) + { + // 1 left, -1 right + side = m_side_strategy.apply(s1, s2, point); + } + else + { + calculation_type const pi = constants::half_period(); + calculation_type const s1_lat = get<1>(s1); + calculation_type const s2_lat = get<1>(s2); + + side = math::sign(ci.count) + * (pi - s1_lat - s2_lat <= pi // segment goes through north pole + ? -1 // going right all points will be on right side + : 1); // going right all points will be on left side + } + } + + if (side == 0) + { + // Point is lying on segment + state.m_touches = true; + state.m_count = 0; + return false; + } + + // Side is NEG for right, POS for left. + // The count is -2 for left, 2 for right (or -1/1) + // Side positive thus means RIGHT and LEFTSIDE or LEFT and RIGHTSIDE + // See accompagnying figure (TODO) + if (side * ci.count > 0) + { + state.m_count += ci.count; + } + + state.m_raw_count += ci.count; + } + else + { + // Count negated because the segment is on the other side of the globe + // so it is reversed to match this side of the globe + + // Assuming geometry wraps around north pole, for segments on the other side of the globe + // the point will always be RIGHT+RIGHTSIDE or LEFT+LEFTSIDE, so side*-count always < 0 + //state.m_count_n -= 0; + + // Assuming geometry wraps around south pole, for segments on the other side of the globe + // the point will always be RIGHT+LEFTSIDE or LEFT+RIGHTSIDE, so side*-count always > 0 + state.m_count_s -= ci.count; + + state.m_raw_count_anti -= ci.count; + } + } + return ! state.m_touches; + } + + static inline int result(counter const& state) + { + return state.code(); + } + +private: + + static inline count_info check_segment(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, bool& eq2, bool& s_antipodal) + { + if (check_touch(point, seg1, seg2, state, eq1, eq2, s_antipodal)) + { + return count_info(0, false); + } + + return calculate_count(point, seg1, seg2, eq1, eq2, s_antipodal); + } + + static inline int check_touch(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + counter& state, + bool& eq1, + bool& eq2, + bool& s_antipodal) + { + calculation_type const c0 = 0; + calculation_type const c2 = 2; + calculation_type const pi = constants::half_period(); + calculation_type const half_pi = pi / c2; + + calculation_type const p_lon = get<0>(point); + calculation_type const s1_lon = get<0>(seg1); + calculation_type const s2_lon = get<0>(seg2); + calculation_type const p_lat = get<1>(point); + calculation_type const s1_lat = get<1>(seg1); + calculation_type const s2_lat = get<1>(seg2); + + // NOTE: lat in {-90, 90} and arbitrary lon + // it doesn't matter what lon it is if it's a pole + // so e.g. if one of the segment endpoints is a pole + // then only the other lon matters + + bool eq1_strict = longitudes_equal(s1_lon, p_lon); + bool eq2_strict = longitudes_equal(s2_lon, p_lon); + bool eq1_anti = false; + bool eq2_anti = false; + + calculation_type const anti_p_lon = p_lon + (p_lon <= c0 ? pi : -pi); + + eq1 = eq1_strict // lon strictly equal to s1 + || (eq1_anti = longitudes_equal(s1_lon, anti_p_lon)) // anti-lon strictly equal to s1 + || math::equals(math::abs(s1_lat), half_pi); // s1 is pole + eq2 = eq2_strict // lon strictly equal to s2 + || (eq2_anti = longitudes_equal(s2_lon, anti_p_lon)) // anti-lon strictly equal to s2 + || math::equals(math::abs(s2_lat), half_pi); // s2 is pole + + // segment overlapping pole + calculation_type const s_lon_diff = math::longitude_distance_signed(s1_lon, s2_lon); + s_antipodal = math::equals(s_lon_diff, pi); + if (s_antipodal) + { + eq1 = eq2 = eq1 || eq2; + + // segment overlapping pole and point is pole + if (math::equals(math::abs(p_lat), half_pi)) + { + eq1 = eq2 = true; + } + } + + // Both equal p -> segment vertical + // The only thing which has to be done is check if point is ON segment + if (eq1 && eq2) + { + // segment endpoints on the same sides of the globe + if (! s_antipodal) + { + // p's lat between segment endpoints' lats + if ( (s1_lat <= p_lat && s2_lat >= p_lat) || (s2_lat <= p_lat && s1_lat >= p_lat) ) + { + if (!eq1_anti || !eq2_anti) + { + state.m_touches = true; + } + } + } + else + { + // going through north or south pole? + if (pi - s1_lat - s2_lat <= pi) + { + if ( (eq1_strict && s1_lat <= p_lat) || (eq2_strict && s2_lat <= p_lat) // north + || math::equals(p_lat, half_pi) ) // point on north pole + { + state.m_touches = true; + } + else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, -half_pi) ) // point on south pole + { + return false; + } + } + else // south pole + { + if ( (eq1_strict && s1_lat >= p_lat) || (eq2_strict && s2_lat >= p_lat) // south + || math::equals(p_lat, -half_pi) ) // point on south pole + { + state.m_touches = true; + } + else if (! eq1_strict && ! eq2_strict && math::equals(p_lat, half_pi) ) // point on north pole + { + return false; + } + } + } + + return true; + } + + return false; + } + + // Called if point is not aligned with a vertical segment + static inline count_info calculate_count(Point const& point, + PointOfSegment const& seg1, + PointOfSegment const& seg2, + bool eq1, bool eq2, bool s_antipodal) + { + // If both segment endpoints were poles below checks wouldn't be enough + // but this means that either both are the same or that they are N/S poles + // and therefore the segment is not valid. + // If needed (eq1 && eq2 ? 0) could be returned + + calculation_type const c0 = 0; + calculation_type const c2 = 2; + calculation_type const pi = constants::half_period(); + calculation_type const half_pi = pi / c2; + + calculation_type const p = get<0>(point); + calculation_type const s1 = get<0>(seg1); + calculation_type const s2 = get<0>(seg2); + + calculation_type const s1_p = math::longitude_distance_signed(s1, p); + + if (s_antipodal) + { + return count_info(s1_p < c0 ? -2 : 2, false); // choose W/E + } + + calculation_type const s1_s2 = math::longitude_distance_signed(s1, s2); + + if (eq1 || eq2) // Point on level s1 or s2 + { + return count_info(s1_s2 < c0 ? -1 : 1, // choose W/E + longitudes_equal(p + pi, (eq1 ? s1 : s2))); + } + + // Point between s1 and s2 + if ( math::sign(s1_p) == math::sign(s1_s2) + && math::abs(s1_p) < math::abs(s1_s2) ) + { + return count_info(s1_s2 < c0 ? -2 : 2, false); // choose W/E + } + + calculation_type const s1_p_anti = math::longitude_distance_signed(s1, p + pi); + + // Anti-Point between s1 and s2 + if ( math::sign(s1_p_anti) == math::sign(s1_s2) + && math::abs(s1_p_anti) < math::abs(s1_s2) ) + { + return count_info(s1_s2 < c0 ? -2 : 2, true); // choose W/E + } + + return count_info(0, false); + } + + + // Fix for https://svn.boost.org/trac/boost/ticket/9628 + // For floating point coordinates, the coordinate of a point is compared + // with the segment's points using some EPS. If the coordinates are "equal" + // the sides are calculated. Therefore we can treat a segment as a long areal + // geometry having some width. There is a small ~triangular area somewhere + // between the segment's effective area and a segment's line used in sides + // calculation where the segment is on the one side of the line but on the + // other side of a segment (due to the width). + // Below picture assuming D = 1, if D = 0 horiz<->vert, E<->N, RIGHT<->UP. + // For the s1 of a segment going NE the real side is RIGHT but the point may + // be detected as LEFT, like this: + // RIGHT + // ___-----> + // ^ O Pt __ __ + // EPS __ __ + // v__ __ BUT DETECTED AS LEFT OF THIS LINE + // _____7 + // _____/ + // _____/ + // In the code below actually D = 0, so segments are nearly-vertical + // Called when the point is on the same level as one of the segment's points + // but the point is not aligned with a vertical segment + inline int side_equal(Point const& point, + PointOfSegment const& se, + count_info const& ci, + PointOfSegment const& s1, PointOfSegment const& s2) const + { + typedef typename coordinate_type::type scoord_t; + typedef typename coordinate_system::type::units units_t; + + if (math::equals(get<1>(point), get<1>(se))) + { + return 0; + } + + // Create a horizontal segment intersecting the original segment's endpoint + // equal to the point, with the derived direction (E/W). + PointOfSegment ss1, ss2; + set<1>(ss1, get<1>(se)); + set<0>(ss1, get<0>(se)); + set<1>(ss2, get<1>(se)); + scoord_t ss20 = get<0>(se); + if (ci.count > 0) + { + ss20 += small_angle(); + } + else + { + ss20 -= small_angle(); + } + math::normalize_longitude(ss20); + set<0>(ss2, ss20); + + // Check the side using this vertical segment + return m_side_strategy.apply(ss1, ss2, point); + } + + // 1 deg or pi/180 rad + static inline calculation_type small_angle() + { + return constants::half_period() / calculation_type(180); + }; + + static inline bool longitudes_equal(calculation_type const& lon1, calculation_type const& lon2) + { + return math::equals( + math::longitude_distance_signed(lon1, lon2), + calculation_type(0)); + } + + SideStrategy m_side_strategy; +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +/*! +\brief Within detection using winding rule in spherical coordinate system. +\ingroup strategies +\tparam Point \tparam_point +\tparam PointOfSegment \tparam_segment_point +\tparam CalculationType \tparam_calculation + +\qbk{ +[heading See also] +[link geometry.reference.algorithms.within.within_3_with_strategy within (with strategy)] +} + */ +template +< + typename Point, + typename PointOfSegment = Point, + typename CalculationType = void +> +class spherical_winding + : public within::detail::spherical_winding_base + < + Point, + PointOfSegment, + side::spherical_side_formula, + CalculationType + > +{}; + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + +namespace services +{ + +template +struct default_strategy +{ + typedef within::detail::spherical_winding_base + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +template +struct default_strategy +{ + typedef within::detail::spherical_winding_base + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +} // namespace services + +#endif + + +}} // namespace strategy::within + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +namespace strategy { namespace covered_by { namespace services +{ + +template +struct default_strategy +{ + typedef within::detail::spherical_winding_base + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +template +struct default_strategy +{ + typedef within::detail::spherical_winding_base + < + typename geometry::point_type::type, + typename geometry::point_type::type + > type; +}; + +}}} // namespace strategy::covered_by::services +#endif + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGY_SPHERICAL_POINT_IN_POLY_WINDING_HPP diff --git a/include/boost/geometry/strategies/strategies.hpp b/include/boost/geometry/strategies/strategies.hpp index 27a025c7c..14b619845 100644 --- a/include/boost/geometry/strategies/strategies.hpp +++ b/include/boost/geometry/strategies/strategies.hpp @@ -65,17 +65,19 @@ #include #include #include +#include #include #include #include +#include #include #include #include #include -#include #include #include +#include #include #include @@ -88,6 +90,7 @@ #include #include //#include +#include #include #include #include From 392a724693619d8685e4728ee5dd0e7a2e391004 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 11:06:18 +0200 Subject: [PATCH 071/188] [overlay] fix inclusion of rings which were marked as blocked. --- .../detail/overlay/handle_colocations.hpp | 2 +- .../algorithms/detail/overlay/overlay.hpp | 23 ++++++++----------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index 8cc2880f1..07a2c00a6 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -521,7 +521,7 @@ inline void set_colocation(Turns& turns, Clusters const& clusters) { has_ii = true; } - if (turn.both(operation_union) || turn.combination(operation_union, operation_blocked)) + if (turn.both(operation_union)) { has_uu = true; } diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 799b52e09..fd1df271e 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -116,7 +116,6 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C { turn_type const& turn = *it; - bool const both_opposite = turn.both(opposite_operation); bool const colocated_target = target_operation == operation_union ? turn.colocated_uu : turn.colocated_ii; @@ -137,19 +136,17 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C { turn_operation_type const& op = *op_it; - // Block, for example, i for union and u for intersection - // Don't block self-uu for intersection - // Don't block self-ii for union - // Don't block i/u if there is an ii too, for a union - bool opposite_op = op.operation == opposite_operation + // Block closed rings (for union), rings where anything is blocked, + // and (with exceptions): i for union and u for intersection + // Exceptions: don't block self-uu for intersection + // don't block self-ii for union + // don't block (for union) i/u if there is an self-ii too + if (is_closed + || turn.any_blocked() + || (op.operation == opposite_operation && ! colocated_target - && ! (both_opposite - && is_self_turn(turn)); - - if (opposite_op - || is_closed - || turn.both(operation_blocked) - || turn.combination(opposite_operation, operation_blocked)) + && ! (turn.both(opposite_operation) + && is_self_turn(turn)))) { ring_identifier const ring_id ( From 46e45334616d5b8877e92f9a99750137ed688f45 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 11:06:38 +0200 Subject: [PATCH 072/188] [overlay] add missing include --- .../geometry/algorithms/detail/overlay/handle_colocations.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index 07a2c00a6..065aeaff7 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include From e69c05da2fbbfcf364e9a2b2777a3ea3554b16a2 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 11:23:32 +0200 Subject: [PATCH 073/188] [overlay][test] add testcase for needing to avoid having blocked turns in untraversed rings --- test/algorithms/overlay/multi_overlay_cases.hpp | 7 +++++++ .../set_operations/difference/difference_multi.cpp | 1 + .../set_operations/intersection/intersection_multi.cpp | 2 ++ test/algorithms/set_operations/union/union_multi.cpp | 1 + 4 files changed, 11 insertions(+) diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 5e820cd17..ca23ea3a4 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1123,6 +1123,13 @@ static std::string case_recursive_boxes_66[2] = "MULTIPOLYGON(((2 0,0 0,0 1,1 1,1 2,0 2,0 5,1 5,2 4,2 5,5 5,5 0,2 0),(3 1,3 2,2.5 1.5,3 1),(2 3,1 2,2 1,2 2,3 2,4 3,2 3),(0 3,0.5 2.5,1 3,1 4,0 3)))" }; +static std::string case_recursive_boxes_67[2] = +{ + // Needs to avoid including any untraveled ring with blocked turns + "MULTIPOLYGON(((2 2,3 3,3 2,2 2)),((2 2,1 2,1 3,2 3,2 2)),((1 1,2 1,2 0,0 0,0 1,1 1)),((2 4,1 4,0.5 3.5,1 3,0 3,0 5,1 5,2 4)),((4 2,4 3,5 2,4 2)),((4 2,4 1,3 1,4 2)),((3 3,3 4,4 4,4 3,3 3)),((4 4,4 5,5 5,5 4,4 4)))", + "MULTIPOLYGON(((3 4,4 5,4 4,3 4)),((3 1,3 0,2 0,3 1)),((3 1,1 1,1 2,2 3,3 3,3 2,2 2,3 1)),((3 1,3 2,5 2,5 1,3 1)),((2 3,0 3,0 4,1 4,2 3)),((1 3,1 2,0 2,1 3)),((1 4,1 5,2 5,1 4)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index e17865445..1ab96c059 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -371,6 +371,7 @@ void test_areal() #endif TEST_DIFFERENCE_IGNORE(case_recursive_boxes_66, 5, 4.75, 7, 4.0, 9); + TEST_DIFFERENCE(case_recursive_boxes_67, 7, 6.25, 9, 6.0, 10); { ut_settings sym_settings; diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index c3717ef0c..d42f35836 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -322,6 +322,8 @@ void test_areal() TEST_INTERSECTION_IGNORE(case_recursive_boxes_66, 2, -1, 16.0); #endif + TEST_INTERSECTION(case_recursive_boxes_67, 5, -1, 2.5); + test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], 2, 10, 6.0); // Area from SQL Server diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 517c51e1d..347c98588 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -388,6 +388,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_64, 1, 2, -1, 24.5); TEST_UNION(case_recursive_boxes_65, 1, 1, -1, 24.5); TEST_UNION(case_recursive_boxes_66, 1, 1, -1, 24.75); + TEST_UNION(case_recursive_boxes_67, 4, 0, -1, 14.75); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From d2232552d16fc3577b9694bce04352c14700b9a1 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 11:24:10 +0200 Subject: [PATCH 074/188] [test] remove the IGNORE flag for three cases which are already fixed --- .../set_operations/difference/difference_multi.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 1ab96c059..1abf21d95 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -304,7 +304,7 @@ void test_areal() #endif TEST_DIFFERENCE(case_recursive_boxes_35, 5, 1.75, 5, 2.75, 10); TEST_DIFFERENCE(case_recursive_boxes_36, 2, 1.0, 2, 1.5, 3); - TEST_DIFFERENCE_IGNORE(case_recursive_boxes_37, 3, 2.5, 2, 4.25, 2); + TEST_DIFFERENCE(case_recursive_boxes_37, 3, 2.5, 2, 4.25, 2); TEST_DIFFERENCE(case_recursive_boxes_38, 5, 7.75, 4, 3.5, 3); TEST_DIFFERENCE(case_recursive_boxes_39, 3, 6.0, 3, 3.0, 4); TEST_DIFFERENCE(case_recursive_boxes_40, 11, 14.0, 9, 13.0, 11); @@ -336,7 +336,11 @@ void test_areal() TEST_DIFFERENCE(case_recursive_boxes_53, 6, 3.5, 4, 1.5, 9); TEST_DIFFERENCE(case_recursive_boxes_54, 6, 6.5, 8, 6.0, 7); TEST_DIFFERENCE(case_recursive_boxes_55, 4, 5.5, 6, 7.75, 4); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_56, 4, 4.5, 5, 2.75, 6); +#else TEST_DIFFERENCE_IGNORE(case_recursive_boxes_56, 4, 4.5, 5, 2.75, 6); +#endif TEST_DIFFERENCE(case_recursive_boxes_57, 5, 3.75, 9, 6.5, 10); TEST_DIFFERENCE(case_recursive_boxes_58, 4, 2.25, 6, 3.75, 7); #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS @@ -370,7 +374,7 @@ void test_areal() TEST_DIFFERENCE_IGNORE(case_recursive_boxes_65, 4, 4.25, 7, 3.0, 11); #endif - TEST_DIFFERENCE_IGNORE(case_recursive_boxes_66, 5, 4.75, 7, 4.0, 9); + TEST_DIFFERENCE(case_recursive_boxes_66, 5, 4.75, 7, 4.0, 9); TEST_DIFFERENCE(case_recursive_boxes_67, 7, 6.25, 9, 6.0, 10); { From ccadeaeeff74653ec466bc153d4847d3e3702673 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 11:25:18 +0200 Subject: [PATCH 075/188] [overlay] move declaration (this is missing in an earlier commit) --- include/boost/geometry/algorithms/detail/overlay/overlay.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index fd1df271e..77127eea0 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -299,6 +299,7 @@ std::cout << "enrich" << std::endl; #endif typename Strategy::side_strategy_type side_strategy = strategy.get_side_strategy(); cluster_type clusters; + std::map turn_info_per_ring; geometry::enrich_intersection_points(turns, clusters, geometry1, geometry2, @@ -322,11 +323,11 @@ std::cout << "traverse" << std::endl; strategy, robust_policy, turns, rings, + turn_info_per_ring, clusters, visitor ); - std::map turn_info_per_ring; get_ring_turn_info(turn_info_per_ring, turns, clusters); typedef typename Strategy::template area_strategy::type area_strategy_type; From fae33e64f8f14e6d987e909bf1f512177418a54e Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 13:43:36 +0200 Subject: [PATCH 076/188] [overlay] remove condition which is not active anymore --- .../algorithms/detail/overlay/traversal_switch_detector.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index 0b4f393ef..cfca1fb49 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -476,10 +476,6 @@ struct traversal_switch_detector { signed_size_type turn_index = *sit; turn_type const& turn = m_turns[turn_index]; - if (turn.colocated_ii && ! turn.colocated_uu) - { - continue; - } for (int oi = 0; oi < 2; oi++) { int const region = get_region_id(turn.operations[oi]); From 6db9266768f2e2c94697ca549508f88dd1d183fb Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 13:49:02 +0200 Subject: [PATCH 077/188] [overlay] replace colocated_ii / uu with has_colocated_both because only one of them is used. --- .../detail/overlay/handle_colocations.hpp | 29 +++++------- .../algorithms/detail/overlay/overlay.hpp | 5 +- .../algorithms/detail/overlay/turn_info.hpp | 7 +-- test/algorithms/overlay/overlay.cpp | 47 ++++++++++++++----- 4 files changed, 50 insertions(+), 38 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index 065aeaff7..a3eed0605 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -499,6 +499,7 @@ inline void discard_interior_exterior_turns(Turns& turns, Clusters& clusters) template < + overlay_type OverlayType, typename Turns, typename Clusters > @@ -513,32 +514,26 @@ inline void set_colocation(Turns& turns, Clusters const& clusters) cluster_info const& cinfo = cit->second; std::set const& ids = cinfo.turn_indices; - bool has_ii = false; - bool has_uu = false; + bool both_target = false; for (set_iterator it = ids.begin(); it != ids.end(); ++it) { turn_type const& turn = turns[*it]; - if (turn.both(operation_intersection)) + if (turn.both(operation_from_overlay::value)) { - has_ii = true; - } - if (turn.both(operation_union)) - { - has_uu = true; + both_target = true; + break; } } - if (has_ii || has_uu) + + if (both_target) { for (set_iterator it = ids.begin(); it != ids.end(); ++it) { turn_type& turn = turns[*it]; - if (has_ii) + + if (both_target) { - turn.colocated_ii = true; - } - if (has_uu) - { - turn.colocated_uu = true; + turn.has_colocated_both = true; } } } @@ -636,7 +631,9 @@ inline bool handle_colocations(Turns& turns, Clusters& clusters, } assign_cluster_to_turns(turns, clusters, cluster_per_segment); - set_colocation(turns, clusters); + // Get colocated information here and not later, to keep information + // on turns which are discarded afterwards + set_colocation(turns, clusters); discard_interior_exterior_turns < do_reverse::value>::value != Reverse1, diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 77127eea0..7d38e027a 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -116,9 +116,6 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C { turn_type const& turn = *it; - bool const colocated_target = target_operation == operation_union - ? turn.colocated_uu : turn.colocated_ii; - bool is_closed = false; if (turn.cluster_id >= 0 && target_operation == operation_union) { @@ -144,7 +141,7 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C if (is_closed || turn.any_blocked() || (op.operation == opposite_operation - && ! colocated_target + && ! turn.has_colocated_both && ! (turn.both(opposite_operation) && is_self_turn(turn)))) { diff --git a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp index 3a4c2e94a..a35bb1a18 100644 --- a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -93,9 +93,7 @@ struct turn_info signed_size_type cluster_id; // For multiple turns on same location, >= 0. Else -1 bool discarded; - // TODO: move this to enriched - bool colocated_ii; // Colocated with a ii turn (TODO: or a ix turn) - bool colocated_uu; // Colocated with a uu turn or a ux turn + bool has_colocated_both; // Colocated with a uu turn (for union) or ii (other) bool switch_source; // For u/u turns which can either switch or not Container operations; @@ -105,8 +103,7 @@ struct turn_info , touch_only(false) , cluster_id(-1) , discarded(false) - , colocated_ii(false) - , colocated_uu(false) + , has_colocated_both(false) , switch_source(false) {} diff --git a/test/algorithms/overlay/overlay.cpp b/test/algorithms/overlay/overlay.cpp index 8aeaa8fb2..8303afa7b 100644 --- a/test/algorithms/overlay/overlay.cpp +++ b/test/algorithms/overlay/overlay.cpp @@ -246,10 +246,15 @@ struct map_visitor result = true; } } - } - if (! turn.operations[index].enriched.startable) - { - os << "$"; + + os << " {" << turn.operations[index].enriched.region_id + << (turn.operations[index].enriched.isolated ? " ISO" : "") + << "}"; + + if (! turn.operations[index].enriched.startable) + { + os << "$"; + } } return result; @@ -271,17 +276,33 @@ struct map_visitor { out << "#"; } - - std::string font8 = "font-family:Arial;font-size:8px"; - std::string font6 = "font-family:Arial;font-size:6px"; - std::string style = "fill:rgb(0,0,255);" + font8; - if (turn.operations[0].seg_id.source_index == turn.operations[1].seg_id.source_index) + if (turn.discarded) { - style = "fill:rgb(255,0,255);" + font8; + out << "!"; } - else if (turn.colocated) + if (turn.has_colocated_both) { - style = "fill:rgb(255,0,0);" + font8; + out << "+"; + } + bool const self_turn = bg::detail::overlay::is_self_turn(turn); + if (self_turn) + { + out << "@"; + } + + std::string font8 = "font-family:Arial;font-size:6px"; + std::string font6 = "font-family:Arial;font-size:4px"; + std::string style = "fill:rgb(0,0,255);" + font8; + if (self_turn) + { + if (turn.discarded) + { + style = "fill:rgb(128,28,128);" + font6; + } + else + { + style = "fill:rgb(255,0,255);" + font8; + } } else if (turn.discarded) { @@ -303,7 +324,7 @@ struct map_visitor void add_text(Turn const& turn, std::string const& text, std::string const& style) { int const margin = 5; - int const lineheight = 8; + int const lineheight = 6; double const half = 0.5; double const ten = 10; From 1dd54315607be2756c9c42a7c13b1cd280bd8808 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 13:51:27 +0200 Subject: [PATCH 078/188] [overlay] check colocated turns for being blocked. This also moves the place where is_closed is set --- .../detail/overlay/handle_colocations.hpp | 37 +++++++++++++++ .../algorithms/detail/overlay/overlay.hpp | 45 ++++++++++++------- 2 files changed, 66 insertions(+), 16 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index a3eed0605..04aac2d47 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -540,6 +540,43 @@ inline void set_colocation(Turns& turns, Clusters const& clusters) } } +template +< + typename Turns, + typename Clusters +> +inline void check_colocation(bool& has_blocked, bool& is_closed, + int cluster_id, + Turns const& turns, Clusters const& clusters) +{ + typedef typename boost::range_value::type turn_type; + + has_blocked = false; + is_closed = false; + + typename Clusters::const_iterator mit = clusters.find(cluster_id); + if (mit == clusters.end()) + { + return; + } + + cluster_info const& cinfo = mit->second; + + is_closed = cinfo.open_count == 0; + + for (std::set::const_iterator it + = cinfo.turn_indices.begin(); + it != cinfo.turn_indices.end(); ++it) + { + turn_type const& turn = turns[*it]; + if (turn.any_blocked()) + { + has_blocked = true; + } + } +} + + // Checks colocated turns and flags combinations of uu/other, possibly a // combination of a ring touching another geometry's interior ring which is // tangential to the exterior ring diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 7d38e027a..8d567adf1 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -116,15 +116,9 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C { turn_type const& turn = *it; + bool cluster_checked = false; + bool has_blocked = false; bool is_closed = false; - if (turn.cluster_id >= 0 && target_operation == operation_union) - { - typename Clusters::const_iterator mit = clusters.find(turn.cluster_id); - BOOST_ASSERT(mit != clusters.end()); - - cluster_info const& cinfo = mit->second; - is_closed = cinfo.open_count == 0; - } for (typename boost::range_iterator::type op_it = boost::begin(turn.operations); @@ -132,26 +126,45 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C ++op_it) { turn_operation_type const& op = *op_it; + ring_identifier const ring_id + ( + op.seg_id.source_index, + op.seg_id.multi_index, + op.seg_id.ring_index + ); + + if (turn.any_blocked()) + { + turn_info_map[ring_id].has_blocked_turn = true; + } + + if (turn_info_map[ring_id].has_traversed_turn + || turn_info_map[ring_id].has_blocked_turn) + { + continue; + } + + // Check information in colocated turns + if (! cluster_checked && turn.cluster_id >= 0) + { + check_colocation(has_blocked, is_closed, + turn.cluster_id, turns, clusters); + cluster_checked = true; + } // Block closed rings (for union), rings where anything is blocked, // and (with exceptions): i for union and u for intersection // Exceptions: don't block self-uu for intersection // don't block self-ii for union // don't block (for union) i/u if there is an self-ii too - if (is_closed + if (has_blocked || turn.any_blocked() + || (is_closed && target_operation == operation_union) || (op.operation == opposite_operation && ! turn.has_colocated_both && ! (turn.both(opposite_operation) && is_self_turn(turn)))) { - ring_identifier const ring_id - ( - op.seg_id.source_index, - op.seg_id.multi_index, - op.seg_id.ring_index - ); - turn_info_map[ring_id].has_blocked_turn = true; } } From 2088e708cd45e05d78d666ee512c6e1b4336158f Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 13:52:25 +0200 Subject: [PATCH 079/188] [test] add testcase for union for last fix --- test/algorithms/overlay/multi_overlay_cases.hpp | 7 +++++++ test/algorithms/set_operations/union/union_multi.cpp | 1 + 2 files changed, 8 insertions(+) diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index ca23ea3a4..c33cf0bb5 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1130,6 +1130,13 @@ static std::string case_recursive_boxes_67[2] = "MULTIPOLYGON(((3 4,4 5,4 4,3 4)),((3 1,3 0,2 0,3 1)),((3 1,1 1,1 2,2 3,3 3,3 2,2 2,3 1)),((3 1,3 2,5 2,5 1,3 1)),((2 3,0 3,0 4,1 4,2 3)),((1 3,1 2,0 2,1 3)),((1 4,1 5,2 5,1 4)))" }; +static std::string case_recursive_boxes_68[2] = +{ + // Needs checking blocked turns in colocated turns + "MULTIPOLYGON(((3 3,3 4,4 4,4 5,5 5,4.5 4.5,5 4,5 3,4.5 2.5,5 2,2 2,2 4,3 3)),((2 5,3 5,3 4,2 4,1 3,1 2,0 2,0 4,1 4,1 5,2 5)),((2 2,4 0,1 0,2 1,1 1,1 2,2 2)),((1 0,0 0,0 1,1 1,1 0)),((4 0,5 1,5 0,4 0)),((5 1,4 1,4 2,5 1)))", + "MULTIPOLYGON(((2 0,2 1,1 1,2 2,3 2,3 1,4 2,5 2,5 0,2 0)),((2 0,1 0,1 1,2 0)),((2 2,2 3,3 4,3 3,2 2)),((3 2,3 3,4 4,5 4,5 3,4 2,3 2)),((3 4,3 5,4 4,3 4)),((2 4,2 3,1 2,1 3,0 3,0 5,1 5,1 4,2 4)),((1 2,1 1,0 1,0 2,1 2)),((4 4,4 5,5 5,4 4)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 347c98588..dee1db12e 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -389,6 +389,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_65, 1, 1, -1, 24.5); TEST_UNION(case_recursive_boxes_66, 1, 1, -1, 24.75); TEST_UNION(case_recursive_boxes_67, 4, 0, -1, 14.75); + TEST_UNION(case_recursive_boxes_68, 1, 4, -1, 22.5); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 32705f5b324a934ae30e3806d7403827ee1180fb Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 15:37:00 +0200 Subject: [PATCH 080/188] [overlay] change is_closed to using count_left for decision to include polygons --- .../detail/overlay/handle_colocations.hpp | 8 ++------ .../algorithms/detail/overlay/overlay.hpp | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index 04aac2d47..41d18a4ed 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -545,14 +545,12 @@ template typename Turns, typename Clusters > -inline void check_colocation(bool& has_blocked, bool& is_closed, - int cluster_id, - Turns const& turns, Clusters const& clusters) +inline void check_colocation(bool& has_blocked, + int cluster_id, Turns const& turns, Clusters const& clusters) { typedef typename boost::range_value::type turn_type; has_blocked = false; - is_closed = false; typename Clusters::const_iterator mit = clusters.find(cluster_id); if (mit == clusters.end()) @@ -562,8 +560,6 @@ inline void check_colocation(bool& has_blocked, bool& is_closed, cluster_info const& cinfo = mit->second; - is_closed = cinfo.open_count == 0; - for (std::set::const_iterator it = cinfo.turn_indices.begin(); it != cinfo.turn_indices.end(); ++it) diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 8d567adf1..ba0fcfbe4 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -118,7 +118,6 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C bool cluster_checked = false; bool has_blocked = false; - bool is_closed = false; for (typename boost::range_iterator::type op_it = boost::begin(turn.operations); @@ -137,29 +136,35 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C { turn_info_map[ring_id].has_blocked_turn = true; } - if (turn_info_map[ring_id].has_traversed_turn || turn_info_map[ring_id].has_blocked_turn) { continue; } + if (target_operation == operation_union + && ! is_self_turn(turn) + && op.enriched.count_left > 0) + { + // Avoid including untraversed rings in unions which have + // polygons on their left side + turn_info_map[ring_id].has_blocked_turn = true; + continue; + } + // Check information in colocated turns if (! cluster_checked && turn.cluster_id >= 0) { - check_colocation(has_blocked, is_closed, - turn.cluster_id, turns, clusters); + check_colocation(has_blocked, turn.cluster_id, turns, clusters); cluster_checked = true; } - // Block closed rings (for union), rings where anything is blocked, + // Block rings where any other turn is blocked, // and (with exceptions): i for union and u for intersection // Exceptions: don't block self-uu for intersection // don't block self-ii for union // don't block (for union) i/u if there is an self-ii too if (has_blocked - || turn.any_blocked() - || (is_closed && target_operation == operation_union) || (op.operation == opposite_operation && ! turn.has_colocated_both && ! (turn.both(opposite_operation) From c7c4c23f4fd810bdcd8d8d1f8a271f410edd7aa1 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 15:38:18 +0200 Subject: [PATCH 081/188] [test] include testcase for checking left_count --- test/algorithms/overlay/multi_overlay_cases.hpp | 7 +++++++ test/algorithms/set_operations/union/union_multi.cpp | 1 + 2 files changed, 8 insertions(+) diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index c33cf0bb5..76fccd2ae 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1137,6 +1137,13 @@ static std::string case_recursive_boxes_68[2] = "MULTIPOLYGON(((2 0,2 1,1 1,2 2,3 2,3 1,4 2,5 2,5 0,2 0)),((2 0,1 0,1 1,2 0)),((2 2,2 3,3 4,3 3,2 2)),((3 2,3 3,4 4,5 4,5 3,4 2,3 2)),((3 4,3 5,4 4,3 4)),((2 4,2 3,1 2,1 3,0 3,0 5,1 5,1 4,2 4)),((1 2,1 1,0 1,0 2,1 2)),((4 4,4 5,5 5,4 4)))" }; +static std::string case_recursive_boxes_69[2] = +{ + // Needs checking left_count instead of is_closed for decision to block untraversed rings + "MULTIPOLYGON(((3 4,3 5,4 5,3 4)),((3 4,3 2,2 2,3 1,1 1,1 2,0 2,1 3,0 3,1 4,3 4)),((3 1,4 1,4 0,3 0,3 1)),((1 1,2 0,0 0,0 1,1 1)))", + "MULTIPOLYGON(((2 4,1 4,1 3,0 3,0 5,2 5,3 4,2 4)),((3 4,3 5,4 5,4 4,3 4)),((2 1,2 0,1 0,2 1)),((2 1,1 1,1 2,2 2,2 1)),((5 1,5 0,3 0,4 1,5 1)),((4 1,3 1,3 2,4 3,5 2,4 2,4 1)),((1 3,2 3,1 2,1 3)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index dee1db12e..b522679d1 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -390,6 +390,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_66, 1, 1, -1, 24.75); TEST_UNION(case_recursive_boxes_67, 4, 0, -1, 14.75); TEST_UNION(case_recursive_boxes_68, 1, 4, -1, 22.5); + TEST_UNION(case_recursive_boxes_69, 4, 0, -1, 16.25); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 16892b1798639443b334729f4c1b746096fa0779 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 23 Aug 2017 15:48:21 +0200 Subject: [PATCH 082/188] [test] add last added testcases for union to unit test of intersection/difference --- test/algorithms/set_operations/difference/difference_multi.cpp | 2 ++ .../set_operations/intersection/intersection_multi.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 1abf21d95..55e6175db 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -376,6 +376,8 @@ void test_areal() TEST_DIFFERENCE(case_recursive_boxes_66, 5, 4.75, 7, 4.0, 9); TEST_DIFFERENCE(case_recursive_boxes_67, 7, 6.25, 9, 6.0, 10); + TEST_DIFFERENCE(case_recursive_boxes_68, 10, 6.5, 9, 6.5, 7); + TEST_DIFFERENCE(case_recursive_boxes_69, 5, 6.25, 5, 6.75, 8); { ut_settings sym_settings; diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index d42f35836..81788fb9c 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -323,6 +323,8 @@ void test_areal() #endif TEST_INTERSECTION(case_recursive_boxes_67, 5, -1, 2.5); + TEST_INTERSECTION(case_recursive_boxes_68, 8, -1, 9.5); + TEST_INTERSECTION(case_recursive_boxes_69, 6, -1, 3.25); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From a449c39e6fdcc08a0d19cf32981165ab4ce8b63e Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 30 Aug 2017 12:56:48 +0200 Subject: [PATCH 083/188] [overlay] fix missing ring due to discarded self-intersection turn --- .../algorithms/detail/overlay/handle_self_turns.hpp | 9 ++++++++- test/algorithms/overlay/multi_overlay_cases.hpp | 7 +++++++ .../set_operations/intersection/intersection_multi.cpp | 6 ++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp index 2debd48c6..fbdb9841d 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp @@ -102,7 +102,14 @@ struct discard_self_intersection_turns continue; } - // It is a non co-located ii self-turn + if (turn.cluster_id >= 0 && turn.has_colocated_both) + { + // Don't delete a self-ii-turn colocated with another ii-turn + // (for example #case_recursive_boxes_70) + continue; + } + + // It is a ii self-turn // Check if it is within the other geometry // If not, it can be ignored diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 76fccd2ae..1ee772cf6 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1144,6 +1144,13 @@ static std::string case_recursive_boxes_69[2] = "MULTIPOLYGON(((2 4,1 4,1 3,0 3,0 5,2 5,3 4,2 4)),((3 4,3 5,4 5,4 4,3 4)),((2 1,2 0,1 0,2 1)),((2 1,1 1,1 2,2 2,2 1)),((5 1,5 0,3 0,4 1,5 1)),((4 1,3 1,3 2,4 3,5 2,4 2,4 1)),((1 3,2 3,1 2,1 3)))" }; +static std::string case_recursive_boxes_70[2] = +{ + // Needs checking left_count instead of is_closed for decision to block untraversed rings + "MULTIPOLYGON(((2 0,0 0,0 4,1 3,3 3,3 5,5 5,5 3,4.5 2.5,5 2,5 0,2 0),(0 1,0.5 0.5,1 1,0 1),(5 2,4 2,4.5 1.5,5 2),(4 2,3 2,3 1,4 2)),((3 4,2 3,2 4,3 4)),((0 4,0 5,1 5,1.5 4.5,2 5,2 4,0 4)))", + "MULTIPOLYGON(((2 0,0 0,0 5,5 5,5 2,4.5 1.5,5 1,5 0,2 0),(2 1,3 0,3 1,2 1),(4 4,3 3,4 3,4 4),(1 1,1 2,0 2,1 1),(4 3,4.5 2.5,5 3,4 3)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 81788fb9c..9bb21d2a5 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -325,6 +325,12 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_67, 5, -1, 2.5); TEST_INTERSECTION(case_recursive_boxes_68, 8, -1, 9.5); TEST_INTERSECTION(case_recursive_boxes_69, 6, -1, 3.25); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_INTERSECTION(case_recursive_boxes_70, 6, -1, 18.5); +#else + // Misses a necessary self-turn and therefore a ring + TEST_INTERSECTION_IGNORE(case_recursive_boxes_70, 3, -1, 18.0); +#endif test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 64a7c46ab7ac0e96d66902f44bdd1fe159775910 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 30 Aug 2017 12:58:43 +0200 Subject: [PATCH 084/188] [overlay] finetune condition by still deleting colocated self-ii turns when there are also blocked turns in the cluster --- .../overlay/enrich_intersection_points.hpp | 4 +- .../detail/overlay/handle_self_turns.hpp | 49 ++++++++++++++++--- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index 9634d0820..709382edb 100644 --- a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -369,12 +369,12 @@ inline void enrich_intersection_points(Turns& turns, < OverlayType, target_operation - >::apply(turns, geometry1, geometry2); + >::apply(turns, clusters, geometry1, geometry2); detail::overlay::discard_open_turns < OverlayType, target_operation - >::apply(turns, geometry1, geometry2); + >::apply(turns, clusters, geometry1, geometry2); // Create a map of vectors of indexed operation-types to be able // to sort intersection points PER RING diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp index fbdb9841d..aa4b76067 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp @@ -11,6 +11,7 @@ #include +#include #include #include #include @@ -24,9 +25,9 @@ namespace detail { namespace overlay struct discard_turns { - template + template static inline - void apply(Turns& , Geometry0 const& , Geometry1 const& ) + void apply(Turns& , Clusters const& , Geometry0 const& , Geometry1 const& ) {} }; @@ -38,9 +39,9 @@ template <> struct discard_closed_turns { - template + template static inline - void apply(Turns& turns, + void apply(Turns& turns, Clusters const& clusters, Geometry0 const& geometry0, Geometry1 const& geometry1) { typedef typename boost::range_value::type turn_type; @@ -73,9 +74,38 @@ struct discard_closed_turns struct discard_self_intersection_turns { - template +private : + + template static inline - void apply(Turns& turns, + bool any_blocked(signed_size_type cluster_id, + const Turns& turns, Clusters const& clusters) + { + typename Clusters::const_iterator cit = clusters.find(cluster_id); + if (cit == clusters.end()) + { + return false; + } + cluster_info const& cinfo = cit->second; + for (std::set::const_iterator it + = cinfo.turn_indices.begin(); + it != cinfo.turn_indices.end(); ++it) + { + typename boost::range_value::type const& turn = turns[*it]; + if (turn.any_blocked()) + { + return true; + } + } + return false; + } + +public : + + template + static inline + void apply(Turns& turns, Clusters const& clusters, Geometry0 const& geometry0, Geometry1 const& geometry1) { typedef typename boost::range_value::type turn_type; @@ -106,7 +136,12 @@ struct discard_self_intersection_turns { // Don't delete a self-ii-turn colocated with another ii-turn // (for example #case_recursive_boxes_70) - continue; + // But for some cases (#case_58_iet) they should be deleted, + // there are many self-turns there and also blocked turns there + if (! any_blocked(turn.cluster_id, turns, clusters)) + { + continue; + } } // It is a ii self-turn From 670ee5074f82183029a9f501a1c8eb0b033076d0 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 30 Aug 2017 13:19:38 +0200 Subject: [PATCH 085/188] [test] add last testcase also to union/difference --- .../set_operations/difference/difference_multi.cpp | 5 +++++ test/algorithms/set_operations/union/union_multi.cpp | 1 + 2 files changed, 6 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 55e6175db..10ca31ba0 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -378,6 +378,11 @@ void test_areal() TEST_DIFFERENCE(case_recursive_boxes_67, 7, 6.25, 9, 6.0, 10); TEST_DIFFERENCE(case_recursive_boxes_68, 10, 6.5, 9, 6.5, 7); TEST_DIFFERENCE(case_recursive_boxes_69, 5, 6.25, 5, 6.75, 8); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_70, 5, 2.0, 8, 4.5, 11); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_70, 5, 2.0, 6, 4.5, 9); +#endif { ut_settings sym_settings; diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index b522679d1..dcd0d4011 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -391,6 +391,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_67, 4, 0, -1, 14.75); TEST_UNION(case_recursive_boxes_68, 1, 4, -1, 22.5); TEST_UNION(case_recursive_boxes_69, 4, 0, -1, 16.25); + TEST_UNION(case_recursive_boxes_70, 1, 0, -1, 25.0); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From dbe6df7ad3531f944a4e1904d4eee3e7a6ebdaa8 Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Wed, 30 Aug 2017 15:04:01 +0300 Subject: [PATCH 086/188] [formulas] [strategies] [distance] Change interface and earth radius in distance point segment geographic formula --- .../formulas/distance_point_segment.hpp | 52 +++++++------ .../geographic/distance_cross_track.hpp | 74 ++++++------------- .../strategies/geographic/intersection.hpp | 6 +- .../algorithms/distance/distance_geo_pl_l.cpp | 6 +- 4 files changed, 59 insertions(+), 79 deletions(-) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index 83f443ab2..a2f32cd2a 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -25,6 +25,8 @@ https://arxiv.org/abs/1102.1215 */ +#define BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + namespace boost { namespace geometry { namespace formula { @@ -32,15 +34,18 @@ template < typename CT, typename Units, - typename Inverse_type_azimuth, - typename Inverse_type_distance, - typename Direct_type, + template class Inverse, + template class Direct, bool EnableClosestPoint = false > class distance_point_segment{ public: + typedef Inverse inverse_distance_type; + typedef Inverse inverse_azimuth_type; + typedef Direct direct_distance_type; + struct result_distance_point_segment { result_distance_point_segment() @@ -61,7 +66,7 @@ public: Spheroid const& spheroid) { result_distance_point_segment result; - result.distance = Inverse_type_distance::apply(lon1, lat1, + result.distance = inverse_distance_type::apply(lon1, lat1, lon2, lat2, spheroid).distance; if (EnableClosestPoint) @@ -77,10 +82,11 @@ public: static inline apply(CT lon1, CT lat1, //p1 CT lon2, CT lat2, //p2 CT lon3, CT lat3, //query point p3 - Spheroid const& spheroid, - CT earth_radius = - geometry::srs::sphere().get_radius<1>()) + Spheroid const& spheroid) { + CT earth_radius = (CT(2) * spheroid.get_radius<1>() + + spheroid.get_radius<2>()) / CT(3); + result_distance_point_segment result; // Constants @@ -145,8 +151,8 @@ public: } // Compute a1 (GEO) - CT a1 = Inverse_type_azimuth::apply(lon1, lat1, lon2, lat2, spheroid).azimuth; - CT a13 = Inverse_type_azimuth::apply(lon1, lat1, lon3, lat3, spheroid).azimuth; + CT a1 = inverse_azimuth_type::apply(lon1, lat1, lon2, lat2, spheroid).azimuth; + CT a13 = inverse_azimuth_type::apply(lon1, lat1, lon3, lat3, spheroid).azimuth; CT a312 = a1 - a13; @@ -157,8 +163,8 @@ public: return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); } - CT a2 = pi + Inverse_type_azimuth::apply(lon2, lat2, lon1, lat1, spheroid).azimuth; - CT a23 = Inverse_type_azimuth::apply(lon2, lat2, lon3, lat3, spheroid).azimuth; + CT a2 = pi + inverse_azimuth_type::apply(lon2, lat2, lon1, lat1, spheroid).azimuth; + CT a23 = inverse_azimuth_type::apply(lon2, lat2, lon3, lat3, spheroid).azimuth; CT a321 = a2 - a23; @@ -220,13 +226,15 @@ public: prev_distance = res34.distance; // Solve the direct problem to find p4 (GEO) - res14 = Direct_type::apply(lon1, lat1, s14, a1, spheroid); + res14 = direct_distance_type::apply(lon1, lat1, s14, a1, spheroid); // Solve an inverse problem to find g4 // g4 is the angle between segment (p1,p2) and segment (p3,p4) that meet on p4 (GEO) - CT a4 = Inverse_type_azimuth::apply(res14.lon2, res14.lat2, lon2, lat2, spheroid).azimuth; - res34 = Inverse_type_distance::apply(res14.lon2, res14.lat2, lon3, lat3, spheroid); + CT a4 = inverse_azimuth_type::apply(res14.lon2, res14.lat2, + lon2, lat2, spheroid).azimuth; + res34 = inverse_distance_type::apply(res14.lon2, res14.lat2, + lon3, lat3, spheroid); g4 = res34.azimuth - a4; // Normalize g4 @@ -296,20 +304,20 @@ public: std::cout << "s34(sph) =" << s34_sph << std::endl; std::cout << "s34(geo) =" - << Inverse_type_distance::apply(get<0>(p4), get<1>(p4), lon3, lat3, spheroid).distance + << inverse_distance_type::apply(get<0>(p4), get<1>(p4), lon3, lat3, spheroid).distance << ", p4=(" << get<0>(p4) * math::r2d() << "," << get<1>(p4) * math::r2d() << ")" << std::endl; - CT s31 = Inverse_type_distance::apply(lon3, lat3, lon1, lat1, spheroid).distance; - CT s32 = Inverse_type_distance::apply(lon3, lat3, lon2, lat2, spheroid).distance; + CT s31 = inverse_distance_type::apply(lon3, lat3, lon1, lat1, spheroid).distance; + CT s32 = inverse_distance_type::apply(lon3, lat3, lon2, lat2, spheroid).distance; - CT a4 = Inverse_type_azimuth::apply(get<0>(p4), get<1>(p4), lon2, lat2, spheroid).azimuth; - geometry::formula::result_direct res4 = Direct_type::apply(get<0>(p4), get<1>(p4), .04, a4, spheroid); - CT p4_plus = Inverse_type_distance::apply(res4.lon2, res4.lat2, lon3, lat3, spheroid).distance; + CT a4 = inverse_azimuth_type::apply(get<0>(p4), get<1>(p4), lon2, lat2, spheroid).azimuth; + geometry::formula::result_direct res4 = direct_distance_type::apply(get<0>(p4), get<1>(p4), .04, a4, spheroid); + CT p4_plus = inverse_distance_type::apply(res4.lon2, res4.lat2, lon3, lat3, spheroid).distance; - geometry::formula::result_direct res1 = Direct_type::apply(lon1, lat1, s14-.04, a1, spheroid); - CT p4_minus = Inverse_type_distance::apply(res1.lon2, res1.lat2, lon3, lat3, spheroid).distance; + geometry::formula::result_direct res1 = direct_distance_type::apply(lon1, lat1, s14-.04, a1, spheroid); + CT p4_minus = inverse_distance_type::apply(res1.lon2, res1.lat2, lon3, lat3, spheroid).distance; std::cout << "s31=" << s31 << "\ns32=" << s32 << "\np4_plus=" << p4_plus << ", p4=(" << res4.lon2 * math::r2d() << "," << res4.lat2 * math::r2d() << ")" diff --git a/include/boost/geometry/strategies/geographic/distance_cross_track.hpp b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp index 60ebd54fd..295858f6e 100644 --- a/include/boost/geometry/strategies/geographic/distance_cross_track.hpp +++ b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp @@ -65,7 +65,7 @@ template typename Spheroid = srs::spheroid, typename CalculationType = void > -class cross_track_geo +class geographic_cross_track { public : template @@ -92,7 +92,7 @@ public : return distance_type(m_spheroid); } - explicit cross_track_geo(Spheroid const& spheroid = Spheroid()) + explicit geographic_cross_track(Spheroid const& spheroid = Spheroid()) : m_spheroid(spheroid) {} @@ -104,42 +104,12 @@ public : typedef typename return_type::type CT; - typedef typename FormulaPolicy::template inverse - < - CT, - false, - true, - false, - false, - false - > inverse_type_azimuth; - - typedef typename FormulaPolicy::template inverse - < - CT, - true, - true, - false, - true, - true - > inverse_type_distance; - - typedef typename FormulaPolicy::template direct - < - CT, - true, - false, - false, - false - > direct_type; - return (geometry::formula::distance_point_segment < CT, units_type, - inverse_type_azimuth, - inverse_type_distance, - direct_type + FormulaPolicy::template inverse, + FormulaPolicy::template direct >::apply(get<0>(sp1), get<1>(sp1), get<0>(sp2), get<1>(sp2), get<0>(p), get<1>(p), @@ -159,7 +129,7 @@ namespace services //tags template -struct tag > +struct tag > { typedef strategy_tag_distance_point_segment type; }; @@ -169,7 +139,7 @@ template typename FormulaPolicy, typename Spheroid > -struct tag > +struct tag > { typedef strategy_tag_distance_point_segment type; }; @@ -180,7 +150,7 @@ template typename Spheroid, typename CalculationType > -struct tag > +struct tag > { typedef strategy_tag_distance_point_segment type; }; @@ -188,8 +158,8 @@ struct tag > //return types template -struct return_type, P, PS> - : cross_track_geo::template return_type +struct return_type, P, PS> + : geographic_cross_track::template return_type {}; template @@ -199,8 +169,8 @@ template typename P, typename PS > -struct return_type, P, PS> - : cross_track_geo::template return_type +struct return_type, P, PS> + : geographic_cross_track::template return_type {}; template @@ -211,8 +181,8 @@ template typename P, typename PS > -struct return_type, P, PS> - : cross_track_geo::template return_type +struct return_type, P, PS> + : geographic_cross_track::template return_type {}; //comparable types @@ -222,9 +192,9 @@ template typename Spheroid, typename CalculationType > -struct comparable_type > +struct comparable_type > { - typedef cross_track_geo + typedef geographic_cross_track < FormulaPolicy, Spheroid, CalculationType > type; @@ -236,15 +206,15 @@ template typename Spheroid, typename CalculationType > -struct get_comparable > +struct get_comparable > { typedef typename comparable_type < - cross_track_geo + geographic_cross_track >::type comparable_type; public : static inline comparable_type - apply(cross_track_geo const& strategy) + apply(geographic_cross_track const& strategy) { return comparable_type(); } @@ -257,17 +227,17 @@ template typename P, typename PS > -struct result_from_distance, P, PS> +struct result_from_distance, P, PS> { private : - typedef typename cross_track_geo + typedef typename geographic_cross_track < FormulaPolicy >::template return_type::type return_type; public : template static inline return_type - apply(cross_track_geo const& , T const& distance) + apply(geographic_cross_track const& , T const& distance) { return distance; } @@ -281,7 +251,7 @@ struct default_strategy geographic_tag, geographic_tag > { - typedef cross_track_geo<> type; + typedef geographic_cross_track<> type; }; diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp index f4c2c1436..84acd149c 100644 --- a/include/boost/geometry/strategies/geographic/intersection.hpp +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -118,6 +118,7 @@ struct geographic_segments return strategy_type(m_spheroid); } + template struct distance_strategy { typedef distance::geographic @@ -128,9 +129,10 @@ struct geographic_segments > type; }; - inline typename distance_strategy::type get_distance_strategy() const + template + inline typename distance_strategy::type get_distance_strategy() const { - typedef typename distance_strategy::type strategy_type; + typedef typename distance_strategy::type strategy_type; return strategy_type(m_spheroid); } diff --git a/test/algorithms/distance/distance_geo_pl_l.cpp b/test/algorithms/distance/distance_geo_pl_l.cpp index 58cdb3b8b..0e611f065 100644 --- a/test/algorithms/distance/distance_geo_pl_l.cpp +++ b/test/algorithms/distance/distance_geo_pl_l.cpp @@ -40,13 +40,13 @@ typedef bg::strategy::distance::vincenty vincenty_pp; // Strategies for point-segment distance -typedef bg::strategy::distance::cross_track_geo +typedef bg::strategy::distance::geographic_cross_track andoyer_strategy; -typedef bg::strategy::distance::cross_track_geo +typedef bg::strategy::distance::geographic_cross_track thomas_strategy; -typedef bg::strategy::distance::cross_track_geo +typedef bg::strategy::distance::geographic_cross_track vincenty_strategy; //=========================================================================== From 7de13d2707ff390ee12af604bc8a0f2c5eb0a1e1 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 30 Aug 2017 18:04:23 +0200 Subject: [PATCH 087/188] [test][difference] Add failing L/A test cases. --- .../difference/difference_areal_linear.cpp | 12 ++++++++++++ .../set_operations/difference/test_difference.hpp | 1 + 2 files changed, 13 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_areal_linear.cpp b/test/algorithms/set_operations/difference/difference_areal_linear.cpp index c6001f11b..f875b7fbc 100644 --- a/test/algorithms/set_operations/difference/difference_areal_linear.cpp +++ b/test/algorithms/set_operations/difference/difference_areal_linear.cpp @@ -202,6 +202,18 @@ void test_areal_linear() "LINESTRING(-1 -1,10 10)", "POLYGON((5 5,15 15,15 5,5 5))", 1, 2, 6 * std::sqrt(2.0)); + +#ifdef BOOST_GEOMETRY_ENABLE_FAILING_TESTS + test_one_lp("case37_1", + "LINESTRING(1 1,2 2)", + "POLYGON((0 0,0 3,3 3,3 0,0 0),(1 1,1 2,2 2,2 1,1 1))", + 1, 2, std::sqrt(2.0)); + + test_one_lp("case37_2", + "LINESTRING(1 1,2 2,3 3)", + "POLYGON((0 0,0 3,3 3,3 0,0 0),(1 1,1 2,2 2,2 1,1 1))", + 1, 2, std::sqrt(2.0)); +#endif } template diff --git a/test/algorithms/set_operations/difference/test_difference.hpp b/test/algorithms/set_operations/difference/test_difference.hpp index 7321c482b..cc7179db1 100644 --- a/test/algorithms/set_operations/difference/test_difference.hpp +++ b/test/algorithms/set_operations/difference/test_difference.hpp @@ -426,6 +426,7 @@ void test_one_lp(std::string const& caseid, bg::read_wkt(wkt2, g2); bg::correct(g1); + bg::correct(g2); typedef typename setop_output_type::type result_type; result_type pieces; From 2f0705147cec739de6092087c01fd424807e82e5 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Wed, 30 Aug 2017 18:20:17 +0200 Subject: [PATCH 088/188] [test][union] Add failing A/A case (1.65 regression). --- test/algorithms/set_operations/union/union_multi.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index a3b1c2ac1..fc1d05225 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -438,6 +438,13 @@ void test_areal() test_one("mysql_23023665_9", mysql_23023665_9[0], mysql_23023665_9[1], 1, 9, -1, 1250.0); + +#ifdef BOOST_GEOMETRY_ENABLE_FAILING_TESTS + test_one("mysql_regression_1_65", + "MULTIPOLYGON(((23.695652173913043 4.3478260869565215,23.333333333333336 4.166666666666667,25 0,23.695652173913043 4.3478260869565215)),((10 15,0 15,8.870967741935484 9.67741935483871,10.777777750841748 14.44444437710437,10 15)))", + "MULTIPOLYGON(((10 15,20 15,15 25,10 15)),((10 15,0 15,7 10,5 0,15 5,15.90909090909091 4.545454545454546,17 10,10 15)),((23.695652173913043 4.3478260869565215,20 2.5,25 0,23.695652173913043 4.3478260869565215)))", + 3, 0, -1, 181.966397646608); +#endif } // Test cases (generic) From ccd85b37d07dde6fb7f64ba5a9415abe7074727e Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 30 Aug 2017 18:45:39 +0200 Subject: [PATCH 089/188] [buffer] add parameter added in the meantime --- .../algorithms/detail/buffer/buffered_piece_collection.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index c0d906fe6..122b82cbe 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -1369,12 +1370,14 @@ struct buffered_piece_collection overlay_buffer, backtrack_for_buffer > traverser; + std::map turn_info_per_ring; traversed_rings.clear(); buffer_overlay_visitor visitor; traverser::apply(offsetted_rings, offsetted_rings, m_intersection_strategy, m_robust_policy, m_turns, traversed_rings, + turn_info_per_ring, m_clusters, visitor); } From cd3f264a275fc72e8f7758573336ef384d4bfd89 Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Thu, 31 Aug 2017 11:02:55 +0300 Subject: [PATCH 090/188] [formulas] [distance] Activate differential quantities computation in distance point segment --- include/boost/geometry/formulas/distance_point_segment.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index a2f32cd2a..afa4eebd7 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -25,8 +25,6 @@ https://arxiv.org/abs/1102.1215 */ -#define BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG - namespace boost { namespace geometry { namespace formula { @@ -42,7 +40,7 @@ class distance_point_segment{ public: - typedef Inverse inverse_distance_type; + typedef Inverse inverse_distance_type; typedef Inverse inverse_azimuth_type; typedef Direct direct_distance_type; @@ -266,6 +264,9 @@ public: std::cout << "delta_g4=" << delta_g4 << std::endl; std::cout << "g4=" << g4 * math::r2d() << std::endl; std::cout << "der=" << der << std::endl; + std::cout << "M43=" << M43 << std::endl; + std::cout << "spherical limit=" << cos(s14/earth_radius) << std::endl; + std::cout << "m34=" << m34 << std::endl; std::cout << "new_s14=" << s14 << std::endl; std::cout << std::setprecision(16) << "dist =" << res34.distance << std::endl; std::cout << "---------end of step " << counter << std::endl<< std::endl; From 3232fd067532b289790cb46ebccbf1dd5a798b1c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 6 Sep 2017 10:21:39 +0200 Subject: [PATCH 091/188] [test] enable testcase for all operations --- test/algorithms/overlay/multi_overlay_cases.hpp | 7 +++++++ .../set_operations/difference/difference_multi.cpp | 7 +++++++ .../set_operations/intersection/intersection_multi.cpp | 2 ++ test/algorithms/set_operations/union/union_multi.cpp | 7 +------ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 1ee772cf6..cf082a473 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1331,4 +1331,11 @@ static std::string mysql_23023665_12[2] = "MULTIPOLYGON(((2 3,-3 5,-10 -1,2 3)))" }; +static std::string mysql_regression_1_65_2017_08_31[2] = + { + "MULTIPOLYGON(((23.695652173913043 4.3478260869565215,23.333333333333336 4.166666666666667,25 0,23.695652173913043 4.3478260869565215)),((10 15,0 15,8.870967741935484 9.67741935483871,10.777777750841748 14.44444437710437,10 15)))", + "MULTIPOLYGON(((10 15,20 15,15 25,10 15)),((10 15,0 15,7 10,5 0,15 5,15.90909090909091 4.545454545454546,17 10,10 15)),((23.695652173913043 4.3478260869565215,20 2.5,25 0,23.695652173913043 4.3478260869565215)))", + }; + + #endif // BOOST_GEOMETRY_TEST_MULTI_OVERLAY_CASES_HPP diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 10ca31ba0..31b07584b 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -396,6 +396,13 @@ void test_areal() 2, -1, 131.21376870369406, sym_settings); } + +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(mysql_regression_1_65_2017_08_31, 1, 4.30697514e-7, 3, 152.0642, 4); +#else + // Misses one turn which is actually weird because there are no self-turns involved + TEST_DIFFERENCE(mysql_regression_1_65_2017_08_31, 0, 0, 3, 152.0642, 3); +#endif } diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 9bb21d2a5..d49e97c1b 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -364,6 +364,8 @@ void test_areal() #else TEST_INTERSECTION_IGNORE(mysql_23023665_12, 1, -1, 11.812440191387557); #endif + + TEST_INTERSECTION(mysql_regression_1_65_2017_08_31, 2, -1, 29.9022122); } template diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 0e9930c71..5d36dcd61 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -449,12 +449,7 @@ void test_areal() mysql_23023665_9[0], mysql_23023665_9[1], 1, 9, -1, 1250.0); -#ifdef BOOST_GEOMETRY_ENABLE_FAILING_TESTS - test_one("mysql_regression_1_65", - "MULTIPOLYGON(((23.695652173913043 4.3478260869565215,23.333333333333336 4.166666666666667,25 0,23.695652173913043 4.3478260869565215)),((10 15,0 15,8.870967741935484 9.67741935483871,10.777777750841748 14.44444437710437,10 15)))", - "MULTIPOLYGON(((10 15,20 15,15 25,10 15)),((10 15,0 15,7 10,5 0,15 5,15.90909090909091 4.545454545454546,17 10,10 15)),((23.695652173913043 4.3478260869565215,20 2.5,25 0,23.695652173913043 4.3478260869565215)))", - 3, 0, -1, 181.966397646608); -#endif + TEST_UNION(mysql_regression_1_65_2017_08_31, 3, 0, -1, 181.966397646608); } // Test cases (generic) From 41ae79cf155b8324667c0590eeb8aa481ca8f4b7 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 6 Sep 2017 10:58:46 +0200 Subject: [PATCH 092/188] Fix for C++03 --- include/boost/geometry/algorithms/detail/expand/indexed.hpp | 4 ++-- include/boost/geometry/algorithms/detail/expand/point.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/expand/indexed.hpp b/include/boost/geometry/algorithms/detail/expand/indexed.hpp index e268c03de..fe7ee4f78 100644 --- a/include/boost/geometry/algorithms/detail/expand/indexed.hpp +++ b/include/boost/geometry/algorithms/detail/expand/indexed.hpp @@ -59,8 +59,8 @@ struct indexed_loop coordinate_type const coord = get(source); - std::less const less; - std::greater const greater; + std::less less; + std::greater greater; if (less(coord, get(box))) { diff --git a/include/boost/geometry/algorithms/detail/expand/point.hpp b/include/boost/geometry/algorithms/detail/expand/point.hpp index 58e104675..2d8b0feff 100644 --- a/include/boost/geometry/algorithms/detail/expand/point.hpp +++ b/include/boost/geometry/algorithms/detail/expand/point.hpp @@ -63,8 +63,8 @@ struct point_loop Point, Box >::type coordinate_type; - std::less const less; - std::greater const greater; + std::less less; + std::greater greater; coordinate_type const coord = get(source); From 25ee55003b52909c8826305629380b2c787bf793 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 6 Sep 2017 11:36:22 +0200 Subject: [PATCH 093/188] Fix int types of region_id to signed_size_type --- .../algorithms/detail/overlay/aggregate_operations.hpp | 6 +++--- .../geometry/algorithms/detail/overlay/traversal.hpp | 2 +- .../detail/overlay/traversal_intersection_patterns.hpp | 8 ++++---- .../detail/overlay/traversal_switch_detector.hpp | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp index 3c88e9be6..89442848e 100644 --- a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp @@ -132,7 +132,7 @@ struct rank_with_rings inline bool has_unique_region_id() const { - int region_id = -1; + signed_size_type region_id = -1; for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { @@ -149,9 +149,9 @@ struct rank_with_rings return true; } - inline int region_id() const + inline signed_size_type region_id() const { - int region_id = -1; + signed_size_type region_id = -1; for (container_type::const_iterator it = rings.begin(); it != rings.end(); ++it) { diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index d6eeda044..a114d1ee9 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -514,7 +514,7 @@ struct traversal { for (int i = 0; i < 2; i++) { - int const region_id = turn.operations[i].enriched.region_id; + signed_size_type const region_id = turn.operations[i].enriched.region_id; if (outgoing_region_ids.count(region_id) == 1) { selected_rank = 0; diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp index 2e7b36f6f..fc9df0f63 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp @@ -32,8 +32,8 @@ inline bool check_pairs(std::vector const& aggreg { sort_by_side::rank_with_rings const& curr = aggregation[i]; sort_by_side::rank_with_rings const& next = aggregation[i + 1]; - int const curr_id = curr.region_id(); - int const next_id = next.region_id(); + signed_size_type const curr_id = curr.region_id(); + signed_size_type const next_id = next.region_id(); bool const possible = curr.rings.size() == 2 @@ -335,7 +335,7 @@ inline bool intersection_pattern_common_interior5(std::size_t& selected_rank, return false; } - const int incoming_region_id = incoming.region_id(); + signed_size_type const incoming_region_id = incoming.region_id(); bool const outgoing_ok = outgoing.all_to() @@ -359,7 +359,7 @@ inline bool intersection_pattern_common_interior5(std::size_t& selected_rank, { return false; } - const int region_id = rwr.region_id(); + signed_size_type const region_id = rwr.region_id(); if (other_region && region_id != incoming_region_id) { // OK diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index cfca1fb49..bc09f06da 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -352,7 +352,7 @@ struct traversal_switch_detector void create_region(signed_size_type& new_region_id, ring_identifier const& ring_id, - merged_ring_properties& properties, int region_id = -1) + merged_ring_properties& properties, signed_size_type region_id = -1) { if (properties.region_id > 0) { @@ -400,7 +400,7 @@ struct traversal_switch_detector } void propagate_region(signed_size_type& new_region_id, - ring_identifier const& ring_id, int region_id) + ring_identifier const& ring_id, signed_size_type region_id) { typename merge_map::iterator it = m_turns_per_ring.find(ring_id); if (it != m_turns_per_ring.end()) From 5125f938d828db25f370f1f00338f6f61d6336c5 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 6 Sep 2017 11:42:31 +0200 Subject: [PATCH 094/188] Fix std::size_t types of turn_index to signed_size_type --- .../geometry/algorithms/detail/overlay/aggregate_operations.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp index 89442848e..3f2aea1b1 100644 --- a/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/aggregate_operations.hpp @@ -25,7 +25,7 @@ struct ring_with_direction ring_identifier ring_id; direction_type direction; - std::size_t turn_index; + signed_size_type turn_index; int operation_index; operation_type operation; signed_size_type region_id; From df96ce3bcd230f12e955ddf0b8feed66f03fa7bb Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 6 Sep 2017 11:43:14 +0200 Subject: [PATCH 095/188] fix more region_id occurences to signed_size_type --- .../geometry/algorithms/detail/overlay/traversal.hpp | 4 ++-- .../detail/overlay/traversal_switch_detector.hpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index a114d1ee9..73b2b53c3 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -463,8 +463,8 @@ struct traversal if (! detected) { - int incoming_region_id = 0; - std::set outgoing_region_ids; + signed_size_type incoming_region_id = 0; + std::set outgoing_region_ids; for (std::size_t i = 0; i < aggregation.size(); i++) { diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index bc09f06da..9d11d1397 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -345,7 +345,7 @@ struct traversal_switch_detector } - inline int get_region_id(turn_operation_type const& op) const + inline signed_size_type get_region_id(turn_operation_type const& op) const { return op.enriched.region_id; } @@ -464,7 +464,7 @@ struct traversal_switch_detector } // A touching cluster, gather regions - std::set regions; + std::set regions; std::set const& ids = cinfo.turn_indices; @@ -478,8 +478,8 @@ struct traversal_switch_detector turn_type const& turn = m_turns[turn_index]; for (int oi = 0; oi < 2; oi++) { - int const region = get_region_id(turn.operations[oi]); - regions.insert(region); + signed_size_type const region_id = get_region_id(turn.operations[oi]); + regions.insert(region_id); } } // Switch source if this cluster connects the same region @@ -509,8 +509,8 @@ struct traversal_switch_detector continue; } - int const region0 = get_region_id(turn.operations[0]); - int const region1 = get_region_id(turn.operations[1]); + signed_size_type const region0 = get_region_id(turn.operations[0]); + signed_size_type const region1 = get_region_id(turn.operations[1]); // Switch sources for same region turn.switch_source = region0 == region1; From 564b2611979a6de88544cd4b8cad8949871fdb1c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 6 Sep 2017 11:43:36 +0200 Subject: [PATCH 096/188] fix int cluster_id to signed_size_type --- .../algorithms/detail/overlay/traversal_switch_detector.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index 9d11d1397..ebe0158f3 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -120,7 +120,7 @@ struct traversal_switch_detector } bool all_colocated = true; - int unique_cluster_id = -1; + signed_size_type unique_cluster_id = -1; for (typename connection_map::const_iterator it = properties.connected_region_counts.begin(); all_colocated && it != properties.connected_region_counts.end(); ++it) { @@ -130,7 +130,7 @@ struct traversal_switch_detector // Either no cluster (non colocated point), or more clusters all_colocated = false; } - int const cluster_id = *cprop.cluster_indices.begin(); + signed_size_type const cluster_id = *cprop.cluster_indices.begin(); if (cluster_id == -1) { all_colocated = false; From 6e4c61d2027267bd8c35a7794cc20f31260a69c0 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 6 Sep 2017 14:40:44 +0200 Subject: [PATCH 097/188] [overlay] add check for self-cluster, which should be located within the other geometry to avoid creating false rings --- .../detail/overlay/handle_self_turns.hpp | 74 +++++++++++++++++-- .../overlay/multi_overlay_cases.hpp | 8 +- .../intersection/intersection_multi.cpp | 2 + 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp index aa4b76067..d8c48bd8c 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp @@ -100,6 +100,70 @@ private : return false; } + template + static inline + bool is_self_cluster(signed_size_type cluster_id, + const Turns& turns, Clusters const& clusters) + { + typename Clusters::const_iterator cit = clusters.find(cluster_id); + if (cit == clusters.end()) + { + return false; + } + + cluster_info const& cinfo = cit->second; + for (std::set::const_iterator it + = cinfo.turn_indices.begin(); + it != cinfo.turn_indices.end(); ++it) + { + if (! is_self_turn(turns[*it])) + { + return false; + } + } + + return true; + } + + template + static inline + bool within(Turn const& turn, Geometry0 const& geometry0, + Geometry1 const& geometry1) + { + return turn.operations[0].seg_id.source_index == 0 + ? geometry::within(turn.point, geometry1) + : geometry::within(turn.point, geometry0); + } + + template + static inline + void discard_clusters(Turns& turns, Clusters const& clusters, + Geometry0 const& geometry0, Geometry1 const& geometry1) + { + for (typename Clusters::const_iterator cit = clusters.begin(); + cit != clusters.end(); ++cit) + { + signed_size_type cluster_id = cit->first; + + // If there are only self-turns in the cluster, the cluster should + // be located within the other geometry, for intersection + if (is_self_cluster(cluster_id, turns, clusters)) + { + cluster_info const& cinfo = cit->second; + if (! within(turns[*cinfo.turn_indices.begin()], geometry0, geometry1)) + { + // Discard all turns in cluster + for (std::set::const_iterator sit = cinfo.turn_indices.begin(); + sit != cinfo.turn_indices.end(); ++sit) + { + turns[*sit].discarded = true; + } + } + } + } + } + public : template ::type turn_type; for (typename boost::range_iterator::type @@ -147,13 +213,7 @@ public : // It is a ii self-turn // Check if it is within the other geometry // If not, it can be ignored - - bool const within = - turn.operations[0].seg_id.source_index == 0 - ? geometry::within(turn.point, geometry1) - : geometry::within(turn.point, geometry0); - - if (! within) + if (! within(turn, geometry0, geometry1)) { // It is not within another geometry, discard the turn turn.discarded = true; diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index cf082a473..ac488bb8f 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1151,6 +1151,13 @@ static std::string case_recursive_boxes_70[2] = "MULTIPOLYGON(((2 0,0 0,0 5,5 5,5 2,4.5 1.5,5 1,5 0,2 0),(2 1,3 0,3 1,2 1),(4 4,3 3,4 3,4 4),(1 1,1 2,0 2,1 1),(4 3,4.5 2.5,5 3,4 3)))" }; +static std::string case_recursive_boxes_71[2] = +{ + // Needs check for self-cluster within other geometry, in intersections + "MULTIPOLYGON(((4 0,4 1,5 1,5 0,4 0)),((4 3,4 4,5 4,4 3)),((3 3,4 2,3 2,3 3)),((3 3,2 3,3 4,3 3)),((3 2,3 0,1 0,1 3,0 3,0 4,1 4,3 2),(3 2,2 2,2 1,3 2)),((1 4,1 5,2 5,2 4,1 4)))", + "MULTIPOLYGON(((3 0,3 1,4 0,3 0)),((2 2,0 2,0 3,1 3,2 2)),((2 2,2 3,3 3,2 2)),((2 4,0 4,1 5,3 5,3 4,2 4)),((3 3,3 4,4 5,5 4,4 4,4 3,3 3)),((4 3,5 3,4 2,4 3)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", @@ -1337,5 +1344,4 @@ static std::string mysql_regression_1_65_2017_08_31[2] = "MULTIPOLYGON(((10 15,20 15,15 25,10 15)),((10 15,0 15,7 10,5 0,15 5,15.90909090909091 4.545454545454546,17 10,10 15)),((23.695652173913043 4.3478260869565215,20 2.5,25 0,23.695652173913043 4.3478260869565215)))", }; - #endif // BOOST_GEOMETRY_TEST_MULTI_OVERLAY_CASES_HPP diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index d49e97c1b..8dcf4827c 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -332,6 +332,8 @@ void test_areal() TEST_INTERSECTION_IGNORE(case_recursive_boxes_70, 3, -1, 18.0); #endif + TEST_INTERSECTION(case_recursive_boxes_71, 3, -1, 1.75); + test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], 2, 10, 6.0); // Area from SQL Server From ce3118c4c906e56022fd0fe3edda2df60f023777 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 6 Sep 2017 14:52:07 +0200 Subject: [PATCH 098/188] [test] add testcase from last commit also to union/difference --- .../set_operations/difference/difference_multi.cpp | 6 ++++++ test/algorithms/set_operations/union/union_multi.cpp | 1 + 2 files changed, 7 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 31b07584b..4fae334d4 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -384,6 +384,12 @@ void test_areal() TEST_DIFFERENCE_IGNORE(case_recursive_boxes_70, 5, 2.0, 6, 4.5, 9); #endif +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_71, 7, 8.25, 7, 5.75, 8); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_71, 6, 8.25, 7, 5.75, 7); +#endif + { ut_settings sym_settings; #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 5d36dcd61..ad03d7ef7 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -392,6 +392,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_68, 1, 4, -1, 22.5); TEST_UNION(case_recursive_boxes_69, 4, 0, -1, 16.25); TEST_UNION(case_recursive_boxes_70, 1, 0, -1, 25.0); + TEST_UNION(case_recursive_boxes_71, 4, 2, -1, 15.75); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 653dc28bb9db012f20391a89ece18f6d6c99ed98 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 7 Sep 2017 22:48:10 +0200 Subject: [PATCH 099/188] [overlay] Fix follow (setops L/A) by using linear turns (from relate). --- .../algorithms/detail/overlay/follow.hpp | 12 +- .../detail/overlay/intersection_insert.hpp | 153 +++++++++++++++++- 2 files changed, 159 insertions(+), 6 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/follow.hpp b/include/boost/geometry/algorithms/detail/overlay/follow.hpp index 589e12cc2..4a5993ea3 100644 --- a/include/boost/geometry/algorithms/detail/overlay/follow.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/follow.hpp @@ -26,6 +26,7 @@ #include #include +#include namespace boost { namespace geometry @@ -343,6 +344,7 @@ template class follow { +#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR template struct sort_on_segment { @@ -389,7 +391,7 @@ class follow } }; - +#endif // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR public : @@ -433,7 +435,15 @@ public : // Sort intersection points on segments-along-linestring, and distance // (like in enrich is done for poly/poly) + // sort turns by Linear seg_id, then by fraction, then + // for same ring id: x, u, i, c + // for different ring id: c, i, u, x +#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR std::sort(boost::begin(turns), boost::end(turns), sort_on_segment()); +#else + typedef relate::turns::less<0, relate::turns::less_op_linear_areal_single<0> > turn_less; + std::sort(boost::begin(turns), boost::end(turns), turn_less()); +#endif LineStringOut current_piece; geometry::segment_identifier current_segment_id(0, -1, -1, -1); diff --git a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index 7106e7b48..93050bbe2 100644 --- a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -208,6 +208,8 @@ struct intersection_of_linestring_with_areal } #endif +#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + class is_crossing_turn { // return true is the operation is intersection or blocked @@ -310,6 +312,91 @@ struct intersection_of_linestring_with_areal return 0; } +#else // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + + template + static inline bool simple_turns_analysis(Linestring const& linestring, + Areal const& areal, + Strategy const& strategy, + Turns const& turns, + int & inside_value) + { + using namespace overlay; + + bool found_continue = false; + bool found_intersection = false; + bool found_union = false; + bool found_front = false; + + for (typename Turns::const_iterator it = turns.begin(); + it != turns.end(); ++it) + { + method_type const method = it->method; + operation_type const op = it->operations[0].operation; + + if (method == method_crosses) + { + return false; + } + else if (op == operation_intersection) + { + found_intersection = true; + } + else if (op == operation_union) + { + found_union = true; + } + else if (op == operation_continue) + { + found_continue = true; + } + + if ((found_intersection || found_continue) && found_union) + { + return false; + } + + if (it->operations[0].position == position_front) + { + found_front = true; + } + } + + if (found_front) + { + if (found_intersection) + { + inside_value = 1; // inside + } + else if (found_union) + { + inside_value = -1; // outside + } + else // continue and blocked + { + inside_value = 0; + } + return true; + } + + // if needed analyse points of a linestring + // NOTE: range_in_geometry checks points of a linestring + // until a point inside/outside areal is found + // TODO: Could be replaced with point_in_geometry() because found_front is false + inside_value = range_in_geometry(linestring, areal, strategy); + + if ( (found_intersection && inside_value == -1) // going in from outside + || (found_continue && inside_value == -1) // going on boundary from outside + || (found_union && inside_value == 1) ) // going out from inside + { + return false; + } + + return true; + } + +#endif // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + template < typename LineString, typename Areal, @@ -336,14 +423,30 @@ struct intersection_of_linestring_with_areal > follower; typedef typename point_type::type point_type; +#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR typedef detail::overlay::traversal_turn_info - < - point_type, - typename geometry::segment_ratio_type::type - > turn_info; + < + point_type, + typename geometry::segment_ratio_type::type + > turn_info; +#else + typedef detail::overlay::turn_info + < + point_type, + typename geometry::segment_ratio_type::type, + detail::overlay::turn_operation_linear + < + point_type, + typename geometry::segment_ratio_type::type + > + > turn_info; +#endif std::deque turns; detail::get_turns::no_interrupt_policy policy; + +#ifdef BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + geometry::get_turns < false, @@ -366,7 +469,7 @@ struct intersection_of_linestring_with_areal // until a point inside/outside areal is found inside_value = overlay::range_in_geometry(linestring, areal, strategy); } - // add point to the output if conditions are met + // add linestring to the output if conditions are met if (inside_value != 0 && follower::included(inside_value)) { LineStringOut copy; @@ -376,6 +479,46 @@ struct intersection_of_linestring_with_areal return out; } +#else // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + + typedef detail::overlay::get_turn_info_linear_areal + < + detail::overlay::assign_null_policy + > turn_policy; + + dispatch::get_turns + < + typename geometry::tag::type, + typename geometry::tag::type, + LineString, + Areal, + false, + (OverlayType == overlay_intersection ? ReverseAreal : !ReverseAreal), + turn_policy + >::apply(0, linestring, 1, areal, + strategy, robust_policy, + turns, policy); + + int inside_value = 0; + if (simple_turns_analysis(linestring, areal, strategy, turns, inside_value)) + { + // No intersection points, it is either + // inside (interior + borders) + // or outside (exterior + borders) + + // add linestring to the output if conditions are met + if (inside_value != 0 && follower::included(inside_value)) + { + LineStringOut copy; + geometry::convert(linestring, copy); + *out++ = copy; + } + + return out; + } + +#endif // BOOST_GEOMETRY_SETOPS_LA_OLD_BEHAVIOR + #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) int index = 0; for(typename std::deque::const_iterator From 20a858c0edfd6af70a10e405781f5a8eb71c83d3 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 7 Sep 2017 22:49:39 +0200 Subject: [PATCH 100/188] [test][difference] Add L/A test cases. --- .../difference/difference_areal_linear.cpp | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/test/algorithms/set_operations/difference/difference_areal_linear.cpp b/test/algorithms/set_operations/difference/difference_areal_linear.cpp index f875b7fbc..f29d78054 100644 --- a/test/algorithms/set_operations/difference/difference_areal_linear.cpp +++ b/test/algorithms/set_operations/difference/difference_areal_linear.cpp @@ -3,8 +3,8 @@ // Copyright (c) 2010-2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -203,7 +203,6 @@ void test_areal_linear() "POLYGON((5 5,15 15,15 5,5 5))", 1, 2, 6 * std::sqrt(2.0)); -#ifdef BOOST_GEOMETRY_ENABLE_FAILING_TESTS test_one_lp("case37_1", "LINESTRING(1 1,2 2)", "POLYGON((0 0,0 3,3 3,3 0,0 0),(1 1,1 2,2 2,2 1,1 1))", @@ -213,7 +212,26 @@ void test_areal_linear() "LINESTRING(1 1,2 2,3 3)", "POLYGON((0 0,0 3,3 3,3 0,0 0),(1 1,1 2,2 2,2 1,1 1))", 1, 2, std::sqrt(2.0)); -#endif + + test_one_lp("case38", + "LINESTRING(0 0,1 1,2 2,3 3)", + "POLYGON((0 0,0 9,9 9,9 0,0 0),(0 0,2 1,2 2,1 2,0 0))", + 1, 3, 2 * std::sqrt(2.0)); + + // several linestrings are in the output, the result is geometrically correct + // still single linestring could be generated + test_one_lp("case39", + "LINESTRING(0 0,1 1,2 2,3 3)", + "POLYGON((0 0,0 9,9 9,9 0,0 0),(0 0,2 1,2 2,1 2,0 0),(2 2,3 2,3 3,2 3,2 2))", + 2, 5, 3 * std::sqrt(2.0)); + test_one_lp("case40", + "LINESTRING(0 0,1 1,2 2,4 4)", + "POLYGON((0 0,0 9,9 9,9 0,0 0),(0 0,2 1,2 2,1 2,0 0),(2 2,3 2,3 3,2 3,2 2))", + 2, 5, 3 * std::sqrt(2.0)); + test_one_lp("case41", + "LINESTRING(0 0,1 1,2 2,9 9)", + "POLYGON((0 0,0 9,9 9,9 0,0 0),(0 0,2 1,2 2,1 2,0 0),(2 2,3 2,3 3,2 3,2 2),(7 7,8 7,9 9,7 8,7 7))", + 3, 7, 5 * std::sqrt(2.0)); } template From 2bc67373099b29b9162384f6d03cdc951ed5011b Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Fri, 8 Sep 2017 15:21:12 +0200 Subject: [PATCH 101/188] enrich_intersection_points: fix to_index0/to_index1 confusion Found by Coverity: "pointless_expression: The expression to_index1 >= 0 && to_index1 >= 0 does not accomplish anything because it evaluates to either of its identical operands, to_index1 >= 0." --- .../algorithms/detail/overlay/enrich_intersection_points.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index d74e7ae22..fd63ad74b 100644 --- a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -257,7 +257,7 @@ inline void calculate_remaining_distance(Turns& turns) int const to_index0 = op0.enriched.get_next_turn_index(); int const to_index1 = op1.enriched.get_next_turn_index(); - if (to_index1 >= 0 + if (to_index0 >= 0 && to_index1 >= 0 && to_index0 != to_index1) { From da13a2382439d221908b6b65117c5a7ce39b8e39 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 11 Sep 2017 13:30:18 +0200 Subject: [PATCH 102/188] [overlay] Fix L/A case - entire linestring on boundary of polygon. --- .../algorithms/detail/overlay/intersection_insert.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index 93050bbe2..7d0f6c025 100644 --- a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -502,12 +502,13 @@ struct intersection_of_linestring_with_areal int inside_value = 0; if (simple_turns_analysis(linestring, areal, strategy, turns, inside_value)) { - // No intersection points, it is either + // No crossing the boundary, it is either // inside (interior + borders) // or outside (exterior + borders) + // or on boundary // add linestring to the output if conditions are met - if (inside_value != 0 && follower::included(inside_value)) + if (follower::included(inside_value)) { LineStringOut copy; geometry::convert(linestring, copy); From d28766cf5798b5ba35731f888ba9fab4d17588ee Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 11 Sep 2017 13:38:25 +0200 Subject: [PATCH 103/188] [test][intersection] Add L/A test cases - entire linestring on boundary of polygon. --- .../intersection/intersection.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/algorithms/set_operations/intersection/intersection.cpp b/test/algorithms/set_operations/intersection/intersection.cpp index 10d23addd..b865960cb 100644 --- a/test/algorithms/set_operations/intersection/intersection.cpp +++ b/test/algorithms/set_operations/intersection/intersection.cpp @@ -5,8 +5,8 @@ // Copyright (c) 2008-2015 Bruno Lalande, Paris, France. // Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2015, 2016. -// Modifications copyright (c) 2015-2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2015, 2016, 2017. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -602,6 +602,20 @@ void test_areal_linear() typedef typename bg::point_type::type Point; test_one, LineString>("simplex", poly_simplex, "LINESTRING(0 2,4 2)", 1, 2, 2.0); + test_one_lp("case30", + "POLYGON((25 0,0 15,30 15,22 10,25 0))", + "LINESTRING(10 15,20 15)", + 1, 2, 10.0); + + test_one_lp("case31", + "POLYGON((25 0,0 15,30 15,22 10,25 0))", + "LINESTRING(0 15,20 15)", + 1, 2, 20.0); + + test_one_lp("case32", + "POLYGON((25 0,0 15,30 15,22 10,25 0))", + "LINESTRING(25 0, 0 15,20 15)", + 1, 3, 49.15475947422650 /*sqrt(25^2+15^2)+20*/); } From abe8a7e7f352daadc4f2bec9e944c9a9a603feaa Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 11 Sep 2017 22:59:03 +0200 Subject: [PATCH 104/188] [intersection] Fix L/A->P not returning the first endpoint of Linear on boundary of Areal. --- .../algorithms/detail/intersection/multi.hpp | 16 +- .../detail/overlay/intersection_insert.hpp | 284 +++++++++++++----- 2 files changed, 211 insertions(+), 89 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/intersection/multi.hpp b/include/boost/geometry/algorithms/detail/intersection/multi.hpp index 88b49b016..92ce89bb2 100644 --- a/include/boost/geometry/algorithms/detail/intersection/multi.hpp +++ b/include/boost/geometry/algorithms/detail/intersection/multi.hpp @@ -237,7 +237,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, multi_linestring_tag, multi_linestring_tag, point_tag, - false, false, false + linear_tag, linear_tag, pointlike_tag > : detail::intersection::intersection_multi_linestring_multi_linestring_point < GeometryOut @@ -259,7 +259,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, linestring_tag, multi_linestring_tag, point_tag, - false, false, false + linear_tag, linear_tag, pointlike_tag > : detail::intersection::intersection_linestring_multi_linestring_point < GeometryOut @@ -281,7 +281,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, multi_linestring_tag, box_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::clip_multi_linestring < GeometryOut @@ -303,7 +303,7 @@ struct intersection_insert OverlayType, ReverseLinestring, ReverseMultiPolygon, ReverseOut, linestring_tag, multi_polygon_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_linestring_with_areal < ReverseMultiPolygon, @@ -329,7 +329,7 @@ struct intersection_insert OverlayType, ReversePolygon, ReverseMultiLinestring, ReverseOut, polygon_tag, multi_linestring_tag, linestring_tag, - true, false, false + areal_tag, linear_tag, linear_tag > : detail::intersection::intersection_of_areal_with_multi_linestring < ReversePolygon, @@ -353,7 +353,7 @@ struct intersection_insert OverlayType, ReverseMultiLinestring, ReverseRing, ReverseOut, multi_linestring_tag, ring_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_multi_linestring_with_areal < ReverseRing, @@ -376,7 +376,7 @@ struct intersection_insert OverlayType, ReverseMultiLinestring, ReverseRing, ReverseOut, multi_linestring_tag, polygon_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_multi_linestring_with_areal < ReverseRing, @@ -401,7 +401,7 @@ struct intersection_insert OverlayType, ReverseMultiLinestring, ReverseMultiPolygon, ReverseOut, multi_linestring_tag, multi_polygon_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_multi_linestring_with_areal < ReverseMultiPolygon, diff --git a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index 7d0f6c025..60255cd95 100644 --- a/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -539,6 +539,135 @@ struct intersection_of_linestring_with_areal }; +template +inline OutputIterator intersection_output_turn_points(Turns const& turns, + OutputIterator out) +{ + for (typename Turns::const_iterator + it = turns.begin(); it != turns.end(); ++it) + { + *out++ = it->point; + } + + return out; +} + +template +struct intersection_areal_areal_point +{ + template + < + typename Geometry1, typename Geometry2, + typename RobustPolicy, + typename OutputIterator, + typename Strategy + > + static inline OutputIterator apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + OutputIterator out, + Strategy const& strategy) + { + typedef detail::overlay::turn_info + < + PointOut, + typename segment_ratio_type::type + > turn_info; + std::vector turns; + + detail::get_turns::no_interrupt_policy policy; + + geometry::get_turns + < + false, false, detail::overlay::assign_null_policy + >(geometry1, geometry2, strategy, robust_policy, turns, policy); + + return intersection_output_turn_points(turns, out); + } +}; + +template +struct intersection_linear_areal_point +{ + template + < + typename Geometry1, typename Geometry2, + typename RobustPolicy, + typename OutputIterator, + typename Strategy + > + static inline OutputIterator apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + OutputIterator out, + Strategy const& strategy) + { + typedef typename geometry::segment_ratio_type + < + PointOut, RobustPolicy + >::type segment_ratio_type; + + typedef detail::overlay::turn_info + < + PointOut, + segment_ratio_type, + detail::overlay::turn_operation_linear + < + PointOut, + segment_ratio_type + > + > turn_info; + + typedef detail::overlay::get_turn_info_linear_areal + < + detail::overlay::assign_null_policy + > turn_policy; + + std::vector turns; + + detail::get_turns::no_interrupt_policy interrupt_policy; + + dispatch::get_turns + < + typename geometry::tag::type, + typename geometry::tag::type, + Geometry1, + Geometry2, + false, + false, + turn_policy + >::apply(0, geometry1, 1, geometry2, + strategy, robust_policy, + turns, interrupt_policy); + + return intersection_output_turn_points(turns, out); + } +}; + +template +struct intersection_areal_linear_point +{ + template + < + typename Geometry1, typename Geometry2, + typename RobustPolicy, + typename OutputIterator, + typename Strategy + > + static inline OutputIterator apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RobustPolicy const& robust_policy, + OutputIterator out, + Strategy const& strategy) + { + return intersection_linear_areal_point + < + PointOut + >::apply(geometry2, geometry1, robust_policy, out, strategy); + } +}; + + }} // namespace detail::intersection #endif // DOXYGEN_NO_DETAIL @@ -564,9 +693,9 @@ template typename TagIn2 = typename geometry::tag::type, typename TagOut = typename geometry::tag::type, // metafunction finetuning helpers: - bool Areal1 = geometry::is_areal::value, - bool Areal2 = geometry::is_areal::value, - bool ArealOut = geometry::is_areal::value + typename CastedTagIn1 = typename geometry::tag_cast::type, + typename CastedTagIn2 = typename geometry::tag_cast::type, + typename CastedTagOut = typename geometry::tag_cast::type > struct intersection_insert { @@ -593,7 +722,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, TagIn1, TagIn2, TagOut, - true, true, true + areal_tag, areal_tag, areal_tag > : detail::overlay::overlay {}; @@ -615,7 +744,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, TagIn, box_tag, TagOut, - true, true, true + areal_tag, areal_tag, areal_tag > : detail::overlay::overlay {}; @@ -635,7 +764,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, segment_tag, segment_tag, point_tag, - false, false, false + linear_tag, linear_tag, pointlike_tag > : detail::intersection::intersection_segment_segment_point {}; @@ -654,7 +783,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, linestring_tag, linestring_tag, point_tag, - false, false, false + linear_tag, linear_tag, pointlike_tag > : detail::intersection::intersection_linestring_linestring_point {}; @@ -672,7 +801,7 @@ struct intersection_insert overlay_intersection, Reverse1, Reverse2, ReverseOut, linestring_tag, box_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > { template @@ -703,7 +832,7 @@ struct intersection_insert OverlayType, ReverseLinestring, ReversePolygon, ReverseOut, linestring_tag, polygon_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_linestring_with_areal < ReversePolygon, @@ -727,7 +856,7 @@ struct intersection_insert OverlayType, ReverseLinestring, ReverseRing, ReverseOut, linestring_tag, ring_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > : detail::intersection::intersection_of_linestring_with_areal < ReverseRing, @@ -750,7 +879,7 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, segment_tag, box_tag, linestring_tag, - false, true, false + linear_tag, areal_tag, linear_tag > { template @@ -774,8 +903,7 @@ template typename PointOut, overlay_type OverlayType, bool Reverse1, bool Reverse2, bool ReverseOut, - typename Tag1, typename Tag2, - bool Areal1, bool Areal2 + typename Tag1, typename Tag2 > struct intersection_insert < @@ -784,38 +912,59 @@ struct intersection_insert OverlayType, Reverse1, Reverse2, ReverseOut, Tag1, Tag2, point_tag, - Areal1, Areal2, false + areal_tag, areal_tag, pointlike_tag > -{ - template - static inline OutputIterator apply(Geometry1 const& geometry1, - Geometry2 const& geometry2, - RobustPolicy const& robust_policy, - OutputIterator out, Strategy const& strategy) - { + : public detail::intersection::intersection_areal_areal_point + < + PointOut + > +{}; - typedef detail::overlay::turn_info - < - PointOut, - typename segment_ratio_type::type - > turn_info; - std::vector turns; - - detail::get_turns::no_interrupt_policy policy; - geometry::get_turns - < - false, false, detail::overlay::assign_null_policy - >(geometry1, geometry2, strategy, robust_policy, turns, policy); - for (typename std::vector::const_iterator it - = turns.begin(); it != turns.end(); ++it) - { - *out++ = it->point; - } - - return out; - } -}; +template +< + typename Geometry1, typename Geometry2, + typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename Tag1, typename Tag2 +> +struct intersection_insert + < + Geometry1, Geometry2, + PointOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + Tag1, Tag2, point_tag, + linear_tag, areal_tag, pointlike_tag + > + : public detail::intersection::intersection_linear_areal_point + < + PointOut + > +{}; +template +< + typename Geometry1, typename Geometry2, + typename PointOut, + overlay_type OverlayType, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename Tag1, typename Tag2 +> +struct intersection_insert + < + Geometry1, Geometry2, + PointOut, + OverlayType, + Reverse1, Reverse2, ReverseOut, + Tag1, Tag2, point_tag, + areal_tag, linear_tag, pointlike_tag + > + : public detail::intersection::intersection_areal_linear_point + < + PointOut + > +{}; template < @@ -857,7 +1006,7 @@ struct intersection_insert overlay_intersection, Reverse1, Reverse2, ReverseOut, Tag1, Tag2, linestring_tag, - true, true, false + areal_tag, areal_tag, linear_tag > { template @@ -883,47 +1032,20 @@ struct intersection_insert } }; -// dispatch for non-areal geometries +// dispatch for difference/intersection of linear geometries template < - typename Geometry1, typename Geometry2, typename GeometryOut, + typename Linear1, typename Linear2, typename LineStringOut, overlay_type OverlayType, bool Reverse1, bool Reverse2, bool ReverseOut, typename TagIn1, typename TagIn2 > -struct intersection_insert - < - Geometry1, Geometry2, GeometryOut, - OverlayType, - Reverse1, Reverse2, ReverseOut, - TagIn1, TagIn2, linestring_tag, - false, false, false - > : intersection_insert - < - Geometry1, Geometry2, GeometryOut, - OverlayType, - Reverse1, Reverse2, ReverseOut, - typename tag_cast::type, - typename tag_cast::type, - linestring_tag, - false, false, false - > -{}; - - -// dispatch for difference/intersection of linear geometries -template -< - typename Linear1, typename Linear2, typename LineStringOut, - overlay_type OverlayType, - bool Reverse1, bool Reverse2, bool ReverseOut -> struct intersection_insert < Linear1, Linear2, LineStringOut, OverlayType, Reverse1, Reverse2, ReverseOut, - linear_tag, linear_tag, linestring_tag, - false, false, false + TagIn1, TagIn2, linestring_tag, + linear_tag, linear_tag, linear_tag > : detail::overlay::linear_linear_linestring < Linear1, Linear2, LineStringOut, OverlayType @@ -944,7 +1066,7 @@ struct intersection_insert Point1, Point2, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, point_tag, point_tag, point_tag, - false, false, false + pointlike_tag, pointlike_tag, pointlike_tag > : detail::overlay::point_point_point < Point1, Point2, PointOut, OverlayType @@ -963,7 +1085,7 @@ struct intersection_insert MultiPoint, Point, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, multi_point_tag, point_tag, point_tag, - false, false, false + pointlike_tag, pointlike_tag, pointlike_tag > : detail::overlay::multipoint_point_point < MultiPoint, Point, PointOut, OverlayType @@ -982,7 +1104,7 @@ struct intersection_insert Point, MultiPoint, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, point_tag, multi_point_tag, point_tag, - false, false, false + pointlike_tag, pointlike_tag, pointlike_tag > : detail::overlay::point_multipoint_point < Point, MultiPoint, PointOut, OverlayType @@ -1001,7 +1123,7 @@ struct intersection_insert MultiPoint1, MultiPoint2, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, multi_point_tag, multi_point_tag, point_tag, - false, false, false + pointlike_tag, pointlike_tag, pointlike_tag > : detail::overlay::multipoint_multipoint_point < MultiPoint1, MultiPoint2, PointOut, OverlayType @@ -1022,7 +1144,7 @@ struct intersection_insert Point, Linear, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, point_tag, Tag, point_tag, - false, false, false + pointlike_tag, linear_tag, pointlike_tag > : detail_dispatch::overlay::pointlike_linear_point < Point, Linear, PointOut, OverlayType, @@ -1043,7 +1165,7 @@ struct intersection_insert MultiPoint, Linear, PointOut, OverlayType, Reverse1, Reverse2, ReverseOut, multi_point_tag, Tag, point_tag, - false, false, false + pointlike_tag, linear_tag, pointlike_tag > : detail_dispatch::overlay::pointlike_linear_point < MultiPoint, Linear, PointOut, OverlayType, @@ -1063,7 +1185,7 @@ struct intersection_insert Linestring, MultiPoint, PointOut, overlay_intersection, Reverse1, Reverse2, ReverseOut, linestring_tag, multi_point_tag, point_tag, - false, false, false + linear_tag, pointlike_tag, pointlike_tag > { template From 522f71b81f1ac98002b4cf8516fc7a04c94de7d1 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Mon, 11 Sep 2017 22:59:56 +0200 Subject: [PATCH 105/188] [test][intersection] Add L/A->P case of Linestring on boundary of Polygon. --- .../set_operations/intersection/intersection.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/algorithms/set_operations/intersection/intersection.cpp b/test/algorithms/set_operations/intersection/intersection.cpp index b865960cb..b9c55de93 100644 --- a/test/algorithms/set_operations/intersection/intersection.cpp +++ b/test/algorithms/set_operations/intersection/intersection.cpp @@ -616,6 +616,13 @@ void test_areal_linear() "POLYGON((25 0,0 15,30 15,22 10,25 0))", "LINESTRING(25 0, 0 15,20 15)", 1, 3, 49.15475947422650 /*sqrt(25^2+15^2)+20*/); + + typedef typename bg::point_type::type P; + + test_one_lp("case30p", + "POLYGON((25 0,0 15,30 15,22 10,25 0))", + "LINESTRING(10 15,20 15)", + 2, 2, 0); } From 3e97db55ab98de8d6ac79a04e1fddaf2c328038a Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Tue, 12 Sep 2017 14:08:50 +0300 Subject: [PATCH 106/188] [test] Tests for geographic distance of pointlike-linear geometries --- .../algorithms/distance/distance_geo_pl_l.cpp | 306 ++++++++++++++++++ 1 file changed, 306 insertions(+) diff --git a/test/algorithms/distance/distance_geo_pl_l.cpp b/test/algorithms/distance/distance_geo_pl_l.cpp index 0e611f065..061d9e52c 100644 --- a/test/algorithms/distance/distance_geo_pl_l.cpp +++ b/test/algorithms/distance/distance_geo_pl_l.cpp @@ -300,13 +300,319 @@ void test_distance_point_segment(Strategy_pp const& strategy_pp, */ } +//=========================================================================== + +template +void test_distance_point_linestring(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "point/linestring distance tests" << std::endl; +#endif + typedef test_distance_of_geometries tester; + + tester::apply("p-l-01", + "POINT(0 0)", + "LINESTRING(2 0,2 0)", + pp_distance("POINT(0 0)", "POINT(2 0)", strategy_pp), + strategy_ps); + tester::apply("p-l-02", + "POINT(0 0)", + "LINESTRING(2 0,3 0)", + pp_distance("POINT(0 0)", "POINT(2 0)", strategy_pp), + strategy_ps); + tester::apply("p-l-03", + "POINT(2.5 3)", + "LINESTRING(2 0,3 0)", + pp_distance("POINT(2.5 3)", "POINT(2.5 0)", strategy_pp), + strategy_ps); + tester::apply("p-l-04", + "POINT(2 0)", + "LINESTRING(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-l-05", + "POINT(3 0)", + "LINESTRING(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-l-06", + "POINT(2.5 0)", + "LINESTRING(2 0,3 0)", + 0, + strategy_ps); + tester::apply("p-l-07", + "POINT(7.5 10)", + "LINESTRING(1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9 0)", + ps_distance("POINT(7.5 10)", "SEGMENT(7 0,8 0)", strategy_ps), + strategy_ps); + tester::apply("p-l-08", + "POINT(7.5 10)", + "LINESTRING(1 1,2 1,3 1,4 1,5 1,6 1,7 1,20 2,21 2)", + ps_distance("POINT(7.5 10)", "SEGMENT(7 1,20 2)", strategy_ps), + strategy_ps); +} + +void test_distance_point_linestring_strategies() +{ + typedef test_distance_of_geometries tester; + + tester::apply("p-l-03", + "POINT(2.5 3)", + "LINESTRING(2 1,3 1)", + 221147.24332788656, + vincenty_strategy()); + + tester::apply("p-l-03", + "POINT(2.5 3)", + "LINESTRING(2 1,3 1)", + 221147.36682199029, + thomas_strategy()); + + tester::apply("p-l-03", + "POINT(2.5 3)", + "LINESTRING(2 1,3 1)", + 221144.76527049288, + andoyer_strategy()); +} + +//=========================================================================== + +template +void test_distance_point_multilinestring(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "point/multilinestring distance tests" << std::endl; +#endif + typedef test_distance_of_geometries + < + point_type, multi_linestring_type + > tester; + + tester::apply("p-ml-01", + "POINT(0 0)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0))", + pp_distance("POINT(0 0)", "POINT(2 0)", strategy_pp), + strategy_ps); + tester::apply("p-ml-02", + "POINT(2.5 3)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0))", + pp_distance("POINT(2.5 3)", "POINT(2.5 0)", strategy_pp), + strategy_ps); + tester::apply("p-ml-03", + "POINT(2 0)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0))", + 0, + strategy_ps); + tester::apply("p-ml-04", + "POINT(3 0)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0))", + 0, + strategy_ps); + tester::apply("p-ml-05", + "POINT(2.5 0)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0))", + 0, + strategy_ps); + tester::apply("p-ml-06", + "POINT(7.5 10)", + "MULTILINESTRING((-5 0,-3 0),(2 0,3 0,4 0,5 0,6 0,20 1,21 1))", + ps_distance("POINT(7.5 10)", "SEGMENT(6 0,20 1)", strategy_ps), + strategy_ps); + tester::apply("p-ml-07", + "POINT(-8 10)", + "MULTILINESTRING((-20 10,-19 11,-18 10,-6 0,-5 0,-3 0),(2 0,6 0,20 1,21 1))", + ps_distance("POINT(-8 10)", "SEGMENT(-6 0,-18 10)", strategy_ps), + strategy_ps); +} + +//=========================================================================== + +template +void test_distance_linestring_multipoint(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "linestring/multipoint distance tests" << std::endl; +#endif + typedef test_distance_of_geometries + < + linestring_type, multi_point_type + > tester; + + tester::apply("l-mp-01", + "LINESTRING(2 0,0 2,100 80)", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + ps_distance("POINT(1 1)", "SEGMENT(2 0,0 2)", strategy_ps), + strategy_ps); + + tester::apply("l-mp-02", + "LINESTRING(4 0,0 4,100 80)", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + ps_distance("POINT(1 1)", "SEGMENT(0 4,4 0)", strategy_ps), + strategy_ps); + tester::apply("l-mp-03", + "LINESTRING(1 1,2 2,100 80)", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + 0, + strategy_ps); + tester::apply("l-mp-04", + "LINESTRING(3 3,4 4,100 80)", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + pp_distance("POINT(1 1)", "POINT(3 3)", strategy_pp), + strategy_ps); + tester::apply("l-mp-05", + "LINESTRING(0 0,10 0,10 10,0 10,0 0)", + "MULTIPOINT(1 -1,80 80,5 0,150 90)", + 0, + strategy_ps); +} + +//=========================================================================== +template +void test_distance_multipoint_multilinestring(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "multipoint/multilinestring distance tests" << std::endl; +#endif + typedef test_distance_of_geometries + < + multi_point_type, multi_linestring_type + > tester; + + tester::apply("mp-ml-01", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "MULTILINESTRING((2 0,0 2),(2 2,3 3))", + ps_distance("POINT(1 1)", "SEGMENT(2 0,0 2)", strategy_ps), + strategy_ps); + tester::apply("mp-ml-02", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "MULTILINESTRING((3 0,0 3),(4 4,5 5))", + ps_distance("POINT(1 1)", "SEGMENT(3 0,0 3)", strategy_ps), + strategy_ps); + tester::apply("mp-ml-03", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "MULTILINESTRING((4 4,5 5),(1 1,2 2))", + 0, + strategy_ps); + tester::apply("mp-ml-04", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "MULTILINESTRING((4 4,3 3),(4 4,5 5))", + pp_distance("POINT(1 1)", "POINT(3 3)", strategy_pp), + strategy_ps); +} + +//=========================================================================== + +template +void test_distance_multipoint_segment(Strategy_pp const& strategy_pp, + Strategy_ps const& strategy_ps) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "multipoint/segment distance tests" << std::endl; +#endif + typedef test_distance_of_geometries tester; + + tester::apply("mp-s-01", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "SEGMENT(2 0,0 2)", + ps_distance("POINT(1 1)", "SEGMENT(2 0,0 2)", strategy_ps), + strategy_ps); + tester::apply("mp-s-02", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "SEGMENT(0 -3,1 -10)", + pp_distance("POINT(0 0)", "POINT(0 -3)", strategy_pp), + strategy_ps); + tester::apply("mp-s-03", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "SEGMENT(1 1,2 2)", + 0, + strategy_ps); + tester::apply("mp-s-04", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "SEGMENT(3 3,4 4)", + pp_distance("POINT(1 1)", "POINT(3 3)", strategy_pp), + strategy_ps); + tester::apply("mp-s-05", + "MULTIPOINT(0 0,1 0,0 1,1 1)", + "SEGMENT(0.5 -3,1 -10)", + pp_distance("POINT(1 0)", "POINT(0.5 -3)", strategy_pp), + strategy_ps); +} + +//=========================================================================== + +template +void test_empty_input_pointlike_linear(Strategy const& strategy) +{ +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << std::endl; + std::cout << "testing on empty inputs... " << std::flush; +#endif + bg::model::linestring line_empty; + bg::model::multi_point multipoint_empty; + bg::model::multi_linestring > multiline_empty; + + Point point = from_wkt("POINT(0 0)"); + bg::model::linestring line = + from_wkt >("LINESTRING(0 0,1 1)"); + + // 1st geometry is empty + //test_empty_input(multipoint_empty, line, strategy); + + // 2nd geometry is empty + test_empty_input(point, line_empty, strategy); + test_empty_input(point, multiline_empty, strategy); + + // both geometries are empty + //test_empty_input(multipoint_empty, line_empty, strategy); + //test_empty_input(multipoint_empty, multiline_empty, strategy); + +#ifdef BOOST_GEOMETRY_TEST_DEBUG + std::cout << "done!" << std::endl; +#endif +} + //=========================================================================== //=========================================================================== //=========================================================================== BOOST_AUTO_TEST_CASE( test_all_point_segment ) { + //TODO: Operations with multipoints need geographic pt-box strategy + //before activating + test_distance_point_segment(vincenty_pp(), vincenty_strategy()); test_distance_point_segment(thomas_pp(), thomas_strategy()); test_distance_point_segment(andoyer_pp(), andoyer_strategy()); + + test_distance_point_linestring(vincenty_pp(), vincenty_strategy()); + test_distance_point_linestring(thomas_pp(), thomas_strategy()); + test_distance_point_linestring(andoyer_pp(), andoyer_strategy()); + test_distance_point_linestring_strategies(); + + test_distance_point_multilinestring(vincenty_pp(), vincenty_strategy()); + test_distance_point_multilinestring(thomas_pp(), thomas_strategy()); + test_distance_point_multilinestring(andoyer_pp(), andoyer_strategy()); + + // test_distance_linestring_multipoint(vincenty_pp(), vincenty_strategy()); + // test_distance_linestring_multipoint(thomas_pp(), thomas_strategy()); + // test_distance_linestring_multipoint(andoyer_pp(), andoyer_strategy()); + + // test_distance_multipoint_multilinestring(vincenty_pp(), vincenty_strategy()); + // test_distance_multipoint_multilinestring(thomas_pp(), thomas_strategy()); + // test_distance_multipoint_multilinestring(andoyer_pp(), andoyer_strategy()); + + // test_distance_multipoint_segment(vincenty_pp(), vincenty_strategy()); + // test_distance_multipoint_segment(thomas_pp(), thomas_strategy()); + // test_distance_multipoint_segment(andoyer_pp(), andoyer_strategy()); + + test_empty_input_pointlike_linear(vincenty_strategy()); } From 47e2f0b4e96a0a4ee614921de56f46c2c24f6b72 Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Tue, 12 Sep 2017 16:45:31 +0300 Subject: [PATCH 107/188] [test] Tests for small and large distances from point to segment --- .../formulas/distance_point_segment.hpp | 1 + .../algorithms/distance/distance_geo_pl_l.cpp | 49 ++++++++++--------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index afa4eebd7..4ff4dda8f 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -107,6 +107,7 @@ public: } //segment on equator + //TODO: use the meridian distance when it'll be available if (math::equals(lat1, 0) && math::equals(lat2, 0)) { if (lon3 <= lon1) diff --git a/test/algorithms/distance/distance_geo_pl_l.cpp b/test/algorithms/distance/distance_geo_pl_l.cpp index 061d9e52c..5ea565fe8 100644 --- a/test/algorithms/distance/distance_geo_pl_l.cpp +++ b/test/algorithms/distance/distance_geo_pl_l.cpp @@ -250,54 +250,57 @@ void test_distance_point_segment(Strategy_pp const& strategy_pp, pp_distance("POINT(2.5 5)", "POINT(2 4)", strategy_pp), strategy_ps); - - //Nearly antipodal points not treated correctly in BG so far -/* // very small distances to segment tester::apply("p-s-07", "POINT(90 1e-3)", "SEGMENT(0.5 0,175.5 0)", - 110.57427582147321, - //1e-3 * d2r * 6335439.3272858784, - //to_comparable(strategy, 1e-3 * d2r * strategy.radius()), + pp_distance("POINT(90 0)", "POINT(90 1e-3)", strategy_pp), strategy_ps); tester::apply("p-s-08", "POINT(90 1e-4)", "SEGMENT(0.5 0,175.5 0)", - 6176.4469462110519, - //1e-4 * d2r * 6335439.3272858784, - //1e-4 * d2r * strategy.radius(), - //to_comparable(strategy, 1e-4 * d2r * strategy.radius()), + pp_distance("POINT(90 0)", "POINT(90 1e-4)", strategy_pp), strategy_ps); tester::apply("p-s-09", "POINT(90 1e-5)", "SEGMENT(0.5 0,175.5 0)", - 4917.5121192812849, - //1e-5 * d2r * strategy.radius(), - //to_comparable(strategy, 1e-5 * d2r * strategy.radius()), + pp_distance("POINT(90 0)", "POINT(90 1e-5)", strategy_pp), strategy_ps); tester::apply("p-s-10", "POINT(90 1e-6)", "SEGMENT(0.5 0,175.5 0)", - 4997.0354014473833, - //1e-6 * d2r * strategy.radius(), - //to_comparable(strategy, 1e-6 * d2r * strategy.radius()), + pp_distance("POINT(90 0)", "POINT(90 1e-6)", strategy_pp), strategy_ps); tester::apply("p-s-11", "POINT(90 1e-7)", "SEGMENT(0.5 0,175.5 0)", - 109854.20732250962, - //1e-7 * d2r * strategy.radius(), - //to_comparable(strategy, 1e-7 * d2r * strategy.radius()), + pp_distance("POINT(90 0)", "POINT(90 1e-7)", strategy_pp), strategy_ps); tester::apply("p-s-12", "POINT(90 1e-8)", "SEGMENT(0.5 0,175.5 0)", - 1372504.9365672076, - //1e-8 * d2r * strategy.radius(), - //to_comparable(strategy, 1e-8 * d2r * strategy.radius()), + pp_distance("POINT(90 0)", "POINT(90 1e-8)", strategy_pp), strategy_ps); -*/ + + // very large distance to segment + tester::apply("p-s-13", + "POINT(90 90)", + "SEGMENT(0.5 0,175.5 0)", + pp_distance("POINT(90 0)", "POINT(90 90)", strategy_pp), + strategy_ps); + tester::apply("p-s-14", + "POINT(90 90)", + "SEGMENT(0.5 -89,175.5 -89)", + pp_distance("POINT(90 -89)", "POINT(90 90)", strategy_pp), + strategy_ps); + // degenerate segment + tester::apply("p-s-14", + "POINT(90 90)", + "SEGMENT(0.5 -90,175.5 -90)", + pp_distance("POINT(90 -90)", "POINT(90 90)", strategy_pp), + strategy_ps); + + } //=========================================================================== From 650451f977bb3270079f85486e91fca82d97b1c7 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 12 Sep 2017 20:55:04 +0200 Subject: [PATCH 108/188] [formulas][strategies] Add/use mean_radius and unit_spheroid functions. --- .../boost/geometry/formulas/geographic.hpp | 7 +- .../boost/geometry/formulas/mean_radius.hpp | 71 +++++++++++++++++++ .../boost/geometry/formulas/unit_spheroid.hpp | 43 +++++++++++ .../strategies/geographic/intersection.hpp | 13 ++-- 4 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 include/boost/geometry/formulas/mean_radius.hpp create mode 100644 include/boost/geometry/formulas/unit_spheroid.hpp diff --git a/include/boost/geometry/formulas/geographic.hpp b/include/boost/geometry/formulas/geographic.hpp index f6feb6663..b338273f3 100644 --- a/include/boost/geometry/formulas/geographic.hpp +++ b/include/boost/geometry/formulas/geographic.hpp @@ -1,6 +1,6 @@ // Boost.Geometry -// Copyright (c) 2016, Oracle and/or its affiliates. +// Copyright (c) 2016-2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -186,7 +187,7 @@ inline Point3d projected_to_surface(Point3d const& direction, Spheroid const& sp //coord_t const b_sqr = math::sqr(get_radius<2>(spheroid)); // "unit" spheroid, a = 1 coord_t const a_sqr = 1; - coord_t const b_sqr = math::sqr(get_radius<2>(spheroid) / get_radius<0>(spheroid)); + coord_t const b_sqr = math::sqr(formula::unit_spheroid_b(spheroid)); coord_t const param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr; coord_t const delta = c4 * param_a; @@ -226,7 +227,7 @@ inline bool projected_to_surface(Point3d const& origin, Point3d const& direction //coord_t const b_sqr = math::sqr(get_radius<2>(spheroid)); // "unit" spheroid, a = 1 coord_t const a_sqr = 1; - coord_t const b_sqr = math::sqr(get_radius<2>(spheroid) / get_radius<0>(spheroid)); + coord_t const b_sqr = math::sqr(formula::unit_spheroid_b(spheroid)); coord_t const param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr; coord_t const param_b = c2 * ((ox*dx + oy*dy) / a_sqr + oz*dz / b_sqr); diff --git a/include/boost/geometry/formulas/mean_radius.hpp b/include/boost/geometry/formulas/mean_radius.hpp new file mode 100644 index 000000000..83c9b5403 --- /dev/null +++ b/include/boost/geometry/formulas/mean_radius.hpp @@ -0,0 +1,71 @@ +// Boost.Geometry + +// Copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// 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) + +#ifndef BOOST_GEOMETRY_FORMULAS_MEAN_RADIUS_HPP +#define BOOST_GEOMETRY_FORMULAS_MEAN_RADIUS_HPP + +#include +#include +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace formula_dispatch +{ + +template ::type> +struct mean_radius + : not_implemented +{}; + +template +struct mean_radius +{ + static inline ResultType apply(Geometry const& geometry) + { + return ResultType(get_radius<0>(geometry)); + } +}; + +template +struct mean_radius +{ + static inline ResultType apply(Geometry const& geometry) + { + // (2*a + b) / 3 + return (ResultType(2) * ResultType(get_radius<0>(geometry)) + + ResultType(get_radius<2>(geometry))) + / ResultType(3); + } +}; + +} // namespace formula_dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace formula +{ + +template +inline ResultType mean_radius(Geometry const& geometry) +{ + return formula_dispatch::mean_radius::apply(geometry); +} + +} // namespace formula +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_MEAN_RADIUS_HPP diff --git a/include/boost/geometry/formulas/unit_spheroid.hpp b/include/boost/geometry/formulas/unit_spheroid.hpp new file mode 100644 index 000000000..7fdedb458 --- /dev/null +++ b/include/boost/geometry/formulas/unit_spheroid.hpp @@ -0,0 +1,43 @@ +// Boost.Geometry + +// Copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// 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) + +#ifndef BOOST_GEOMETRY_FORMULAS_UNIT_SPHEROID_HPP +#define BOOST_GEOMETRY_FORMULAS_UNIT_SPHEROID_HPP + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace formula +{ + +template +inline ResultType unit_spheroid_b(Spheroid const& spheroid) +{ + return ResultType(get_radius<2>(spheroid)) + / ResultType(get_radius<0>(spheroid)); +} + +template +inline ResultSpheroid unit_spheroid(Spheroid const& spheroid) +{ + typedef typename radius_type::type radius_t; + return ResultSpheroid(radius_t(1), + unit_spheroid_b(spheroid)); +} + +} // namespace formula +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_UNIT_SPHEROID_HPP diff --git a/include/boost/geometry/strategies/geographic/intersection.hpp b/include/boost/geometry/strategies/geographic/intersection.hpp index 489c7deb9..e91659d40 100644 --- a/include/boost/geometry/strategies/geographic/intersection.hpp +++ b/include/boost/geometry/strategies/geographic/intersection.hpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -291,10 +292,12 @@ private: typedef typename select_calculation_type ::type calc_t; + typedef srs::spheroid spheroid_type; + static const calc_t c0 = 0; // normalized spheroid - srs::spheroid spheroid = normalized_spheroid(m_spheroid); + spheroid_type spheroid = formula::unit_spheroid(m_spheroid); // TODO: check only 2 first coordinates here? using geometry::detail::equals::equals_point_point; @@ -960,14 +963,6 @@ private: ip_flag; } - template - static inline srs::spheroid normalized_spheroid(SpheroidT const& spheroid) - { - return srs::spheroid(CalcT(1), - CalcT(get_radius<2>(spheroid)) // b/a - / CalcT(get_radius<0>(spheroid))); - } - private: Spheroid m_spheroid; }; From 4e1e18fa39d6ce0549e21e141136d54ff9ce715b Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Wed, 13 Sep 2017 11:28:24 +0300 Subject: [PATCH 109/188] [formulas] [distance] Rename inverse distance formula type name in point-segment formula --- .../formulas/distance_point_segment.hpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index 4ff4dda8f..bf61b38bc 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -40,7 +40,7 @@ class distance_point_segment{ public: - typedef Inverse inverse_distance_type; + typedef Inverse inverse_distance_quantities_type; typedef Inverse inverse_azimuth_type; typedef Direct direct_distance_type; @@ -64,9 +64,9 @@ public: Spheroid const& spheroid) { result_distance_point_segment result; - result.distance = inverse_distance_type::apply(lon1, lat1, - lon2, lat2, - spheroid).distance; + result.distance = inverse_distance_quantities_type::apply(lon1, lat1, + lon2, lat2, + spheroid).distance; if (EnableClosestPoint) { result.closest_point_lon = lon1; @@ -232,8 +232,8 @@ public: CT a4 = inverse_azimuth_type::apply(res14.lon2, res14.lat2, lon2, lat2, spheroid).azimuth; - res34 = inverse_distance_type::apply(res14.lon2, res14.lat2, - lon3, lat3, spheroid); + res34 = inverse_distance_quantities_type::apply(res14.lon2, res14.lat2, + lon3, lat3, spheroid); g4 = res34.azimuth - a4; // Normalize g4 @@ -306,20 +306,20 @@ public: std::cout << "s34(sph) =" << s34_sph << std::endl; std::cout << "s34(geo) =" - << inverse_distance_type::apply(get<0>(p4), get<1>(p4), lon3, lat3, spheroid).distance + << inverse_distance_quantities_type::apply(get<0>(p4), get<1>(p4), lon3, lat3, spheroid).distance << ", p4=(" << get<0>(p4) * math::r2d() << "," << get<1>(p4) * math::r2d() << ")" << std::endl; - CT s31 = inverse_distance_type::apply(lon3, lat3, lon1, lat1, spheroid).distance; - CT s32 = inverse_distance_type::apply(lon3, lat3, lon2, lat2, spheroid).distance; + CT s31 = inverse_distance_quantities_type::apply(lon3, lat3, lon1, lat1, spheroid).distance; + CT s32 = inverse_distance_quantities_type::apply(lon3, lat3, lon2, lat2, spheroid).distance; CT a4 = inverse_azimuth_type::apply(get<0>(p4), get<1>(p4), lon2, lat2, spheroid).azimuth; geometry::formula::result_direct res4 = direct_distance_type::apply(get<0>(p4), get<1>(p4), .04, a4, spheroid); - CT p4_plus = inverse_distance_type::apply(res4.lon2, res4.lat2, lon3, lat3, spheroid).distance; + CT p4_plus = inverse_distance_quantities_type::apply(res4.lon2, res4.lat2, lon3, lat3, spheroid).distance; geometry::formula::result_direct res1 = direct_distance_type::apply(lon1, lat1, s14-.04, a1, spheroid); - CT p4_minus = inverse_distance_type::apply(res1.lon2, res1.lat2, lon3, lat3, spheroid).distance; + CT p4_minus = inverse_distance_quantities_type::apply(res1.lon2, res1.lat2, lon3, lat3, spheroid).distance; std::cout << "s31=" << s31 << "\ns32=" << s32 << "\np4_plus=" << p4_plus << ", p4=(" << res4.lon2 * math::r2d() << "," << res4.lat2 * math::r2d() << ")" From ce9928a1aad8b5c4a94a1379c390efdd21220234 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 13 Sep 2017 13:07:10 +0200 Subject: [PATCH 110/188] [overlay] fix case which needed a specific selection of a matching ranked point --- .../algorithms/detail/overlay/traversal.hpp | 83 ++++++++++++++----- .../overlay/multi_overlay_cases.hpp | 7 ++ .../set_operations/union/union_multi.cpp | 1 + 3 files changed, 68 insertions(+), 23 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index 73b2b53c3..136b3c12f 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -393,13 +393,50 @@ struct traversal return true; } + inline int select_turn_in_cluster_union(std::size_t selected_rank, + typename sbs_type::rp const& ranked_point, + signed_size_type start_turn_index, int start_op_index) const + { + // Returns 0 if it not OK + // Returns 1 if it OK + // Returns 2 if it OK and start turn matches + // Returns 3 if it OK and start turn and start op both match + if (ranked_point.rank != selected_rank + || ranked_point.direction != sort_by_side::dir_to) + { + return 0; + } + + turn_type const& turn = m_turns[ranked_point.turn_index]; + turn_operation_type const& op = turn.operations[ranked_point.operation_index]; + + // Check counts: in some cases interior rings might be generated with + // polygons on both sides + + // Check finalized: TODO: this should be finetuned, it is not necessary + bool const ok = op.enriched.count_left == 0 + && op.enriched.count_right > 0 + && ! op.visited.finalized(); + + if (! ok) + { + return 0; + } + + return ranked_point.turn_index == start_turn_index + && ranked_point.operation_index == start_op_index ? 3 + : ranked_point.turn_index == start_turn_index ? 2 + : 1 + ; + } + inline bool select_from_cluster_union(signed_size_type& turn_index, - int& op_index, sbs_type& sbs) const + int& op_index, sbs_type& sbs, + signed_size_type start_turn_index, int start_op_index) const { std::vector aggregation; sort_by_side::aggregate_operations(sbs, aggregation, m_turns, operation_union); - sort_by_side::rank_with_rings const& incoming = aggregation.front(); // Take the first one outgoing for the incoming region @@ -415,32 +452,31 @@ struct traversal } } + int best_code = 0; + bool result = false; for (std::size_t i = 1; i < sbs.m_ranked_points.size(); i++) { typename sbs_type::rp const& ranked_point = sbs.m_ranked_points[i]; - if (ranked_point.rank == selected_rank - && ranked_point.direction == sort_by_side::dir_to) + + if (ranked_point.rank > selected_rank) { + // Sorted on rank, so it makes no sense to continue + break; + } + + int const code + = select_turn_in_cluster_union(selected_rank, ranked_point, + start_turn_index, start_op_index); + + if (code > best_code) + { + // It is 1 or higher and matching better than previous turn_index = ranked_point.turn_index; op_index = ranked_point.operation_index; - - turn_type const& turn = m_turns[turn_index]; - turn_operation_type const& op = turn.operations[op_index]; - - if (op.enriched.count_left == 0 - && op.enriched.count_right > 0 - && ! op.visited.finalized()) - { - // In some cases interior rings might be generated with polygons - // on both sides - - // TODO: this should be finetuned such that checking - // finalized is not necessary - return true; - } + result = true; } } - return false; + return result; } inline bool analyze_cluster_intersection(signed_size_type& turn_index, @@ -573,7 +609,7 @@ struct traversal inline bool select_turn_from_cluster(signed_size_type& turn_index, int& op_index, - signed_size_type start_turn_index, + signed_size_type start_turn_index, int start_op_index, segment_identifier const& previous_seg_id) const { bool const is_union = target_operation == operation_union; @@ -620,7 +656,8 @@ struct traversal if (is_union) { - result = select_from_cluster_union(turn_index, op_index, sbs); + result = select_from_cluster_union(turn_index, op_index, sbs, + start_turn_index, start_op_index); } else { @@ -787,7 +824,7 @@ struct traversal if (current_turn.cluster_id >= 0) { if (! select_turn_from_cluster(turn_index, op_index, - start_turn_index, previous_seg_id)) + start_turn_index, start_op_index, previous_seg_id)) { return false; } diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index ac488bb8f..4fdba6ab9 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1158,6 +1158,13 @@ static std::string case_recursive_boxes_71[2] = "MULTIPOLYGON(((3 0,3 1,4 0,3 0)),((2 2,0 2,0 3,1 3,2 2)),((2 2,2 3,3 3,2 2)),((2 4,0 4,1 5,3 5,3 4,2 4)),((3 3,3 4,4 5,5 4,4 4,4 3,3 3)),((4 3,5 3,4 2,4 3)))" }; +static std::string case_recursive_boxes_72[2] = +{ + // Needs selection of ranked point in union (to finish the ring) + "MULTIPOLYGON(((3 1,4 1,4 0,3 0,3 1)),((1 0,1 1,2 1,2 0,1 0)),((3 3,3 5,5 5,5 2,4 2,4 3,3 3)),((3 3,2 2,2 3,3 3)),((1 3,0 2,0 3,1 3)),((1 4,2 4,2 3,1 2,1 4)),((1 4,1 5,2 5,1 4)),((2 5,3 5,2 4,2 5)),((1 5,0 4,0 5,1 5)))", + "MULTIPOLYGON(((4 0,4 1,5 0,4 0)),((4 4,4 5,5 5,5 4,4 4)),((2 4,2 3,1 3,1 4,2 4)),((2 4,2 5,3 5,2 4)),((2 2,2 3,3 3,2 2)),((2 2,3 2,3 1,2 1,2 2)),((3 3,4 2,3 2,3 3)),((3 1,3 0,2 0,3 1)),((1 3,2 2,1 1,1 3)),((1 2,0 2,0 3,1 2)),((1 4,0 4,0 5,1 5,1 4)),((4 2,4 3,5 2,4 2)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index ad03d7ef7..e7ccc1f7b 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -393,6 +393,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_69, 4, 0, -1, 16.25); TEST_UNION(case_recursive_boxes_70, 1, 0, -1, 25.0); TEST_UNION(case_recursive_boxes_71, 4, 2, -1, 15.75); + TEST_UNION(case_recursive_boxes_72, 10, 0, -1, 15.0); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 1849ddca84481814bd7606a0db28784a8cb588c9 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 13 Sep 2017 13:08:11 +0200 Subject: [PATCH 111/188] [overlay] need to mark cc turns as traversed to avoid regression in cases 75 and 76 which otherwise would have a duplicate ring in union --- .../algorithms/detail/overlay/traversal.hpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index 136b3c12f..36adff10c 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -147,13 +147,27 @@ struct traversal || op.visited.started() || op.visited.finished() ) { - ring_identifier const ring_id + ring_identifier const ring_id ( op.seg_id.source_index, op.seg_id.multi_index, op.seg_id.ring_index ); turn_info_map[ring_id].has_traversed_turn = true; + + if (op.operation == operation_continue) + { + // Continue operations should mark the other operation + // as traversed too + turn_operation_type& other_op = turn.operations[1 - i]; + ring_identifier const other_ring_id + ( + other_op.seg_id.source_index, + other_op.seg_id.multi_index, + other_op.seg_id.ring_index + ); + turn_info_map[other_ring_id].has_traversed_turn = true; + } } op.visited.finalize(); } From 33f97b8f2106afdc40c27c92ca672364421fb2e9 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 13 Sep 2017 13:35:58 +0200 Subject: [PATCH 112/188] [test] add new testcase also to difference/intersection --- test/algorithms/set_operations/difference/difference_multi.cpp | 2 ++ .../set_operations/intersection/intersection_multi.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 4fae334d4..fa3201cf8 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -390,6 +390,8 @@ void test_areal() TEST_DIFFERENCE_IGNORE(case_recursive_boxes_71, 6, 8.25, 7, 5.75, 7); #endif + TEST_DIFFERENCE(case_recursive_boxes_72, 6, 6.5, 7, 4.0, 10); + { ut_settings sym_settings; #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 8dcf4827c..d16bee8eb 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -333,6 +333,7 @@ void test_areal() #endif TEST_INTERSECTION(case_recursive_boxes_71, 3, -1, 1.75); + TEST_INTERSECTION(case_recursive_boxes_72, 8, -1, 4.5); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 800baaac14fd44f04fd405a692684cc2fb6c08cf Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 13 Sep 2017 16:05:59 +0200 Subject: [PATCH 113/188] [overlay] fix recent change, assign best_code (this avoids regression in buffer) --- include/boost/geometry/algorithms/detail/overlay/traversal.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index 36adff10c..1d24e52af 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -485,6 +485,7 @@ struct traversal if (code > best_code) { // It is 1 or higher and matching better than previous + best_code = code; turn_index = ranked_point.turn_index; op_index = ranked_point.operation_index; result = true; From 2bc7063e73741db292ca1d0b8d19192dd6fa2420 Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Wed, 13 Sep 2017 18:06:53 +0300 Subject: [PATCH 114/188] [formulas] [distance] Use mean radius formula in distance point segment formula --- include/boost/geometry/formulas/distance_point_segment.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index bf61b38bc..4bd6a9a48 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -12,6 +12,7 @@ #define BOOST_GEOMETRY_FORMULAS_CROSS_TRACK_GEO_HPP #include +#include #ifndef BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS #define BOOST_GEOMETRY_DETAIL_POINT_SEGMENT_DISTANCE_MAX_STEPS 100 @@ -82,8 +83,7 @@ public: CT lon3, CT lat3, //query point p3 Spheroid const& spheroid) { - CT earth_radius = (CT(2) * spheroid.get_radius<1>() - + spheroid.get_radius<2>()) / CT(3); + CT const earth_radius = geometry::formula::mean_radius(spheroid); result_distance_point_segment result; From d139bfd597424d4c0cf7872884549b5d71f72011 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Thu, 14 Sep 2017 21:19:01 +0200 Subject: [PATCH 115/188] [formulas][strategies] Add authalic_radius_sqr() formula and use it in geographic area strategy. --- .../geometry/formulas/authalic_radius_sqr.hpp | 96 +++++++++++++++++++ .../geometry/strategies/geographic/area.hpp | 30 ++---- 2 files changed, 103 insertions(+), 23 deletions(-) create mode 100644 include/boost/geometry/formulas/authalic_radius_sqr.hpp diff --git a/include/boost/geometry/formulas/authalic_radius_sqr.hpp b/include/boost/geometry/formulas/authalic_radius_sqr.hpp new file mode 100644 index 000000000..54da31761 --- /dev/null +++ b/include/boost/geometry/formulas/authalic_radius_sqr.hpp @@ -0,0 +1,96 @@ +// Boost.Geometry + +// Copyright (c) 2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// 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) + +#ifndef BOOST_GEOMETRY_FORMULAS_AUTHALIC_RADIUS_SQR_HPP +#define BOOST_GEOMETRY_FORMULAS_AUTHALIC_RADIUS_SQR_HPP + +#include +#include +#include + +#include + +#include + +#include + +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace formula_dispatch +{ + +template ::type> +struct authalic_radius_sqr + : not_implemented +{}; + +template +struct authalic_radius_sqr +{ + static inline ResultType apply(Geometry const& geometry) + { + return math::sqr(get_radius<0>(geometry)); + } +}; + +template +struct authalic_radius_sqr +{ + static inline ResultType apply(Geometry const& geometry) + { + ResultType const a2 = math::sqr(get_radius<0>(geometry)); + ResultType const e2 = formula::eccentricity_sqr(geometry); + + return apply(a2, e2); + } + + static inline ResultType apply(ResultType const& a2, ResultType const& e2) + { + ResultType const c0 = 0; + + if (math::equals(e2, c0)) + { + return a2; + } + + ResultType const e = math::sqrt(e2); + ResultType const c2 = 2; + + //ResultType const b2 = math::sqr(get_radius<2>(geometry)); + //return a2 / c2 + b2 * boost::math::atanh(e) / (c2 * e); + + ResultType const c1 = 1; + return (a2 / c2) * ( c1 + (c1 - e2) * boost::math::atanh(e) / e ); + } +}; + +} // namespace formula_dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace formula +{ + +template +inline ResultType authalic_radius_sqr(Geometry const& geometry) +{ + return formula_dispatch::authalic_radius_sqr::apply(geometry); +} + +} // namespace formula +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_AUTHALIC_RADIUS_SQR_HPP diff --git a/include/boost/geometry/strategies/geographic/area.hpp b/include/boost/geometry/strategies/geographic/area.hpp index 44dc2e694..7b03abf4f 100644 --- a/include/boost/geometry/strategies/geographic/area.hpp +++ b/include/boost/geometry/strategies/geographic/area.hpp @@ -15,12 +15,11 @@ #include #include -#include +#include +#include #include -#include - namespace boost { namespace geometry { @@ -88,31 +87,16 @@ protected : inline spheroid_constants(Spheroid const& spheroid) : m_spheroid(spheroid) , m_a2(math::sqr(get_radius<0>(spheroid))) - , m_e2(formula::flattening(spheroid) - * (CT(2.0) - CT(formula::flattening(spheroid)))) + , m_e2(formula::eccentricity_sqr(spheroid)) , m_ep2(m_e2 / (CT(1.0) - m_e2)) , m_ep(math::sqrt(m_ep2)) - , m_c2(authalic_radius(spheroid, m_a2, m_e2)) + , m_c2(formula_dispatch::authalic_radius_sqr + < + CT, Spheroid, srs_spheroid_tag + >::apply(m_a2, m_e2)) {} }; - static inline CT authalic_radius(Spheroid const& sph, CT const& a2, CT const& e2) - { - CT const c0 = 0; - - if (math::equals(e2, c0)) - { - return a2; - } - - CT const sqrt_e2 = math::sqrt(e2); - CT const c2 = 2; - - return (a2 / c2) + - ((math::sqr(get_radius<2>(sph)) * boost::math::atanh(sqrt_e2)) - / (c2 * sqrt_e2)); - } - struct area_sums { CT m_excess_sum; From bc60e0737b12a3abe0363f6661b8a16cea3c1fb0 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 17 Sep 2017 21:13:24 +0200 Subject: [PATCH 116/188] [sections] Fix section_functions for boxes covering more than half of the globe. --- .../detail/sections/section_functions.hpp | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/sections/section_functions.hpp b/include/boost/geometry/algorithms/detail/sections/section_functions.hpp index 67df3060c..d283784e2 100644 --- a/include/boost/geometry/algorithms/detail/sections/section_functions.hpp +++ b/include/boost/geometry/algorithms/detail/sections/section_functions.hpp @@ -19,6 +19,9 @@ #include #include +// For spherical/geographic longitudes covered_by point/box +#include + namespace boost { namespace geometry { @@ -61,17 +64,33 @@ struct preceding_check<0, Geometry, spherical_tag> calc_t const c0 = 0; + calc_t const value = get<0>(point); + calc_t const other_min = get(other_box); + calc_t const other_max = get(other_box); + + bool const pt_covered = strategy::within::covered_by_range + < + Point, 0, spherical_tag + >::apply(value, + other_min, + other_max); + + if (pt_covered) + { + return false; + } + if (dir == 1) { calc_t const diff_min = math::longitude_distance_signed < units_t, calc_t - >(get(other_box), get<0>(point)); + >(other_min, value); calc_t const diff_min_min = math::longitude_distance_signed < units_t, calc_t - >(get(other_box), get(point_box)); + >(other_min, get(point_box)); return diff_min < c0 && diff_min_min <= c0 && diff_min_min <= diff_min; } @@ -80,12 +99,12 @@ struct preceding_check<0, Geometry, spherical_tag> calc_t const diff_max = math::longitude_distance_signed < units_t, calc_t - >(get(other_box), get<0>(point)); + >(other_max, value); calc_t const diff_max_max = math::longitude_distance_signed < units_t, calc_t - >(get(other_box), get(point_box)); + >(other_max, get(point_box)); return diff_max > c0 && diff_max_max >= c0 && diff_max <= diff_max_max; } From f93e3ad806b3231acbffbddfe1e41a52e16dbf9b Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sun, 17 Sep 2017 21:14:18 +0200 Subject: [PATCH 117/188] [test][union][overlay] Add geographic A/A test case (big polygons, one covering pole). --- .../overlay/get_turns_areal_areal_sph.cpp | 6 +++- .../algorithms/set_operations/union/union.cpp | 33 +++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/test/algorithms/overlay/get_turns_areal_areal_sph.cpp b/test/algorithms/overlay/get_turns_areal_areal_sph.cpp index 18544cd8f..4905e4b53 100644 --- a/test/algorithms/overlay/get_turns_areal_areal_sph.cpp +++ b/test/algorithms/overlay/get_turns_areal_areal_sph.cpp @@ -1,7 +1,7 @@ // Boost.Geometry // Unit Test -// Copyright (c) 2016, Oracle and/or its affiliates. +// Copyright (c) 2016-2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -62,6 +62,10 @@ void test_all() expected("mcc")("cui")); test_geometry(case_18_sph[0], case_18_sph[1], expected("mcc")("ccc")("ccc")("cui")); + + test_geometry("POLYGON((16 15,-132 10,-56 89,67 5,16 15))", + "POLYGON((101 49,12 40,-164 10,117 0,101 49))", + expected("iiu")("iui")); } int test_main(int, char* []) diff --git a/test/algorithms/set_operations/union/union.cpp b/test/algorithms/set_operations/union/union.cpp index a0e13f82d..40644c6c0 100644 --- a/test/algorithms/set_operations/union/union.cpp +++ b/test/algorithms/set_operations/union/union.cpp @@ -5,8 +5,8 @@ // Copyright (c) 2008-2016 Bruno Lalande, Paris, France. // Copyright (c) 2009-2016 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2016. -// Modifications copyright (c) 2016, Oracle and/or its affiliates. +// This file was modified by Oracle on 2016,2017. +// Modifications copyright (c) 2016-2017, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library @@ -482,6 +482,33 @@ void test_areal() 1, 1, -1, 220.5); } +void test_geographic() +{ + typedef bg::model::point > point; + typedef bg::model::polygon polygon; + typedef bg::model::multi_polygon multipolygon; + + bg::srs::spheroid sph(6378137.0000000000, 6356752.3142451793); + + bg::strategy::intersection::geographic_segments<> is(sph); + bg::strategy::area::geographic as(sph); + + polygon p1, p2; + + boost::geometry::read_wkt("POLYGON((16 15,-132 10,-56 89,67 5,16 15))", p1); + boost::geometry::read_wkt("POLYGON((101 49,12 40,-164 10,117 0,101 49))", p2); + + multipolygon result; + boost::geometry::union_(p1, p2, result, is); + + double result_area = bg::area(result, as); + + BOOST_CHECK(boost::size(result) == 1 + && boost::size(bg::exterior_ring(bg::range::at(result, 0))) == 9 + && boost::size(bg::interior_rings(bg::range::at(result, 0))) == 0); + BOOST_CHECK_CLOSE(result_area, 144265751613509.06, 0.001); +} + template void test_all() { @@ -557,5 +584,7 @@ int test_main(int, char* []) #endif #endif + test_geographic(); + return 0; } From 02de7cc212e462ae9b0cb7b69713660b2681de40 Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Tue, 19 Sep 2017 15:13:33 +0300 Subject: [PATCH 118/188] [formulas] [strategies] Fix cross track distance for both spherical and geographic --- .../formulas/distance_point_segment.hpp | 100 ++++++++++++------ .../spherical/distance_cross_track.hpp | 54 +++++++--- .../algorithms/distance/distance_geo_pl_l.cpp | 5 + test/algorithms/distance/distance_se_pl_l.cpp | 9 ++ 4 files changed, 118 insertions(+), 50 deletions(-) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index 4bd6a9a48..4fc4e7ad4 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -43,6 +43,7 @@ public: typedef Inverse inverse_distance_quantities_type; typedef Inverse inverse_azimuth_type; + typedef Inverse inverse_azimuth_reverse_type; typedef Direct direct_distance_type; struct result_distance_point_segment @@ -110,6 +111,9 @@ public: //TODO: use the meridian distance when it'll be available if (math::equals(lat1, 0) && math::equals(lat2, 0)) { +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "Equatorial segment" << std::endl; +#endif if (lon3 <= lon1) { return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); @@ -121,51 +125,52 @@ public: return non_iterative_case(lon3, lat1, lon3, lat3, spheroid); } - //segment on meridian - if (math::equals(lon1, lon2)) + //TODO no quantities needed ? + CT d1 = inverse_distance_quantities_type::apply(lon1, lat1, + lon3, lat3, spheroid).distance; + CT d3 = inverse_distance_quantities_type::apply(lon1, lat1, + lon2, lat2, spheroid).distance; + + if (geometry::math::equals(d3, 0.0)) { - if (lat1 > lat2) + // "Degenerate" segment, return either d1 or d2 + result_distance_point_segment result; + result.distance = d1; + if (EnableClosestPoint) { - std::swap(lat1, lat2); + result.closest_point_lon = lon1; + result.closest_point_lat = lat1; } - if (lat3 <= lat1) - { - return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); - } - if (lat3 >= lat2) - { - return non_iterative_case(lon2, lat2, lon3, lat3, spheroid); - } - return non_iterative_case(lon1, lat3, lon3, lat3, spheroid); + return result; } - // Easy cases - if (lon3 <= lon1 && lat3 <= lat1) - { - return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); - } - if (lon3 >= lon2 && lat3 <= lat2) - { - return non_iterative_case(lon2, lat2, lon3, lat3, spheroid); - } + CT d2 = inverse_distance_quantities_type::apply(lon2, lat2, + lon3, lat3, spheroid).distance; // Compute a1 (GEO) - CT a1 = inverse_azimuth_type::apply(lon1, lat1, lon2, lat2, spheroid).azimuth; + geometry::formula::result_inverse res12 = + inverse_azimuth_reverse_type::apply(lon1, lat1, lon2, lat2, spheroid); + CT a1 = res12.azimuth; CT a13 = inverse_azimuth_type::apply(lon1, lat1, lon3, lat3, spheroid).azimuth; - CT a312 = a1 - a13; + CT a312 = a13 - a1; - if (a312 > half_pi) + if (geometry::math::equals(a312, 0.0)) { - // projection of p3 on geodesic spanned by segment (p1,p2) fall - // outside of segment on the side of p1 - return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "point on segment" << std::endl; +#endif + result_distance_point_segment result; + result.distance = 0.0; + if (EnableClosestPoint) + { + result.closest_point_lon = lon3; + result.closest_point_lat = lat3; + } + return result; } - CT a2 = pi + inverse_azimuth_type::apply(lon2, lat2, lon1, lat1, spheroid).azimuth; - CT a23 = inverse_azimuth_type::apply(lon2, lat2, lon3, lat3, spheroid).azimuth; - - CT a321 = a2 - a23; + CT projection1 = cos( a312 ) * d1 / d3; #ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG std::cout << "segment=(" << lon1 * math::r2d(); @@ -177,12 +182,41 @@ public: std::cout << ")\na1=" << a1 * math::r2d() << std::endl; std::cout << "a13=" << a13 * math::r2d() << std::endl; std::cout << "a312=" << a312 * math::r2d() << std::endl; + std::cout << "cos(a312)=" << cos(a312) << std::endl; +#endif + //if (a312 > half_pi) + if (projection1 < 0.0) + { +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "projection closer to p1" << std::endl; +#endif + // projection of p3 on geodesic spanned by segment (p1,p2) fall + // outside of segment on the side of p1 + return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); + } + +// CT a2 = pi + inverse_azimuth_type::apply(lon2, lat2, lon1, lat1, spheroid).azimuth; +// CT a23 = inverse_azimuth_type::apply(lon2, lat2, lon3, lat3, spheroid).azimuth; + + CT a2 = res12.reverse_azimuth - pi; + CT a23 = inverse_azimuth_type::apply(lon2, lat2, lon3, lat3, spheroid).azimuth; + + CT a321 = a23 - a2; + +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG std::cout << "a2=" << a2 * math::r2d() << std::endl; std::cout << "a23=" << a13 * math::r2d() << std::endl; std::cout << "a321=" << a321 * math::r2d() << std::endl; + std::cout << "cos(a321)=" << cos(a321) << std::endl; #endif - if (a321 < half_pi) + CT projection2 = cos( a321 ) * d2 / d3; + + //if (a321 < half_pi) + if (projection2 < 0.0) { +#ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG + std::cout << "projection closer to p2" << std::endl; +#endif // projection of p3 on geodesic spanned by segment (p1,p2) fall // outside of segment on the side of p2 return non_iterative_case(lon2, lat2, lon3, lat3, spheroid); diff --git a/include/boost/geometry/strategies/spherical/distance_cross_track.hpp b/include/boost/geometry/strategies/spherical/distance_cross_track.hpp index 7daafa4a1..8f7df9b2a 100644 --- a/include/boost/geometry/strategies/spherical/distance_cross_track.hpp +++ b/include/boost/geometry/strategies/spherical/distance_cross_track.hpp @@ -373,19 +373,6 @@ public : typedef typename return_type::type return_type; -#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK - std::cout << "Course " << dsv(sp1) << " to " << dsv(p) << " " - << crs_AD * geometry::math::r2d() << std::endl; - std::cout << "Course " << dsv(sp1) << " to " << dsv(sp2) << " " - << crs_AB * geometry::math::r2d() << std::endl; - std::cout << "Course " << dsv(sp2) << " to " << dsv(p) << " " - << crs_BD * geometry::math::r2d << std::endl; - std::cout << "Projection AD-AB " << projection1 << " : " - << d_crs1 * geometry::math::r2d() << std::endl; - std::cout << "Projection BD-BA " << projection2 << " : " - << d_crs2 * geometry::math::r2d() << std::endl; -#endif - // http://williams.best.vwh.net/avform.htm#XTE return_type d1 = m_strategy.apply(sp1, p); return_type d3 = m_strategy.apply(sp1, sp2); @@ -398,10 +385,25 @@ public : return_type d2 = m_strategy.apply(sp2, p); - return_type crs_AD = geometry::detail::course(sp1, p); - return_type crs_AB = geometry::detail::course(sp1, sp2); - return_type crs_BA = crs_AB - geometry::math::pi(); - return_type crs_BD = geometry::detail::course(sp2, p); + return_type lon1 = geometry::get_as_radian<0>(sp1); + return_type lat1 = geometry::get_as_radian<1>(sp1); + return_type lon2 = geometry::get_as_radian<0>(sp2); + return_type lat2 = geometry::get_as_radian<1>(sp2); + return_type lon = geometry::get_as_radian<0>(p); + return_type lat = geometry::get_as_radian<1>(p); + + return_type crs_AD = geometry::formula::spherical_azimuth + (lon1, lat1, lon, lat).azimuth; + + geometry::formula::result_spherical result = + geometry::formula::spherical_azimuth + (lon1, lat1, lon2, lat2); + return_type crs_AB = result.azimuth; + return_type crs_BA = result.reverse_azimuth - geometry::math::pi(); + + return_type crs_BD = geometry::formula::spherical_azimuth + (lon2, lat2, lon, lat).azimuth; + return_type d_crs1 = crs_AD - crs_AB; return_type d_crs2 = crs_BD - crs_BA; @@ -409,6 +411,24 @@ public : return_type projection1 = cos( d_crs1 ) * d1 / d3; return_type projection2 = cos( d_crs2 ) * d2 / d3; +#ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK + std::cout << "Course " << dsv(sp1) << " to " << dsv(p) << " " + << crs_AD * geometry::math::r2d() << std::endl; + std::cout << "Course " << dsv(sp1) << " to " << dsv(sp2) << " " + << crs_AB * geometry::math::r2d() << std::endl; + std::cout << "Course " << dsv(sp2) << " to " << dsv(sp1) << " " + << crs_BA * geometry::math::r2d() << std::endl; + std::cout << "Course " << dsv(sp2) << " to " << dsv(p) << " " + << crs_BD * geometry::math::r2d() << std::endl; + std::cout << "Projection AD-AB " << projection1 << " : " + << d_crs1 * geometry::math::r2d() << std::endl; + std::cout << "Projection BD-BA " << projection2 << " : " + << d_crs2 * geometry::math::r2d() << std::endl; + std::cout << " d1: " << (d1 ) + << " d2: " << (d2 ) + << std::endl; +#endif + if (projection1 > 0.0 && projection2 > 0.0) { #ifdef BOOST_GEOMETRY_DEBUG_CROSS_TRACK diff --git a/test/algorithms/distance/distance_geo_pl_l.cpp b/test/algorithms/distance/distance_geo_pl_l.cpp index 5ea565fe8..5efecc171 100644 --- a/test/algorithms/distance/distance_geo_pl_l.cpp +++ b/test/algorithms/distance/distance_geo_pl_l.cpp @@ -249,6 +249,11 @@ void test_distance_point_segment(Strategy_pp const& strategy_pp, "SEGMENT(2 2,2 4)", pp_distance("POINT(2.5 5)", "POINT(2 4)", strategy_pp), strategy_ps); + tester::apply("p-s-mer4", + "POINT(1 80)", + "SEGMENT(0 0,0 90)", + pp_distance("POINT(1 80)", "POINT(0 80.00149225834545)", andoyer_pp()), + andoyer_strategy()); // very small distances to segment tester::apply("p-s-07", diff --git a/test/algorithms/distance/distance_se_pl_l.cpp b/test/algorithms/distance/distance_se_pl_l.cpp index f298bae25..5b3dfd5a9 100644 --- a/test/algorithms/distance/distance_se_pl_l.cpp +++ b/test/algorithms/distance/distance_se_pl_l.cpp @@ -10,6 +10,7 @@ // http://www.boost.org/users/license.html #include +#define BOOST_GEOMETRY_TEST_DEBUG #ifndef BOOST_TEST_MODULE #define BOOST_TEST_MODULE test_distance_spherical_equatorial_pl_l @@ -160,6 +161,14 @@ void test_distance_point_segment(Strategy const& strategy) "POINT(3.5 3)", strategy), strategy); + tester::apply("p-s-07", + "POINT(0 0)", + "SEGMENT(0 10,10 10)", + ps_distance("POINT(0 0)", "SEGMENT(10 10,0 10)", strategy), + pp_comparable_distance("POINT(0 0)", + "POINT(0 10)", + strategy), + strategy); // very small distances to segment tester::apply("p-s-07", "POINT(90 1e-3)", From 3b05e5ca4604aeab7e716a3e52ee95b39a5acdd6 Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Tue, 19 Sep 2017 17:25:47 +0300 Subject: [PATCH 119/188] [strategies] [formulas] Replace course algorithm by azimuth formula --- .../geographic/distance_cross_track.hpp | 2 -- .../spherical/distance_cross_track.hpp | 7 +++-- .../spherical/side_by_cross_track.hpp | 26 +++++++++++++++---- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/include/boost/geometry/strategies/geographic/distance_cross_track.hpp b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp index 295858f6e..4a220d1c9 100644 --- a/include/boost/geometry/strategies/geographic/distance_cross_track.hpp +++ b/include/boost/geometry/strategies/geographic/distance_cross_track.hpp @@ -23,8 +23,6 @@ #include #include -#include - #include #include #include diff --git a/include/boost/geometry/strategies/spherical/distance_cross_track.hpp b/include/boost/geometry/strategies/spherical/distance_cross_track.hpp index 8f7df9b2a..82a2fb3d6 100644 --- a/include/boost/geometry/strategies/spherical/distance_cross_track.hpp +++ b/include/boost/geometry/strategies/spherical/distance_cross_track.hpp @@ -2,9 +2,10 @@ // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014, Oracle and/or its affiliates. +// This file was modified by Oracle on 2014-2017. +// Modifications copyright (c) 2014-2017, Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, @@ -26,8 +27,6 @@ #include #include -#include - #include #include #include diff --git a/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp b/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp index 3f7be0555..1f14ce9aa 100644 --- a/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp +++ b/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp @@ -2,9 +2,10 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014, Oracle and/or its affiliates. +// This file was modified by Oracle on 2014-2017. +// Modifications copyright (c) 2014-2017, Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, @@ -18,7 +19,7 @@ #include #include -#include +#include #include #include @@ -59,8 +60,23 @@ public : >::type calc_t; calc_t d1 = 0.001; // m_strategy.apply(sp1, p); - calc_t crs_AD = geometry::detail::course(p1, p); - calc_t crs_AB = geometry::detail::course(p1, p2); + + calc_t lon1 = geometry::get_as_radian<0>(p1); + calc_t lat1 = geometry::get_as_radian<1>(p1); + calc_t lon2 = geometry::get_as_radian<0>(p2); + calc_t lat2 = geometry::get_as_radian<1>(p2); + calc_t lon = geometry::get_as_radian<0>(p); + calc_t lat = geometry::get_as_radian<1>(p); + + calc_t crs_AD = geometry::formula::spherical_azimuth + (lon1, lat1, lon, lat).azimuth; + + //calc_t crs_AD = geometry::detail::course(p1, p); + + calc_t crs_AB = geometry::formula::spherical_azimuth + (lon1, lat1, lon2, lat2).azimuth; + + //calc_t crs_AB = geometry::detail::course(p1, p2); calc_t XTD = asin(sin(d1) * sin(crs_AD - crs_AB)); return math::equals(XTD, 0) ? 0 : XTD < 0 ? 1 : -1; From 19eda49034fc4c594c6b920c8e350ee3205f34cc Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Tue, 19 Sep 2017 17:28:08 +0300 Subject: [PATCH 120/188] [strategies] [formulas] Some code cleaning --- include/boost/geometry/formulas/distance_point_segment.hpp | 5 ----- .../geometry/strategies/spherical/side_by_cross_track.hpp | 3 --- 2 files changed, 8 deletions(-) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index 4fc4e7ad4..790823f4a 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -184,7 +184,6 @@ public: std::cout << "a312=" << a312 * math::r2d() << std::endl; std::cout << "cos(a312)=" << cos(a312) << std::endl; #endif - //if (a312 > half_pi) if (projection1 < 0.0) { #ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG @@ -195,9 +194,6 @@ public: return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); } -// CT a2 = pi + inverse_azimuth_type::apply(lon2, lat2, lon1, lat1, spheroid).azimuth; -// CT a23 = inverse_azimuth_type::apply(lon2, lat2, lon3, lat3, spheroid).azimuth; - CT a2 = res12.reverse_azimuth - pi; CT a23 = inverse_azimuth_type::apply(lon2, lat2, lon3, lat3, spheroid).azimuth; @@ -211,7 +207,6 @@ public: #endif CT projection2 = cos( a321 ) * d2 / d3; - //if (a321 < half_pi) if (projection2 < 0.0) { #ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG diff --git a/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp b/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp index 1f14ce9aa..2b195e96f 100644 --- a/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp +++ b/include/boost/geometry/strategies/spherical/side_by_cross_track.hpp @@ -71,12 +71,9 @@ public : calc_t crs_AD = geometry::formula::spherical_azimuth (lon1, lat1, lon, lat).azimuth; - //calc_t crs_AD = geometry::detail::course(p1, p); - calc_t crs_AB = geometry::formula::spherical_azimuth (lon1, lat1, lon2, lat2).azimuth; - //calc_t crs_AB = geometry::detail::course(p1, p2); calc_t XTD = asin(sin(d1) * sin(crs_AD - crs_AB)); return math::equals(XTD, 0) ? 0 : XTD < 0 ? 1 : -1; From d9f3641795fc370b5508974c7f74db1b0ec32d0d Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Tue, 19 Sep 2017 18:01:43 +0300 Subject: [PATCH 121/188] [formulas] [strategies] [tests] Code simplification for special cases and some unit test cases --- .../formulas/distance_point_segment.hpp | 91 +++++++++---------- .../algorithms/distance/distance_geo_pl_l.cpp | 19 ++++ 2 files changed, 64 insertions(+), 46 deletions(-) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index 790823f4a..1462fd8dd 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -42,6 +42,7 @@ class distance_point_segment{ public: typedef Inverse inverse_distance_quantities_type; + typedef Inverse inverse_distance_type; typedef Inverse inverse_azimuth_type; typedef Inverse inverse_azimuth_reverse_type; typedef Direct direct_distance_type; @@ -59,24 +60,34 @@ public: CT closest_point_lat; }; + result_distance_point_segment + static inline non_iterative_case(CT lon, CT lat, CT distance) + { + result_distance_point_segment result; + result.distance = distance; + + if (EnableClosestPoint) + { + result.closest_point_lon = lon; + result.closest_point_lat = lat; + } + return result; + } + template result_distance_point_segment static inline non_iterative_case(CT lon1, CT lat1, //p1 CT lon2, CT lat2, //p2 Spheroid const& spheroid) { - result_distance_point_segment result; - result.distance = inverse_distance_quantities_type::apply(lon1, lat1, - lon2, lat2, - spheroid).distance; - if (EnableClosestPoint) - { - result.closest_point_lon = lon1; - result.closest_point_lat = lat1; - } - return result; + CT distance = inverse_distance_quantities_type::apply(lon1, lat1, + lon2, lat2, + spheroid).distance; + return non_iterative_case(lon1, lat1, distance); } + + template result_distance_point_segment static inline apply(CT lon1, CT lat1, //p1 @@ -92,6 +103,7 @@ public: CT const f = flattening(spheroid); CT const pi = math::pi(); CT const half_pi = pi / CT(2); + CT const c0 = CT(0); // Convert to radians lon1 = math::as_radian(lon1); @@ -125,49 +137,33 @@ public: return non_iterative_case(lon3, lat1, lon3, lat3, spheroid); } - //TODO no quantities needed ? - CT d1 = inverse_distance_quantities_type::apply(lon1, lat1, - lon3, lat3, spheroid).distance; - CT d3 = inverse_distance_quantities_type::apply(lon1, lat1, - lon2, lat2, spheroid).distance; + CT d1 = inverse_distance_type::apply(lon1, lat1, + lon3, lat3, spheroid).distance; + CT d3 = inverse_distance_type::apply(lon1, lat1, + lon2, lat2, spheroid).distance; - if (geometry::math::equals(d3, 0.0)) + if (geometry::math::equals(d3, c0)) { - // "Degenerate" segment, return either d1 or d2 - result_distance_point_segment result; - result.distance = d1; - if (EnableClosestPoint) - { - result.closest_point_lon = lon1; - result.closest_point_lat = lat1; - } - return result; + return non_iterative_case(lon1, lat2, d1); } - CT d2 = inverse_distance_quantities_type::apply(lon2, lat2, - lon3, lat3, spheroid).distance; + CT d2 = inverse_distance_type::apply(lon2, lat2, + lon3, lat3, spheroid).distance; - // Compute a1 (GEO) + // Compute a12 (GEO) geometry::formula::result_inverse res12 = inverse_azimuth_reverse_type::apply(lon1, lat1, lon2, lat2, spheroid); - CT a1 = res12.azimuth; + CT a12 = res12.azimuth; CT a13 = inverse_azimuth_type::apply(lon1, lat1, lon3, lat3, spheroid).azimuth; - CT a312 = a13 - a1; + CT a312 = a13 - a12; - if (geometry::math::equals(a312, 0.0)) + if (geometry::math::equals(a312, c0)) { #ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG std::cout << "point on segment" << std::endl; #endif - result_distance_point_segment result; - result.distance = 0.0; - if (EnableClosestPoint) - { - result.closest_point_lon = lon3; - result.closest_point_lat = lat3; - } - return result; + return non_iterative_case(lon3, lat3, c0); } CT projection1 = cos( a312 ) * d1 / d3; @@ -179,7 +175,7 @@ public: std::cout << "," << lat2 * math::r2d(); std::cout << ")\np=(" << lon3 * math::r2d(); std::cout << "," << lat3 * math::r2d(); - std::cout << ")\na1=" << a1 * math::r2d() << std::endl; + std::cout << ")\na1=" << a12 * math::r2d() << std::endl; std::cout << "a13=" << a13 * math::r2d() << std::endl; std::cout << "a312=" << a312 * math::r2d() << std::endl; std::cout << "cos(a312)=" << cos(a312) << std::endl; @@ -194,13 +190,13 @@ public: return non_iterative_case(lon1, lat1, lon3, lat3, spheroid); } - CT a2 = res12.reverse_azimuth - pi; + CT a21 = res12.reverse_azimuth - pi; CT a23 = inverse_azimuth_type::apply(lon2, lat2, lon3, lat3, spheroid).azimuth; - CT a321 = a23 - a2; + CT a321 = a23 - a21; #ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG - std::cout << "a2=" << a2 * math::r2d() << std::endl; + std::cout << "a21=" << a21 * math::r2d() << std::endl; std::cout << "a23=" << a13 * math::r2d() << std::endl; std::cout << "a321=" << a321 * math::r2d() << std::endl; std::cout << "cos(a321)=" << cos(a321) << std::endl; @@ -218,7 +214,10 @@ public: } // Guess s14 (SPHERICAL) - typedef geometry::model::point > point; + typedef geometry::model::point< + CT, 2, + geometry::cs::spherical_equatorial + > point; CT bet1 = atan((1 - f) * tan(lon1)); CT bet2 = atan((1 - f) * tan(lon2)); @@ -254,7 +253,7 @@ public: prev_distance = res34.distance; // Solve the direct problem to find p4 (GEO) - res14 = direct_distance_type::apply(lon1, lat1, s14, a1, spheroid); + res14 = direct_distance_type::apply(lon1, lat1, s14, a12, spheroid); // Solve an inverse problem to find g4 // g4 is the angle between segment (p1,p2) and segment (p3,p4) that meet on p4 (GEO) @@ -347,7 +346,7 @@ public: geometry::formula::result_direct res4 = direct_distance_type::apply(get<0>(p4), get<1>(p4), .04, a4, spheroid); CT p4_plus = inverse_distance_quantities_type::apply(res4.lon2, res4.lat2, lon3, lat3, spheroid).distance; - geometry::formula::result_direct res1 = direct_distance_type::apply(lon1, lat1, s14-.04, a1, spheroid); + geometry::formula::result_direct res1 = direct_distance_type::apply(lon1, lat1, s14-.04, a12, spheroid); CT p4_minus = inverse_distance_quantities_type::apply(res1.lon2, res1.lat2, lon3, lat3, spheroid).distance; std::cout << "s31=" << s31 << "\ns32=" << s32 diff --git a/test/algorithms/distance/distance_geo_pl_l.cpp b/test/algorithms/distance/distance_geo_pl_l.cpp index 5efecc171..f687ed293 100644 --- a/test/algorithms/distance/distance_geo_pl_l.cpp +++ b/test/algorithms/distance/distance_geo_pl_l.cpp @@ -109,6 +109,11 @@ void test_distance_point_segment(Strategy_pp const& strategy_pp, "SEGMENT(2 0,3 0)", 0, strategy_ps); + tester::apply("p-s-05", + "POINT(2.5 0)", + "SEGMENT(2 0,3 0)", + 0, + strategy_ps); tester::apply("p-s-06", "POINT(3.5 3)", "SEGMENT(2 0,3 0)", @@ -255,6 +260,20 @@ void test_distance_point_segment(Strategy_pp const& strategy_pp, pp_distance("POINT(1 80)", "POINT(0 80.00149225834545)", andoyer_pp()), andoyer_strategy()); + //degenerate segment + tester::apply("p-s-deg", + "POINT(1 80)", + "SEGMENT(0 0,0 0)", + pp_distance("POINT(0 0)", "POINT(1 80)", strategy_pp), + strategy_ps); + + //point on segment + tester::apply("p-s-deg", + "POINT(0 80)", + "SEGMENT(0 0,0 90)", + 0, + strategy_ps); + // very small distances to segment tester::apply("p-s-07", "POINT(90 1e-3)", From 31f1872e46200eaa6fac6322f65b80ec5b85c982 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 20 Sep 2017 10:21:50 +0200 Subject: [PATCH 122/188] [overlay] fix debug information --- include/boost/geometry/algorithms/detail/overlay/overlay.hpp | 1 + test/algorithms/overlay/overlay.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index ba0fcfbe4..bf1028f28 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -342,6 +342,7 @@ std::cout << "traverse" << std::endl; clusters, visitor ); + visitor.visit_turns(3, turns); get_ring_turn_info(turn_info_per_ring, turns, clusters); diff --git a/test/algorithms/overlay/overlay.cpp b/test/algorithms/overlay/overlay.cpp index 8303afa7b..795325d92 100644 --- a/test/algorithms/overlay/overlay.cpp +++ b/test/algorithms/overlay/overlay.cpp @@ -88,7 +88,7 @@ struct map_visitor m_mapper.map(turn.point, "fill:rgb(0,128,255);" // Blueish "stroke:rgb(0,0,0);stroke-width:1", 3); break; - case 2 : + case 3 : label_turn(index, turn); break; } From af0ca5525bebdb3e16134f398e984ba5cbfe871a Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 20 Sep 2017 10:22:10 +0200 Subject: [PATCH 123/188] [buffer] fix debug SVG --- test/algorithms/buffer/test_buffer_svg.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/algorithms/buffer/test_buffer_svg.hpp b/test/algorithms/buffer/test_buffer_svg.hpp index 428a18714..67570e462 100644 --- a/test/algorithms/buffer/test_buffer_svg.hpp +++ b/test/algorithms/buffer/test_buffer_svg.hpp @@ -25,6 +25,7 @@ #include #include +#include inline char piece_type_char(bg::strategy::buffer::piece_type const& type) From 35e426700ce427cc45330cc1595e2064d9b97321 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 20 Sep 2017 11:04:51 +0200 Subject: [PATCH 124/188] [overlay] split methods to assign connected regions when all region ids are assigned --- .../overlay/traversal_switch_detector.hpp | 73 +++++++++++-------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index ebe0158f3..a55e56ccb 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -238,7 +238,7 @@ struct traversal_switch_detector } } - void assign_regions() + void assign_region_ids() { for (typename merge_map::const_iterator it = m_turns_per_ring.begin(); it != m_turns_per_ring.end(); ++it) @@ -259,40 +259,50 @@ struct traversal_switch_detector op.enriched.region_id = properties.region_id; } } - signed_size_type const& id0 = turn.operations[0].enriched.region_id; - signed_size_type const& id1 = turn.operations[1].enriched.region_id; - if (id0 != id1 && id0 != -1 && id1 != -1) + } + } + } + + void assign_connected_regions() + { + for (std::size_t turn_index = 0; turn_index < m_turns.size(); ++turn_index) + { + turn_type const& turn = m_turns[turn_index]; + + signed_size_type const& id0 = turn.operations[0].enriched.region_id; + signed_size_type const& id1 = turn.operations[1].enriched.region_id; + + if (id0 != id1 && id0 != -1 && id1 != -1) + { + // Force insertion + m_connected_regions[id0].region_id = id0; + m_connected_regions[id1].region_id = id1; + + connection_properties& prop0 = m_connected_regions[id0].connected_region_counts[id1]; + connection_properties& prop1 = m_connected_regions[id1].connected_region_counts[id0]; + + if (turn.cluster_id < 0) { - // Force insertion - m_connected_regions[id0].region_id = id0; - m_connected_regions[id1].region_id = id1; - - connection_properties& prop0 = m_connected_regions[id0].connected_region_counts[id1]; - connection_properties& prop1 = m_connected_regions[id1].connected_region_counts[id0]; - - if (turn.cluster_id < 0) + // Turn is not colocated, add reference to connection + prop0.count++; + prop1.count++; + } + else + { + // Turn is colocated, only add region reference if it was not yet registered + if (prop0.cluster_indices.count(turn.cluster_id) == 0) { - // Turn is not colocated, add reference to connection prop0.count++; + } + if (prop1.cluster_indices.count(turn.cluster_id) == 0) + { prop1.count++; } - else - { - // Turn is colocated, only add region reference if it was not yet registered - if (prop0.cluster_indices.count(turn.cluster_id) == 0) - { - prop0.count++; - } - if (prop1.cluster_indices.count(turn.cluster_id) == 0) - { - prop1.count++; - } - } - // Insert cluster-id (also -1 is inserted - reinsertion of - // same cluster id is OK) - prop0.cluster_indices.insert(turn.cluster_id); - prop1.cluster_indices.insert(turn.cluster_id); } + // Insert cluster-id (also -1 is inserted - reinsertion of + // same cluster id is OK) + prop0.cluster_indices.insert(turn.cluster_id); + prop1.cluster_indices.insert(turn.cluster_id); } } } @@ -438,7 +448,7 @@ struct traversal_switch_detector } } - // All rings having turns are in the map. Now iterate them + // All rings having turns are in turns/ring map. Process them. { signed_size_type new_region_id = 1; for (typename merge_map::iterator it @@ -447,7 +457,8 @@ struct traversal_switch_detector create_region(new_region_id, it->first, it->second); } - assign_regions(); + assign_region_ids(); + assign_connected_regions(); get_isolated_regions(); assign_isolation(); } From 79e0e5d258e07e55289e25c9ed87635838da09c1 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 20 Sep 2017 12:43:09 +0200 Subject: [PATCH 125/188] [overlay] for convenience and for next step, add is_clustered method --- .../detail/overlay/enrich_intersection_points.hpp | 5 ++--- .../algorithms/detail/overlay/handle_colocations.hpp | 7 ++++++- .../algorithms/detail/overlay/handle_self_turns.hpp | 2 +- .../geometry/algorithms/detail/overlay/overlay.hpp | 2 +- .../geometry/algorithms/detail/overlay/traversal.hpp | 12 ++++++------ .../detail/overlay/traversal_ring_creator.hpp | 2 +- .../detail/overlay/traversal_switch_detector.hpp | 6 +++--- .../geometry/algorithms/detail/overlay/turn_info.hpp | 6 +++++- 8 files changed, 25 insertions(+), 17 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index d74e7ae22..8118d99c7 100644 --- a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -118,7 +118,7 @@ inline void enrich_assign(Operations& operations, Turns& turns) // Cluster behaviour: next should point after cluster, unless // their seg_ids are not the same - while (turn.cluster_id != -1 + while (turn.is_clustered() && it->turn_index != next->turn_index && turn.cluster_id == turns[next->turn_index].cluster_id && op.seg_id == turns[next->turn_index].operations[next->operation_index].seg_id) @@ -355,12 +355,11 @@ inline void enrich_intersection_points(Turns& turns, } if (detail::overlay::is_self_turn(turn) - && turn.cluster_id < 0 + && ! turn.is_clustered() && ! turn.both(target_operation)) { // Only keep self-uu-turns or self-ii-turns turn.discarded = true; - turn.cluster_id = -1; continue; } diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp index beb42a9e0..47656ab7d 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp @@ -182,6 +182,7 @@ inline signed_size_type add_turn_to_cluster(Turn const& turn, if (cid0 == -1 && cid1 == -1) { + // Because of this, first cluster ID will be 1 ++cluster_id; add_cluster_id(turn.operations[0], cluster_per_segment, cluster_id); add_cluster_id(turn.operations[1], cluster_per_segment, cluster_id); @@ -315,7 +316,7 @@ inline void assign_cluster_to_turns(Turns& turns, if (it != cluster_per_segment.end()) { #if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS) - if (turn.cluster_id != -1 + if (turn.is_clustered() && turn.cluster_id != it->second) { std::cout << " CONFLICT " << std::endl; @@ -656,6 +657,10 @@ inline bool handle_colocations(Turns& turns, Clusters& clusters, > cluster_per_segment_type; cluster_per_segment_type cluster_per_segment; + + // Assign to zero, because of pre-increment later the cluster_id + // effectively starts with 1 + // (and can later be negated to use uniquely with turn_index) signed_size_type cluster_id = 0; for (typename map_type::const_iterator it = map.begin(); diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp index d8c48bd8c..633a03fd5 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp @@ -198,7 +198,7 @@ public : continue; } - if (turn.cluster_id >= 0 && turn.has_colocated_both) + if (turn.is_clustered() && turn.has_colocated_both) { // Don't delete a self-ii-turn colocated with another ii-turn // (for example #case_recursive_boxes_70) diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index bf1028f28..93e4d0741 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -153,7 +153,7 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C } // Check information in colocated turns - if (! cluster_checked && turn.cluster_id >= 0) + if (! cluster_checked && turn.is_clustered()) { check_colocation(has_blocked, turn.cluster_id, turns, clusters); cluster_checked = true; diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index 1d24e52af..d63f80016 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -219,7 +219,7 @@ struct traversal { op.visited.set_visited(); } - if (turn.cluster_id >= 0) + if (turn.is_clustered()) { set_visited_in_cluster(turn.cluster_id, op.enriched.rank); } @@ -280,7 +280,7 @@ struct traversal // It is not a dead end if there is an operation to continue, or of // there is a cluster (assuming for now we can get out of the cluster) - return turn.cluster_id >= 0 + return turn.is_clustered() || turn.has(target_operation) || turn.has(operation_continue); } @@ -630,7 +630,7 @@ struct traversal bool const is_union = target_operation == operation_union; turn_type const& turn = m_turns[turn_index]; - BOOST_ASSERT(turn.cluster_id >= 0); + BOOST_ASSERT(turn.is_clustered()); typename Clusters::const_iterator mit = m_clusters.find(turn.cluster_id); BOOST_ASSERT(mit != m_clusters.end()); @@ -814,7 +814,7 @@ struct traversal if (target_operation == operation_intersection) { bool const back_at_start_cluster - = current_turn.cluster_id >= 0 + = current_turn.is_clustered() && m_turns[start_turn_index].cluster_id == current_turn.cluster_id; if (turn_index == start_turn_index || back_at_start_cluster) @@ -825,7 +825,7 @@ struct traversal return true; } - if (current_turn.cluster_id < 0 + if (! current_turn.is_clustered() && current_turn.both(operation_intersection)) { if (analyze_ii_intersection(turn_index, op_index, @@ -836,7 +836,7 @@ struct traversal } } - if (current_turn.cluster_id >= 0) + if (current_turn.is_clustered()) { if (! select_turn_from_cluster(turn_index, op_index, start_turn_index, start_op_index, previous_seg_id)) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp index 8096a2269..4df3f6e7a 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_ring_creator.hpp @@ -206,7 +206,7 @@ struct traversal_ring_creator return traverse_error_none; } - if (start_turn.cluster_id >= 0) + if (start_turn.is_clustered()) { turn_type const& turn = m_turns[current_turn_index]; if (turn.cluster_id == start_turn.cluster_id) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index a55e56ccb..d240e982d 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -316,7 +316,7 @@ struct traversal_switch_detector return false; } - if (turn.cluster_id == -1) + if (! turn.is_clustered()) { // If it is a uu/ii-turn (non clustered), it is never same region return ! (turn.both(operation_union) || turn.both(operation_intersection)); @@ -504,7 +504,7 @@ struct traversal_switch_detector if (turn.discarded || turn.blocked() - || turn.cluster_id >= 0 + || turn.is_clustered() || ! (turn.both(operation_union) || turn.both(operation_intersection))) { // Skip discarded, blocked, non-uu/ii and clustered turns @@ -536,7 +536,7 @@ struct traversal_switch_detector turn_type const& turn = m_turns[turn_index]; if ((turn.both(operation_union) || turn.both(operation_intersection)) - && turn.cluster_id < 0) + && ! turn.is_clustered()) { std::cout << "UU/II SWITCH RESULT " << turn_index << " -> " diff --git a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp index a35bb1a18..3ed75cf09 100644 --- a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -90,7 +90,7 @@ struct turn_info Point point; method_type method; bool touch_only; // True in case of method touch(interior) and lines do not cross - signed_size_type cluster_id; // For multiple turns on same location, >= 0. Else -1 + signed_size_type cluster_id; // For multiple turns on same location, > 0. Else -1. 0 is unused. bool discarded; bool has_colocated_both; // Colocated with a uu turn (for union) or ii (other) @@ -135,6 +135,10 @@ struct turn_info { return has(operation_blocked); } + inline bool is_clustered() const + { + return cluster_id > 0; + } private : inline bool has12(operation_type type1, operation_type type2) const From f572abf5c14aecd9983cfe16cec89248f763a315 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 20 Sep 2017 12:52:18 +0200 Subject: [PATCH 126/188] [overlay] fix case which was not detected as isolated because it was not clustered. Isolation is now independent on clustering. Including unit test --- .../overlay/traversal_switch_detector.hpp | 62 ++++++++----------- .../overlay/multi_overlay_cases.hpp | 7 +++ .../intersection/intersection_multi.cpp | 1 + 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index d240e982d..bdf7003be 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -68,7 +68,8 @@ struct traversal_switch_detector struct connection_properties { std::size_t count; - std::set cluster_indices; + // Contains turn-index OR, if clustered, minus-cluster_id + std::set unique_turn_ids; connection_properties() : count(0) {} @@ -119,37 +120,36 @@ struct traversal_switch_detector return properties.isolated; } - bool all_colocated = true; - signed_size_type unique_cluster_id = -1; + bool single_connection_point = true; + signed_size_type const unassigned_id = m_turns.size() + 1; + signed_size_type first_turn_id = unassigned_id; for (typename connection_map::const_iterator it = properties.connected_region_counts.begin(); - all_colocated && it != properties.connected_region_counts.end(); ++it) + it != properties.connected_region_counts.end(); ++it) { connection_properties const& cprop = it->second; - if (cprop.cluster_indices.size() != 1) + if (cprop.unique_turn_ids.size() != 1) { - // Either no cluster (non colocated point), or more clusters - all_colocated = false; + // More turns or clusters on this region + single_connection_point = false; + break; } - signed_size_type const cluster_id = *cprop.cluster_indices.begin(); - if (cluster_id == -1) + signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin(); + + if (first_turn_id == unassigned_id) { - all_colocated = false; + first_turn_id = unique_turn_id; } - else if (unique_cluster_id == -1) + else if (first_turn_id != unique_turn_id) { - unique_cluster_id = cluster_id; - } - else if (unique_cluster_id != cluster_id) - { - all_colocated = false; + single_connection_point = false; + break; } } - if (all_colocated) + if (single_connection_point) { return isolation_yes; } - // It is isolated if there is only one connection, or if there are more connections but all // of them are isolated themselves, or if there are more connections // but they are all colocated @@ -281,28 +281,20 @@ struct traversal_switch_detector connection_properties& prop0 = m_connected_regions[id0].connected_region_counts[id1]; connection_properties& prop1 = m_connected_regions[id1].connected_region_counts[id0]; - if (turn.cluster_id < 0) + signed_size_type const unique_turn_id + = turn.is_clustered() ? -turn.cluster_id : turn_index; + + // Reference this turn or cluster to later check uniqueness on ring + if (prop0.unique_turn_ids.count(unique_turn_id) == 0) { - // Turn is not colocated, add reference to connection prop0.count++; - prop1.count++; + prop0.unique_turn_ids.insert(unique_turn_id); } - else + if (prop1.unique_turn_ids.count(unique_turn_id) == 0) { - // Turn is colocated, only add region reference if it was not yet registered - if (prop0.cluster_indices.count(turn.cluster_id) == 0) - { - prop0.count++; - } - if (prop1.cluster_indices.count(turn.cluster_id) == 0) - { - prop1.count++; - } + prop1.count++; + prop1.unique_turn_ids.insert(unique_turn_id); } - // Insert cluster-id (also -1 is inserted - reinsertion of - // same cluster id is OK) - prop0.cluster_indices.insert(turn.cluster_id); - prop1.cluster_indices.insert(turn.cluster_id); } } } diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 4fdba6ab9..0b74a159b 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1165,6 +1165,13 @@ static std::string case_recursive_boxes_72[2] = "MULTIPOLYGON(((4 0,4 1,5 0,4 0)),((4 4,4 5,5 5,5 4,4 4)),((2 4,2 3,1 3,1 4,2 4)),((2 4,2 5,3 5,2 4)),((2 2,2 3,3 3,2 2)),((2 2,3 2,3 1,2 1,2 2)),((3 3,4 2,3 2,3 3)),((3 1,3 0,2 0,3 1)),((1 3,2 2,1 1,1 3)),((1 2,0 2,0 3,1 2)),((1 4,0 4,0 5,1 5,1 4)),((4 2,4 3,5 2,4 2)))" }; +static std::string case_recursive_boxes_73[2] = +{ + // Needs handling connection points identical for clustered/non-clustered turns + "MULTIPOLYGON(((3 5,5 5,5 4,4.5 3.5,5 3,5 2,3 2,3 1,4 0,0 0,0 5,1 4,2 4,2 5,3 5),(2 3,2 2,3 3,2 3),(1 2,0 2,1 1,1 2)),((1 4,1 5,2 5,1 4)),((4 0,4 1,5 1,5 0,4 0)))", + "MULTIPOLYGON(((2 0,0 0,0 5,1 5,2 4,2 5,4 5,4.5 4.5,5 5,5 4,4 4,4 3,5 3,5 0,2 0),(0 2,0.5 1.5,1 2,0 2),(1 4,2 3,2 4,1 4)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index d16bee8eb..6448239de 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -334,6 +334,7 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_71, 3, -1, 1.75); TEST_INTERSECTION(case_recursive_boxes_72, 8, -1, 4.5); + TEST_INTERSECTION(case_recursive_boxes_73, 3, -1, 18.5); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 0c7199c230ccf17855637a503e3a358ee94c834c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 20 Sep 2017 13:05:05 +0200 Subject: [PATCH 127/188] [test] add last testcase also to union/diff --- test/algorithms/set_operations/difference/difference_multi.cpp | 1 + test/algorithms/set_operations/union/union_multi.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index fa3201cf8..be37c37f5 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -391,6 +391,7 @@ void test_areal() #endif TEST_DIFFERENCE(case_recursive_boxes_72, 6, 6.5, 7, 4.0, 10); + TEST_DIFFERENCE(case_recursive_boxes_73, 4, 1.75, 5, 4.0, 8); { ut_settings sym_settings; diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index e7ccc1f7b..06a0f06a7 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -394,6 +394,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_70, 1, 0, -1, 25.0); TEST_UNION(case_recursive_boxes_71, 4, 2, -1, 15.75); TEST_UNION(case_recursive_boxes_72, 10, 0, -1, 15.0); + TEST_UNION(case_recursive_boxes_73, 1, 2, -1, 24.25); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From ca62efc16d801115c02295d6f1d6340d053150ca Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 20 Sep 2017 13:32:22 +0200 Subject: [PATCH 128/188] [overlay] extract method has_single_connection_point --- .../overlay/traversal_switch_detector.hpp | 56 ++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index bdf7003be..070dd2739 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -111,6 +111,35 @@ struct traversal_switch_detector { } + bool has_single_connection_point(region_properties const& properties) const + { + signed_size_type const unassigned_id = m_turns.size() + 1; + signed_size_type first_turn_id = unassigned_id; + for (typename connection_map::const_iterator it + = properties.connected_region_counts.begin(); + it != properties.connected_region_counts.end(); ++it) + { + connection_properties const& cprop = it->second; + if (cprop.unique_turn_ids.size() != 1) + { + // Multiple turns or clusters on this region + return false; + } + + signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin(); + + if (first_turn_id == unassigned_id) + { + first_turn_id = unique_turn_id; + } + else if (first_turn_id != unique_turn_id) + { + return false; + } + } + return true; + } + isolation_type get_isolation(region_properties const& properties, signed_size_type parent_region_id, const std::set& visited) @@ -120,32 +149,7 @@ struct traversal_switch_detector return properties.isolated; } - bool single_connection_point = true; - signed_size_type const unassigned_id = m_turns.size() + 1; - signed_size_type first_turn_id = unassigned_id; - for (typename connection_map::const_iterator it = properties.connected_region_counts.begin(); - it != properties.connected_region_counts.end(); ++it) - { - connection_properties const& cprop = it->second; - if (cprop.unique_turn_ids.size() != 1) - { - // More turns or clusters on this region - single_connection_point = false; - break; - } - signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin(); - - if (first_turn_id == unassigned_id) - { - first_turn_id = unique_turn_id; - } - else if (first_turn_id != unique_turn_id) - { - single_connection_point = false; - break; - } - } - if (single_connection_point) + if (has_single_connection_point(properties)) { return isolation_yes; } From 3160ee6804a2cc183fb3a64479380b831e4a2f60 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 20 Sep 2017 17:00:52 +0200 Subject: [PATCH 129/188] [overlay] fix additional case where isolation information was incorrect. Including unit test. This also influences two other testcases but they are not completely fixed by this fix --- .../overlay/traversal_switch_detector.hpp | 163 +++++++++++------- .../overlay/multi_overlay_cases.hpp | 7 + .../intersection/intersection_multi.cpp | 9 +- 3 files changed, 113 insertions(+), 66 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index 070dd2739..3553b9949 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -48,7 +48,13 @@ template > struct traversal_switch_detector { - enum isolation_type { isolation_unknown = -1, isolation_no = 0, isolation_yes = 1 }; + enum isolation_type + { + isolation_unknown = -1, + isolation_no = 0, + isolation_yes = 1, + isolation_multiple = 2 + }; typedef typename boost::range_value::type turn_type; typedef typename turn_type::turn_operation_type turn_operation_type; @@ -111,26 +117,45 @@ struct traversal_switch_detector { } - bool has_single_connection_point(region_properties const& properties) const + bool one_connection_to_another_region(region_properties const& region) const { - signed_size_type const unassigned_id = m_turns.size() + 1; - signed_size_type first_turn_id = unassigned_id; - for (typename connection_map::const_iterator it - = properties.connected_region_counts.begin(); - it != properties.connected_region_counts.end(); ++it) + if (region.connected_region_counts.size() == 1) + { + connection_properties const& cprop = region.connected_region_counts.begin()->second; + return cprop.count <= 1; + } + return region.connected_region_counts.empty(); + } + + // TODO: might be combined with previous + bool multiple_connections_to_one_region(region_properties const& region) const + { + if (region.connected_region_counts.size() == 1) + { + connection_properties const& cprop = region.connected_region_counts.begin()->second; + return cprop.count > 1; + } + return false; + } + + bool one_connection_to_multiple_regions(region_properties const& region) const + { + bool first = true; + signed_size_type first_turn_id = 0; + for (typename connection_map::const_iterator it = region.connected_region_counts.begin(); + it != region.connected_region_counts.end(); ++it) { connection_properties const& cprop = it->second; - if (cprop.unique_turn_ids.size() != 1) + + if (cprop.count != 1) { - // Multiple turns or clusters on this region return false; } - signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin(); - - if (first_turn_id == unassigned_id) + if (first) { first_turn_id = unique_turn_id; + first = false; } else if (first_turn_id != unique_turn_id) { @@ -140,85 +165,95 @@ struct traversal_switch_detector return true; } - isolation_type get_isolation(region_properties const& properties, - signed_size_type parent_region_id, - const std::set& visited) + // TODO: might be combined with previous + bool has_only_isolated_children(region_properties const& region) const { - if (properties.isolated != isolation_unknown) - { - return properties.isolated; - } + bool first = true; + signed_size_type first_turn_id = 0; - if (has_single_connection_point(properties)) - { - return isolation_yes; - } - - // It is isolated if there is only one connection, or if there are more connections but all - // of them are isolated themselves, or if there are more connections - // but they are all colocated - std::size_t non_isolation_count = 0; - bool child_not_isolated = false; - for (typename connection_map::const_iterator it = properties.connected_region_counts.begin(); - it != properties.connected_region_counts.end(); ++it) + for (typename connection_map::const_iterator it = region.connected_region_counts.begin(); + it != region.connected_region_counts.end(); ++it) { signed_size_type const region_id = it->first; connection_properties const& cprop = it->second; - if (region_id == parent_region_id) + if (cprop.count != 1) { - // Normal situation, skip its direct parent - continue; - } - if (visited.count(region_id) > 0) - { - // Find one of its ancestors again, this is a ring. Not isolated. - return isolation_no; - } - if (cprop.count > 1) - { - return isolation_no; + return false; } - typename region_connection_map::iterator mit = m_connected_regions.find(region_id); + typename region_connection_map::const_iterator mit = m_connected_regions.find(region_id); if (mit == m_connected_regions.end()) { // Should not occur - continue; + return false; } - std::set vis = visited; - vis.insert(parent_region_id); - - region_properties& prop = mit->second; - if (prop.isolated == isolation_unknown) + region_properties const& connected_region = mit->second; + if (connected_region.isolated != isolation_yes + && connected_region.isolated != isolation_multiple) { - isolation_type const iso = get_isolation(prop, properties.region_id, vis); - prop.isolated = iso; - if (iso == isolation_no) + signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin(); + if (first) { - child_not_isolated = true; + first_turn_id = unique_turn_id; + first = false; + } + else if (first_turn_id != unique_turn_id) + { + return false; } } - if (prop.isolated == isolation_no) - { - non_isolation_count++; - } } - - return child_not_isolated || non_isolation_count > 1 ? isolation_no : isolation_yes; + // If there is only one connection (with a 'parent'), and all other + // connections are itself isolated, it is isolated + return true; } void get_isolated_regions() { - for (typename region_connection_map::iterator it = m_connected_regions.begin(); + typedef typename region_connection_map::iterator it_type; + + // First time: check regions isolated (one connection only), + // semi-isolated (multiple connections between same region), + // and complex isolated (connection with multiple rings but all + // at same point) + for (it_type it = m_connected_regions.begin(); it != m_connected_regions.end(); ++it) { region_properties& properties = it->second; - if (properties.isolated == isolation_unknown) + if (one_connection_to_another_region(properties)) { - std::set visited; - properties.isolated = get_isolation(properties, properties.region_id, visited); + properties.isolated = isolation_yes; + } + else if (multiple_connections_to_one_region(properties)) + { + properties.isolated = isolation_multiple; + } + else if (one_connection_to_multiple_regions(properties)) + { + properties.isolated = isolation_yes; + } + } + + // Propagate isolation to next level + // TODO: should be optimized + std::size_t defensive_check = 0; + bool changed = true; + while (changed && defensive_check++ < m_connected_regions.size()) + { + changed = false; + for (it_type it = m_connected_regions.begin(); + it != m_connected_regions.end(); ++it) + { + region_properties& properties = it->second; + + if (properties.isolated == isolation_unknown + && has_only_isolated_children(properties)) + { + properties.isolated = isolation_yes; + changed = true; + } } } } diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 0b74a159b..dfd7a7dd0 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1172,6 +1172,13 @@ static std::string case_recursive_boxes_73[2] = "MULTIPOLYGON(((2 0,0 0,0 5,1 5,2 4,2 5,4 5,4.5 4.5,5 5,5 4,4 4,4 3,5 3,5 0,2 0),(0 2,0.5 1.5,1 2,0 2),(1 4,2 3,2 4,1 4)))" }; +static std::string case_recursive_boxes_74[2] = +{ + // Needs another method to find isolated regions (previous attempted used parents, that is dropped now) + "MULTIPOLYGON(((3 5,5 5,5 0,0 0,0 5,3 5),(2 3,1.5 2.5,2 2,3 3,2 3),(3 3,4 2,4 3,3 3),(2 0,3 1,2 1,2 0)))", + "MULTIPOLYGON(((2 0,0 0,0 5,1 5,2 4,2 5,5 5,5 0,2 0),(2 1,2.5 0.5,3 1,2 1),(2 3,3 3,3 4,2 4,2 3),(3 1,4 1,5 2,3 2,3 1)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 6448239de..5751bf0af 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -176,7 +176,7 @@ void test_areal() 19, 87, 12.5); // Area from SQL Server #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS - TEST_INTERSECTION_IGNORE(case_recursive_boxes_4, 11, 177, 67.0); + TEST_INTERSECTION_IGNORE(case_recursive_boxes_4, 13, 169, 67.0); #else TEST_INTERSECTION_IGNORE(case_recursive_boxes_4, 8, 178, 67.0); #endif @@ -312,7 +312,7 @@ void test_areal() #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_INTERSECTION(case_recursive_boxes_64, 5, -1, 17.25); #else - TEST_INTERSECTION_IGNORE(case_recursive_boxes_64, 3, -1, 17.25); + TEST_INTERSECTION_IGNORE(case_recursive_boxes_64, 4, -1, 17.25); #endif TEST_INTERSECTION(case_recursive_boxes_65, 3, -1, 17.25); @@ -335,6 +335,11 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_71, 3, -1, 1.75); TEST_INTERSECTION(case_recursive_boxes_72, 8, -1, 4.5); TEST_INTERSECTION(case_recursive_boxes_73, 3, -1, 18.5); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_INTERSECTION(case_recursive_boxes_74, 3, -1, 20.25); +#else + TEST_INTERSECTION_IGNORE(case_recursive_boxes_74, 2, -1, 20.25); +#endif test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 866b297482138a8fe7976b27603ced3db05e7900 Mon Sep 17 00:00:00 2001 From: Vissarion Fysikopoulos Date: Thu, 21 Sep 2017 13:22:51 +0100 Subject: [PATCH 130/188] [formulas] [tests] Distance point-segment; normalization and some changes in the method and tests --- .../formulas/distance_point_segment.hpp | 50 ++++++++++--------- .../algorithms/distance/distance_geo_pl_l.cpp | 4 +- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/include/boost/geometry/formulas/distance_point_segment.hpp b/include/boost/geometry/formulas/distance_point_segment.hpp index 1462fd8dd..1700a8842 100644 --- a/include/boost/geometry/formulas/distance_point_segment.hpp +++ b/include/boost/geometry/formulas/distance_point_segment.hpp @@ -86,7 +86,24 @@ public: return non_iterative_case(lon1, lat1, distance); } - + template + T static inline normalize(T g4) + { + CT const pi = math::pi(); + if (g4 < 0 && g4 < -pi)//close to -270 + { + return g4 + 1.5 * pi; + } + else if (g4 > 0 && g4 > pi)//close to 270 + { + return - g4 + 1.5 * pi; + } + else if (g4 < 0 && g4 > -pi)//close to -90 + { + return -g4 - pi/2; + } + return g4 - pi/2; + } template result_distance_point_segment @@ -121,7 +138,7 @@ public: //segment on equator //TODO: use the meridian distance when it'll be available - if (math::equals(lat1, 0) && math::equals(lat2, 0)) + if (math::equals(lat1, c0) && math::equals(lat2, c0)) { #ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG std::cout << "Equatorial segment" << std::endl; @@ -214,10 +231,11 @@ public: } // Guess s14 (SPHERICAL) - typedef geometry::model::point< - CT, 2, - geometry::cs::spherical_equatorial - > point; + typedef geometry::model::point + < + CT, 2, + geometry::cs::spherical_equatorial + > point; CT bet1 = atan((1 - f) * tan(lon1)); CT bet2 = atan((1 - f) * tan(lon2)); @@ -264,27 +282,11 @@ public: lon3, lat3, spheroid); g4 = res34.azimuth - a4; - // Normalize g4 - if (g4 < 0 && g4 < -pi)//close to -270 - { - delta_g4 = g4 + 1.5 * pi; - } - else if (g4 > 0 && g4 > pi)//close to 270 - { - delta_g4 = - g4 + 1.5 * pi; - } - else if (g4 < 0 && g4 > -pi)//close to -90 - { - delta_g4 = -g4 - pi/2; - } - else //close to 90 - { - delta_g4 = g4 - pi/2; - } + delta_g4 = normalize(g4); CT M43 = res34.geodesic_scale; // cos(s14/earth_radius) is the spherical limit CT m34 = res34.reduced_length; - CT der = M43 / m34; + CT der = (M43 / m34) * sin(g4); s14 = s14 - delta_g4 / der; #ifdef BOOST_GEOMETRY_DISTANCE_POINT_SEGMENT_DEBUG diff --git a/test/algorithms/distance/distance_geo_pl_l.cpp b/test/algorithms/distance/distance_geo_pl_l.cpp index f687ed293..6da51ca55 100644 --- a/test/algorithms/distance/distance_geo_pl_l.cpp +++ b/test/algorithms/distance/distance_geo_pl_l.cpp @@ -247,8 +247,8 @@ void test_distance_point_segment(Strategy_pp const& strategy_pp, tester::apply("p-s-mer2", "POINT(2.5 3)", "SEGMENT(2 2,2 4)", - pp_distance("POINT(2.5 3)", "POINT(2 3)", strategy_pp), - strategy_ps); + pp_distance("POINT(2.5 3)", "POINT(2 3.000114792872075)", andoyer_pp()), + andoyer_strategy()); tester::apply("p-s-mer3", "POINT(2.5 5)", "SEGMENT(2 2,2 4)", From 210b6625975fb31e8b995fbf4a23c69fadcbee0c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 27 Sep 2017 13:05:25 +0200 Subject: [PATCH 131/188] [overlay] correct (partly) the behaviour of isolation detection for an isolated ring, connected to another isolated ring which is connected multiple times to it --- .../overlay/traversal_switch_detector.hpp | 123 ++++++++++++++---- .../overlay/multi_overlay_cases.hpp | 7 + .../intersection/intersection_multi.cpp | 4 + 3 files changed, 108 insertions(+), 26 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index 3553b9949..2ca2fe1e6 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -58,13 +58,14 @@ struct traversal_switch_detector typedef typename boost::range_value::type turn_type; typedef typename turn_type::turn_operation_type turn_operation_type; + typedef std::set set_type; // Per ring, first turns are collected (in turn_indices), and later // a region_id is assigned struct merged_ring_properties { signed_size_type region_id; - std::set turn_indices; + set_type turn_indices; merged_ring_properties() : region_id(-1) @@ -75,7 +76,7 @@ struct traversal_switch_detector { std::size_t count; // Contains turn-index OR, if clustered, minus-cluster_id - std::set unique_turn_ids; + set_type unique_turn_ids; connection_properties() : count(0) {} @@ -89,6 +90,7 @@ struct traversal_switch_detector { signed_size_type region_id; isolation_type isolated; + set_type unique_turn_ids; // Maps from connected region_id to their properties connection_map connected_region_counts; @@ -103,7 +105,7 @@ struct traversal_switch_detector typedef std::map merge_map; typedef std::map region_connection_map; - typedef std::set::const_iterator set_iterator; + typedef set_type::const_iterator set_iterator; inline traversal_switch_detector(Geometry1 const& geometry1, Geometry2 const& geometry2, Turns& turns, Clusters& clusters, @@ -117,6 +119,33 @@ struct traversal_switch_detector { } + bool inspect_difference(set_type& turn_id_difference, + set_type const& turn_ids, + set_type const& other_turn_ids) const + { + // TODO: consider if std::set_difference can be used in the final version + int const turn_count = turn_ids.size(); + int const other_turn_count = other_turn_ids.size(); + + // First quick check on size (TODO: implement multiple-multiple connections) + if (turn_count - other_turn_count > 1) + { + return false; + } + + // Check if all turns are also present in the connection. + // The difference is returned + for (set_iterator it = turn_ids.begin(); it != turn_ids.end(); ++it) + { + signed_size_type const& id = *it; + if (other_turn_ids.count(id) == 0) + { + turn_id_difference.insert(id); + } + } + return true; + } + bool one_connection_to_another_region(region_properties const& region) const { if (region.connected_region_counts.size() == 1) @@ -165,11 +194,12 @@ struct traversal_switch_detector return true; } - // TODO: might be combined with previous bool has_only_isolated_children(region_properties const& region) const { - bool first = true; + bool first_with_turn = true; + bool first_with_multiple = true; signed_size_type first_turn_id = 0; + signed_size_type first_multiple_region_id = 0; for (typename connection_map::const_iterator it = region.connected_region_counts.begin(); it != region.connected_region_counts.end(); ++it) @@ -177,11 +207,6 @@ struct traversal_switch_detector signed_size_type const region_id = it->first; connection_properties const& cprop = it->second; - if (cprop.count != 1) - { - return false; - } - typename region_connection_map::const_iterator mit = m_connected_regions.find(region_id); if (mit == m_connected_regions.end()) { @@ -190,14 +215,49 @@ struct traversal_switch_detector } region_properties const& connected_region = mit->second; - if (connected_region.isolated != isolation_yes - && connected_region.isolated != isolation_multiple) + + bool const multiple = connected_region.isolated == isolation_multiple; + + if (cprop.count != 1) + { + if (! multiple) + { + return false; + } + + // It connects multiple times to an isolated region. + // This is allowed as long as it happens only once + if (first_with_multiple) + { + first_multiple_region_id = connected_region.region_id; + first_with_multiple = false; + } + else if (first_multiple_region_id != connected_region.region_id) + { + return false; + } + + // Turns in region should be either present in the connection, + // of form part of the connection with the other region + set_type diff; + if (! inspect_difference(diff, region.unique_turn_ids, + connected_region.unique_turn_ids)) + { + return false; + } + if (diff.size() > 1) + { + // For now: + return false; + } + + if (connected_region.isolated != isolation_yes && ! multiple) { signed_size_type const unique_turn_id = *cprop.unique_turn_ids.begin(); - if (first) + if (first_with_turn) { first_turn_id = unique_turn_id; - first = false; + first_with_turn = false; } else if (first_turn_id != unique_turn_id) { @@ -308,21 +368,33 @@ struct traversal_switch_detector { turn_type const& turn = m_turns[turn_index]; - signed_size_type const& id0 = turn.operations[0].enriched.region_id; - signed_size_type const& id1 = turn.operations[1].enriched.region_id; + signed_size_type const unique_turn_id + = turn.is_clustered() ? -turn.cluster_id : turn_index; + + turn_operation_type op0 = turn.operations[0]; + turn_operation_type op1 = turn.operations[1]; + + signed_size_type const& id0 = op0.enriched.region_id; + signed_size_type const& id1 = op1.enriched.region_id; + + // Add region (by assigning) and add involved turns + if (id0 != -1) + { + m_connected_regions[id0].region_id = id0; + m_connected_regions[id0].unique_turn_ids.insert(unique_turn_id); + } + if (id1 != -1 && id0 != id1) + { + m_connected_regions[id1].region_id = id1; + m_connected_regions[id1].unique_turn_ids.insert(unique_turn_id); + } if (id0 != id1 && id0 != -1 && id1 != -1) { - // Force insertion - m_connected_regions[id0].region_id = id0; - m_connected_regions[id1].region_id = id1; - + // Assign connections connection_properties& prop0 = m_connected_regions[id0].connected_region_counts[id1]; connection_properties& prop1 = m_connected_regions[id1].connected_region_counts[id0]; - signed_size_type const unique_turn_id - = turn.is_clustered() ? -turn.cluster_id : turn_index; - // Reference this turn or cluster to later check uniqueness on ring if (prop0.unique_turn_ids.count(unique_turn_id) == 0) { @@ -506,9 +578,8 @@ struct traversal_switch_detector } // A touching cluster, gather regions - std::set regions; - - std::set const& ids = cinfo.turn_indices; + set_type regions; + set_type const& ids = cinfo.turn_indices; #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSAL_SWITCH_DETECTOR) std::cout << "SWITCH EXAMINE CLUSTER " << it->first << std::endl; diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index dfd7a7dd0..2eb35aeac 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -629,6 +629,13 @@ static std::string case_140_multi[2] = "MULTIPOLYGON(((2 0,2 8,8 8,8 6,10 6,10 2,6 2,6 0,2 0),(6 6,6 4,4 4,4 6,6 6)),((5 4.5,4 6,5.5 5, 5 4.5)))" }; +static std::string case_141_multi[2] = +{ + // Version to test more isolation/validity cases + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(2 3,1 2,2 1,3 2,2 3),(2 7,3 8,2 9,1 8,2 7),(10 3,9 4,8 3,9 2,10 3),(7 10,6 9,7 8,8 9,7 10),(10 7,9 8,8 7,9 6,10 7)))", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(0 5,2 3,4 5,2 7),(3 2,4 1,5 2,4 3,3 2),(3 8,4 7,5 8,4 9,3 8),(7 2,8 1,9 2,8 2,8 3,7 2),(8 7,7 8,6 7,7 6,8 7)))" +}; + static std::string case_recursive_boxes_1[2] = { "MULTIPOLYGON(((1 0,0 0,0 1,1 1,1 2,0 2,0 4,2 4,2 5,3 5,3 6,1 6,1 5,0 5,0 10,9 10,9 9,7 9,7 8,6 8,6 7,8 7,8 6,9 6,9 4,8 4,8 3,10 3,10 0,6 0,6 1,5 1,5 0,1 0),(8 4,8 5,7 5,7 6,6 6,6 5,5 5,5 4,4 4,4 3,5 3,5 2,7 2,7 3,6 3,6 4,8 4),(8 1,9 1,9 2,8 2,8 1),(4 7,4 9,3 9,3 7,4 7)),((9 9,10 9,10 7,10 6,9 6,9 7,8 7,8 8,9 8,9 9)))", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 5751bf0af..6c03295dd 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -162,6 +162,10 @@ void test_areal() TEST_INTERSECTION(case_139_multi, 2, 23, 40.546875); TEST_INTERSECTION(case_140_multi, 2, 23, 40.546875); + // TODO: isolated region with multiple connection should be handled + // differently + TEST_INTERSECTION_IGNORE(case_141_multi, 2, -1, 74.5); + #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_INTERSECTION(case_recursive_boxes_1, 10, 97, 47.0); #else From 5369afe1c516ceda24aa95be8b8d0391dd2238a3 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 27 Sep 2017 13:23:26 +0200 Subject: [PATCH 132/188] [test] add last testcase, plus 3 missing ones, also to union/difference tests --- .../algorithms/set_operations/difference/difference_multi.cpp | 4 ++++ test/algorithms/set_operations/union/union_multi.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index be37c37f5..00bac601a 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -200,6 +200,10 @@ void test_areal() TEST_DIFFERENCE(case_135_multi, 2, 2.0, 2, 13.0, 2); TEST_DIFFERENCE(case_136_multi, 2, 2.0, 3, 13.5, 3); TEST_DIFFERENCE(case_137_multi, 2, 2.5, 2, 13.0, 2); + TEST_DIFFERENCE(case_138_multi, 5, 16.6, 3, 8.225, 8); + TEST_DIFFERENCE(case_139_multi, 4, 16.328125, 3, 8.078125, 7); + TEST_DIFFERENCE(case_140_multi, 4, 16.328125, 3, 8.078125, 7); + TEST_DIFFERENCE(case_141_multi, 5, 15.5, 5, 10.0, 10); // Areas correspond with POSTGIS, // #clips in PostGIS is 11,11,5 but should most probably be be 12,12,6 diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 06a0f06a7..9a96703cb 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -204,6 +204,10 @@ void test_areal() TEST_UNION(case_135_multi, 1, 2, -1, 22.0); TEST_UNION(case_136_multi, 1, 2, -1, 22.0); TEST_UNION(case_137_multi, 1, 2, -1, 22.0); + TEST_UNION(case_138_multi, 2, 1, -1, 65.225); + TEST_UNION(case_139_multi, 2, 1, -1, 64.953); + TEST_UNION(case_140_multi, 2, 1, -1, 64.953); + TEST_UNION(case_141_multi, 1, 0, -1, 100.0); test_one("case_recursive_boxes_1", case_recursive_boxes_1[0], case_recursive_boxes_1[1], From 6cce6a06863aecec57d7b8f66cdc5b4eb5410b8c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 27 Sep 2017 13:24:18 +0200 Subject: [PATCH 133/188] [overlay] add explaining pictures --- .../overlay/traversal_switch_detector.hpp | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index 2ca2fe1e6..3f1787f0d 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -148,6 +148,15 @@ struct traversal_switch_detector bool one_connection_to_another_region(region_properties const& region) const { + // For example: + // +----------------------+ + // | __ | + // | / \| + // | | x + // | \__/| + // | | + // +----------------------+ + if (region.connected_region_counts.size() == 1) { connection_properties const& cprop = region.connected_region_counts.begin()->second; @@ -159,6 +168,18 @@ struct traversal_switch_detector // TODO: might be combined with previous bool multiple_connections_to_one_region(region_properties const& region) const { + // For example: + // +----------------------+ + // | __ | + // | / \| + // | | x + // | \ /| + // | / \| + // | | x + // | \__/| + // | | + // +----------------------+ + if (region.connected_region_counts.size() == 1) { connection_properties const& cprop = region.connected_region_counts.begin()->second; @@ -169,6 +190,15 @@ struct traversal_switch_detector bool one_connection_to_multiple_regions(region_properties const& region) const { + // For example: + // +----------------------+ + // | __ | __ + // | / \|/ | + // | | x | + // | \__/|\__| + // | | + // +----------------------+ + bool first = true; signed_size_type first_turn_id = 0; for (typename connection_map::const_iterator it = region.connected_region_counts.begin(); From d8f2d238630548e4d37124b9ca311b5cb49d0a67 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Tue, 3 Oct 2017 01:56:53 +0200 Subject: [PATCH 134/188] [test][formulas] Print test name/id in error msg in inverse formulas test. --- test/formulas/inverse.cpp | 38 ++++++++++++++++++++++++---------- test/formulas/test_formula.hpp | 30 +++++++++++++++++++++------ 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/test/formulas/inverse.cpp b/test/formulas/inverse.cpp index 8fa06d80a..e72cf3bb5 100644 --- a/test/formulas/inverse.cpp +++ b/test/formulas/inverse.cpp @@ -1,7 +1,7 @@ // Boost.Geometry // Unit Test -// Copyright (c) 2016 Oracle and/or its affiliates. +// Copyright (c) 2016-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -9,6 +9,9 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) + +#include + #include "test_formula.hpp" #include "inverse_cases.hpp" @@ -16,14 +19,27 @@ #include #include -template -void check_inverse(Result const& result, expected_result const& expected, expected_result const& reference, double reference_error) + +void check_inverse(std::string const& name, + expected_results const& results, + bg::formula::result_inverse const& result, + expected_result const& expected, + expected_result const& reference, + double reference_error) { - check_one(result.distance, expected.distance, reference.distance, reference_error); - check_one(result.azimuth, expected.azimuth, reference.azimuth, reference_error, true); - check_one(result.reverse_azimuth, expected.reverse_azimuth, reference.reverse_azimuth, reference_error, true); - check_one(result.reduced_length, expected.reduced_length, reference.reduced_length, reference_error); - check_one(result.geodesic_scale, expected.geodesic_scale, reference.geodesic_scale, reference_error); + std::stringstream ss; + ss << "(" << results.p1.lon << " " << results.p1.lat << ")->(" << results.p2.lon << " " << results.p2.lat << ")"; + + check_one(name + "_d " + ss.str(), + result.distance, expected.distance, reference.distance, reference_error); + check_one(name + "_a " + ss.str(), + result.azimuth, expected.azimuth, reference.azimuth, reference_error, true); + check_one(name + "_ra " + ss.str(), + result.reverse_azimuth, expected.reverse_azimuth, reference.reverse_azimuth, reference_error, true); + check_one(name + "_rl " + ss.str(), + result.reduced_length, expected.reduced_length, reference.reduced_length, reference_error); + check_one(name + "_gs " + ss.str(), + result.geodesic_scale, expected.geodesic_scale, reference.geodesic_scale, reference_error); } void test_all(expected_results const& results) @@ -45,19 +61,19 @@ void test_all(expected_results const& results) result_v = vi_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); result_v.azimuth *= r2d; result_v.reverse_azimuth *= r2d; - check_inverse(result_v, results.vincenty, results.reference, 0.0000001); + check_inverse("vincenty", results, result_v, results.vincenty, results.reference, 0.0000001); typedef bg::formula::thomas_inverse th_t; result_t = th_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); result_t.azimuth *= r2d; result_t.reverse_azimuth *= r2d; - check_inverse(result_t, results.thomas, results.reference, 0.00001); + check_inverse("thomas", results, result_t, results.thomas, results.reference, 0.00001); typedef bg::formula::andoyer_inverse an_t; result_a = an_t::apply(lon1r, lat1r, lon2r, lat2r, spheroid); result_a.azimuth *= r2d; result_a.reverse_azimuth *= r2d; - check_inverse(result_a, results.andoyer, results.reference, 0.001); + check_inverse("andoyer", results, result_a, results.andoyer, results.reference, 0.001); } int test_main(int, char*[]) diff --git a/test/formulas/test_formula.hpp b/test/formulas/test_formula.hpp index 60502281d..8efb428cc 100644 --- a/test/formulas/test_formula.hpp +++ b/test/formulas/test_formula.hpp @@ -24,9 +24,17 @@ void normalize_deg(double & deg) deg += 360.0; } -void check_one(double result, double expected, double reference, double reference_error, + +#define BOOST_GEOMETRY_CHECK_CLOSE( L, R, T, M ) BOOST_TEST_TOOL_IMPL( 0, \ + ::boost::test_tools::check_is_close_t(), M, CHECK, CHECK_MSG, (L)(R)(::boost::math::fpc::percent_tolerance(T)) ) + + +void check_one(std::string const& name, + double result, double expected, double reference, double reference_error, bool normalize = false, bool check_reference_only = false) { + std::string id = name.empty() ? "" : (name + " : "); + if (normalize) { normalize_deg(result); @@ -45,19 +53,22 @@ void check_one(double result, double expected, double reference, double referenc { bool is_close = abs_result <= 30 * eps && abs_expected <= 30 * eps; BOOST_CHECK_MESSAGE((is_close), - std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); } else if (res_max > 100 * eps) { - BOOST_CHECK_CLOSE(result, expected, 0.1); + BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 0.1, + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); } else if (res_max > 10 * eps) { - BOOST_CHECK_CLOSE(result, expected, 10); + BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 10, + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); } else if (res_max > eps) { - BOOST_CHECK_CLOSE(result, expected, 1000); + BOOST_GEOMETRY_CHECK_CLOSE(result, expected, 1000, + id << std::setprecision(20) << "result {" << result << "} different than expected {" << expected << "}."); } } @@ -66,7 +77,14 @@ void check_one(double result, double expected, double reference, double referenc double ref_diff = bg::math::abs(result - reference); double ref_max = (std::max)(bg::math::abs(result), bg::math::abs(reference)); bool is_ref_close = ref_diff <= reference_error || ref_diff <= reference_error * ref_max; - BOOST_CHECK_MESSAGE((is_ref_close), std::setprecision(20) << "result {" << result << "} and reference {" << reference << "} not close enough."); + BOOST_CHECK_MESSAGE((is_ref_close), + id << std::setprecision(20) << "result {" << result << "} and reference {" << reference << "} not close enough."); +} + +void check_one(double result, double expected, double reference, double reference_error, + bool normalize = false, bool check_reference_only = false) +{ + check_one("", result, expected, reference, reference_error, normalize, check_reference_only); } #endif // BOOST_GEOMETRY_TEST_FORMULA_HPP From c5710b2ab99f27b6292ebe01db46da8e5a26cf11 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 4 Oct 2017 14:00:40 +0200 Subject: [PATCH 135/188] [overlay] skip ranks of isolated regions --- .../algorithms/detail/overlay/traversal.hpp | 1 + .../traversal_intersection_patterns.hpp | 62 +++++++++++++++++++ .../overlay/multi_overlay_cases.hpp | 7 +++ .../intersection/intersection_multi.cpp | 2 + 4 files changed, 72 insertions(+) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index d63f80016..e8a9a51a0 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -510,6 +510,7 @@ struct traversal || intersection_pattern_common_interior3(selected_rank, aggregation) || intersection_pattern_common_interior4(selected_rank, aggregation) || intersection_pattern_common_interior5(selected_rank, aggregation) + || intersection_pattern_common_interior6(selected_rank, aggregation) ; if (! detected) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp index fc9df0f63..a8abea230 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_intersection_patterns.hpp @@ -381,6 +381,68 @@ inline bool intersection_pattern_common_interior5(std::size_t& selected_rank, return true; } +inline bool intersection_pattern_common_interior6(std::size_t& selected_rank, + std::vector const& aggregation) +{ + // Pattern: isolated regions in between + + // See #case_recursive_boxes_75 + + // Incoming: one region + // In between: several rings having isolated region, all the same + // Outging == incoming + + std::size_t const n = aggregation.size(); + if (n < 3) + { + return false; + } + + sort_by_side::rank_with_rings const& incoming = aggregation.front(); + sort_by_side::rank_with_rings const& outgoing = aggregation.back(); + sort_by_side::rank_with_rings const& first_isolated = aggregation[2]; + + bool const incoming_ok = + incoming.all_from() + && incoming.has_unique_region_id() + && ! incoming.is_isolated(); + + if (! incoming_ok) + { + return false; + } + + signed_size_type const incoming_region_id = incoming.region_id(); + + bool const outgoing_ok = + outgoing.all_to() + && outgoing.has_unique_region_id() + && ! outgoing.is_isolated() + && outgoing.region_id() == incoming_region_id; + + if (! outgoing_ok) + { + return false; + } + + const signed_size_type isolated_region_id = first_isolated.region_id(); + + for (std::size_t i = 1; i < n - 1; i++) + { + sort_by_side::rank_with_rings const& rwr = aggregation[i]; + if (! rwr.has_unique_region_id() + || ! rwr.is_isolated() + || rwr.region_id() != isolated_region_id) + { + return false; + } + } + + selected_rank = n - 1; + + return true; +} + }} // namespace detail::overlay #endif // DOXYGEN_NO_DETAIL diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 2eb35aeac..c9462434d 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1186,6 +1186,13 @@ static std::string case_recursive_boxes_74[2] = "MULTIPOLYGON(((2 0,0 0,0 5,1 5,2 4,2 5,5 5,5 0,2 0),(2 1,2.5 0.5,3 1,2 1),(2 3,3 3,3 4,2 4,2 3),(3 1,4 1,5 2,3 2,3 1)))" }; +static std::string case_recursive_boxes_75[2] = +{ + // Needs intersection pattern 6 (skip all isolated ranks in between) + "MULTIPOLYGON(((3 1,2 1,1 0,0 0,0 5,5 5,5 3,4 2,5 2,5 0,3 0,3 1),(2 3,1 3,2 2,2 3),(3 4,3.5 3.5,4 4,3 4),(3 3,3 2,4 2,4 3,3 3)))", + "MULTIPOLYGON(((3 5,4 5,4.5 4.5,5 5,5 0,4 0,4 1,2 1,2 2,1 2,1 3,0 2,0 5,1 5,0.5 4.5,1 4,2 5,3 5),(3 4,3 3,4 4,3 4),(3 3,4 2,4 3,3 3)),((2 1,2 0,1 0,1 1,2 1)),((0 2,1 2,0.5 1.5,1 1,0 1,0 2)),((1 0,0 0,0 1,1 0)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 6c03295dd..c62c37c9e 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -345,6 +345,8 @@ void test_areal() TEST_INTERSECTION_IGNORE(case_recursive_boxes_74, 2, -1, 20.25); #endif + TEST_INTERSECTION(case_recursive_boxes_75, 5, -1, 16.75); + test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], 2, 10, 6.0); // Area from SQL Server From 0c5038233f5d27f7118d3f049d157b04faabf9b6 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 4 Oct 2017 15:29:34 +0200 Subject: [PATCH 136/188] [test] add last testcases also to union/difference tests --- .../set_operations/difference/difference_multi.cpp | 7 +++++++ test/algorithms/set_operations/union/union_multi.cpp | 2 ++ 2 files changed, 9 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 00bac601a..031f9be3c 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -397,6 +397,13 @@ void test_areal() TEST_DIFFERENCE(case_recursive_boxes_72, 6, 6.5, 7, 4.0, 10); TEST_DIFFERENCE(case_recursive_boxes_73, 4, 1.75, 5, 4.0, 8); + TEST_DIFFERENCE(case_recursive_boxes_74, 3, 3.00, 3, 1.5, 5); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_75, 7, 4.5, 4, 2.0, 11); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_75, 5, 4.5, 4, 2.0, 9); +#endif + { ut_settings sym_settings; #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 9a96703cb..9e3fa9c9f 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -399,6 +399,8 @@ void test_areal() TEST_UNION(case_recursive_boxes_71, 4, 2, -1, 15.75); TEST_UNION(case_recursive_boxes_72, 10, 0, -1, 15.0); TEST_UNION(case_recursive_boxes_73, 1, 2, -1, 24.25); + TEST_UNION(case_recursive_boxes_74, 1, 1, -1, 24.75); + TEST_UNION(case_recursive_boxes_75, 1, 2, -1, 23.25); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 65a5c2ba53d34085c9fdbaeb980d391d17c0f26b Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 4 Oct 2017 18:12:37 +0200 Subject: [PATCH 137/188] [overlay] fix bug (it always returned false), and it should not consider all turns in a cluster here. This fixes #case_recursive_boxes_76 Including unit test --- .../overlay/traversal_switch_detector.hpp | 27 ++++--------------- .../overlay/multi_overlay_cases.hpp | 7 +++++ .../intersection/intersection_multi.cpp | 1 + 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index 3f1787f0d..8168dc4c5 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -463,28 +463,11 @@ struct traversal_switch_detector == turn.operations[1].enriched.zone; } - // If a cluster contains an ii/cc it is not same region (for intersection) - typename Clusters::const_iterator it = m_clusters.find(turn.cluster_id); - if (it == m_clusters.end()) - { - // Should not occur - return true; - } - - cluster_info const& cinfo = it->second; - for (set_iterator sit = cinfo.turn_indices.begin(); - sit != cinfo.turn_indices.end(); ++sit) - { - turn_type const& cluster_turn = m_turns[*sit]; - if (cluster_turn.both(operation_union) - || cluster_turn.both(operation_intersection)) - { - return false; - } - } - - // It is the same region - return false; + // For an intersection, two regions connect if they are not ii + // (ii-regions are isolated) or, in some cases, not iu (for example + // when a multi-polygon is inside an interior ring and connecting it) + return ! (turn.both(operation_intersection) + || turn.combination(operation_intersection, operation_union)); } diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index c9462434d..f0f105d8b 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1193,6 +1193,13 @@ static std::string case_recursive_boxes_75[2] = "MULTIPOLYGON(((3 5,4 5,4.5 4.5,5 5,5 0,4 0,4 1,2 1,2 2,1 2,1 3,0 2,0 5,1 5,0.5 4.5,1 4,2 5,3 5),(3 4,3 3,4 4,3 4),(3 3,4 2,4 3,3 3)),((2 1,2 0,1 0,1 1,2 1)),((0 2,1 2,0.5 1.5,1 1,0 1,0 2)),((1 0,0 0,0 1,1 0)))" }; +static std::string case_recursive_boxes_76[2] = +{ + // Needs considering ix/ix turns (opposite in both directions) as the same region + "MULTIPOLYGON(((3 5,3 4,4 5,4 4,5 4,5 0,0 0,0 4,1 4,1 5,3 5),(3 1,2 1,2.5 0.5,3 1),(1 2,0 2,0.5 1.5,1 2)))", + "MULTIPOLYGON(((2 0,0 0,0 3,1 4,0 4,0 5,2 5,2 4,3 4,4 5,5 5,5 0,2 0),(3 1,4 0,4 1,3 1),(3 2,2 1,3 1,3 2),(3 3,3 4,2 3,3 3),(2 1,1 1,1.5 0.5,2 1),(4 3,4 4,3 4,4 3)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index c62c37c9e..3f41b4094 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -346,6 +346,7 @@ void test_areal() #endif TEST_INTERSECTION(case_recursive_boxes_75, 5, -1, 16.75); + TEST_INTERSECTION(case_recursive_boxes_76, 2, -1, 18.25); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From a5507f2e6854e191f47809aa665a423e8dcee034 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 4 Oct 2017 18:22:18 +0200 Subject: [PATCH 138/188] [test] add testcases to union/difference --- test/algorithms/set_operations/difference/difference_multi.cpp | 2 ++ test/algorithms/set_operations/union/union_multi.cpp | 1 + 2 files changed, 3 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 031f9be3c..1baa77cff 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -404,6 +404,8 @@ void test_areal() TEST_DIFFERENCE_IGNORE(case_recursive_boxes_75, 5, 4.5, 4, 2.0, 9); #endif + TEST_DIFFERENCE(case_recursive_boxes_76, 7, 3.75, 4, 2.5, 9); + { ut_settings sym_settings; #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 9e3fa9c9f..9e4cbf335 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -401,6 +401,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_73, 1, 2, -1, 24.25); TEST_UNION(case_recursive_boxes_74, 1, 1, -1, 24.75); TEST_UNION(case_recursive_boxes_75, 1, 2, -1, 23.25); + TEST_UNION(case_recursive_boxes_76, 1, 0, -1, 24.5); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 641ee182572e9c261d1b84da994b3e6d5625c132 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 09:18:10 +0200 Subject: [PATCH 139/188] [dissolve] fix call to traverse which needs extra argument now (which is not (yet) used for dissolve) --- .../boost/geometry/algorithms/detail/overlay/traverse.hpp | 2 +- include/boost/geometry/extensions/algorithms/dissolve.hpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/traverse.hpp b/include/boost/geometry/algorithms/detail/overlay/traverse.hpp index 2ec71850b..b9cbea312 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traverse.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traverse.hpp @@ -62,7 +62,7 @@ public : typename RobustPolicy, typename Turns, typename Rings, - typename TurnInfoMap, + typename TurnInfoMap, typename Clusters, typename Visitor > diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index 8ffb7cdfc..78e288f49 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -196,6 +196,8 @@ struct dissolve_ring_or_polygon clusters, geometry, geometry, rescale_policy, side_strategy); + std::map turn_info_per_ring; + detail::overlay::traverse < false, false, @@ -204,7 +206,7 @@ struct dissolve_ring_or_polygon backtrack_for_dissolve >::apply(geometry, geometry, strategy, rescale_policy, - turns, rings, clusters, visitor); + turns, rings, turn_info_per_ring, clusters, visitor); clear_visit_info(turns); @@ -221,7 +223,7 @@ struct dissolve_ring_or_polygon backtrack_for_dissolve >::apply(geometry, geometry, strategy, rescale_policy, - turns, rings, clusters, visitor); + turns, rings, turn_info_per_ring, clusters, visitor); std::map map; detail::overlay::get_ring_turn_info(map, turns, clusters); From 1d23e0b5115e808b1f5d8ff881bac0d627b8a110 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 09:18:56 +0200 Subject: [PATCH 140/188] [dissolve][test] fix test environment, exclude now failing cases --- extensions/test/algorithms/dissolve.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index 46163e0c4..c62876d37 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -225,13 +225,12 @@ void test_all() test_one("5", "POLYGON((0 0,0 4,4 4,4 0,0 4,2 0,0 0))", 0, 8, 12.0); -#endif - // With spike test_one("6", "POLYGON((0 0,0 4,4 4,4 2,6 2,4 2,4 0,0 0))", 0, 6, 16); +#endif // Non intersection, but with duplicate @@ -248,7 +247,7 @@ void test_all() // Hole: interior tangent to exterior test_one("h1", "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 2,2 4,3 2,1 2))", - 0, 6, 16); + 0, 5, 16); // Hole: interior intersecting exterior test_one("h2", @@ -338,9 +337,12 @@ void test_all() test_one("ggl_list_20110307_javier_01_a", "MULTIPOLYGON(((560 -400, 600 -400, 600 -440, 560 -440, 560 -400)), ((480 -400, 520 -400, 520 -440, 480 -440, 480 -400)), ((600 -320, 640 -320, 640 -360, 600 -360, 600 -320)), ((520 -400, 560 -400, 560 -440, 520 -440, 520 -400)))", 1, 14, 6400); + +#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS test_one("ggl_list_20110307_javier_01_b", "POLYGON((0 0, 2000 0, 2000 -2000, 0 -2000, 0 0), (560 -400, 560 -440, 600 -440, 600 -400, 560 -400), (480 -400, 480 -440, 520 -440, 520 -400, 480 -400), (600 -320, 600 -360, 640 -360, 640 -320, 600 -320), (520 -400, 520 -440, 560 -440, 560 -400, 520 -400))", 1, 19, 3993600); +#endif } From aaf2a5f4d6d3ee4ca5e0f2b90afd32c18c429dc4 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 10:00:36 +0200 Subject: [PATCH 141/188] [dissolve] use the map, which is now passed, instead of the old one which was created explicitly --- extensions/test/algorithms/dissolve.cpp | 2 +- include/boost/geometry/extensions/algorithms/dissolve.hpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index c62876d37..b6b6c158c 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -247,7 +247,7 @@ void test_all() // Hole: interior tangent to exterior test_one("h1", "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 2,2 4,3 2,1 2))", - 0, 5, 16); + 0, 6, 16); // Hole: interior intersecting exterior test_one("h2", diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index 78e288f49..7e15e107d 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -225,8 +225,7 @@ struct dissolve_ring_or_polygon strategy, rescale_policy, turns, rings, turn_info_per_ring, clusters, visitor); - std::map map; - detail::overlay::get_ring_turn_info(map, turns, clusters); + detail::overlay::get_ring_turn_info(turn_info_per_ring, turns, clusters); typedef typename geometry::point_type::type point_type; typedef typename Strategy::template area_strategy @@ -238,7 +237,7 @@ struct dissolve_ring_or_polygon std::map selected; - detail::overlay::select_rings(geometry, map, selected, strategy); + detail::overlay::select_rings(geometry, turn_info_per_ring, selected, strategy); // Add intersected rings { From 2ece2f2fca2cb5bb595df2d9a2dc59faf8d12a3f Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 10:01:46 +0200 Subject: [PATCH 142/188] [dissolve] pass visitor, this makes dissolve_overlay_visitor redundant --- .../extensions/algorithms/dissolve.hpp | 54 +++++-------------- .../extensions/multi/algorithms/dissolve.hpp | 14 +++-- 2 files changed, 23 insertions(+), 45 deletions(-) diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index 7e15e107d..e7441f01d 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -110,50 +110,19 @@ public : } }; -struct dissolve_overlay_visitor -{ -public : - void print(char const* /*header*/) - { - } - - template - void print(char const* /*header*/, Turns const& /*turns*/, int /*turn_index*/) - { - } - - template - void print(char const* /*header*/, Turns const& /*turns*/, int /*turn_index*/, int /*op_index*/) - { - } - - template - void visit_turns(int , Turns const& ) {} - - template - void visit_clusters(Clusters const& , Turns const& ) {} - - template - void visit_traverse(Turns const& /*turns*/, Turn const& /*turn*/, Operation const& /*op*/, const char* /*header*/) - { - } - - template - void visit_traverse_reject(Turns const& , Turn const& , Operation const& , - detail::overlay::traverse_error_type ) - {} -}; - - - template struct dissolve_ring_or_polygon { - template + template + < + typename RescalePolicy, typename OutputIterator, + typename Strategy, typename Visitor + > static inline OutputIterator apply(Geometry const& geometry, RescalePolicy const& rescale_policy, OutputIterator out, - Strategy const& strategy) + Strategy const& strategy, + Visitor& visitor) { typedef typename point_type::type point_type; @@ -186,7 +155,6 @@ struct dissolve_ring_or_polygon > cluster_type; cluster_type clusters; - dissolve_overlay_visitor visitor; // Enrich/traverse the polygons twice: once for union... typename Strategy::side_strategy_type const @@ -344,13 +312,15 @@ inline OutputIterator dissolve_inserter(Geometry const& geometry, rescale_policy_type robust_policy = geometry::get_rescale_policy(geometry); + detail::overlay::overlay_null_visitor visitor; + return dispatch::dissolve < typename tag::type, typename tag::type, Geometry, GeometryOut - >::apply(geometry, robust_policy, out, strategy); + >::apply(geometry, robust_policy, out, strategy, visitor); } /*! @@ -397,6 +367,8 @@ inline void dissolve(Geometry const& geometry, Collection& output_collection, St concepts::check(); + detail::overlay::overlay_null_visitor visitor; + dispatch::dissolve < typename tag::type, @@ -405,7 +377,7 @@ inline void dissolve(Geometry const& geometry, Collection& output_collection, St geometry_out >::apply(geometry, detail::no_rescale_policy(), std::back_inserter(output_collection), - strategy); + strategy, visitor); } template diff --git a/include/boost/geometry/extensions/multi/algorithms/dissolve.hpp b/include/boost/geometry/extensions/multi/algorithms/dissolve.hpp index 2abb05e0e..bedc995ff 100644 --- a/include/boost/geometry/extensions/multi/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/multi/algorithms/dissolve.hpp @@ -38,9 +38,14 @@ namespace detail { namespace dissolve template struct dissolve_multi { - template - static inline OutputIterator apply(Multi const& multi, RescalePolicy const& rescale_policy, - OutputIterator out, Strategy const& strategy) + template + < + typename RescalePolicy, typename OutputIterator, + typename Strategy, typename Visitor + > + static inline OutputIterator apply(Multi const& multi, + RescalePolicy const& rescale_policy, OutputIterator out, + Strategy const& strategy, Visitor& visitor) { typedef typename boost::range_value::type polygon_type; typedef typename boost::range_iterator::type iterator_type; @@ -55,7 +60,8 @@ struct dissolve_multi < polygon_type, GeometryOut - >::apply(*it, rescale_policy, std::back_inserter(step1), strategy); + >::apply(*it, rescale_policy, std::back_inserter(step1), + strategy, visitor); } // Step 2: remove mutual overlap From 0bb0e7fcae45cfa0c67db55ed462fad58db86ef0 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 10:23:08 +0200 Subject: [PATCH 143/188] [dissolve][test] enhance test information, check different outputs, prepare svg-debug-info --- extensions/test/algorithms/dissolve.cpp | 88 +++++++++++++++++++++---- 1 file changed, 74 insertions(+), 14 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index b6b6c158c..f5f1be21c 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -68,6 +68,14 @@ struct map_segment }; +template +std::string as_wkt(Geometry const& geometry) +{ + std::ostringstream out; + out << bg::wkt(geometry); + return out.str(); +} + template void test_dissolve(std::string const& caseid, Geometry const& geometry, std::size_t /*expected_hole_count*/, std::size_t expected_point_count, @@ -79,17 +87,65 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, //std::cout << bg::area(geometry) << std::endl; - std::vector dissolved_vector; - bg::dissolve_inserter(geometry, std::back_inserter(dissolved_vector)); + std::vector dissolved1; + + // Check dispatch::dissolve + { + typedef typename bg::strategy::intersection::services::default_strategy + < + typename bg::cs_tag::type + >::type strategy_type; + + typedef typename bg::rescale_policy_type + < + typename bg::point_type::type + >::type rescale_policy_type; + + rescale_policy_type robust_policy + = bg::get_rescale_policy(geometry); + + // This will optionally also create SVG with turn-debug information + strategy_type strategy; + bg::detail::overlay::overlay_null_visitor visitor; + + bg::dispatch::dissolve + < + typename bg::tag::type, + typename bg::tag::type, + Geometry, + GeometryOut + >::apply(geometry, robust_policy, std::back_inserter(dissolved1), + strategy, visitor); + } + + // Check dissolve_inserter + std::vector dissolved2; + bg::dissolve_inserter(geometry, std::back_inserter(dissolved2)); + + // Check dissolve and difference dissolve/dissolve_inserter + std::vector dissolved3; + bg::dissolve(geometry, dissolved3); + + // Make output unique (TODO: this should probably be moved to dissolve itself) + BOOST_FOREACH(GeometryOut& dissolved, dissolved1) + { + bg::unique(dissolved); + } + BOOST_FOREACH(GeometryOut& dissolved, dissolved2) + { + bg::unique(dissolved); + } + BOOST_FOREACH(GeometryOut& dissolved, dissolved3) + { + bg::unique(dissolved); + } typename bg::default_area_result::type length_or_area = 0; //std::size_t holes = 0; std::size_t count = 0; - BOOST_FOREACH(GeometryOut& dissolved, dissolved_vector) + BOOST_FOREACH(GeometryOut& dissolved, dissolved2) { - bg::unique(dissolved); - length_or_area += is_line ? bg::length(dissolved) : bg::area(dissolved); @@ -108,10 +164,19 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, //BOOST_CHECK_EQUAL(holes, expected_hole_count); BOOST_CHECK_CLOSE(length_or_area, expected_length_or_area, percentage); - // Compile check, it should also compile inplace, outputting to the same geometry + BOOST_CHECK_EQUAL(dissolved1.size(), dissolved2.size()); + BOOST_CHECK_EQUAL(dissolved1.size(), dissolved3.size()); + if (dissolved1.size() == dissolved2.size() + && dissolved1.size() == dissolved3.size()) { - std::vector dissolved; - bg::dissolve(geometry, dissolved); + for (std::size_t i = 0; i < dissolved1.size(); i++) + { + std::string const wkt1 = as_wkt(dissolved1[i]); + std::string const wkt2 = as_wkt(dissolved2[i]); + std::string const wkt3 = as_wkt(dissolved3[i]); + BOOST_CHECK_MESSAGE(wkt1 == wkt2, caseid << " : output differs: " << wkt1 << " VERSUS " << wkt2); + BOOST_CHECK_MESSAGE(wkt1 == wkt3, caseid << " : output differs: " << wkt1 << " VERSUS " << wkt3); + } } @@ -138,8 +203,7 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, bg::for_each_segment(geometry, map_segment(mapper)); - - BOOST_FOREACH(GeometryOut& dissolved, dissolved_vector) + BOOST_FOREACH(GeometryOut& dissolved, dissolved1) { mapper.map(dissolved, "opacity:0.6;fill:none;stroke:rgb(255,0,0);stroke-width:5"); } @@ -191,8 +255,6 @@ void test_one(std::string const& caseid, std::string const& wkt, } - - template void test_all() { @@ -232,13 +294,11 @@ void test_all() 0, 6, 16); #endif - // Non intersection, but with duplicate test_one("d1", "POLYGON((0 0,0 4,4 0,4 0,0 0))", 0, 4, 8); - // With many duplicates test_one("d2", "POLYGON((0 0,0 1,0 1,0 1,0 2,0 2,0 3,0 3,0 3,0 3,0 4,2 4,2 4,4 4,4 0,4 0,3 0,3 0,3 0,3 0,3 0,0 0))", From 6e02971a66e724590e3a47d9bec409e41d5e8a87 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 10:52:42 +0200 Subject: [PATCH 144/188] [test] enhance dissolve SVG with information from visitor --- extensions/test/algorithms/dissolve.cpp | 122 +++++++++++------- .../extensions/algorithms/dissolve.hpp | 13 +- 2 files changed, 89 insertions(+), 46 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index f5f1be21c..6d2621dd2 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -40,33 +40,64 @@ # include #endif +#if defined(TEST_WITH_SVG) template -struct map_segment +struct map_visitor { - map_segment(Mapper& m) - : m_mapper(&m) + map_visitor(Mapper& mapper) + : m_mapper(mapper) {} - map_segment& operator=(map_segment const& other) + void print(char const* header) + {} + + template + void print(char const* header, Turns const& turns, int turn_index) { - if(this != &other) + std::string style = "fill:rgb(0,0,0);font-family:Arial;font-size:6px"; + stream(turns, turns[turn_index], turns[turn_index].operations[0], header, style); + } + + template + void print(char const* header, Turns const& turns, int turn_index, int op_index) + { + std::string style = "fill:rgb(0,0,0);font-family:Arial;font-size:6px"; + stream(turns, turns[turn_index], turns[turn_index].operations[op_index], header, style); + } + + template + void visit_turns(int phase, Turns const& turns) + { + typedef typename boost::range_value::type turn_type; + BOOST_FOREACH(turn_type const& turn, turns) { - this->m_mapper = other.m_mapper; + switch (phase) + { + case 1 : // after self_turns + m_mapper.map(turn.point, "fill:rgb(255,128,0);" + "stroke:rgb(0,0,0);stroke-width:1", 4); + break; + // TODO: add enriched information as label + } } - return *this; } + template + void visit_clusters(Clusters const& , Turns const& ) {} - template - inline void operator()(Segment const& s) - { - // create a little offset - m_mapper->map(s, "opacity:0.6;fill:none;stroke:rgb(0,0,0);stroke-width:2"); - } + template + void visit_traverse(Turns const& , Turn const& , Operation const& , char const*) + {} - Mapper* m_mapper; + template + void visit_traverse_reject(Turns const& , Turn const& , Operation const& , + bg::detail::overlay::traverse_error_type ) + {} + + Mapper& m_mapper; }; +#endif template std::string as_wkt(Geometry const& geometry) @@ -106,7 +137,31 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, // This will optionally also create SVG with turn-debug information strategy_type strategy; + + +#if ! defined(TEST_WITH_SVG) bg::detail::overlay::overlay_null_visitor visitor; +#else + std::ostringstream filename; + filename << "dissolve_" << caseid << "_" + << string_from_type::name() + << ".svg"; + + std::ofstream svg(filename.str().c_str()); + + typedef bg::svg_mapper + < + typename bg::point_type::type + > mapper_type; + + mapper_type mapper(svg, 500, 500); + mapper.add(geometry); + + mapper.map(geometry, "fill-opacity:0.5;fill:rgb(153,204,0);" + "stroke:rgb(153,204,0);stroke-width:3;fill-rule:nonzero"); + + map_visitor visitor(mapper); +#endif bg::dispatch::dissolve < @@ -116,6 +171,14 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, GeometryOut >::apply(geometry, robust_policy, std::back_inserter(dissolved1), strategy, visitor); + +#if defined(TEST_WITH_SVG) + BOOST_FOREACH(GeometryOut& dissolved, dissolved1) + { + mapper.map(dissolved, "fill:none;stroke-opacity:0.4;stroke:rgb(255,0,255);stroke-width:8;"); + } +#endif + } // Check dissolve_inserter @@ -178,37 +241,6 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, BOOST_CHECK_MESSAGE(wkt1 == wkt3, caseid << " : output differs: " << wkt1 << " VERSUS " << wkt3); } } - - -#if defined(TEST_WITH_SVG) - { - std::ostringstream filename; - filename << "dissolve_" - << caseid << "_" - << string_from_type::name() - << ".svg"; - - std::ofstream svg(filename.str().c_str()); - - typedef - bg::svg_mapper - < - typename bg::point_type::type - > mapper_type; - - mapper_type mapper(svg, 500, 500); - mapper.add(geometry); - - mapper.map(geometry, "opacity:0.6;fill:rgb(0,0,255);stroke:rgb(0,0,0);stroke-width:1;fill-rule:nonzero"); - - bg::for_each_segment(geometry, map_segment(mapper)); - - BOOST_FOREACH(GeometryOut& dissolved, dissolved1) - { - mapper.map(dissolved, "opacity:0.6;fill:none;stroke:rgb(255,0,0);stroke-width:5"); - } - } -#endif } diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index e7441f01d..838d36f67 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -140,6 +140,8 @@ struct dissolve_ring_or_polygon detail::overlay::assign_null_policy >(geometry, strategy, rescale_policy, turns, policy); + visitor.visit_turns(1, turns); + // The dissolve process is not necessary if there are no turns at all if (boost::size(turns) > 0) @@ -164,6 +166,10 @@ struct dissolve_ring_or_polygon clusters, geometry, geometry, rescale_policy, side_strategy); + visitor.visit_turns(2, turns); + + visitor.visit_clusters(clusters, turns); + std::map turn_info_per_ring; detail::overlay::traverse @@ -176,13 +182,16 @@ struct dissolve_ring_or_polygon strategy, rescale_policy, turns, rings, turn_info_per_ring, clusters, visitor); - clear_visit_info(turns); + visitor.visit_turns(3, turns); // ... and for intersection + clear_visit_info(turns); enrich_intersection_points(turns, clusters, geometry, geometry, rescale_policy, side_strategy); + visitor.visit_turns(4, turns); + detail::overlay::traverse < false, false, @@ -193,6 +202,8 @@ struct dissolve_ring_or_polygon strategy, rescale_policy, turns, rings, turn_info_per_ring, clusters, visitor); + visitor.visit_turns(5, turns); + detail::overlay::get_ring_turn_info(turn_info_per_ring, turns, clusters); typedef typename geometry::point_type::type point_type; From f73abdab1b846142d6fd363a94b0d2bf39a91063 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 11:20:37 +0200 Subject: [PATCH 145/188] [test] add labels for dissolve-svg --- extensions/test/algorithms/dissolve.cpp | 82 ++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index 6d2621dd2..47189aae6 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -1,7 +1,7 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) // Unit Test -// Copyright (c) 2010-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2010-2017 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 @@ -38,6 +38,7 @@ #if defined(TEST_WITH_SVG) # include # include +# include #endif #if defined(TEST_WITH_SVG) @@ -69,6 +70,7 @@ struct map_visitor void visit_turns(int phase, Turns const& turns) { typedef typename boost::range_value::type turn_type; + std:size_t index = 0; BOOST_FOREACH(turn_type const& turn, turns) { switch (phase) @@ -77,8 +79,14 @@ struct map_visitor m_mapper.map(turn.point, "fill:rgb(255,128,0);" "stroke:rgb(0,0,0);stroke-width:1", 4); break; - // TODO: add enriched information as label + case 3 : // after enrich/traverse for union + label_turn(index, turn, -5, "fill:rgb(0,0,128);"); + break; + case 5 : // after enrich/traverse for intersection + label_turn(index, turn, 5, "fill:rgb(0,0,0);"); + break; } + index++; } } @@ -94,6 +102,76 @@ struct map_visitor bg::detail::overlay::traverse_error_type ) {} +private : + + template + bool label_operation(Turn const& turn, std::size_t index, std::ostream& os) + { + os << bg::operation_char(turn.operations[index].operation); + bool result = false; + if (! turn.discarded) + { + if (turn.operations[index].enriched.next_ip_index != -1) + { + os << "->" << turn.operations[index].enriched.next_ip_index; + if (turn.operations[index].enriched.next_ip_index != -1) + { + result = true; + } + } + else + { + os << "->" << turn.operations[index].enriched.travels_to_ip_index; + if (turn.operations[index].enriched.travels_to_ip_index != -1) + { + result = true; + } + } + } + + return result; + } + + template + void label_turn(std::size_t index, Turn const& turn, int y_offset, std::string const& color) + { + std::ostringstream out; + out << index << " "; + if (turn.cluster_id != -1) + { + out << " c=" << turn.cluster_id << " "; + } + bool lab1 = label_operation(turn, 0, out); + out << " / "; + bool lab2 = label_operation(turn, 1, out); + if (turn.switch_source) + { + out << "#"; + } + if (turn.discarded) + { + out << "!"; + } + + std::string font8 = "font-family:Arial;font-size:6px"; + std::string font6 = "font-family:Arial;font-size:4px"; + std::string style = color + ";" + font8; + if (turn.discarded) + { + style = "fill:rgb(92,92,92);" + font6; + } + else if (turn.cluster_id != -1) + { + style = color + ";" + font8; + } + else if (! lab1 || ! lab2) + { + style = color + ";" + font6; + } + + m_mapper.text(turn.point, out.str(), style, 5, y_offset, 6); + } + Mapper& m_mapper; }; From 5eed687af3c5eb45d7a7d01566617fdf2d0f1ad0 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 11:21:50 +0200 Subject: [PATCH 146/188] [dissolve] clear turns between the two phases of enrich/traverse --- .../extensions/algorithms/dissolve.hpp | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index 838d36f67..40bcf3fb3 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -113,6 +113,28 @@ public : template struct dissolve_ring_or_polygon { + template + static inline void clear(Turns& turns) + { + typedef typename boost::range_value::type turn_type; + + for (typename boost::range_iterator::type + it = boost::begin(turns); + it != boost::end(turns); + ++it) + { + turn_type& turn = *it; + turn.discarded = false; + turn.cluster_id = -1; + turn.has_colocated_both = false; + turn.switch_source = false; + turn.touch_only = false; + } + + clear_visit_info(turns); + } + + template < typename RescalePolicy, typename OutputIterator, @@ -158,7 +180,7 @@ struct dissolve_ring_or_polygon cluster_type clusters; - // Enrich/traverse the polygons twice: once for union... + // Enrich/traverse the polygons twice: first for union... typename Strategy::side_strategy_type const side_strategy = strategy.get_side_strategy(); @@ -184,8 +206,8 @@ struct dissolve_ring_or_polygon visitor.visit_turns(3, turns); - // ... and for intersection - clear_visit_info(turns); + // ... and then for intersection + clear(turns); enrich_intersection_points(turns, clusters, geometry, geometry, rescale_policy, side_strategy); From 7783eb6a240fff753b5fe62c152840e2193f8bdc Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 12:30:48 +0200 Subject: [PATCH 147/188] [overlay] distinguish dissolve intersection/union to be able to avoid self-turn being true for dissolve (otherwise all is true), to avoid having those turns discarded. This fixes some (but not all) of the regressions detected earlier --- extensions/test/algorithms/dissolve.cpp | 9 +++---- .../detail/overlay/is_self_turn.hpp | 11 +++++++- .../detail/overlay/overlay_type.hpp | 27 ++++++++++++++++++- .../extensions/algorithms/dissolve.hpp | 12 ++++----- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index 47189aae6..1b5f852a4 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -386,23 +386,22 @@ void test_all() test_one("3", "POLYGON((0 2,2 4,2 0,4 2,0 2))", 0, 8, 4.0); +#endif // Self tangent - polygons are now included twice test_one("4", "POLYGON((0 0,0 4,4 4,4 0,2 4,0 0))", - 0, 8, 8.0); - + 0, 7, 8.0); // Self tangent in corner - polygons are now included twice test_one("5", "POLYGON((0 0,0 4,4 4,4 0,0 4,2 0,0 0))", - 0, 8, 12.0); + 0, 7, 12.0); // With spike test_one("6", "POLYGON((0 0,0 4,4 4,4 2,6 2,4 2,4 0,0 0))", 0, 6, 16); -#endif // Non intersection, but with duplicate test_one("d1", @@ -508,11 +507,9 @@ void test_all() "MULTIPOLYGON(((560 -400, 600 -400, 600 -440, 560 -440, 560 -400)), ((480 -400, 520 -400, 520 -440, 480 -440, 480 -400)), ((600 -320, 640 -320, 640 -360, 600 -360, 600 -320)), ((520 -400, 560 -400, 560 -440, 520 -440, 520 -400)))", 1, 14, 6400); -#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS test_one("ggl_list_20110307_javier_01_b", "POLYGON((0 0, 2000 0, 2000 -2000, 0 -2000, 0 0), (560 -400, 560 -440, 600 -440, 600 -400, 560 -400), (480 -400, 480 -440, 520 -440, 520 -400, 480 -400), (600 -320, 600 -360, 640 -360, 640 -320, 600 -320), (520 -400, 520 -440, 560 -440, 560 -400, 520 -400))", 1, 19, 3993600); -#endif } diff --git a/include/boost/geometry/algorithms/detail/overlay/is_self_turn.hpp b/include/boost/geometry/algorithms/detail/overlay/is_self_turn.hpp index 9cb7a0fca..9423a24b3 100644 --- a/include/boost/geometry/algorithms/detail/overlay/is_self_turn.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/is_self_turn.hpp @@ -41,7 +41,7 @@ struct is_self_turn_check }; template <> -struct is_self_turn_check +struct is_self_turn_check { template static inline bool apply(Turn const& turn) @@ -50,6 +50,15 @@ struct is_self_turn_check } }; +template <> +struct is_self_turn_check +{ + template + static inline bool apply(Turn const& turn) + { + return false; + } +}; template bool is_self_turn(Turn const& turn) diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay_type.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay_type.hpp index 0f6084097..f3ec9eaa6 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay_type.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay_type.hpp @@ -21,7 +21,8 @@ enum overlay_type overlay_intersection, overlay_difference, overlay_buffer, - overlay_dissolve + overlay_dissolve_union, + overlay_dissolve_intersection }; #ifndef DOXYGEN_NO_DETAIL @@ -41,6 +42,17 @@ enum operation_type template struct operation_from_overlay +{ +}; + +template <> +struct operation_from_overlay +{ + static const operation_type value = operation_union; +}; + +template <> +struct operation_from_overlay { static const operation_type value = operation_union; }; @@ -57,6 +69,19 @@ struct operation_from_overlay static const operation_type value = operation_intersection; }; +template <> +struct operation_from_overlay +{ + static const operation_type value = operation_union; +}; + +template <> +struct operation_from_overlay +{ + static const operation_type value = operation_intersection; +}; + + }} // namespace detail::overlay #endif //DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index 40bcf3fb3..ac0bc5a84 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -184,7 +184,7 @@ struct dissolve_ring_or_polygon typename Strategy::side_strategy_type const side_strategy = strategy.get_side_strategy(); - enrich_intersection_points(turns, + enrich_intersection_points(turns, clusters, geometry, geometry, rescale_policy, side_strategy); @@ -198,7 +198,7 @@ struct dissolve_ring_or_polygon < false, false, Geometry, Geometry, - overlay_dissolve, + overlay_dissolve_union, backtrack_for_dissolve >::apply(geometry, geometry, strategy, rescale_policy, @@ -208,7 +208,7 @@ struct dissolve_ring_or_polygon // ... and then for intersection clear(turns); - enrich_intersection_points(turns, + enrich_intersection_points(turns, clusters, geometry, geometry, rescale_policy, side_strategy); @@ -218,7 +218,7 @@ struct dissolve_ring_or_polygon < false, false, Geometry, Geometry, - overlay_intersection, + overlay_dissolve_intersection, backtrack_for_dissolve >::apply(geometry, geometry, strategy, rescale_policy, @@ -226,7 +226,7 @@ struct dissolve_ring_or_polygon visitor.visit_turns(5, turns); - detail::overlay::get_ring_turn_info(turn_info_per_ring, turns, clusters); + detail::overlay::get_ring_turn_info(turn_info_per_ring, turns, clusters); typedef typename geometry::point_type::type point_type; typedef typename Strategy::template area_strategy @@ -238,7 +238,7 @@ struct dissolve_ring_or_polygon std::map selected; - detail::overlay::select_rings(geometry, turn_info_per_ring, selected, strategy); + detail::overlay::select_rings(geometry, turn_info_per_ring, selected, strategy); // Add intersected rings { From ee02d25a42fc2dd7195ca518fa7d2082ebe93c7c Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 13:02:14 +0200 Subject: [PATCH 148/188] [test] add Johan's testcases for dissolve --- extensions/test/algorithms/dissolve.cpp | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index 1b5f852a4..305f70bae 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -41,6 +41,21 @@ # include #endif + +// Testcases send by Johan Doré at September 24, 2017: +static std::string dissolve_mail_2017_09_24_a = "POLYGON((0 1, 1 0, 1 1, 0 0, 0 1))"; // two triangles +static std::string dissolve_mail_2017_09_24_b = "POLYGON((1 0, 0 0, 0 4, 4 4, 4 0))"; // input is not closed +static std::string dissolve_mail_2017_09_24_c = "POLYGON((0 0, 1 0, 0 -1, 0.0001 1))"; // spike and not closed +static std::string dissolve_mail_2017_09_24_d = "POLYGON((0 0, 1 0, 0 -1, 0 1))"; // spike and not closed + +// Large polygon with self-intersections +static std::string dissolve_mail_2017_09_24_e = "POLYGON((25.21475410461420 -1.19600892066955, 25.36818313598630 -1.20732820034027, 25.36953926086420 -1.20868659019470, 25.37180328369140 -1.20959210395812, 25.37451934814450 -1.21049761772155, 25.37632942199700 -1.21185600757598, 25.37904357910150 -1.21276152133941, 25.38085556030270 -1.21321427822113, 25.38221168518060 -1.21366703510284, 25.38311767578120 -1.21411991119384, 25.38447570800780 -1.21411991119384, 25.38447570800780 -1.21185600757598, 25.38492774963370 -1.21140325069427, 25.38492774963370 -1.21095049381256, 25.38945388793940 -1.21095049381256, 25.38990592956540 -1.21049761772155, 25.39035987854000 -1.21004486083984, 25.39081192016600 -1.21004486083984, 25.39081192016600 -1.20959210395812, 25.39126396179190 -1.20913934707641, 25.39171600341790 -1.20913934707641, 25.39262199401850 -1.20868659019470, 25.39262199401850 -1.20823383331298, 25.39352798461910 -1.20823383331298, 25.39398002624510 -1.20778107643127, 25.39443206787100 -1.20778107643127, 25.39443206787100 -1.20732820034027, 25.39488410949700 -1.20687544345855, 25.39443206787100 -1.20642268657684, 25.39420700073240 -1.20596992969512, 25.39386749267570 -1.20551717281341, 25.39352798461910 -1.20551717281341, 25.39352798461910 -1.20506441593170, 25.39386749267570 -1.20551717281341, 25.39398002624510 -1.20551717281341, 25.39420700073240 -1.20596992969512, 25.39488410949700 -1.20687544345855, 25.39533805847160 -1.20778107643127, 25.39579010009760 -1.20823383331298, 25.39579010009760 -1.20868659019470, 25.39624214172360 -1.20913934707641, 25.39669609069820 -1.20959210395812, 25.39669609069820 -1.21049761772155, 25.39714813232420 -1.21095049381256, 25.39760017395010 -1.21185600757598, 25.39805221557610 -1.21321427822113, 25.39850616455070 -1.21411991119384, 25.39895820617670 -1.21547818183898, 25.39895820617670 -1.21638369560241, 25.39986419677730 -1.21819484233856, 25.40031623840330 -1.21955311298370, 25.40076828002920 -1.22045862674713, 25.40076828002920 -1.22181701660156, 25.40122032165520 -1.22226977348327, 25.40122032165520 -1.22408080101013, 25.40212631225580 -1.22498643398284, 25.40212631225580 -1.22634470462799, 25.40257835388180 -1.22725021839141, 25.40257835388180 -1.22770309448242, 25.40303230285640 -1.22860860824584, 25.40303230285640 -1.23041963577270, 25.40348434448240 -1.23041963577270, 25.21475410461420 -1.19600892066955))"; + +// Idem but moving closing point eastwards to view self-intersections better +static std::string dissolve_mail_2017_09_24_f = "POLYGON((25.35475410461420 -1.20600892066955, 25.36818313598630 -1.20732820034027, 25.36953926086420 -1.20868659019470, 25.37180328369140 -1.20959210395812, 25.37451934814450 -1.21049761772155, 25.37632942199700 -1.21185600757598, 25.37904357910150 -1.21276152133941, 25.38085556030270 -1.21321427822113, 25.38221168518060 -1.21366703510284, 25.38311767578120 -1.21411991119384, 25.38447570800780 -1.21411991119384, 25.38447570800780 -1.21185600757598, 25.38492774963370 -1.21140325069427, 25.38492774963370 -1.21095049381256, 25.38945388793940 -1.21095049381256, 25.38990592956540 -1.21049761772155, 25.39035987854000 -1.21004486083984, 25.39081192016600 -1.21004486083984, 25.39081192016600 -1.20959210395812, 25.39126396179190 -1.20913934707641, 25.39171600341790 -1.20913934707641, 25.39262199401850 -1.20868659019470, 25.39262199401850 -1.20823383331298, 25.39352798461910 -1.20823383331298, 25.39398002624510 -1.20778107643127, 25.39443206787100 -1.20778107643127, 25.39443206787100 -1.20732820034027, 25.39488410949700 -1.20687544345855, 25.39443206787100 -1.20642268657684, 25.39420700073240 -1.20596992969512, 25.39386749267570 -1.20551717281341, 25.39352798461910 -1.20551717281341, 25.39352798461910 -1.20506441593170, 25.39386749267570 -1.20551717281341, 25.39398002624510 -1.20551717281341, 25.39420700073240 -1.20596992969512, 25.39488410949700 -1.20687544345855, 25.39533805847160 -1.20778107643127, 25.39579010009760 -1.20823383331298, 25.39579010009760 -1.20868659019470, 25.39624214172360 -1.20913934707641, 25.39669609069820 -1.20959210395812, 25.39669609069820 -1.21049761772155, 25.39714813232420 -1.21095049381256, 25.39760017395010 -1.21185600757598, 25.39805221557610 -1.21321427822113, 25.39850616455070 -1.21411991119384, 25.39895820617670 -1.21547818183898, 25.39895820617670 -1.21638369560241, 25.39986419677730 -1.21819484233856, 25.40031623840330 -1.21955311298370, 25.40076828002920 -1.22045862674713, 25.40076828002920 -1.22181701660156, 25.40122032165520 -1.22226977348327, 25.40122032165520 -1.22408080101013, 25.40212631225580 -1.22498643398284, 25.40212631225580 -1.22634470462799, 25.40257835388180 -1.22725021839141, 25.40257835388180 -1.22770309448242, 25.40303230285640 -1.22860860824584, 25.40303230285640 -1.23041963577270, 25.40348434448240 -1.23041963577270, 25.35475410461420 -1.20600892066955))"; + + + #if defined(TEST_WITH_SVG) template struct map_visitor @@ -364,6 +379,9 @@ void test_one(std::string const& caseid, std::string const& wkt, } +#define TEST_DISSOLVE(caseid, area, clips, holes, points) \ + (test_one) ( #caseid, caseid, holes, points, area) + template void test_all() @@ -443,6 +461,17 @@ void test_all() "POLYGON((5 0,2.5 9,9.5 3.5,0.5 3.5,7.5 9,5 0))", 0, 11, 25.6158412); +#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS + TEST_DISSOLVE(dissolve_mail_2017_09_24_a, 99.99, 2, 0, 6); +#endif + + // These are not all complete yet + TEST_DISSOLVE(dissolve_mail_2017_09_24_b, 16.0, 1, 0, 5); + TEST_DISSOLVE(dissolve_mail_2017_09_24_c, 0.4999, 1, 0, 4); + TEST_DISSOLVE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 4); + TEST_DISSOLVE(dissolve_mail_2017_09_24_e, 0.0018011, 1, 0, 64); + TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.0003612064, 2, 0, 64); + #ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS test_one("pentagram_rev", "POLYGON((5 0,7.5 9,0.5 3.5,9.5 3.5,2.5 9,5 0))", From a73246bbd23a91c42a367f635560e712150188fb Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 13:46:01 +0200 Subject: [PATCH 149/188] [dissolve] change in order of algorithm / comment --- .../detail/overlay/assign_parents.hpp | 2 +- .../extensions/algorithms/dissolve.hpp | 188 +++++++++--------- 2 files changed, 93 insertions(+), 97 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp index 78160f520..7cc91f726 100644 --- a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -372,7 +372,7 @@ inline void assign_parents(Geometry1 const& geometry1, } -// Version for one geometry (called by buffer) +// Version for one geometry (called by buffer/dissolve) template < typename Geometry, diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index ac0bc5a84..6fef44a64 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -164,108 +164,104 @@ struct dissolve_ring_or_polygon visitor.visit_turns(1, turns); - // The dissolve process is not necessary if there are no turns at all - - if (boost::size(turns) > 0) - { - typedef typename ring_type::type ring_type; - typedef std::vector out_vector; - out_vector rings; - - typedef std::map - < - signed_size_type, - detail::overlay::cluster_info - > cluster_type; - - cluster_type clusters; - - // Enrich/traverse the polygons twice: first for union... - typename Strategy::side_strategy_type const - side_strategy = strategy.get_side_strategy(); - - enrich_intersection_points(turns, - clusters, geometry, geometry, rescale_policy, - side_strategy); - - visitor.visit_turns(2, turns); - - visitor.visit_clusters(clusters, turns); - - std::map turn_info_per_ring; - - detail::overlay::traverse - < - false, false, - Geometry, Geometry, - overlay_dissolve_union, - backtrack_for_dissolve - >::apply(geometry, geometry, - strategy, rescale_policy, - turns, rings, turn_info_per_ring, clusters, visitor); - - visitor.visit_turns(3, turns); - - // ... and then for intersection - clear(turns); - enrich_intersection_points(turns, - clusters, geometry, geometry, rescale_policy, - side_strategy); - - visitor.visit_turns(4, turns); - - detail::overlay::traverse - < - false, false, - Geometry, Geometry, - overlay_dissolve_intersection, - backtrack_for_dissolve - >::apply(geometry, geometry, - strategy, rescale_policy, - turns, rings, turn_info_per_ring, clusters, visitor); - - visitor.visit_turns(5, turns); - - detail::overlay::get_ring_turn_info(turn_info_per_ring, turns, clusters); - - typedef typename geometry::point_type::type point_type; - typedef typename Strategy::template area_strategy - < - point_type - >::type area_strategy_type; - typedef typename area_strategy_type::return_type area_result_type; - typedef detail::overlay::ring_properties properties; - - std::map selected; - - detail::overlay::select_rings(geometry, turn_info_per_ring, selected, strategy); - - // Add intersected rings - { - area_strategy_type const area_strategy = strategy.template get_area_strategy(); - - ring_identifier id(2, 0, -1); - for (typename boost::range_iterator const>::type - it = boost::begin(rings); - it != boost::end(rings); - ++it) - { - selected[id] = properties(*it, area_strategy); - id.multi_index++; - } - } - - detail::overlay::assign_parents(geometry, rings, selected, strategy, true); - return detail::overlay::add_rings(selected, geometry, rings, out); - - } - else + if (boost::size(turns) == 0) { + // No self-turns, then add original geometry GeometryOut g; geometry::convert(geometry, g); *out++ = g; return out; } + + typedef typename ring_type::type ring_type; + typedef std::vector out_vector; + out_vector rings; + + typedef std::map + < + signed_size_type, + detail::overlay::cluster_info + > cluster_type; + + cluster_type clusters; + + // Enrich/traverse the polygons twice: first for union... + typename Strategy::side_strategy_type const + side_strategy = strategy.get_side_strategy(); + + enrich_intersection_points(turns, + clusters, geometry, geometry, rescale_policy, + side_strategy); + + visitor.visit_turns(2, turns); + + visitor.visit_clusters(clusters, turns); + + std::map turn_info_per_ring; + + detail::overlay::traverse + < + false, false, + Geometry, Geometry, + overlay_dissolve_union, + backtrack_for_dissolve + >::apply(geometry, geometry, + strategy, rescale_policy, + turns, rings, turn_info_per_ring, clusters, visitor); + + visitor.visit_turns(3, turns); + + // ... and then for intersection + clear(turns); + enrich_intersection_points(turns, + clusters, geometry, geometry, rescale_policy, + side_strategy); + + visitor.visit_turns(4, turns); + + detail::overlay::traverse + < + false, false, + Geometry, Geometry, + overlay_dissolve_intersection, + backtrack_for_dissolve + >::apply(geometry, geometry, + strategy, rescale_policy, + turns, rings, turn_info_per_ring, clusters, visitor); + + visitor.visit_turns(5, turns); + + detail::overlay::get_ring_turn_info(turn_info_per_ring, turns, clusters); + + typedef typename geometry::point_type::type point_type; + typedef typename Strategy::template area_strategy + < + point_type + >::type area_strategy_type; + typedef typename area_strategy_type::return_type area_result_type; + typedef detail::overlay::ring_properties properties; + + std::map selected; + + detail::overlay::select_rings(geometry, turn_info_per_ring, selected, strategy); + + // Add intersected rings + { + area_strategy_type const area_strategy = strategy.template get_area_strategy(); + + ring_identifier id(2, 0, -1); + for (typename boost::range_iterator const>::type + it = boost::begin(rings); + it != boost::end(rings); + ++it) + { + selected[id] = properties(*it, area_strategy); + id.multi_index++; + } + } + + detail::overlay::assign_parents(geometry, rings, selected, strategy, true); + return detail::overlay::add_rings(selected, geometry, rings, out); } }; From 1187e1e8058779c95b989c955ef736875faa0726 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 15:46:20 +0200 Subject: [PATCH 150/188] [dissolve] fix case with two triangles which needs to be corrected w.r.t. next turn if there is only one turn --- extensions/test/algorithms/dissolve.cpp | 18 +++++++++--------- .../algorithms/detail/overlay/traversal.hpp | 7 ++++++- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index 305f70bae..e9b6a4587 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -399,22 +399,20 @@ void test_all() "POLYGON((1 2,1 1,2 1,2 2.25,3 2.25,3 0,0 0,0 3,3 3,2.75 2,1 2))", 1, 12, 7.9296875); -#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS // Self intersecting in last segment test_one("3", "POLYGON((0 2,2 4,2 0,4 2,0 2))", 0, 8, 4.0); -#endif // Self tangent - polygons are now included twice test_one("4", "POLYGON((0 0,0 4,4 4,4 0,2 4,0 0))", - 0, 7, 8.0); + 0, 8, 8.0); // Self tangent in corner - polygons are now included twice test_one("5", "POLYGON((0 0,0 4,4 4,4 0,0 4,2 0,0 0))", - 0, 7, 12.0); + 0, 8, 12.0); // With spike test_one("6", @@ -461,16 +459,14 @@ void test_all() "POLYGON((5 0,2.5 9,9.5 3.5,0.5 3.5,7.5 9,5 0))", 0, 11, 25.6158412); -#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS - TEST_DISSOLVE(dissolve_mail_2017_09_24_a, 99.99, 2, 0, 6); -#endif + TEST_DISSOLVE(dissolve_mail_2017_09_24_a, 0.5, 2, 0, 8); // These are not all complete yet TEST_DISSOLVE(dissolve_mail_2017_09_24_b, 16.0, 1, 0, 5); TEST_DISSOLVE(dissolve_mail_2017_09_24_c, 0.4999, 1, 0, 4); TEST_DISSOLVE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 4); - TEST_DISSOLVE(dissolve_mail_2017_09_24_e, 0.0018011, 1, 0, 64); - TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.0003612064, 2, 0, 64); + TEST_DISSOLVE(dissolve_mail_2017_09_24_e, 0.001801138128, 1, 0, 69); + TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.000361308800, 2, 0, 69); #ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS test_one("pentagram_rev", @@ -478,20 +474,24 @@ void test_all() 0, 11, 25.6158412); // Poygons contain too many polygons +#endif // CCW polygons should turn CW after dissolve test_one("cw", "POLYGON((2 8,8 8,8 0,0 0,0 6,4 6,4 4,2 4,2 8))", 0, 7, 60); +#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS test_one("ccw", "POLYGON((2 8,2 4,4 4,4 6,0 6,0 0,8 0,8 8,2 8))", 0, 12, 64); // TODO: should have the same, 7, 60. The polygon is dissolved (splitted) but the extra polygon is still on top of the other +#endif // https://svn.boost.org/trac/boost/ticket/10713 test_one("ticket_10713", "POLYGON((-0.7189743518829346 4.1308121681213379, 0.0831791982054710 4.1034231185913086, 0.1004156470298767 4.1107301712036133, 0.1044322624802589 4.1026973724365234, 0.0831791982054710 4.1034231185913086, -0.7711903452873230 3.7412264347076416, -0.7189743518829346 4.1308121681213379))", 0, 8, 0.157052766); +#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS // Reported by Artem Pavlenko at gitter // https://gitter.im/boostorg/geometry?at=58ef46408e4b63533dc49b48 test_one("artem1", diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp index 73b2b53c3..735ca82cb 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal.hpp @@ -661,7 +661,9 @@ struct traversal turn_operation_type const& start_op, int start_op_index) const { - if (OverlayType != overlay_buffer) + if (OverlayType != overlay_buffer + && OverlayType != overlay_dissolve_union + && OverlayType != overlay_dissolve_intersection) { return; } @@ -689,6 +691,9 @@ struct traversal bool const correct = ! start_turn.both(operation_union) + && start_op.seg_id.source_index == other_op.seg_id.source_index + && start_op.seg_id.multi_index == other_op.seg_id.multi_index + && start_op.seg_id.ring_index == other_op.seg_id.ring_index && start_op.seg_id.segment_index == to_vertex_index; #if defined(BOOST_GEOMETRY_DEBUG_TRAVERSE) From 5f865f79f254f90f0429e0a50937f12dd846a3db Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 16:01:40 +0200 Subject: [PATCH 151/188] [test] dissolve: start testing #clips and #holes too --- extensions/test/algorithms/dissolve.cpp | 140 +++++++++++++----------- 1 file changed, 76 insertions(+), 64 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index e9b6a4587..7b8fcbf46 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -42,18 +42,53 @@ #endif -// Testcases send by Johan Doré at September 24, 2017: -static std::string dissolve_mail_2017_09_24_a = "POLYGON((0 1, 1 0, 1 1, 0 0, 0 1))"; // two triangles -static std::string dissolve_mail_2017_09_24_b = "POLYGON((1 0, 0 0, 0 4, 4 4, 4 0))"; // input is not closed -static std::string dissolve_mail_2017_09_24_c = "POLYGON((0 0, 1 0, 0 -1, 0.0001 1))"; // spike and not closed -static std::string dissolve_mail_2017_09_24_d = "POLYGON((0 0, 1 0, 0 -1, 0 1))"; // spike and not closed +namespace +{ + // Simplex + std::string const dissolve_1 = "POLYGON((0 0,0 4,1.5 2.5,2.5 1.5,4 0,0 0))"; -// Large polygon with self-intersections -static std::string dissolve_mail_2017_09_24_e = "POLYGON((25.21475410461420 -1.19600892066955, 25.36818313598630 -1.20732820034027, 25.36953926086420 -1.20868659019470, 25.37180328369140 -1.20959210395812, 25.37451934814450 -1.21049761772155, 25.37632942199700 -1.21185600757598, 25.37904357910150 -1.21276152133941, 25.38085556030270 -1.21321427822113, 25.38221168518060 -1.21366703510284, 25.38311767578120 -1.21411991119384, 25.38447570800780 -1.21411991119384, 25.38447570800780 -1.21185600757598, 25.38492774963370 -1.21140325069427, 25.38492774963370 -1.21095049381256, 25.38945388793940 -1.21095049381256, 25.38990592956540 -1.21049761772155, 25.39035987854000 -1.21004486083984, 25.39081192016600 -1.21004486083984, 25.39081192016600 -1.20959210395812, 25.39126396179190 -1.20913934707641, 25.39171600341790 -1.20913934707641, 25.39262199401850 -1.20868659019470, 25.39262199401850 -1.20823383331298, 25.39352798461910 -1.20823383331298, 25.39398002624510 -1.20778107643127, 25.39443206787100 -1.20778107643127, 25.39443206787100 -1.20732820034027, 25.39488410949700 -1.20687544345855, 25.39443206787100 -1.20642268657684, 25.39420700073240 -1.20596992969512, 25.39386749267570 -1.20551717281341, 25.39352798461910 -1.20551717281341, 25.39352798461910 -1.20506441593170, 25.39386749267570 -1.20551717281341, 25.39398002624510 -1.20551717281341, 25.39420700073240 -1.20596992969512, 25.39488410949700 -1.20687544345855, 25.39533805847160 -1.20778107643127, 25.39579010009760 -1.20823383331298, 25.39579010009760 -1.20868659019470, 25.39624214172360 -1.20913934707641, 25.39669609069820 -1.20959210395812, 25.39669609069820 -1.21049761772155, 25.39714813232420 -1.21095049381256, 25.39760017395010 -1.21185600757598, 25.39805221557610 -1.21321427822113, 25.39850616455070 -1.21411991119384, 25.39895820617670 -1.21547818183898, 25.39895820617670 -1.21638369560241, 25.39986419677730 -1.21819484233856, 25.40031623840330 -1.21955311298370, 25.40076828002920 -1.22045862674713, 25.40076828002920 -1.22181701660156, 25.40122032165520 -1.22226977348327, 25.40122032165520 -1.22408080101013, 25.40212631225580 -1.22498643398284, 25.40212631225580 -1.22634470462799, 25.40257835388180 -1.22725021839141, 25.40257835388180 -1.22770309448242, 25.40303230285640 -1.22860860824584, 25.40303230285640 -1.23041963577270, 25.40348434448240 -1.23041963577270, 25.21475410461420 -1.19600892066955))"; + // Self intersecting + std::string const dissolve_2 = "POLYGON((1 2,1 1,2 1,2 2.25,3 2.25,3 0,0 0,0 3,3 3,2.75 2,1 2))"; -// Idem but moving closing point eastwards to view self-intersections better -static std::string dissolve_mail_2017_09_24_f = "POLYGON((25.35475410461420 -1.20600892066955, 25.36818313598630 -1.20732820034027, 25.36953926086420 -1.20868659019470, 25.37180328369140 -1.20959210395812, 25.37451934814450 -1.21049761772155, 25.37632942199700 -1.21185600757598, 25.37904357910150 -1.21276152133941, 25.38085556030270 -1.21321427822113, 25.38221168518060 -1.21366703510284, 25.38311767578120 -1.21411991119384, 25.38447570800780 -1.21411991119384, 25.38447570800780 -1.21185600757598, 25.38492774963370 -1.21140325069427, 25.38492774963370 -1.21095049381256, 25.38945388793940 -1.21095049381256, 25.38990592956540 -1.21049761772155, 25.39035987854000 -1.21004486083984, 25.39081192016600 -1.21004486083984, 25.39081192016600 -1.20959210395812, 25.39126396179190 -1.20913934707641, 25.39171600341790 -1.20913934707641, 25.39262199401850 -1.20868659019470, 25.39262199401850 -1.20823383331298, 25.39352798461910 -1.20823383331298, 25.39398002624510 -1.20778107643127, 25.39443206787100 -1.20778107643127, 25.39443206787100 -1.20732820034027, 25.39488410949700 -1.20687544345855, 25.39443206787100 -1.20642268657684, 25.39420700073240 -1.20596992969512, 25.39386749267570 -1.20551717281341, 25.39352798461910 -1.20551717281341, 25.39352798461910 -1.20506441593170, 25.39386749267570 -1.20551717281341, 25.39398002624510 -1.20551717281341, 25.39420700073240 -1.20596992969512, 25.39488410949700 -1.20687544345855, 25.39533805847160 -1.20778107643127, 25.39579010009760 -1.20823383331298, 25.39579010009760 -1.20868659019470, 25.39624214172360 -1.20913934707641, 25.39669609069820 -1.20959210395812, 25.39669609069820 -1.21049761772155, 25.39714813232420 -1.21095049381256, 25.39760017395010 -1.21185600757598, 25.39805221557610 -1.21321427822113, 25.39850616455070 -1.21411991119384, 25.39895820617670 -1.21547818183898, 25.39895820617670 -1.21638369560241, 25.39986419677730 -1.21819484233856, 25.40031623840330 -1.21955311298370, 25.40076828002920 -1.22045862674713, 25.40076828002920 -1.22181701660156, 25.40122032165520 -1.22226977348327, 25.40122032165520 -1.22408080101013, 25.40212631225580 -1.22498643398284, 25.40212631225580 -1.22634470462799, 25.40257835388180 -1.22725021839141, 25.40257835388180 -1.22770309448242, 25.40303230285640 -1.22860860824584, 25.40303230285640 -1.23041963577270, 25.40348434448240 -1.23041963577270, 25.35475410461420 -1.20600892066955))"; + // Self intersecting in last segment + std::string const dissolve_3 = "POLYGON((0 2,2 4,2 0,4 2,0 2))"; + // Self tangent - polygons are now included twice + std::string const dissolve_4 = "POLYGON((0 0,0 4,4 4,4 0,2 4,0 0))"; + + // Self tangent in corner - polygons are now included twice + std::string const dissolve_5 = "POLYGON((0 0,0 4,4 4,4 0,0 4,2 0,0 0))"; + + // With spike + std::string const dissolve_6 = "POLYGON((0 0,0 4,4 4,4 2,6 2,4 2,4 0,0 0))"; + + // Non intersection, but with duplicate + std::string const dissolve_d1 = "POLYGON((0 0,0 4,4 0,4 0,0 0))"; + + // With many duplicate points + std::string const dissolve_d2 = "POLYGON((0 0,0 1,0 1,0 1,0 2,0 2,0 3,0 3,0 3,0 3,0 4,2 4,2 4,4 4,4 0,4 0,3 0,3 0,3 0,3 0,3 0,0 0))"; + + // Case with touching reversed interior ring inside, which should be removed + std::string const dissolve_h1_a = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 2,2 4,3 2,1 2))"; + + // Case with correct interior ring inside, which should be stay + std::string const dissolve_h1_b = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 2,3 2,2 4,1 2))"; + + // Testcases send by Johan Doré at September 24, 2017: + std::string const dissolve_mail_2017_09_24_a = "POLYGON((0 1, 1 0, 1 1, 0 0, 0 1))"; // two triangles + std::string const dissolve_mail_2017_09_24_b = "POLYGON((1 0, 0 0, 0 4, 4 4, 4 0))"; // input is not closed + std::string const dissolve_mail_2017_09_24_c = "POLYGON((0 0, 1 0, 0 -1, 0.0001 1))"; // spike and not closed + std::string const dissolve_mail_2017_09_24_d = "POLYGON((0 0, 1 0, 0 -1, 0 1))"; // spike and not closed + + // Large polygon with self-intersections + std::string const dissolve_mail_2017_09_24_e = "POLYGON((25.21475410461420 -1.19600892066955, 25.36818313598630 -1.20732820034027, 25.36953926086420 -1.20868659019470, 25.37180328369140 -1.20959210395812, 25.37451934814450 -1.21049761772155, 25.37632942199700 -1.21185600757598, 25.37904357910150 -1.21276152133941, 25.38085556030270 -1.21321427822113, 25.38221168518060 -1.21366703510284, 25.38311767578120 -1.21411991119384, 25.38447570800780 -1.21411991119384, 25.38447570800780 -1.21185600757598, 25.38492774963370 -1.21140325069427, 25.38492774963370 -1.21095049381256, 25.38945388793940 -1.21095049381256, 25.38990592956540 -1.21049761772155, 25.39035987854000 -1.21004486083984, 25.39081192016600 -1.21004486083984, 25.39081192016600 -1.20959210395812, 25.39126396179190 -1.20913934707641, 25.39171600341790 -1.20913934707641, 25.39262199401850 -1.20868659019470, 25.39262199401850 -1.20823383331298, 25.39352798461910 -1.20823383331298, 25.39398002624510 -1.20778107643127, 25.39443206787100 -1.20778107643127, 25.39443206787100 -1.20732820034027, 25.39488410949700 -1.20687544345855, 25.39443206787100 -1.20642268657684, 25.39420700073240 -1.20596992969512, 25.39386749267570 -1.20551717281341, 25.39352798461910 -1.20551717281341, 25.39352798461910 -1.20506441593170, 25.39386749267570 -1.20551717281341, 25.39398002624510 -1.20551717281341, 25.39420700073240 -1.20596992969512, 25.39488410949700 -1.20687544345855, 25.39533805847160 -1.20778107643127, 25.39579010009760 -1.20823383331298, 25.39579010009760 -1.20868659019470, 25.39624214172360 -1.20913934707641, 25.39669609069820 -1.20959210395812, 25.39669609069820 -1.21049761772155, 25.39714813232420 -1.21095049381256, 25.39760017395010 -1.21185600757598, 25.39805221557610 -1.21321427822113, 25.39850616455070 -1.21411991119384, 25.39895820617670 -1.21547818183898, 25.39895820617670 -1.21638369560241, 25.39986419677730 -1.21819484233856, 25.40031623840330 -1.21955311298370, 25.40076828002920 -1.22045862674713, 25.40076828002920 -1.22181701660156, 25.40122032165520 -1.22226977348327, 25.40122032165520 -1.22408080101013, 25.40212631225580 -1.22498643398284, 25.40212631225580 -1.22634470462799, 25.40257835388180 -1.22725021839141, 25.40257835388180 -1.22770309448242, 25.40303230285640 -1.22860860824584, 25.40303230285640 -1.23041963577270, 25.40348434448240 -1.23041963577270, 25.21475410461420 -1.19600892066955))"; + + // Idem but moving closing point eastwards to view self-intersections better + std::string const dissolve_mail_2017_09_24_f = "POLYGON((25.35475410461420 -1.20600892066955, 25.36818313598630 -1.20732820034027, 25.36953926086420 -1.20868659019470, 25.37180328369140 -1.20959210395812, 25.37451934814450 -1.21049761772155, 25.37632942199700 -1.21185600757598, 25.37904357910150 -1.21276152133941, 25.38085556030270 -1.21321427822113, 25.38221168518060 -1.21366703510284, 25.38311767578120 -1.21411991119384, 25.38447570800780 -1.21411991119384, 25.38447570800780 -1.21185600757598, 25.38492774963370 -1.21140325069427, 25.38492774963370 -1.21095049381256, 25.38945388793940 -1.21095049381256, 25.38990592956540 -1.21049761772155, 25.39035987854000 -1.21004486083984, 25.39081192016600 -1.21004486083984, 25.39081192016600 -1.20959210395812, 25.39126396179190 -1.20913934707641, 25.39171600341790 -1.20913934707641, 25.39262199401850 -1.20868659019470, 25.39262199401850 -1.20823383331298, 25.39352798461910 -1.20823383331298, 25.39398002624510 -1.20778107643127, 25.39443206787100 -1.20778107643127, 25.39443206787100 -1.20732820034027, 25.39488410949700 -1.20687544345855, 25.39443206787100 -1.20642268657684, 25.39420700073240 -1.20596992969512, 25.39386749267570 -1.20551717281341, 25.39352798461910 -1.20551717281341, 25.39352798461910 -1.20506441593170, 25.39386749267570 -1.20551717281341, 25.39398002624510 -1.20551717281341, 25.39420700073240 -1.20596992969512, 25.39488410949700 -1.20687544345855, 25.39533805847160 -1.20778107643127, 25.39579010009760 -1.20823383331298, 25.39579010009760 -1.20868659019470, 25.39624214172360 -1.20913934707641, 25.39669609069820 -1.20959210395812, 25.39669609069820 -1.21049761772155, 25.39714813232420 -1.21095049381256, 25.39760017395010 -1.21185600757598, 25.39805221557610 -1.21321427822113, 25.39850616455070 -1.21411991119384, 25.39895820617670 -1.21547818183898, 25.39895820617670 -1.21638369560241, 25.39986419677730 -1.21819484233856, 25.40031623840330 -1.21955311298370, 25.40076828002920 -1.22045862674713, 25.40076828002920 -1.22181701660156, 25.40122032165520 -1.22226977348327, 25.40122032165520 -1.22408080101013, 25.40212631225580 -1.22498643398284, 25.40212631225580 -1.22634470462799, 25.40257835388180 -1.22725021839141, 25.40257835388180 -1.22770309448242, 25.40303230285640 -1.22860860824584, 25.40303230285640 -1.23041963577270, 25.40348434448240 -1.23041963577270, 25.35475410461420 -1.20600892066955))"; + + std::string const ticket17 = "POLYGON ((-122.28139163 37.37319149,-122.28100699 37.37273669,-122.28002186 37.37303123,-122.27979681 37.37290072,-122.28007349 37.37240493,-122.27977334 37.37220360,-122.27819720 37.37288580,-122.27714184 37.37275161,-122.27678628 37.37253167,-122.27766437 37.37180973,-122.27804382 37.37121453,-122.27687664 37.37101354,-122.27645829 37.37203386,-122.27604423 37.37249110,-122.27632234 37.37343339,-122.27760980 37.37391082,-122.27812478 37.37800320,-122.26117222 37.39121007,-122.25572289 37.39566631,-122.25547269 37.39564971,-122.25366304 37.39552993,-122.24919976 37.39580268,-122.24417933 37.39366907,-122.24051443 37.39094143,-122.23246277 37.38100418,-122.23606766 37.38141338,-122.24001587 37.37738940,-122.23666848 37.37609347,-122.23057450 37.37882170,-122.22679803 37.37807143,-122.22525727 37.37448817,-122.22523229 37.37443000,-122.23083199 37.37609347,-122.23033486 37.37777891,-122.23169030 37.37732117,-122.23229178 37.37709687,-122.23237761 37.37631249,-122.23297776 37.37438834,-122.23872850 37.37165986,-122.24044511 37.36934068,-122.24671067 37.36865847,-122.24825570 37.36981819,-122.25151719 37.36947713,-122.25357721 37.36756706,-122.26001451 37.36579354,-122.25615213 37.36545239,-122.25486458 37.36245083,-122.25357721 37.36108651,-122.25194642 37.36013139,-122.24885652 37.35958557,-122.24911401 37.35849399,-122.25357721 37.35808470,-122.25675286 37.35897159,-122.25855539 37.35753887,-122.26181687 37.35828939,-122.26713837 37.35897159,-122.26782510 37.36108651,-122.26662339 37.36456559,-122.27288911 37.36722601,-122.27366159 37.36531602,-122.27168740 37.36470213,-122.27391900 37.36374701,-122.27074326 37.36245083,-122.27134408 37.35951742,-122.27426240 37.36135926,-122.27709482 37.36115474,-122.27966974 37.36231438,-122.27958391 37.36463382,-122.27572152 37.36463382,-122.27563569 37.36524779,-122.27700899 37.36593000,-122.27709482 37.36763529,-122.27554978 37.36838573,-122.27667254 37.36931478,-122.27677932 37.36932073,-122.27769362 37.36853987,-122.27942490 37.36830803,-122.28178776 37.36677917,-122.28509559 37.36443500,-122.28845129 37.36413744,-122.29194403 37.36695946,-122.29382577 37.36726817,-122.29600414 37.36898512,-122.29733083 37.36995398,-122.29593239 37.37141436,-122.29416649 37.37075898,-122.29325026 37.37108436,-122.29652910 37.37311697,-122.29584237 37.37374461,-122.29537583 37.37573372,-122.29487677 37.37752502,-122.30923212 37.37593011,-122.31122484 37.38230086,-122.31467994 37.38092472,-122.31715663 37.38252181,-122.32307970 37.38166978,-122.31985618 37.37667694,-122.32210304 37.37580220,-122.32581446 37.37589532,-122.32401730 37.37331839,-122.32960417 37.37189020,-122.33465527 37.37331906,-122.33425328 37.37623680,-122.33620676 37.37726132,-122.33397986 37.37822382,-122.33358918 37.38036590,-122.33202637 37.37986918,-122.33147954 37.38101784,-122.33394080 37.38198017,-122.33545239 37.38587943,-122.33478058 37.38785697,-122.33386050 37.38723721,-122.33350041 37.38571137,-122.33122003 37.38548891,-122.33140008 37.38650606,-122.33366042 37.38817490,-122.33244019 37.39157602,-122.33298157 37.39419201,-122.33164013 37.39477028,-122.33202017 37.39518351,-122.33358038 37.39499282,-122.33376050 37.39597811,-122.33550067 37.39734478,-122.33556069 37.39481797,-122.33344040 37.39292676,-122.33638094 37.38892189,-122.34240644 37.38852719,-122.34906293 37.38726898,-122.35072321 37.39338769,-122.34910291 37.39445252,-122.34796272 37.39410291,-122.34449043 37.39640534,-122.34500223 37.39729709,-122.34936291 37.39670910,-122.35098322 37.39531066,-122.35364623 37.39554510,-122.35434369 37.39612111,-122.35798429 37.39600988,-122.35768430 37.39478621,-122.36334519 37.39206871,-122.36604726 37.39203267,-122.36778592 37.39335592,-122.36518870 37.40022011,-122.36554552 37.40247752,-122.36370519 37.40331974,-122.36270506 37.40530591,-122.36320512 37.40670418,-122.36149849 37.40851392,-122.36730580 37.41054938,-122.37263720 37.41378932,-122.37161871 37.42076600,-122.36566153 37.42006292,-122.36520547 37.42742106,-122.37165953 37.43661157,-122.35943972 37.44459022,-122.35356359 37.44600810,-122.33792254 37.45796329,-122.35228518 37.47478091,-122.35127080 37.48181199,-122.34867342 37.48487322,-122.34359717 37.48801082,-122.33388431 37.48677650,-122.33142321 37.48429747,-122.32929580 37.48473149,-122.32609609 37.48291144,-122.32344850 37.48228229,-122.31924364 37.48410234,-122.31677299 37.48114051,-122.31431751 37.47848973,-122.31259201 37.47682190,-122.31515972 37.47568196,-122.31691389 37.47360309,-122.31292494 37.46960081,-122.31130153 37.46937743,-122.30889894 37.47124987,-122.30612839 37.47011613,-122.30149630 37.46568378,-122.30064277 37.46363784,-122.29283821 37.45922376,-122.28630141 37.45415497,-122.28883099 37.44629920,-122.28316717 37.44197138,-122.27554148 37.42297597,-122.25597410 37.40553692,-122.25196579 37.40129593,-122.25012043 37.40049143,-122.24823207 37.39897758,-122.24754551 37.39740941,-122.24778582 37.39621607,-122.24934787 37.39599102,-122.25005170 37.39871849,-122.25222328 37.39863668,-122.25342491 37.39737529,-122.25520162 37.39667289,-122.25528737 37.39522726,-122.27747460 37.37809616,-122.27977493 37.37858717,-122.28157729 37.37920106,-122.28322534 37.37952846,-122.28416939 37.38092656,-122.28621223 37.37984219,-122.28638389 37.37613857,-122.28382607 37.37843722,-122.27930278 37.37718220,-122.28196361 37.37652740,-122.28295058 37.37568167,-122.28216101 37.37523148,-122.28114822 37.37543608,-122.27934569 37.37528613,-122.27996369 37.37448121,-122.28104521 37.37454944,-122.28185197 37.37422883,-122.28290767 37.37474038,-122.28376597 37.37467224,-122.28428104 37.37399012,-122.28402346 37.37338989,-122.28610922 37.37364914,-122.28651264 37.37327388,-122.28672722 37.37207343,-122.28628398 37.37205448,-122.28574460 37.37166682,-122.28479711 37.37200981,-122.28327731 37.37137228,-122.28285511 37.37100700,-122.28279409 37.37125669,-122.28315527 37.37173756,-122.28321872 37.37220569,-122.28187007 37.37231918,-122.28193109 37.37294908,-122.28139163 37.37319149))"; + std::string const toolkit = "POLYGON((170718 605997,170718 605997,170776 606016,170773 606015,170786 606020,170778 606016,170787 606021,170781 606017,170795 606028,170795 606028,170829 606055,170939 606140,170933 605968,170933 605968,170932 605908,170929 605834,170920 605866,170961 605803,170739 605684,170699 605749,170691 605766,170693 605762,170686 605775,170688 605771,170673 605794,170676 605790,170668 605800,170672 605796,170651 605818,170653 605816,170639 605829,170568 605899,170662 605943,170633 605875,170603 605961,170718 605997))"; +} #if defined(TEST_WITH_SVG) @@ -202,7 +237,9 @@ std::string as_wkt(Geometry const& geometry) template void test_dissolve(std::string const& caseid, Geometry const& geometry, - std::size_t /*expected_hole_count*/, std::size_t expected_point_count, + std::size_t expected_clip_count, + std::size_t expected_hole_count, + std::size_t expected_point_count, double expected_length_or_area, double percentage) { typedef typename bg::coordinate_type::type coordinate_type; @@ -297,7 +334,7 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, } typename bg::default_area_result::type length_or_area = 0; - //std::size_t holes = 0; + std::size_t holes = 0; std::size_t count = 0; BOOST_FOREACH(GeometryOut& dissolved, dissolved2) @@ -305,7 +342,7 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, length_or_area += is_line ? bg::length(dissolved) : bg::area(dissolved); - //holes += bg::num_interior_rings(dissolved); + holes += bg::num_interior_rings(dissolved); count += bg::num_points(dissolved); } @@ -316,6 +353,16 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, << " type: " << string_from_type::name() ); + if (expected_clip_count > 0) + { + BOOST_CHECK_MESSAGE(dissolved1.size() == expected_clip_count, + "dissolve: " << caseid + << " #clips expected: " << expected_clip_count + << " detected: " << dissolved1.size() + << " type: " << string_from_type::name() + ); + } + //BOOST_CHECK_EQUAL(holes, expected_hole_count); BOOST_CHECK_CLOSE(length_or_area, expected_length_or_area, percentage); @@ -340,13 +387,15 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, template void test_one(std::string const& caseid, std::string const& wkt, std::size_t expected_hole_count, std::size_t expected_point_count, - double expected_length_or_area, double percentage = 0.001) + double expected_length_or_area, + std::size_t expected_clip_count = 0, // to be moved later + double percentage = 0.001) { Geometry geometry; bg::read_wkt(wkt, geometry); test_dissolve(caseid, geometry, - expected_hole_count, expected_point_count, + expected_clip_count, expected_hole_count, expected_point_count, expected_length_or_area, percentage); #ifdef BOOST_GEOMETRY_TEST_MULTI_PERMUTATIONS @@ -380,7 +429,7 @@ void test_one(std::string const& caseid, std::string const& wkt, } #define TEST_DISSOLVE(caseid, area, clips, holes, points) \ - (test_one) ( #caseid, caseid, holes, points, area) + (test_one) ( #caseid, caseid, holes, points, area, clips) template @@ -389,50 +438,17 @@ void test_all() typedef bg::model::ring

ring; typedef bg::model::polygon

polygon; - // Simplex - test_one("1", - "POLYGON((0 0,0 4,1.5 2.5,2.5 1.5,4 0,0 0))", - 0, 6, 8); + TEST_DISSOLVE(dissolve_1, 8.0, 1, 0, 6); + TEST_DISSOLVE(dissolve_2, 7.9296875, 1, 1, 12); + TEST_DISSOLVE(dissolve_3, 4.0, 2, 0, 8); + TEST_DISSOLVE(dissolve_4, 8.0, 2, 0, 8); + TEST_DISSOLVE(dissolve_5, 12.0, 2, 0, 8); + TEST_DISSOLVE(dissolve_6, 16.0, 1, 0, 6); + TEST_DISSOLVE(dissolve_d1, 8.0, 1, 0, 4); + TEST_DISSOLVE(dissolve_d2, 16.0, 1, 0, 10); - // Self intersecting - test_one("2", - "POLYGON((1 2,1 1,2 1,2 2.25,3 2.25,3 0,0 0,0 3,3 3,2.75 2,1 2))", - 1, 12, 7.9296875); - - // Self intersecting in last segment - test_one("3", - "POLYGON((0 2,2 4,2 0,4 2,0 2))", - 0, 8, 4.0); - - // Self tangent - polygons are now included twice - test_one("4", - "POLYGON((0 0,0 4,4 4,4 0,2 4,0 0))", - 0, 8, 8.0); - - // Self tangent in corner - polygons are now included twice - test_one("5", - "POLYGON((0 0,0 4,4 4,4 0,0 4,2 0,0 0))", - 0, 8, 12.0); - - // With spike - test_one("6", - "POLYGON((0 0,0 4,4 4,4 2,6 2,4 2,4 0,0 0))", - 0, 6, 16); - - // Non intersection, but with duplicate - test_one("d1", - "POLYGON((0 0,0 4,4 0,4 0,0 0))", - 0, 4, 8); - - // With many duplicates - test_one("d2", - "POLYGON((0 0,0 1,0 1,0 1,0 2,0 2,0 3,0 3,0 3,0 3,0 4,2 4,2 4,4 4,4 0,4 0,3 0,3 0,3 0,3 0,3 0,0 0))", - 0, 10, 16); - - // Hole: interior tangent to exterior - test_one("h1", - "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 2,2 4,3 2,1 2))", - 0, 6, 16); + TEST_DISSOLVE(dissolve_h1_a, 16.0, 1, 0, 6); + TEST_DISSOLVE(dissolve_h1_b, 14.0, 1, 1, 10); // Hole: interior intersecting exterior test_one("h2", @@ -465,15 +481,13 @@ void test_all() TEST_DISSOLVE(dissolve_mail_2017_09_24_b, 16.0, 1, 0, 5); TEST_DISSOLVE(dissolve_mail_2017_09_24_c, 0.4999, 1, 0, 4); TEST_DISSOLVE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 4); - TEST_DISSOLVE(dissolve_mail_2017_09_24_e, 0.001801138128, 1, 0, 69); - TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.000361308800, 2, 0, 69); + TEST_DISSOLVE(dissolve_mail_2017_09_24_e, 0.001801138128, 5, 0, 69); + TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.000361308800, 5, 0, 69); #ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS test_one("pentagram_rev", "POLYGON((5 0,7.5 9,0.5 3.5,9.5 3.5,2.5 9,5 0))", 0, 11, 25.6158412); - - // Poygons contain too many polygons #endif // CCW polygons should turn CW after dissolve @@ -561,12 +575,10 @@ void test_all() */ - std::string const ticket17 = "POLYGON ((-122.28139163 37.37319149,-122.28100699 37.37273669,-122.28002186 37.37303123,-122.27979681 37.37290072,-122.28007349 37.37240493,-122.27977334 37.37220360,-122.27819720 37.37288580,-122.27714184 37.37275161,-122.27678628 37.37253167,-122.27766437 37.37180973,-122.27804382 37.37121453,-122.27687664 37.37101354,-122.27645829 37.37203386,-122.27604423 37.37249110,-122.27632234 37.37343339,-122.27760980 37.37391082,-122.27812478 37.37800320,-122.26117222 37.39121007,-122.25572289 37.39566631,-122.25547269 37.39564971,-122.25366304 37.39552993,-122.24919976 37.39580268,-122.24417933 37.39366907,-122.24051443 37.39094143,-122.23246277 37.38100418,-122.23606766 37.38141338,-122.24001587 37.37738940,-122.23666848 37.37609347,-122.23057450 37.37882170,-122.22679803 37.37807143,-122.22525727 37.37448817,-122.22523229 37.37443000,-122.23083199 37.37609347,-122.23033486 37.37777891,-122.23169030 37.37732117,-122.23229178 37.37709687,-122.23237761 37.37631249,-122.23297776 37.37438834,-122.23872850 37.37165986,-122.24044511 37.36934068,-122.24671067 37.36865847,-122.24825570 37.36981819,-122.25151719 37.36947713,-122.25357721 37.36756706,-122.26001451 37.36579354,-122.25615213 37.36545239,-122.25486458 37.36245083,-122.25357721 37.36108651,-122.25194642 37.36013139,-122.24885652 37.35958557,-122.24911401 37.35849399,-122.25357721 37.35808470,-122.25675286 37.35897159,-122.25855539 37.35753887,-122.26181687 37.35828939,-122.26713837 37.35897159,-122.26782510 37.36108651,-122.26662339 37.36456559,-122.27288911 37.36722601,-122.27366159 37.36531602,-122.27168740 37.36470213,-122.27391900 37.36374701,-122.27074326 37.36245083,-122.27134408 37.35951742,-122.27426240 37.36135926,-122.27709482 37.36115474,-122.27966974 37.36231438,-122.27958391 37.36463382,-122.27572152 37.36463382,-122.27563569 37.36524779,-122.27700899 37.36593000,-122.27709482 37.36763529,-122.27554978 37.36838573,-122.27667254 37.36931478,-122.27677932 37.36932073,-122.27769362 37.36853987,-122.27942490 37.36830803,-122.28178776 37.36677917,-122.28509559 37.36443500,-122.28845129 37.36413744,-122.29194403 37.36695946,-122.29382577 37.36726817,-122.29600414 37.36898512,-122.29733083 37.36995398,-122.29593239 37.37141436,-122.29416649 37.37075898,-122.29325026 37.37108436,-122.29652910 37.37311697,-122.29584237 37.37374461,-122.29537583 37.37573372,-122.29487677 37.37752502,-122.30923212 37.37593011,-122.31122484 37.38230086,-122.31467994 37.38092472,-122.31715663 37.38252181,-122.32307970 37.38166978,-122.31985618 37.37667694,-122.32210304 37.37580220,-122.32581446 37.37589532,-122.32401730 37.37331839,-122.32960417 37.37189020,-122.33465527 37.37331906,-122.33425328 37.37623680,-122.33620676 37.37726132,-122.33397986 37.37822382,-122.33358918 37.38036590,-122.33202637 37.37986918,-122.33147954 37.38101784,-122.33394080 37.38198017,-122.33545239 37.38587943,-122.33478058 37.38785697,-122.33386050 37.38723721,-122.33350041 37.38571137,-122.33122003 37.38548891,-122.33140008 37.38650606,-122.33366042 37.38817490,-122.33244019 37.39157602,-122.33298157 37.39419201,-122.33164013 37.39477028,-122.33202017 37.39518351,-122.33358038 37.39499282,-122.33376050 37.39597811,-122.33550067 37.39734478,-122.33556069 37.39481797,-122.33344040 37.39292676,-122.33638094 37.38892189,-122.34240644 37.38852719,-122.34906293 37.38726898,-122.35072321 37.39338769,-122.34910291 37.39445252,-122.34796272 37.39410291,-122.34449043 37.39640534,-122.34500223 37.39729709,-122.34936291 37.39670910,-122.35098322 37.39531066,-122.35364623 37.39554510,-122.35434369 37.39612111,-122.35798429 37.39600988,-122.35768430 37.39478621,-122.36334519 37.39206871,-122.36604726 37.39203267,-122.36778592 37.39335592,-122.36518870 37.40022011,-122.36554552 37.40247752,-122.36370519 37.40331974,-122.36270506 37.40530591,-122.36320512 37.40670418,-122.36149849 37.40851392,-122.36730580 37.41054938,-122.37263720 37.41378932,-122.37161871 37.42076600,-122.36566153 37.42006292,-122.36520547 37.42742106,-122.37165953 37.43661157,-122.35943972 37.44459022,-122.35356359 37.44600810,-122.33792254 37.45796329,-122.35228518 37.47478091,-122.35127080 37.48181199,-122.34867342 37.48487322,-122.34359717 37.48801082,-122.33388431 37.48677650,-122.33142321 37.48429747,-122.32929580 37.48473149,-122.32609609 37.48291144,-122.32344850 37.48228229,-122.31924364 37.48410234,-122.31677299 37.48114051,-122.31431751 37.47848973,-122.31259201 37.47682190,-122.31515972 37.47568196,-122.31691389 37.47360309,-122.31292494 37.46960081,-122.31130153 37.46937743,-122.30889894 37.47124987,-122.30612839 37.47011613,-122.30149630 37.46568378,-122.30064277 37.46363784,-122.29283821 37.45922376,-122.28630141 37.45415497,-122.28883099 37.44629920,-122.28316717 37.44197138,-122.27554148 37.42297597,-122.25597410 37.40553692,-122.25196579 37.40129593,-122.25012043 37.40049143,-122.24823207 37.39897758,-122.24754551 37.39740941,-122.24778582 37.39621607,-122.24934787 37.39599102,-122.25005170 37.39871849,-122.25222328 37.39863668,-122.25342491 37.39737529,-122.25520162 37.39667289,-122.25528737 37.39522726,-122.27747460 37.37809616,-122.27977493 37.37858717,-122.28157729 37.37920106,-122.28322534 37.37952846,-122.28416939 37.38092656,-122.28621223 37.37984219,-122.28638389 37.37613857,-122.28382607 37.37843722,-122.27930278 37.37718220,-122.28196361 37.37652740,-122.28295058 37.37568167,-122.28216101 37.37523148,-122.28114822 37.37543608,-122.27934569 37.37528613,-122.27996369 37.37448121,-122.28104521 37.37454944,-122.28185197 37.37422883,-122.28290767 37.37474038,-122.28376597 37.37467224,-122.28428104 37.37399012,-122.28402346 37.37338989,-122.28610922 37.37364914,-122.28651264 37.37327388,-122.28672722 37.37207343,-122.28628398 37.37205448,-122.28574460 37.37166682,-122.28479711 37.37200981,-122.28327731 37.37137228,-122.28285511 37.37100700,-122.28279409 37.37125669,-122.28315527 37.37173756,-122.28321872 37.37220569,-122.28187007 37.37231918,-122.28193109 37.37294908,-122.28139163 37.37319149))"; test_one("ticket17", ticket17, 1, 228, 0.00920834633689); // Real-life - std::string const toolkit = "POLYGON((170718 605997,170718 605997,170776 606016,170773 606015,170786 606020,170778 606016,170787 606021,170781 606017,170795 606028,170795 606028,170829 606055,170939 606140,170933 605968,170933 605968,170932 605908,170929 605834,170920 605866,170961 605803,170739 605684,170699 605749,170691 605766,170693 605762,170686 605775,170688 605771,170673 605794,170676 605790,170668 605800,170672 605796,170651 605818,170653 605816,170639 605829,170568 605899,170662 605943,170633 605875,170603 605961,170718 605997))"; test_one("toolkit", toolkit, 0, 25, 91756.916526794434); From c77f1c99abcbc0e170d885429b42910eaa99faea Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 16:26:28 +0200 Subject: [PATCH 152/188] [test] improve test, phase 2, now always check for clips/holes --- extensions/test/algorithms/dissolve.cpp | 126 +++++++++++------------- 1 file changed, 56 insertions(+), 70 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index 7b8fcbf46..24c7f5855 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -62,6 +62,18 @@ namespace // With spike std::string const dissolve_6 = "POLYGON((0 0,0 4,4 4,4 2,6 2,4 2,4 0,0 0))"; + // Many intersections + std::string const dissolve_7 = "POLYGON((1 3,0 9,9 5,1 7,9 8,2 5,10 10,9 2,1 3))"; + + // Pentagram + std::string const dissolve_8 = "POLYGON((5 0,2.5 9,9.5 3.5,0.5 3.5,7.5 9,5 0))"; + // CCW pentagram + std::string const dissolve_9 = "POLYGON((5 0,7.5 9,0.5 3.5,9.5 3.5,2.5 9,5 0))"; + // CW, one keyhole + std::string const dissolve_10 = "POLYGON((2 8,8 8,8 0,0 0,0 6,4 6,4 4,2 4,2 8))"; + // CCW, one keyhole + std::string const dissolve_11 = "POLYGON((2 8,2 4,4 4,4 6,0 6,0 0,8 0,8 8,2 8))"; + // Non intersection, but with duplicate std::string const dissolve_d1 = "POLYGON((0 0,0 4,4 0,4 0,0 0))"; @@ -74,6 +86,15 @@ namespace // Case with correct interior ring inside, which should be stay std::string const dissolve_h1_b = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 2,3 2,2 4,1 2))"; + // Hole: interior intersecting exterior + std::string const dissolve_h2 = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 3,5 4,1 1))"; + + // Hole: two intersecting holes + std::string const dissolve_h3 = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 3,3 3,3 1,1 1),(2 2,2 3.5,3.5 3.5,3.5 2,2 2))"; + + // Hole: self-intersecting hole + std::string const dissolve_h4 = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,3 3,3 2.5,1 3.5,1.5 3.5,1 1))"; + // Testcases send by Johan Doré at September 24, 2017: std::string const dissolve_mail_2017_09_24_a = "POLYGON((0 1, 1 0, 1 1, 0 0, 0 1))"; // two triangles std::string const dissolve_mail_2017_09_24_b = "POLYGON((1 0, 0 0, 0 4, 4 4, 4 0))"; // input is not closed @@ -86,8 +107,11 @@ namespace // Idem but moving closing point eastwards to view self-intersections better std::string const dissolve_mail_2017_09_24_f = "POLYGON((25.35475410461420 -1.20600892066955, 25.36818313598630 -1.20732820034027, 25.36953926086420 -1.20868659019470, 25.37180328369140 -1.20959210395812, 25.37451934814450 -1.21049761772155, 25.37632942199700 -1.21185600757598, 25.37904357910150 -1.21276152133941, 25.38085556030270 -1.21321427822113, 25.38221168518060 -1.21366703510284, 25.38311767578120 -1.21411991119384, 25.38447570800780 -1.21411991119384, 25.38447570800780 -1.21185600757598, 25.38492774963370 -1.21140325069427, 25.38492774963370 -1.21095049381256, 25.38945388793940 -1.21095049381256, 25.38990592956540 -1.21049761772155, 25.39035987854000 -1.21004486083984, 25.39081192016600 -1.21004486083984, 25.39081192016600 -1.20959210395812, 25.39126396179190 -1.20913934707641, 25.39171600341790 -1.20913934707641, 25.39262199401850 -1.20868659019470, 25.39262199401850 -1.20823383331298, 25.39352798461910 -1.20823383331298, 25.39398002624510 -1.20778107643127, 25.39443206787100 -1.20778107643127, 25.39443206787100 -1.20732820034027, 25.39488410949700 -1.20687544345855, 25.39443206787100 -1.20642268657684, 25.39420700073240 -1.20596992969512, 25.39386749267570 -1.20551717281341, 25.39352798461910 -1.20551717281341, 25.39352798461910 -1.20506441593170, 25.39386749267570 -1.20551717281341, 25.39398002624510 -1.20551717281341, 25.39420700073240 -1.20596992969512, 25.39488410949700 -1.20687544345855, 25.39533805847160 -1.20778107643127, 25.39579010009760 -1.20823383331298, 25.39579010009760 -1.20868659019470, 25.39624214172360 -1.20913934707641, 25.39669609069820 -1.20959210395812, 25.39669609069820 -1.21049761772155, 25.39714813232420 -1.21095049381256, 25.39760017395010 -1.21185600757598, 25.39805221557610 -1.21321427822113, 25.39850616455070 -1.21411991119384, 25.39895820617670 -1.21547818183898, 25.39895820617670 -1.21638369560241, 25.39986419677730 -1.21819484233856, 25.40031623840330 -1.21955311298370, 25.40076828002920 -1.22045862674713, 25.40076828002920 -1.22181701660156, 25.40122032165520 -1.22226977348327, 25.40122032165520 -1.22408080101013, 25.40212631225580 -1.22498643398284, 25.40212631225580 -1.22634470462799, 25.40257835388180 -1.22725021839141, 25.40257835388180 -1.22770309448242, 25.40303230285640 -1.22860860824584, 25.40303230285640 -1.23041963577270, 25.40348434448240 -1.23041963577270, 25.35475410461420 -1.20600892066955))"; - std::string const ticket17 = "POLYGON ((-122.28139163 37.37319149,-122.28100699 37.37273669,-122.28002186 37.37303123,-122.27979681 37.37290072,-122.28007349 37.37240493,-122.27977334 37.37220360,-122.27819720 37.37288580,-122.27714184 37.37275161,-122.27678628 37.37253167,-122.27766437 37.37180973,-122.27804382 37.37121453,-122.27687664 37.37101354,-122.27645829 37.37203386,-122.27604423 37.37249110,-122.27632234 37.37343339,-122.27760980 37.37391082,-122.27812478 37.37800320,-122.26117222 37.39121007,-122.25572289 37.39566631,-122.25547269 37.39564971,-122.25366304 37.39552993,-122.24919976 37.39580268,-122.24417933 37.39366907,-122.24051443 37.39094143,-122.23246277 37.38100418,-122.23606766 37.38141338,-122.24001587 37.37738940,-122.23666848 37.37609347,-122.23057450 37.37882170,-122.22679803 37.37807143,-122.22525727 37.37448817,-122.22523229 37.37443000,-122.23083199 37.37609347,-122.23033486 37.37777891,-122.23169030 37.37732117,-122.23229178 37.37709687,-122.23237761 37.37631249,-122.23297776 37.37438834,-122.23872850 37.37165986,-122.24044511 37.36934068,-122.24671067 37.36865847,-122.24825570 37.36981819,-122.25151719 37.36947713,-122.25357721 37.36756706,-122.26001451 37.36579354,-122.25615213 37.36545239,-122.25486458 37.36245083,-122.25357721 37.36108651,-122.25194642 37.36013139,-122.24885652 37.35958557,-122.24911401 37.35849399,-122.25357721 37.35808470,-122.25675286 37.35897159,-122.25855539 37.35753887,-122.26181687 37.35828939,-122.26713837 37.35897159,-122.26782510 37.36108651,-122.26662339 37.36456559,-122.27288911 37.36722601,-122.27366159 37.36531602,-122.27168740 37.36470213,-122.27391900 37.36374701,-122.27074326 37.36245083,-122.27134408 37.35951742,-122.27426240 37.36135926,-122.27709482 37.36115474,-122.27966974 37.36231438,-122.27958391 37.36463382,-122.27572152 37.36463382,-122.27563569 37.36524779,-122.27700899 37.36593000,-122.27709482 37.36763529,-122.27554978 37.36838573,-122.27667254 37.36931478,-122.27677932 37.36932073,-122.27769362 37.36853987,-122.27942490 37.36830803,-122.28178776 37.36677917,-122.28509559 37.36443500,-122.28845129 37.36413744,-122.29194403 37.36695946,-122.29382577 37.36726817,-122.29600414 37.36898512,-122.29733083 37.36995398,-122.29593239 37.37141436,-122.29416649 37.37075898,-122.29325026 37.37108436,-122.29652910 37.37311697,-122.29584237 37.37374461,-122.29537583 37.37573372,-122.29487677 37.37752502,-122.30923212 37.37593011,-122.31122484 37.38230086,-122.31467994 37.38092472,-122.31715663 37.38252181,-122.32307970 37.38166978,-122.31985618 37.37667694,-122.32210304 37.37580220,-122.32581446 37.37589532,-122.32401730 37.37331839,-122.32960417 37.37189020,-122.33465527 37.37331906,-122.33425328 37.37623680,-122.33620676 37.37726132,-122.33397986 37.37822382,-122.33358918 37.38036590,-122.33202637 37.37986918,-122.33147954 37.38101784,-122.33394080 37.38198017,-122.33545239 37.38587943,-122.33478058 37.38785697,-122.33386050 37.38723721,-122.33350041 37.38571137,-122.33122003 37.38548891,-122.33140008 37.38650606,-122.33366042 37.38817490,-122.33244019 37.39157602,-122.33298157 37.39419201,-122.33164013 37.39477028,-122.33202017 37.39518351,-122.33358038 37.39499282,-122.33376050 37.39597811,-122.33550067 37.39734478,-122.33556069 37.39481797,-122.33344040 37.39292676,-122.33638094 37.38892189,-122.34240644 37.38852719,-122.34906293 37.38726898,-122.35072321 37.39338769,-122.34910291 37.39445252,-122.34796272 37.39410291,-122.34449043 37.39640534,-122.34500223 37.39729709,-122.34936291 37.39670910,-122.35098322 37.39531066,-122.35364623 37.39554510,-122.35434369 37.39612111,-122.35798429 37.39600988,-122.35768430 37.39478621,-122.36334519 37.39206871,-122.36604726 37.39203267,-122.36778592 37.39335592,-122.36518870 37.40022011,-122.36554552 37.40247752,-122.36370519 37.40331974,-122.36270506 37.40530591,-122.36320512 37.40670418,-122.36149849 37.40851392,-122.36730580 37.41054938,-122.37263720 37.41378932,-122.37161871 37.42076600,-122.36566153 37.42006292,-122.36520547 37.42742106,-122.37165953 37.43661157,-122.35943972 37.44459022,-122.35356359 37.44600810,-122.33792254 37.45796329,-122.35228518 37.47478091,-122.35127080 37.48181199,-122.34867342 37.48487322,-122.34359717 37.48801082,-122.33388431 37.48677650,-122.33142321 37.48429747,-122.32929580 37.48473149,-122.32609609 37.48291144,-122.32344850 37.48228229,-122.31924364 37.48410234,-122.31677299 37.48114051,-122.31431751 37.47848973,-122.31259201 37.47682190,-122.31515972 37.47568196,-122.31691389 37.47360309,-122.31292494 37.46960081,-122.31130153 37.46937743,-122.30889894 37.47124987,-122.30612839 37.47011613,-122.30149630 37.46568378,-122.30064277 37.46363784,-122.29283821 37.45922376,-122.28630141 37.45415497,-122.28883099 37.44629920,-122.28316717 37.44197138,-122.27554148 37.42297597,-122.25597410 37.40553692,-122.25196579 37.40129593,-122.25012043 37.40049143,-122.24823207 37.39897758,-122.24754551 37.39740941,-122.24778582 37.39621607,-122.24934787 37.39599102,-122.25005170 37.39871849,-122.25222328 37.39863668,-122.25342491 37.39737529,-122.25520162 37.39667289,-122.25528737 37.39522726,-122.27747460 37.37809616,-122.27977493 37.37858717,-122.28157729 37.37920106,-122.28322534 37.37952846,-122.28416939 37.38092656,-122.28621223 37.37984219,-122.28638389 37.37613857,-122.28382607 37.37843722,-122.27930278 37.37718220,-122.28196361 37.37652740,-122.28295058 37.37568167,-122.28216101 37.37523148,-122.28114822 37.37543608,-122.27934569 37.37528613,-122.27996369 37.37448121,-122.28104521 37.37454944,-122.28185197 37.37422883,-122.28290767 37.37474038,-122.28376597 37.37467224,-122.28428104 37.37399012,-122.28402346 37.37338989,-122.28610922 37.37364914,-122.28651264 37.37327388,-122.28672722 37.37207343,-122.28628398 37.37205448,-122.28574460 37.37166682,-122.28479711 37.37200981,-122.28327731 37.37137228,-122.28285511 37.37100700,-122.28279409 37.37125669,-122.28315527 37.37173756,-122.28321872 37.37220569,-122.28187007 37.37231918,-122.28193109 37.37294908,-122.28139163 37.37319149))"; - std::string const toolkit = "POLYGON((170718 605997,170718 605997,170776 606016,170773 606015,170786 606020,170778 606016,170787 606021,170781 606017,170795 606028,170795 606028,170829 606055,170939 606140,170933 605968,170933 605968,170932 605908,170929 605834,170920 605866,170961 605803,170739 605684,170699 605749,170691 605766,170693 605762,170686 605775,170688 605771,170673 605794,170676 605790,170668 605800,170672 605796,170651 605818,170653 605816,170639 605829,170568 605899,170662 605943,170633 605875,170603 605961,170718 605997))"; + std::string const dissolve_ticket17 = "POLYGON ((-122.28139163 37.37319149,-122.28100699 37.37273669,-122.28002186 37.37303123,-122.27979681 37.37290072,-122.28007349 37.37240493,-122.27977334 37.37220360,-122.27819720 37.37288580,-122.27714184 37.37275161,-122.27678628 37.37253167,-122.27766437 37.37180973,-122.27804382 37.37121453,-122.27687664 37.37101354,-122.27645829 37.37203386,-122.27604423 37.37249110,-122.27632234 37.37343339,-122.27760980 37.37391082,-122.27812478 37.37800320,-122.26117222 37.39121007,-122.25572289 37.39566631,-122.25547269 37.39564971,-122.25366304 37.39552993,-122.24919976 37.39580268,-122.24417933 37.39366907,-122.24051443 37.39094143,-122.23246277 37.38100418,-122.23606766 37.38141338,-122.24001587 37.37738940,-122.23666848 37.37609347,-122.23057450 37.37882170,-122.22679803 37.37807143,-122.22525727 37.37448817,-122.22523229 37.37443000,-122.23083199 37.37609347,-122.23033486 37.37777891,-122.23169030 37.37732117,-122.23229178 37.37709687,-122.23237761 37.37631249,-122.23297776 37.37438834,-122.23872850 37.37165986,-122.24044511 37.36934068,-122.24671067 37.36865847,-122.24825570 37.36981819,-122.25151719 37.36947713,-122.25357721 37.36756706,-122.26001451 37.36579354,-122.25615213 37.36545239,-122.25486458 37.36245083,-122.25357721 37.36108651,-122.25194642 37.36013139,-122.24885652 37.35958557,-122.24911401 37.35849399,-122.25357721 37.35808470,-122.25675286 37.35897159,-122.25855539 37.35753887,-122.26181687 37.35828939,-122.26713837 37.35897159,-122.26782510 37.36108651,-122.26662339 37.36456559,-122.27288911 37.36722601,-122.27366159 37.36531602,-122.27168740 37.36470213,-122.27391900 37.36374701,-122.27074326 37.36245083,-122.27134408 37.35951742,-122.27426240 37.36135926,-122.27709482 37.36115474,-122.27966974 37.36231438,-122.27958391 37.36463382,-122.27572152 37.36463382,-122.27563569 37.36524779,-122.27700899 37.36593000,-122.27709482 37.36763529,-122.27554978 37.36838573,-122.27667254 37.36931478,-122.27677932 37.36932073,-122.27769362 37.36853987,-122.27942490 37.36830803,-122.28178776 37.36677917,-122.28509559 37.36443500,-122.28845129 37.36413744,-122.29194403 37.36695946,-122.29382577 37.36726817,-122.29600414 37.36898512,-122.29733083 37.36995398,-122.29593239 37.37141436,-122.29416649 37.37075898,-122.29325026 37.37108436,-122.29652910 37.37311697,-122.29584237 37.37374461,-122.29537583 37.37573372,-122.29487677 37.37752502,-122.30923212 37.37593011,-122.31122484 37.38230086,-122.31467994 37.38092472,-122.31715663 37.38252181,-122.32307970 37.38166978,-122.31985618 37.37667694,-122.32210304 37.37580220,-122.32581446 37.37589532,-122.32401730 37.37331839,-122.32960417 37.37189020,-122.33465527 37.37331906,-122.33425328 37.37623680,-122.33620676 37.37726132,-122.33397986 37.37822382,-122.33358918 37.38036590,-122.33202637 37.37986918,-122.33147954 37.38101784,-122.33394080 37.38198017,-122.33545239 37.38587943,-122.33478058 37.38785697,-122.33386050 37.38723721,-122.33350041 37.38571137,-122.33122003 37.38548891,-122.33140008 37.38650606,-122.33366042 37.38817490,-122.33244019 37.39157602,-122.33298157 37.39419201,-122.33164013 37.39477028,-122.33202017 37.39518351,-122.33358038 37.39499282,-122.33376050 37.39597811,-122.33550067 37.39734478,-122.33556069 37.39481797,-122.33344040 37.39292676,-122.33638094 37.38892189,-122.34240644 37.38852719,-122.34906293 37.38726898,-122.35072321 37.39338769,-122.34910291 37.39445252,-122.34796272 37.39410291,-122.34449043 37.39640534,-122.34500223 37.39729709,-122.34936291 37.39670910,-122.35098322 37.39531066,-122.35364623 37.39554510,-122.35434369 37.39612111,-122.35798429 37.39600988,-122.35768430 37.39478621,-122.36334519 37.39206871,-122.36604726 37.39203267,-122.36778592 37.39335592,-122.36518870 37.40022011,-122.36554552 37.40247752,-122.36370519 37.40331974,-122.36270506 37.40530591,-122.36320512 37.40670418,-122.36149849 37.40851392,-122.36730580 37.41054938,-122.37263720 37.41378932,-122.37161871 37.42076600,-122.36566153 37.42006292,-122.36520547 37.42742106,-122.37165953 37.43661157,-122.35943972 37.44459022,-122.35356359 37.44600810,-122.33792254 37.45796329,-122.35228518 37.47478091,-122.35127080 37.48181199,-122.34867342 37.48487322,-122.34359717 37.48801082,-122.33388431 37.48677650,-122.33142321 37.48429747,-122.32929580 37.48473149,-122.32609609 37.48291144,-122.32344850 37.48228229,-122.31924364 37.48410234,-122.31677299 37.48114051,-122.31431751 37.47848973,-122.31259201 37.47682190,-122.31515972 37.47568196,-122.31691389 37.47360309,-122.31292494 37.46960081,-122.31130153 37.46937743,-122.30889894 37.47124987,-122.30612839 37.47011613,-122.30149630 37.46568378,-122.30064277 37.46363784,-122.29283821 37.45922376,-122.28630141 37.45415497,-122.28883099 37.44629920,-122.28316717 37.44197138,-122.27554148 37.42297597,-122.25597410 37.40553692,-122.25196579 37.40129593,-122.25012043 37.40049143,-122.24823207 37.39897758,-122.24754551 37.39740941,-122.24778582 37.39621607,-122.24934787 37.39599102,-122.25005170 37.39871849,-122.25222328 37.39863668,-122.25342491 37.39737529,-122.25520162 37.39667289,-122.25528737 37.39522726,-122.27747460 37.37809616,-122.27977493 37.37858717,-122.28157729 37.37920106,-122.28322534 37.37952846,-122.28416939 37.38092656,-122.28621223 37.37984219,-122.28638389 37.37613857,-122.28382607 37.37843722,-122.27930278 37.37718220,-122.28196361 37.37652740,-122.28295058 37.37568167,-122.28216101 37.37523148,-122.28114822 37.37543608,-122.27934569 37.37528613,-122.27996369 37.37448121,-122.28104521 37.37454944,-122.28185197 37.37422883,-122.28290767 37.37474038,-122.28376597 37.37467224,-122.28428104 37.37399012,-122.28402346 37.37338989,-122.28610922 37.37364914,-122.28651264 37.37327388,-122.28672722 37.37207343,-122.28628398 37.37205448,-122.28574460 37.37166682,-122.28479711 37.37200981,-122.28327731 37.37137228,-122.28285511 37.37100700,-122.28279409 37.37125669,-122.28315527 37.37173756,-122.28321872 37.37220569,-122.28187007 37.37231918,-122.28193109 37.37294908,-122.28139163 37.37319149))"; + std::string const dissolve_reallife = "POLYGON((170718 605997,170718 605997,170776 606016,170773 606015,170786 606020,170778 606016,170787 606021,170781 606017,170795 606028,170795 606028,170829 606055,170939 606140,170933 605968,170933 605968,170932 605908,170929 605834,170920 605866,170961 605803,170739 605684,170699 605749,170691 605766,170693 605762,170686 605775,170688 605771,170673 605794,170676 605790,170668 605800,170672 605796,170651 605818,170653 605816,170639 605829,170568 605899,170662 605943,170633 605875,170603 605961,170718 605997))"; + + // https://svn.boost.org/trac/boost/ticket/10713 + std::string const dissolve_ticket10713 = "POLYGON((-0.7189743518829346 4.1308121681213379, 0.0831791982054710 4.1034231185913086, 0.1004156470298767 4.1107301712036133, 0.1044322624802589 4.1026973724365234, 0.0831791982054710 4.1034231185913086, -0.7711903452873230 3.7412264347076416, -0.7189743518829346 4.1308121681213379))"; } @@ -353,18 +377,14 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, << " type: " << string_from_type::name() ); - if (expected_clip_count > 0) - { - BOOST_CHECK_MESSAGE(dissolved1.size() == expected_clip_count, - "dissolve: " << caseid - << " #clips expected: " << expected_clip_count - << " detected: " << dissolved1.size() - << " type: " << string_from_type::name() - ); - } + BOOST_CHECK_MESSAGE(dissolved1.size() == expected_clip_count, + "dissolve: " << caseid + << " #clips expected: " << expected_clip_count + << " detected: " << dissolved1.size() + << " type: " << string_from_type::name() + ); - - //BOOST_CHECK_EQUAL(holes, expected_hole_count); + BOOST_CHECK_EQUAL(holes, expected_hole_count); BOOST_CHECK_CLOSE(length_or_area, expected_length_or_area, percentage); BOOST_CHECK_EQUAL(dissolved1.size(), dissolved2.size()); @@ -444,36 +464,18 @@ void test_all() TEST_DISSOLVE(dissolve_4, 8.0, 2, 0, 8); TEST_DISSOLVE(dissolve_5, 12.0, 2, 0, 8); TEST_DISSOLVE(dissolve_6, 16.0, 1, 0, 6); + + TEST_DISSOLVE(dissolve_7, 50.48056402439, 1, 0, 7); + TEST_DISSOLVE(dissolve_8, 25.6158412, 1, 0, 11); + TEST_DISSOLVE(dissolve_d1, 8.0, 1, 0, 4); TEST_DISSOLVE(dissolve_d2, 16.0, 1, 0, 10); TEST_DISSOLVE(dissolve_h1_a, 16.0, 1, 0, 6); TEST_DISSOLVE(dissolve_h1_b, 14.0, 1, 1, 10); - - // Hole: interior intersecting exterior - test_one("h2", - "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 3,5 4,1 1))", - 0, 8, 16.25); - - // Hole: two intersecting holes - test_one("h3", - "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 3,3 3,3 1,1 1),(2 2,2 3.5,3.5 3.5,3.5 2,2 2))", - 0, 5, 16); - - // Hole: self-intersecting hole - test_one("h4", - "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,3 3,3 2.5,1 3.5,1.5 3.5,1 1))", - 1, 9, 14.484848484848484); - - // See power point - test_one("case_1", - "POLYGON((1 3,0 9,9 5,1 7,9 8,2 5,10 10,9 2,1 3))", - 0, 7, 50.48056402439); - - // See power point, and http://en.wikipedia.org/wiki/Pentagram - test_one("pentagram", - "POLYGON((5 0,2.5 9,9.5 3.5,0.5 3.5,7.5 9,5 0))", - 0, 11, 25.6158412); + TEST_DISSOLVE(dissolve_h2, 16.25, 1, 0, 8); + TEST_DISSOLVE(dissolve_h3, 16.0, 1, 0, 5); // no generated hole (yet) + TEST_DISSOLVE(dissolve_h4, 14.484848, 1, 1, 9); TEST_DISSOLVE(dissolve_mail_2017_09_24_a, 0.5, 2, 0, 8); @@ -485,28 +487,20 @@ void test_all() TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.000361308800, 5, 0, 69); #ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS - test_one("pentagram_rev", - "POLYGON((5 0,7.5 9,0.5 3.5,9.5 3.5,2.5 9,5 0))", - 0, 11, 25.6158412); + TEST_DISSOLVE(dissolve_9, 25.6158412, 1, 0, 11); #endif // CCW polygons should turn CW after dissolve - test_one("cw", - "POLYGON((2 8,8 8,8 0,0 0,0 6,4 6,4 4,2 4,2 8))", - 0, 7, 60); + TEST_DISSOLVE(dissolve_10, 60.0, 1, 0, 7); #ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS - test_one("ccw", - "POLYGON((2 8,2 4,4 4,4 6,0 6,0 0,8 0,8 8,2 8))", - 0, 12, 64); // TODO: should have the same, 7, 60. The polygon is dissolved (splitted) but the extra polygon is still on top of the other + // TODO: should be the same, 7, 60. The polygon is dissolved (splitted) but the extra polygon is still on top of the other + TEST_DISSOLVE(dissolve_11, 60.0, 1, 0, 7); #endif - // https://svn.boost.org/trac/boost/ticket/10713 - test_one("ticket_10713", - "POLYGON((-0.7189743518829346 4.1308121681213379, 0.0831791982054710 4.1034231185913086, 0.1004156470298767 4.1107301712036133, 0.1044322624802589 4.1026973724365234, 0.0831791982054710 4.1034231185913086, -0.7711903452873230 3.7412264347076416, -0.7189743518829346 4.1308121681213379))", - 0, 8, 0.157052766); + TEST_DISSOLVE(dissolve_ticket10713, 0.157052766, 2, 0, 8); #ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS - // Reported by Artem Pavlenko at gitter + // Reported by Artem Pavlenko at gitter - until now these did not work yet // https://gitter.im/boostorg/geometry?at=58ef46408e4b63533dc49b48 test_one("artem1", "POLYGON((36.9121 2.03883,26.2052 54.353,60.0781 64.2202,96.2171 55.9826,71.1506 39.8365,5.72552 94.1523,4.06819 13.9054,59.7155 44.5877,60.9243 16.4597,48.8696 93.039,36.9121 2.03883))", @@ -523,40 +517,38 @@ void test_all() test_one("three_triangles", "MULTIPOLYGON(((1 1,5 5,8 0,1 1)),((4 2,0 8,5 9,4 2)),((5 3,4 8,10 4,5 3)))" , - 1, 13, 42.614078674948232); + 1, 13, 42.614078674948232, 1); test_one("simplex_two", "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)))", - 0, 8, 14.7); + 0, 8, 14.7, 1); test_one("simplex_three", "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)))", - 0, 14, 16.7945); + 0, 14, 16.7945, 1); test_one("simplex_four", "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)),((5 5,7 7,8 4,5 5)))", - 0, 18, 20.7581); + 0, 18, 20.7581, 1); // disjoint test_one("simplex_disjoint", "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((1 6,2 10,5 7,1 6)),((3 4,5 6,6 2,3 4)),((6 5,8 7,9 4,6 5)))", - 0, 16, 24.0); + 0, 16, 24.0, 4); // new hole of four test_one("new_hole", "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)),((3 1,5 4,8 4,3 1)))", - 1, 18, 19.5206); + 1, 18, 19.5206, 1); // GGL mailing list - report Javier - 2011, March 7 test_one("ggl_list_20110307_javier_01_a", "MULTIPOLYGON(((560 -400, 600 -400, 600 -440, 560 -440, 560 -400)), ((480 -400, 520 -400, 520 -440, 480 -440, 480 -400)), ((600 -320, 640 -320, 640 -360, 600 -360, 600 -320)), ((520 -400, 560 -400, 560 -440, 520 -440, 520 -400)))", - 1, 14, 6400); + 0, 14, 6400, 2); test_one("ggl_list_20110307_javier_01_b", "POLYGON((0 0, 2000 0, 2000 -2000, 0 -2000, 0 0), (560 -400, 560 -440, 600 -440, 600 -400, 560 -400), (480 -400, 480 -440, 520 -440, 520 -400, 480 -400), (600 -320, 600 -360, 640 -360, 640 -320, 600 -320), (520 -400, 520 -440, 560 -440, 560 -400, 520 -400))", - 1, 19, 3993600); + 2, 19, 3993600, 1); } - - /* //Should be solved (completely) differently // From mail on the ggl-mailing list @@ -574,14 +566,8 @@ void test_all() 0, 11, 25.6158412); */ - - test_one("ticket17", ticket17, - 1, 228, 0.00920834633689); - - // Real-life - test_one("toolkit", toolkit, - 0, 25, 91756.916526794434); - + TEST_DISSOLVE(dissolve_ticket17, 0.00920834633689, 1, 1, 228); + TEST_DISSOLVE(dissolve_reallife, 91756.916526794434, 1, 0, 25); } From 23e9f8628bd0de3115c8757f40c93d3e77ee98c1 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 16:38:35 +0200 Subject: [PATCH 153/188] [test] also move/cleanup multi-polygon tests --- extensions/test/algorithms/dissolve.cpp | 68 ++++++++++--------------- 1 file changed, 27 insertions(+), 41 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index 24c7f5855..b59c38453 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -95,7 +95,18 @@ namespace // Hole: self-intersecting hole std::string const dissolve_h4 = "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,3 3,3 2.5,1 3.5,1.5 3.5,1 1))"; - // Testcases send by Johan Doré at September 24, 2017: + std::string const multi_three_triangles = "MULTIPOLYGON(((1 1,5 5,8 0,1 1)),((4 2,0 8,5 9,4 2)),((5 3,4 8,10 4,5 3)))"; + std::string const multi_simplex_two = "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)))"; + std::string const multi_simplex_three = "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)))"; + std::string const multi_simplex_four = "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)),((5 5,7 7,8 4,5 5)))"; + std::string const multi_disjoint = "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((1 6,2 10,5 7,1 6)),((3 4,5 6,6 2,3 4)),((6 5,8 7,9 4,6 5)))"; + std::string const multi_new_interior = "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)),((3 1,5 4,8 4,3 1)))"; + + // Testcases sent on GGL mailing list - report Javier - 2011, March 7 + std::string const ggl_list_20110307_javier_01_a = "MULTIPOLYGON(((560 -400, 600 -400, 600 -440, 560 -440, 560 -400)), ((480 -400, 520 -400, 520 -440, 480 -440, 480 -400)), ((600 -320, 640 -320, 640 -360, 600 -360, 600 -320)), ((520 -400, 560 -400, 560 -440, 520 -440, 520 -400)))"; + std::string const ggl_list_20110307_javier_01_b = "POLYGON((0 0, 2000 0, 2000 -2000, 0 -2000, 0 0), (560 -400, 560 -440, 600 -440, 600 -400, 560 -400), (480 -400, 480 -440, 520 -440, 520 -400, 480 -400), (600 -320, 600 -360, 640 -360, 640 -320, 600 -320), (520 -400, 520 -440, 560 -440, 560 -400, 520 -400))"; + + // Testcases sent by Johan Doré at September 24, 2017: std::string const dissolve_mail_2017_09_24_a = "POLYGON((0 1, 1 0, 1 1, 0 0, 0 1))"; // two triangles std::string const dissolve_mail_2017_09_24_b = "POLYGON((1 0, 0 0, 0 4, 4 4, 4 0))"; // input is not closed std::string const dissolve_mail_2017_09_24_c = "POLYGON((0 0, 1 0, 0 -1, 0.0001 1))"; // spike and not closed @@ -451,12 +462,15 @@ void test_one(std::string const& caseid, std::string const& wkt, #define TEST_DISSOLVE(caseid, area, clips, holes, points) \ (test_one) ( #caseid, caseid, holes, points, area, clips) +#define TEST_MULTI(caseid, area, clips, holes, points) \ + (test_one) ( #caseid, caseid, holes, points, area, clips) template void test_all() { typedef bg::model::ring

ring; typedef bg::model::polygon

polygon; + typedef bg::model::multi_polygon multi_polygon; TEST_DISSOLVE(dissolve_1, 8.0, 1, 0, 6); TEST_DISSOLVE(dissolve_2, 7.9296875, 1, 1, 12); @@ -499,6 +513,18 @@ void test_all() TEST_DISSOLVE(dissolve_ticket10713, 0.157052766, 2, 0, 8); + TEST_MULTI(multi_three_triangles, 42.614078674948232, 1, 1, 13); + TEST_MULTI(multi_simplex_two, 14.7, 1, 0, 8); + TEST_MULTI(multi_simplex_three, 16.7945, 1, 0, 14); + TEST_MULTI(multi_simplex_four, 20.7581, 1, 0, 18); + TEST_MULTI(multi_disjoint, 24.0, 4, 0, 16); + TEST_MULTI(multi_new_interior, 19.5206, 1, 1, 18); + TEST_MULTI(ggl_list_20110307_javier_01_a, 6400.0, 2, 0, 14); + + TEST_DISSOLVE(ggl_list_20110307_javier_01_b, 3993600.0, 1, 2, 19); + TEST_DISSOLVE(dissolve_ticket17, 0.00920834633689, 1, 1, 228); + TEST_DISSOLVE(dissolve_reallife, 91756.916526794434, 1, 0, 25); + #ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS // Reported by Artem Pavlenko at gitter - until now these did not work yet // https://gitter.im/boostorg/geometry?at=58ef46408e4b63533dc49b48 @@ -511,44 +537,6 @@ void test_all() 0, 0, 0); // TODO: expected #endif - // Multi-geometries - { - typedef bg::model::multi_polygon multi_polygon; - - test_one("three_triangles", - "MULTIPOLYGON(((1 1,5 5,8 0,1 1)),((4 2,0 8,5 9,4 2)),((5 3,4 8,10 4,5 3)))" , - 1, 13, 42.614078674948232, 1); - - test_one("simplex_two", - "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)))", - 0, 8, 14.7, 1); - test_one("simplex_three", - "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)))", - 0, 14, 16.7945, 1); - test_one("simplex_four", - "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)),((5 5,7 7,8 4,5 5)))", - 0, 18, 20.7581, 1); - - // disjoint - test_one("simplex_disjoint", - "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((1 6,2 10,5 7,1 6)),((3 4,5 6,6 2,3 4)),((6 5,8 7,9 4,6 5)))", - 0, 16, 24.0, 4); - - // new hole of four - test_one("new_hole", - "MULTIPOLYGON(((0 0,1 4,4 1,0 0)),((2 2,3 6,6 3,2 2)),((3 4,5 6,6 2,3 4)),((3 1,5 4,8 4,3 1)))", - 1, 18, 19.5206, 1); - - // GGL mailing list - report Javier - 2011, March 7 - test_one("ggl_list_20110307_javier_01_a", - "MULTIPOLYGON(((560 -400, 600 -400, 600 -440, 560 -440, 560 -400)), ((480 -400, 520 -400, 520 -440, 480 -440, 480 -400)), ((600 -320, 640 -320, 640 -360, 600 -360, 600 -320)), ((520 -400, 560 -400, 560 -440, 520 -440, 520 -400)))", - 0, 14, 6400, 2); - - test_one("ggl_list_20110307_javier_01_b", - "POLYGON((0 0, 2000 0, 2000 -2000, 0 -2000, 0 0), (560 -400, 560 -440, 600 -440, 600 -400, 560 -400), (480 -400, 480 -440, 520 -440, 520 -400, 480 -400), (600 -320, 600 -360, 640 -360, 640 -320, 600 -320), (520 -400, 520 -440, 560 -440, 560 -400, 520 -400))", - 2, 19, 3993600, 1); - } - /* //Should be solved (completely) differently // From mail on the ggl-mailing list @@ -566,8 +554,6 @@ void test_all() 0, 11, 25.6158412); */ - TEST_DISSOLVE(dissolve_ticket17, 0.00920834633689, 1, 1, 228); - TEST_DISSOLVE(dissolve_reallife, 91756.916526794434, 1, 0, 25); } From a04050a31aa6649f6c89115da1e172a1cbc59908 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 6 Oct 2017 17:38:04 +0200 Subject: [PATCH 154/188] [test] for dissolve unit tests now also check validity - and most cases are already valid --- extensions/test/algorithms/dissolve.cpp | 72 ++++++++++++++++++------- 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index b59c38453..894e8dcb3 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -18,6 +18,7 @@ // To check results #include #include +#include #include #include @@ -262,6 +263,19 @@ private : #endif +//! Unittest settings: validity, tolerance +struct ut_settings +{ + double percentage; + bool test_validity; + + explicit ut_settings(double p = 0.001, bool tv = true) + : percentage(p) + , test_validity(tv) + {} + +}; + template std::string as_wkt(Geometry const& geometry) { @@ -272,10 +286,11 @@ std::string as_wkt(Geometry const& geometry) template void test_dissolve(std::string const& caseid, Geometry const& geometry, + double expected_area, std::size_t expected_clip_count, std::size_t expected_hole_count, std::size_t expected_point_count, - double expected_length_or_area, double percentage) + ut_settings const& settings) { typedef typename bg::coordinate_type::type coordinate_type; @@ -350,10 +365,20 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, std::vector dissolved2; bg::dissolve_inserter(geometry, std::back_inserter(dissolved2)); - // Check dissolve and difference dissolve/dissolve_inserter - std::vector dissolved3; + // Check dissolve and validity, assuming GeometryOut is a single polygon + typedef bg::model::multi_polygon multi_polygon; + multi_polygon dissolved3; bg::dissolve(geometry, dissolved3); + if (settings.test_validity) + { + std::string message; + bool const valid = bg::is_valid(dissolved3, message); + BOOST_CHECK_MESSAGE(valid, + "dissolve: " << caseid + << " geometry is not valid: " << message); + } + // Make output unique (TODO: this should probably be moved to dissolve itself) BOOST_FOREACH(GeometryOut& dissolved, dissolved1) { @@ -396,7 +421,7 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, ); BOOST_CHECK_EQUAL(holes, expected_hole_count); - BOOST_CHECK_CLOSE(length_or_area, expected_length_or_area, percentage); + BOOST_CHECK_CLOSE(length_or_area, expected_area, settings.percentage); BOOST_CHECK_EQUAL(dissolved1.size(), dissolved2.size()); BOOST_CHECK_EQUAL(dissolved1.size(), dissolved3.size()); @@ -417,17 +442,19 @@ void test_dissolve(std::string const& caseid, Geometry const& geometry, template void test_one(std::string const& caseid, std::string const& wkt, - std::size_t expected_hole_count, std::size_t expected_point_count, - double expected_length_or_area, - std::size_t expected_clip_count = 0, // to be moved later - double percentage = 0.001) + double expected_area, + std::size_t expected_clip_count, + std::size_t expected_hole_count, + std::size_t expected_point_count, + ut_settings const& settings) { Geometry geometry; bg::read_wkt(wkt, geometry); test_dissolve(caseid, geometry, + expected_area, expected_clip_count, expected_hole_count, expected_point_count, - expected_length_or_area, percentage); + settings); #ifdef BOOST_GEOMETRY_TEST_MULTI_PERMUTATIONS // Test different combinations of a multi-polygon @@ -452,18 +479,24 @@ void test_one(std::string const& caseid, std::string const& wkt, out << "_" << index; geometry2.push_back(geometry[index]); } - test_dissolve(out.str(), geometry2, expected_hole_count, - expected_point_count, expected_length_or_area, percentage); + test_dissolve(out.str(), geometry2, expected_area, + expected_clip_count, expected_hole_count, expected_point_count, settings); } while (std::next_permutation(indices.begin(), indices.end())); #endif } -#define TEST_DISSOLVE(caseid, area, clips, holes, points) \ - (test_one) ( #caseid, caseid, holes, points, area, clips) +#define TEST_DISSOLVE(caseid, area, clips, holes, points) { \ + ut_settings settings; \ + (test_one) ( #caseid, caseid, area, clips, holes, points, settings); } -#define TEST_MULTI(caseid, area, clips, holes, points) \ - (test_one) ( #caseid, caseid, holes, points, area, clips) +#define TEST_DISSOLVE_IGNORE(caseid, area, clips, holes, points) { \ + ut_settings settings; settings.test_validity = false; \ + (test_one) ( #caseid, caseid, area, clips, holes, points, settings); } + +#define TEST_MULTI(caseid, area, clips, holes, points) { \ + ut_settings settings; \ + (test_one) ( #caseid, caseid, area, clips, holes, points, settings); } template void test_all() @@ -493,10 +526,11 @@ void test_all() TEST_DISSOLVE(dissolve_mail_2017_09_24_a, 0.5, 2, 0, 8); - // These are not all complete yet - TEST_DISSOLVE(dissolve_mail_2017_09_24_b, 16.0, 1, 0, 5); - TEST_DISSOLVE(dissolve_mail_2017_09_24_c, 0.4999, 1, 0, 4); - TEST_DISSOLVE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 4); + // These do not have closed input, and output is not closed either. TODO. + TEST_DISSOLVE_IGNORE(dissolve_mail_2017_09_24_b, 16.0, 1, 0, 5); + TEST_DISSOLVE_IGNORE(dissolve_mail_2017_09_24_c, 0.4999, 1, 0, 4); + TEST_DISSOLVE_IGNORE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 4); + TEST_DISSOLVE(dissolve_mail_2017_09_24_e, 0.001801138128, 5, 0, 69); TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.000361308800, 5, 0, 69); From 9a022331b23085e9c285cb7e079957b6fb5c98bf Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 11 Oct 2017 10:42:52 +0200 Subject: [PATCH 155/188] [overlay] insert missing curly brace (earlier commit error) --- .../algorithms/detail/overlay/traversal_switch_detector.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp index 8168dc4c5..24fe2b98e 100644 --- a/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_switch_detector.hpp @@ -280,6 +280,7 @@ struct traversal_switch_detector // For now: return false; } + } if (connected_region.isolated != isolation_yes && ! multiple) { From 4d1b094e6a100431b965c354b91e9cc5b310b96d Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 11 Oct 2017 12:58:29 +0200 Subject: [PATCH 156/188] [overlay] discard self-turns i/u for intersection traveling to themselves --- .../overlay/enrich_intersection_points.hpp | 3 ++ .../detail/overlay/handle_self_turns.hpp | 54 +++++++++++++++++++ .../overlay/multi_overlay_cases.hpp | 7 +++ .../intersection/intersection_multi.cpp | 1 + 4 files changed, 65 insertions(+) diff --git a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index dc3be7c38..e25445651 100644 --- a/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -416,6 +416,9 @@ inline void enrich_intersection_points(Turns& turns, detail::overlay::enrich_assign(mit->second, turns); } + // Check some specific type of self-turns (after getting enriched info) + detail::overlay::discard_self_turns_which_loop(turns); + if (has_colocations) { // First gather cluster properties (using even clusters with diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp index 633a03fd5..b1ab28b38 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp @@ -232,6 +232,60 @@ struct discard_open_turns // For difference, it should be done in a different way (TODO) + +template +inline void discard_self_turns_which_loop(Turns& turns) +{ + if (operation_from_overlay::value == operation_union) + { + // For union, self-turn i/u traveling to itself are allowed to form + // holes. #case_recursive_boxes_37 + // TODO: this can be finetuned by inspecting the cluster too, + // and if there are non-self-turns the polygons on their sides can + // be checked + return; + } + + typedef typename boost::range_value::type turn_type; + typedef typename turn_type::turn_operation_type op_type; + + signed_size_type turn_index = 0; + for (typename boost::range_iterator::type + it = boost::begin(turns); + it != boost::end(turns); + ++it, ++turn_index) + { + turn_type& turn = *it; + + if (! is_self_turn(turn)) + { + continue; + } + if (! turn.combination(operation_intersection, operation_union)) + { + // ii may travel to itself + continue; + } + + for (int i = 0; i < 2; i++) + { + op_type& op = turn.operations[i]; + + if (op.enriched.startable + && op.enriched.get_next_turn_index() == turn_index) + { + // Self-turn i/u traveling to itself. Discard it. + // (alternatively it might be made unstartable - but the + // intersection-operation may not be traveled anyway, and the + // union-operation is not traveled at all in intersections + // #case_recursive_boxes_77 + turn.discarded = true; + } + } + } + +} + }} // namespace detail::overlay #endif //DOXYGEN_NO_DETAIL diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index f0f105d8b..d5d27164c 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1200,6 +1200,13 @@ static std::string case_recursive_boxes_76[2] = "MULTIPOLYGON(((2 0,0 0,0 3,1 4,0 4,0 5,2 5,2 4,3 4,4 5,5 5,5 0,2 0),(3 1,4 0,4 1,3 1),(3 2,2 1,3 1,3 2),(3 3,3 4,2 3,3 3),(2 1,1 1,1.5 0.5,2 1),(4 3,4 4,3 4,4 3)))" }; +static std::string case_recursive_boxes_77[2] = +{ + // Needs discarding self i/u turn traveling to itself (for intersection only) + "MULTIPOLYGON(((1 2,2 3,2 2,1 1,1 2)),((1 4,1 5,2 5,2 4,1 4)),((1 4,0 3,0 4,1 4)),((4 2,4 1,3 1,3 3,4 2)),((3 1,3 0,1 0,2 1,2.5 0.5,3 1)),((3 3,3 5,4 5,4 3,3 3)))", + "MULTIPOLYGON(((3 4,3 5,4 5,4 4,3 4)),((3 1,3 0,2 0,3 1)),((3 1,2 1,2 2,3 2,3 1)),((2 3,2 4,3 3,2 3)),((2 1,1 0,0 0,1 1,2 1)),((1 4,2 3,2 2,0 2,0 4,1 4),(2 3,1 3,1.5 2.5,2 3)),((1 4,1 5,2 5,2 4,1 4)),((4 1,5 0,4 0,4 1)),((4 1,4 2,5 1,4 1)),((4 2,5 3,5 2,4 2)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 3f41b4094..4f25102ec 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -348,6 +348,7 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_75, 5, -1, 16.75); TEST_INTERSECTION(case_recursive_boxes_76, 2, -1, 18.25); + TEST_INTERSECTION(case_recursive_boxes_77, 5, -1, 3.5); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], 2, 10, 6.0); // Area from SQL Server From 3010148541b6ac39737234cbf6ab4f9b0b2e7618 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 11 Oct 2017 13:52:31 +0200 Subject: [PATCH 157/188] [test] add last testcases to union/difference --- test/algorithms/set_operations/difference/difference_multi.cpp | 1 + test/algorithms/set_operations/union/union_multi.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 1baa77cff..332935e4d 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -405,6 +405,7 @@ void test_areal() #endif TEST_DIFFERENCE(case_recursive_boxes_76, 7, 3.75, 4, 2.5, 9); + TEST_DIFFERENCE(case_recursive_boxes_77, 4, 3.75, 7, 6.25, 8); { ut_settings sym_settings; diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 9e4cbf335..72b2d7f85 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -402,6 +402,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_74, 1, 1, -1, 24.75); TEST_UNION(case_recursive_boxes_75, 1, 2, -1, 23.25); TEST_UNION(case_recursive_boxes_76, 1, 0, -1, 24.5); + TEST_UNION(case_recursive_boxes_77, 8, 1, -1, 13.5); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From b20aeba7243aabe70949b6cb42260798ef7fea9b Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 11 Oct 2017 13:54:48 +0200 Subject: [PATCH 158/188] [overlay] fix cases where non-traversed rings were incorrectly added. This also changes the order of checks --- .../algorithms/detail/overlay/overlay.hpp | 25 +++++++++++-------- .../overlay/multi_overlay_cases.hpp | 7 ++++++ .../intersection/intersection_multi.cpp | 3 ++- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 93e4d0741..85986500f 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -132,6 +132,21 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C op.seg_id.ring_index ); + if (! is_self_turn(turn) + && ( + (target_operation == operation_union + && op.enriched.count_left > 0) + || (target_operation == operation_intersection + && op.enriched.count_right <= 2))) + { + // Avoid including untraversed rings which have polygons on + // their left side (union) or not two on their right side (int) + // This can only be done for non-self-turns because of count + // information + turn_info_map[ring_id].has_blocked_turn = true; + continue; + } + if (turn.any_blocked()) { turn_info_map[ring_id].has_blocked_turn = true; @@ -142,16 +157,6 @@ inline void get_ring_turn_info(TurnInfoMap& turn_info_map, Turns const& turns, C continue; } - if (target_operation == operation_union - && ! is_self_turn(turn) - && op.enriched.count_left > 0) - { - // Avoid including untraversed rings in unions which have - // polygons on their left side - turn_info_map[ring_id].has_blocked_turn = true; - continue; - } - // Check information in colocated turns if (! cluster_checked && turn.is_clustered()) { diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index d5d27164c..e8bd83dd5 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1207,6 +1207,13 @@ static std::string case_recursive_boxes_77[2] = "MULTIPOLYGON(((3 4,3 5,4 5,4 4,3 4)),((3 1,3 0,2 0,3 1)),((3 1,2 1,2 2,3 2,3 1)),((2 3,2 4,3 3,2 3)),((2 1,1 0,0 0,1 1,2 1)),((1 4,2 3,2 2,0 2,0 4,1 4),(2 3,1 3,1.5 2.5,2 3)),((1 4,1 5,2 5,2 4,1 4)),((4 1,5 0,4 0,4 1)),((4 1,4 2,5 1,4 1)),((4 2,5 3,5 2,4 2)))" }; +static std::string case_recursive_boxes_78[2] = +{ + // Needs checking intersection/right_count in overlay, as was already done for union + "MULTIPOLYGON(((3 2,1 2,1 4,3 2)),((3 2,3 4,4 4,3.5 3.5,4.5 2.5,5 3,5 1,4 1,3 0,3 2),(4 2,3.5 1.5,4 1,4 2)),((1 2,1 1,0 0,0 2,1 2)),((0 4,1 3,0 3,0 4)),((0 4,1 5,1.5 4.5,2 5,3 5,3 4,0 4)),((1 1,2 1,1 0,1 1)),((4 1,5 0,4 0,4 1)),((4 3,4 5,5 4,5 3,4 3)))", + "MULTIPOLYGON(((2 4,1 4,1 5,3 5,2 4)),((2 4,4 4,4 3,5 3,5 2,4 1,4 2,3 2,3 3,2 2,2 4)),((2 2,2 1,0 1,0 4,1 3,1 4,2 3,1 2,2 2)),((1 4,0 4,0 5,1 4)),((0 1,1 0,0 0,0 1)),((4 1,3 1,3 2,4 1)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 4f25102ec..93224d8d6 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -347,8 +347,9 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_75, 5, -1, 16.75); TEST_INTERSECTION(case_recursive_boxes_76, 2, -1, 18.25); - TEST_INTERSECTION(case_recursive_boxes_77, 5, -1, 3.5); + TEST_INTERSECTION(case_recursive_boxes_78, 9, -1, 8.0); + test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], 2, 10, 6.0); // Area from SQL Server From 1a7b13dadca5346a7b946b10afc5241593505be7 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 11 Oct 2017 14:38:28 +0200 Subject: [PATCH 159/188] [test] add last testcases to union/difference --- test/algorithms/set_operations/difference/difference_multi.cpp | 1 + test/algorithms/set_operations/union/union_multi.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 332935e4d..034076dc6 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -406,6 +406,7 @@ void test_areal() TEST_DIFFERENCE(case_recursive_boxes_76, 7, 3.75, 4, 2.5, 9); TEST_DIFFERENCE(case_recursive_boxes_77, 4, 3.75, 7, 6.25, 8); + TEST_DIFFERENCE(case_recursive_boxes_78, 11, 5.5, 8, 4.5, 14); { ut_settings sym_settings; diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 72b2d7f85..e8ada9695 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -403,6 +403,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_75, 1, 2, -1, 23.25); TEST_UNION(case_recursive_boxes_76, 1, 0, -1, 24.5); TEST_UNION(case_recursive_boxes_77, 8, 1, -1, 13.5); + TEST_UNION(case_recursive_boxes_78, 2, 5, -1, 18.0); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 877ef9834070011a2dae6c67ab60bb8b50f35dfe Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 11 Oct 2017 14:58:44 +0200 Subject: [PATCH 160/188] [overlay] add check to skip self-turns in rings, boxes, and polygons without interior rings or multi-polygons with only one such polygon --- .../detail/overlay/needs_self_turns.hpp | 83 +++++++++++++++++++ .../algorithms/detail/overlay/overlay.hpp | 5 ++ 2 files changed, 88 insertions(+) create mode 100644 include/boost/geometry/algorithms/detail/overlay/needs_self_turns.hpp diff --git a/include/boost/geometry/algorithms/detail/overlay/needs_self_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/needs_self_turns.hpp new file mode 100644 index 000000000..87fdd00d5 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/needs_self_turns.hpp @@ -0,0 +1,83 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2017-2017 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) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_NEEDS_SELF_TURNS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_NEEDS_SELF_TURNS_HPP + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template +< + typename Geometry, + typename Tag = typename tag::type +> +struct needs_self_turns +{ +}; + +template +struct needs_self_turns +{ + static inline bool apply(Geometry const&) + { + return false; + } +}; + +template +struct needs_self_turns +{ + static inline bool apply(Geometry const&) + { + return false; + } +}; + +template +struct needs_self_turns +{ + static inline bool apply(Geometry const& polygon) + { + return geometry::num_interior_rings(polygon) > 0; + } +}; + +template +struct needs_self_turns +{ + static inline bool apply(Geometry const& multi) + { + typedef typename boost::range_value::type polygon_type; + std::size_t const n = boost::size(multi); + return n > 1 || (n == 1 + && needs_self_turns + ::apply(*boost::begin(multi))); + } +}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_NEEDS_SELF_TURNS_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp index 85986500f..b415f97a0 100644 --- a/include/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -305,9 +306,13 @@ std::cout << "get turns" << std::endl; visitor.visit_turns(1, turns); #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + if (needs_self_turns::apply(geometry1)) { self_get_turn_points::self_turns(geometry1, strategy, robust_policy, turns, policy, 0); + } + if (needs_self_turns::apply(geometry2)) + { self_get_turn_points::self_turns(geometry2, strategy, robust_policy, turns, policy, 1); } From 477b502bb4c807a9c13a6f828db24bf7cd75010b Mon Sep 17 00:00:00 2001 From: Mateusz Loskot Date: Wed, 11 Oct 2017 19:16:40 +0100 Subject: [PATCH 161/188] [doc] Fix 'a as' typo to read 'as a' --- doc/design_rationale.qbk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/design_rationale.qbk b/doc/design_rationale.qbk index 301a9ee81..377e62da0 100644 --- a/doc/design_rationale.qbk +++ b/doc/design_rationale.qbk @@ -546,7 +546,7 @@ where a strategy is specified explicitly and constructed with a radius. [heading Point Concept] The five traits classes mentioned in the previous sections form together the Point Concept. Any point type for which specializations are implemented in -the traits namespace should be accepted a as valid type. So the Point Concept +the traits namespace should be accepted as a valid type. So the Point Concept consists of: * a specialization for `traits::tag` From 658a7d99cd2f11546008310bba12808220d70e1c Mon Sep 17 00:00:00 2001 From: Mateusz Loskot Date: Wed, 11 Oct 2017 20:21:20 +0100 Subject: [PATCH 162/188] [doc] Document Axis Order convention Clarifies question #427 --- doc/design_rationale.qbk | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/design_rationale.qbk b/doc/design_rationale.qbk index 377e62da0..93db7584d 100644 --- a/doc/design_rationale.qbk +++ b/doc/design_rationale.qbk @@ -637,6 +637,20 @@ and modify our distance function: Of course also the apply functions in the dispatch specializations will return a result like this. They have a strategy as a template parameter everywhere, making the less verbose version possible. +[heading Axis Order] + +This is an important note for users who develop GIS applications, require compliance with OGC +standards or use variety of coordinate reference systems (CRS). + +Boost.Geometry convention is that coordinate values of 2D tuple is always given according to +mathematical axis order: X,Y. Considering GIS applications, that always means easting,northing or, +in terms of geographic coordinate system: longitude,latitude. +Boost.Geometry does not allow or respect coordinate values listed in the axis order as +specified by coordinate reference system (CRS). + +In practice, users may easily adapt point types with alternate axis ordering and +specify coordinate access in order as expected by Boost.Geometry. + [heading Summary] In this design rationale, __boost_geometry__ is step by step designed using tag dispatching, From fac0c80692e696b21f403c1a2c532cd3d10f516f Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Wed, 18 Oct 2017 15:34:07 +0200 Subject: [PATCH 163/188] [overlay] fix sort order, which might contain duplicate turn_index values in e.g. buffer --- .../algorithms/detail/overlay/sort_by_side.hpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp index 5ad2e41b1..fea5698ae 100644 --- a/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/sort_by_side.hpp @@ -91,13 +91,20 @@ struct less_by_index template inline bool operator()(const T& first, const T& second) const { + // Length might be considered too // First order by from/to if (first.direction != second.direction) { return first.direction < second.direction; } - // All the same, order by turn index (we might consider length too) - return first.turn_index < second.turn_index; + // Then by turn index + if (first.turn_index != second.turn_index) + { + return first.turn_index < second.turn_index; + } + // This can also be the same (for example in buffer), but seg_id is + // never the same + return first.seg_id < second.seg_id; } }; From 917b18e66ca29eaff204b7cb668ea3f8e09ba8ce Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Thu, 19 Oct 2017 15:27:47 +0200 Subject: [PATCH 164/188] [wkt] apply the wkt change for all geometries instead of only polygon. This is necessary for the splitted correct_closure algorithm, which needs an exact WKT representation of its geometry --- include/boost/geometry/io/wkt/write.hpp | 86 ++++++++++++++----------- 1 file changed, 48 insertions(+), 38 deletions(-) diff --git a/include/boost/geometry/io/wkt/write.hpp b/include/boost/geometry/io/wkt/write.hpp index b98c894b3..344c0ef0f 100644 --- a/include/boost/geometry/io/wkt/write.hpp +++ b/include/boost/geometry/io/wkt/write.hpp @@ -1,12 +1,12 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. -// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. -// Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2007-2017 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2017 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2017 Mateusz Loskot, London, UK. +// Copyright (c) 2014-2017 Adam Wulkiewicz, Lodz, Poland. // This file was modified by Oracle on 2015. -// Modifications copyright (c) 2015, Oracle and/or its affiliates. +// Modifications copyright (c) 2015-2017, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -111,7 +111,7 @@ template struct wkt_point { template - static inline void apply(std::basic_ostream& os, Point const& p) + static inline void apply(std::basic_ostream& os, Point const& p, bool) { os << Policy::apply() << "("; stream_coordinate::type::value>::apply(os, p); @@ -123,12 +123,18 @@ struct wkt_point \brief Stream ranges as WKT \note policy is used to stream prefix/postfix, enabling derived classes to override this */ -template +template +< + typename Range, + bool ForceClosurePossible, + typename PrefixPolicy, + typename SuffixPolicy +> struct wkt_range { template static inline void apply(std::basic_ostream& os, - Range const& range, bool force_closed) + Range const& range, bool force_closure) { typedef typename boost::range_iterator::type iterator_type; @@ -153,7 +159,8 @@ struct wkt_range } // optionally, close range to ring by repeating the first point - if (force_closed + if (ForceClosurePossible + && force_closure && boost::size(range) > 1 && detail::disjoint::disjoint_point_point(*begin, *(end - 1))) { @@ -164,12 +171,6 @@ struct wkt_range os << SuffixPolicy::apply(); } - template - static inline void apply(std::basic_ostream& os, - Range const& range) - { - apply(os, range, false); - } private: typedef typename boost::range_value::type point_type; @@ -179,11 +180,12 @@ private: \brief Stream sequence of points as WKT-part, e.g. (1 2),(3 4) \note Used in polygon, all multi-geometries */ -template +template struct wkt_sequence : wkt_range < Range, + ForceClosurePossible, opening_parenthesis, closing_parenthesis > @@ -194,15 +196,14 @@ struct wkt_poly { template static inline void apply(std::basic_ostream& os, - Polygon const& poly) + Polygon const& poly, bool force_closure) { typedef typename ring_type::type ring; - bool const force_closed = true; os << PrefixPolicy::apply(); // TODO: check EMPTY here os << "("; - wkt_sequence::apply(os, exterior_ring(poly), force_closed); + wkt_sequence::apply(os, exterior_ring(poly), force_closure); typename interior_return_type::type rings = interior_rings(poly); @@ -210,10 +211,11 @@ struct wkt_poly it = boost::begin(rings); it != boost::end(rings); ++it) { os << ","; - wkt_sequence::apply(os, *it, force_closed); + wkt_sequence::apply(os, *it, force_closure); } os << ")"; } + }; template @@ -221,7 +223,7 @@ struct wkt_multi { template static inline void apply(std::basic_ostream& os, - Multi const& geometry) + Multi const& geometry, bool force_closure) { os << PrefixPolicy::apply(); // TODO: check EMPTY here @@ -236,7 +238,7 @@ struct wkt_multi { os << ","; } - StreamPolicy::apply(os, *it); + StreamPolicy::apply(os, *it, force_closure); } os << ")"; @@ -250,14 +252,14 @@ struct wkt_box template static inline void apply(std::basic_ostream& os, - Box const& box) + Box const& box, bool force_closure) { - // Convert to ring, then stream - typedef model::ring ring_type; + // Convert to open cw ring, then stream + typedef model::ring ring_type; ring_type ring; geometry::convert(box, ring); os << "POLYGON("; - wkt_sequence::apply(os, ring); + wkt_sequence::apply(os, ring, force_closure); os << ")"; } @@ -278,7 +280,7 @@ struct wkt_segment template static inline void apply(std::basic_ostream& os, - Segment const& segment) + Segment const& segment, bool) { // Convert to two points, then stream typedef boost::array sequence; @@ -290,7 +292,7 @@ struct wkt_segment // In Boost.Geometry a segment is represented // in WKT-format like (for 2D): LINESTRING(x y,x y) os << "LINESTRING"; - wkt_sequence::apply(os, points); + wkt_sequence::apply(os, points, false); } private: @@ -324,6 +326,7 @@ struct wkt : detail::wkt::wkt_range < Linestring, + false, detail::wkt::prefix_linestring_par, detail::wkt::closing_parenthesis > @@ -355,6 +358,7 @@ struct wkt : detail::wkt::wkt_range < Ring, + true, detail::wkt::prefix_ring_par_par, detail::wkt::double_closing_parenthesis > @@ -393,7 +397,8 @@ struct wkt Multi, detail::wkt::wkt_sequence < - typename boost::range_value::type + typename boost::range_value::type, + false >, detail::wkt::prefix_multilinestring > @@ -418,9 +423,10 @@ template struct devarianted_wkt { template - static inline void apply(OutputStream& os, Geometry const& geometry) + static inline void apply(OutputStream& os, Geometry const& geometry, + bool force_closure) { - wkt::apply(os, geometry); + wkt::apply(os, geometry, force_closure); } }; @@ -431,25 +437,27 @@ struct devarianted_wkt > struct visitor: static_visitor { OutputStream& m_os; + bool m_force_closure; - visitor(OutputStream& os) + visitor(OutputStream& os, bool force_closure) : m_os(os) + , m_force_closure(force_closure) {} template inline void operator()(Geometry const& geometry) const { - devarianted_wkt::apply(m_os, geometry); + devarianted_wkt::apply(m_os, geometry, m_force_closure); } }; template static inline void apply( OutputStream& os, - variant const& geometry - ) + variant const& geometry, + bool force_closure) { - boost::apply_visitor(visitor(os), geometry); + boost::apply_visitor(visitor(os, force_closure), geometry); } }; @@ -473,8 +481,9 @@ class wkt_manipulator { public: - inline wkt_manipulator(Geometry const& g) + inline wkt_manipulator(Geometry const& g, bool force_closure = true) : m_geometry(g) + , m_force_closure(force_closure) {} template @@ -482,13 +491,14 @@ public: std::basic_ostream& os, wkt_manipulator const& m) { - dispatch::devarianted_wkt::apply(os, m.m_geometry); + dispatch::devarianted_wkt::apply(os, m.m_geometry, m.m_force_closure); os.flush(); return os; } private: Geometry const& m_geometry; + bool m_force_closure; }; /*! From 38aa0c24a944f0df3ea9795140a75635991a93ed Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Thu, 19 Oct 2017 15:51:43 +0200 Subject: [PATCH 165/188] [correct] Split off correct_closure from correct Including unit test --- include/boost/geometry/algorithms/correct.hpp | 20 +- .../geometry/algorithms/correct_closure.hpp | 235 ++++++++++++++++++ include/boost/geometry/io/wkt/write.hpp | 3 +- test/algorithms/Jamfile.v2 | 1 + test/algorithms/correct.cpp | 5 +- test/algorithms/correct_closure.cpp | 114 +++++++++ test/algorithms/test_correct.hpp | 3 +- 7 files changed, 359 insertions(+), 22 deletions(-) create mode 100644 include/boost/geometry/algorithms/correct_closure.hpp create mode 100644 test/algorithms/correct_closure.cpp diff --git a/include/boost/geometry/algorithms/correct.hpp b/include/boost/geometry/algorithms/correct.hpp index a572d921d..07e012faf 100644 --- a/include/boost/geometry/algorithms/correct.hpp +++ b/include/boost/geometry/algorithms/correct.hpp @@ -32,6 +32,7 @@ #include #include +#include #include #include @@ -45,7 +46,6 @@ #include #include -#include #include #include @@ -140,23 +140,9 @@ struct correct_ring template static inline void apply(Ring& r, Strategy const& strategy) { - // Check close-ness - if (boost::size(r) > 2) - { - // check if closed, if not, close it - bool const disjoint = geometry::disjoint(*boost::begin(r), *(boost::end(r) - 1)); - closure_selector const s = geometry::closure::value; + // Correct closure if necessary + detail::correct_closure::close_or_open_ring::apply(r); - if (disjoint && (s == closed)) - { - geometry::append(r, *boost::begin(r)); - } - if (! disjoint && s != closed) - { - // Open it by removing last point - geometry::traits::resize::apply(r, boost::size(r) - 1); - } - } // Check area typedef typename Strategy::return_type area_result_type; Predicate predicate; diff --git a/include/boost/geometry/algorithms/correct_closure.hpp b/include/boost/geometry/algorithms/correct_closure.hpp new file mode 100644 index 000000000..323107f23 --- /dev/null +++ b/include/boost/geometry/algorithms/correct_closure.hpp @@ -0,0 +1,235 @@ +// Boost.Geometry + +// Copyright (c) 2017 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) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_CORRECT_CLOSURE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_CORRECT_CLOSURE_HPP + +#include + +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace boost { namespace geometry +{ + +// Silence warning C4127: conditional expression is constant +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) +#endif + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace correct_closure +{ + +template +struct nop +{ + static inline void apply(Geometry& ) + {} +}; + + +// Close a ring, if not closed, or open it +template +struct close_or_open_ring +{ + static inline void apply(Ring& r) + { + if (boost::size(r) <= 2) + { + return; + } + + bool const disjoint = geometry::disjoint(*boost::begin(r), + *(boost::end(r) - 1)); + closure_selector const s = geometry::closure::value; + + if (disjoint && s == closed) + { + // Close it by adding first point + geometry::append(r, *boost::begin(r)); + } + else if (! disjoint && s != closed) + { + // Open it by removing last point + geometry::traits::resize::apply(r, boost::size(r) - 1); + } + } +}; + +// Close/open exterior ring and all its interior rings +template +struct close_or_open_polygon +{ + typedef typename ring_type::type ring_type; + + static inline void apply(Polygon& poly) + { + close_or_open_ring::apply(exterior_ring(poly)); + + typename interior_return_type::type + rings = interior_rings(poly); + + for (typename detail::interior_iterator::type + it = boost::begin(rings); it != boost::end(rings); ++it) + { + close_or_open_ring::apply(*it); + } + } +}; + +}} // namespace detail::correct_closure +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template ::type> +struct correct_closure: not_implemented +{}; + +template +struct correct_closure + : detail::correct_closure::nop +{}; + +template +struct correct_closure + : detail::correct_closure::nop +{}; + +template +struct correct_closure + : detail::correct_closure::nop +{}; + + +template +struct correct_closure + : detail::correct_closure::nop +{}; + +template +struct correct_closure + : detail::correct_closure::close_or_open_ring +{}; + +template +struct correct_closure + : detail::correct_closure::close_or_open_polygon +{}; + + +template +struct correct_closure + : detail::correct_closure::nop +{}; + + +template +struct correct_closure + : detail::correct_closure::nop +{}; + + +template +struct correct_closure + : detail::multi_modify + < + Geometry, + detail::correct_closure::close_or_open_polygon + < + typename boost::range_value::type + > + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +namespace resolve_variant +{ + +template +struct correct_closure +{ + static inline void apply(Geometry& geometry) + { + concepts::check(); + dispatch::correct_closure::apply(geometry); + } +}; + +template +struct correct_closure > +{ + struct visitor: boost::static_visitor + { + template + void operator()(Geometry& geometry) const + { + correct_closure::apply(geometry); + } + }; + + static inline void + apply(boost::variant& geometry) + { + visitor vis; + boost::apply_visitor(vis, geometry); + } +}; + +} // namespace resolve_variant + + +/*! +\brief Closes or opens a geometry, according to its type +\details Corrects a geometry w.r.t. closure points to all rings which do not + have a closing point and are typed as they should have one, the first point + is appended. +\ingroup correct_closure +\tparam Geometry \tparam_geometry +\param geometry \param_geometry which will be corrected if necessary +*/ +template +inline void correct_closure(Geometry& geometry) +{ + resolve_variant::correct_closure::apply(geometry); +} + + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_CORRECT_CLOSURE_HPP diff --git a/include/boost/geometry/io/wkt/write.hpp b/include/boost/geometry/io/wkt/write.hpp index 344c0ef0f..998992087 100644 --- a/include/boost/geometry/io/wkt/write.hpp +++ b/include/boost/geometry/io/wkt/write.hpp @@ -254,7 +254,8 @@ struct wkt_box static inline void apply(std::basic_ostream& os, Box const& box, bool force_closure) { - // Convert to open cw ring, then stream + // Convert to open cw ring, then stream. In this case the box can be + // specified as open and close (by force_closure) typedef model::ring ring_type; ring_type ring; geometry::convert(box, ring); diff --git a/test/algorithms/Jamfile.v2 b/test/algorithms/Jamfile.v2 index 6797b4147..2806c1a10 100644 --- a/test/algorithms/Jamfile.v2 +++ b/test/algorithms/Jamfile.v2 @@ -28,6 +28,7 @@ test-suite boost-geometry-algorithms [ run convex_hull_multi.cpp : : : : algorithms_convex_hull_multi ] [ run correct.cpp : : : : algorithms_correct ] [ run correct_multi.cpp : : : : algorithms_correct_multi ] + [ run correct_closure.cpp : : : : algorithms_correct_closure ] [ run for_each.cpp : : : : algorithms_for_each ] [ run for_each_multi.cpp : : : : algorithms_for_each_multi ] [ run is_convex.cpp : : : : algorithms_is_convex ] diff --git a/test/algorithms/correct.cpp b/test/algorithms/correct.cpp index 59152601a..154117634 100644 --- a/test/algorithms/correct.cpp +++ b/test/algorithms/correct.cpp @@ -136,8 +136,9 @@ void test_ring_polygon() template void test_box() { - // Boxes - std::string proper_box = "POLYGON((0 0,0 2,2 2,2 0,0 0))"; + // Boxes. Reference is an open box (because in this test WKT is not + // explicitly closed) + std::string proper_box = "POLYGON((0 0,0 2,2 2,2 0))"; test_geometry >(proper_box, proper_box); test_geometry >("BOX(0 0,2 2)", proper_box); test_geometry >("BOX(2 2,0 0)", proper_box); diff --git a/test/algorithms/correct_closure.cpp b/test/algorithms/correct_closure.cpp new file mode 100644 index 000000000..6dec6970d --- /dev/null +++ b/test/algorithms/correct_closure.cpp @@ -0,0 +1,114 @@ +// Boost.Geometry +// Unit Test + +// Copyright (c) 2017 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 +#include +#include + +#include +#include +#include + +#include + + +template +void check_geometry(Geometry const& geometry, std::string const& expected) +{ + std::ostringstream out; + out << bg::wkt_manipulator(geometry, false); + BOOST_CHECK_EQUAL(out.str(), expected); +} + +template +void test_geometry(std::string const& wkt, std::string const& expected) +{ + Geometry geometry; + bg::read_wkt(wkt, geometry); + + // Test tye type + bg::correct_closure(geometry); + check_geometry(geometry, expected); + + // Test varianted type + boost::variant v(geometry); + bg::correct_closure(v); + check_geometry(v, expected); +} + +template +void test_all() +{ + typedef bg::model::ring cw_closed_ring_type; + typedef bg::model::ring cw_open_ring_type; + typedef bg::model::ring ccw_closed_ring_type; + typedef bg::model::ring ccw_open_ring_type; + + // Define clockwise and counter clockwise polygon + std::string cw_ring = "POLYGON((0 0,0 1,1 1,1 0,0 0))"; + std::string cw_open_ring = "POLYGON((0 0,0 1,1 1,1 0))"; + + std::string ccw_ring = "POLYGON((0 0,1 0,1 1,0 1,0 0))"; + std::string ccw_open_ring = "POLYGON((0 0,1 0,1 1,0 1))"; + + // Cases which should be closed or opened + test_geometry(cw_open_ring, cw_ring); + test_geometry(cw_ring, cw_open_ring); + test_geometry(ccw_open_ring, ccw_ring); + test_geometry(ccw_ring, ccw_open_ring); + + // Cases which are incorrect but should still be closed or opened + test_geometry(ccw_open_ring, ccw_ring); + test_geometry(cw_ring, cw_open_ring); + + // Cases where no action is necessary (even if order is incorrect) + test_geometry(cw_ring, cw_ring); + test_geometry(ccw_ring, ccw_ring); + test_geometry(cw_open_ring, cw_open_ring); + test_geometry(ccw_open_ring, ccw_open_ring); + test_geometry(cw_ring, cw_ring); + test_geometry(ccw_ring, ccw_ring); + test_geometry(cw_open_ring, cw_open_ring); + test_geometry(ccw_open_ring, ccw_open_ring); + + // Polygon cases + std::string cw_polygon = + "POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,2 1,2 2,1 2,1 1))"; + + std::string cw_open_polygon = + "POLYGON((0 0,0 4,4 4,4 0),(1 1,2 1,2 2,1 2))"; + + typedef bg::model::polygon cw_closed_polygon_type; + typedef bg::model::polygon cw_open_polygon_type; + + test_geometry(cw_open_polygon, cw_polygon); + test_geometry(cw_polygon, cw_open_polygon); + + test_geometry(cw_polygon, cw_polygon); + test_geometry(cw_open_polygon, cw_open_polygon); +} + + +int test_main(int, char* []) +{ + test_all >(); + test_all >(); + test_all >(); + + test_all > >(); + test_all > >(); + + return 0; +} diff --git a/test/algorithms/test_correct.hpp b/test/algorithms/test_correct.hpp index 2d2723cd5..1576b1602 100644 --- a/test/algorithms/test_correct.hpp +++ b/test/algorithms/test_correct.hpp @@ -27,12 +27,11 @@ #include #include - template void check_geometry(Geometry const& geometry, std::string const& expected) { std::ostringstream out; - out << bg::wkt(geometry); + out << bg::wkt_manipulator(geometry, false); BOOST_CHECK_EQUAL(out.str(), expected); } From 0f098948b2635e18925c935b7545c707e95ef4bd Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Thu, 19 Oct 2017 16:36:09 +0200 Subject: [PATCH 166/188] [wkt] Avoid any breaking change in wkt by using the old default for rings, and for boxes take by default always all 5 points --- include/boost/geometry/io/wkt/write.hpp | 40 ++++++++++++++++++------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/include/boost/geometry/io/wkt/write.hpp b/include/boost/geometry/io/wkt/write.hpp index 998992087..ff2b4d2a3 100644 --- a/include/boost/geometry/io/wkt/write.hpp +++ b/include/boost/geometry/io/wkt/write.hpp @@ -134,7 +134,7 @@ struct wkt_range { template static inline void apply(std::basic_ostream& os, - Range const& range, bool force_closure) + Range const& range, bool force_closure = ForceClosurePossible) { typedef typename boost::range_iterator::type iterator_type; @@ -254,14 +254,17 @@ struct wkt_box static inline void apply(std::basic_ostream& os, Box const& box, bool force_closure) { - // Convert to open cw ring, then stream. In this case the box can be - // specified as open and close (by force_closure) - typedef model::ring ring_type; - ring_type ring; - geometry::convert(box, ring); - os << "POLYGON("; - wkt_sequence::apply(os, ring, force_closure); - os << ")"; + // Convert to a clockwire ring, then stream. + // Never close it based on last point (box might be empty and + // that should result in POLYGON((0 0,0 0,0 0,0 0, ...)) ) + if (force_closure) + { + do_apply >(os, box); + } + else + { + do_apply >(os, box); + } } private: @@ -271,6 +274,18 @@ struct wkt_box // Only streaming of boxes with two dimensions is support, otherwise it is a polyhedron! //assert_dimension(); } + + template + static inline void do_apply(std::basic_ostream& os, + Box const& box) + { + RingType ring; + geometry::convert(box, ring); + os << "POLYGON("; + wkt_sequence::apply(os, ring); + os << ")"; + } + }; @@ -293,7 +308,7 @@ struct wkt_segment // In Boost.Geometry a segment is represented // in WKT-format like (for 2D): LINESTRING(x y,x y) os << "LINESTRING"; - wkt_sequence::apply(os, points, false); + wkt_sequence::apply(os, points); } private: @@ -482,7 +497,10 @@ class wkt_manipulator { public: - inline wkt_manipulator(Geometry const& g, bool force_closure = true) + // Boost.Geometry, by default, closes polygons explictly, but not rings + // NOTE: this might change in the future! + inline wkt_manipulator(Geometry const& g, + bool force_closure = ! boost::is_same::type, ring_tag>::value) : m_geometry(g) , m_force_closure(force_closure) {} From aa6778e83c2a0c0b53456d3bda0874142fdcdab8 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Thu, 19 Oct 2017 16:38:49 +0200 Subject: [PATCH 167/188] [test] dissolve algorithm cannot use bg::correct, because orientation is unknown and can be wrong. Orientation should not be reversed. Therefore correct_closure is split of from correct, and can (should) be used to close the geometries correctly. Only then some cases of dissolve can be fixed. bg::dissolve can itself not close the rings by design of the library (it would require drastic changes) --- extensions/test/algorithms/dissolve.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index 894e8dcb3..a4a73af4b 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -16,6 +16,7 @@ #include // To check results +#include #include #include #include @@ -451,6 +452,10 @@ void test_one(std::string const& caseid, std::string const& wkt, Geometry geometry; bg::read_wkt(wkt, geometry); + // If defined as closed, it should be closed. The algorithm itself + // cannot close it without making a copy. + bg::correct_closure(geometry); + test_dissolve(caseid, geometry, expected_area, expected_clip_count, expected_hole_count, expected_point_count, @@ -526,10 +531,9 @@ void test_all() TEST_DISSOLVE(dissolve_mail_2017_09_24_a, 0.5, 2, 0, 8); - // These do not have closed input, and output is not closed either. TODO. - TEST_DISSOLVE_IGNORE(dissolve_mail_2017_09_24_b, 16.0, 1, 0, 5); - TEST_DISSOLVE_IGNORE(dissolve_mail_2017_09_24_c, 0.4999, 1, 0, 4); - TEST_DISSOLVE_IGNORE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 4); + TEST_DISSOLVE(dissolve_mail_2017_09_24_b, 16.0, 1, 0, 6); + TEST_DISSOLVE(dissolve_mail_2017_09_24_c, 0.5, 2, 0, 8); + TEST_DISSOLVE_IGNORE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 5); TEST_DISSOLVE(dissolve_mail_2017_09_24_e, 0.001801138128, 5, 0, 69); TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.000361308800, 5, 0, 69); From 13a8d13cb66b97caf428f28dacfa5e6dd1c79074 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 20 Oct 2017 12:48:13 +0200 Subject: [PATCH 168/188] [dissolve] fix cases where turns were missed because 1) they were on adjacent segments and 2) vertical segments were not sectionalized properly for this purpose. --- extensions/test/algorithms/dissolve.cpp | 3 +- .../detail/has_self_intersections.hpp | 6 ++- .../detail/intersects/implementation.hpp | 3 +- .../algorithms/detail/is_simple/linear.hpp | 3 +- .../algorithms/detail/overlay/get_turns.hpp | 49 ++++++++++--------- .../detail/overlay/self_turn_points.hpp | 32 +++++++----- .../detail/touches/implementation.hpp | 6 ++- .../extensions/algorithms/dissolve.hpp | 2 +- 8 files changed, 63 insertions(+), 41 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index a4a73af4b..53953344e 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -533,8 +533,7 @@ void test_all() TEST_DISSOLVE(dissolve_mail_2017_09_24_b, 16.0, 1, 0, 6); TEST_DISSOLVE(dissolve_mail_2017_09_24_c, 0.5, 2, 0, 8); - TEST_DISSOLVE_IGNORE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 5); - + TEST_DISSOLVE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 5); TEST_DISSOLVE(dissolve_mail_2017_09_24_e, 0.001801138128, 5, 0, 69); TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.000361308800, 5, 0, 69); diff --git a/include/boost/geometry/algorithms/detail/has_self_intersections.hpp b/include/boost/geometry/algorithms/detail/has_self_intersections.hpp index c34bb217a..1289d946a 100644 --- a/include/boost/geometry/algorithms/detail/has_self_intersections.hpp +++ b/include/boost/geometry/algorithms/detail/has_self_intersections.hpp @@ -81,7 +81,11 @@ inline bool has_self_intersections(Geometry const& geometry, std::deque turns; detail::disjoint::disjoint_interrupt_policy policy; - detail::self_get_turn_points::self_turns(geometry, strategy, robust_policy, turns, policy); + detail::self_get_turn_points::self_turns + < + false, + detail::overlay::assign_null_policy + >(geometry, strategy, robust_policy, turns, policy, 0, false); #ifdef BOOST_GEOMETRY_DEBUG_HAS_SELF_INTERSECTIONS bool first = true; diff --git a/include/boost/geometry/algorithms/detail/intersects/implementation.hpp b/include/boost/geometry/algorithms/detail/intersects/implementation.hpp index 2379168e8..f9b5402d0 100644 --- a/include/boost/geometry/algorithms/detail/intersects/implementation.hpp +++ b/include/boost/geometry/algorithms/detail/intersects/implementation.hpp @@ -72,10 +72,11 @@ struct self_intersects rescale_policy_type robust_policy; detail::disjoint::disjoint_interrupt_policy policy; + // TODO: skip_adjacent should be set to false detail::self_get_turn_points::get_turns < false, turn_policy - >::apply(geometry, strategy, robust_policy, turns, policy, 0); + >::apply(geometry, strategy, robust_policy, turns, policy, 0, true); return policy.has_intersections; } }; diff --git a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp index 5acf56c5b..e36049a96 100644 --- a/include/boost/geometry/algorithms/detail/is_simple/linear.hpp +++ b/include/boost/geometry/algorithms/detail/is_simple/linear.hpp @@ -217,6 +217,7 @@ inline bool has_self_intersections(Linear const& linear, Strategy const& strateg is_acceptable_turn > interrupt_policy(predicate); + // TODO: skip_adjacent should be set to false detail::self_get_turn_points::get_turns < false, turn_policy @@ -224,7 +225,7 @@ inline bool has_self_intersections(Linear const& linear, Strategy const& strateg strategy, detail::no_rescale_policy(), turns, - interrupt_policy, 0); + interrupt_policy, 0, true); detail::is_valid::debug_print_turns(turns.begin(), turns.end()); debug_print_boundary_points(linear); diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp index f88dfe842..fd1e49ca2 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -144,7 +144,7 @@ class get_turns_in_sections template - static inline bool neighbouring(Section const& section, + static inline bool adjacent(Section const& section, signed_size_type index1, signed_size_type index2) { // About n-2: @@ -152,7 +152,7 @@ class get_turns_in_sections // -> 0-3 are adjacent, don't check on intersections) // Also tested for open polygons, and/or duplicates // About first condition: will be optimized by compiler (static) - // It checks if it is areal (box,ring,(multi)polygon + // It checks if it is areal (box, ring, (multi)polygon) signed_size_type const n = static_cast(section.range_count); boost::ignore_unused_variable_warning(n); @@ -180,7 +180,7 @@ public : static inline bool apply( int source_id1, Geometry1 const& geometry1, Section1 const& sec1, int source_id2, Geometry2 const& geometry2, Section2 const& sec2, - bool skip_larger, + bool skip_larger, bool skip_adjacent, IntersectionStrategy const& intersection_strategy, RobustPolicy const& robust_policy, Turns& turns, @@ -214,11 +214,6 @@ public : signed_size_type index1 = sec1.begin_index; signed_size_type ndi1 = sec1.non_duplicate_index; - bool const same_source = - source_id1 == source_id2 - && sec1.ring_id.multi_index == sec2.ring_id.multi_index - && sec1.ring_id.ring_index == sec2.ring_id.ring_index; - range1_iterator prev1, it1, end1; get_start_point_iterator(sec1, view1, prev1, it1, end1, @@ -254,22 +249,32 @@ public : it2 != end2 && ! detail::section::exceeding<0>(dir2, *prev2, sec2.bounding_box, sec1.bounding_box, robust_policy); ++prev2, ++it2, ++index2, ++next2, ++ndi2) { - bool skip = same_source; - if (skip) + bool skip = false; + + if (source_id1 == source_id2 + && sec1.ring_id.multi_index == sec2.ring_id.multi_index + && sec1.ring_id.ring_index == sec2.ring_id.ring_index) { - // If sources are the same (possibly self-intersecting): - // skip if it is a neighbouring segment. - // (including first-last segment - // and two segments with one or more degenerate/duplicate - // (zero-length) segments in between) + // Sources and rings are the same - // Also skip if index1 < index2 to avoid getting all - // intersections twice (only do this on same source!) + if (skip_larger && index1 >= index2) + { + // Skip to avoid getting all intersections twice + skip = true; + } + else if (skip_adjacent) + { + // In some cases (dissolve, has_self_intersections) + // neighbouring segments should be checked + // (for example to detect spikes properly) - skip = (skip_larger && index1 >= index2) - || ndi2 == ndi1 + 1 - || neighbouring(sec1, index1, index2) - ; + // skip if it is a neighbouring segment. + // (including, for areas, first-last segment + // and two segments with one or more degenerate/duplicate + // (zero-length) segments in between) + skip = ndi2 == ndi1 + 1 + || adjacent(sec1, index1, index2); + } } if (! skip) @@ -431,7 +436,7 @@ struct section_visitor TurnPolicy >::apply(m_source_id1, m_geometry1, sec1, m_source_id2, m_geometry2, sec2, - false, + false, false, m_intersection_strategy, m_rescale_policy, m_turns, m_interrupt_policy); diff --git a/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp b/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp index 5e9d8efa8..a00606b08 100644 --- a/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -78,19 +78,22 @@ struct self_section_visitor Turns& m_turns; InterruptPolicy& m_interrupt_policy; std::size_t m_source_index; + bool m_skip_adjacent; inline self_section_visitor(Geometry const& g, IntersectionStrategy const& is, RobustPolicy const& rp, Turns& turns, InterruptPolicy& ip, - std::size_t source_index) + std::size_t source_index, + bool skip_adjacent) : m_geometry(g) , m_intersection_strategy(is) , m_rescale_policy(rp) , m_turns(turns) , m_interrupt_policy(ip) , m_source_index(source_index) + , m_skip_adjacent(skip_adjacent) {} template @@ -109,7 +112,7 @@ struct self_section_visitor TurnPolicy >::apply(m_source_index, m_geometry, sec1, m_source_index, m_geometry, sec2, - false, + false, m_skip_adjacent, m_intersection_strategy, m_rescale_policy, m_turns, m_interrupt_policy); @@ -132,7 +135,7 @@ struct get_turns RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, - std::size_t source_index) + std::size_t source_index, bool skip_adjacent) { typedef model::box < @@ -143,9 +146,11 @@ struct get_turns >::type > box_type; - typedef geometry::sections sections_type; + // sectionalize in two dimensions to detect + // all potential spikes correctly + typedef geometry::sections sections_type; - typedef boost::mpl::vector_c dimensions; + typedef boost::mpl::vector_c dimensions; sections_type sec; geometry::sectionalize(geometry, robust_policy, sec, @@ -155,7 +160,7 @@ struct get_turns < Reverse, Geometry, Turns, TurnPolicy, IntersectionStrategy, RobustPolicy, InterruptPolicy - > visitor(geometry, intersection_strategy, robust_policy, turns, interrupt_policy, source_index); + > visitor(geometry, intersection_strategy, robust_policy, turns, interrupt_policy, source_index, skip_adjacent); // false if interrupted geometry::partition @@ -224,7 +229,8 @@ struct self_get_turn_points RobustPolicy const& , Turns& , InterruptPolicy& , - std::size_t) + std::size_t /*source_index*/, + bool /*skip_adjacent*/) { return true; } @@ -286,7 +292,8 @@ inline void self_turns(Geometry const& geometry, RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, - std::size_t source_index = 0) + std::size_t source_index = 0, + bool skip_adjacent = false) { concepts::check(); @@ -298,7 +305,8 @@ inline void self_turns(Geometry const& geometry, typename tag::type, Geometry, turn_policy - >::apply(geometry, strategy, robust_policy, turns, interrupt_policy, source_index); + >::apply(geometry, strategy, robust_policy, turns, interrupt_policy, + source_index, skip_adjacent); } }} // namespace detail::self_get_turn_points @@ -331,7 +339,8 @@ inline void self_turns(Geometry const& geometry, RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, - std::size_t source_index = 0) + std::size_t source_index = 0, + bool skip_adjacent = false) { concepts::check(); @@ -344,7 +353,8 @@ inline void self_turns(Geometry const& geometry, < reverse, AssignPolicy - >(geometry, strategy, robust_policy, turns, interrupt_policy, source_index); + >(geometry, strategy, robust_policy, turns, interrupt_policy, + source_index, skip_adjacent); } diff --git a/include/boost/geometry/algorithms/detail/touches/implementation.hpp b/include/boost/geometry/algorithms/detail/touches/implementation.hpp index 94f1fba58..0cdb8ad1d 100644 --- a/include/boost/geometry/algorithms/detail/touches/implementation.hpp +++ b/include/boost/geometry/algorithms/detail/touches/implementation.hpp @@ -413,7 +413,8 @@ struct touches #endif // DOXYGEN_NO_DISPATCH -namespace resolve_variant { +namespace resolve_variant +{ template struct self_touches @@ -443,10 +444,11 @@ struct self_touches detail::touches::areal_interrupt_policy policy; strategy_type strategy; rescale_policy_type robust_policy; + // TODO: skip_adjacent should be set to false detail::self_get_turn_points::get_turns < false, policy_type - >::apply(geometry, strategy, robust_policy, turns, policy, 0); + >::apply(geometry, strategy, robust_policy, turns, policy, 0, true); return policy.result(); } diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index 6fef44a64..06ad04e6f 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -160,7 +160,7 @@ struct dissolve_ring_or_polygon geometry::self_turns < detail::overlay::assign_null_policy - >(geometry, strategy, rescale_policy, turns, policy); + >(geometry, strategy, rescale_policy, turns, policy, 0, false); visitor.visit_turns(1, turns); From 4382d85fb4bbca0cfc399b3b3b820f8f368521c1 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 20 Oct 2017 12:48:54 +0200 Subject: [PATCH 169/188] [dissolve] add testcase which is derived from (d) but has another spike, or is horizontal instead of vertical --- extensions/test/algorithms/dissolve.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index 53953344e..f6a4f8cc3 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -120,6 +120,10 @@ namespace // Idem but moving closing point eastwards to view self-intersections better std::string const dissolve_mail_2017_09_24_f = "POLYGON((25.35475410461420 -1.20600892066955, 25.36818313598630 -1.20732820034027, 25.36953926086420 -1.20868659019470, 25.37180328369140 -1.20959210395812, 25.37451934814450 -1.21049761772155, 25.37632942199700 -1.21185600757598, 25.37904357910150 -1.21276152133941, 25.38085556030270 -1.21321427822113, 25.38221168518060 -1.21366703510284, 25.38311767578120 -1.21411991119384, 25.38447570800780 -1.21411991119384, 25.38447570800780 -1.21185600757598, 25.38492774963370 -1.21140325069427, 25.38492774963370 -1.21095049381256, 25.38945388793940 -1.21095049381256, 25.38990592956540 -1.21049761772155, 25.39035987854000 -1.21004486083984, 25.39081192016600 -1.21004486083984, 25.39081192016600 -1.20959210395812, 25.39126396179190 -1.20913934707641, 25.39171600341790 -1.20913934707641, 25.39262199401850 -1.20868659019470, 25.39262199401850 -1.20823383331298, 25.39352798461910 -1.20823383331298, 25.39398002624510 -1.20778107643127, 25.39443206787100 -1.20778107643127, 25.39443206787100 -1.20732820034027, 25.39488410949700 -1.20687544345855, 25.39443206787100 -1.20642268657684, 25.39420700073240 -1.20596992969512, 25.39386749267570 -1.20551717281341, 25.39352798461910 -1.20551717281341, 25.39352798461910 -1.20506441593170, 25.39386749267570 -1.20551717281341, 25.39398002624510 -1.20551717281341, 25.39420700073240 -1.20596992969512, 25.39488410949700 -1.20687544345855, 25.39533805847160 -1.20778107643127, 25.39579010009760 -1.20823383331298, 25.39579010009760 -1.20868659019470, 25.39624214172360 -1.20913934707641, 25.39669609069820 -1.20959210395812, 25.39669609069820 -1.21049761772155, 25.39714813232420 -1.21095049381256, 25.39760017395010 -1.21185600757598, 25.39805221557610 -1.21321427822113, 25.39850616455070 -1.21411991119384, 25.39895820617670 -1.21547818183898, 25.39895820617670 -1.21638369560241, 25.39986419677730 -1.21819484233856, 25.40031623840330 -1.21955311298370, 25.40076828002920 -1.22045862674713, 25.40076828002920 -1.22181701660156, 25.40122032165520 -1.22226977348327, 25.40122032165520 -1.22408080101013, 25.40212631225580 -1.22498643398284, 25.40212631225580 -1.22634470462799, 25.40257835388180 -1.22725021839141, 25.40257835388180 -1.22770309448242, 25.40303230285640 -1.22860860824584, 25.40303230285640 -1.23041963577270, 25.40348434448240 -1.23041963577270, 25.35475410461420 -1.20600892066955))"; + // As d but with other spike + std::string const dissolve_mail_2017_09_24_g = "POLYGON((1 0, 0 -1, 0 1, 0 0,1 0))"; + std::string const dissolve_mail_2017_09_24_h = "POLYGON((0 0, 0 1, 2 1, 1 1,0 0))"; + std::string const dissolve_ticket17 = "POLYGON ((-122.28139163 37.37319149,-122.28100699 37.37273669,-122.28002186 37.37303123,-122.27979681 37.37290072,-122.28007349 37.37240493,-122.27977334 37.37220360,-122.27819720 37.37288580,-122.27714184 37.37275161,-122.27678628 37.37253167,-122.27766437 37.37180973,-122.27804382 37.37121453,-122.27687664 37.37101354,-122.27645829 37.37203386,-122.27604423 37.37249110,-122.27632234 37.37343339,-122.27760980 37.37391082,-122.27812478 37.37800320,-122.26117222 37.39121007,-122.25572289 37.39566631,-122.25547269 37.39564971,-122.25366304 37.39552993,-122.24919976 37.39580268,-122.24417933 37.39366907,-122.24051443 37.39094143,-122.23246277 37.38100418,-122.23606766 37.38141338,-122.24001587 37.37738940,-122.23666848 37.37609347,-122.23057450 37.37882170,-122.22679803 37.37807143,-122.22525727 37.37448817,-122.22523229 37.37443000,-122.23083199 37.37609347,-122.23033486 37.37777891,-122.23169030 37.37732117,-122.23229178 37.37709687,-122.23237761 37.37631249,-122.23297776 37.37438834,-122.23872850 37.37165986,-122.24044511 37.36934068,-122.24671067 37.36865847,-122.24825570 37.36981819,-122.25151719 37.36947713,-122.25357721 37.36756706,-122.26001451 37.36579354,-122.25615213 37.36545239,-122.25486458 37.36245083,-122.25357721 37.36108651,-122.25194642 37.36013139,-122.24885652 37.35958557,-122.24911401 37.35849399,-122.25357721 37.35808470,-122.25675286 37.35897159,-122.25855539 37.35753887,-122.26181687 37.35828939,-122.26713837 37.35897159,-122.26782510 37.36108651,-122.26662339 37.36456559,-122.27288911 37.36722601,-122.27366159 37.36531602,-122.27168740 37.36470213,-122.27391900 37.36374701,-122.27074326 37.36245083,-122.27134408 37.35951742,-122.27426240 37.36135926,-122.27709482 37.36115474,-122.27966974 37.36231438,-122.27958391 37.36463382,-122.27572152 37.36463382,-122.27563569 37.36524779,-122.27700899 37.36593000,-122.27709482 37.36763529,-122.27554978 37.36838573,-122.27667254 37.36931478,-122.27677932 37.36932073,-122.27769362 37.36853987,-122.27942490 37.36830803,-122.28178776 37.36677917,-122.28509559 37.36443500,-122.28845129 37.36413744,-122.29194403 37.36695946,-122.29382577 37.36726817,-122.29600414 37.36898512,-122.29733083 37.36995398,-122.29593239 37.37141436,-122.29416649 37.37075898,-122.29325026 37.37108436,-122.29652910 37.37311697,-122.29584237 37.37374461,-122.29537583 37.37573372,-122.29487677 37.37752502,-122.30923212 37.37593011,-122.31122484 37.38230086,-122.31467994 37.38092472,-122.31715663 37.38252181,-122.32307970 37.38166978,-122.31985618 37.37667694,-122.32210304 37.37580220,-122.32581446 37.37589532,-122.32401730 37.37331839,-122.32960417 37.37189020,-122.33465527 37.37331906,-122.33425328 37.37623680,-122.33620676 37.37726132,-122.33397986 37.37822382,-122.33358918 37.38036590,-122.33202637 37.37986918,-122.33147954 37.38101784,-122.33394080 37.38198017,-122.33545239 37.38587943,-122.33478058 37.38785697,-122.33386050 37.38723721,-122.33350041 37.38571137,-122.33122003 37.38548891,-122.33140008 37.38650606,-122.33366042 37.38817490,-122.33244019 37.39157602,-122.33298157 37.39419201,-122.33164013 37.39477028,-122.33202017 37.39518351,-122.33358038 37.39499282,-122.33376050 37.39597811,-122.33550067 37.39734478,-122.33556069 37.39481797,-122.33344040 37.39292676,-122.33638094 37.38892189,-122.34240644 37.38852719,-122.34906293 37.38726898,-122.35072321 37.39338769,-122.34910291 37.39445252,-122.34796272 37.39410291,-122.34449043 37.39640534,-122.34500223 37.39729709,-122.34936291 37.39670910,-122.35098322 37.39531066,-122.35364623 37.39554510,-122.35434369 37.39612111,-122.35798429 37.39600988,-122.35768430 37.39478621,-122.36334519 37.39206871,-122.36604726 37.39203267,-122.36778592 37.39335592,-122.36518870 37.40022011,-122.36554552 37.40247752,-122.36370519 37.40331974,-122.36270506 37.40530591,-122.36320512 37.40670418,-122.36149849 37.40851392,-122.36730580 37.41054938,-122.37263720 37.41378932,-122.37161871 37.42076600,-122.36566153 37.42006292,-122.36520547 37.42742106,-122.37165953 37.43661157,-122.35943972 37.44459022,-122.35356359 37.44600810,-122.33792254 37.45796329,-122.35228518 37.47478091,-122.35127080 37.48181199,-122.34867342 37.48487322,-122.34359717 37.48801082,-122.33388431 37.48677650,-122.33142321 37.48429747,-122.32929580 37.48473149,-122.32609609 37.48291144,-122.32344850 37.48228229,-122.31924364 37.48410234,-122.31677299 37.48114051,-122.31431751 37.47848973,-122.31259201 37.47682190,-122.31515972 37.47568196,-122.31691389 37.47360309,-122.31292494 37.46960081,-122.31130153 37.46937743,-122.30889894 37.47124987,-122.30612839 37.47011613,-122.30149630 37.46568378,-122.30064277 37.46363784,-122.29283821 37.45922376,-122.28630141 37.45415497,-122.28883099 37.44629920,-122.28316717 37.44197138,-122.27554148 37.42297597,-122.25597410 37.40553692,-122.25196579 37.40129593,-122.25012043 37.40049143,-122.24823207 37.39897758,-122.24754551 37.39740941,-122.24778582 37.39621607,-122.24934787 37.39599102,-122.25005170 37.39871849,-122.25222328 37.39863668,-122.25342491 37.39737529,-122.25520162 37.39667289,-122.25528737 37.39522726,-122.27747460 37.37809616,-122.27977493 37.37858717,-122.28157729 37.37920106,-122.28322534 37.37952846,-122.28416939 37.38092656,-122.28621223 37.37984219,-122.28638389 37.37613857,-122.28382607 37.37843722,-122.27930278 37.37718220,-122.28196361 37.37652740,-122.28295058 37.37568167,-122.28216101 37.37523148,-122.28114822 37.37543608,-122.27934569 37.37528613,-122.27996369 37.37448121,-122.28104521 37.37454944,-122.28185197 37.37422883,-122.28290767 37.37474038,-122.28376597 37.37467224,-122.28428104 37.37399012,-122.28402346 37.37338989,-122.28610922 37.37364914,-122.28651264 37.37327388,-122.28672722 37.37207343,-122.28628398 37.37205448,-122.28574460 37.37166682,-122.28479711 37.37200981,-122.28327731 37.37137228,-122.28285511 37.37100700,-122.28279409 37.37125669,-122.28315527 37.37173756,-122.28321872 37.37220569,-122.28187007 37.37231918,-122.28193109 37.37294908,-122.28139163 37.37319149))"; std::string const dissolve_reallife = "POLYGON((170718 605997,170718 605997,170776 606016,170773 606015,170786 606020,170778 606016,170787 606021,170781 606017,170795 606028,170795 606028,170829 606055,170939 606140,170933 605968,170933 605968,170932 605908,170929 605834,170920 605866,170961 605803,170739 605684,170699 605749,170691 605766,170693 605762,170686 605775,170688 605771,170673 605794,170676 605790,170668 605800,170672 605796,170651 605818,170653 605816,170639 605829,170568 605899,170662 605943,170633 605875,170603 605961,170718 605997))"; @@ -536,6 +540,8 @@ void test_all() TEST_DISSOLVE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 5); TEST_DISSOLVE(dissolve_mail_2017_09_24_e, 0.001801138128, 5, 0, 69); TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.000361308800, 5, 0, 69); + TEST_DISSOLVE(dissolve_mail_2017_09_24_g, 0.5, 1, 0, 4); + TEST_DISSOLVE(dissolve_mail_2017_09_24_h, 0.5, 1, 0, 4); #ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS TEST_DISSOLVE(dissolve_9, 25.6158412, 1, 0, 11); From b39b9a319b77226ba44e42f73673f4be46154483 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 20 Oct 2017 15:11:19 +0200 Subject: [PATCH 170/188] [dissolve] reject negative child rings of negative parents (the parents are reversed later), the same way as positive child rings of positive parents are removed. This fixes the hexagram case and also dissolve_10 (both ccw) --- extensions/test/algorithms/dissolve.cpp | 18 +++----- .../buffer/buffered_piece_collection.hpp | 5 ++- .../detail/overlay/assign_parents.hpp | 43 ++++++++++++------- .../extensions/algorithms/dissolve.hpp | 5 ++- 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index f6a4f8cc3..ab370429c 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -524,6 +524,11 @@ void test_all() TEST_DISSOLVE(dissolve_7, 50.48056402439, 1, 0, 7); TEST_DISSOLVE(dissolve_8, 25.6158412, 1, 0, 11); + // CCW polygons should turn CW after dissolve + TEST_DISSOLVE(dissolve_9, 25.6158412, 1, 0, 11); + TEST_DISSOLVE(dissolve_10, 60.0, 1, 0, 7); + TEST_DISSOLVE(dissolve_11, 60.0, 1, 0, 7); + TEST_DISSOLVE(dissolve_d1, 8.0, 1, 0, 4); TEST_DISSOLVE(dissolve_d2, 16.0, 1, 0, 10); @@ -537,23 +542,12 @@ void test_all() TEST_DISSOLVE(dissolve_mail_2017_09_24_b, 16.0, 1, 0, 6); TEST_DISSOLVE(dissolve_mail_2017_09_24_c, 0.5, 2, 0, 8); - TEST_DISSOLVE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 5); + TEST_DISSOLVE(dissolve_mail_2017_09_24_d, 0.5, 1, 0, 4); TEST_DISSOLVE(dissolve_mail_2017_09_24_e, 0.001801138128, 5, 0, 69); TEST_DISSOLVE(dissolve_mail_2017_09_24_f, 0.000361308800, 5, 0, 69); TEST_DISSOLVE(dissolve_mail_2017_09_24_g, 0.5, 1, 0, 4); TEST_DISSOLVE(dissolve_mail_2017_09_24_h, 0.5, 1, 0, 4); -#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS - TEST_DISSOLVE(dissolve_9, 25.6158412, 1, 0, 11); -#endif - - // CCW polygons should turn CW after dissolve - TEST_DISSOLVE(dissolve_10, 60.0, 1, 0, 7); -#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS - // TODO: should be the same, 7, 60. The polygon is dissolved (splitted) but the extra polygon is still on top of the other - TEST_DISSOLVE(dissolve_11, 60.0, 1, 0, 7); -#endif - TEST_DISSOLVE(dissolve_ticket10713, 0.157052766, 2, 0, 8); TEST_MULTI(multi_three_triangles, 42.614078674948232, 1, 1, 13); diff --git a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index 122b82cbe..f78f11f19 100644 --- a/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/include/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -1445,7 +1445,10 @@ struct buffered_piece_collection } } - detail::overlay::assign_parents(offsetted_rings, traversed_rings, selected, m_intersection_strategy, true); + // Assign parents, checking orientation but NOT discarding double + // negative rings (negative child with negative parent) + detail::overlay::assign_parents(offsetted_rings, traversed_rings, + selected, m_intersection_strategy, true, false); return detail::overlay::add_rings(selected, offsetted_rings, traversed_rings, out); } diff --git a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp index 7cc91f726..c8ce65100 100644 --- a/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -224,7 +224,8 @@ inline void assign_parents(Geometry1 const& geometry1, RingCollection const& collection, RingMap& ring_map, Strategy const& strategy, - bool check_for_orientation = false) + bool check_for_orientation = false, + bool discard_double_negative = false) { typedef typename geometry::tag::type tag1; typedef typename geometry::tag::type tag2; @@ -334,28 +335,38 @@ inline void assign_parents(Geometry1 const& geometry1, for (map_iterator_type it = boost::begin(ring_map); it != boost::end(ring_map); ++it) { - if (geometry::math::equals(it->second.get_area(), 0)) + ring_info_type& info = it->second; + if (geometry::math::equals(info.get_area(), 0)) { - it->second.discarded = true; + info.discarded = true; } - else if (it->second.parent.source_index >= 0 - && math::larger(it->second.get_area(), 0)) + else if (info.parent.source_index >= 0) { - const ring_info_type& parent = ring_map[it->second.parent]; + const ring_info_type& parent = ring_map[info.parent]; + bool const pos = math::larger(info.get_area(), 0); + bool const parent_pos = math::larger(parent.area, 0); - if (math::larger(parent.area, 0)) + bool const double_neg = discard_double_negative && ! pos && ! parent_pos; + + if ((pos && parent_pos) || double_neg) { // Discard positive inner ring with positive parent - it->second.discarded = true; + // Also, for some cases (dissolve), negative inner ring + // with negative parent shouild be discarded + info.discarded = true; + } + + if (pos || info.discarded) + { + // Remove parent ID from any positive or discarded inner rings + info.parent.source_index = -1; } - // Remove parent ID from any positive inner ring - it->second.parent.source_index = -1; } - else if (it->second.parent.source_index < 0 - && math::smaller(it->second.get_area(), 0)) + else if (info.parent.source_index < 0 + && math::smaller(info.get_area(), 0)) { // Reverse negative ring without parent - it->second.reversed = true; + info.reversed = true; } } } @@ -384,13 +395,15 @@ inline void assign_parents(Geometry const& geometry, RingCollection const& collection, RingMap& ring_map, Strategy const& strategy, - bool check_for_orientation) + bool check_for_orientation, + bool discard_double_negative) { // Call it with an empty geometry as second geometry (source_id == 1) // (ring_map should be empty for source_id==1) Geometry empty; - assign_parents(geometry, empty, collection, ring_map, strategy, check_for_orientation); + assign_parents(geometry, empty, collection, ring_map, strategy, + check_for_orientation, discard_double_negative); } diff --git a/include/boost/geometry/extensions/algorithms/dissolve.hpp b/include/boost/geometry/extensions/algorithms/dissolve.hpp index 06ad04e6f..26621982e 100644 --- a/include/boost/geometry/extensions/algorithms/dissolve.hpp +++ b/include/boost/geometry/extensions/algorithms/dissolve.hpp @@ -260,7 +260,10 @@ struct dissolve_ring_or_polygon } } - detail::overlay::assign_parents(geometry, rings, selected, strategy, true); + // Assign parents, checking orientation and discarding negative + // children with negative parents + detail::overlay::assign_parents(geometry, rings, selected, + strategy, true, true); return detail::overlay::add_rings(selected, geometry, rings, out); } }; From 8484bcc92337eec26488033046e4be0d1c17790b Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 20 Oct 2017 15:14:02 +0200 Subject: [PATCH 171/188] [test] Move and enable acute/obtuse heptagrams which are fixed now --- extensions/test/algorithms/dissolve.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index ab370429c..8ad52f005 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -76,6 +76,12 @@ namespace // CCW, one keyhole std::string const dissolve_11 = "POLYGON((2 8,2 4,4 4,4 6,0 6,0 0,8 0,8 8,2 8))"; + // More pentagrams + // Source: http://upload.wikimedia.org/wikipedia/commons/8/83/Acute_heptagram.svg + std::string const dissolve_12 = "POLYGON((409 5,229 793.631528,733.348792 161.198146,4.543671 512.172194,813.456329 512.172194,84.651208 161.198146,589 793.631528))"; + // Source: http://upload.wikimedia.org/wikipedia/commons/a/a7/Obtuse_heptagram.svg + std::string const dissolve_13 = "POLYGON((409 5,813.456329 512.172194,229 793.631528,84.651208 161.198146,733.348792 161.198146,589 793.631528,4.543671 512.172194))"; + // Non intersection, but with duplicate std::string const dissolve_d1 = "POLYGON((0 0,0 4,4 0,4 0,0 0))"; @@ -529,6 +535,10 @@ void test_all() TEST_DISSOLVE(dissolve_10, 60.0, 1, 0, 7); TEST_DISSOLVE(dissolve_11, 60.0, 1, 0, 7); + // More pentagrams + TEST_DISSOLVE(dissolve_12, 186556.84077318, 1, 0, 15); + TEST_DISSOLVE(dissolve_13, 361733.91651, 1, 0, 15); + TEST_DISSOLVE(dissolve_d1, 8.0, 1, 0, 4); TEST_DISSOLVE(dissolve_d2, 16.0, 1, 0, 10); @@ -580,15 +590,7 @@ void test_all() test_one("mail_denis_1", "POLYGON((55 10, 141 237, 249 23, 21 171, 252 169, 24 89, 266 73, 55 10))", 0, 7, 50.48056402439); - // Source: http://upload.wikimedia.org/wikipedia/commons/8/83/Acute_heptagram.svg - test_one("acute_heptagram", - "POLYGON((409 5,229 793.631528,733.348792 161.198146,4.543671 512.172194,813.456329 512.172194,84.651208 161.198146,589 793.631528))", - 0, 11, 25.6158412); - // Source: http://upload.wikimedia.org/wikipedia/commons/a/a7/Obtuse_heptagram.svg - test_one("obtuse_heptagram", - "POLYGON((409 5,813.456329 512.172194,229 793.631528,84.651208 161.198146,733.348792 161.198146,589 793.631528,4.543671 512.172194))", - 0, 11, 25.6158412); */ } From 36e70aa1ce71202dd2562ea6f8a448754e82a9bb Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 20 Oct 2017 15:14:57 +0200 Subject: [PATCH 172/188] [dissolve] move/partly enable other testcases --- extensions/test/algorithms/dissolve.cpp | 31 +++++++++++-------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index 8ad52f005..b6d494a17 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -114,12 +114,19 @@ namespace std::string const ggl_list_20110307_javier_01_a = "MULTIPOLYGON(((560 -400, 600 -400, 600 -440, 560 -440, 560 -400)), ((480 -400, 520 -400, 520 -440, 480 -440, 480 -400)), ((600 -320, 640 -320, 640 -360, 600 -360, 600 -320)), ((520 -400, 560 -400, 560 -440, 520 -440, 520 -400)))"; std::string const ggl_list_20110307_javier_01_b = "POLYGON((0 0, 2000 0, 2000 -2000, 0 -2000, 0 0), (560 -400, 560 -440, 600 -440, 600 -400, 560 -400), (480 -400, 480 -440, 520 -440, 520 -400, 480 -400), (600 -320, 600 -360, 640 -360, 640 -320, 600 -320), (520 -400, 520 -440, 560 -440, 560 -400, 520 -400))"; + std::string const ggl_list_denis = "POLYGON((55 10, 141 237, 249 23, 21 171, 252 169, 24 89, 266 73, 55 10))"; + // Testcases sent by Johan Doré at September 24, 2017: std::string const dissolve_mail_2017_09_24_a = "POLYGON((0 1, 1 0, 1 1, 0 0, 0 1))"; // two triangles std::string const dissolve_mail_2017_09_24_b = "POLYGON((1 0, 0 0, 0 4, 4 4, 4 0))"; // input is not closed std::string const dissolve_mail_2017_09_24_c = "POLYGON((0 0, 1 0, 0 -1, 0.0001 1))"; // spike and not closed std::string const dissolve_mail_2017_09_24_d = "POLYGON((0 0, 1 0, 0 -1, 0 1))"; // spike and not closed + // Testcases sent by Artem Pavlenko via gitter + // https://gitter.im/boostorg/geometry?at=58ef46408e4b63533dc49b48 + std::string const gitter_2013_04_a = "POLYGON((36.9121 2.03883,26.2052 54.353,60.0781 64.2202,96.2171 55.9826,71.1506 39.8365,5.72552 94.1523,4.06819 13.9054,59.7155 44.5877,60.9243 16.4597,48.8696 93.039,36.9121 2.03883))"; + std::string const gitter_2013_04_b = "POLYGON((337 176,602 377,294 372,581 166,453 449,337 176))"; + // Large polygon with self-intersections std::string const dissolve_mail_2017_09_24_e = "POLYGON((25.21475410461420 -1.19600892066955, 25.36818313598630 -1.20732820034027, 25.36953926086420 -1.20868659019470, 25.37180328369140 -1.20959210395812, 25.37451934814450 -1.21049761772155, 25.37632942199700 -1.21185600757598, 25.37904357910150 -1.21276152133941, 25.38085556030270 -1.21321427822113, 25.38221168518060 -1.21366703510284, 25.38311767578120 -1.21411991119384, 25.38447570800780 -1.21411991119384, 25.38447570800780 -1.21185600757598, 25.38492774963370 -1.21140325069427, 25.38492774963370 -1.21095049381256, 25.38945388793940 -1.21095049381256, 25.38990592956540 -1.21049761772155, 25.39035987854000 -1.21004486083984, 25.39081192016600 -1.21004486083984, 25.39081192016600 -1.20959210395812, 25.39126396179190 -1.20913934707641, 25.39171600341790 -1.20913934707641, 25.39262199401850 -1.20868659019470, 25.39262199401850 -1.20823383331298, 25.39352798461910 -1.20823383331298, 25.39398002624510 -1.20778107643127, 25.39443206787100 -1.20778107643127, 25.39443206787100 -1.20732820034027, 25.39488410949700 -1.20687544345855, 25.39443206787100 -1.20642268657684, 25.39420700073240 -1.20596992969512, 25.39386749267570 -1.20551717281341, 25.39352798461910 -1.20551717281341, 25.39352798461910 -1.20506441593170, 25.39386749267570 -1.20551717281341, 25.39398002624510 -1.20551717281341, 25.39420700073240 -1.20596992969512, 25.39488410949700 -1.20687544345855, 25.39533805847160 -1.20778107643127, 25.39579010009760 -1.20823383331298, 25.39579010009760 -1.20868659019470, 25.39624214172360 -1.20913934707641, 25.39669609069820 -1.20959210395812, 25.39669609069820 -1.21049761772155, 25.39714813232420 -1.21095049381256, 25.39760017395010 -1.21185600757598, 25.39805221557610 -1.21321427822113, 25.39850616455070 -1.21411991119384, 25.39895820617670 -1.21547818183898, 25.39895820617670 -1.21638369560241, 25.39986419677730 -1.21819484233856, 25.40031623840330 -1.21955311298370, 25.40076828002920 -1.22045862674713, 25.40076828002920 -1.22181701660156, 25.40122032165520 -1.22226977348327, 25.40122032165520 -1.22408080101013, 25.40212631225580 -1.22498643398284, 25.40212631225580 -1.22634470462799, 25.40257835388180 -1.22725021839141, 25.40257835388180 -1.22770309448242, 25.40303230285640 -1.22860860824584, 25.40303230285640 -1.23041963577270, 25.40348434448240 -1.23041963577270, 25.21475410461420 -1.19600892066955))"; @@ -573,26 +580,16 @@ void test_all() TEST_DISSOLVE(dissolve_reallife, 91756.916526794434, 1, 0, 25); #ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS - // Reported by Artem Pavlenko at gitter - until now these did not work yet - // https://gitter.im/boostorg/geometry?at=58ef46408e4b63533dc49b48 - test_one("artem1", - "POLYGON((36.9121 2.03883,26.2052 54.353,60.0781 64.2202,96.2171 55.9826,71.1506 39.8365,5.72552 94.1523,4.06819 13.9054,59.7155 44.5877,60.9243 16.4597,48.8696 93.039,36.9121 2.03883))", - 0, 0, 0); // TODO: expected - // https://gitter.im/boostorg/geometry?at=58ef6a2b69a692963ea83a6d - test_one("artem2", - "POLYGON((337 176,602 377,294 372,581 166,453 449,337 176))", - 0, 0, 0); // TODO: expected + // To fix this, and ggl_list_denis, it is necessary to follow in both + // positive AND negative direction, which is not supported yet. + TEST_DISSOLVE(gitter_2013_04_a, 99999.0, 9, 99, 999); #endif -/* - //Should be solved (completely) differently - // From mail on the ggl-mailing list - test_one("mail_denis_1", - "POLYGON((55 10, 141 237, 249 23, 21 171, 252 169, 24 89, 266 73, 55 10))", - 0, 7, 50.48056402439); - -*/ + TEST_DISSOLVE(gitter_2013_04_b, 31210.429356259738, 1, 0, 11); +#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS + TEST_DISSOLVE(ggl_list_denis, 99999.0, 9, 99, 999); +#endif } From 7ccda4bfc51e4c249e8e58f90ea6c6383fb1b5e4 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Fri, 20 Oct 2017 16:34:18 +0200 Subject: [PATCH 173/188] [test] add testcases for dissolve, some still failing --- extensions/test/algorithms/dissolve.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/extensions/test/algorithms/dissolve.cpp b/extensions/test/algorithms/dissolve.cpp index b6d494a17..c54f298fa 100644 --- a/extensions/test/algorithms/dissolve.cpp +++ b/extensions/test/algorithms/dissolve.cpp @@ -82,6 +82,11 @@ namespace // Source: http://upload.wikimedia.org/wikipedia/commons/a/a7/Obtuse_heptagram.svg std::string const dissolve_13 = "POLYGON((409 5,813.456329 512.172194,229 793.631528,84.651208 161.198146,733.348792 161.198146,589 793.631528,4.543671 512.172194))"; + std::string const dissolve_14 = "POLYGON((0 0,0 2,2 0,4 2,4 0,2 2,0 0))"; + std::string const dissolve_15 = "POLYGON((0 0,2 2,4 0,4 2,2 0,0 2,0 0))"; + + std::string const dissolve_16 = "POLYGON((1 3,4 5,7 3,4 1,1 3),(2 2,4 4,6 2,6 4,4 2,2 4,2 2))"; + // Non intersection, but with duplicate std::string const dissolve_d1 = "POLYGON((0 0,0 4,4 0,4 0,0 0))"; @@ -546,6 +551,12 @@ void test_all() TEST_DISSOLVE(dissolve_12, 186556.84077318, 1, 0, 15); TEST_DISSOLVE(dissolve_13, 361733.91651, 1, 0, 15); + TEST_DISSOLVE(dissolve_14, 4.0, 3, 0, 13); + TEST_DISSOLVE(dissolve_15, 4.0, 3, 0, 13); +#ifdef BOOST_GEOMETRY_TEST_INCLUDE_FAILING_TESTS + TEST_DISSOLVE(dissolve_16, 99999.0, 9, 99, 999); +#endif + TEST_DISSOLVE(dissolve_d1, 8.0, 1, 0, 4); TEST_DISSOLVE(dissolve_d2, 16.0, 1, 0, 10); From 38c38397cc16e88d05b2c9929f1172eb4102fe3a Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 10:10:16 +0200 Subject: [PATCH 174/188] [within] remove unused constants --- .../geometry/strategies/spherical/point_in_poly_winding.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp b/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp index fddbad95f..0f1a901d1 100644 --- a/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp +++ b/include/boost/geometry/strategies/spherical/point_in_poly_winding.hpp @@ -366,9 +366,7 @@ private: // If needed (eq1 && eq2 ? 0) could be returned calculation_type const c0 = 0; - calculation_type const c2 = 2; calculation_type const pi = constants::half_period(); - calculation_type const half_pi = pi / c2; calculation_type const p = get<0>(point); calculation_type const s1 = get<0>(seg1); From 94ce58de02e6ec223f10b366381087636faf1065 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 11:26:21 +0200 Subject: [PATCH 175/188] [overlay] fix bug in new code, it should check for the operation because it can happen that union travels to itself and that is irrelevant --- .../algorithms/detail/overlay/handle_self_turns.hpp | 3 ++- test/algorithms/overlay/multi_overlay_cases.hpp | 7 +++++++ .../set_operations/intersection/intersection_multi.cpp | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp index b1ab28b38..9c4a3094e 100644 --- a/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/handle_self_turns.hpp @@ -272,9 +272,10 @@ inline void discard_self_turns_which_loop(Turns& turns) op_type& op = turn.operations[i]; if (op.enriched.startable + && op.operation == operation_intersection && op.enriched.get_next_turn_index() == turn_index) { - // Self-turn i/u traveling to itself. Discard it. + // Self-turn i/u, i part traveling to itself. Discard it. // (alternatively it might be made unstartable - but the // intersection-operation may not be traveled anyway, and the // union-operation is not traveled at all in intersections diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index e8bd83dd5..18b77d500 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1214,6 +1214,13 @@ static std::string case_recursive_boxes_78[2] = "MULTIPOLYGON(((2 4,1 4,1 5,3 5,2 4)),((2 4,4 4,4 3,5 3,5 2,4 1,4 2,3 2,3 3,2 2,2 4)),((2 2,2 1,0 1,0 4,1 3,1 4,2 3,1 2,2 2)),((1 4,0 4,0 5,1 4)),((0 1,1 0,0 0,0 1)),((4 1,3 1,3 2,4 1)))" }; +static std::string case_recursive_boxes_79[2] = +{ + // Found by bug in discard_self_turns_which_loop, it needs only checking intersection and not union + "MULTIPOLYGON(((2 3,2 2,1 2,1 4,4 4,4 3,2 3)),((2 0,0 0,0 2,1 2,1.5 1.5,2 2,2 1,1 1,2 0)),((2 0,2 1,3 2,4 2,3.5 1.5,4 1,4 0,2 0),(2 1,2.5 0.5,3 1,2 1)))", + "MULTIPOLYGON(((2 0,1 0,1 1,0 1,0 3,1 4,4 4,4 0,2 0),(2 2,1 2,1 1,2 2),(4 3,3 3,3 2,4 3)))" +}; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 93224d8d6..427316134 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -349,6 +349,7 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_76, 2, -1, 18.25); TEST_INTERSECTION(case_recursive_boxes_77, 5, -1, 3.5); TEST_INTERSECTION(case_recursive_boxes_78, 9, -1, 8.0); + TEST_INTERSECTION(case_recursive_boxes_79, 5, -1, 9.0); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 704488a7318243a67a14dcecea09d4b7ecd7353b Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 12:17:52 +0200 Subject: [PATCH 176/188] [test] add last testcase to union/difference too --- .../set_operations/difference/difference_multi.cpp | 5 +++++ test/algorithms/set_operations/union/union_multi.cpp | 1 + 2 files changed, 6 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 034076dc6..e87da89e0 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -407,6 +407,11 @@ void test_areal() TEST_DIFFERENCE(case_recursive_boxes_76, 7, 3.75, 4, 2.5, 9); TEST_DIFFERENCE(case_recursive_boxes_77, 4, 3.75, 7, 6.25, 8); TEST_DIFFERENCE(case_recursive_boxes_78, 11, 5.5, 8, 4.5, 14); +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE(case_recursive_boxes_79, 2, 1.25, 6, 4.5, 8); +#else + TEST_DIFFERENCE_IGNORE(case_recursive_boxes_79, 2, 1.25, 5, 4.5, 7); +#endif { ut_settings sym_settings; diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index e8ada9695..b0b52ae9d 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -404,6 +404,7 @@ void test_areal() TEST_UNION(case_recursive_boxes_76, 1, 0, -1, 24.5); TEST_UNION(case_recursive_boxes_77, 8, 1, -1, 13.5); TEST_UNION(case_recursive_boxes_78, 2, 5, -1, 18.0); + TEST_UNION(case_recursive_boxes_79, 1, 2, -1, 14.75); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 15b8bcbd795ca866f76f35ec4ddb63ce49d7b5f8 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 12:19:27 +0200 Subject: [PATCH 177/188] [test] modify cases for tickets 12751 and 12752 which are (a/b) now both valid if self-turns are used --- .../set_operations/difference/difference_multi.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index e87da89e0..3196bd62a 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -480,7 +480,8 @@ void test_specific_areal() #else // Testing consistency of testcase itself - BOOST_CHECK_EQUAL(a_min_b, ticket_12751[2]); + boost::ignore_unused(a_min_b); + // BOOST_CHECK_EQUAL(a_min_b, ticket_12751[2]); TEST_DIFFERENCE_WITH(2, 3, ticket_12751, 1, 2537992.5, 2, 294963.5, 3); #endif @@ -490,12 +491,16 @@ void test_specific_areal() { // Ticket 12752 (Volker) // Spikes in a-b and b-a, failure in symmetric difference - ut_settings settings; + settings.remove_spikes = true; settings.sym_difference = false; +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + TEST_DIFFERENCE_WITH(0, 1, ticket_12752, 3, 2776692.0, 3, 7893.0, 2); +#else + // If self-intersections are not tested, result is not valid settings.test_validity = false; - TEST_DIFFERENCE_WITH(0, 1, ticket_12752, 3, 2776692.0, 3, 7893.0, 6); +#endif } { From 8dee4ab1c8e502dca149c2b321f2d2cd1c04c2f2 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 13:12:39 +0200 Subject: [PATCH 178/188] [is_valid] check reported testcase, it is valid now. Also add it to union to check the self-intersection-points (which are many) --- test/algorithms/is_valid.cpp | 5 +++++ test/algorithms/overlay/multi_overlay_cases.hpp | 8 ++++++++ test/algorithms/set_operations/union/union_multi.cpp | 2 ++ test/algorithms/test_is_valid.hpp | 2 +- 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/test/algorithms/is_valid.cpp b/test/algorithms/is_valid.cpp index b683b4c21..e6b84ff4d 100644 --- a/test/algorithms/is_valid.cpp +++ b/test/algorithms/is_valid.cpp @@ -969,6 +969,11 @@ inline void test_open_multipolygons() "MULTIPOLYGON(((0 0,10 0,10 10,0 10),(0 0,1 9,9 9,9 1)),((0 0,8 2,8 8,2 8)))", true); + test::apply + ("ticket_12503", + "MULTIPOLYGON(((15 20,12 23,15 17,15 20)),((36 25,35 25,34 24,34 23,35 23,36 25)),((15 15,10 13,11 23,12 23,12 25,10 25,7 24,8 6,15 15)),((12 29,11 30,12 30,11 31,13 31,13 34,6 38,6 32,8 31,12 27,12 29)),((9 26,6 31,7 26,7 24,9 26)),((15 48,15 45,18 44,15 48)),((38 39,18 44,26 34,38 39)),((15 45,13 34,15 33,15 45)),((17 32,15 33,15 32,17 32)),((21 31,16 38,18 32,17 32,19 30,21 31)),((15 32,13 31,13 30,15 29,15 32)),((17 29,15 29,15 28,17 29)),((15 28,13 30,12 29,14 27,15 28)),((26 27,28 30,31 27,26 34,21 31,22 29,19 30,18 30,17 29,19 29,19 28,23 28,24 27,25 27,24 26,25 24,30 24,26 27)),((17 26,15 28,15 26,17 26)),((32 26,34 27,31 27,27 27,32 26)),((19 26,17 26,19 25,19 26)),((35 23,33 18,41 15,35 23)),((24 26,24 27,19 26,20 25,23 24,24 26)),((32 13,49 1,48 4,46 5,33 15,32 13)),((33 25,32 26,32 25,31 24,32 23,33 25)),((42 15,43 22,44 22,44 23,43 23,35 23,42 15)),((44 42,38 39,40 39,39 34,34 27,33 25,35 25,38 31,36 25,43 23,44 42)),((48 46,44 23,48 22,48 46)),((15 3,23 2,18 11,15 3)),((30 19,28 20,27 21,25 24,23 24,22 23,26 20,29 17,30 19)),((24 19,21 21,21 20,22 19,24 19)),((31 24,30 24,27 21,31 22,30 19,31 19,34 23,31 23,31 24)),((21 20,20 21,21 18,21 20)),((14 26,12 26,12 25,15 25,15 20,17 17,20 21,21 21,22 23,20 24,19 25,16 25,15 26,14 27,14 26),(17 24,20 22,20 21,17 24)),((23 18,22 19,22 17,23 18)),((28 13,31 10,32 13,30 15,28 13)),((18 17,17 17,16 16,18 17)),((16 16,15 17,15 15,16 16)),((30 17,29 17,29 16,30 15,30 17)),((33 18,31 19,30 17,33 15,33 18)),((42 13,47 7,48 4,48 22,44 22,43 14,42 13)),((42 15,41 15,42 14,43 14,42 15)),((24 2,49 1,27 11,23 13,25 6,27 10,24 2)),((29 16,24 19,23 18,28 13,29 16)),((17 13,16 15,15 15,15 11,17 13)),((20 14,23 13,22 17,20 15,21 17,21 18,18 17,19 15,17 13,18 11,20 14)),((5 3,15 3,15 11,8 5,8 6,5 3)))", + true); + // MySQL report 12.06.2015 { std::string wkt = "MULTIPOLYGON(" diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 18b77d500..77f65b6f9 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1221,6 +1221,7 @@ static std::string case_recursive_boxes_79[2] = "MULTIPOLYGON(((2 0,1 0,1 1,0 1,0 3,1 4,4 4,4 0,2 0),(2 2,1 2,1 1,2 2),(4 3,3 3,3 2,4 3)))" }; + static std::string pie_21_7_21_0_3[2] = { "MULTIPOLYGON(((2500 2500,2500 3875,2855 3828,3187 3690,3472 3472,3690 3187,3828 2855,3875 2500,3828 2144,3690 1812,3472 1527,3187 1309,2855 1171,2499 1125,2144 1171,1812 1309,1527 1527,1309 1812,1171 2144,1125 2499,1171 2855,1309 3187,2500 2500)))", @@ -1361,6 +1362,13 @@ static std::string ticket_12752[2] = }; +// Ticket for validity, input is CCW +static std::string ticket_12503[2] = + { + "MULTIPOLYGON (((15 17, 12 23, 15 20, 15 17)), ((35 23, 34 23, 34 24, 35 25, 36 25, 35 23)), ((8 6, 7 24, 10 25, 12 25, 12 23, 11 23, 10 13, 15 15, 8 6)), ((12 27, 8 31, 6 32, 6 38, 13 34, 13 31, 11 31, 12 30, 11 30, 12 29, 12 27)), ((7 24, 7 26, 6 31, 9 26, 7 24)), ((18 44, 15 45, 15 48, 18 44)), ((26 34, 18 44, 38 39, 26 34)), ((15 33, 13 34, 15 45, 15 33)), ((15 32, 15 33, 17 32, 15 32)), ((19 30, 17 32, 18 32, 16 38, 21 31, 19 30)), ((15 29, 13 30, 13 31, 15 32, 15 29)), ((15 28, 15 29, 17 29, 15 28)), ((14 27, 12 29, 13 30, 15 28, 14 27)), ((30 24, 25 24, 24 26, 25 27, 24 27, 23 28, 19 28, 19 29, 17 29, 18 30, 19 30, 22 29, 21 31, 26 34, 31 27, 28 30, 26 27, 30 24)), ((15 26, 15 28, 17 26, 15 26)), ((27 27, 31 27, 34 27, 32 26, 27 27)), ((19 25, 17 26, 19 26, 19 25)), ((41 15, 33 18, 35 23, 41 15)), ((23 24, 20 25, 19 26, 24 27, 24 26, 23 24)), ((33 15, 46 5, 48 4, 49 1, 32 13, 33 15)), ((32 23, 31 24, 32 25, 32 26, 33 25, 32 23)), ((35 23, 43 23, 44 23, 44 22, 43 22, 42 15, 35 23)), ((43 23, 36 25, 38 31, 35 25, 33 25, 34 27, 39 34, 40 39, 38 39, 44 42, 43 23)), ((48 22, 44 23, 48 46, 48 22)), ((18 11, 23 2, 15 3, 18 11)), ((29 17, 26 20, 22 23, 23 24, 25 24, 27 21, 28 20, 30 19, 29 17)), ((22 19, 21 20, 21 21, 24 19, 22 19)), ((31 23, 34 23, 31 19, 30 19, 31 22, 27 21, 30 24, 31 24, 31 23)), ((21 18, 20 21, 21 20, 21 18)), ((14 27, 15 26, 16 25, 19 25, 20 24, 22 23, 21 21, 20 21, 17 17, 15 20, 15 25, 12 25, 12 26, 14 26, 14 27), (20 21, 20 22, 17 24, 20 21)), ((22 17, 22 19, 23 18, 22 17)), ((30 15, 32 13, 31 10, 28 13, 30 15)), ((16 16, 17 17, 18 17, 16 16)), ((15 15, 15 17, 16 16, 15 15)), ((30 15, 29 16, 29 17, 30 17, 30 15)), ((33 15, 30 17, 31 19, 33 18, 33 15)), ((43 14, 44 22, 48 22, 48 4, 47 7, 42 13, 43 14)), ((43 14, 42 14, 41 15, 42 15, 43 14)), ((27 10, 25 6, 23 13, 27 11, 49 1, 24 2, 27 10)), ((28 13, 23 18, 24 19, 29 16, 28 13)), ((15 11, 15 15, 16 15, 17 13, 15 11)), ((18 11, 17 13, 19 15, 18 17, 21 18, 21 17, 20 15, 22 17, 23 13, 20 14, 18 11)), ((8 6, 8 5, 15 11, 15 3, 5 3, 8 6)))", + "MULTIPOLYGON(((13 18,18 18,18 23,13 23,13 18)))" + }; + static std::string bug_21155501[2] = { "MULTIPOLYGON(((-8.3935546875 27.449790329784214,4.9658203125 18.729501999072138,11.8212890625 23.563987128451217,9.7119140625 25.48295117535531,9.8876953125 31.728167146023935,8.3056640625 32.99023555965106,8.5693359375 37.16031654673677,-1.8896484375 35.60371874069731,-0.5712890625 32.02670629333614,-8.9208984375 29.458731185355344,-8.3935546875 27.449790329784214)))", diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index b0b52ae9d..477daca6d 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -444,6 +444,8 @@ void test_areal() 1, 0, -1, 575.831180350007); #endif + TEST_UNION(ticket_12503, 42, 1, -1, 945.625); + // Should have 1 hole. Needs self turns. #ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS TEST_UNION(mysql_23023665_7, 1, 1, -1, 99.19494); diff --git a/test/algorithms/test_is_valid.hpp b/test/algorithms/test_is_valid.hpp index c230eca19..df0be5b81 100644 --- a/test/algorithms/test_is_valid.hpp +++ b/test/algorithms/test_is_valid.hpp @@ -400,7 +400,7 @@ public: bool expected_result) { std::stringstream sstr; - sstr << case_id << "-original"; + sstr << case_id << "-original"; // which is: CCW open base_test(sstr.str(), geometry, expected_result); if ( is_convertible_to_closed::apply(geometry) ) From 433b5fae585da879e904c3491102a7fb533b33bf Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 13:46:39 +0200 Subject: [PATCH 179/188] [test] add ticket_12503 to intersection (OK)/difference (failing) --- .../set_operations/difference/difference_multi.cpp | 9 +++++++++ .../set_operations/intersection/intersection_multi.cpp | 2 ++ 2 files changed, 11 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 3196bd62a..01846ca6d 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -174,6 +174,15 @@ void test_areal() TEST_DIFFERENCE_IGNORE(bug_21155501, 1, 3.758937, 0, 0.0, 1); #endif +#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + // The result is valid but wrong, version b includes nearly all area + // which was original between all the self-touching polygons +// TEST_DIFFERENCE(ticket_12503, 46, 920.625, 41, 497.125, 10); + TEST_DIFFERENCE_IGNORE(ticket_12503, 45, 920.625, 3, 7.625, 48); +#else + TEST_DIFFERENCE_IGNORE(ticket_12503, 45, 920.625, 3, 7.625, 48); +#endif + // Areas and #clips correspond with POSTGIS (except sym case) test_one("case_101_multi", case_101_multi[0], case_101_multi[1], diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 427316134..264a1bcdd 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -374,6 +374,8 @@ void test_areal() ); + TEST_INTERSECTION(ticket_12503, 2, 13, 17.375); + test_one("mysql_23023665_7", mysql_23023665_7[0], mysql_23023665_7[1], 2, 11, 9.80505786783); From 9024c5f09598b03dd414dae635d78b56021cc431 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 14:57:11 +0200 Subject: [PATCH 180/188] [test] Add testcase for incorrectly generated interior ring --- test/algorithms/overlay/multi_overlay_cases.hpp | 14 ++++++++++++++ .../set_operations/union/union_multi.cpp | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/test/algorithms/overlay/multi_overlay_cases.hpp b/test/algorithms/overlay/multi_overlay_cases.hpp index 77f65b6f9..9914b8655 100644 --- a/test/algorithms/overlay/multi_overlay_cases.hpp +++ b/test/algorithms/overlay/multi_overlay_cases.hpp @@ -1221,6 +1221,20 @@ static std::string case_recursive_boxes_79[2] = "MULTIPOLYGON(((2 0,1 0,1 1,0 1,0 3,1 4,4 4,4 0,2 0),(2 2,1 2,1 1,2 2),(4 3,3 3,3 2,4 3)))" }; +static std::string case_recursive_boxes_80[2] = +{ + // Creates very small interior ring (~0) for union. This is a robustness + // problem, it should not be generated. The intersection point is a tiny + // distance away from real IP, and therefore it generates a correct + // interior ring, and is considered as valid. But if you combine this + // resulting union later with other polygons, with another rescaling model, + // it most probably will be invalid. + // These cases are found with recursive_polygons and size=4. + // For size=5 the scaling is such that it does not occur (so often) + // It needs removing the rescaling. + "MULTIPOLYGON(((3.5 2.5,4 3,4 2,3 2,3 3,3.5 2.5)))", + "MULTIPOLYGON(((1 1,1 2,2 1,1 1)),((3 2,3 3,4 3,3 2)))" +}; static std::string pie_21_7_21_0_3[2] = { diff --git a/test/algorithms/set_operations/union/union_multi.cpp b/test/algorithms/set_operations/union/union_multi.cpp index 477daca6d..eb98884df 100644 --- a/test/algorithms/set_operations/union/union_multi.cpp +++ b/test/algorithms/set_operations/union/union_multi.cpp @@ -406,6 +406,14 @@ void test_areal() TEST_UNION(case_recursive_boxes_78, 2, 5, -1, 18.0); TEST_UNION(case_recursive_boxes_79, 1, 2, -1, 14.75); +#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + // This is correct: no holes generated + TEST_UNION(case_recursive_boxes_80, 2, 0, -1, 1.5); +#else + // See comment for this testcase + TEST_UNION(case_recursive_boxes_80, 2, 1, -1, 1.5); +#endif + test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], 1, 0, 12, 23.0); // Area from SQL Server From 8a1bede41b8e3d92412cfaa70dd9aaa43ee1cc38 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 15:18:38 +0200 Subject: [PATCH 181/188] [test] Add option to recursive_polygons to check area of resulting polygons (which should normally be >= 0.25) --- .../overlay/areal_areal/test_overlay_p_q.hpp | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/robustness/overlay/areal_areal/test_overlay_p_q.hpp b/test/robustness/overlay/areal_areal/test_overlay_p_q.hpp index c4c590c67..ac9ffa3e3 100644 --- a/test/robustness/overlay/areal_areal/test_overlay_p_q.hpp +++ b/test/robustness/overlay/areal_areal/test_overlay_p_q.hpp @@ -43,6 +43,7 @@ struct p_q_settings bool also_difference; bool validity; bool wkt; + bool verify_area; double tolerance; p_q_settings() @@ -50,6 +51,7 @@ struct p_q_settings , also_difference(false) , validity(false) , wkt(false) + , verify_area(false) , tolerance(1.0e-3) // since rescaling to integer the tolerance should be less. Was originally 1.0e-6 {} }; @@ -67,6 +69,49 @@ inline typename bg::default_area_result::type p_q_area(Geometry const& } } +struct verify_area +{ + template + static inline bool check_ring(Iterator begin, Iterator end) + { + for (Iterator it = begin; it != end; ++it) + { + double const area = bg::area(*it); + if (fabs(area) < 0.01) + { + return false; + } + } + return true; + } + + template + static inline bool check_rings(Interiors const& rings) + { + return check_ring(boost::begin(rings), boost::end(rings)); + } + + template + static inline bool check_polys(Iterator begin, Iterator end) + { + for (Iterator it = begin; it != end; ++it) + { + // If necessary, exterior_ring can be checked too + if (! check_rings(bg::interior_rings(*it))) + { + return false; + } + } + return true; + } + + template + static inline bool apply(Geometry const& g) + { + return check_polys(boost::begin(g), boost::end(g)); + } +}; + template static bool test_overlay_p_q(std::string const& caseid, G1 const& p, G2 const& q, @@ -136,6 +181,17 @@ static bool test_overlay_p_q(std::string const& caseid, wrong = true; } } + + if (settings.verify_area && ! verify_area::apply(out_u)) + { + std::cout << "Union/interior area incorrect" << std::endl; + wrong = true; + } + if (settings.verify_area && ! verify_area::apply(out_i)) + { + std::cout << "Intersection/interior area incorrect" << std::endl; + wrong = true; + } } if (true) From dd6958b90367f67a02ae8c168b9b1ee98d5ae741 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 15:19:13 +0200 Subject: [PATCH 182/188] [test] add last testcase also to intersection/difference --- .../set_operations/difference/difference_multi.cpp | 8 ++++++++ .../set_operations/intersection/intersection_multi.cpp | 1 + 2 files changed, 9 insertions(+) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 01846ca6d..520918286 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -422,6 +422,14 @@ void test_areal() TEST_DIFFERENCE_IGNORE(case_recursive_boxes_79, 2, 1.25, 5, 4.5, 7); #endif +#if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + TEST_DIFFERENCE(case_recursive_boxes_80, 1, 0.5, 2, 0.75, 2); +#else + // one polygon is divided into two, for same reason as union creates a small + // interior ring there + TEST_DIFFERENCE(case_recursive_boxes_80, 1, 0.5, 2, 0.75, 3); +#endif + { ut_settings sym_settings; #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS) diff --git a/test/algorithms/set_operations/intersection/intersection_multi.cpp b/test/algorithms/set_operations/intersection/intersection_multi.cpp index 264a1bcdd..4992897c3 100644 --- a/test/algorithms/set_operations/intersection/intersection_multi.cpp +++ b/test/algorithms/set_operations/intersection/intersection_multi.cpp @@ -350,6 +350,7 @@ void test_areal() TEST_INTERSECTION(case_recursive_boxes_77, 5, -1, 3.5); TEST_INTERSECTION(case_recursive_boxes_78, 9, -1, 8.0); TEST_INTERSECTION(case_recursive_boxes_79, 5, -1, 9.0); + TEST_INTERSECTION(case_recursive_boxes_80, 1, -1, 0.25); test_one("ggl_list_20120915_h2_a", ggl_list_20120915_h2[0], ggl_list_20120915_h2[1], From 1e0ecf37c7896bad34a9ab70a86a33332df5a5fd Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 15:24:10 +0200 Subject: [PATCH 183/188] [test] remove line which was still there unintended --- test/algorithms/set_operations/difference/difference_multi.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index 520918286..b792b088b 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -178,7 +178,6 @@ void test_areal() // The result is valid but wrong, version b includes nearly all area // which was original between all the self-touching polygons // TEST_DIFFERENCE(ticket_12503, 46, 920.625, 41, 497.125, 10); - TEST_DIFFERENCE_IGNORE(ticket_12503, 45, 920.625, 3, 7.625, 48); #else TEST_DIFFERENCE_IGNORE(ticket_12503, 45, 920.625, 3, 7.625, 48); #endif From 48843415b0e5e3ca890bc8da3b6f5f275257c3e3 Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 15:24:39 +0200 Subject: [PATCH 184/188] [test] add condition for robustness in some cases --- .../set_operations/difference/difference_multi.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/algorithms/set_operations/difference/difference_multi.cpp b/test/algorithms/set_operations/difference/difference_multi.cpp index b792b088b..15b3cb1bf 100644 --- a/test/algorithms/set_operations/difference/difference_multi.cpp +++ b/test/algorithms/set_operations/difference/difference_multi.cpp @@ -155,10 +155,14 @@ void test_areal() ut_settings settings; settings.percentage = 0.001; -#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS + // This testcase is actually different for all combinations +#if (!defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS)) || defined(BOOST_GEOMETRY_NO_ROBUSTNESS) + settings.test_validity = false; +#endif + +#if defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS) || defined(BOOST_GEOMETRY_NO_ROBUSTNESS) TEST_DIFFERENCE_WITH(0, 1, ggl_list_20120221_volker, 2, 7962.66, 2, 2775258.93, 4); #else - settings.test_validity = false; TEST_DIFFERENCE_WITH(0, 1, ggl_list_20120221_volker, 2, 7962.66, 1, 2775258.93, 3); #endif } @@ -442,7 +446,7 @@ void test_areal() sym_settings); } -#ifdef BOOST_GEOMETRY_INCLUDE_SELF_TURNS +#if defined(BOOST_GEOMETRY_INCLUDE_SELF_TURNS) && ! defined(BOOST_GEOMETRY_NO_ROBUSTNESS) TEST_DIFFERENCE(mysql_regression_1_65_2017_08_31, 1, 4.30697514e-7, 3, 152.0642, 4); #else // Misses one turn which is actually weird because there are no self-turns involved From ddd95fa45a758d7ca7f7d53381989b698c1a9fdd Mon Sep 17 00:00:00 2001 From: Barend Gehrels Date: Mon, 23 Oct 2017 15:26:17 +0200 Subject: [PATCH 185/188] [doc] add start of release notes for 1.66 --- doc/release_notes.qbk | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/release_notes.qbk b/doc/release_notes.qbk index 24f1e2377..921360512 100644 --- a/doc/release_notes.qbk +++ b/doc/release_notes.qbk @@ -18,6 +18,21 @@ [section:release_notes Release Notes] +[/=================] +[heading Boost 1.66] +[/=================] + +[*Improvements] + +[*Solved issues] + +* [@https://svn.boost.org/trac10/ticket/12503 12503] Validity of complex polygon + +[*Bugfixes] + +* Fixes in validity of union/intersection/differece which were sometimes invalid. In most cases, results are valid now. +* Fixes in results of union/intersection/differece which could be incorrect in very complex cases + [/=================] [heading Boost 1.65] [/=================] From 14592168c46586d580b8b0362fbc05325b73afab Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 27 Oct 2017 01:22:01 +0200 Subject: [PATCH 186/188] [relate] Fix reference to temporary (reported by clang-4.0 asan). --- .../geometry/algorithms/detail/relate/linear_areal.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp index f1b4fdf81..ddbd7d615 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -260,17 +260,19 @@ struct linear_areal if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; + typedef typename IntersectionStrategy::template point_in_geometry_strategy::type within_strategy_type; + within_strategy_type const within_strategy = intersection_strategy.template get_point_in_geometry_strategy(); boundary_checker boundary_checker1(geometry1); no_turns_la_linestring_pred < Geometry2, Result, - typename IntersectionStrategy::template point_in_geometry_strategy::type, + within_strategy_type, boundary_checker, TransposeResult > pred1(geometry2, result, - intersection_strategy.template get_point_in_geometry_strategy(), + within_strategy, boundary_checker1); for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) From 8eacc794c47776c123aad9354e91fe234574c3c7 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Fri, 27 Oct 2017 15:54:46 +0200 Subject: [PATCH 187/188] [ci] Run tests from 2 manual lists, one for each parallel container (prevent timeout). --- circle.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 19b083f81..49f4dd742 100644 --- a/circle.yml +++ b/circle.yml @@ -16,6 +16,8 @@ machine: environment: # define tests list, if parallelism is enabled they are run in parallel TESTS: test index/test + TESTS0: test/core test/concepts test/geometries test/arithmetic test/algorithms test/formulas test/iterators + TESTS1: test/strategies test/policies test/io test/util test/views index/test # this is not fully bulletproof, ideally one should check # if the current branch originates in master or develop @@ -109,7 +111,11 @@ test: #- cd $BOOST_ROOT/libs/geometry && index=0 ; run_tests() { for i in "$1"/* ; do if [ -f "$i"/Jamfile* ] ; then ((index++)) ; echo "$index - $i" ; ../../b2 cxxflags="--coverage" linkflags="--coverage" "$i" ; run_tests "$i" ; fi ; done } ; run_tests test # so for now just run the tests from the list - - cd $BOOST_ROOT/libs/geometry && status=0; index=0 ; for t in ${TESTS[@]} ; do if [ $(((index++)%CIRCLE_NODE_TOTAL)) == $CIRCLE_NODE_INDEX ] ; then ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t ; fi || status=$? ; done ; exit $status : + #- cd $BOOST_ROOT/libs/geometry && status=0; index=0 ; for t in ${TESTS[@]} ; do if [ $(((index++)%CIRCLE_NODE_TOTAL)) == $CIRCLE_NODE_INDEX ] ; then ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t ; fi || status=$? ; done ; exit $status : + # parallel: true + + # so for now just run the tests from 2 lists + - cd $BOOST_ROOT/libs/geometry && status=0; case $CIRCLE_NODE_INDEX in 0) for t in ${TESTS1[@]} ; do ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t || status=$? ; done ;; 1) for t in ${TESTS2[@]} ; do ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t || status=$? ; done ;; esac ; exit $status : parallel: true post: From 7e366baca979cdb1b9fe862c7a9d0ddc9a9f1106 Mon Sep 17 00:00:00 2001 From: Adam Wulkiewicz Date: Sat, 28 Oct 2017 21:51:14 +0200 Subject: [PATCH 188/188] [ci] Fix variable names in circle.yml script. --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 49f4dd742..20ec47d86 100644 --- a/circle.yml +++ b/circle.yml @@ -115,7 +115,7 @@ test: # parallel: true # so for now just run the tests from 2 lists - - cd $BOOST_ROOT/libs/geometry && status=0; case $CIRCLE_NODE_INDEX in 0) for t in ${TESTS1[@]} ; do ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t || status=$? ; done ;; 1) for t in ${TESTS2[@]} ; do ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t || status=$? ; done ;; esac ; exit $status : + - cd $BOOST_ROOT/libs/geometry && status=0; case $CIRCLE_NODE_INDEX in 0) for t in ${TESTS0[@]} ; do ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t || status=$? ; done ;; 1) for t in ${TESTS1[@]} ; do ../../b2 -j2 cxxflags="--coverage" linkflags="--coverage" $t || status=$? ; done ;; esac ; exit $status : parallel: true post: