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 8840952db..b6d5345d5 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 @@ -492,7 +492,8 @@ struct get_turn_info_linear_linear result_ignore_ip0 = !opposite ? // <=> ip_count == 1 || ip_count == 2 && !opposite append0_last : - (append0_last && p0j); + (append0_last && (p0j || q0_last && q1i)); + // NOTE: based on how collinear is calculated for opposite segments if ( append0_first || append0_last ) { @@ -533,7 +534,9 @@ struct get_turn_info_linear_linear result_ignore_ip1 = !opposite ? // <=> ip_count == 2 && !opposite append1_last : - (append1_last && q1j); + (append1_last && (q1j || p1_last && p0i)); + // NOTE: based on how collinear is calculated for opposite segments + // this condition is symmetric to the one above if ( append1_first || append1_last ) { diff --git a/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp new file mode 100644 index 000000000..d5511b988 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp @@ -0,0 +1,184 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014 Oracle and/or its affiliates. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_BOUNDARY_CHECKER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_BOUNDARY_CHECKER_HPP + +#include +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace relate { + +enum boundary_query { boundary_front, boundary_back, boundary_any }; + +template ::type> +class boundary_checker {}; + +template +class boundary_checker +{ + typedef typename point_type::type point_type; + +public: + boundary_checker(Geometry const& g) + : has_boundary( boost::size(g) >= 2 + && !detail::equals::equals_point_point(range::front(g), range::back(g)) ) + , geometry(g) + {} + + template + bool is_endpoint_boundary(point_type const& pt) const + { + BOOST_ASSERT( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) + && detail::equals::equals_point_point(pt, range::front(geometry)) + || (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) + && detail::equals::equals_point_point(pt, range::back(geometry)) ); + return has_boundary; + } + + template + bool is_boundary(point_type const& pt, segment_identifier const& sid) const + { + if ( !has_boundary ) + return false; + + if ( BoundaryQuery == boundary_front ) + { + return sid.segment_index == 0 + && detail::equals::equals_point_point(pt, range::front(geometry)); + } + else if ( BoundaryQuery == boundary_back ) + { + return sid.segment_index + 2 == geometry::num_points(geometry) + && detail::equals::equals_point_point(pt, range::back(geometry)); + } + else if ( BoundaryQuery == boundary_any ) + { + return sid.segment_index == 0 + && detail::equals::equals_point_point(pt, range::front(geometry)) + || sid.segment_index + 2 == geometry::num_points(geometry) + && detail::equals::equals_point_point(pt, range::back(geometry)); + } + + BOOST_ASSERT(false); + return false; + } + +private: + bool has_boundary; + Geometry const& geometry; +}; + +template +class boundary_checker +{ + typedef typename point_type::type point_type; + +public: + boundary_checker(Geometry const& g) + : is_filled(false), geometry(g) + {} + + template + bool is_endpoint_boundary(point_type const& pt) + { + return is_boundary_point(pt); + } + + template + bool is_boundary(point_type const& pt, segment_identifier const& sid) + { + if ( BoundaryQuery == boundary_front ) + { + if ( sid.segment_index != 0 ) + return false; + } + + if ( BoundaryQuery == boundary_back ) + { + if ( sid.segment_index + 2 != geometry::num_points(sub_geometry::get(geometry, sid)) ) + return false; + } + + if ( BoundaryQuery == boundary_any ) + { + if ( sid.segment_index != 0 + && sid.segment_index + 2 != geometry::num_points(sub_geometry::get(geometry, sid)) ) + return false; + } + + return is_boundary_point(pt); + } + +private: + // First call O(NlogN) + // Each next call O(logN) + bool is_boundary_point(point_type const& pt) + { + typedef typename boost::range_size::type size_type; + size_type multi_count = boost::size(geometry); + + if ( multi_count < 1 ) + return false; + + if ( ! is_filled ) + { + //boundary_points.clear(); + boundary_points.reserve(multi_count * 2); + + typedef typename boost::range_iterator::type multi_iterator; + for ( multi_iterator it = boost::begin(geometry) ; + it != boost::end(geometry) ; ++ it ) + { + // empty or point - no boundary + if ( boost::size(*it) < 2 ) + continue; + + // linear ring or point - no boundary + if ( equals::equals_point_point(range::front(*it), range::back(*it)) ) + continue; + + boundary_points.push_back(range::front(*it)); + boundary_points.push_back(range::back(*it)); + } + + std::sort(boundary_points.begin(), boundary_points.end(), geometry::less()); + + is_filled = true; + } + + std::size_t equal_points_count + = boost::size( + std::equal_range(boundary_points.begin(), + boundary_points.end(), + pt, + geometry::less()) + ); + + return equal_points_count % 2 != 0 && equal_points_count > 0; // the number is odd and > 0 + } + + bool is_filled; + // TODO: store references/pointers instead of points? + std::vector boundary_points; + Geometry const& geometry; +}; + +}} // namespace detail::relate +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_BOUNDARY_CHECKER_HPP diff --git a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp index e7bac41b8..0d9f57370 100644 --- a/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/include/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -9,15 +9,18 @@ // 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_LINEAR_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_LINEAR_HPP +#include +#include + #include #include #include -#include - -#include +#include namespace boost { namespace geometry { @@ -25,170 +28,188 @@ namespace boost { namespace geometry #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace relate { -// update_result +enum linestring_kind { linestring_exterior, linestring_point, linestring_closed, linestring_open }; -template -struct update_result_dispatch +template +linestring_kind check_linestring_kind(Linestring const& ls) { - static inline void apply(result & res) + std::size_t count = boost::size(ls); + if ( count == 0 ) + return linestring_exterior; + else if ( count == 1 ) + return linestring_point; + else { - res.template update(); - } -}; - -template -struct update_result_dispatch -{ - static inline void apply(result & res) - { - res.template update(); - } -}; - -template -inline void update_result(result & res) -{ - update_result_dispatch::apply(res); -} - -// boundary_checker - -enum boundary_query { boundary_front, boundary_back, boundary_front_explicit, boundary_back_explicit, boundary_any }; - -template ::type> -class boundary_checker {}; - -template -class boundary_checker -{ - typedef typename point_type::type point_type; - -public: - boundary_checker(Geometry const& g) - : has_boundary(! detail::equals::equals_point_point(range::front(g), range::back(g))) - , geometry(g) - {} - - template - bool is_boundary(point_type const& pt, segment_identifier const& sid) const - { - // TODO: replace with assert? - if ( boost::size(geometry) < 2 ) - return false; - - // TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point? - - if ( !has_boundary ) - return false; - - if ( BoundaryQuery == boundary_front_explicit || BoundaryQuery == boundary_back_explicit ) - return true; - - if ( BoundaryQuery == boundary_front ) - return sid.segment_index == 0 - && detail::equals::equals_point_point(pt, range::front(geometry)); - - if ( BoundaryQuery == boundary_back ) - return sid.segment_index + 2 == geometry::num_points(geometry) - && detail::equals::equals_point_point(pt, range::back(geometry)); - - if ( BoundaryQuery == boundary_any ) - return sid.segment_index == 0 - && detail::equals::equals_point_point(pt, range::front(geometry)) - || sid.segment_index + 2 == geometry::num_points(geometry) - && detail::equals::equals_point_point(pt, range::back(geometry)); - - BOOST_ASSERT(false); - return false; - } - -private: - bool has_boundary; - Geometry const& geometry; -}; - -template -class boundary_checker -{ - typedef typename point_type::type point_type; - -public: - boundary_checker(Geometry const& g) - : is_filled(false), geometry(g) - {} - - template - bool is_boundary(point_type const& pt, segment_identifier const& sid) - { - typedef typename boost::range_size::type size_type; - size_type multi_count = boost::size(geometry); - - // TODO: replace with assert? - if ( multi_count < 1 ) - return false; - - // TODO: for explicit parameters ASSERT could be used - - if ( BoundaryQuery == boundary_front || BoundaryQuery == boundary_front_explicit ) + bool equal_fb = equals::equals_point_point(range::front(ls), range::back(ls)); + if ( equal_fb ) { - if ( sid.segment_index != 0 ) - return false; - } - - if ( BoundaryQuery == boundary_back || BoundaryQuery == boundary_back_explicit ) - { - if ( sid.segment_index + 2 != geometry::num_points(geometry) ) - return false; - } - - if ( BoundaryQuery == boundary_any ) - { - if ( sid.segment_index != 0 && sid.segment_index + 2 != geometry::num_points(geometry) ) - return false; - } - - if ( ! is_filled ) - { - //boundary_points.clear(); - boundary_points.reserve(multi_count * 2); - - typedef typename boost::range_iterator::type multi_iterator; - for ( multi_iterator it = boost::begin(geometry) ; - it != boost::end(geometry) ; ++ it ) + 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 ) { - // point - no boundary - if ( boost::size(*it) < 2 ) - continue; - - // TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point? - - boundary_points.push_back(range::front(*it)); - boundary_points.push_back(range::back(*it)); + if ( !equals::equals_point_point(range::front(ls), *it) ) + return linestring_closed; } - std::sort(boundary_points.begin(), boundary_points.end(), geometry::less()); + return linestring_point; + } + else + return linestring_open; + } +} - is_filled = true; +// 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; } - std::size_t equal_points_count - = boost::size( - std::equal_range(boundary_points.begin(), - boundary_points.end(), - pt, - geometry::less()) - ); + bool found = false; - return equal_points_count % 2 != 0 && equal_points_count > 0; // the number is odd and > 0 - + 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; + } +}; + +// 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 +{ + static const bool transpose_result = OpId != 0; + +public: + disjoint_linestring_pred(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: - bool is_filled; - // TODO: store references/pointers instead of points? - std::vector boundary_points; - Geometry const& geometry; + result * m_result_ptr; + BoundaryChecker * m_boundary_checker_ptr; + const OtherGeometry * m_other_geometry; + char m_detected_mask_point; + bool m_detected_open_boundary; }; // currently works only for linestrings @@ -200,44 +221,8 @@ struct linear_linear static inline result apply(Geometry1 const& geometry1, Geometry2 const& geometry2) { - // TODO: currently this only works for linestrings - std::size_t s1 = boost::size(geometry1); - std::size_t s2 = boost::size(geometry2); - if ( s1 == 0 || s2 == 0 ) - { - // throw on empty output? - return result("FFFFFFFFF"); - } - else if ( s1 == 1 && s2 == 1 ) - { -// TODO : not working for MultiLinestrings -// return point_point::apply(range::front(geometry1), range::front(geometry2)); - BOOST_ASSERT(false); - } - else if ( s1 == 1 /*&& s2 > 1*/ ) - { -// TODO : not working for MultiLinestrings -// return point_geometry::apply(range::front(geometry1), geometry2); - BOOST_ASSERT(false); - } - else if ( s2 == 1 /*&& s1 > 1*/ ) - { -// TODO : not working for MultiLinestrings -// return geometry_point::apply(geometry1, range::front(geometry2)); - BOOST_ASSERT(false); - } - - // TODO: handle also linestrings with points_num == 2 and equals(front, back) - treat like point? - - result res("FFFFFFFFT"); - static const std::size_t dimension = geometry::dimension::value; - if ( dimension < 10 ) - res.template set(); - - // TEMP - //bool has_boundary1 = ! detail::equals::equals_point_point(range::front(geometry1), range::back(geometry1)); - //bool has_boundary2 = ! detail::equals::equals_point_point(range::front(geometry2), range::back(geometry2)); - //handle_boundaries(res, geometry1, geometry2, has_boundary1, has_boundary2); + result res; // FFFFFFFFF + set::value>(res);// FFFFFFFFd, d in [1,9] or T // get and analyse turns typedef typename turns::get_turns::turn_info turn_type; @@ -246,13 +231,20 @@ struct linear_linear turns::get_turns::apply(turns, geometry1, geometry2); - if ( turns.empty() ) - { - return res; - } - boundary_checker boundary_checker1(geometry1); + disjoint_linestring_pred<0, boundary_checker, Geometry2> pred1(res, boundary_checker1, geometry2); + for_each_disjoint_linestring_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); + if ( res.interrupt ) + return res; + boundary_checker boundary_checker2(geometry2); + disjoint_linestring_pred<1, boundary_checker, Geometry1> pred2(res, boundary_checker2, geometry1); + for_each_disjoint_linestring_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); + if ( res.interrupt ) + return res; + + if ( turns.empty() ) + return res; // TODO: turns must be sorted and followed only if it's possible to go out and in on the same point // for linear geometries union operation must be detected which I guess would be quite often @@ -266,6 +258,9 @@ struct linear_linear geometry1, geometry2, boundary_checker1, boundary_checker2); } + + if ( res.interrupt ) + return res; { std::sort(turns.begin(), turns.end(), turns::less_seg_dist_op<0,2,3,1,4,0,1>()); @@ -428,7 +423,7 @@ struct linear_linear { static const std::size_t op_id = OpId; static const std::size_t other_op_id = (OpId + 1) % 2; - static const bool reverse_result = OpId != 0; + static const bool transpose_result = OpId != 0; public: turns_analyser() @@ -474,7 +469,7 @@ struct linear_linear m_exit_watcher.reset_detected_exit(); // not the last IP - update_result(res); + update(res); } // fake exit point, reset state // in reality this will be op == overlay::operation_intersection @@ -495,11 +490,10 @@ struct linear_linear segment_identifier const& prev_seg_id = prev->operations[op_id].seg_id; // if there is a boundary on the last point - if ( boost::size(sub_geometry::get(geometry, prev_seg_id)) > 1 - && boundary_checker.template is_boundary - (range::back(sub_geometry::get(geometry, prev_seg_id)), prev_seg_id) ) + if ( boundary_checker.template is_endpoint_boundary + (range::back(sub_geometry::get(geometry, prev_seg_id))) ) { - update_result(res); + update(res); } } @@ -512,23 +506,23 @@ struct linear_linear bool was_outside = m_exit_watcher.enter(it->point, other_id); // interiors overlaps - update_result(res); + update(res); // going inside on boundary point if ( boundary_checker.template is_boundary(it->point, seg_id) ) { bool other_b = it->operations[other_op_id].operation == overlay::operation_blocked ? - other_boundary_checker.template is_boundary(it->point, other_id) : + other_boundary_checker.template is_endpoint_boundary(it->point) : other_boundary_checker.template is_boundary(it->point, other_id); // it's also the boundary of the other geometry if ( other_b ) { - update_result(res); + update(res); } else { - update_result(res); + update(res); } } // going inside on non-boundary point @@ -537,16 +531,16 @@ struct linear_linear // if we didn't enter in the past, we were outside if ( was_outside && !fake_enter_detected ) { - update_result(res); + update(res); // if it's the first IP then the first point is outside if ( first_in_range ) { // if there is a boundary on the first point - if ( boundary_checker.template is_boundary - (range::front(sub_geometry::get(geometry, seg_id)), seg_id) ) + if ( boundary_checker.template is_endpoint_boundary + (range::front(sub_geometry::get(geometry, seg_id))) ) { - update_result(res); + update(res); } } } @@ -564,20 +558,20 @@ struct linear_linear if ( op_blocked ) { // check if this is indeed the boundary point - if ( boundary_checker.template is_boundary(it->point, seg_id) ) + if ( boundary_checker.template is_endpoint_boundary(it->point) ) { bool other_b = it->operations[other_op_id].operation == overlay::operation_blocked ? - other_boundary_checker.template is_boundary(it->point, other_id) : + other_boundary_checker.template is_endpoint_boundary(it->point) : other_boundary_checker.template is_boundary(it->point, other_id); // it's also the boundary of the other geometry if ( other_b ) { - update_result(res); + update(res); } else { - update_result(res); + update(res); } } } @@ -585,21 +579,21 @@ struct linear_linear // we're outside else { - update_result(res); + update(res); // boundaries don't overlap - just an optimization if ( it->method == overlay::method_crosses ) { - update_result(res); + update(res); // it's the first point in range if ( first_in_range ) { // if there is a boundary on the first point - if ( boundary_checker.template is_boundary - (range::front(sub_geometry::get(geometry, seg_id)), seg_id) ) + if ( boundary_checker.template is_endpoint_boundary + (range::front(sub_geometry::get(geometry, seg_id))) ) { - update_result(res); + update(res); } } } @@ -608,7 +602,7 @@ struct linear_linear { bool this_b = op_blocked ? - boundary_checker.template is_boundary(it->point, seg_id) : + boundary_checker.template is_endpoint_boundary(it->point) : boundary_checker.template is_boundary(it->point, seg_id); // if current IP is on boundary of the geometry @@ -616,41 +610,41 @@ struct linear_linear { bool other_b = it->operations[other_op_id].operation == overlay::operation_blocked ? - other_boundary_checker.template is_boundary(it->point, other_id) : + other_boundary_checker.template is_endpoint_boundary(it->point) : other_boundary_checker.template is_boundary(it->point, other_id); // it's also the boundary of the other geometry if ( other_b ) { - update_result(res); + update(res); } else { - update_result(res); + update(res); } // first IP on the last segment point - this means that the first point is outside if ( first_in_range && op_blocked ) { // if there is a boundary on the first point - if ( boundary_checker.template is_boundary - (range::front(sub_geometry::get(geometry, seg_id)), seg_id) ) + if ( boundary_checker.template is_endpoint_boundary + (range::front(sub_geometry::get(geometry, seg_id))) ) { - update_result(res); + update(res); } } } // boundaries don't overlap else { - update_result(res); + update(res); if ( first_in_range ) { // if there is a boundary on the first point - if ( boundary_checker.template is_boundary - (range::front(sub_geometry::get(geometry, seg_id)), seg_id) ) + if ( boundary_checker.template is_endpoint_boundary + (range::front(sub_geometry::get(geometry, seg_id))) ) { - update_result(res); + update(res); } } } @@ -667,7 +661,7 @@ struct linear_linear || m_last_union ) { // for sure - update_result(res); + update(res); BOOST_ASSERT(first != last); // TODO: ERROR! @@ -675,13 +669,12 @@ struct linear_linear TurnIt prev = last; --prev; segment_identifier const& prev_seg_id = prev->operations[OpId].seg_id; - + // if there is a boundary on the last point - if ( boost::size(sub_geometry::get(geometry, prev_seg_id)) > 1 - && boundary_checker.template is_boundary - (range::back(sub_geometry::get(geometry, prev_seg_id)), prev_seg_id) ) + if ( boundary_checker.template is_endpoint_boundary + (range::back(sub_geometry::get(geometry, prev_seg_id))) ) { - update_result(res); + update(res); } } } @@ -715,6 +708,9 @@ struct linear_linear analyser.apply(res, first, it, last, geometry, other_geometry, boundary_checker, other_boundary_checker); + + if ( res.interrupt ) + return; } analyser.apply(res, first, last, last, diff --git a/include/boost/geometry/algorithms/detail/relate/point_geometry.hpp b/include/boost/geometry/algorithms/detail/relate/point_geometry.hpp index 9e240d2bd..68b5acbfd 100644 --- a/include/boost/geometry/algorithms/detail/relate/point_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/relate/point_geometry.hpp @@ -14,8 +14,8 @@ #include // later change to equal/point_point.hpp #include -#include -#include +//#include +//#include #include @@ -30,104 +30,22 @@ struct point_point { static inline result apply(Point1 const& point1, Point2 const& point2) { + result res; + bool equal = detail::equals::equals_point_point(point1, point2); - if ( equal ) - return result("0FFFFFFFT"); + { + set(res); + set::value>(res); + } else - return result("FF0FFF0FT"); - } -}; - -// NOTE: Those tests should be consistent with within(Point, Box) and covered_by(Point, Box) -// There is no EPS used in those functions, values are compared using < or <= -// so comparing MIN and MAX in the same way should be fine - -template ::value> -struct box_has_interior -{ - static inline bool apply(Box const& box) - { - return geometry::get(box) < geometry::get(box) - && box_has_interior::apply(box); - } -}; - -template -struct box_has_interior -{ - static inline bool apply(Box const&) { return true; } -}; - -// NOTE: especially important here (see the NOTE above). - -template ::value> -struct box_has_equal_min_max -{ - static inline bool apply(Box const& box) - { - return geometry::get(box) == geometry::get(box) - && box_has_equal_min_max::apply(box); - } -}; - -template -struct box_has_equal_min_max -{ - static inline bool apply(Box const&) { return true; } -}; - -template -struct point_box -{ - static inline result apply(Point const& point, Box const& box) - { - if ( geometry::within(point, box) ) // this also means that the box has interior { - return result("0FFFFFTTT"); + set(res); + set(res); + set::value>(res); } - else if ( geometry::covered_by(point, box) ) // point is on the boundary - { - //if ( box_has_interior::apply(box) ) - //{ - // return result("F0FFFFTTT"); - //} - //else if ( box_has_equal_min_max::apply(box) ) // no boundary outside point - //{ - // return result("F0FFFFFFT"); - //} - //else // no interior outside point - //{ - // return result("F0FFFFFTT"); - //} - return result("F0FFFF**T"); - } - else - { - /*if ( box_has_interior::apply(box) ) - { - return result("FF0FFFTTT"); - } - else - { - return result("FF0FFFFTT"); - }*/ - return result("FF0FFF*TT"); - } - } -}; -template -struct box_point -{ - static inline result apply(Box const& box, Point const& point) - { - if ( geometry::within(point, box) ) - return result("0FTFFTFFT"); - else if ( geometry::covered_by(point, box) ) - return result("FF*0F*FFT"); - else - return result("FF*FFT0FT"); + return res; } }; @@ -137,17 +55,31 @@ struct point_geometry { static inline result apply(Point const& point, Geometry const& geometry) { + result res; + int pig = detail::within::point_in_geometry(point, geometry); // TODO: * - if geometry has interior and/or boundary // e.g. isn't 1-point linestring or linear ring or 0-area polygon - if ( pig < 0 ) // not within - return result("FF0FFF**T"); + if ( pig > 0 ) // within + { + set(res); + } else if ( pig == 0 ) - return result("F0FFFF**T"); - else // pig > 0 - within - return result("0FFFFF**T"); + { + set(res); + } + else // pig < 0 - not within + { + set(res); + } + + set(res); // TODO + set(res); // TODO + set::value>(res); + + return res; } }; @@ -157,20 +89,132 @@ struct geometry_point { static inline result apply(Geometry const& geometry, Point const& point) { + result res; + int pig = detail::within::point_in_geometry(point, geometry); // TODO: * - if geometry has interior and/or boundary // e.g. isn't 1-point linestring or linear ring or 0-area polygon - if ( pig < 0 ) // not within - return result("FF*FF*0FT"); + if ( pig > 0 ) // within + { + set(res); + } else if ( pig == 0 ) - return result("FF*0F*FFT"); - else // pig > 0 - within - return result("0F*FF*FFT"); + { + set(res); + } + else // pig < 0 - not within + { + set(res); + } + + set(res); // TODO + set(res); // TODO + set::value>(res); + + return res; } }; +// TODO: rewrite the folowing: + +//// NOTE: Those tests should be consistent with within(Point, Box) and covered_by(Point, Box) +//// There is no EPS used in those functions, values are compared using < or <= +//// so comparing MIN and MAX in the same way should be fine +// +//template ::value> +//struct box_has_interior +//{ +// static inline bool apply(Box const& box) +// { +// return geometry::get(box) < geometry::get(box) +// && box_has_interior::apply(box); +// } +//}; +// +//template +//struct box_has_interior +//{ +// static inline bool apply(Box const&) { return true; } +//}; +// +//// NOTE: especially important here (see the NOTE above). +// +//template ::value> +//struct box_has_equal_min_max +//{ +// static inline bool apply(Box const& box) +// { +// return geometry::get(box) == geometry::get(box) +// && box_has_equal_min_max::apply(box); +// } +//}; +// +//template +//struct box_has_equal_min_max +//{ +// static inline bool apply(Box const&) { return true; } +//}; +// +//template +//struct point_box +//{ +// static inline result apply(Point const& point, Box const& box) +// { +// result res; +// +// if ( geometry::within(point, box) ) // this also means that the box has interior +// { +// return result("0FFFFFTTT"); +// } +// else if ( geometry::covered_by(point, box) ) // point is on the boundary +// { +// //if ( box_has_interior::apply(box) ) +// //{ +// // return result("F0FFFFTTT"); +// //} +// //else if ( box_has_equal_min_max::apply(box) ) // no boundary outside point +// //{ +// // return result("F0FFFFFFT"); +// //} +// //else // no interior outside point +// //{ +// // return result("F0FFFFFTT"); +// //} +// return result("F0FFFF**T"); +// } +// else +// { +// /*if ( box_has_interior::apply(box) ) +// { +// return result("FF0FFFTTT"); +// } +// else +// { +// return result("FF0FFFFTT"); +// }*/ +// return result("FF0FFF*TT"); +// } +// +// return res; +// } +//}; +// +//template +//struct box_point +//{ +// static inline result apply(Box const& box, Point const& point) +// { +// if ( geometry::within(point, box) ) +// return result("0FTFFTFFT"); +// else if ( geometry::covered_by(point, box) ) +// return result("FF*0F*FFT"); +// else +// return result("FF*FFT0FT"); +// } +//}; + }} // namespace detail::relate #endif // DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/algorithms/detail/relate/relate.hpp b/include/boost/geometry/algorithms/detail/relate/relate.hpp index f473a81ae..cf955ce45 100644 --- a/include/boost/geometry/algorithms/detail/relate/relate.hpp +++ b/include/boost/geometry/algorithms/detail/relate/relate.hpp @@ -62,15 +62,15 @@ struct relate : detail::relate::point_point {}; -template -struct relate - : detail::relate::point_box -{}; - -template -struct relate - : detail::relate::box_point -{}; +//template +//struct relate +// : detail::relate::point_box +//{}; +// +//template +//struct relate +// : detail::relate::box_point +//{}; template struct relate @@ -92,6 +92,16 @@ struct relate : detail::relate::linear_linear {}; +template +struct relate + : detail::relate::linear_linear +{}; + +template +struct relate + : detail::relate::linear_linear +{}; + }} // namespace detail_dispatch::relate namespace detail { namespace relate { diff --git a/include/boost/geometry/algorithms/detail/relate/result.hpp b/include/boost/geometry/algorithms/detail/relate/result.hpp index 49155cbdf..b639a08c9 100644 --- a/include/boost/geometry/algorithms/detail/relate/result.hpp +++ b/include/boost/geometry/algorithms/detail/relate/result.hpp @@ -23,58 +23,122 @@ enum field { interior = 0, boundary = 1, exterior = 2 }; class result { public: - result() - { - set('F'); - } - result(const char * str) + static const bool interrupt = false; + + inline result() { - ::memcpy(array, str, 9); + ::memset(array, 'F', 9); } template - char get() const + inline char get() const { return array[F1 * 3 + F2]; } template - void set() + inline void set() { array[F1 * 3 + F2] = V; } - template - void update() - { - BOOST_STATIC_ASSERT('0' <= D && D <= '9'); - char * c = array + F1 * 3 + F2; - if ( D > *c || *c > '9') - *c = D; - } - - void set(char v) - { - ::memset(array, v, 9); - } - - std::pair get_code() const + inline std::pair get_code() const { return std::make_pair(array, array+9); } - void transpose() - { - std::swap(array[1], array[3]); - std::swap(array[2], array[6]); - std::swap(array[5], array[7]); - } - private: char array[9]; }; +template +inline void set(Result & res) +{ + res.template set(); +} + +template +inline void set(Result & res) +{ + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); +} + +template +inline void set(Result & res) +{ + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); + res.template set(); +} + +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(); +} + +template +struct update_result_dispatch +{ + template + static inline void apply(Result & res) + { + update(res); + } +}; + +template +struct update_result_dispatch +{ + template + static inline void apply(Result & res) + { + update(res); + } +}; + +template +inline void update(Result & res) +{ + update_result_dispatch::apply(res); +} + +template +inline Result return_result() +{ + Result res; + set(res); + return res; +} + +template +struct result_dimension +{ + BOOST_STATIC_ASSERT(geometry::dimension::value >= 0); + static const char value + = ( geometry::dimension::value <= 9 ) ? + ( '0' + geometry::dimension::value ) : + 'T'; +}; + }} // namespace detail::relate #endif // DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp b/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp index ea10fc465..9cf4b8a46 100644 --- a/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp +++ b/include/boost/geometry/algorithms/detail/within/point_in_geometry.hpp @@ -83,6 +83,17 @@ template {}; +template +struct point_in_geometry +{ + template static inline + int apply(Point1 const& point1, Point2 const& point2, Strategy const& strategy) + { + boost::ignore_unused_variable_warning(strategy); + return strategy.apply(point1, point2) ? 1 : -1; + } +}; + template struct point_in_geometry { @@ -105,9 +116,11 @@ struct point_in_geometry else return 1; } -// else if ( count == 1 -// && detail::equals::equals_point_point(point, *boost::begin(linestring)) ) -// return 0; + else if ( count == 1 ) + { + if ( detail::equals::equals_point_point(point, *boost::begin(linestring)) ) + return 1; + } return -1; } diff --git a/include/boost/geometry/multi/algorithms/detail/within/point_in_geometry.hpp b/include/boost/geometry/multi/algorithms/detail/within/point_in_geometry.hpp index da8005512..970dfcd7d 100644 --- a/include/boost/geometry/multi/algorithms/detail/within/point_in_geometry.hpp +++ b/include/boost/geometry/multi/algorithms/detail/within/point_in_geometry.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Copyright (c) 2013 Adam Wulkiewicz, Lodz, Poland. -// 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. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -113,6 +113,26 @@ struct point_in_geometry } }; +template +struct point_in_geometry +{ + template static inline + int apply(Point const& point, Geometry const& geometry, Strategy const& strategy) + { + typedef typename boost::range_value::type point_type; + typedef typename boost::range_const_iterator::type iterator; + for ( iterator it = boost::begin(geometry) ; it != boost::end(geometry) ; ++it ) + { + int pip = point_in_geometry::apply(point, *it, strategy); + + if ( pip > 0 ) // inside + return 1; + } + + return -1; // outside + } +}; + }} // namespace detail_dispatch::within #endif // DOXYGEN_NO_DETAIL diff --git a/include/boost/geometry/strategies/agnostic/point_in_point.hpp b/include/boost/geometry/strategies/agnostic/point_in_point.hpp new file mode 100644 index 000000000..b45bad70f --- /dev/null +++ b/include/boost/geometry/strategies/agnostic/point_in_point.hpp @@ -0,0 +1,88 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014 Oracle and/or its affiliates. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +#ifndef BOOST_GEOMETRY_STRATEGY_AGNOSTIC_POINT_IN_POINT_HPP +#define BOOST_GEOMETRY_STRATEGY_AGNOSTIC_POINT_IN_POINT_HPP + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +namespace strategy { namespace within +{ + +template +< + typename Point1, typename Point2 +> +struct point_in_point +{ + static inline bool apply(Point1 const& point1, Point2 const& point2) + { + return detail::equals::equals_point_point(point1, point2); + } +}; + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS + +namespace services +{ + +template +struct default_strategy +{ + typedef strategy::within::point_in_point type; +}; + +template +struct default_strategy +{ + typedef strategy::within::point_in_point::type> type; +}; + +} // namespace services + +#endif + + +}} // namespace strategy::within + + + +#ifndef DOXYGEN_NO_STRATEGY_SPECIALIZATIONS +namespace strategy { namespace covered_by { namespace services +{ + +template +struct default_strategy +{ + typedef strategy::within::point_in_point type; +}; + +template +struct default_strategy +{ + typedef strategy::within::point_in_point::type> type; +}; + +}}} // namespace strategy::covered_by::services +#endif + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_STRATEGY_AGNOSTIC_POINT_IN_POINT_HPP diff --git a/include/boost/geometry/strategies/strategies.hpp b/include/boost/geometry/strategies/strategies.hpp index 3aa9ab00f..70cb2d95f 100644 --- a/include/boost/geometry/strategies/strategies.hpp +++ b/include/boost/geometry/strategies/strategies.hpp @@ -4,6 +4,9 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -11,6 +14,8 @@ // 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_STRATEGIES_STRATEGIES_HPP #define BOOST_GEOMETRY_STRATEGIES_STRATEGIES_HPP @@ -46,6 +51,7 @@ #include #include +#include #include #include diff --git a/test/algorithms/overlay/get_turns_linear_linear.cpp b/test/algorithms/overlay/get_turns_linear_linear.cpp index 57584d41d..89876ab68 100644 --- a/test/algorithms/overlay/get_turns_linear_linear.cpp +++ b/test/algorithms/overlay/get_turns_linear_linear.cpp @@ -207,6 +207,8 @@ void test_all() test_geometry("LINESTRING(0 0,10 0)", "LINESTRING(-1 -1,1 0,10 0,20 -1)", "mii", "txu"); test_geometry("LINESTRING(0 0,10 0)", "LINESTRING(20 -1,10 0,1 0,-1 -1)", "miu", "txi"); + test_geometry("LINESTRING(-1 -1,1 0,10 0,20 -1)", "LINESTRING(0 0,10 0)", "mii", "tux"); + test_geometry("LINESTRING(20 -1,10 0,1 0,-1 -1)", "LINESTRING(0 0,10 0)", "mui", "tix"); 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)", @@ -221,6 +223,20 @@ void test_all() "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); + 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"); + + test_geometry("LINESTRING(30 1,20 1,10 0,0 0)", "LINESTRING(1 1,2 0,3 1,20 1,25 1)", "mix", "tui", "muu"); + test_geometry("LINESTRING(1 1,2 0,3 1,20 1,25 1)", "LINESTRING(30 1,20 1,10 0,0 0)", "mxi", "tiu", "muu"); + + test_geometry("LINESTRING(0 0,30 0)", "LINESTRING(4 0,4 1,20 1,5 0,1 0)", "muu", "mui", "mix"); + 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); + 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); + //if ( boost::is_same::value ) //{ // to_svg("LINESTRING(0 0,1 0,2 0,2.5 0,3 1)", "LINESTRING(0 0,2 0,2.5 0,3 1)", "test11.svg"); diff --git a/test/algorithms/relate.cpp b/test/algorithms/relate.cpp index db040d355..1f0eee59d 100644 --- a/test/algorithms/relate.cpp +++ b/test/algorithms/relate.cpp @@ -176,6 +176,14 @@ void test_linestring_linestring() test_geometry("LINESTRING(0 0,5 0)", "LINESTRING(5 0,10 0,5 5,5 0)", "FF00F01F2"); //to_svg("LINESTRING(0 0,5 0)", "LINESTRING(5 0,10 0,5 5,0 0,5 0)", "test_relate_00.svg"); + + // 1-point LS (a Point) NOT disjoint + test_geometry("LINESTRING(1 0)", "LINESTRING(0 0,5 0)", "0FFFFF102"); + test_geometry("LINESTRING(0 0,5 0)", "LINESTRING(1 0)", "0F1FF0FF2"); + test_geometry("LINESTRING(0 0,5 0)", "LINESTRING(1 0,1 0)", "0F1FF0FF2"); + 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"); } template @@ -184,7 +192,20 @@ void test_linestring_multi_linestring() typedef bg::model::linestring

ls; typedef bg::model::multi_linestring mls; + // LS disjoint test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,2 1))", "101FF0102"); + // linear ring disjoint + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,2 1,2 2,1 1))", "101FF01F2"); + // 2xLS forming non-simple linear ring disjoint + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,2 1,2 2),(1 1,2 2))", "101FF01F2"); + // 1-point LS (a Point) disjoint + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1))", "101FF00F2"); + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,1 1))", "101FF00F2"); + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,2 0),(1 1,1 1,1 1))", "101FF00F2"); + // 1-point LS (a Point) NOT disjoint + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,9 0),(2 0))", "101FF0FF2"); + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,9 0),(2 0,2 0))", "101FF0FF2"); + test_geometry("LINESTRING(0 0,10 0)", "MULTILINESTRING((1 0,9 0),(2 0,2 0,2 0))", "101FF0FF2"); } template