// Boost.Geometry // Copyright (c) 2017, 2019 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_MULTI_POINT_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_MULTI_POINT_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace within { struct multi_point_point { template static inline bool apply(MultiPoint const& multi_point, Point const& point, Strategy const& strategy) { typedef typename boost::range_const_iterator::type iterator; for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it ) { if (! strategy.apply(*it, point)) { return false; } } // all points of MultiPoint inside Point return true; } }; // NOTE: currently the strategy is ignored, math::equals() is used inside geometry::less<> struct multi_point_multi_point { template static inline bool apply(MultiPoint1 const& multi_point1, MultiPoint2 const& multi_point2, Strategy const& /*strategy*/) { typedef typename boost::range_value::type point2_type; typedef typename Strategy::cs_tag cs_tag; typedef geometry::less less_type; less_type const less = less_type(); std::vector points2(boost::begin(multi_point2), boost::end(multi_point2)); std::sort(points2.begin(), points2.end(), less); bool result = false; typedef typename boost::range_const_iterator::type iterator; for ( iterator it = boost::begin(multi_point1) ; it != boost::end(multi_point1) ; ++it ) { if (! std::binary_search(points2.begin(), points2.end(), *it, less)) { return false; } else { result = true; } } return result; } }; // TODO: the complexity could be lesser // the second geometry could be "prepared"/sorted // For Linear geometries partition could be used // For Areal geometries point_in_geometry() would have to call the winding // strategy differently, currently it linearly calls the strategy for each // segment. So the segments would have to be sorted in a way consistent with // the strategy and then the strategy called only for the segments in range. template struct multi_point_single_geometry { template static inline bool apply(MultiPoint const& multi_point, LinearOrAreal const& linear_or_areal, Strategy const& strategy) { //typedef typename boost::range_value::type point1_type; typedef typename point_type::type point2_type; typedef model::box box2_type; // Create envelope of geometry box2_type box; geometry::envelope(linear_or_areal, box, strategy.get_envelope_strategy()); geometry::detail::expand_by_epsilon(box); typedef typename Strategy::disjoint_point_box_strategy_type point_in_box_type; // Test each Point with envelope and then geometry if needed // If in the exterior, break bool result = false; typedef typename boost::range_const_iterator::type iterator; for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it ) { int in_val = 0; // exterior of box and of geometry if (! point_in_box_type::apply(*it, box) || (in_val = point_in_geometry(*it, linear_or_areal, strategy)) < 0) { result = false; break; } // interior : interior/boundary if (Within ? in_val > 0 : in_val >= 0) { result = true; } } return result; } }; // TODO: same here, probably the complexity could be lesser template struct multi_point_multi_geometry { template static inline bool apply(MultiPoint const& multi_point, LinearOrAreal const& linear_or_areal, Strategy const& strategy) { typedef typename point_type::type point2_type; typedef model::box box2_type; static const bool is_linear = is_same < typename tag_cast < typename tag::type, linear_tag >::type, linear_tag >::value; typename Strategy::envelope_strategy_type const envelope_strategy = strategy.get_envelope_strategy(); // TODO: box pairs could be constructed on the fly, inside the rtree // Prepare range of envelopes and ids std::size_t count2 = boost::size(linear_or_areal); typedef std::pair box_pair_type; typedef std::vector box_pair_vector; box_pair_vector boxes(count2); for (std::size_t i = 0 ; i < count2 ; ++i) { geometry::envelope(linear_or_areal, boxes[i].first, envelope_strategy); geometry::detail::expand_by_epsilon(boxes[i].first); boxes[i].second = i; } // Create R-tree typedef strategy::index::services::from_strategy < Strategy > index_strategy_from; typedef index::parameters < index::rstar<4>, typename index_strategy_from::type > index_parameters_type; index::rtree rtree(boxes.begin(), boxes.end(), index_parameters_type(index::rstar<4>(), index_strategy_from::get(strategy))); // For each point find overlapping envelopes and test corresponding single geometries // If a point is in the exterior break bool result = false; typedef typename boost::range_const_iterator::type iterator; for ( iterator it = boost::begin(multi_point) ; it != boost::end(multi_point) ; ++it ) { // TODO: investigate the possibility of using satisfies // TODO: investigate the possibility of using iterative queries (optimization below) box_pair_vector inters_boxes; rtree.query(index::intersects(*it), std::back_inserter(inters_boxes)); bool found_interior = false; bool found_boundary = false; int boundaries = 0; typedef typename box_pair_vector::const_iterator box_iterator; for (box_iterator box_it = inters_boxes.begin() ; box_it != inters_boxes.end() ; ++box_it ) { int const in_val = point_in_geometry(*it, range::at(linear_or_areal, box_it->second), strategy); if (in_val > 0) { found_interior = true; } else if (in_val == 0) { ++boundaries; } // If the result was set previously (interior or // interior/boundary found) the only thing that needs to be // done for other points is to make sure they're not // overlapping the exterior no need to analyse boundaries. if (result && in_val >= 0) { break; } } if (boundaries > 0) { if (is_linear && boundaries % 2 == 0) { found_interior = true; } else { found_boundary = true; } } // exterior if (! found_interior && ! found_boundary) { result = false; break; } // interior : interior/boundary if (Within ? found_interior : (found_interior || found_boundary)) { result = true; } } return result; } }; }} // namespace detail::within #endif // DOXYGEN_NO_DETAIL }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_MULTI_POINT_HPP