diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp index bbadaa8de..fe1938496 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp @@ -28,9 +28,11 @@ struct turn_operation_linear { turn_operation_linear() : position(position_middle) + , is_collinear(false) {} turn_position position; + bool is_collinear; // valid only for Linear geometry }; // SEGMENT_INTERSECTION RESULT @@ -347,12 +349,16 @@ struct get_turn_info_for_endpoint if ( append_first || append_last ) { bool handled = handle_internal(pi, pj, pk, qi, qj, qk, ip, - is_p_first_ip, is_p_last_ip, is_q_first_ip, is_q_last_ip, is_qi_ip, is_qj_ip, + is_p_first_ip, is_p_last_ip, + is_q_first_ip, is_q_last_ip, + is_qi_ip, is_qj_ip, tp_model, result, p_operation, q_operation); if ( !handled ) { handle_internal(qi, qj, qk, pi, pj, pk, ip, - is_q_first_ip, is_q_last_ip, is_p_first_ip, is_p_last_ip, is_pi_ip, is_pj_ip, + is_q_first_ip, is_q_last_ip, + is_p_first_ip, is_p_last_ip, + is_pi_ip, is_pj_ip, tp_model, result, q_operation, p_operation); } @@ -382,7 +388,8 @@ struct get_turn_info_for_endpoint static inline bool handle_internal(Point1 const& i1, Point1 const& j1, Point1 const& /*k1*/, Point2 const& i2, Point2 const& j2, Point2 const& k2, Point const& ip, - bool first1, bool last1, bool first2, bool last2, + bool first1, bool last1, + bool first2, bool last2, bool ip_i2, bool ip_j2, TurnInfo const& tp_model, IntersectionResult const& result, @@ -420,6 +427,7 @@ struct get_turn_info_for_endpoint if ( tp.both(operation_continue) ) { + // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! bool opposite = result.template get<1>().opposite; op1 = operation_intersection; @@ -443,23 +451,23 @@ struct get_turn_info_for_endpoint BOOST_ASSERT(ip_i2 == equals::equals_point_point(i2, ip)); BOOST_ASSERT(ip_j2 == equals::equals_point_point(j2, ip)); #endif - if ( ip_j2 ) + if ( ip_i2 ) { // don't output this IP - for the first point of other geometry segment op1 = operation_none; op2 = operation_none; return true; } - else if ( ip_i2 ) + else if ( ip_j2 ) { // NOTE: this conversion may be problematic - Point1 j2_conv; - geometry::convert(j2, j2_conv); + Point1 i2_conv; + geometry::convert(i2, i2_conv); - side_calculator side_calc(j2_conv, j1, i1, i2, j2, k2); + side_calculator side_calc(i2_conv, j1, i1, i2, j2, k2); TurnInfo tp = tp_model; - equal::apply(j2_conv, j1, i1, i2, j2, k2, + equal::apply(i2_conv, j1, i1, i2, j2, k2, tp, result.template get<0>(), result.template get<1>(), side_calc); // TODO: must the above be calculated? @@ -467,10 +475,11 @@ struct get_turn_info_for_endpoint if ( tp.both(operation_continue) ) { - bool opposite = result.template get<1>().opposite; + // THIS IS WRT THE ORIGINAL SEGMENTS! NOT THE ONES ABOVE! + bool second_going_out = result.template get<0>().count > 1; op1 = operation_blocked; - op2 = opposite ? operation_intersection : operation_union; + op2 = second_going_out ? operation_union : operation_intersection; } else { @@ -523,8 +532,29 @@ struct get_turn_info_for_endpoint tp.method = method; tp.operations[0].operation = op0; tp.operations[1].operation = op1; - // tp.operations[0].position = pos0; - // tp.operations[1].position = pos1; + tp.operations[0].position = pos0; + tp.operations[1].position = pos1; + + // NOTE: this probably shouldn't be set for the first point + // for which there is no preceding segment + if ( result.template get<0>().count > 1 ) + { + //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + } + else //if ( result.template get<0>().count == 1 ) + { + if ( op0 == operation_blocked && op1 == operation_intersection ) + { + tp.operations[0].is_collinear = true; + } + else if ( op0 == operation_intersection && op1 == operation_blocked ) + { + tp.operations[1].is_collinear = true; + } + } + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); *out++ = tp; } diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp index 8a6c481d3..db74ec40b 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp @@ -112,6 +112,11 @@ struct get_turn_info_linear_areal swapped_side_calc); } + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + replace_method_and_operations_tm(tp.method, tp.operations[0].operation, tp.operations[1].operation); AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); @@ -144,6 +149,11 @@ struct get_turn_info_linear_areal touch::apply(pi, pj, pk, qi, qj, qk, tp, result.template get<0>(), result.template get<1>(), side_calc); + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + replace_method_and_operations_tm(tp.method, tp.operations[0].operation, tp.operations[1].operation); AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); @@ -159,27 +169,32 @@ struct get_turn_info_linear_areal { // do nothing } - else if ( ! result.template get<1>().opposite ) - { - // Both equal - // or collinear-and-ending at intersection point - equal::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>(), side_calc); - - replacer_of_method_and_operations_ec replacer(method_touch); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); - - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); - *out++ = tp; - } else { - equal_opposite - < - TurnInfo, - AssignPolicy - >::apply(pi, qi, - tp, out, result.template get<0>(), result.template get<1>()); + tp.operations[0].is_collinear = true; + + if ( ! result.template get<1>().opposite ) + { + // Both equal + // or collinear-and-ending at intersection point + equal::apply(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>(), side_calc); + + replacer_of_method_and_operations_ec replacer(method_touch); + replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + *out++ = tp; + } + else + { + equal_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, qi, + tp, out, result.template get<0>(), result.template get<1>()); + } } } break; @@ -192,42 +207,46 @@ struct get_turn_info_linear_areal { // do nothing } - else if (! result.template get<1>().opposite) + else { + tp.operations[0].is_collinear = true; - if (result.template get<1>().arrival[0] == 0) + if (! result.template get<1>().opposite) { - // Collinear, but similar thus handled as equal - equal::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>(), side_calc); + if (result.template get<1>().arrival[0] == 0) + { + // Collinear, but similar thus handled as equal + equal::apply(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>(), side_calc); - replacer_of_method_and_operations_ec replacer(method_touch); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + replacer_of_method_and_operations_ec replacer(method_touch); + replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + } + else + { + collinear::apply(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>(), side_calc); + + replacer_of_method_and_operations_ec replacer(method_touch_interior); + replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + } + + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + *out++ = tp; } else { - collinear::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>(), side_calc); - + // Is this always 'm' ? replacer_of_method_and_operations_ec replacer(method_touch_interior); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + + collinear_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, pj, pk, qi, qj, qk, + tp, out, result.template get<0>(), result.template get<1>(), side_calc, + replacer); } - - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); - *out++ = tp; - } - else - { - // Is this always 'm' ? - replacer_of_method_and_operations_ec replacer(method_touch_interior); - - collinear_opposite - < - TurnInfo, - AssignPolicy - >::apply(pi, pj, pk, qi, qj, qk, - tp, out, result.template get<0>(), result.template get<1>(), side_calc, - replacer); } } break; @@ -267,11 +286,14 @@ struct get_turn_info_linear_areal method = (method == method_touch ? method_equal : method_collinear); } - // assuming Linear is always the first one + // Assuming G1 is always Linear if ( op0 == operation_blocked ) op0 = operation_continue; - op1 = operation_union; + if ( op1 == operation_blocked ) + op1 = operation_continue; + else if ( op1 == operation_intersection ) + op1 = operation_union; } template @@ -294,11 +316,14 @@ struct get_turn_info_linear_areal method = m_method; } - // assuming Linear is always the first one + // Assuming G1 is always Linear if ( op0 == operation_blocked ) op0 = operation_continue; - op1 = operation_union; + if ( op1 == operation_blocked ) + op1 = operation_continue; + else if ( op1 == operation_intersection ) + op1 = operation_union; } private: @@ -387,61 +412,99 @@ struct get_turn_info_linear_areal tp.operations[0].position = position_front; tp.operations[1].position = position_middle; -// NOTE: the conversion may be problematic - Point1 qi_conv; - geometry::convert(qi, qi_conv); - - method_type replaced_method = method_touch_interior; - - if ( q0j ) + if ( opposite ) // opposite -> collinear { - if ( !opposite ) - { - side_calculator side_calc(qi_conv, pi, pj, qi, qj, qk); - equal::apply(qi_conv, pi, pj, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>(), side_calc); - } - else // opposite -> collinear - { - tp.operations[0].operation = operation_continue; - tp.operations[1].operation = operation_union; - } - - replaced_method = method_touch; + tp.operations[0].operation = operation_continue; + tp.operations[1].operation = operation_union; + tp.method = q0j ? method_touch : method_touch_interior; } else { + // NOTE: the conversion may be problematic + Point1 qi_conv; + geometry::convert(qi, qi_conv); + + method_type replaced_method = method_touch_interior; + + if ( q0j ) + { + side_calculator side_calc(qi_conv, pi, pj, qi, qj, qk); + + equal::apply(qi_conv, pi, pj, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>(), side_calc); + + replaced_method = method_touch; + } + else + { // NOTE: the conversion may be problematic - Point2 pi_conv; - geometry::convert(pi, pi_conv); + Point2 pi_conv; + geometry::convert(pi, pi_conv); - side_calculator side_calc(qi_conv, pi, pj, qi, pi_conv, qj); + side_calculator side_calc(qi_conv, pi, pj, qi, pi_conv, qj); - // Collinear, but similar thus handled as equal - equal::apply(qi_conv, pi, pj, qi, pi_conv, qj, - tp, result.template get<0>(), result.template get<1>(), side_calc); + // Collinear, but similar thus handled as equal + equal::apply(qi_conv, pi, pj, qi, pi_conv, qj, + tp, result.template get<0>(), result.template get<1>(), side_calc); + } + + replacer_of_method_and_operations_ec replacer(replaced_method); + replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); } - replacer_of_method_and_operations_ec replacer(replaced_method); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); - // equals<> or collinear<> will assign the second point, // we'd like to assign the first one geometry::convert(result.template get<0>().intersections[0], tp.point); + // NOTE: not really needed especially for the first point + // for which there is no preceding segment (but consistent with the L/L) + if ( result.template get<0>().count > 1 ) + { + //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); + tp.operations[0].is_collinear = true; + } + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); *out++ = tp; } // ANALYSE AND ASSIGN LAST - // IP on the first point of Linear Geometry - if ( EnableLast && is_p_last && ( ip_count > 1 ? p1j : p0j ) && !q0i && !q1i ) // !q0i && !q1i prevents duplication + // IP on the last point of Linear Geometry + if ( EnableLast + && is_p_last + && ( ip_count > 1 ? p1j : p0j ) + && (!q0i || (q0i && q1j)) // prevents duplication + && !q1i ) // prevents duplication { TurnInfo tp = tp_model; + + if ( result.template get<0>().count > 1 ) + { + //BOOST_ASSERT( result.template get<1>().dir_a == 0 && result.template get<1>().dir_b == 0 ); + tp.operations[0].is_collinear = true; + tp.operations[1].operation = opposite ? operation_continue : operation_union; + } + else //if ( result.template get<0>().count == 1 ) + { + Point1 qi_conv; + geometry::convert(qi, qi_conv); + side_calculator side_calc(qi_conv, pj, pi, qi, qj, qk); + + equal::apply(qi_conv, pj, pi, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>(), side_calc); + + replacer_of_method_and_operations_ec replacer(method_none); + replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + + if ( tp.both(operation_continue) ) + { + tp.operations[0].is_collinear = true; + } + } + tp.method = ( ip_count > 1 ? q1j : q0j ) ? method_touch : method_touch_interior; tp.operations[0].operation = operation_blocked; - tp.operations[1].operation = operation_union; tp.operations[0].position = position_back; tp.operations[1].position = position_middle; diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp index e054652fd..2cee246dc 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp @@ -112,6 +112,15 @@ struct get_turn_info_linear_linear swapped_side_calc); } + if ( tp.operations[0].operation == operation_blocked ) + { + tp.operations[1].is_collinear = true; + } + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + replace_method_and_operations_tm(tp.method, tp.operations[0].operation, tp.operations[1].operation); AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); @@ -144,6 +153,15 @@ struct get_turn_info_linear_linear touch::apply(pi, pj, pk, qi, qj, qk, tp, result.template get<0>(), result.template get<1>(), side_calc); + if ( tp.operations[0].operation == operation_blocked ) + { + tp.operations[1].is_collinear = true; + } + if ( tp.operations[1].operation == operation_blocked ) + { + tp.operations[0].is_collinear = true; + } + replace_method_and_operations_tm(tp.method, tp.operations[0].operation, tp.operations[1].operation); AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); @@ -159,27 +177,33 @@ struct get_turn_info_linear_linear { // do nothing } - else if ( ! result.template get<1>().opposite ) - { - // Both equal - // or collinear-and-ending at intersection point - equal::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>(), side_calc); - - replacer_of_method_and_operations_ec replacer(method_touch); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); - - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); - *out++ = tp; - } else { - equal_opposite - < - TurnInfo, - AssignPolicy - >::apply(pi, qi, - tp, out, result.template get<0>(), result.template get<1>()); + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; + + if ( ! result.template get<1>().opposite ) + { + // Both equal + // or collinear-and-ending at intersection point + equal::apply(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>(), side_calc); + + replacer_of_method_and_operations_ec replacer(method_touch); + replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + *out++ = tp; + } + else + { + equal_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, qi, + tp, out, result.template get<0>(), result.template get<1>()); + } } } break; @@ -192,47 +216,52 @@ struct get_turn_info_linear_linear { // do nothing } - else if (! result.template get<1>().opposite) + else { + tp.operations[0].is_collinear = true; + tp.operations[1].is_collinear = true; - if (result.template get<1>().arrival[0] == 0) - { - // Collinear, but similar thus handled as equal - equal::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>(), side_calc); + if (! result.template get<1>().opposite) + { + if (result.template get<1>().arrival[0] == 0) + { + // Collinear, but similar thus handled as equal + equal::apply(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>(), side_calc); - // NOTE: don't change the method only if methods are WRT IPs, not segments! - // (currently this approach is used) - // override assigned method - //tp.method = method_collinear; + // NOTE: don't change the method only if methods are WRT IPs, not segments! + // (currently this approach is used) + // override assigned method + //tp.method = method_collinear; - replacer_of_method_and_operations_ec replacer(method_touch); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + replacer_of_method_and_operations_ec replacer(method_touch); + replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + } + else + { + collinear::apply(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>(), side_calc); + + replacer_of_method_and_operations_ec replacer(method_touch_interior); + replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + } + + AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + *out++ = tp; } else { - collinear::apply(pi, pj, pk, qi, qj, qk, - tp, result.template get<0>(), result.template get<1>(), side_calc); - + // If this always 'm' ? replacer_of_method_and_operations_ec replacer(method_touch_interior); - replacer(tp.method, tp.operations[0].operation, tp.operations[1].operation); + + collinear_opposite + < + TurnInfo, + AssignPolicy + >::apply(pi, pj, pk, qi, qj, qk, + tp, out, result.template get<0>(), result.template get<1>(), side_calc, + replacer); } - - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); - *out++ = tp; - } - else - { - // If this always 'm' ? - replacer_of_method_and_operations_ec replacer(method_touch_interior); - - collinear_opposite - < - TurnInfo, - AssignPolicy - >::apply(pi, pj, pk, qi, qj, qk, - tp, out, result.template get<0>(), result.template get<1>(), side_calc, - replacer); } } break; diff --git a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp index 4e6af9ba1..c84057f3f 100644 --- a/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -735,29 +735,76 @@ struct get_turns_polygon_cs } }; -// GET_TURN_INFO_TYPE +}} // namespace detail::get_turns +#endif // DOXYGEN_NO_DETAIL -template ::type, + bool IsPoint = boost::is_base_of::value, bool IsLinear = boost::is_base_of::value, bool IsAreal = boost::is_base_of::value> -struct tag_base : not_implemented +struct group_tag + : not_implemented {}; -template -struct tag_base +template +struct group_tag +{ + typedef pointlike_tag type; +}; + +template +struct group_tag { typedef linear_tag type; }; -template -struct tag_base +template +struct group_tag { typedef areal_tag type; }; +template ::type> +struct group_dim + : not_implemented +{}; + +template +struct group_dim +{ + static const int value = 0; +}; + +template +struct group_dim +{ + static const int value = 1; +}; + +template +struct group_dim +{ + static const int value = 2; +}; + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace get_turns { + +// GET_TURN_INFO_TYPE + template ::type, typename Tag2 = typename tag::type, - typename TagBase1 = typename tag_base::type, typename TagBase2 = typename tag_base::type> + typename TagBase1 = typename group_tag::type, typename TagBase2 = typename group_tag::type> struct get_turn_info_type : overlay::get_turn_info {}; @@ -774,7 +821,7 @@ struct get_turn_info_type::type, typename Tag2 = typename tag::type, - typename TagBase1 = typename tag_base::type, typename TagBase2 = typename tag_base::type> + typename TagBase1 = typename group_tag::type, typename TagBase2 = typename group_tag::type> struct turn_operation_type { typedef overlay::turn_operation type; diff --git a/include/boost/geometry/algorithms/detail/range_helpers.hpp b/include/boost/geometry/algorithms/detail/range_helpers.hpp index 75cddcb78..6f546e0a4 100644 --- a/include/boost/geometry/algorithms/detail/range_helpers.hpp +++ b/include/boost/geometry/algorithms/detail/range_helpers.hpp @@ -22,6 +22,22 @@ namespace boost { namespace geometry #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace range { +template +inline typename boost::range_value::type const& +at(Range const& rng, typename boost::range_size::type i) +{ + BOOST_ASSERT(i < boost::size(rng)); + return *(boost::begin(rng) + i); +} + +template +inline typename boost::range_value::type & +at(Range & rng, typename boost::range_size::type i) +{ + BOOST_ASSERT(i < boost::size(rng)); + return *(boost::begin(rng) + i); +} + template inline typename boost::range_value::type const& front(Range const& rng) diff --git a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp index deabf3d2d..b16e6bae6 100644 --- a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp +++ b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp @@ -41,6 +41,7 @@ public: template bool is_endpoint_boundary(point_type const& pt) const { + boost::ignore_unused_variable_warning(pt); #ifdef BOOST_GEOMETRY_DEBUG_RELATE_BOUNDARY_CHECKER // may give false positives for INT BOOST_ASSERT( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) @@ -95,13 +96,13 @@ public: {} template - bool is_endpoint_boundary(point_type const& pt) + bool is_endpoint_boundary(point_type const& pt) const { return is_boundary_point(pt); } template - bool is_boundary(point_type const& pt, segment_identifier const& sid) + bool is_boundary(point_type const& pt, segment_identifier const& sid) const { if ( BoundaryQuery == boundary_front ) { @@ -128,7 +129,7 @@ public: private: // First call O(NlogN) // Each next call O(logN) - bool is_boundary_point(point_type const& pt) + bool is_boundary_point(point_type const& pt) const { typedef typename boost::range_size::type size_type; size_type multi_count = boost::size(geometry); @@ -173,9 +174,10 @@ private: return equal_points_count % 2 != 0;// && equal_points_count > 0; // the number is odd and > 0 } - bool is_filled; + mutable bool is_filled; // TODO: store references/pointers instead of points? - std::vector boundary_points; + mutable std::vector boundary_points; + Geometry const& geometry; }; diff --git a/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp b/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp new file mode 100644 index 000000000..941d8a5a6 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/relate/follow_helpers.hpp @@ -0,0 +1,304 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-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_FOLLOW_HELPERS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +// TODO: +// For 1-point linestrings or with all equal points turns won't be generated! +// Check for those degenerated cases may be connected with this one! + +template ::type, + bool IsMulti = boost::is_base_of::value +> +struct for_each_disjoint_geometry_if + : public not_implemented +{}; + +template +struct for_each_disjoint_geometry_if +{ + template + static inline bool apply(TurnIt first, TurnIt last, + Geometry const& geometry, + Pred & pred) + { + if ( first != last ) + return false; + pred(geometry); + return true; + } +}; + +template +struct for_each_disjoint_geometry_if +{ + template + static inline bool apply(TurnIt first, TurnIt last, + Geometry const& geometry, + Pred & pred) + { + BOOST_ASSERT(first != last); + + const std::size_t count = boost::size(geometry); + boost::ignore_unused_variable_warning(count); + + // O(I) + // gather info about turns generated for contained geometries + std::vector detected_intersections(count, false); + for ( TurnIt it = first ; it != last ; ++it ) + { + int multi_index = it->operations[OpId].seg_id.multi_index; + BOOST_ASSERT(multi_index >= 0); + std::size_t index = static_cast(multi_index); + BOOST_ASSERT(index < count); + detected_intersections[index] = true; + } + + bool found = false; + + // O(N) + // check predicate for each contained geometry without generated turn + for ( std::vector::iterator it = detected_intersections.begin() ; + it != detected_intersections.end() ; ++it ) + { + // if there were no intersections for this multi_index + if ( *it == false ) + { + found = true; + bool cont = pred( + *(boost::begin(geometry) + + std::distance(detected_intersections.begin(), it))); + if ( !cont ) + break; + } + } + + return found; + } +}; + +// TODO: rename to point_id_ref? +template +class point_identifier +{ +public: + point_identifier() : sid_ptr(0), pt_ptr(0) {} + point_identifier(segment_identifier const& sid, Point const& pt) + : sid_ptr(boost::addressof(sid)) + , pt_ptr(boost::addressof(pt)) + {} + segment_identifier const& seg_id() const + { + BOOST_ASSERT(sid_ptr); + return *sid_ptr; + } + Point const& point() const + { + BOOST_ASSERT(pt_ptr); + return *pt_ptr; + } + + //friend bool operator==(point_identifier const& l, point_identifier const& r) + //{ + // return l.seg_id() == r.seg_id() + // && detail::equals::equals_point_point(l.point(), r.point()); + //} + +private: + const segment_identifier * sid_ptr; + const Point * pt_ptr; +}; + +class same_multi_index +{ +public: + same_multi_index(segment_identifier const& sid) + : sid_ptr(boost::addressof(sid)) + {} + + bool operator()(segment_identifier const& sid) const + { + return sid.multi_index == sid_ptr->multi_index; + } + + template + bool operator()(point_identifier const& pid) const + { + return operator()(pid.seg_id()); + } + +private: + const segment_identifier * sid_ptr; +}; + +class segment_watcher +{ +public: + segment_watcher() + : m_seg_id_ptr(0) + {} + + bool update(segment_identifier const& seg_id) + { + bool result = m_seg_id_ptr == 0 || !same_multi_index(*m_seg_id_ptr)(seg_id); + m_seg_id_ptr = boost::addressof(seg_id); + return result; + } + +private: + const segment_identifier * m_seg_id_ptr; +}; + +template +class exit_watcher +{ + typedef point_identifier point_info; + +public: + exit_watcher() + : exit_operation(overlay::operation_none) + {} + + void enter(Point const& point, segment_identifier const& other_id) + { + other_entry_points.push_back(point_info(other_id, point)); + } + + void exit(Point const& point, + segment_identifier const& other_id, + overlay::operation_type exit_op) + { + typedef typename std::vector::iterator point_iterator; + // search for the entry point in the same range of other geometry + point_iterator entry_it = std::find_if(other_entry_points.begin(), + other_entry_points.end(), + same_multi_index(other_id)); + + // this end point has corresponding entry point + if ( entry_it != other_entry_points.end() ) + { + // here we know that we possibly left LS + // we must still check if we didn't get back on the same point + exit_operation = exit_op; + exit_id = point_info(other_id, point); + + // erase the corresponding entry point + other_entry_points.erase(entry_it); + } + } + + bool is_outside() const + { + // if we didn't entered anything in the past, we're outside + return other_entry_points.empty(); + } + + overlay::operation_type get_exit_operation() const + { + return exit_operation; + } + + Point const& get_exit_point() const + { + BOOST_ASSERT(exit_operation != overlay::operation_none); + return exit_id.point(); + } + + void reset_detected_exit() + { + exit_operation = overlay::operation_none; + } + + void reset() + { + exit_operation = overlay::operation_none; + other_entry_points.clear(); + } + +private: + overlay::operation_type exit_operation; + point_info exit_id; + std::vector other_entry_points; // TODO: use map here or sorted vector? +}; + +template +static inline bool is_endpoint_on_boundary(Point const& pt, + BoundaryChecker & boundary_checker) +{ + return boundary_checker.template is_endpoint_boundary(pt); +} + +template +static inline bool is_ip_on_boundary(IntersectionPoint const& ip, + OperationInfo const& operation_info, + BoundaryChecker & boundary_checker, + segment_identifier const& seg_id) +{ + boost::ignore_unused_variable_warning(seg_id); + + bool res = false; + + // IP on the last point of the linestring + if ( (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) + && operation_info.operation == overlay::operation_blocked ) + { + BOOST_ASSERT(operation_info.position == overlay::position_back); + // check if this point is a boundary + res = boundary_checker.template is_endpoint_boundary(ip); + +#ifdef BOOST_GEOMETRY_DEBUG_RELATE + BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); +#endif + } + // IP on the last point of the linestring + else if ( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) + && operation_info.position == overlay::position_front ) + { + // check if this point is a boundary + res = boundary_checker.template is_endpoint_boundary(ip); + +#ifdef BOOST_GEOMETRY_DEBUG_RELATE + BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); +#endif + } + // IP somewhere in the interior + else + { +#ifdef BOOST_GEOMETRY_DEBUG_RELATE + BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); +#endif + } + + return res; +} + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP diff --git a/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp new file mode 100644 index 000000000..933d8b6d8 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -0,0 +1,819 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013-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_LINEAR_AREAL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP + +#include +#include + +#include +#include +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +template ::type + >::value +> +struct simple_geometry +{ + template + static inline Geometry & apply(Geometry & g, Id const& ) { return g; } +}; + +template +struct simple_geometry +{ + template + static inline + typename boost::mpl::if_c + < + boost::is_const::value, + typename boost::range_value::type const&, + typename boost::range_value::type + >::type + apply(Geometry & g, Id const& id) + { + BOOST_ASSERT(id.multi_index >= 0); + return *(boost::begin(g) + id.multi_index); + } +}; + +// TODO: In the worst case for MultiLinestring/MultiPolygon this is O(NM) +// Use the rtree in this case! +template +class no_turns_la_linestring_pred +{ +public: + no_turns_la_linestring_pred(Geometry2 const& geometry2, + Result & res, + BoundaryChecker const& boundary_checker) + : m_geometry2(geometry2) + , m_result_ptr(boost::addressof(res)) + , m_boundary_checker_ptr(boost::addressof(boundary_checker)) + , m_interrupt_flags(0) + {} + + template + bool operator()(Linestring const& linestring) + { + std::size_t count = boost::size(linestring); + + // invalid input + if ( count < 2 ) + { + // ignore + // TODO: throw an exception? + return true; + } + + int pig = detail::within::point_in_geometry(range::front(linestring), m_geometry2); + BOOST_ASSERT_MSG(pig != 0, "There should be no IPs"); + + if ( pig > 0 ) + { + update(*m_result_ptr); + m_interrupt_flags |= 1; + } + else + { + update(*m_result_ptr); + m_interrupt_flags |= 2; + } + + // check if there is a boundary + if ( m_boundary_checker_ptr->template + is_endpoint_boundary(range::front(linestring)) + || m_boundary_checker_ptr->template + is_endpoint_boundary(range::back(linestring)) ) + { + if ( pig > 0 ) + { + update(*m_result_ptr); + m_interrupt_flags |= 4; + } + else + { + update(*m_result_ptr); + m_interrupt_flags |= 8; + } + + return m_interrupt_flags != 0xF; + } + + return true; + } + +private: + Geometry2 const& m_geometry2; + Result * m_result_ptr; + const BoundaryChecker * m_boundary_checker_ptr; + unsigned m_interrupt_flags; +}; + +template +class no_turns_la_areal_pred +{ +public: + no_turns_la_areal_pred(Result & res) + : m_result_ptr(boost::addressof(res)) + {} + + template + bool operator()(Areal const& areal) + { + // TODO: + // add an assertion for empty/invalid geometries + + update(*m_result_ptr); + update(*m_result_ptr); + + return false; + } + +private: + Result * m_result_ptr; +}; + +template +struct linear_areal +{ + // check Linear / Areal + BOOST_STATIC_ASSERT(detail::group_dim::value == 1 + && detail::group_dim::value == 2); + + static const bool interruption_enabled = true; + + typedef typename geometry::point_type::type point1_type; + typedef typename geometry::point_type::type point2_type; + + template + static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result) + { + // The result should be FFFFFFFFF + set::value, TransposeResult>(result);// FFFFFFFFd, d in [1,9] or T + + if ( result.interrupt ) + return; + + // get and analyse turns + typedef typename turns::get_turns::turn_info turn_type; + typedef typename std::vector::iterator turn_iterator; + std::vector turns; + + interrupt_policy_linear_areal interrupt_policy(geometry2, result); + + turns::get_turns::apply(turns, geometry1, geometry2, interrupt_policy); + if ( result.interrupt ) + return; + + boundary_checker boundary_checker1(geometry1); + no_turns_la_linestring_pred + < + Geometry2, + Result, + boundary_checker, + TransposeResult + > pred1(geometry2, result, boundary_checker1); + for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); + if ( result.interrupt ) + return; + + no_turns_la_areal_pred pred2(result); + for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); + if ( result.interrupt ) + return; + + if ( turns.empty() ) + return; + + // This is set here because in the case if empty Areal geometry were passed + // those shouldn't be set + set(result);// FFFFFF2Fd +// TODO: NEED TO SUPPORT exterior, boundary +// FOR THIS WE MUST CHECK IF THE BOUNDARY OF THE POLYGON TREATED LIKE LINEAR RING GOES OUT OF THE LINESTRING G1 +// THIS WON'T WORK IN ALL CASES! + set(result);// FFFFFF21d + if ( result.interrupt ) + return; + + // for different multi and same ring id: x, u, i, c + // for same multi and different ring id: c, i, u, x + std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,2,3,1,4,0,0>()); + + turns_analyser analyser; + analyse_each_turn(result, analyser, + turns.begin(), turns.end(), + geometry1, geometry2, + boundary_checker1); + +// TODO check if this is required by the result + if ( interrupt_policy.is_boundary_found ) + { + + } + } + + template + class interrupt_policy_linear_areal + { + public: + static bool const enabled = true; + + interrupt_policy_linear_areal(Areal const& areal, Result & result) + : m_result(result), m_areal(areal) + , is_boundary_found(false) + {} + +// TODO: since we update result for some operations here, we must not do it in the analyser! + + template + inline bool apply(Range const& turns) + { + typedef typename boost::range_iterator::type iterator; + + for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it) + { + if ( it->operations[0].operation == overlay::operation_intersection ) + { + bool const no_interior_rings + = boost::empty( + geometry::interior_rings( + simple_geometry::apply(m_areal, it->operations[1].seg_id))); + + // WARNING! THIS IS TRUE ONLY IF THE POLYGON IS SIMPLE! + // OR WITHOUT INTERIOR RINGS (AND OF COURSE VALID) + if ( no_interior_rings ) + update(m_result); + } + else if ( it->operations[0].operation == overlay::operation_continue ) + { + update(m_result); + is_boundary_found = true; + } + else if ( ( it->operations[0].operation == overlay::operation_union + || it->operations[0].operation == overlay::operation_blocked ) + && it->operations[0].position == overlay::position_middle ) + { +// TODO: here we could also check the boundaries and set BB at this point + update(m_result); + } + } + + return m_result.interrupt; + } + + private: + Result & m_result; + Areal const& m_areal; + + public: + bool is_boundary_found; + }; + + // This analyser should be used like Input or SinglePass Iterator + template + class turns_analyser + { + typedef typename TurnInfo::point_type turn_point_type; + + static const std::size_t op_id = 0; + static const std::size_t other_op_id = 1; + + public: + turns_analyser() + : m_previous_turn_ptr(0) + , m_previous_operation(overlay::operation_none) + , m_boundary_counter(0) + , m_interior_detected(false) + {} + + template + void apply(Result & res, + TurnIt first, TurnIt it, TurnIt last, + Geometry const& geometry, + OtherGeometry const& other_geometry, + BoundaryChecker const& boundary_checker) + { + if ( it != last ) + { + overlay::operation_type op = it->operations[op_id].operation; + + if ( op != overlay::operation_union + && op != overlay::operation_intersection + && op != overlay::operation_blocked + && op != overlay::operation_continue ) // operation_boundary / operation_boundary_intersection + { + return; + } + + segment_identifier const& seg_id = it->operations[op_id].seg_id; + segment_identifier const& other_id = it->operations[other_op_id].seg_id; + + const bool first_in_range = m_seg_watcher.update(seg_id); + + // handle possible exit + bool fake_enter_detected = false; + if ( m_exit_watcher.get_exit_operation() == overlay::operation_union ) + { + // real exit point - may be multiple + // we know that we entered and now we exit + if ( !detail::equals::equals_point_point(it->point, m_exit_watcher.get_exit_point()) ) + { + m_exit_watcher.reset_detected_exit(); + + // not the last IP + update(res); + } + // fake exit point, reset state + else if ( op == overlay::operation_intersection + || op == overlay::operation_continue ) // operation_boundary + { + m_exit_watcher.reset_detected_exit(); + fake_enter_detected = true; + } + } + else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked ) + { + // ignore multiple BLOCKs + if ( op == overlay::operation_blocked ) + return; + + m_exit_watcher.reset_detected_exit(); + } + +// NOTE: THE WHOLE m_interior_detected HANDLING IS HERE BECAUSE WE CAN'T EFFICIENTLY SORT TURNS (CORRECTLY) +// BECAUSE THE SAME IP MAY BE REPRESENTED BY TWO SEGMENTS WITH DIFFERENT DISTANCES +// IT WOULD REQUIRE THE CALCULATION OF MAX DISTANCE +// TODO: WE COULD GET RID OF THE TEST IF THE DISTANCES WERE NORMALIZED + +// TODO: THIS IS POTENTIALLY ERROREOUS! +// THIS ALGORITHM DEPENDS ON SOME SPECIFIC SEQUENCE OF OPERATIONS +// IT WOULD GIVE WRONG RESULTS E.G. +// IN THE CASE OF SELF-TOUCHING POINT WHEN 'i' WOULD BE BEFORE 'u' + + // handle the interior overlap + if ( m_interior_detected ) + { + // real interior overlap + if ( !detail::equals::equals_point_point(it->point, m_previous_turn_ptr->point) ) + { + update(res); + m_interior_detected = false; + } + // fake interior overlap + else if ( op == overlay::operation_continue ) + { + m_interior_detected = false; + } + } + + // if the new linestring started just now, + // but the previous one went out on the previous point, + // we must check if the boundary of the previous segment is outside + // NOTE: couldn't it be integrated with the handling of the union above? + // THIS IS REDUNDANT WITH THE HANDLING OF THE END OF THE RANGE + //if ( first_in_range + // && ! fake_enter_detected + // && m_previous_operation == overlay::operation_union ) + //{ + // BOOST_ASSERT(it != first); + // BOOST_ASSERT(m_previous_turn_ptr); + + // segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; + + // bool prev_back_b = is_endpoint_on_boundary( + // range::back(sub_geometry::get(geometry, prev_seg_id)), + // boundary_checker); + + // // if there is a boundary on the last point + // if ( prev_back_b ) + // { + // update(res); + // } + //} + + // i/u, c/u + if ( op == overlay::operation_intersection + || op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection + { + bool no_enters_detected = m_exit_watcher.is_outside(); + m_exit_watcher.enter(it->point, other_id); + + if ( op == overlay::operation_intersection ) + { + if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear ) + --m_boundary_counter; + + if ( m_boundary_counter == 0 ) + { + // interiors overlaps + //update(res); + + // don't update now + // we might enter a boundary of some other ring on the same IP + m_interior_detected = true; + } + } + else // operation_boundary + { + // don't add to the count for all met boundaries + // only if this is the "new" boundary + if ( first_in_range || !it->operations[op_id].is_collinear ) + ++m_boundary_counter; + + update(res); + } + + bool this_b = is_ip_on_boundary(it->point, + it->operations[op_id], + boundary_checker, + seg_id); + // going inside on boundary point + if ( this_b ) + { + update(res); + } + // going inside on non-boundary point + else + { + update(res); + + // if we didn't enter in the past, we were outside + if ( no_enters_detected + && !fake_enter_detected + && it->operations[op_id].position != overlay::position_front ) + { + bool from_inside = first_in_range + && calculate_from_inside(geometry, + other_geometry, + *it); + + if ( from_inside ) + update(res); + else + update(res); + + // if it's the first IP then the first point is outside + if ( first_in_range ) + { + bool front_b = is_endpoint_on_boundary( + range::front(sub_geometry::get(geometry, seg_id)), + boundary_checker); + + // if there is a boundary on the first point + if ( front_b ) + { + if ( from_inside ) + update(res); + else + update(res); + } + } + } + } + } + // u/u, x/u + else if ( op == overlay::operation_union || op == overlay::operation_blocked ) + { + bool op_blocked = op == overlay::operation_blocked; + bool no_enters_detected = m_exit_watcher.is_outside(); + + if ( op == overlay::operation_union ) + { + if ( m_boundary_counter > 0 && it->operations[op_id].is_collinear ) + --m_boundary_counter; + } + else // overlay::operation_blocked + { + m_boundary_counter = 0; + } + + // we're inside, possibly going out right now + if ( ! no_enters_detected ) + { + if ( op_blocked ) + { + // check if this is indeed the boundary point + // NOTE: is_ip_on_boundary<>() should be called here but the result will be the same + if ( is_endpoint_on_boundary(it->point, boundary_checker) ) + { + update(res); + } + } + // union, inside, but no exit -> collinear on self-intersection point + // not needed since we're already inside the boundary + /*else if ( !exit_detected ) + { + update(res); + }*/ + } + // we're outside or inside and this is the first turn + else + { + bool this_b = is_ip_on_boundary(it->point, + it->operations[op_id], + boundary_checker, + seg_id); + // if current IP is on boundary of the geometry + if ( this_b ) + { + update(res); + } + // if current IP is not on boundary of the geometry + else + { + update(res); + } + + // TODO: very similar code is used in the handling of intersection + if ( it->operations[op_id].position != overlay::position_front ) + { + bool first_from_inside = first_in_range + && calculate_from_inside(geometry, + other_geometry, + *it); + if ( first_from_inside ) + { + update(res); + + // notify the exit_watcher that we started inside + m_exit_watcher.enter(it->point, other_id); + } + else + { + update(res); + } + + // first IP on the last segment point - this means that the first point is outside or inside + if ( first_in_range && ( !this_b || op_blocked ) ) + { + bool front_b = is_endpoint_on_boundary( + range::front(sub_geometry::get(geometry, seg_id)), + boundary_checker); + + // if there is a boundary on the first point + if ( front_b ) + { + if ( first_from_inside ) + update(res); + else + update(res); + } + } + } + } + + // if we're going along a boundary, we exit only if the linestring was collinear + if ( m_boundary_counter == 0 + || it->operations[op_id].is_collinear ) + { + // notify the exit watcher about the possible exit + m_exit_watcher.exit(it->point, other_id, op); + } + } + + // store ref to previously analysed (valid) turn + m_previous_turn_ptr = boost::addressof(*it); + // and previously analysed (valid) operation + m_previous_operation = op; + } + // it == last + else + { + // here, the possible exit is the real one + // we know that we entered and now we exit + if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT + ||*/ m_previous_operation == overlay::operation_union ) + { + // for sure + update(res); + + BOOST_ASSERT(first != last); + BOOST_ASSERT(m_previous_turn_ptr); + + segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; + + bool prev_back_b = is_endpoint_on_boundary( + range::back(sub_geometry::get(geometry, prev_seg_id)), + boundary_checker); + + // if there is a boundary on the last point + if ( prev_back_b ) + { + update(res); + } + } + // we might enter some Areal and didn't go out, + else if ( m_previous_operation == overlay::operation_intersection ) + { + // just in case + update(res); + + BOOST_ASSERT(first != last); + BOOST_ASSERT(m_previous_turn_ptr); + + segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; + + bool prev_back_b = is_endpoint_on_boundary( + range::back(sub_geometry::get(geometry, prev_seg_id)), + boundary_checker); + + // if there is a boundary on the last point + if ( prev_back_b ) + { + update(res); + } + } + + // handle the interior overlap + if ( m_interior_detected ) + { + // just in case + update(res); + m_interior_detected = false; + } + + BOOST_ASSERT_MSG(m_previous_operation != overlay::operation_continue, + "Unexpected operation! Probably the error in get_turns(L,A) or relate(L,A)"); + + // Reset exit watcher before the analysis of the next Linestring + m_exit_watcher.reset(); + m_boundary_counter = 0; + } + } + + template + static inline bool calculate_from_inside(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Turn const& turn) + { + if ( turn.operations[op_id].position == overlay::position_front ) + return false; + + static const bool reverse2 = detail::overlay::do_reverse< + geometry::point_order::value + >::value; + + typedef typename closeable_view + < + typename range_type::type const, + closure::value + >::type range2_cview; + + typedef typename reversible_view + < + range2_cview const, + reverse2 ? iterate_reverse : iterate_forward + >::type range2_view; + + typedef typename sub_geometry::result_type::type range1_ref; + + range1_ref range1 = sub_geometry::get(geometry1, turn.operations[op_id].seg_id); + range2_cview const cview(sub_geometry::get(geometry2, turn.operations[other_op_id].seg_id)); + range2_view const range2(cview); + + std::size_t s1 = boost::size(range1); + std::size_t s2 = boost::size(range2); + BOOST_ASSERT(s1 > 1 && s2 > 2); + std::size_t seg_count2 = s2 - 1; + + std::size_t p_seg_ij = turn.operations[op_id].seg_id.segment_index; + std::size_t q_seg_ij = turn.operations[other_op_id].seg_id.segment_index; + + BOOST_ASSERT(p_seg_ij + 1 < s1 && q_seg_ij + 1 < s2); + + point1_type const& pi = range::at(range1, p_seg_ij); + point2_type const& qi = range::at(range2, q_seg_ij); + point2_type const& qj = range::at(range2, q_seg_ij + 1); + point1_type qi_conv; + geometry::convert(qi, qi_conv); + bool is_ip_qj = equals::equals_point_point(turn.point, qj); +// TODO: test this! + BOOST_ASSERT(!equals::equals_point_point(turn.point, pi)); + BOOST_ASSERT(!equals::equals_point_point(turn.point, qi)); + point1_type new_pj; + geometry::convert(turn.point, new_pj); + + if ( is_ip_qj ) + { + std::size_t q_seg_jk = (q_seg_ij + 1) % seg_count2; + BOOST_ASSERT(q_seg_jk + 1 < s2); + point2_type const& qk = range::at(range2, q_seg_jk + 1); + // Will this sequence of point be always correct? + overlay::side_calculator side_calc(qi_conv, new_pj, pi, qi, qj, qk); + + return calculate_from_inside_sides(side_calc); + } + else + { + point1_type new_qj; + geometry::convert(turn.point, new_qj); + + overlay::side_calculator side_calc(qi_conv, new_pj, pi, qi, new_qj, qj); + + return calculate_from_inside_sides(side_calc); + } + } + + template + static inline bool calculate_from_inside_sides(SideCalc const& side_calc) + { + // From equal<> + + int const side_pk_p = side_calc.pk_wrt_p1(); + int const side_qk_p = side_calc.qk_wrt_p1(); + // If they turn to same side (not opposite sides) + if (! overlay::base_turn_handler::opposite(side_pk_p, side_qk_p)) + { + int const side_pk_q2 = side_calc.pk_wrt_q2(); + return side_pk_q2 == -1; + } + else + { + return side_pk_p == -1; + } + + } + + private: + exit_watcher m_exit_watcher; + segment_watcher m_seg_watcher; + TurnInfo * m_previous_turn_ptr; + overlay::operation_type m_previous_operation; + unsigned m_boundary_counter; + bool m_interior_detected; + }; + + template + static inline void analyse_each_turn(Result & res, + Analyser & analyser, + TurnIt first, TurnIt last, + Geometry const& geometry, + OtherGeometry const& other_geometry, + BoundaryChecker const& boundary_checker) + { + if ( first == last ) + return; + + for ( TurnIt it = first ; it != last ; ++it ) + { + analyser.apply(res, first, it, last, + geometry, other_geometry, + boundary_checker); + + if ( res.interrupt ) + return; + } + + analyser.apply(res, first, last, last, + geometry, other_geometry, + boundary_checker); + } +}; + +template +struct areal_linear +{ + template + static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result) + { + linear_areal::apply(geometry2, geometry1, result); + } +}; + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index 24c6925fc..cce713e5e 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace boost { namespace geometry { @@ -27,113 +28,12 @@ namespace boost { namespace geometry #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace relate { -enum linestring_kind { linestring_exterior, linestring_point, linestring_closed, linestring_open }; - -template -linestring_kind check_linestring_kind(Linestring const& ls) -{ - std::size_t count = boost::size(ls); - if ( count == 0 ) - return linestring_exterior; - else if ( count == 1 ) - return linestring_point; - else - { - bool equal_fb = equals::equals_point_point(range::front(ls), range::back(ls)); - if ( equal_fb ) - { - typedef typename boost::range_iterator::type iterator; - iterator first = boost::begin(ls); - ++first; - iterator last = boost::end(ls); - --last; - for ( iterator it = first ; it != last ; ++it ) - { - if ( !equals::equals_point_point(range::front(ls), *it) ) - return linestring_closed; - } - - return linestring_point; - } - else - return linestring_open; - } -} - -// TODO: -// For 1-point linestrings or with all equal points turns won't be generated! -// Check for those degenerated cases may be connected with this one! - -template ::type> -struct for_each_disjoint_linestring_if {}; - -template -struct for_each_disjoint_linestring_if -{ - template - static inline bool apply(TurnIt first, TurnIt last, - Geometry const& geometry, - Pred & pred) - { - if ( first != last ) - return false; - pred(geometry); - return true; - } -}; - -template -struct for_each_disjoint_linestring_if -{ - template - static inline bool apply(TurnIt first, TurnIt last, - Geometry const& geometry, - Pred & pred) - { - BOOST_ASSERT(first != last); - - std::size_t count = boost::size(geometry); - std::vector detected_intersections(count, false); - for ( TurnIt it = first ; it != last ; ++it ) - { - int multi_index = it->operations[OpId].seg_id.multi_index; - BOOST_ASSERT(multi_index >= 0); - std::size_t index = static_cast(multi_index); - BOOST_ASSERT(index < count); - detected_intersections[index] = true; - } - - bool found = false; - - for ( std::vector::iterator it = detected_intersections.begin() ; - it != detected_intersections.end() ; ++it ) - { - // if there were no intersections for this multi_index - if ( *it == false ) - { - found = true; - bool cont = pred( - *(boost::begin(geometry) - + std::distance(detected_intersections.begin(), it))); - if ( !cont ) - break; - } - } - - return found; - } -}; - -template +template class disjoint_linestring_pred { - static const bool transpose_result = OpId != 0; - public: disjoint_linestring_pred(Result & res, - BoundaryChecker & boundary_checker) + BoundaryChecker const& boundary_checker) : m_result_ptr(boost::addressof(res)) , m_boundary_checker_ptr(boost::addressof(boundary_checker)) {} @@ -151,7 +51,7 @@ public: return true; } - update(*m_result_ptr); + update(*m_result_ptr); // check if there is a boundary if ( m_boundary_checker_ptr->template @@ -159,7 +59,7 @@ public: || m_boundary_checker_ptr->template is_endpoint_boundary(range::back(linestring)) ) { - update(*m_result_ptr); + update(*m_result_ptr); return false; } @@ -169,95 +69,128 @@ public: private: Result * m_result_ptr; - BoundaryChecker * m_boundary_checker_ptr; + const BoundaryChecker * m_boundary_checker_ptr; }; +//enum linestring_kind { linestring_exterior, linestring_point, linestring_closed, linestring_open }; +// +//template +//linestring_kind check_linestring_kind(Linestring const& ls) +//{ +// std::size_t count = boost::size(ls); +// if ( count == 0 ) +// return linestring_exterior; +// else if ( count == 1 ) +// return linestring_point; +// else +// { +// bool equal_fb = equals::equals_point_point(range::front(ls), range::back(ls)); +// if ( equal_fb ) +// { +// typedef typename boost::range_iterator::type iterator; +// iterator first = boost::begin(ls); +// ++first; +// iterator last = boost::end(ls); +// --last; +// for ( iterator it = first ; it != last ; ++it ) +// { +// if ( !equals::equals_point_point(range::front(ls), *it) ) +// return linestring_closed; +// } +// +// return linestring_point; +// } +// else +// return linestring_open; +// } +//} + // Called in a loop for: // Ls/Ls - worst O(N) - 1x point_in_geometry(MLs) // Ls/MLs - worst O(N) - 1x point_in_geometry(MLs) // MLs/Ls - worst O(N^2) - Bx point_in_geometry(Ls) // MLs/MLs - worst O(N^2) - Bx point_in_geometry(Ls) // TODO: later use spatial index -template -class disjoint_linestring_pred_with_point_size_handling -{ - static const bool transpose_result = OpId != 0; - -public: - disjoint_linestring_pred_with_point_size_handling(Result & res, - BoundaryChecker & boundary_checker, - OtherGeometry const& other_geometry) - : m_result_ptr(boost::addressof(res)) - , m_boundary_checker_ptr(boost::addressof(boundary_checker)) - , m_other_geometry(boost::addressof(other_geometry)) - , m_detected_mask_point(0) - , m_detected_open_boundary(false) - {} - - template - bool operator()(Linestring const& linestring) - { - linestring_kind lk = check_linestring_kind(linestring); - - if ( lk == linestring_point ) // just an optimization - { - if ( m_detected_mask_point != 7 ) - { - // check the relation - int pig = within::point_in_geometry(range::front(linestring), *m_other_geometry); - - // point inside - if ( pig > 0 ) - { - update(*m_result_ptr); - m_detected_mask_point |= 1; - } - // point on boundary - else if ( pig == 0 ) - { - update(*m_result_ptr); - m_detected_mask_point |= 2; - } - // point outside - else - { - update(*m_result_ptr); - m_detected_mask_point |= 4; - } - } - } - // NOTE: For closed Linestrings I/I=1 could be set automatically - // but for MultiLinestrings endpoints of closed Linestrings must also be checked for boundary - else if ( lk == linestring_open || lk == linestring_closed ) - { - if ( !m_detected_open_boundary ) // just an optimization - { - update(*m_result_ptr); - - // check if there is a boundary - if ( m_boundary_checker_ptr->template - is_endpoint_boundary(range::front(linestring)) - || m_boundary_checker_ptr->template - is_endpoint_boundary(range::back(linestring)) ) - { - update(*m_result_ptr); - - m_detected_open_boundary = true; - } - } - } - - bool all_detected = m_detected_mask_point == 7 && m_detected_open_boundary; - return !all_detected && !m_result_ptr->interrupt; - } - -private: - Result * m_result_ptr; - BoundaryChecker * m_boundary_checker_ptr; - const OtherGeometry * m_other_geometry; - char m_detected_mask_point; - bool m_detected_open_boundary; -}; +//template +//class disjoint_linestring_pred_with_point_size_handling +//{ +// static const bool transpose_result = OpId != 0; +// +//public: +// disjoint_linestring_pred_with_point_size_handling(Result & res, +// BoundaryChecker & boundary_checker, +// OtherGeometry const& other_geometry) +// : m_result_ptr(boost::addressof(res)) +// , m_boundary_checker_ptr(boost::addressof(boundary_checker)) +// , m_other_geometry(boost::addressof(other_geometry)) +// , m_detected_mask_point(0) +// , m_detected_open_boundary(false) +// {} +// +// template +// bool operator()(Linestring const& linestring) +// { +// linestring_kind lk = check_linestring_kind(linestring); +// +// if ( lk == linestring_point ) // just an optimization +// { +// if ( m_detected_mask_point != 7 ) +// { +// // check the relation +// int pig = within::point_in_geometry(range::front(linestring), *m_other_geometry); +// +// // point inside +// if ( pig > 0 ) +// { +// update(*m_result_ptr); +// m_detected_mask_point |= 1; +// } +// // point on boundary +// else if ( pig == 0 ) +// { +// update(*m_result_ptr); +// m_detected_mask_point |= 2; +// } +// // point outside +// else +// { +// update(*m_result_ptr); +// m_detected_mask_point |= 4; +// } +// } +// } +// // NOTE: For closed Linestrings I/I=1 could be set automatically +// // but for MultiLinestrings endpoints of closed Linestrings must also be checked for boundary +// else if ( lk == linestring_open || lk == linestring_closed ) +// { +// if ( !m_detected_open_boundary ) // just an optimization +// { +// update(*m_result_ptr); +// +// // check if there is a boundary +// if ( m_boundary_checker_ptr->template +// is_endpoint_boundary(range::front(linestring)) +// || m_boundary_checker_ptr->template +// is_endpoint_boundary(range::back(linestring)) ) +// { +// update(*m_result_ptr); +// +// m_detected_open_boundary = true; +// } +// } +// } +// +// bool all_detected = m_detected_mask_point == 7 && m_detected_open_boundary; +// return !all_detected && !m_result_ptr->interrupt; +// } +// +//private: +// Result * m_result_ptr; +// BoundaryChecker * m_boundary_checker_ptr; +// const OtherGeometry * m_other_geometry; +// char m_detected_mask_point; +// bool m_detected_open_boundary; +//}; template struct linear_linear @@ -280,19 +213,22 @@ struct linear_linear typedef typename std::vector::iterator turn_iterator; std::vector turns; -// TODO: INTEGRATE INTERRUPT POLICY WITH THE PASSED RESULT + interrupt_policy_linear_linear interrupt_policy(result); - turns::get_turns::apply(turns, geometry1, geometry2); + turns::get_turns::apply(turns, geometry1, geometry2, interrupt_policy); + + if ( result.interrupt ) + return; boundary_checker boundary_checker1(geometry1); - disjoint_linestring_pred<0, Result, boundary_checker > pred1(result, boundary_checker1); - for_each_disjoint_linestring_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); + disjoint_linestring_pred, false> pred1(result, boundary_checker1); + for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); if ( result.interrupt ) return; boundary_checker boundary_checker2(geometry2); - disjoint_linestring_pred<1, Result, boundary_checker > pred2(result, boundary_checker2); - for_each_disjoint_linestring_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); + disjoint_linestring_pred, true> pred2(result, boundary_checker2); + for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); if ( result.interrupt ) return; @@ -305,6 +241,7 @@ struct linear_linear // TODO: ADD A CHECK TO THE RESULT INDICATING IF THE FIRST AND/OR SECOND GEOMETRY MUST BE ANALYSED { + // x, u, i, c std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,2,3,1,4,0,0>()); turns_analyser<0, turn_type> analyser; @@ -318,6 +255,7 @@ struct linear_linear return; { + // x, u, i, c std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,2,3,1,4,0,1>()); turns_analyser<1, turn_type> analyser; @@ -328,147 +266,47 @@ struct linear_linear } } - // TODO: rename to point_id_ref? - template - class point_identifier + template + class interrupt_policy_linear_linear { public: - point_identifier() : sid_ptr(0), pt_ptr(0) {} - point_identifier(segment_identifier const& sid, Point const& pt) - : sid_ptr(boost::addressof(sid)) - , pt_ptr(boost::addressof(pt)) - {} - segment_identifier const& seg_id() const - { - BOOST_ASSERT(sid_ptr); - return *sid_ptr; - } - Point const& point() const - { - BOOST_ASSERT(pt_ptr); - return *pt_ptr; - } + static bool const enabled = true; - //friend bool operator==(point_identifier const& l, point_identifier const& r) - //{ - // return l.seg_id() == r.seg_id() - // && detail::equals::equals_point_point(l.point(), r.point()); - //} - - private: - const segment_identifier * sid_ptr; - const Point * pt_ptr; - }; - - class same_ranges - { - public: - same_ranges(segment_identifier const& sid) - : sid_ptr(boost::addressof(sid)) + explicit interrupt_policy_linear_linear(Result & result) + : m_result(result) {} - bool operator()(segment_identifier const& sid) const +// TODO: since we update result for some operations here, we must not do it in the analyser! + + template + inline bool apply(Range const& turns) { - return sid.multi_index == sid_ptr->multi_index - && sid.ring_index == sid_ptr->ring_index; - } - - template - bool operator()(point_identifier const& pid) const - { - return operator()(pid.seg_id()); - } - - private: - const segment_identifier * sid_ptr; - }; - - class segment_watcher - { - public: - segment_watcher() - : m_seg_id_ptr(0) - {} - - bool update(segment_identifier const& seg_id) - { - bool result = m_seg_id_ptr == 0 || !same_ranges(*m_seg_id_ptr)(seg_id); - m_seg_id_ptr = boost::addressof(seg_id); - return result; - } - - private: - const segment_identifier * m_seg_id_ptr; - }; - - template - class exit_watcher - { - typedef point_identifier point_info; - - public: - exit_watcher() - : exit_operation(overlay::operation_none) - {} - - // returns true if before the call we were outside - bool enter(Point const& point, segment_identifier const& other_id) - { - bool result = other_entry_points.empty(); - other_entry_points.push_back(point_info(other_id, point)); - return result; - } - - // returns true if before the call we were outside - bool exit(Point const& point, - segment_identifier const& other_id, - overlay::operation_type exit_op) - { - // if we didn't entered anything in the past, we're outside - if ( other_entry_points.empty() ) - return true; - - typedef typename std::vector::iterator point_iterator; - // search for the entry point in the same range of other geometry - point_iterator entry_it = std::find_if(other_entry_points.begin(), - other_entry_points.end(), - same_ranges(other_id)); - - // this end point has corresponding entry point - if ( entry_it != other_entry_points.end() ) + typedef typename boost::range_iterator::type iterator; + + for (iterator it = boost::begin(turns) ; it != boost::end(turns) ; ++it) { - // here we know that we possibly left LS - // we must still check if we didn't get back on the same point - exit_operation = exit_op; - exit_id = point_info(other_id, point); - - // erase the corresponding entry point - other_entry_points.erase(entry_it); + if ( it->operations[0].operation == overlay::operation_intersection + || it->operations[1].operation == overlay::operation_intersection ) + { + update(m_result); + } + else if ( ( it->operations[0].operation == overlay::operation_union + || it->operations[0].operation == overlay::operation_blocked + || it->operations[1].operation == overlay::operation_union + || it->operations[1].operation == overlay::operation_blocked ) + && it->operations[0].position == overlay::position_middle + && it->operations[1].position == overlay::position_middle ) + { +// TODO: here we could also check the boundaries and set IB,BI,BB at this point + update(m_result); + } } - return false; - } - - overlay::operation_type get_exit_operation() const - { - return exit_operation; - } - - Point const& get_exit_point() const - { - BOOST_ASSERT(exit_operation != overlay::operation_none); - return exit_id.point(); - } - - void reset_detected_exit() - { - exit_operation = overlay::operation_none; + return m_result.interrupt; } private: - overlay::operation_type exit_operation; - point_info exit_id; - std::vector other_entry_points; // TODO: use map here or sorted vector? + Result & m_result; }; // This analyser should be used like Input or SinglePass Iterator @@ -497,8 +335,8 @@ struct linear_linear TurnIt first, TurnIt it, TurnIt last, Geometry const& geometry, OtherGeometry const& other_geometry, - BoundaryChecker & boundary_checker, - OtherBoundaryChecker & other_boundary_checker) + BoundaryChecker const& boundary_checker, + OtherBoundaryChecker const& other_boundary_checker) { if ( it != last ) { @@ -549,30 +387,32 @@ struct linear_linear // but the previous one went out on the previous point, // we must check if the boundary of the previous segment is outside // NOTE: couldn't it be integrated with the handling of the union above? - if ( first_in_range - && ! fake_enter_detected - && m_previous_operation == overlay::operation_union ) - { - BOOST_ASSERT(it != first); - BOOST_ASSERT(m_previous_turn_ptr); + // THIS IS REDUNDANT WITH THE HANDLING OF THE END OF THE RANGE + //if ( first_in_range + // && ! fake_enter_detected + // && m_previous_operation == overlay::operation_union ) + //{ + // BOOST_ASSERT(it != first); + // BOOST_ASSERT(m_previous_turn_ptr); - segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; + // segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; - bool prev_back_b = is_endpoint_on_boundary( - range::back(sub_geometry::get(geometry, prev_seg_id)), - boundary_checker); + // bool prev_back_b = is_endpoint_on_boundary( + // range::back(sub_geometry::get(geometry, prev_seg_id)), + // boundary_checker); - // if there is a boundary on the last point - if ( prev_back_b ) - { - update(res); - } - } + // // if there is a boundary on the last point + // if ( prev_back_b ) + // { + // update(res); + // } + //} // i/i, i/x, i/u if ( op == overlay::operation_intersection ) { - bool was_outside = m_exit_watcher.enter(it->point, other_id); + bool was_outside = m_exit_watcher.is_outside(); + m_exit_watcher.enter(it->point, other_id); // interiors overlaps update(res); @@ -629,11 +469,23 @@ struct linear_linear // u/i, u/u, u/x, x/i, x/u, x/x else if ( op == overlay::operation_union || op == overlay::operation_blocked ) { - bool op_blocked = op == overlay::operation_blocked; - bool was_outside = m_exit_watcher.exit(it->point, other_id, op); + // TODO: is exit watcher still needed? + // couldn't is_collinear and some going inside counter be used instead? - // we're inside, possibly going out right now - if ( ! was_outside ) + bool is_collinear = it->operations[op_id].is_collinear; + bool was_outside = m_exit_watcher.is_outside(); + + // to exit we must be currently inside and the current segment must be collinear + if ( !was_outside && is_collinear ) + { + m_exit_watcher.exit(it->point, other_id, op); + } + + bool op_blocked = op == overlay::operation_blocked; + + // we're inside and going out from inside + // possibly going out right now + if ( ! was_outside && is_collinear ) { if ( op_blocked ) { @@ -658,14 +510,19 @@ struct linear_linear } } } - // we're outside + // we're outside or intersects some segment from the outside else { - update(res); + // if we are truly outside + if ( was_outside /*&& !is_collinear*/ ) + { + update(res); + } // boundaries don't overlap - just an optimization if ( it->method == overlay::method_crosses ) { + // the L1 is going from one side of the L2 to the other through the point update(res); // it's the first point in range @@ -715,7 +572,7 @@ struct linear_linear } // first IP on the last segment point - this means that the first point is outside - if ( first_in_range && ( !this_b || op_blocked ) ) + if ( first_in_range && ( !this_b || op_blocked ) && was_outside /*&& !is_collinear*/ ) { bool front_b = is_endpoint_on_boundary( range::front(sub_geometry::get(geometry, seg_id)), @@ -742,8 +599,8 @@ struct linear_linear { // here, the possible exit is the real one // we know that we entered and now we exit - if ( m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT - || m_previous_operation == overlay::operation_union ) + if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT + ||*/ m_previous_operation == overlay::operation_union ) { // for sure update(res); @@ -751,7 +608,7 @@ struct linear_linear BOOST_ASSERT(first != last); BOOST_ASSERT(m_previous_turn_ptr); - segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[OpId].seg_id; + segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; bool prev_back_b = is_endpoint_on_boundary( range::back(sub_geometry::get(geometry, prev_seg_id)), @@ -763,67 +620,14 @@ struct linear_linear update(res); } } + + // Just in case, + // reset exit watcher before the analysis of the next Linestring + // note that if there are some enters stored there may be some error above + m_exit_watcher.reset(); } } - template - static inline - bool is_endpoint_on_boundary(Point const& pt, - BoundaryChecker & boundary_checker) - { - return boundary_checker.template is_endpoint_boundary(pt); - } - - template - static inline - bool is_ip_on_boundary(IntersectionPoint const& ip, - OperationInfo const& operation_info, - BoundaryChecker & boundary_checker, - segment_identifier const& seg_id) - { - boost::ignore_unused_variable_warning(seg_id); - - bool res = false; - - // IP on the last point of the linestring - if ( (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) - && operation_info.operation == overlay::operation_blocked ) - { - BOOST_ASSERT(operation_info.position == overlay::position_back); - // check if this point is a boundary - res = boundary_checker.template is_endpoint_boundary(ip); - -#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR - BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); -#endif - } - // IP on the last point of the linestring - else if ( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) - && operation_info.position == overlay::position_front ) - { - // check if this point is a boundary - res = boundary_checker.template is_endpoint_boundary(ip); - -#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR - BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); -#endif - } - // IP somewhere in the interior - else - { -#ifdef BOOST_GEOMETRY_DEBUG_RELATE_LINEAR_LINEAR - BOOST_ASSERT(res == boundary_checker.template is_boundary(ip, seg_id)); -#endif - } - - return res; - } - private: exit_watcher m_exit_watcher; segment_watcher m_seg_watcher; @@ -843,8 +647,8 @@ struct linear_linear TurnIt first, TurnIt last, Geometry const& geometry, OtherGeometry const& other_geometry, - BoundaryChecker & boundary_checker, - OtherBoundaryChecker & other_boundary_checker) + BoundaryChecker const& boundary_checker, + OtherBoundaryChecker const& other_boundary_checker) { if ( first == last ) return; @@ -863,140 +667,6 @@ struct linear_linear geometry, other_geometry, boundary_checker, other_boundary_checker); } - - //template - //static inline void analyse_turns_simple(result & res, - // TurnIt first, TurnIt last, - // Geometry1 const& geometry1, - // Geometry2 const& geometry2, - // bool has_boundary1, bool has_boundary2) - //{ - // for ( TurnIt it = first ; it != last ; ++it ) - // { - // // 'i' - // if ( it->method == overlay::method_crosses ) - // { - // res.template update(); // always true - // res.template update(); // not always true - // res.template update(); // not always true - // } - // // 'e' 'c' - // else if ( it->method == overlay::method_equal - // || it->method == overlay::method_collinear ) - // { - // res.template update(); // always true - // } - // // 't' 'm' - // else if ( it->method == overlay::method_touch - // || it->method == overlay::method_touch_interior ) - // { - // bool b = handle_boundary_point(res, *it, geometry1, geometry2, has_boundary1, has_boundary2); - // - // if ( it->has(overlay::operation_union) ) - // { - // if ( !b ) - // res.template update(); - // if ( it->operations[0].operation == overlay::operation_union ) - // res.template update(); - // if ( it->operations[1].operation == overlay::operation_union ) - // res.template update(); - // } - - // if ( it->has(overlay::operation_intersection) ) - // res.template update(); - - // if ( it->has(overlay::operation_blocked) ) - // if ( !b ) - // res.template update(); - // } - // - // } - //} - - //template - //static inline bool handle_boundary_point(result & res, - // Turn const& turn, - // Geometry1 const& geometry1, Geometry2 const& geometry2, - // bool has_boundary1, bool has_boundary2) - //{ - // bool pt_on_boundary1 = has_boundary1 && equals_terminal_point(turn.point, geometry1); - // bool pt_on_boundary2 = has_boundary2 && equals_terminal_point(turn.point, geometry2); - - // if ( pt_on_boundary1 && pt_on_boundary2 ) - // res.template update(); - // else if ( pt_on_boundary1 ) - // res.template update(); - // else if ( pt_on_boundary2 ) - // res.template update(); - // else - // return false; - // return true; - //} - - //// TODO: replace with generic point_in_boundary working also for multilinestrings - //template - //static inline bool equals_terminal_point(Point const& point, Geometry const& geometry) - //{ - // return detail::equals::equals_point_point(point, range::front(geometry)) - // || detail::equals::equals_point_point(point, range::back(geometry)); - //} - - //static inline void handle_boundaries(result & res, - // Geometry1 const& geometry1, Geometry2 const& geometry2, - // bool has_boundary1, bool has_boundary2) - //{ - // int pig_front = detail::within::point_in_geometry(range::front(geometry1), geometry2); - - // if ( has_boundary1 ) - // { - // int pig_back = detail::within::point_in_geometry(range::back(geometry1), geometry2); - - // if ( pig_front > 0 || pig_back > 0 ) - // res.template set(); - // if ( pig_front == 0 || pig_back == 0 ) - // res.template set(); - // if ( pig_front < 0 || pig_back < 0 ) - // { - // res.template set(); - // res.template set(); - // } - // } - // else - // { - // if ( pig_front > 0 ) - // res.template set(); - // else if ( pig_front == 0 ) - // res.template set(); - // else if ( pig_front < 0 ) - // res.template set(); - // } - - // pig_front = detail::within::point_in_geometry(range::front(geometry2), geometry1); - - // if ( has_boundary2 ) - // { - // int pig_back = detail::within::point_in_geometry(range::back(geometry2), geometry1); - - // if ( pig_front > 0 || pig_back > 0 ) - // res.template set(); - // if ( pig_front == 0 || pig_back == 0 ) - // res.template set(); - // if ( pig_front < 0 || pig_back < 0 ) - // { - // res.template set(); - // res.template set(); - // } - // } - // else - // { - // if ( pig_front > 0 ) - // res.template set(); - // else if ( pig_front == 0 ) - // res.template set(); - // else if ( pig_front < 0 ) - // res.template set(); - // } - //} }; }} // namespace detail::relate diff --git a/include/boost/geometry/algorithms/detail/relate/relate.hpp b/include/boost/geometry/algorithms/detail/relate/relate.hpp index f3352e00d..ac5b2fe99 100644 --- a/include/boost/geometry/algorithms/detail/relate/relate.hpp +++ b/include/boost/geometry/algorithms/detail/relate/relate.hpp @@ -46,6 +46,7 @@ #include #include #include +#include namespace boost { namespace geometry { @@ -120,6 +121,26 @@ struct relate {}; +template +struct relate + : detail::relate::linear_areal +{}; + +template +struct relate + : detail::relate::areal_linear +{}; + +template +struct relate + : detail::relate::linear_areal +{}; + +template +struct relate + : detail::relate::areal_linear +{}; + }} // namespace detail_dispatch::relate namespace detail { namespace relate { @@ -132,9 +153,83 @@ struct interruption_enabled }; template -inline void relate(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result) +struct result_handler_type + : not_implemented +{}; + +template +struct result_handler_type { - return detail_dispatch::relate::relate::apply(geometry1, geometry2, result); + typedef matrix_handler type; +}; + +template +struct result_handler_type +{ + typedef mask_handler + < + mask9, + interruption_enabled + < + Geometry1, + Geometry2 + >::value + > type; +}; + +template +struct result_handler_type > +{ + typedef mask_handler + < + boost::tuples::cons, + interruption_enabled + < + Geometry1, + Geometry2 + >::value + > type; +}; + +template +struct result_handler_type > +{ + typedef static_mask_handler + < + static_mask, + interruption_enabled + < + Geometry1, + Geometry2 + >::value + > type; +}; + +template +inline +typename result_handler_type + < + Geometry1, + Geometry2, + MatrixOrMask + >::type::result_type +relate(Geometry1 const& geometry1, + Geometry2 const& geometry2, + MatrixOrMask const& matrix_or_mask = MatrixOrMask()) +{ + typedef typename result_handler_type + < + Geometry1, + Geometry2, + MatrixOrMask + >::type handler_type; + + handler_type handler(matrix_or_mask); + detail_dispatch::relate::relate::apply(geometry1, geometry2, handler); + return handler.result(); } }} // namespace detail::relate diff --git a/include/boost/geometry/algorithms/detail/relate/result.hpp b/include/boost/geometry/algorithms/detail/relate/result.hpp index d5bf04f3d..18d4c61c3 100644 --- a/include/boost/geometry/algorithms/detail/relate/result.hpp +++ b/include/boost/geometry/algorithms/detail/relate/result.hpp @@ -2,16 +2,25 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013. -// Modifications copyright (c) 2013, Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014. +// Modifications copyright (c) 2013, 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_RESULT_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_RESULT_HPP +#include +#include +#include + +// TEMP - move this header to geometry/detail +#include + namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL @@ -19,128 +28,866 @@ namespace detail { namespace relate { enum field { interior = 0, boundary = 1, exterior = 2 }; -// With DE9IM only Dimension < 10 is supported -class result +// TODO add height? + +template +class matrix { + BOOST_STATIC_ASSERT(Width == 2 || Width == 3); + public: - static const bool interrupt = false; - -// TODO: replace with std::string? - inline result() + static const std::size_t size = Width * Width; + + inline matrix() { - ::memset(m_array, 'F', 9); + ::memset(m_array, 'F', size); } template inline char get() const { - return m_array[F1 * 3 + F2]; + static const bool in_bounds = F1 * Width + F2 < size; + return get_dispatch(integral_constant()); } template inline void set() { - m_array[F1 * 3 + F2] = V; + static const bool in_bounds = F1 * Width + F2 < size; + set_dispatch(integral_constant()); } - inline std::pair get_code() const + template + inline void update() { - return std::make_pair(m_array, m_array+9); + static const bool in_bounds = F1 * Width + F2 < size; + update_dispatch(integral_constant()); + } + + inline const char * data() const + { + return m_array; } private: - char m_array[9]; + template + inline char get_dispatch(integral_constant) const + { + return m_array[F1 * Width + F2]; + } + template + inline char get_dispatch(integral_constant) const + { + return 'F'; + } + + template + inline void set_dispatch(integral_constant) + { + BOOST_STATIC_ASSERT(('0' <= V && V <= '9') || V == 'T' || V == 'F'); + m_array[F1 * Width + F2] = V; + } + template + inline void set_dispatch(integral_constant) + {} + + template + inline void update_dispatch(integral_constant) + { + BOOST_STATIC_ASSERT('0' <= D && D <= '9'); + char c = m_array[F1 * Width + F2]; + if ( D > c || c > '9') + m_array[F1 * Width + F2] = D; + } + template + inline void update_dispatch(integral_constant) + {} + + char m_array[size]; }; -// TODO: possible optimizations -// 1. interrupt in a template xxx make it static const if Interrupt == false -// 2. static_mask setting interrupt in compile-time +// TODO add EnableDimensions parameter? -template -class mask - : public result +struct matrix9 {}; +//struct matrix4 {}; + +template +struct matrix_width + : not_implemented +{}; + +template <> +struct matrix_width +{ + static const std::size_t value = 3; +}; + +template +class matrix_handler + : private matrix::value> +{ + typedef matrix::value> base_t; + +public: + typedef std::string result_type; + + static const bool interrupt = false; + + matrix_handler(Matrix const&) + {} + + result_type result() const + { + return std::string(this->data(), + this->data() + base_t::size); + } + + template + inline void set() + { + static_cast(*this).template set(); + } + + template + inline void update() + { + static_cast(*this).template update(); + } +}; + +// RUN-TIME MASKS + +class mask9 { public: + static const std::size_t width = 3; // TEMP - bool interrupt; - - inline mask(std::string const& de9im_mask) - : interrupt(false) + inline mask9(std::string const& de9im_mask) { + // TODO: throw an exception here? BOOST_ASSERT(de9im_mask.size() == 9); ::memcpy(m_mask, de9im_mask.c_str(), 9); } - template - inline void set() - { - handle_interrupt_dispatch(boost::integral_constant()); - - result::set(); - } - - inline bool check() const - { - if ( interrupt ) - return false; - - std::pair range = result::get_code(); - const char* m_it = m_mask; - const char* a_it = range.first; - for ( ; a_it != range.second ; ++a_it, ++m_it ) - { - if ( *m_it == 'F' ) - { - if ( *a_it != 'F' ) - return false; - } - else if ( *m_it == 'T' ) - { - if ( *a_it != 'T' && ( *a_it < '0' || *a_it > '9' ) ) - return false; - } - else if ( *m_it >= '0' && *m_it <= '9' ) - { - if ( *a_it != *m_it ) - return false; - } - } - - return true; - } - -private: - template - void handle_interrupt_dispatch(boost::integral_constant) - {} - - template - void handle_interrupt_dispatch(boost::integral_constant) - { - char m = get_mask(); - - if ( V >= '0' && V <= '9' ) - { - if ( m == 'F' || ( m < V && m >= '0' && m <= '9' ) ) - interrupt = true; - } - else if ( V == 'T' ) - { - if ( m == 'F' ) - interrupt = true; - } - } - template - inline char get_mask() const + inline char get() const { return m_mask[F1 * 3 + F2]; } +private: char m_mask[9]; }; +template +struct interrupt_dispatch +{ + template + static inline bool apply(Mask const&) + { + return false; + } +}; + +template +struct interrupt_dispatch +{ + template + static inline bool apply(Mask const& mask) + { + char m = mask.template get(); + return check(m); + } + + template + static inline bool check(char m) + { + if ( V >= '0' && V <= '9' ) + { + return m == 'F' || ( m < V && m >= '0' && m <= '9' ); + } + else if ( V == 'T' ) + { + return m == 'F'; + } + return false; + } +}; + +template ::value> +struct interrupt_dispatch_tuple +{ + template + static inline bool apply(Masks const& masks) + { + typedef typename boost::tuples::element::type mask_type; + mask_type const& mask = boost::get(masks); + return interrupt_dispatch::template apply(mask) + && interrupt_dispatch_tuple::template apply(masks); + } +}; + +template +struct interrupt_dispatch_tuple +{ + template + static inline bool apply(Masks const& ) + { + return true; + } +}; + +template +struct interrupt_dispatch, true> +{ + typedef boost::tuple mask_type; + + template + static inline bool apply(mask_type const& mask) + { + return interrupt_dispatch_tuple::template apply(mask); + } +}; + +template +struct interrupt_dispatch, true> +{ + typedef boost::tuples::cons mask_type; + + template + static inline bool apply(mask_type const& mask) + { + return interrupt_dispatch_tuple::template apply(mask); + } +}; + +template +inline bool interrupt(Mask const& mask) +{ + return interrupt_dispatch + ::template apply(mask); +} + +template +struct check_dispatch +{ + template + static inline bool apply(Mask const& mask, Matrix const& matrix) + { + return per_one(mask, matrix) + && per_one(mask, matrix) + && per_one(mask, matrix) + && per_one(mask, matrix) + && per_one(mask, matrix) + && per_one(mask, matrix) + && per_one(mask, matrix) + && per_one(mask, matrix) + && per_one(mask, matrix); + } + + template + static inline bool per_one(Mask const& mask, Matrix const& matrix) + { + const char mask_el = mask.template get(); + const char el = matrix.template get(); + + if ( mask_el == 'F' ) + { + return el == 'F'; + } + else if ( mask_el == 'T' ) + { + return el == 'T' || ( el >= '0' && el <= '9' ); + } + else if ( mask_el >= '0' && mask_el <= '9' ) + { + return el == mask_el; + } + + return true; + } +}; + +template ::value> +struct check_dispatch_tuple +{ + template + static inline bool apply(Masks const& masks, Matrix const& matrix) + { + typedef typename boost::tuples::element::type mask_type; + mask_type const& mask = boost::get(masks); + return check_dispatch::apply(mask, matrix) + || check_dispatch_tuple::apply(masks, matrix); + } +}; + +template +struct check_dispatch_tuple +{ + template + static inline bool apply(Masks const&, Matrix const&) + { + return false; + } +}; + +template +struct check_dispatch< boost::tuple > +{ + typedef boost::tuple mask_type; + + template + static inline bool apply(mask_type const& mask, Matrix const& matrix) + { + return check_dispatch_tuple::template apply(mask, matrix); + } +}; + +template +struct check_dispatch< boost::tuples::cons > +{ + typedef boost::tuples::cons mask_type; + + template + static inline bool apply(mask_type const& mask, Matrix const& matrix) + { + return check_dispatch_tuple::template apply(mask, matrix); + } +}; + +template +inline bool check(Mask const& mask, Matrix const& matrix) +{ + return check_dispatch::apply(mask, matrix); +} + +template <> +struct matrix_width +{ + static const std::size_t value = 3; +}; + +template ::value> +struct matrix_width_tuple +{ + static const std::size_t + current = matrix_width::type>::value; + static const std::size_t + next = matrix_width_tuple::value; + + static const std::size_t + value = current > next ? current : next; +}; + +template +struct matrix_width_tuple +{ + static const std::size_t value = 0; +}; + +template +struct matrix_width< boost::tuples::cons > +{ + static const std::size_t + value = matrix_width_tuple< boost::tuples::cons >::value; +}; + +template +class mask_handler + : private matrix::value> +{ + typedef matrix::value> base_t; + +public: + typedef bool result_type; + + bool interrupt; + + inline mask_handler(Mask const& m) + : interrupt(false) + , m_mask(m) + {} + + result_type result() const + { + return !interrupt + && check(m_mask, static_cast(*this)); + } + + template + inline void set() + { + if ( relate::interrupt(m_mask) ) + { + interrupt = true; + } + else + { + base_t::template set(); + } + } + + template + inline void update() + { + if ( relate::interrupt(m_mask) ) + { + interrupt = true; + } + else + { + base_t::template update(); + } + } + +private: + Mask const& m_mask; +}; + +// STATIC MASK + +template +class static_mask +{ + typedef boost::mpl::vector_c + < + char, II, IB, IE, BI, BB, BE, EI, EB, EE + > vector_type; + +public: + template + struct get + { + BOOST_STATIC_ASSERT(F1 * 3 + F2 < boost::mpl::size::value); + + static const char value + = boost::mpl::at_c::type::value; + }; +}; + +template +struct static_should_handle_element +{ + static const char mask_el = StaticMask::template get::value; + static const bool value = mask_el == 'F' + || mask_el == 'T' + || ( mask_el >= '0' && mask_el <= '9' ); +}; + +template +struct static_interrupt_dispatch +{ + static const bool value = false; +}; + +template +struct static_interrupt_dispatch +{ + static const char mask_el = StaticMask::template get::value; + + static const bool value + = ( V >= '0' && V <= '9' ) ? + ( mask_el == 'F' || ( mask_el < V && mask_el >= '0' && mask_el <= '9' ) ) : + ( ( V == 'T' ) ? mask_el == 'F' : false ); +}; + +template +struct static_interrupt_sequence +{ + typedef typename boost::mpl::deref::type StaticMask; + + static const bool value + = static_interrupt_dispatch + < + StaticMask, + V, F1, F2, + true, + !boost::mpl::is_sequence::value + >::value + && static_interrupt_sequence + < + typename boost::mpl::next::type, + Last, + V, F1, F2 + >::value; +}; + +template +struct static_interrupt_sequence +{ + static const bool value = true; +}; + +template +struct static_interrupt_dispatch +{ + static const bool value + = static_interrupt_sequence + < + typename boost::mpl::begin::type, + typename boost::mpl::end::type, + V, F1, F2 + >::value; +}; + +template +struct static_interrupt +{ + static const bool value + = static_interrupt_dispatch + < + StaticMask, + V, F1, F2, + EnableInterrupt, + !boost::mpl::is_sequence::value + >::value; +}; + +template +struct static_check_dispatch +{ + template + static inline bool apply(Matrix const& matrix) + { + return per_one::apply(matrix) + && per_one::apply(matrix) + && per_one::apply(matrix) + && per_one::apply(matrix) + && per_one::apply(matrix) + && per_one::apply(matrix) + && per_one::apply(matrix) + && per_one::apply(matrix) + && per_one::apply(matrix); + } + + template + struct per_one + { + static const char mask_el = StaticMask::template get::value; + static const int version + = mask_el == 'F' ? 0 + : mask_el == 'T' ? 1 + : mask_el >= '0' && mask_el <= '9' ? 2 + : 3; + + template + static inline bool apply(Matrix const& matrix) + { + const char el = matrix.template get(); + return apply_dispatch(el, integral_constant()); + } + + // mask_el == 'F' + static inline bool apply_dispatch(char el, integral_constant) + { + return el == 'F'; + } + // mask_el == 'T' + static inline bool apply_dispatch(char el, integral_constant) + { + return el == 'T' || ( el >= '0' && el <= '9' ); + } + // mask_el >= '0' && mask_el <= '9' + static inline bool apply_dispatch(char el, integral_constant) + { + return el == mask_el; + } + // else + static inline bool apply_dispatch(char el, integral_constant) + { + return true; + } + }; +}; + +template +struct static_check_sequence +{ + typedef typename boost::mpl::deref::type StaticMask; + + template + static inline bool apply(Matrix const& matrix) + { + return static_check_dispatch + < + StaticMask, + !boost::mpl::is_sequence::value + >::apply(matrix) + || static_check_sequence + < + typename boost::mpl::next::type, + Last + >::apply(matrix); + } +}; + +template +struct static_check_sequence +{ + template + static inline bool apply(Matrix const& matrix) + { + return false; + } +}; + +template +struct static_check_dispatch +{ + template + static inline bool apply(Matrix const& matrix) + { + return static_check_sequence + < + typename boost::mpl::begin::type, + typename boost::mpl::end::type + >::apply(matrix); + } +}; + +template +struct static_check +{ + template + static inline bool apply(Matrix const& matrix) + { + return static_check_dispatch + < + StaticMask, + !boost::mpl::is_sequence::value + >::apply(matrix); + } +}; + +template +class static_mask_handler + : private matrix<3> +{ + typedef matrix<3> base_t; + +public: + typedef bool result_type; + + bool interrupt; + + inline static_mask_handler(StaticMask const& /*dummy*/) + : interrupt(false) + {} + + result_type result() const + { + return (!Interrupt || !interrupt) + && static_check::apply(static_cast(*this)); + } + + template + inline void set() + { + static const bool interrupt_c = static_interrupt::value; + static const bool should_handle = static_should_handle_element::value; + static const int version = interrupt_c ? 0 + : should_handle ? 1 + : 2; + + set_dispatch(integral_constant()); + } + + template + inline void update() + { + static const bool interrupt_c = static_interrupt::value; + static const bool should_handle = static_should_handle_element::value; + static const int version = interrupt_c ? 0 + : should_handle ? 1 + : 2; + + update_dispatch(integral_constant()); + } + +private: + // Interrupt && interrupt + template + inline void set_dispatch(integral_constant) + { + interrupt = true; + } + // else should_handle + template + inline void set_dispatch(integral_constant) + { + base_t::template set(); + } + // else + template + inline void set_dispatch(integral_constant) + {} + + // Interrupt && interrupt + template + inline void update_dispatch(integral_constant) + { + interrupt = true; + } + // else should_handle + template + inline void update_dispatch(integral_constant) + { + base_t::template update(); + } + // else + template + inline void update_dispatch(integral_constant) + {} +}; + +// OPERATORS + +template inline +boost::tuples::cons< + Mask1, + boost::tuples::cons +> +operator||(Mask1 const& m1, Mask2 const& m2) +{ + namespace bt = boost::tuples; + + return + bt::cons< Mask1, bt::cons > + ( m1, bt::cons(m2, bt::null_type()) ); +} + +template inline +typename index::detail::tuples::push_back< + boost::tuples::cons, Mask +>::type +operator||(boost::tuples::cons const& t, Mask const& m) +{ + namespace bt = boost::tuples; + + return + index::detail::tuples::push_back< + bt::cons, Mask + >::apply(t, m); +} + +// PREDEFINED MASKS + +// TODO: +// 1. specialize for simplified masks if available +// e.g. for TOUCHES use 1 mask for A/A +// 2. Think about dimensions > 2 e.g. should TOUCHES be true +// if the interior of the Areal overlaps the boundary of the Volumetric +// like it's true for Linear/Areal + +// EQUALS +//typedef static_mask<'T', '*', 'F', '*', '*', 'F', 'F', 'F', '*'> static_mask_equals; // wikipedia +typedef static_mask<'T', 'F', 'F', 'F', 'T', 'F', 'F', 'F', 'T'> static_mask_equals; // OGC + +// DISJOINT +typedef static_mask<'F', 'F', '*', 'F', 'F', '*', '*', '*', '*'> static_mask_disjoint; + +// TOUCHES - NOT P/P +template ::value, + std::size_t Dim2 = detail::group_dim::value> +struct static_mask_touches_type +{ + typedef boost::mpl::vector< + static_mask<'F', 'T', '*', '*', '*', '*', '*', '*', '*'>, + static_mask<'F', '*', '*', 'T', '*', '*', '*', '*', '*'>, + static_mask<'F', '*', '*', '*', 'T', '*', '*', '*', '*'> + > type; +}; +// According to OGC, doesn't apply to P/P +// Using the above mask the result would be always false +template +struct static_mask_touches_type + : not_implemented::type, + typename geometry::tag::type> +{}; + +// WITHIN +typedef static_mask<'T', '*', 'F', '*', '*', 'F', '*', '*', '*'> static_mask_within; + +// COVERED_BY (non OGC) +typedef boost::mpl::vector< + static_mask<'T', '*', 'F', '*', '*', 'F', '*', '*', '*'>, + static_mask<'*', 'T', 'F', '*', '*', 'F', '*', '*', '*'>, + static_mask<'*', '*', 'F', 'T', '*', 'F', '*', '*', '*'>, + static_mask<'*', '*', 'F', '*', 'T', 'F', '*', '*', '*'> + > static_mask_covered_by; + +// CROSSES +// dim(G1) < dim(G2) - P/L P/A L/A +template ::value, + std::size_t Dim2 = detail::group_dim::value, + bool D1LessD2 = (Dim1 < Dim2) +> +struct static_mask_crosses_type +{ + typedef static_mask<'T', '*', 'T', '*', '*', '*', '*', '*', '*'> type; +}; +// TODO: I'm not sure if this one below should be available! +// dim(G1) > dim(G2) - L/P A/P A/L +template +struct static_mask_crosses_type +{ + typedef static_mask<'T', '*', '*', '*', '*', '*', 'T', '*', '*'> type; +}; +// dim(G1) == dim(G2) - P/P A/A +template +struct static_mask_crosses_type + : not_implemented::type, + typename geometry::tag::type> +{}; +// dim(G1) == 1 && dim(G2) == 1 - L/L +template +struct static_mask_crosses_type +{ + typedef static_mask<'0', '*', '*', '*', '*', '*', '*', '*', '*'> type; +}; + +// OVERLAPS + +// dim(G1) != dim(G2) - NOT P/P, L/L, A/A +template ::value, + std::size_t Dim2 = detail::group_dim::value +> +struct static_mask_overlaps_type + : not_implemented::type, + typename geometry::tag::type> +{}; +// dim(G1) == D && dim(G2) == D - P/P A/A +template +struct static_mask_overlaps_type +{ + typedef static_mask<'T', '*', 'T', '*', '*', '*', 'T', '*', '*'> type; +}; +// dim(G1) == 1 && dim(G2) == 1 - L/L +template +struct static_mask_overlaps_type +{ + typedef static_mask<'1', '*', 'T', '*', '*', '*', 'T', '*', '*'> type; +}; + +// RESULTS/HANDLERS UTILS + template inline void set(Result & res) { @@ -204,10 +951,7 @@ inline void set(Result & res) template inline void update(Result & res) { - BOOST_STATIC_ASSERT('0' <= D && D <= '9'); - char c = res.template get(); - if ( D > c || c > '9') - res.template set(); + res.template update(); } template diff --git a/include/boost/geometry/algorithms/detail/relate/turns.hpp b/include/boost/geometry/algorithms/detail/relate/turns.hpp index 9803c64fc..a396534a3 100644 --- a/include/boost/geometry/algorithms/detail/relate/turns.hpp +++ b/include/boost/geometry/algorithms/detail/relate/turns.hpp @@ -83,17 +83,32 @@ struct get_turns > turn_info; template - static inline void apply(Turns & turns, Geometry1 const& geometry1, Geometry2 const& geometry2) + static inline void apply(Turns & turns, + Geometry1 const& geometry1, + Geometry2 const& geometry2) { detail::get_turns::no_interrupt_policy interrupt_policy; + apply(turns, geometry1, geometry2, interrupt_policy); + } + + template + static inline void apply(Turns & turns, + Geometry1 const& geometry1, + Geometry2 const& geometry2, + InterruptPolicy & interrupt_policy) + { static const bool reverse1 = detail::overlay::do_reverse::value>::value; static const bool reverse2 = detail::overlay::do_reverse::value>::value; dispatch::get_turns < - typename tag::type, typename tag::type, - Geometry1, Geometry2, reverse1, reverse2, + typename geometry::tag::type, + typename geometry::tag::type, + Geometry1, + Geometry2, + reverse1, + reverse2, GetTurnPolicy >::apply(0, geometry1, 1, geometry2, detail::no_rescale_policy(), turns, interrupt_policy); } @@ -124,47 +139,68 @@ struct less_seg_dist_op } template static inline - bool use_operation(Op const& left, Op const& right) + bool less_operation(Op const& left, Op const& right) { return order_op(left) < order_op(right); } + template static inline + bool greater_operation(Op const& left, Op const& right) + { + return order_op(left) > order_op(right); + } + template static inline bool use_other_multi_ring_id(Op const& left, Op const& right) { + // VER1 //return left.other_id.ring_index < right.other_id.ring_index; - if ( left.other_id.ring_index == -1 ) - { - if ( right.other_id.ring_index == -1 ) - return use_operation(left, right); // sort by operation - else - return true; // right always greater - } - else // left.other_id.ring_index != -1 - { - if ( right.other_id.ring_index == -1 ) - return false; // left always greater + // VER2 + //if ( left.other_id.ring_index == -1 ) + //{ + // if ( right.other_id.ring_index == -1 ) + // return less_operation(left, right); // sort by operation + // else + // return true; // right always greater + //} + //else // left.other_id.ring_index != -1 + //{ + // if ( right.other_id.ring_index == -1 ) + // return false; // left always greater - // here both ring_indexes are greater than -1 - // so first, sort also by multi_index - return left.other_id.multi_index < right.other_id.multi_index || ( - left.other_id.multi_index == right.other_id.multi_index && ( - left.other_id.ring_index < right.other_id.ring_index || ( - left.other_id.ring_index == right.other_id.ring_index && - use_operation(left, right) ) - ) - ); + // // here both ring_indexes are greater than -1 + // // so first, sort also by multi_index + // return left.other_id.multi_index < right.other_id.multi_index || ( + // left.other_id.multi_index == right.other_id.multi_index && ( + // left.other_id.ring_index < right.other_id.ring_index || ( + // left.other_id.ring_index == right.other_id.ring_index && + // less_operation(left, right) ) + // ) + // ); + //} + + // VER3 + if ( left.other_id.multi_index == right.other_id.multi_index ) + { + if ( left.other_id.ring_index == right.other_id.ring_index ) + return less_operation(left, right); + else + return greater_operation(left, right); + } + else + { + return less_operation(left, right); } } template static inline bool use_distance(Op const& left, Op const& right) { - return left.enriched.distance < right.enriched.distance || ( - geometry::math::equals(left.enriched.distance, right.enriched.distance) && - use_other_multi_ring_id(left, right) - ); + if ( geometry::math::equals(left.enriched.distance, right.enriched.distance) ) + return use_other_multi_ring_id(left, right); + else + return left.enriched.distance < right.enriched.distance; } template diff --git a/include/boost/geometry/algorithms/detail/sub_geometry.hpp b/include/boost/geometry/algorithms/detail/sub_geometry.hpp index 06f1914a8..98a1d617e 100644 --- a/include/boost/geometry/algorithms/detail/sub_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/sub_geometry.hpp @@ -70,12 +70,15 @@ struct get result_type apply(Geometry & geometry, Id const& id) { if ( id.ring_index < 0 ) + { return geometry::exterior_ring(geometry); + } else { - BOOST_ASSERT( id.ring_index < boost::size(geometry::interior_rings(geometry)) ); + std::size_t ri = static_cast(id.ring_index); + BOOST_ASSERT( ri < boost::size(geometry::interior_rings(geometry)) ); - return *(boost::begin(geometry::interior_rings(geometry)) + id.ring_index); + return *(boost::begin(geometry::interior_rings(geometry)) + ri); } } }; diff --git a/include/boost/geometry/index/detail/tuples.hpp b/include/boost/geometry/index/detail/tuples.hpp index 28e347bed..e7f6d2235 100644 --- a/include/boost/geometry/index/detail/tuples.hpp +++ b/include/boost/geometry/index/detail/tuples.hpp @@ -164,13 +164,16 @@ struct add_unique >::type type; }; -template -struct push_back_impl +template ::value> +struct push_back { typedef boost::tuples::cons< typename boost::tuples::element::type, - typename push_back_impl::type + typename push_back::type > type; static type apply(Tuple const& tup, T const& t) @@ -178,13 +181,13 @@ struct push_back_impl return type( boost::get(tup), - push_back_impl::apply(tup, t) + push_back::apply(tup, t) ); } }; template -struct push_back_impl +struct push_back { typedef boost::tuples::cons type; diff --git a/include/boost/geometry/index/predicates.hpp b/include/boost/geometry/index/predicates.hpp index ad6a537f0..e03c9ce5a 100644 --- a/include/boost/geometry/index/predicates.hpp +++ b/include/boost/geometry/index/predicates.hpp @@ -362,11 +362,8 @@ operator&&(Pred1 const& p1, Pred2 const& p2) } template inline -typename tuples::push_back_impl< - boost::tuples::cons, - Pred, - 0, - boost::tuples::length >::value +typename tuples::push_back< + boost::tuples::cons, Pred >::type operator&&(boost::tuples::cons const& t, Pred const& p) { @@ -374,8 +371,8 @@ operator&&(boost::tuples::cons const& t, Pred const& p) namespace bt = boost::tuples; return - tuples::push_back_impl< - bt::cons, Pred, 0, bt::length< bt::cons >::value + tuples::push_back< + bt::cons, Pred >::apply(t, p); } diff --git a/test/algorithms/overlay/get_turns_linear_areal.cpp b/test/algorithms/overlay/get_turns_linear_areal.cpp index a3ba4befa..c807fdfde 100644 --- a/test/algorithms/overlay/get_turns_linear_areal.cpp +++ b/test/algorithms/overlay/get_turns_linear_areal.cpp @@ -20,10 +20,7 @@ #include "test_get_turns.hpp" #include -//TEST -#include -//#include -//#include +//#include "to_svg.hpp" template void test_all() @@ -34,41 +31,50 @@ void test_all() test_geometry("LINESTRING(15 5,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)", "POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))", - expected("miu")("iuu")("tcu")("tuu")("mcu")("miu")("muu")("tiu")("mcu")("miu")("mcu")("miu")("mxu").vec); + expected("miu")("iuu")("tcc")("tuu")("mcu")("mic")("muu")("tiu")("mcu")("mic")("mcc")("miu")("mxu").vec); test_geometry("LINESTRING(5 0,5 5,10 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "miu", "mxu"); test_geometry("LINESTRING(0 0,5 5,10 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "tiu", "txu"); test_geometry("LINESTRING(0 0,5 0,5 5,10 5,10 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", - expected("tcu")("miu")("mcu")("txu").vec); + expected("tcu")("mic")("mcc")("txu").vec); test_geometry("LINESTRING(10 0,5 0,5 5,10 5,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", - expected("tcu")("miu")("mcu")("txu").vec); + expected("tcc")("miu")("mcu")("txc").vec); test_geometry("LINESTRING(0 0,10 0,10 10)", "POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))", - expected("tcu")("miu")("mcu")("miu")("mxu").vec); + expected("tcu")("mic")("mcu")("mic")("mxu").vec); + test_geometry("LINESTRING(11 1,10 0,0 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", + "tcc", "txu"); + test_geometry("LINESTRING(0 0,10 0,11 1)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", + "tcu", "tuc"); + test_geometry("LINESTRING(10 0,0 0,-1 1)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", + "tcc", "tuu"); - to_svg("LINESTRING(15 5,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)", - "POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))", - "ls_poly1.svg"); - to_svg("LINESTRING(15 3,15 5,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)", - "POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))", - "ls_poly2.svg"); - to_svg("LINESTRING(15 7,15 5,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)", - "POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))", - "ls_poly3.svg"); - to_svg("LINESTRING(15 5,15 7,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)", - "POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))", - "ls_poly4.svg"); - to_svg("LINESTRING(15 5,15 3,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)", - "POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))", - "ls_poly5.svg"); + // true hole + test_geometry("LINESTRING(9 1,10 5,9 9)", + "POLYGON((0 0,0 10,10 10,10 5,10 0,0 0)(2 2,10 5,2 8,2 2))", + expected("tiu")("tiu").vec); + test_geometry("LINESTRING(10 1,10 5,10 9)", + "POLYGON((0 0,0 10,10 10,10 5,10 0,0 0)(2 2,10 5,2 8,2 2))", + expected("mcu")("ecc")("tiu")("mxc").vec); - to_svg("POLYGON((0 0,5 5,0 10,20 10,20 2,19 0,0 0)(10 3,15 3,15 7,10 7,10 3))", - "LINESTRING(15 5,24 5,20 2,19 0,13 -4,1 0,10 0,13 3,15 7,16 10,10 10,8 10,4 6,2 8,1 10)", - "poly_ls.svg"); + // fake hole + test_geometry("LINESTRING(9 1,10 5,9 9)", + "POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))", + expected("tuu")("tiu").vec); + test_geometry("LINESTRING(10 1,10 5,10 9)", + "POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))", + expected("mcu")("tuc")("tcu")("mxc").vec); + + test_geometry("LINESTRING(10 1,10 5,2 2)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", + expected("mcu")("mic")("tcu")("txc")); + test_geometry("LINESTRING(10 1,10 5,2 8)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", + expected("mcu")("mic")("tcc")("txu")); } int test_main(int, char* []) @@ -85,142 +91,3 @@ int test_main(int, char* []) #endif return 0; } - -/* -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(1 0,3 0)", "lsls0000.svg"); -to_svg("LINESTRING(1 0,3 0)", "LINESTRING(2 0,0 0)", "lsls0001.svg"); -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(3 0,1 0)", "lsls0002.svg"); - -to_svg("LINESTRING(0 0,1 0)", "LINESTRING(1 0,2 0)", "lsls0003.svg"); -to_svg("LINESTRING(0 0,1 0)", "LINESTRING(2 0,1 0)", "lsls0004.svg"); -to_svg("LINESTRING(1 0,2 0)", "LINESTRING(1 0,0 0)", "lsls0005.svg"); - -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(0 0,2 0)", "lsls0006.svg"); -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(2 0,0 0)", "lsls0007.svg"); - -to_svg("LINESTRING(0 0,3 0)", "LINESTRING(1 0,2 0)", "lsls0008.svg"); -to_svg("LINESTRING(0 0,3 0)", "LINESTRING(2 0,1 0)", "lsls0009.svg"); - -to_svg("LINESTRING(0 0,1 0)", "LINESTRING(1 0,1 1)", "lsls00010.svg"); -to_svg("LINESTRING(0 0,1 0)", "LINESTRING(1 1,1 0)", "lsls00011.svg"); -to_svg("LINESTRING(0 0,1 0)", "LINESTRING(0 0,0 1)", "lsls00012.svg"); -to_svg("LINESTRING(0 0,1 0)", "LINESTRING(0 1,0 0)", "lsls00013.svg"); -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(1 0,1 1)", "lsls00014.svg"); -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(1 1,1 0)", "lsls00015.svg"); - -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(1 0,3 1)", "lsls00016.svg"); - -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(2 0,1 0)", "lsls00017.svg"); -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(0 0,1 0)", "lsls00018.svg"); -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(1 0,0 0)", "lsls00019.svg"); -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(1 0,2 0)", "lsls00020.svg"); - -to_svg("LINESTRING(0 0,2 0)", "LINESTRING(0 0,2 0)", "lsls000.svg"); -to_svg("LINESTRING(0 0,2 0,2 0,3 0)", "LINESTRING(0 0,2 0)", "lsls001.svg"); -to_svg("LINESTRING(1 0,1 1)", "LINESTRING(0 0,1 0,2 0)", "lsls0020.svg"); -to_svg("LINESTRING(1 0,0 0)", "LINESTRING(0 0,1 0,2 0)", "lsls0021.svg"); -to_svg("LINESTRING(1 0,2 0)", "LINESTRING(0 0,1 0,2 0)", "lsls0022.svg"); -to_svg("LINESTRING(1 1,1 0)", "LINESTRING(0 0,1 0,2 0)", "lsls0023.svg"); -to_svg("LINESTRING(0 0,1 0)", "LINESTRING(0 0,1 0,2 0)", "lsls0024.svg"); -to_svg("LINESTRING(2 0,1 0)", "LINESTRING(0 0,1 0,2 0)", "lsls0025.svg"); -to_svg("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,1 1)", "lsls00200.svg"); -to_svg("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,0 0)", "lsls00211.svg"); -to_svg("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 0,2 0)", "lsls00222.svg"); -to_svg("LINESTRING(0 0,1 0,2 0)", "LINESTRING(1 1,1 0)", "lsls00233.svg"); -to_svg("LINESTRING(0 0,1 0,2 0)", "LINESTRING(0 0,1 0)", "lsls00244.svg"); -to_svg("LINESTRING(0 0,1 0,2 0)", "LINESTRING(2 0,1 0)", "lsls00255.svg"); - -to_svg("LINESTRING(0 0,2 0,4 0,6 0,8 0)", "LINESTRING(1 0,3 0,5 0,6 0,9 0)", "lsls01.svg"); -to_svg("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(1 0,3 0,4 0,4 2,4 5)", "lsls02.svg"); -to_svg("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(1 0,4 0,4 4)", "lsls031.svg"); -to_svg("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,2 0,0 0)", "lsls032.svg"); -to_svg("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,2 2,0 2)", "lsls0321.svg"); -to_svg("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,2 0)", "lsls033.svg"); -to_svg("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,4 4)", "lsls034.svg"); -to_svg("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,3 1)", "lsls035.svg"); -to_svg("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(4 0,4 -1)", "lsls036.svg"); -to_svg("LINESTRING(0 0,2 0,4 0,4 4)", "LINESTRING(1 0,4 0,4 3)", "lsls04.svg"); -to_svg("LINESTRING(1 0,2 0,4 0,6 0,8 0)", "LINESTRING(0 0,3 0,5 0,6 0,9 0)", "lsls05.svg"); - -to_svg("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,10 9)", "lsls061.svg"); -to_svg("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,10 -9)", "lsls062.svg"); -to_svg("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,-10 9)", "lsls063.svg"); -to_svg("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,-10 -9)", "lsls064.svg"); -to_svg("LINESTRING(0 0,1 0,10 9,10 10)", "LINESTRING(1 0,10 9)", "lsls065.svg"); -to_svg("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,9 9)", "lsls071.svg"); -to_svg("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,9 -9)", "lsls072.svg"); -to_svg("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,-9 9)", "lsls073.svg"); -to_svg("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 0,-9 -9)", "lsls074.svg"); -to_svg("LINESTRING(0 0,1 0,10 0,10 10)", "LINESTRING(1 0,9 9)", "lsls081.svg"); -to_svg("LINESTRING(0 0,1 0,10 0,10 10)", "LINESTRING(0 0,9 9)", "lsls082.svg"); -to_svg("LINESTRING(0 0,1 0)", "LINESTRING(1 0,9 9)", "lsls083.svg"); -to_svg("LINESTRING(0 0,1 0)", "LINESTRING(9 9,1 0)", "lsls084.svg"); -to_svg("LINESTRING(0 0,1 0)", "LINESTRING(1 0,2 0)", "lsls085.svg"); -to_svg("LINESTRING(0 0,1 0)", "LINESTRING(2 0,1 0)", "lsls086.svg"); -to_svg("LINESTRING(0 0,10 0,10 10)", "LINESTRING(1 1,10 5)", "lsls091.svg"); -to_svg("LINESTRING(0 0,10 0,10 5,10 10)", "LINESTRING(1 1,10 5)", "lsls092.svg"); -to_svg("LINESTRING(0 0,10 0,10 10)", "LINESTRING(19 1,10 5)", "lsls093.svg"); -to_svg("LINESTRING(0 0,10 0,10 5,10 10)", "LINESTRING(19 1,10 5)", "lsls094.svg"); - -to_svg("LINESTRING(5 3,1 1,3 3,2 2,0 0)", "LINESTRING(0 0,3 3,6 3)", "1F100F10T.svg"); - -to_svg("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 1,1 0,3 0,3 1)", "lsls_01.svg"); -to_svg("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 -1,1 0,3 0,3 -1)", "lsls_02.svg"); -to_svg("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 1,3 0,1 0,1 1)", "lsls_03.svg"); -to_svg("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 -1,3 0,1 0,1 -1)", "lsls_04.svg"); -to_svg("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 1,2 0,4 0,4 1)", "lsls_05.svg"); -to_svg("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 -1,2 0,4 0,4 -1)", "lsls_06.svg"); -to_svg("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 1,4 0,2 0,2 1)", "lsls_07.svg"); -to_svg("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 -1,4 0,2 0,2 -1)", "lsls_08.svg"); - -to_svg("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 1,1 0,2 0,3 0,3 1)", "lsls_11.svg"); -to_svg("LINESTRING(0 0,2 0,4 0)", "LINESTRING(1 -1,1 0,2 0,3 0,3 -1)", "lsls_12.svg"); -to_svg("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 1,3 0,2 0,1 0,1 1)", "lsls_13.svg"); -to_svg("LINESTRING(0 0,2 0,4 0)", "LINESTRING(3 -1,3 0,2 0,1 0,1 -1)", "lsls_14.svg"); -to_svg("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 1,2 0,3 0,4 0,4 1)", "lsls_15.svg"); -to_svg("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(2 -1,2 0,3 0,4 0,4 -1)", "lsls_16.svg"); -to_svg("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 1,4 0,3 0,2 0,2 1)", "lsls_17.svg"); -to_svg("LINESTRING(0 0,2 0,3 0,4 0,6 0)", "LINESTRING(4 -1,4 0,3 0,2 0,2 -1)", "lsls_18.svg"); - -to_svg("LINESTRING(0 5,5 5,10 5,10 10,5 10,5 5,5 0)", "LINESTRING(0 5,5 5,5 10,10 10,10 5,5 5,5 0)", "lsls11.svg"); -to_svg("LINESTRING(0 5,5 5,10 5,10 10,5 10,5 5,5 0)", "LINESTRING(5 0,5 5,10 5,10 10,5 10,5 5,0 5)", "lsls12.svg"); -to_svg("LINESTRING(5 0,5 5,5 10,10 10,10 5,5 5,0 5)", "LINESTRING(0 5,5 5,5 10,10 10,10 5,5 5,5 0)", "lsls13.svg"); -to_svg("LINESTRING(5 0,5 5,5 10,10 10,10 5,5 5,0 5)", "LINESTRING(5 0,5 5,10 5,10 10,5 10,5 5,0 5)", "lsls14.svg"); - -to_svg("LINESTRING(0 5,10 5,10 10,5 10,5 0)", "LINESTRING(0 5,5 5,5 10,10 10,10 5,5 5,5 0)", "lsls15.svg"); -to_svg("LINESTRING(0 5,10 5,10 10,5 10,5 0)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls16.svg"); -to_svg("LINESTRING(0 5,10 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls161.svg"); -to_svg("LINESTRING(0 5,8 5,10 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls162.svg"); -to_svg("LINESTRING(0 5,8 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1631.svg"); -to_svg("LINESTRING(0 5,1 5,7 5,8 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1632.svg"); -to_svg("LINESTRING(0 5,1 5,7 5,8 5)", "LINESTRING(5 10,10 10,10 5,0 5)", "lsls1633.svg"); -to_svg("LINESTRING(0 5,8 4)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1641.svg"); -to_svg("LINESTRING(0 5,8 6)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1642.svg"); -to_svg("LINESTRING(1 5,8 4)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1643.svg"); -to_svg("LINESTRING(1 5,8 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls1644.svg"); -to_svg("LINESTRING(0 5,5 5,8 4)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls165.svg"); -to_svg("LINESTRING(0 5,5 5,8 5)", "LINESTRING(5 0,5 10,10 10,10 5,0 5)", "lsls166.svg"); -to_svg("LINESTRING(0 5,5 5,8 5)", "LINESTRING(0 10,10 0,5 0,5 10,10 10,10 5,0 5)", "lsls167.svg"); -to_svg("LINESTRING(0 5,5 5,8 5)", "LINESTRING(0 10,5 5,10 0,5 0,5 5,5 10,10 10,10 5,0 5)", "lsls168.svg"); - -to_svg("LINESTRING(0 0,0 10,10 10,10 0,0 0)", "LINESTRING(0 2,0 0,10 0,10 10,0 10,0 8,0 2)", "lsls1690.svg"); -to_svg("LINESTRING(0 0,10 0,10 10,0 10,0 0)", "LINESTRING(0 8,0 0,10 0,10 10,0 10,0 8)", "lsls1691.svg"); -to_svg("LINESTRING(0 0,10 0,10 10,0 10,0 0)", "LINESTRING(0 2,0 0,10 0,10 10,0 10,0 8)", "lsls1692.svg"); -to_svg("LINESTRING(0 0,0 10,10 10,10 0,0 0)", "LINESTRING(0 2,0 0,10 0,10 10,0 10,0 8)", "lsls1693.svg"); -to_svg("LINESTRING(0 2,0 0,10 0,10 10,0 10,0 8)", "LINESTRING(0 0,10 0,10 10,0 10,0 0)", "lsls1694.svg"); -to_svg("LINESTRING(0 2,0 0,10 0,10 10,0 10,0 8)", "LINESTRING(0 0,0 10,10 10,10 0,0 0)", "lsls1695.svg"); - -to_svg("LINESTRING(0 8,0 0,10 0,10 10,0 10,0 2)", "ls1.svg"); -to_svg("LINESTRING(8 8,0 0,10 0,10 10,0 10,8 2)", "ls2.svg"); - -typedef bg::model::multi_linestring mls; -to_svg("LINESTRING(0 5,10 5,10 10,5 10,5 0)", "MULTILINESTRING((5 0,5 7),(5 8,5 10,10 10,10 5,0 5))", "lsls17.svg"); -to_svg("LINESTRING(0 5,10 5,10 10,5 10,5 0)", "MULTILINESTRING((5 0,5 4,5 6,5 7),(5 8,5 10,10 10,10 5,0 5))", "lsls18.svg"); -to_svg("LINESTRING(0 5,10 5,10 10,5 10,5 0)", "MULTILINESTRING((5 0,5 8),(5 7,5 10,10 10,10 5,0 5))", "lsls19.svg"); -to_svg("MULTILINESTRING((5 0,5 7),(5 8,5 10,10 10,10 5,0 5))", "LINESTRING(0 5,10 5,10 10,5 10,5 0)", "lsls20.svg"); -to_svg("MULTILINESTRING((5 0,5 8),(5 7,5 10,10 10,10 5,0 5))", "LINESTRING(0 5,10 5,10 10,5 10,5 0)", "lsls21.svg"); - -to_svg("LINESTRING(0 5,5 5,10 5,10 10,5 10,5 5,5 0)", "LINESTRING(0 5,5 5,0 10,10 10,10 5,5 5,5 0)", "lsls100.svg"); - -to_svg("LINESTRING(5 0,5 5,5 0)", "LINESTRING(0 5,5 5,0 10,2 10,5 5,5 10,10 10,10 5,5 5,10 2,10 0,8 0,5 5,5 0)", "lsls101.svg"); -*/ diff --git a/test/algorithms/overlay/get_turns_linear_linear.cpp b/test/algorithms/overlay/get_turns_linear_linear.cpp index 3190d5d84..ebaff5128 100644 --- a/test/algorithms/overlay/get_turns_linear_linear.cpp +++ b/test/algorithms/overlay/get_turns_linear_linear.cpp @@ -78,16 +78,16 @@ void test_all() test_geometry("LINESTRING(-1 1,0 0,1 0,4 0,5 5,10 5,15 0,31 0)", "LINESTRING(-1 -1,0 0,1 0,2 0,2.5 1,3 0,30 0)", - expected("tii")("ecc")("muu")("mii")("muu")("mii")("mux").vec); + expected("tii")("ecc")("muu")("mii")("muu")("mii")("mux")); test_geometry("LINESTRING(-1 1,0 0,1 0,4 0,5 5,10 5,15 0,31 0)", "LINESTRING(30 0,3 0,2.5 1,2 0,1 0,0 0,-1 -1)", - expected("tiu")("ecc")("mui")("miu")("mui")("miu")("mui").vec); + expected("tiu")("ecc")("mui")("miu")("mui")("miu")("mui")); test_geometry("LINESTRING(31 0,15 0,10 5,5 5,4 0,1 0,0 0,-1 1)", "LINESTRING(-1 -1,0 0,1 0,2 0,2.5 1,3 0,30 0)", - expected("tui")("ecc")("miu")("mui")("miu")("mui")("mix").vec); + expected("tui")("ecc")("miu")("mui")("miu")("mui")("mix")); test_geometry("LINESTRING(31 0,15 0,10 5,5 5,4 0,1 0,0 0,-1 1)", "LINESTRING(30 0,3 0,2.5 1,2 0,1 0,0 0,-1 -1)", - expected("tuu")("ecc")("mii")("muu")("mii")("muu")("mii").vec); + expected("tuu")("ecc")("mii")("muu")("mii")("muu")("mii")); test_geometry("LINESTRING(-1 0,1 0,2 1,3 2)", "LINESTRING(4 5,3 2,1 0,0 0)", "mix", "txi", "ecc"); test_geometry("LINESTRING(4 5,3 2,1 0,0 0)", "LINESTRING(-1 0,1 0,2 1,3 2)", "mxi", "tix", "ecc"); @@ -99,9 +99,14 @@ void test_all() test_geometry("LINESTRING(4 0,4 1,20 1,5 0,1 0)", "LINESTRING(0 0,30 0)", "muu", "miu", "mxi"); test_geometry("LINESTRING(30 0,0 0)", "LINESTRING(1 0,5 0,20 1,4 1,4 0,5 0)", - expected("mui")("miu")("mui")("mix").vec); + expected("mui")("miu")("mui")("mix")); test_geometry("LINESTRING(1 0,5 0,20 1,4 1,4 0,5 0)", "LINESTRING(30 0,0 0)", - expected("miu")("mui")("miu")("mxi").vec); + expected("miu")("mui")("miu")("mxi")); + + test_geometry("LINESTRING(1 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,4 -1)", + expected("mii")("iuu")("muu")); + test_geometry("LINESTRING(1 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,5 0,4 1)", + expected("mii")("muu")("muu")); //if ( boost::is_same::value ) //{ diff --git a/test/algorithms/overlay/test_get_turns.hpp b/test/algorithms/overlay/test_get_turns.hpp index d22de2cfa..4ca5198d3 100644 --- a/test/algorithms/overlay/test_get_turns.hpp +++ b/test/algorithms/overlay/test_get_turns.hpp @@ -49,13 +49,13 @@ struct equal_turn const std::string * turn_ptr; }; -template -void check_geometry( +template +void check_geometry_range( Geometry1 const& g1, Geometry2 const& g2, std::string const& wkt1, std::string const& wkt2, - std::vector const& expected) + Range const& expected) { typedef bg::detail::overlay::turn_info < @@ -77,16 +77,17 @@ void check_geometry( turn_policy_t >::apply(0, g1, 1, g2, bg::detail::no_rescale_policy(), turns, interrupt_policy); - bool ok = expected.size() == turns.size(); + bool ok = boost::size(expected) == turns.size(); BOOST_CHECK_MESSAGE(ok, "get_turns: " << wkt1 << " and " << wkt2 - << " -> Expected turns #: " << expected.size() << " detected turns #: " << turns.size()); + << " -> Expected turns #: " << boost::size(expected) << " detected turns #: " << turns.size()); - BOOST_FOREACH(std::string const& s, expected) + for ( typename boost::range_iterator::type sit = boost::begin(expected) ; + sit != boost::end(expected) ; ++sit) { typename std::vector::iterator - it = std::find_if(turns.begin(), turns.end(), equal_turn(s)); + it = std::find_if(turns.begin(), turns.end(), equal_turn(*sit)); if ( it != turns.end() ) turns.erase(it); @@ -94,20 +95,20 @@ void check_geometry( { BOOST_CHECK_MESSAGE(false, "get_turns: " << wkt1 << " and " << wkt2 - << " -> Expected turn: " << s << " not found"); + << " -> Expected turn: " << *sit << " not found"); } } } -template -void test_geometry(std::string const& wkt1, std::string const& wkt2, - std::vector const& expected) +template +void test_geometry_range(std::string const& wkt1, std::string const& wkt2, + Range const& expected) { Geometry1 geometry1; Geometry2 geometry2; bg::read_wkt(wkt1, geometry1); bg::read_wkt(wkt2, geometry2); - check_geometry(geometry1, geometry2, wkt1, wkt2, expected); + check_geometry_range(geometry1, geometry2, wkt1, wkt2, expected); } template @@ -116,7 +117,7 @@ void test_geometry(std::string const& wkt1, std::string const& wkt2, { std::vector expected; expected.push_back(ex0); - test_geometry(wkt1, wkt2, expected); + test_geometry_range(wkt1, wkt2, expected); } template @@ -126,7 +127,7 @@ void test_geometry(std::string const& wkt1, std::string const& wkt2, std::vector expected; expected.push_back(ex0); expected.push_back(ex1); - test_geometry(wkt1, wkt2, expected); + test_geometry_range(wkt1, wkt2, expected); } template @@ -137,7 +138,7 @@ void test_geometry(std::string const& wkt1, std::string const& wkt2, expected.push_back(ex0); expected.push_back(ex1); expected.push_back(ex2); - test_geometry(wkt1, wkt2, expected); + test_geometry_range(wkt1, wkt2, expected); } struct expected_pusher @@ -147,6 +148,15 @@ struct expected_pusher vec.push_back(ex); return *this; } + + typedef std::vector::iterator iterator; + typedef std::vector::const_iterator const_iterator; + + //iterator begin() { return vec.begin(); } + //iterator end() { return vec.end(); } + const_iterator begin() const { return vec.begin(); } + const_iterator end() const { return vec.end(); } + std::vector vec; }; @@ -156,4 +166,18 @@ expected_pusher expected(std::string const& ex) return res(ex); } +template +void test_geometry(std::string const& wkt1, std::string const& wkt2, + std::vector const& expected) +{ + test_geometry_range(wkt1, wkt2, expected); +} + +template +void test_geometry(std::string const& wkt1, std::string const& wkt2, + expected_pusher const& expected) +{ + test_geometry_range(wkt1, wkt2, expected); +} + #endif // BOOST_GEOMETRY_TEST_ALGORITHMS_OVERLAY_TEST_GET_TURNS_HPP diff --git a/test/algorithms/relate.cpp b/test/algorithms/relate.cpp index 25fd490f2..5a9ac467d 100644 --- a/test/algorithms/relate.cpp +++ b/test/algorithms/relate.cpp @@ -37,6 +37,7 @@ #include #include #include +#include //TEST #include @@ -59,9 +60,7 @@ void check_geometry(Geometry1 const& geometry1, std::string const& expected) { { - bgdr::result res; - bgdr::relate(geometry1, geometry2, res); - std::string res_str(boost::begin(res.get_code()), boost::end(res.get_code())); + std::string res_str = bgdr::relate(geometry1, geometry2); bool ok = boost::equal(res_str, expected); BOOST_CHECK_MESSAGE(ok, "relate: " << wkt1 @@ -72,9 +71,7 @@ void check_geometry(Geometry1 const& geometry1, // changed sequence of geometries - transposed result { - bgdr::result res; - bgdr::relate(geometry2, geometry1, res); - std::string res_str(boost::begin(res.get_code()), boost::end(res.get_code())); + std::string res_str = bgdr::relate(geometry2, geometry1, bgdr::matrix9()); std::string expected_tr = transposed(expected); bool ok = boost::equal(res_str, expected_tr); BOOST_CHECK_MESSAGE(ok, @@ -84,20 +81,16 @@ void check_geometry(Geometry1 const& geometry1, << " detected: " << res_str); } - static const bool int_en = bgdr::interruption_enabled::value; - { - bgdr::mask mask(expected); - bgdr::relate(geometry1, geometry2, mask); - std::string res_str(boost::begin(mask.get_code()), boost::end(mask.get_code())); - BOOST_CHECK_MESSAGE((!mask.interrupt && mask.check()), + bool result = bgdr::relate(geometry1, geometry2, bgdr::mask9(expected)); + // TODO: SHOULD BE !interrupted - CHECK THIS! + BOOST_CHECK_MESSAGE(result, "relate: " << wkt1 << " and " << wkt2 - << " -> Expected: " << expected - << " detected: " << res_str); + << " -> Expected: " << expected); } - if ( int_en ) + if ( bg::detail::relate::interruption_enabled::value ) { // brake the expected output std::string expected_interrupt = expected; @@ -117,14 +110,12 @@ void check_geometry(Geometry1 const& geometry1, if ( changed ) { - bgdr::mask mask(expected_interrupt); - bgdr::relate(geometry1, geometry2, mask); - std::string res_str(boost::begin(mask.get_code()), boost::end(mask.get_code())); - BOOST_CHECK_MESSAGE(mask.interrupt, + bool result = bgdr::relate(geometry1, geometry2, bgdr::mask9(expected_interrupt)); + // TODO: SHOULD BE interrupted - CHECK THIS! + BOOST_CHECK_MESSAGE(!result, "relate: " << wkt1 << " and " << wkt2 - << " -> Expected interrupt for:" << expected_interrupt - << " detected: " << res_str); + << " -> Expected interrupt for:" << expected_interrupt); } } } @@ -300,6 +291,28 @@ void test_linestring_linestring() "LINESTRING(30 0,4 0,3 1,2 0,1 0,0 0,-1 -1)", "101FF0102"); + // self-IP + test_geometry("LINESTRING(1 0,9 0)", + "LINESTRING(0 0,10 0,10 10,5 0,0 10)", + "1FF0FF102"); + test_geometry("LINESTRING(1 0,5 0,9 0)", + "LINESTRING(0 0,10 0,10 10,5 0,0 10)", + "1FF0FF102"); + test_geometry("LINESTRING(1 0,9 0)", + "LINESTRING(0 0,10 0,10 10,5 10,5 -1)", + "1FF0FF102"); + test_geometry("LINESTRING(1 0,9 0)", + "LINESTRING(0 0,10 0,5 0,5 5)", + "1FF0FF102"); + test_geometry("LINESTRING(1 0,7 0)", "LINESTRING(0 0,10 0,10 10,4 -1)", + "1FF0FF102"); + test_geometry("LINESTRING(1 0,5 0,7 0)", "LINESTRING(0 0,10 0,10 10,4 -1)", + "1FF0FF102"); + test_geometry("LINESTRING(1 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,4 -1)", + "1F10F0102"); + test_geometry("LINESTRING(1 0,5 0,7 0,8 1)", "LINESTRING(0 0,10 0,10 10,4 -1)", + "1F10F0102"); + // linear ring test_geometry("LINESTRING(0 0,10 0)", "LINESTRING(5 0,9 0,5 5,1 0,5 0)", "1F1FF01F2"); test_geometry("LINESTRING(0 0,5 0,10 0)", "LINESTRING(5 0,9 0,5 5,1 0,5 0)", "1F1FF01F2"); @@ -318,6 +331,24 @@ void test_linestring_linestring() //test_geometry("LINESTRING(0 0,5 0)", "LINESTRING(1 0,1 0,1 0)", "0F1FF0FF2"); // Point/Point //test_geometry("LINESTRING(0 0)", "LINESTRING(0 0)", "0FFFFFFF2"); + + // OTHER MASKS + { + namespace bgdr = bg::detail::relate; + ls ls1, ls2, ls3; + bg::read_wkt("LINESTRING(0 0,2 0)", ls1); + bg::read_wkt("LINESTRING(2 0,4 0)", ls2); + bg::read_wkt("LINESTRING(1 0,1 1)", ls3); + BOOST_CHECK(bgdr::relate(ls1, ls2, bgdr::mask9("FT*******") + || bgdr::mask9("F**T*****") + || bgdr::mask9("F***T****"))); + BOOST_CHECK(bgdr::relate(ls1, ls3, bgdr::mask9("FT*******") + || bgdr::mask9("F**T*****") + || bgdr::mask9("F***T****"))); + BOOST_CHECK(bgdr::relate(ls3, ls1, bgdr::mask9("FT*******") + || bgdr::mask9("F**T*****") + || bgdr::mask9("F***T****"))); + } } template @@ -344,6 +375,184 @@ void test_linestring_multi_linestring() //test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,9 0),(2 0,2 0,2 0))", "101FF0FF2"); } +template +void test_linestring_polygon() +{ + typedef bg::model::linestring

ls; + typedef bg::model::polygon

poly; + + // LS disjoint + test_geometry("LINESTRING(11 0,11 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "FF1FF0212"); + + // II BB + test_geometry("LINESTRING(0 0,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "1FFF0F212"); + test_geometry("LINESTRING(5 0,5 5,10 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "1FFF0F212"); + test_geometry("LINESTRING(5 1,5 5,9 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "1FF0FF212"); + + // IE + test_geometry("LINESTRING(11 1,11 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "FF1FF0212"); + // IE IB0 + test_geometry("LINESTRING(11 1,10 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "FF1F00212"); + // IE IB1 + test_geometry("LINESTRING(11 1,10 5,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212"); + test_geometry("LINESTRING(11 1,10 10,0 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212"); + test_geometry("LINESTRING(11 1,10 0,0 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212"); + test_geometry("LINESTRING(0 -1,1 0,2 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212"); + // IE IB0 II + test_geometry("LINESTRING(11 1,10 5,5 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "1010F0212"); + // IE IB0 lring + test_geometry("LINESTRING(11 1,10 5,11 5,11 1)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F01FFF212"); + // IE IB1 lring + test_geometry("LINESTRING(11 1,10 5,10 10,11 5,11 1)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11FFF212"); + + // IB1 II + test_geometry("LINESTRING(0 0,5 0,5 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "11F00F212"); + // BI0 II IB1 + test_geometry("LINESTRING(5 0,5 5,10 5,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "11FF0F212"); + + // IB1 II IB1 + test_geometry("LINESTRING(1 0,2 0,3 1,4 0,5 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "11FF0F212"); + // IB1 IE IB1 + test_geometry("LINESTRING(1 0,2 0,3 -1,4 0,5 0)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F0F212"); + + // II IB1 + test_geometry("LINESTRING(5 5,10 5,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "11F00F212"); + // IB1 II + test_geometry("LINESTRING(10 10,10 5,5 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "11F00F212"); + // IE IB1 + test_geometry("LINESTRING(15 5,10 5,10 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212"); + // IB1 IE + test_geometry("LINESTRING(10 10,10 5,15 5)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "F11F00212"); + + // II IB0 IE + test_geometry("LINESTRING(5 5,10 5,15 10)", "POLYGON((0 0,0 10,10 10,10 0,0 0))", "1010F0212"); + + // non-simple polygon with hole + test_geometry("LINESTRING(9 1,10 5,9 9)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", + "10F0FF212"); + test_geometry("LINESTRING(10 1,10 5,10 9)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", + "F1FF0F212"); + test_geometry("LINESTRING(2 8,10 5,2 2)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", + "F1FF0F212"); + test_geometry("LINESTRING(10 1,10 5,2 2)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", + "F1FF0F212"); + test_geometry("LINESTRING(10 1,10 5,2 8)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", + "F1FF0F212"); + + // non-simple polygon with hole, linear ring + test_geometry("LINESTRING(9 1,10 5,9 9,1 9,1 1,9 1)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", + "10FFFF212"); + test_geometry("LINESTRING(10 5,10 9,11 5,10 1,10 5)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", + "F11FFF212"); + test_geometry("LINESTRING(11 5,10 1,10 5,10 9,11 5)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5))", + "F11FFF212"); + + // non-simple polygon with self-touching holes + test_geometry("LINESTRING(7 1,8 5,7 9)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(8 1,9 1,9 9,8 9,8 1),(2 2,8 5,2 8,2 2))", + "10F0FF212"); + test_geometry("LINESTRING(8 2,8 5,8 8)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(8 1,9 1,9 9,8 9,8 1),(2 2,8 5,2 8,2 2))", + "F1FF0F212"); + test_geometry("LINESTRING(2 8,8 5,2 2)", + "POLYGON((0 0,0 10,10 10,10 0,0 0),(8 1,9 1,9 9,8 9,8 1),(2 2,8 5,2 8,2 2))", + "F1FF0F212"); + + // non-simple polygon self-touching + test_geometry("LINESTRING(9 1,10 5,9 9)", + "POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))", + "10F0FF212"); + test_geometry("LINESTRING(10 1,10 5,10 9)", + "POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))", + "F1FF0F212"); + test_geometry("LINESTRING(2 8,10 5,2 2)", + "POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))", + "F1FF0F212"); + + // non-simple polygon self-touching, linear ring + test_geometry("LINESTRING(9 1,10 5,9 9,1 9,1 1,9 1)", + "POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))", + "10FFFF212"); + test_geometry("LINESTRING(10 5,10 9,11 5,10 1,10 5)", + "POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))", + "F11FFF212"); + test_geometry("LINESTRING(11 5,10 1,10 5,10 9,11 5)", + "POLYGON((0 0,0 10,10 10,10 5,2 8,2 2,10 5,10 0,0 0))", + "F11FFF212"); + + // polygons with exterior ring equals the linestring + test_geometry("LINESTRING(0 0,10 0,10 10,0 10,0 0)", + "POLYGON((0 0,0 10,10 10,10 0,0 0))", + "F1FFFF2F2"); + to_svg("LINESTRING(0 0,10 0,10 10,0 10,0 0)", + "POLYGON((0 0,0 10,10 10,10 0,0 0))", + "F1FFFF2F2.svg"); + + // ccw + { + typedef bg::model::polygon ccwpoly; + + // IE IB0 II + test_geometry("LINESTRING(11 1,10 5,5 5)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1010F0212"); + // IE IB1 II + test_geometry("LINESTRING(11 1,10 1,10 5,5 5)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1110F0212"); + test_geometry("LINESTRING(11 1,10 5,10 1,5 5)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1110F0212"); + // II IB0 IE + test_geometry("LINESTRING(5 1,10 5,11 1)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1010F0212"); + // IE IB1 II + test_geometry("LINESTRING(5 5,10 1,10 5,11 5)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1110F0212"); + test_geometry("LINESTRING(5 5,10 5,10 1,11 5)", "POLYGON((0 0,10 0,10 10,0 10,0 0))", "1110F0212"); + + } + +} + +template +void test_linestring_multi_polygon() +{ + typedef bg::model::linestring

ls; + typedef bg::model::polygon

poly; + typedef bg::model::multi_polygon mpoly; + + test_geometry("LINESTRING(10 1,10 5,10 9)", + "MULTIPOLYGON(((0 20,0 30,10 30,10 20,0 20)),((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)))", + "F1FF0F212"); + test_geometry("LINESTRING(10 1,10 5,10 9)", + "MULTIPOLYGON(((0 20,0 30,10 30,10 20,0 20)),((0 0,0 10,10 10,10 0,0 0)))", + "F1FF0F212"); + + test_geometry("LINESTRING(10 1,10 5,2 2)", + "MULTIPOLYGON(((0 20,0 30,10 30,10 20,0 20)),((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)))", + "F1FF0F212"); + test_geometry("LINESTRING(10 1,10 5,2 2)", + "MULTIPOLYGON(((0 20,0 30,10 30,10 20,0 20)),((0 0,0 10,10 10,10 0,0 0)))", + "11F00F212"); + + test_geometry("LINESTRING(10 1,10 5,2 2)", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)),((10 5,3 3,3 7,10 5)))", + "F1FF0F212"); + test_geometry("LINESTRING(10 1,10 5,2 8)", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)),((10 5,3 3,3 7,10 5)))", + "F1FF0F212"); + test_geometry("LINESTRING(10 1,10 5,3 3)", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)),((10 5,3 3,3 7,10 5)))", + "F1FF0F212"); + test_geometry("LINESTRING(10 1,10 5,3 7)", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)),((10 5,3 3,3 7,10 5)))", + "F1FF0F212"); + test_geometry("LINESTRING(10 1,10 5,5 5)", + "MULTIPOLYGON(((0 0,0 10,10 10,10 0,0 0),(10 5,2 8,2 2,10 5)),((10 5,3 3,3 7,10 5)))", + "11F00F212"); +} + template void test_all() { @@ -354,6 +563,8 @@ void test_all() test_point_multilinestring

(); test_linestring_linestring

(); test_linestring_multi_linestring

(); + test_linestring_polygon

(); + test_linestring_multi_polygon

(); } int test_main( int , char* [] )