// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. // This file was modified by Oracle on 2013, 2014, 2015. // Modifications copyright (c) 2013-2015, 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. // 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_WITHIN_POINT_IN_GEOMETRY_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_POINT_IN_GEOMETRY_HPP #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 { // TODO: is this needed? inline int check_result_type(int result) { return result; } template inline T check_result_type(T result) { BOOST_GEOMETRY_ASSERT(false); return result; } template inline int point_in_range(Point const& point, Range const& range, Strategy const& strategy) { boost::ignore_unused(strategy); typedef typename boost::range_iterator::type iterator_type; typename Strategy::state_type state; iterator_type it = boost::begin(range); iterator_type end = boost::end(range); for ( iterator_type previous = it++ ; it != end ; ++previous, ++it ) { if ( ! strategy.apply(point, *previous, *it, state) ) { break; } } return check_result_type(strategy.result(state)); } template inline int point_in_range(Point const& point, Range const& range) { typedef typename point_type::type point_type1; typedef typename point_type::type point_type2; typedef typename strategy::within::services::default_strategy < typename tag::type, typename tag::type, typename tag::type, typename tag_cast::type, areal_tag>::type, typename tag_cast < typename cs_tag::type, spherical_tag >::type, typename tag_cast < typename cs_tag::type, spherical_tag >::type, Point, Geometry >::type strategy_type; typedef typename strategy::covered_by::services::default_strategy < typename tag::type, typename tag::type, typename tag::type, typename tag_cast::type, areal_tag>::type, typename tag_cast < typename cs_tag::type, spherical_tag >::type, typename tag_cast < typename cs_tag::type, spherical_tag >::type, Point, Geometry >::type strategy_type2; static const bool same_strategies = boost::is_same::value; BOOST_MPL_ASSERT_MSG((same_strategies), DEFAULT_WITHIN_AND_COVERED_BY_STRATEGIES_NOT_COMPATIBLE, (strategy_type, strategy_type2)); return point_in_range(point, range, strategy_type()); } }} // namespace detail::within namespace detail_dispatch { namespace within { // checks the relation between a point P and geometry G // returns 1 if P is in the interior of G // returns 0 if P is on the boundry of G // returns -1 if P is in the exterior of G template ::type> struct point_in_geometry : not_implemented {}; template struct point_in_geometry { template static inline int apply(Point1 const& point1, Point2 const& point2, Strategy const& strategy) { boost::ignore_unused(strategy); return strategy.apply(point1, point2) ? 1 : -1; } }; template struct point_in_geometry { template static inline int apply(Point const& point, Segment const& segment, Strategy const& strategy) { boost::ignore_unused(strategy); typedef typename geometry::point_type::type point_type; point_type p0, p1; // TODO: don't copy points detail::assign_point_from_index<0>(segment, p0); detail::assign_point_from_index<1>(segment, p1); typename Strategy::state_type state; strategy.apply(point, p0, p1, state); int r = detail::within::check_result_type(strategy.result(state)); if ( r != 0 ) return -1; // exterior // if the point is equal to the one of the terminal points if ( detail::equals::equals_point_point(point, p0) || detail::equals::equals_point_point(point, p1) ) return 0; // boundary else return 1; // interior } }; template struct point_in_geometry { template static inline int apply(Point const& point, Linestring const& linestring, Strategy const& strategy) { std::size_t count = boost::size(linestring); if ( count > 1 ) { if ( detail::within::point_in_range(point, linestring, strategy) != 0 ) return -1; // exterior // if the linestring doesn't have a boundary if (detail::equals::equals_point_point(range::front(linestring), range::back(linestring))) return 1; // interior // else if the point is equal to the one of the terminal points else if (detail::equals::equals_point_point(point, range::front(linestring)) || detail::equals::equals_point_point(point, range::back(linestring))) return 0; // boundary else return 1; // interior } // TODO: for now degenerated linestrings are ignored // throw an exception here? /*else if ( count == 1 ) { if ( detail::equals::equals_point_point(point, range::front(linestring)) ) return 1; }*/ return -1; // exterior } }; template struct point_in_geometry { template static inline int apply(Point const& point, Ring const& ring, Strategy const& strategy) { if ( boost::size(ring) < core_detail::closure::minimum_ring_size < geometry::closure::value >::value ) { return -1; } detail::normalized_view view(ring); return detail::within::point_in_range(point, view, strategy); } }; // Polygon: in exterior ring, and if so, not within interior ring(s) template struct point_in_geometry { template static inline int apply(Point const& point, Polygon const& polygon, Strategy const& strategy) { int const code = point_in_geometry < typename ring_type::type >::apply(point, exterior_ring(polygon), strategy); if (code == 1) { typename interior_return_type::type rings = interior_rings(polygon); for (typename detail::interior_iterator::type it = boost::begin(rings); it != boost::end(rings); ++it) { int const interior_code = point_in_geometry < typename ring_type::type >::apply(point, *it, strategy); if (interior_code != -1) { // If 0, return 0 (touch) // If 1 (inside hole) return -1 (outside polygon) // If -1 (outside hole) check other holes if any return -interior_code; } } } return code; } }; 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); //BOOST_GEOMETRY_ASSERT(pip != 0); if ( pip > 0 ) // inside return 1; } return -1; // outside } }; template struct point_in_geometry { template static inline int apply(Point const& point, Geometry const& geometry, Strategy const& strategy) { int pip = -1; // outside typedef typename boost::range_value::type linestring_type; typedef typename boost::range_value::type point_type; typedef typename boost::range_iterator::type iterator; iterator it = boost::begin(geometry); for ( ; it != boost::end(geometry) ; ++it ) { pip = point_in_geometry::apply(point, *it, strategy); // inside or on the boundary if ( pip >= 0 ) { ++it; break; } } // outside if ( pip < 0 ) return -1; // TODO: the following isn't needed for covered_by() unsigned boundaries = pip == 0 ? 1 : 0; for ( ; it != boost::end(geometry) ; ++it ) { if ( boost::size(*it) < 2 ) continue; point_type const& front = range::front(*it); point_type const& back = range::back(*it); // is closed_ring - no boundary if ( detail::equals::equals_point_point(front, back) ) continue; // is point on boundary if ( detail::equals::equals_point_point(point, front) || detail::equals::equals_point_point(point, back) ) { ++boundaries; } } // if the number of boundaries is odd, the point is on the boundary return boundaries % 2 ? 0 : 1; } }; template struct point_in_geometry { template static inline int apply(Point const& point, Geometry const& geometry, Strategy const& strategy) { // For invalid multipolygons //int res = -1; // outside typedef typename boost::range_value::type polygon_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); // inside or on the boundary if ( pip >= 0 ) return pip; // For invalid multi-polygons //if ( 1 == pip ) // inside polygon // return 1; //else if ( res < pip ) // point must be inside at least one polygon // res = pip; } return -1; // for valid multipolygons //return res; // for invalid multipolygons } }; }} // namespace detail_dispatch::within namespace detail { namespace within { // 1 - in the interior // 0 - in the boundry // -1 - in the exterior template inline int point_in_geometry(Point const& point, Geometry const& geometry, Strategy const& strategy) { concepts::within::check < typename tag::type, typename tag::type, typename tag_cast::type, areal_tag>::type, Strategy >(); return detail_dispatch::within::point_in_geometry::apply(point, geometry, strategy); } template inline int point_in_geometry(Point const& point, Geometry const& geometry) { typedef typename point_type::type point_type1; typedef typename point_type::type point_type2; typedef typename strategy::within::services::default_strategy < typename tag::type, typename tag::type, typename tag::type, typename tag_cast::type, areal_tag>::type, typename tag_cast < typename cs_tag::type, spherical_tag >::type, typename tag_cast < typename cs_tag::type, spherical_tag >::type, Point, Geometry >::type strategy_type; typedef typename strategy::covered_by::services::default_strategy < typename tag::type, typename tag::type, typename tag::type, typename tag_cast::type, areal_tag>::type, typename tag_cast < typename cs_tag::type, spherical_tag >::type, typename tag_cast < typename cs_tag::type, spherical_tag >::type, Point, Geometry >::type strategy_type2; static const bool same_strategies = boost::is_same::value; BOOST_MPL_ASSERT_MSG((same_strategies), DEFAULT_WITHIN_AND_COVERED_BY_STRATEGIES_NOT_COMPATIBLE, (strategy_type, strategy_type2)); return point_in_geometry(point, geometry, strategy_type()); } }} // namespace detail::within #endif // DOXYGEN_NO_DETAIL }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_POINT_IN_GEOMETRY_HPP