diff options
Diffstat (limited to 'boost/geometry/algorithms')
86 files changed, 20233 insertions, 0 deletions
diff --git a/boost/geometry/algorithms/append.hpp b/boost/geometry/algorithms/append.hpp new file mode 100644 index 0000000000..72b2bbadab --- /dev/null +++ b/boost/geometry/algorithms/append.hpp @@ -0,0 +1,233 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_APPEND_HPP +#define BOOST_GEOMETRY_ALGORITHMS_APPEND_HPP + +#include <boost/range.hpp> + + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/mutable_range.hpp> +#include <boost/geometry/core/point_type.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/algorithms/num_interior_rings.hpp> +#include <boost/geometry/algorithms/detail/convert_point_to_point.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace append +{ + +template <typename Geometry, typename Point> +struct append_no_action +{ + static inline void apply(Geometry& , Point const& , + int = 0, int = 0) + { + } +}; + +template <typename Geometry, typename Point> +struct append_point +{ + static inline void apply(Geometry& geometry, Point const& point, + int = 0, int = 0) + { + typename geometry::point_type<Geometry>::type copy; + geometry::detail::conversion::convert_point_to_point(point, copy); + traits::push_back<Geometry>::apply(geometry, copy); + } +}; + + +template <typename Geometry, typename Range> +struct append_range +{ + typedef typename boost::range_value<Range>::type point_type; + + static inline void apply(Geometry& geometry, Range const& range, + int = 0, int = 0) + { + for (typename boost::range_iterator<Range const>::type + it = boost::begin(range); + it != boost::end(range); + ++it) + { + append_point<Geometry, point_type>::apply(geometry, *it); + } + } +}; + + +template <typename Polygon, typename Point> +struct point_to_polygon +{ + typedef typename ring_type<Polygon>::type ring_type; + + static inline void apply(Polygon& polygon, Point const& point, + int ring_index, int = 0) + { + if (ring_index == -1) + { + append_point<ring_type, Point>::apply( + exterior_ring(polygon), point); + } + else if (ring_index < int(num_interior_rings(polygon))) + { + append_point<ring_type, Point>::apply( + interior_rings(polygon)[ring_index], point); + } + } +}; + + +template <typename Polygon, typename Range> +struct range_to_polygon +{ + typedef typename ring_type<Polygon>::type ring_type; + + static inline void apply(Polygon& polygon, Range const& range, + int ring_index, int ) + { + if (ring_index == -1) + { + append_range<ring_type, Range>::apply( + exterior_ring(polygon), range); + } + else if (ring_index < int(num_interior_rings(polygon))) + { + append_range<ring_type, Range>::apply( + interior_rings(polygon)[ring_index], range); + } + } +}; + + +}} // namespace detail::append +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +namespace splitted_dispatch +{ + +template <typename Tag, typename Geometry, typename Point> +struct append_point + : detail::append::append_no_action<Geometry, Point> +{}; + +template <typename Geometry, typename Point> +struct append_point<linestring_tag, Geometry, Point> + : detail::append::append_point<Geometry, Point> +{}; + +template <typename Geometry, typename Point> +struct append_point<ring_tag, Geometry, Point> + : detail::append::append_point<Geometry, Point> +{}; + + +template <typename Polygon, typename Point> +struct append_point<polygon_tag, Polygon, Point> + : detail::append::point_to_polygon<Polygon, Point> +{}; + + +template <typename Tag, typename Geometry, typename Range> +struct append_range + : detail::append::append_no_action<Geometry, Range> +{}; + +template <typename Geometry, typename Range> +struct append_range<linestring_tag, Geometry, Range> + : detail::append::append_range<Geometry, Range> +{}; + +template <typename Geometry, typename Range> +struct append_range<ring_tag, Geometry, Range> + : detail::append::append_range<Geometry, Range> +{}; + + +template <typename Polygon, typename Range> +struct append_range<polygon_tag, Polygon, Range> + : detail::append::range_to_polygon<Polygon, Range> +{}; + +} + + +// Default: append a range (or linestring or ring or whatever) to any geometry +template +< + typename Geometry, typename RangeOrPoint, + typename TagRangeOrPoint = typename tag<RangeOrPoint>::type +> +struct append + : splitted_dispatch::append_range<typename tag<Geometry>::type, Geometry, RangeOrPoint> +{}; + +// Specialization for point to append a point to any geometry +template <typename Geometry, typename RangeOrPoint> +struct append<Geometry, RangeOrPoint, point_tag> + : splitted_dispatch::append_point<typename tag<Geometry>::type, Geometry, RangeOrPoint> +{}; + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Appends one or more points to a linestring, ring, polygon, multi-geometry +\ingroup append +\tparam Geometry \tparam_geometry +\tparam RangeOrPoint Either a range or a point, fullfilling Boost.Range concept or Boost.Geometry Point Concept +\param geometry \param_geometry +\param range_or_point The point or range to add +\param ring_index The index of the ring in case of a polygon: + exterior ring (-1, the default) or interior ring index +\param multi_index Reserved for multi polygons or multi linestrings + +\qbk{[include reference/algorithms/append.qbk]} +} + */ +template <typename Geometry, typename RangeOrPoint> +inline void append(Geometry& geometry, RangeOrPoint const& range_or_point, + int ring_index = -1, int multi_index = 0) +{ + concept::check<Geometry>(); + + dispatch::append + < + Geometry, + RangeOrPoint + >::apply(geometry, range_or_point, ring_index, multi_index); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_APPEND_HPP diff --git a/boost/geometry/algorithms/area.hpp b/boost/geometry/algorithms/area.hpp new file mode 100644 index 0000000000..919ab66fe1 --- /dev/null +++ b/boost/geometry/algorithms/area.hpp @@ -0,0 +1,295 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_AREA_HPP +#define BOOST_GEOMETRY_ALGORITHMS_AREA_HPP + +#include <boost/concept_check.hpp> +#include <boost/mpl/if.hpp> +#include <boost/range/functions.hpp> +#include <boost/range/metafunctions.hpp> + +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/point_order.hpp> +#include <boost/geometry/core/ring_type.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/detail/calculate_null.hpp> +#include <boost/geometry/algorithms/detail/calculate_sum.hpp> +// #include <boost/geometry/algorithms/detail/throw_on_empty_input.hpp> + +#include <boost/geometry/strategies/area.hpp> +#include <boost/geometry/strategies/default_area_result.hpp> + +#include <boost/geometry/strategies/concepts/area_concept.hpp> + +#include <boost/geometry/util/math.hpp> +#include <boost/geometry/util/order_as_direction.hpp> +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/views/reversible_view.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace area +{ + +template<typename Box, typename Strategy> +struct box_area +{ + typedef typename coordinate_type<Box>::type return_type; + + static inline return_type apply(Box const& box, Strategy const&) + { + // Currently only works for 2D Cartesian boxes + assert_dimension<Box, 2>(); + + return_type const dx = get<max_corner, 0>(box) + - get<min_corner, 0>(box); + return_type const dy = get<max_corner, 1>(box) + - get<min_corner, 1>(box); + + return dx * dy; + } +}; + + +template +< + typename Ring, + iterate_direction Direction, + closure_selector Closure, + typename Strategy +> +struct ring_area +{ + BOOST_CONCEPT_ASSERT( (geometry::concept::AreaStrategy<Strategy>) ); + + typedef typename Strategy::return_type type; + + static inline type apply(Ring const& ring, Strategy const& strategy) + { + assert_dimension<Ring, 2>(); + + // Ignore warning (because using static method sometimes) on strategy + boost::ignore_unused_variable_warning(strategy); + + // An open ring has at least three points, + // A closed ring has at least four points, + // if not, there is no (zero) area + if (boost::size(ring) + < core_detail::closure::minimum_ring_size<Closure>::value) + { + return type(); + } + + typedef typename reversible_view<Ring const, Direction>::type rview_type; + typedef typename closeable_view + < + rview_type const, Closure + >::type view_type; + typedef typename boost::range_iterator<view_type const>::type iterator_type; + + rview_type rview(ring); + view_type view(rview); + typename Strategy::state_type state; + iterator_type it = boost::begin(view); + iterator_type end = boost::end(view); + + for (iterator_type previous = it++; + it != end; + ++previous, ++it) + { + strategy.apply(*previous, *it, state); + } + + return strategy.result(state); + } +}; + + +}} // namespace detail::area + + +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Geometry, + typename Strategy = typename strategy::area::services::default_strategy + < + typename cs_tag + < + typename point_type<Geometry>::type + >::type, + typename point_type<Geometry>::type + >::type, + typename Tag = typename tag<Geometry>::type +> +struct area + : detail::calculate_null + < + typename Strategy::return_type, + Geometry, + Strategy + > {}; + + +template +< + typename Geometry, + typename Strategy +> +struct area<Geometry, Strategy, box_tag> + : detail::area::box_area<Geometry, Strategy> +{}; + + +template +< + typename Ring, + typename Strategy +> +struct area<Ring, Strategy, ring_tag> + : detail::area::ring_area + < + Ring, + order_as_direction<geometry::point_order<Ring>::value>::value, + geometry::closure<Ring>::value, + Strategy + > +{}; + + +template +< + typename Polygon, + typename Strategy +> +struct area<Polygon, Strategy, polygon_tag> + : detail::calculate_polygon_sum + < + typename Strategy::return_type, + Polygon, + Strategy, + detail::area::ring_area + < + typename ring_type<Polygon const>::type, + order_as_direction<geometry::point_order<Polygon>::value>::value, + geometry::closure<Polygon>::value, + Strategy + > + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + + +/*! +\brief \brief_calc{area} +\ingroup area +\details \details_calc{area}. \details_default_strategy + +The area algorithm calculates the surface area of all geometries having a surface, namely +box, polygon, ring, multipolygon. The units are the square of the units used for the points +defining the surface. If subject geometry is defined in meters, then area is calculated +in square meters. + +The area calculation can be done in all three common coordinate systems, Cartesian, Spherical +and Geographic as well. + +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_calc{area} + +\qbk{[include reference/algorithms/area.qbk]} +\qbk{[heading Examples]} +\qbk{[area] [area_output]} +*/ +template <typename Geometry> +inline typename default_area_result<Geometry>::type area(Geometry const& geometry) +{ + concept::check<Geometry const>(); + + typedef typename point_type<Geometry>::type point_type; + typedef typename strategy::area::services::default_strategy + < + typename cs_tag<point_type>::type, + point_type + >::type strategy_type; + + // detail::throw_on_empty_input(geometry); + + return dispatch::area + < + Geometry + >::apply(geometry, strategy_type()); +} + +/*! +\brief \brief_calc{area} \brief_strategy +\ingroup area +\details \details_calc{area} \brief_strategy. \details_strategy_reasons +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{Area} +\param geometry \param_geometry +\param strategy \param_strategy{area} +\return \return_calc{area} + +\qbk{distinguish,with strategy} + +\qbk{ +[include reference/algorithms/area.qbk] + +[heading Example] +[area_with_strategy] +[area_with_strategy_output] + +[heading Available Strategies] +\* [link geometry.reference.strategies.strategy_area_surveyor Surveyor (cartesian)] +\* [link geometry.reference.strategies.strategy_area_huiller Huiller (spherical)] +} + */ +template <typename Geometry, typename Strategy> +inline typename Strategy::return_type area( + Geometry const& geometry, Strategy const& strategy) +{ + concept::check<Geometry const>(); + + // detail::throw_on_empty_input(geometry); + + return dispatch::area + < + Geometry, + Strategy + >::apply(geometry, strategy); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_AREA_HPP diff --git a/boost/geometry/algorithms/assign.hpp b/boost/geometry/algorithms/assign.hpp new file mode 100644 index 0000000000..8c153c878d --- /dev/null +++ b/boost/geometry/algorithms/assign.hpp @@ -0,0 +1,171 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_ASSIGN_HPP +#define BOOST_GEOMETRY_ALGORITHMS_ASSIGN_HPP + + +#include <cstddef> + +#include <boost/concept/requires.hpp> +#include <boost/concept_check.hpp> +#include <boost/mpl/assert.hpp> +#include <boost/mpl/if.hpp> +#include <boost/numeric/conversion/bounds.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <boost/type_traits.hpp> + +#include <boost/geometry/algorithms/detail/assign_box_corners.hpp> +#include <boost/geometry/algorithms/detail/assign_indexed_point.hpp> +#include <boost/geometry/algorithms/detail/assign_values.hpp> +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/append.hpp> +#include <boost/geometry/algorithms/clear.hpp> +#include <boost/geometry/arithmetic/arithmetic.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/util/for_each_coordinate.hpp> + +namespace boost { namespace geometry +{ + +/*! +\brief Assign a range of points to a linestring, ring or polygon +\note The point-type of the range might be different from the point-type of the geometry +\ingroup assign +\tparam Geometry \tparam_geometry +\tparam Range \tparam_range_point +\param geometry \param_geometry +\param range \param_range_point + +\qbk{ +[heading Notes] +[note Assign automatically clears the geometry before assigning (use append if you don't want that)] +[heading Example] +[assign_points] [assign_points_output] + +[heading See also] +\* [link geometry.reference.algorithms.append append] +} + */ +template <typename Geometry, typename Range> +inline void assign_points(Geometry& geometry, Range const& range) +{ + concept::check<Geometry>(); + + clear(geometry); + geometry::append(geometry, range, -1, 0); +} + + +/*! +\brief assign to a box inverse infinite +\details The assign_inverse function initialize a 2D or 3D box with large coordinates, the +min corner is very large, the max corner is very small. This is a convenient starting point to +collect the minimum bounding box of a geometry. +\ingroup assign +\tparam Geometry \tparam_geometry +\param geometry \param_geometry + +\qbk{ +[heading Example] +[assign_inverse] [assign_inverse_output] + +[heading See also] +\* [link geometry.reference.algorithms.make.make_inverse make_inverse] +} + */ +template <typename Geometry> +inline void assign_inverse(Geometry& geometry) +{ + concept::check<Geometry>(); + + dispatch::assign_inverse + < + typename tag<Geometry>::type, + Geometry + >::apply(geometry); +} + +/*! +\brief assign zero values to a box, point +\ingroup assign +\details The assign_zero function initializes a 2D or 3D point or box with coordinates of zero +\tparam Geometry \tparam_geometry +\param geometry \param_geometry + + */ +template <typename Geometry> +inline void assign_zero(Geometry& geometry) +{ + concept::check<Geometry>(); + + dispatch::assign_zero + < + typename tag<Geometry>::type, + Geometry + >::apply(geometry); +} + +/*! +\brief Assigns one geometry to another geometry +\details The assign algorithm assigns one geometry, e.g. a BOX, to another geometry, e.g. a RING. This only +if it is possible and applicable. +\ingroup assign +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry (target) +\param geometry2 \param_geometry (source) + +\qbk{ +[heading Example] +[assign] [assign_output] + +[heading See also] +\* [link geometry.reference.algorithms.convert convert] +} + */ +template <typename Geometry1, typename Geometry2> +inline void assign(Geometry1& geometry1, Geometry2 const& geometry2) +{ + concept::check_concepts_and_equal_dimensions<Geometry1, Geometry2 const>(); + + bool const same_point_order = + point_order<Geometry1>::value == point_order<Geometry2>::value; + bool const same_closure = + closure<Geometry1>::value == closure<Geometry2>::value; + + BOOST_MPL_ASSERT_MSG + ( + same_point_order, ASSIGN_IS_NOT_SUPPORTED_FOR_DIFFERENT_POINT_ORDER + , (types<Geometry1, Geometry2>) + ); + BOOST_MPL_ASSERT_MSG + ( + same_closure, ASSIGN_IS_NOT_SUPPORTED_FOR_DIFFERENT_CLOSURE + , (types<Geometry1, Geometry2>) + ); + + dispatch::convert<Geometry2, Geometry1>::apply(geometry2, geometry1); +} + + +}} // namespace boost::geometry + + + +#endif // BOOST_GEOMETRY_ALGORITHMS_ASSIGN_HPP diff --git a/boost/geometry/algorithms/buffer.hpp b/boost/geometry/algorithms/buffer.hpp new file mode 100644 index 0000000000..e22e36addc --- /dev/null +++ b/boost/geometry/algorithms/buffer.hpp @@ -0,0 +1,167 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_BUFFER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_BUFFER_HPP + +#include <cstddef> + +#include <boost/numeric/conversion/cast.hpp> + + +#include <boost/geometry/algorithms/clear.hpp> +#include <boost/geometry/algorithms/detail/disjoint.hpp> +#include <boost/geometry/arithmetic/arithmetic.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/geometries/segment.hpp> +#include <boost/geometry/util/math.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + +template <typename BoxIn, typename BoxOut, typename T, std::size_t C, std::size_t D, std::size_t N> +struct box_loop +{ + typedef typename coordinate_type<BoxOut>::type coordinate_type; + + static inline void apply(BoxIn const& box_in, T const& distance, BoxOut& box_out) + { + coordinate_type d = distance; + set<C, D>(box_out, get<C, D>(box_in) + d); + box_loop<BoxIn, BoxOut, T, C, D + 1, N>::apply(box_in, distance, box_out); + } +}; + +template <typename BoxIn, typename BoxOut, typename T, std::size_t C, std::size_t N> +struct box_loop<BoxIn, BoxOut, T, C, N, N> +{ + static inline void apply(BoxIn const&, T const&, BoxOut&) {} +}; + +// Extends a box with the same amount in all directions +template<typename BoxIn, typename BoxOut, typename T> +inline void buffer_box(BoxIn const& box_in, T const& distance, BoxOut& box_out) +{ + assert_dimension_equal<BoxIn, BoxOut>(); + + static const std::size_t N = dimension<BoxIn>::value; + + box_loop<BoxIn, BoxOut, T, min_corner, 0, N>::apply(box_in, -distance, box_out); + box_loop<BoxIn, BoxOut, T, max_corner, 0, N>::apply(box_in, distance, box_out); +} + + + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template <typename TagIn, typename TagOut, typename Input, typename T, typename Output> +struct buffer {}; + + +template <typename BoxIn, typename T, typename BoxOut> +struct buffer<box_tag, box_tag, BoxIn, T, BoxOut> +{ + static inline void apply(BoxIn const& box_in, T const& distance, + T const& , BoxIn& box_out) + { + detail::buffer::buffer_box(box_in, distance, box_out); + } +}; + +// Many things to do. Point is easy, other geometries require self intersections +// For point, note that it should output as a polygon (like the rest). Buffers +// of a set of geometries are often lateron combined using a "dissolve" operation. +// Two points close to each other get a combined kidney shaped buffer then. + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brief_calc{buffer} +\ingroup buffer +\details \details_calc{buffer, \det_buffer}. +\tparam Input \tparam_geometry +\tparam Output \tparam_geometry +\tparam Distance \tparam_numeric +\param geometry_in \param_geometry +\param geometry_out \param_geometry +\param distance The distance to be used for the buffer +\param chord_length (optional) The length of the chord's in the generated arcs around points or bends +\note Currently only implemented for box, the trivial case, but still useful + +\qbk{[include reference/algorithms/buffer.qbk]} + */ +template <typename Input, typename Output, typename Distance> +inline void buffer(Input const& geometry_in, Output& geometry_out, + Distance const& distance, Distance const& chord_length = -1) +{ + concept::check<Input const>(); + concept::check<Output>(); + + dispatch::buffer + < + typename tag<Input>::type, + typename tag<Output>::type, + Input, + Distance, + Output + >::apply(geometry_in, distance, chord_length, geometry_out); +} + +/*! +\brief \brief_calc{buffer} +\ingroup buffer +\details \details_calc{return_buffer, \det_buffer}. \details_return{buffer}. +\tparam Input \tparam_geometry +\tparam Output \tparam_geometry +\tparam Distance \tparam_numeric +\param geometry \param_geometry +\param distance The distance to be used for the buffer +\param chord_length (optional) The length of the chord's in the generated arcs around points or bends +\return \return_calc{buffer} + */ +template <typename Output, typename Input, typename T> +Output return_buffer(Input const& geometry, T const& distance, T const& chord_length = -1) +{ + concept::check<Input const>(); + concept::check<Output>(); + + Output geometry_out; + + dispatch::buffer + < + typename tag<Input>::type, + typename tag<Output>::type, + Input, + T, + Output + >::apply(geometry, distance, chord_length, geometry_out); + + return geometry_out; +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_BUFFER_HPP diff --git a/boost/geometry/algorithms/centroid.hpp b/boost/geometry/algorithms/centroid.hpp new file mode 100644 index 0000000000..69ad9fe829 --- /dev/null +++ b/boost/geometry/algorithms/centroid.hpp @@ -0,0 +1,470 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP +#define BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP + + +#include <cstddef> + +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/exception.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/tag_cast.hpp> + +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/distance.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/strategies/centroid.hpp> +#include <boost/geometry/strategies/concepts/centroid_concept.hpp> +#include <boost/geometry/views/closeable_view.hpp> + +#include <boost/geometry/util/for_each_coordinate.hpp> +#include <boost/geometry/util/select_coordinate_type.hpp> + + + +namespace boost { namespace geometry +{ + + +#if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW) + +/*! +\brief Centroid Exception +\ingroup centroid +\details The centroid_exception is thrown if the free centroid function is called with + geometries for which the centroid cannot be calculated. For example: a linestring + without points, a polygon without points, an empty multi-geometry. +\qbk{ +[heading See also] +\* [link geometry.reference.algorithms.centroid the centroid function] +} + + */ +class centroid_exception : public geometry::exception +{ +public: + + inline centroid_exception() {} + + virtual char const* what() const throw() + { + return "Boost.Geometry Centroid calculation exception"; + } +}; + +#endif + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace centroid +{ + +template<typename Point, typename PointCentroid, typename Strategy> +struct centroid_point +{ + static inline void apply(Point const& point, PointCentroid& centroid, + Strategy const&) + { + geometry::convert(point, centroid); + } +}; + +template +< + typename Box, + typename Point, + std::size_t Dimension, + std::size_t DimensionCount +> +struct centroid_box_calculator +{ + typedef typename select_coordinate_type + < + Box, Point + >::type coordinate_type; + static inline void apply(Box const& box, Point& centroid) + { + coordinate_type const c1 = get<min_corner, Dimension>(box); + coordinate_type const c2 = get<max_corner, Dimension>(box); + coordinate_type m = c1 + c2; + m /= 2.0; + + set<Dimension>(centroid, m); + + centroid_box_calculator + < + Box, Point, + Dimension + 1, DimensionCount + >::apply(box, centroid); + } +}; + + +template<typename Box, typename Point, std::size_t DimensionCount> +struct centroid_box_calculator<Box, Point, DimensionCount, DimensionCount> +{ + static inline void apply(Box const& , Point& ) + { + } +}; + + +template<typename Box, typename Point, typename Strategy> +struct centroid_box +{ + static inline void apply(Box const& box, Point& centroid, + Strategy const&) + { + centroid_box_calculator + < + Box, Point, + 0, dimension<Box>::type::value + >::apply(box, centroid); + } +}; + + +// There is one thing where centroid is different from e.g. within. +// If the ring has only one point, it might make sense that +// that point is the centroid. +template<typename Point, typename Range> +inline bool range_ok(Range const& range, Point& centroid) +{ + std::size_t const n = boost::size(range); + if (n > 1) + { + return true; + } + else if (n <= 0) + { +#if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW) + throw centroid_exception(); +#endif + return false; + } + else // if (n == 1) + { + // Take over the first point in a "coordinate neutral way" + geometry::convert(*boost::begin(range), centroid); + return false; + } + return true; +} + + +/*! + \brief Calculate the centroid of a ring. +*/ +template<typename Ring, closure_selector Closure, typename Strategy> +struct centroid_range_state +{ + static inline void apply(Ring const& ring, + Strategy const& strategy, typename Strategy::state_type& state) + { + typedef typename closeable_view<Ring const, Closure>::type view_type; + + typedef typename boost::range_iterator<view_type const>::type iterator_type; + + view_type view(ring); + iterator_type it = boost::begin(view); + iterator_type end = boost::end(view); + + for (iterator_type previous = it++; + it != end; + ++previous, ++it) + { + strategy.apply(*previous, *it, state); + } + } +}; + +template<typename Range, typename Point, closure_selector Closure, typename Strategy> +struct centroid_range +{ + static inline void apply(Range const& range, Point& centroid, + Strategy const& strategy) + { + if (range_ok(range, centroid)) + { + typename Strategy::state_type state; + centroid_range_state + < + Range, + Closure, + Strategy + >::apply(range, strategy, state); + strategy.result(state, centroid); + } + } +}; + + +/*! + \brief Centroid of a polygon. + \note Because outer ring is clockwise, inners are counter clockwise, + triangle approach is OK and works for polygons with rings. +*/ +template<typename Polygon, typename Strategy> +struct centroid_polygon_state +{ + typedef typename ring_type<Polygon>::type ring_type; + + static inline void apply(Polygon const& poly, + Strategy const& strategy, typename Strategy::state_type& state) + { + typedef centroid_range_state + < + ring_type, + geometry::closure<ring_type>::value, + Strategy + > per_ring; + + per_ring::apply(exterior_ring(poly), strategy, state); + + typename interior_return_type<Polygon const>::type rings + = interior_rings(poly); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + per_ring::apply(*it, strategy, state); + } + } +}; + +template<typename Polygon, typename Point, typename Strategy> +struct centroid_polygon +{ + static inline void apply(Polygon const& poly, Point& centroid, + Strategy const& strategy) + { + if (range_ok(exterior_ring(poly), centroid)) + { + typename Strategy::state_type state; + centroid_polygon_state + < + Polygon, + Strategy + >::apply(poly, strategy, state); + strategy.result(state, centroid); + } + } +}; + + +}} // namespace detail::centroid +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag, + typename Geometry, + typename Point, + typename Strategy +> +struct centroid {}; + +template +< + typename Geometry, + typename Point, + typename Strategy +> +struct centroid<point_tag, Geometry, Point, Strategy> + : detail::centroid::centroid_point<Geometry, Point, Strategy> +{}; + +template +< + typename Box, + typename Point, + typename Strategy +> +struct centroid<box_tag, Box, Point, Strategy> + : detail::centroid::centroid_box<Box, Point, Strategy> +{}; + +template <typename Ring, typename Point, typename Strategy> +struct centroid<ring_tag, Ring, Point, Strategy> + : detail::centroid::centroid_range + < + Ring, + Point, + geometry::closure<Ring>::value, + Strategy + > +{}; + +template <typename Linestring, typename Point, typename Strategy> +struct centroid<linestring_tag, Linestring, Point, Strategy> + : detail::centroid::centroid_range + < + Linestring, + Point, + closed, + Strategy + > + {}; + +template <typename Polygon, typename Point, typename Strategy> +struct centroid<polygon_tag, Polygon, Point, Strategy> + : detail::centroid::centroid_polygon<Polygon, Point, Strategy> + {}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brief_calc{centroid} \brief_strategy +\ingroup centroid +\details \details_calc{centroid,geometric center (or: center of mass)}. \details_strategy_reasons +\tparam Geometry \tparam_geometry +\tparam Point \tparam_point +\tparam Strategy \tparam_strategy{Centroid} +\param geometry \param_geometry +\param c \param_point \param_set{centroid} +\param strategy \param_strategy{centroid} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/centroid.qbk]} +\qbk{[include reference/algorithms/centroid_strategies.qbk]} +} + +*/ +template<typename Geometry, typename Point, typename Strategy> +inline void centroid(Geometry const& geometry, Point& c, + Strategy const& strategy) +{ + //BOOST_CONCEPT_ASSERT( (geometry::concept::CentroidStrategy<Strategy>) ); + + concept::check_concepts_and_equal_dimensions<Point, Geometry const>(); + + typedef typename point_type<Geometry>::type point_type; + + // Call dispatch apply method. That one returns true if centroid + // should be taken from state. + dispatch::centroid + < + typename tag<Geometry>::type, + Geometry, + Point, + Strategy + >::apply(geometry, c, strategy); +} + + +/*! +\brief \brief_calc{centroid} +\ingroup centroid +\details \details_calc{centroid,geometric center (or: center of mass)}. \details_default_strategy +\tparam Geometry \tparam_geometry +\tparam Point \tparam_point +\param geometry \param_geometry +\param c The calculated centroid will be assigned to this point reference + +\qbk{[include reference/algorithms/centroid.qbk]} +\qbk{ +[heading Example] +[centroid] +[centroid_output] +} + */ +template<typename Geometry, typename Point> +inline void centroid(Geometry const& geometry, Point& c) +{ + concept::check_concepts_and_equal_dimensions<Point, Geometry const>(); + + typedef typename strategy::centroid::services::default_strategy + < + typename cs_tag<Geometry>::type, + typename tag_cast + < + typename tag<Geometry>::type, + pointlike_tag, + linear_tag, + areal_tag + >::type, + dimension<Geometry>::type::value, + Point, + Geometry + >::type strategy_type; + + centroid(geometry, c, strategy_type()); +} + + +/*! +\brief \brief_calc{centroid} +\ingroup centroid +\details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}. +\tparam Point \tparam_point +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_calc{centroid} + +\qbk{[include reference/algorithms/centroid.qbk]} + */ +template<typename Point, typename Geometry> +inline Point return_centroid(Geometry const& geometry) +{ + concept::check_concepts_and_equal_dimensions<Point, Geometry const>(); + + Point c; + centroid(geometry, c); + return c; +} + +/*! +\brief \brief_calc{centroid} \brief_strategy +\ingroup centroid +\details \details_calc{centroid,geometric center (or: center of mass)}. \details_return{centroid}. \details_strategy_reasons +\tparam Point \tparam_point +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{centroid} +\param geometry \param_geometry +\param strategy \param_strategy{centroid} +\return \return_calc{centroid} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/centroid.qbk]} +\qbk{[include reference/algorithms/centroid_strategies.qbk]} + */ +template<typename Point, typename Geometry, typename Strategy> +inline Point return_centroid(Geometry const& geometry, Strategy const& strategy) +{ + //BOOST_CONCEPT_ASSERT( (geometry::concept::CentroidStrategy<Strategy>) ); + + concept::check_concepts_and_equal_dimensions<Point, Geometry const>(); + + Point c; + centroid(geometry, c, strategy); + return c; +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP diff --git a/boost/geometry/algorithms/clear.hpp b/boost/geometry/algorithms/clear.hpp new file mode 100644 index 0000000000..d7336587ee --- /dev/null +++ b/boost/geometry/algorithms/clear.hpp @@ -0,0 +1,159 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_CLEAR_HPP +#define BOOST_GEOMETRY_ALGORITHMS_CLEAR_HPP + +#include <boost/mpl/assert.hpp> +#include <boost/type_traits/remove_const.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/mutable_range.hpp> +#include <boost/geometry/core/tag_cast.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace clear +{ + +template <typename Geometry> +struct collection_clear +{ + static inline void apply(Geometry& geometry) + { + traits::clear<Geometry>::apply(geometry); + } +}; + +template <typename Polygon> +struct polygon_clear +{ + static inline void apply(Polygon& polygon) + { + traits::clear + < + typename boost::remove_reference + < + typename traits::interior_mutable_type<Polygon>::type + >::type + >::apply(interior_rings(polygon)); + traits::clear + < + typename boost::remove_reference + < + typename traits::ring_mutable_type<Polygon>::type + >::type + >::apply(exterior_ring(polygon)); + } +}; + +template <typename Geometry> +struct no_action +{ + static inline void apply(Geometry& ) + { + } +}; + +}} // namespace detail::clear +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Geometry, + typename Tag = typename tag_cast<typename tag<Geometry>::type, multi_tag>::type +> +struct clear +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + +// Point/box/segment do not have clear. So specialize to do nothing. +template <typename Geometry> +struct clear<Geometry, point_tag> + : detail::clear::no_action<Geometry> +{}; + +template <typename Geometry> +struct clear<Geometry, box_tag> + : detail::clear::no_action<Geometry> +{}; + +template <typename Geometry> +struct clear<Geometry, segment_tag> + : detail::clear::no_action<Geometry> +{}; + +template <typename Geometry> +struct clear<Geometry, linestring_tag> + : detail::clear::collection_clear<Geometry> +{}; + +template <typename Geometry> +struct clear<Geometry, ring_tag> + : detail::clear::collection_clear<Geometry> +{}; + + +// Polygon can (indirectly) use std for clear +template <typename Polygon> +struct clear<Polygon, polygon_tag> + : detail::clear::polygon_clear<Polygon> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Clears a linestring, ring or polygon (exterior+interiors) or multi* +\details Generic function to clear a geometry. All points will be removed from the collection or collections + making up the geometry. In most cases this is equivalent to the .clear() method of a std::vector<...>. In + the case of a polygon, this clear functionality is automatically called for the exterior ring, and for the + interior ring collection. In the case of a point, boxes and segments, nothing will happen. +\ingroup clear +\tparam Geometry \tparam_geometry +\param geometry \param_geometry which will be cleared +\note points and boxes cannot be cleared, instead they can be set to zero by "assign_zero" + +\qbk{[include reference/algorithms/clear.qbk]} +*/ +template <typename Geometry> +inline void clear(Geometry& geometry) +{ + concept::check<Geometry>(); + + dispatch::clear<Geometry>::apply(geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_CLEAR_HPP diff --git a/boost/geometry/algorithms/comparable_distance.hpp b/boost/geometry/algorithms/comparable_distance.hpp new file mode 100644 index 0000000000..3467045ca2 --- /dev/null +++ b/boost/geometry/algorithms/comparable_distance.hpp @@ -0,0 +1,74 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_COMPARABLE_DISTANCE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_COMPARABLE_DISTANCE_HPP + + +#include <boost/geometry/algorithms/distance.hpp> + + +namespace boost { namespace geometry +{ + + +/*! +\brief \brief_calc2{comparable distance measurement} +\ingroup distance +\details The free function comparable_distance does not necessarily calculate the distance, + but it calculates a distance measure such that two distances are comparable to each other. + For example: for the Cartesian coordinate system, Pythagoras is used but the square root + is not taken, which makes it faster and the results of two point pairs can still be + compared to each other. +\tparam Geometry1 first geometry type +\tparam Geometry2 second geometry type +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_calc{comparable distance} + +\qbk{[include reference/algorithms/comparable_distance.qbk]} + */ +template <typename Geometry1, typename Geometry2> +inline typename default_distance_result<Geometry1, Geometry2>::type comparable_distance( + Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + typedef typename point_type<Geometry1>::type point1_type; + typedef typename point_type<Geometry2>::type point2_type; + + // Define a point-point-distance-strategy + // for either the normal case, either the reversed case + + typedef typename strategy::distance::services::comparable_type + < + typename boost::mpl::if_c + < + geometry::reverse_dispatch + <Geometry1, Geometry2>::type::value, + typename strategy::distance::services::default_strategy + <point_tag, point2_type, point1_type>::type, + typename strategy::distance::services::default_strategy + <point_tag, point1_type, point2_type>::type + >::type + >::type strategy_type; + + return distance(geometry1, geometry2, strategy_type()); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_COMPARABLE_DISTANCE_HPP diff --git a/boost/geometry/algorithms/convert.hpp b/boost/geometry/algorithms/convert.hpp new file mode 100644 index 0000000000..fbbf74c17f --- /dev/null +++ b/boost/geometry/algorithms/convert.hpp @@ -0,0 +1,411 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_CONVERT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_CONVERT_HPP + + +#include <cstddef> + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/range.hpp> +#include <boost/type_traits/is_array.hpp> + +#include <boost/geometry/arithmetic/arithmetic.hpp> +#include <boost/geometry/algorithms/not_implemented.hpp> +#include <boost/geometry/algorithms/append.hpp> +#include <boost/geometry/algorithms/clear.hpp> +#include <boost/geometry/algorithms/for_each.hpp> +#include <boost/geometry/algorithms/detail/assign_values.hpp> +#include <boost/geometry/algorithms/detail/assign_box_corners.hpp> +#include <boost/geometry/algorithms/detail/assign_indexed_point.hpp> +#include <boost/geometry/algorithms/detail/convert_point_to_point.hpp> +#include <boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp> + +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/views/reversible_view.hpp> + +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/point_order.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace conversion +{ + +template +< + typename Point, + typename Box, + std::size_t Index, + std::size_t Dimension, + std::size_t DimensionCount +> +struct point_to_box +{ + static inline void apply(Point const& point, Box& box) + { + typedef typename coordinate_type<Box>::type coordinate_type; + + set<Index, Dimension>(box, + boost::numeric_cast<coordinate_type>(get<Dimension>(point))); + point_to_box + < + Point, Box, + Index, Dimension + 1, DimensionCount + >::apply(point, box); + } +}; + + +template +< + typename Point, + typename Box, + std::size_t Index, + std::size_t DimensionCount +> +struct point_to_box<Point, Box, Index, DimensionCount, DimensionCount> +{ + static inline void apply(Point const& , Box& ) + {} +}; + +template <typename Box, typename Range, bool Close, bool Reverse> +struct box_to_range +{ + static inline void apply(Box const& box, Range& range) + { + traits::resize<Range>::apply(range, Close ? 5 : 4); + assign_box_corners_oriented<Reverse>(box, range); + if (Close) + { + range[4] = range[0]; + } + } +}; + +template <typename Segment, typename Range> +struct segment_to_range +{ + static inline void apply(Segment const& segment, Range& range) + { + traits::resize<Range>::apply(range, 2); + + typename boost::range_iterator<Range>::type it = boost::begin(range); + + assign_point_from_index<0>(segment, *it); + ++it; + assign_point_from_index<1>(segment, *it); + } +}; + +template +< + typename Range1, + typename Range2, + bool Reverse = false +> +struct range_to_range +{ + typedef typename reversible_view + < + Range1 const, + Reverse ? iterate_reverse : iterate_forward + >::type rview_type; + typedef typename closeable_view + < + rview_type const, + geometry::closure<Range1>::value + >::type view_type; + + static inline void apply(Range1 const& source, Range2& destination) + { + geometry::clear(destination); + + rview_type rview(source); + + // We consider input always as closed, and skip last + // point for open output. + view_type view(rview); + + int n = boost::size(view); + if (geometry::closure<Range2>::value == geometry::open) + { + n--; + } + + int i = 0; + for (typename boost::range_iterator<view_type const>::type it + = boost::begin(view); + it != boost::end(view) && i < n; + ++it, ++i) + { + geometry::append(destination, *it); + } + } +}; + +template <typename Polygon1, typename Polygon2> +struct polygon_to_polygon +{ + typedef range_to_range + < + typename geometry::ring_type<Polygon1>::type, + typename geometry::ring_type<Polygon2>::type, + geometry::point_order<Polygon1>::value + != geometry::point_order<Polygon2>::value + > per_ring; + + static inline void apply(Polygon1 const& source, Polygon2& destination) + { + // Clearing managed per ring, and in the resizing of interior rings + + per_ring::apply(geometry::exterior_ring(source), + geometry::exterior_ring(destination)); + + // Container should be resizeable + traits::resize + < + typename boost::remove_reference + < + typename traits::interior_mutable_type<Polygon2>::type + >::type + >::apply(interior_rings(destination), num_interior_rings(source)); + + typename interior_return_type<Polygon1 const>::type rings_source + = interior_rings(source); + typename interior_return_type<Polygon2>::type rings_dest + = interior_rings(destination); + + BOOST_AUTO_TPL(it_source, boost::begin(rings_source)); + BOOST_AUTO_TPL(it_dest, boost::begin(rings_dest)); + + for ( ; it_source != boost::end(rings_source); ++it_source, ++it_dest) + { + per_ring::apply(*it_source, *it_dest); + } + } +}; + + +}} // namespace detail::conversion +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Geometry1, typename Geometry2, + typename Tag1 = typename tag_cast<typename tag<Geometry1>::type, multi_tag>::type, + typename Tag2 = typename tag_cast<typename tag<Geometry2>::type, multi_tag>::type, + std::size_t DimensionCount = dimension<Geometry1>::type::value, + bool UseAssignment = boost::is_same<Geometry1, Geometry2>::value + && !boost::is_array<Geometry1>::value +> +struct convert: not_implemented<Tag1, Tag2, mpl::int_<DimensionCount> > +{}; + + +template +< + typename Geometry1, typename Geometry2, + typename Tag, + std::size_t DimensionCount +> +struct convert<Geometry1, Geometry2, Tag, Tag, DimensionCount, true> +{ + // Same geometry type -> copy whole geometry + static inline void apply(Geometry1 const& source, Geometry2& destination) + { + destination = source; + } +}; + + +template +< + typename Geometry1, typename Geometry2, + std::size_t DimensionCount +> +struct convert<Geometry1, Geometry2, point_tag, point_tag, DimensionCount, false> + : detail::conversion::point_to_point<Geometry1, Geometry2, 0, DimensionCount> +{}; + + +template +< + typename Box1, typename Box2, + std::size_t DimensionCount +> +struct convert<Box1, Box2, box_tag, box_tag, DimensionCount, false> + : detail::conversion::indexed_to_indexed<Box1, Box2, 0, DimensionCount> +{}; + + +template +< + typename Segment1, typename Segment2, + std::size_t DimensionCount +> +struct convert<Segment1, Segment2, segment_tag, segment_tag, DimensionCount, false> + : detail::conversion::indexed_to_indexed<Segment1, Segment2, 0, DimensionCount> +{}; + + +template <typename Segment, typename LineString, std::size_t DimensionCount> +struct convert<Segment, LineString, segment_tag, linestring_tag, DimensionCount, false> + : detail::conversion::segment_to_range<Segment, LineString> +{}; + + +template <typename Ring1, typename Ring2, std::size_t DimensionCount> +struct convert<Ring1, Ring2, ring_tag, ring_tag, DimensionCount, false> + : detail::conversion::range_to_range + < + Ring1, + Ring2, + geometry::point_order<Ring1>::value + != geometry::point_order<Ring2>::value + > +{}; + +template <typename LineString1, typename LineString2, std::size_t DimensionCount> +struct convert<LineString1, LineString2, linestring_tag, linestring_tag, DimensionCount, false> + : detail::conversion::range_to_range<LineString1, LineString2> +{}; + +template <typename Polygon1, typename Polygon2, std::size_t DimensionCount> +struct convert<Polygon1, Polygon2, polygon_tag, polygon_tag, DimensionCount, false> + : detail::conversion::polygon_to_polygon<Polygon1, Polygon2> +{}; + +template <typename Box, typename Ring> +struct convert<Box, Ring, box_tag, ring_tag, 2, false> + : detail::conversion::box_to_range + < + Box, + Ring, + geometry::closure<Ring>::value == closed, + geometry::point_order<Ring>::value == counterclockwise + > +{}; + + +template <typename Box, typename Polygon> +struct convert<Box, Polygon, box_tag, polygon_tag, 2, false> +{ + static inline void apply(Box const& box, Polygon& polygon) + { + typedef typename ring_type<Polygon>::type ring_type; + + convert + < + Box, ring_type, + box_tag, ring_tag, + 2, false + >::apply(box, exterior_ring(polygon)); + } +}; + + +template <typename Point, typename Box, std::size_t DimensionCount> +struct convert<Point, Box, point_tag, box_tag, DimensionCount, false> +{ + static inline void apply(Point const& point, Box& box) + { + detail::conversion::point_to_box + < + Point, Box, min_corner, 0, DimensionCount + >::apply(point, box); + detail::conversion::point_to_box + < + Point, Box, max_corner, 0, DimensionCount + >::apply(point, box); + } +}; + + +template <typename Ring, typename Polygon, std::size_t DimensionCount> +struct convert<Ring, Polygon, ring_tag, polygon_tag, DimensionCount, false> +{ + static inline void apply(Ring const& ring, Polygon& polygon) + { + typedef typename ring_type<Polygon>::type ring_type; + convert + < + Ring, ring_type, + ring_tag, ring_tag, + DimensionCount, false + >::apply(ring, exterior_ring(polygon)); + } +}; + + +template <typename Polygon, typename Ring, std::size_t DimensionCount> +struct convert<Polygon, Ring, polygon_tag, ring_tag, DimensionCount, false> +{ + static inline void apply(Polygon const& polygon, Ring& ring) + { + typedef typename ring_type<Polygon>::type ring_type; + + convert + < + ring_type, Ring, + ring_tag, ring_tag, + DimensionCount, false + >::apply(exterior_ring(polygon), ring); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Converts one geometry to another geometry +\details The convert algorithm converts one geometry, e.g. a BOX, to another +geometry, e.g. a RING. This only if it is possible and applicable. +If the point-order is different, or the closure is different between two +geometry types, it will be converted correctly by explicitly reversing the +points or closing or opening the polygon rings. +\ingroup convert +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry (source) +\param geometry2 \param_geometry (target) + +\qbk{[include reference/algorithms/convert.qbk]} + */ +template <typename Geometry1, typename Geometry2> +inline void convert(Geometry1 const& geometry1, Geometry2& geometry2) +{ + concept::check_concepts_and_equal_dimensions<Geometry1 const, Geometry2>(); + + dispatch::convert<Geometry1, Geometry2>::apply(geometry1, geometry2); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_CONVERT_HPP diff --git a/boost/geometry/algorithms/convex_hull.hpp b/boost/geometry/algorithms/convex_hull.hpp new file mode 100644 index 0000000000..56b87c8c15 --- /dev/null +++ b/boost/geometry/algorithms/convex_hull.hpp @@ -0,0 +1,275 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_CONVEX_HULL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_CONVEX_HULL_HPP + +#include <boost/array.hpp> + +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/point_order.hpp> +#include <boost/geometry/core/exterior_ring.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/strategies/convex_hull.hpp> +#include <boost/geometry/strategies/concepts/convex_hull_concept.hpp> + +#include <boost/geometry/views/detail/range_type.hpp> + +#include <boost/geometry/algorithms/num_points.hpp> +#include <boost/geometry/algorithms/detail/as_range.hpp> +#include <boost/geometry/algorithms/detail/assign_box_corners.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace convex_hull +{ + +template +< + typename Geometry, + order_selector Order, + typename Strategy +> +struct hull_insert +{ + + // Member template function (to avoid inconvenient declaration + // of output-iterator-type, from hull_to_geometry) + template <typename OutputIterator> + static inline OutputIterator apply(Geometry const& geometry, + OutputIterator out, Strategy const& strategy) + { + typename Strategy::state_type state; + + strategy.apply(geometry, state); + strategy.result(state, out, Order == clockwise); + return out; + } +}; + +template +< + typename Geometry, + typename Strategy +> +struct hull_to_geometry +{ + template <typename OutputGeometry> + static inline void apply(Geometry const& geometry, OutputGeometry& out, + Strategy const& strategy) + { + hull_insert + < + Geometry, + geometry::point_order<OutputGeometry>::value, + Strategy + >::apply(geometry, + std::back_inserter( + // Handle linestring, ring and polygon the same: + detail::as_range + < + typename range_type<OutputGeometry>::type + >(out)), strategy); + } +}; + + +// Helper metafunction for default strategy retrieval +template <typename Geometry> +struct default_strategy + : strategy_convex_hull + < + Geometry, + typename point_type<Geometry>::type + > +{}; + + +}} // namespace detail::convex_hull +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Geometry, + typename Strategy = typename detail::convex_hull::default_strategy<Geometry>::type, + typename Tag = typename tag<Geometry>::type +> +struct convex_hull + : detail::convex_hull::hull_to_geometry<Geometry, Strategy> +{}; + +template +< + typename Box, + typename Strategy +> +struct convex_hull<Box, Strategy, box_tag> +{ + template <typename OutputGeometry> + static inline void apply(Box const& box, OutputGeometry& out, + Strategy const& ) + { + static bool const Close + = geometry::closure<OutputGeometry>::value == closed; + static bool const Reverse + = geometry::point_order<OutputGeometry>::value == counterclockwise; + + // A hull for boxes is trivial. Any strategy is (currently) skipped. + boost::array<typename point_type<Box>::type, 4> range; + geometry::detail::assign_box_corners_oriented<Reverse>(box, range); + geometry::append(out, range); + if (Close) + { + geometry::append(out, *boost::begin(range)); + } + } +}; + + + +template +< + order_selector Order, + typename Geometry, typename Strategy +> +struct convex_hull_insert + : detail::convex_hull::hull_insert<Geometry, Order, Strategy> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +template<typename Geometry, typename OutputGeometry, typename Strategy> +inline void convex_hull(Geometry const& geometry, + OutputGeometry& out, Strategy const& strategy) +{ + concept::check_concepts_and_equal_dimensions + < + const Geometry, + OutputGeometry + >(); + + BOOST_CONCEPT_ASSERT( (geometry::concept::ConvexHullStrategy<Strategy>) ); + + if (geometry::num_points(geometry) == 0) + { + // Leave output empty + return; + } + + dispatch::convex_hull + < + Geometry, + Strategy + >::apply(geometry, out, strategy); +} + + +/*! +\brief \brief_calc{convex hull} +\ingroup convex_hull +\details \details_calc{convex_hull,convex hull}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry \param_geometry, input geometry +\param hull \param_geometry \param_set{convex hull} + +\qbk{[include reference/algorithms/convex_hull.qbk]} + */ +template<typename Geometry, typename OutputGeometry> +inline void convex_hull(Geometry const& geometry, + OutputGeometry& hull) +{ + concept::check_concepts_and_equal_dimensions + < + const Geometry, + OutputGeometry + >(); + + typedef typename detail::convex_hull::default_strategy<Geometry>::type strategy_type; + + convex_hull(geometry, hull, strategy_type()); +} + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace convex_hull +{ + + +template<typename Geometry, typename OutputIterator, typename Strategy> +inline OutputIterator convex_hull_insert(Geometry const& geometry, + OutputIterator out, Strategy const& strategy) +{ + // Concept: output point type = point type of input geometry + concept::check<Geometry const>(); + concept::check<typename point_type<Geometry>::type>(); + + BOOST_CONCEPT_ASSERT( (geometry::concept::ConvexHullStrategy<Strategy>) ); + + return dispatch::convex_hull_insert + < + geometry::point_order<Geometry>::value, + Geometry, Strategy + >::apply(geometry, out, strategy); +} + + +/*! +\brief Calculate the convex hull of a geometry, output-iterator version +\ingroup convex_hull +\tparam Geometry the input geometry type +\tparam OutputIterator: an output-iterator +\param geometry the geometry to calculate convex hull from +\param out an output iterator outputing points of the convex hull +\note This overloaded version outputs to an output iterator. +In this case, nothing is known about its point-type or + about its clockwise order. Therefore, the input point-type + and order are copied + + */ +template<typename Geometry, typename OutputIterator> +inline OutputIterator convex_hull_insert(Geometry const& geometry, + OutputIterator out) +{ + // Concept: output point type = point type of input geometry + concept::check<Geometry const>(); + concept::check<typename point_type<Geometry>::type>(); + + typedef typename detail::convex_hull::default_strategy<Geometry>::type strategy_type; + + return convex_hull_insert(geometry, out, strategy_type()); +} + + +}} // namespace detail::convex_hull +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_CONVEX_HULL_HPP diff --git a/boost/geometry/algorithms/correct.hpp b/boost/geometry/algorithms/correct.hpp new file mode 100644 index 0000000000..583e395f8e --- /dev/null +++ b/boost/geometry/algorithms/correct.hpp @@ -0,0 +1,265 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_CORRECT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_CORRECT_HPP + + +#include <algorithm> +#include <cstddef> +#include <functional> + +#include <boost/mpl/assert.hpp> +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/mutable_range.hpp> +#include <boost/geometry/core/ring_type.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/area.hpp> +#include <boost/geometry/algorithms/disjoint.hpp> +#include <boost/geometry/util/order_as_direction.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace correct +{ + +template <typename Geometry> +struct correct_nop +{ + static inline void apply(Geometry& ) + {} +}; + + +template <typename Box, std::size_t Dimension, std::size_t DimensionCount> +struct correct_box_loop +{ + typedef typename coordinate_type<Box>::type coordinate_type; + + static inline void apply(Box& box) + { + if (get<min_corner, Dimension>(box) > get<max_corner, Dimension>(box)) + { + // Swap the coordinates + coordinate_type max_value = get<min_corner, Dimension>(box); + coordinate_type min_value = get<max_corner, Dimension>(box); + set<min_corner, Dimension>(box, min_value); + set<max_corner, Dimension>(box, max_value); + } + + correct_box_loop + < + Box, Dimension + 1, DimensionCount + >::apply(box); + } +}; + + + +template <typename Box, std::size_t DimensionCount> +struct correct_box_loop<Box, DimensionCount, DimensionCount> +{ + static inline void apply(Box& ) + {} + +}; + + +// Correct a box: make min/max correct +template <typename Box> +struct correct_box +{ + + static inline void apply(Box& box) + { + // Currently only for Cartesian coordinates + // (or spherical without crossing dateline) + // Future version: adapt using strategies + correct_box_loop + < + Box, 0, dimension<Box>::type::value + >::apply(box); + } +}; + + +// Close a ring, if not closed +template <typename Ring, typename Predicate> +struct correct_ring +{ + typedef typename point_type<Ring>::type point_type; + typedef typename coordinate_type<Ring>::type coordinate_type; + + typedef typename strategy::area::services::default_strategy + < + typename cs_tag<point_type>::type, + point_type + >::type strategy_type; + + typedef detail::area::ring_area + < + Ring, + order_as_direction<geometry::point_order<Ring>::value>::value, + geometry::closure<Ring>::value, + strategy_type + > ring_area_type; + + + static inline void apply(Ring& r) + { + // Check close-ness + if (boost::size(r) > 2) + { + // check if closed, if not, close it + bool const disjoint = geometry::disjoint(*boost::begin(r), *(boost::end(r) - 1)); + closure_selector const s = geometry::closure<Ring>::value; + + if (disjoint && (s == closed)) + { + geometry::append(r, *boost::begin(r)); + } + if (! disjoint && geometry::closure<Ring>::value != closed) + { + // Open it by removing last point + geometry::traits::resize<Ring>::apply(r, boost::size(r) - 1); + } + } + // Check area + Predicate predicate; + typedef typename default_area_result<Ring>::type area_result_type; + area_result_type const zero = area_result_type(); + if (predicate(ring_area_type::apply(r, strategy_type()), zero)) + { + std::reverse(boost::begin(r), boost::end(r)); + } + } +}; + +// Correct a polygon: normalizes all rings, sets outer ring clockwise, sets all +// inner rings counter clockwise (or vice versa depending on orientation) +template <typename Polygon> +struct correct_polygon +{ + typedef typename ring_type<Polygon>::type ring_type; + typedef typename default_area_result<Polygon>::type area_result_type; + + static inline void apply(Polygon& poly) + { + correct_ring + < + ring_type, + std::less<area_result_type> + >::apply(exterior_ring(poly)); + + typename interior_return_type<Polygon>::type rings + = interior_rings(poly); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + correct_ring + < + ring_type, + std::greater<area_result_type> + >::apply(*it); + } + } +}; + + +}} // namespace detail::correct +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template <typename Geometry, typename Tag = typename tag<Geometry>::type> +struct correct: not_implemented<Tag> +{}; + +template <typename Point> +struct correct<Point, point_tag> + : detail::correct::correct_nop<Point> +{}; + +template <typename LineString> +struct correct<LineString, linestring_tag> + : detail::correct::correct_nop<LineString> +{}; + +template <typename Segment> +struct correct<Segment, segment_tag> + : detail::correct::correct_nop<Segment> +{}; + + +template <typename Box> +struct correct<Box, box_tag> + : detail::correct::correct_box<Box> +{}; + +template <typename Ring> +struct correct<Ring, ring_tag> + : detail::correct::correct_ring + < + Ring, + std::less<typename default_area_result<Ring>::type> + > +{}; + +template <typename Polygon> +struct correct<Polygon, polygon_tag> + : detail::correct::correct_polygon<Polygon> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Corrects a geometry +\details Corrects a geometry: all rings which are wrongly oriented with respect + to their expected orientation are reversed. To all rings which do not have a + closing point and are typed as they should have one, the first point is + appended. Also boxes can be corrected. +\ingroup correct +\tparam Geometry \tparam_geometry +\param geometry \param_geometry which will be corrected if necessary + +\qbk{[include reference/algorithms/correct.qbk]} +*/ +template <typename Geometry> +inline void correct(Geometry& geometry) +{ + concept::check<Geometry const>(); + + dispatch::correct<Geometry>::apply(geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_CORRECT_HPP diff --git a/boost/geometry/algorithms/covered_by.hpp b/boost/geometry/algorithms/covered_by.hpp new file mode 100644 index 0000000000..c3c406c4ca --- /dev/null +++ b/boost/geometry/algorithms/covered_by.hpp @@ -0,0 +1,195 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_COVERED_BY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_COVERED_BY_HPP + + +#include <cstddef> + +#include <boost/geometry/algorithms/not_implemented.hpp> +#include <boost/geometry/algorithms/within.hpp> + +#include <boost/geometry/strategies/cartesian/point_in_box.hpp> +#include <boost/geometry/strategies/cartesian/box_in_box.hpp> + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Geometry1, + typename Geometry2, + typename Tag1 = typename tag<Geometry1>::type, + typename Tag2 = typename tag<Geometry2>::type +> +struct covered_by: not_implemented<Tag1, Tag2> +{}; + + +template <typename Point, typename Box> +struct covered_by<Point, Box, point_tag, box_tag> +{ + template <typename Strategy> + static inline bool apply(Point const& point, Box const& box, Strategy const& strategy) + { + return strategy.apply(point, box); + } +}; + +template <typename Box1, typename Box2> +struct covered_by<Box1, Box2, box_tag, box_tag> +{ + template <typename Strategy> + static inline bool apply(Box1 const& box1, Box2 const& box2, Strategy const& strategy) + { + assert_dimension_equal<Box1, Box2>(); + return strategy.apply(box1, box2); + } +}; + + + +template <typename Point, typename Ring> +struct covered_by<Point, Ring, point_tag, ring_tag> +{ + template <typename Strategy> + static inline bool apply(Point const& point, Ring const& ring, Strategy const& strategy) + { + return detail::within::point_in_ring + < + Point, + Ring, + order_as_direction<geometry::point_order<Ring>::value>::value, + geometry::closure<Ring>::value, + Strategy + >::apply(point, ring, strategy) >= 0; + } +}; + +template <typename Point, typename Polygon> +struct covered_by<Point, Polygon, point_tag, polygon_tag> +{ + template <typename Strategy> + static inline bool apply(Point const& point, Polygon const& polygon, Strategy const& strategy) + { + return detail::within::point_in_polygon + < + Point, + Polygon, + order_as_direction<geometry::point_order<Polygon>::value>::value, + geometry::closure<Polygon>::value, + Strategy + >::apply(point, polygon, strategy) >= 0; + } +}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brief_check12{is inside or on border} +\ingroup covered_by +\details \details_check12{covered_by, is inside or on border}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry which might be inside or on the border of the second geometry +\param geometry2 \param_geometry which might cover the first geometry +\return true if geometry1 is inside of or on the border of geometry2, + else false +\note The default strategy is used for covered_by detection + +\qbk{[include reference/algorithms/covered_by.qbk]} + + */ +template<typename Geometry1, typename Geometry2> +inline bool covered_by(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + assert_dimension_equal<Geometry1, Geometry2>(); + + typedef typename point_type<Geometry1>::type point_type1; + typedef typename point_type<Geometry2>::type point_type2; + + typedef typename strategy::covered_by::services::default_strategy + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + typename tag<Geometry1>::type, + typename tag_cast<typename tag<Geometry2>::type, areal_tag>::type, + typename tag_cast + < + typename cs_tag<point_type1>::type, spherical_tag + >::type, + typename tag_cast + < + typename cs_tag<point_type2>::type, spherical_tag + >::type, + Geometry1, + Geometry2 + >::type strategy_type; + + return dispatch::covered_by + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, strategy_type()); +} + +/*! +\brief \brief_check12{is inside or on border} \brief_strategy +\ingroup covered_by +\details \details_check12{covered_by, is inside or on border}, \brief_strategy. \details_strategy_reasons +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry which might be inside or on the border of the second geometry +\param geometry2 \param_geometry which might cover the first geometry +\param strategy strategy to be used +\return true if geometry1 is inside of or on the border of geometry2, + else false + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/covered_by.qbk]} + +*/ +template<typename Geometry1, typename Geometry2, typename Strategy> +inline bool covered_by(Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + concept::within::check + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + typename tag_cast<typename tag<Geometry2>::type, areal_tag>::type, + Strategy + >(); + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + assert_dimension_equal<Geometry1, Geometry2>(); + + return dispatch::covered_by + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, strategy); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_COVERED_BY_HPP diff --git a/boost/geometry/algorithms/detail/as_range.hpp b/boost/geometry/algorithms/detail/as_range.hpp new file mode 100644 index 0000000000..d0dfb07e43 --- /dev/null +++ b/boost/geometry/algorithms/detail/as_range.hpp @@ -0,0 +1,107 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_AS_RANGE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_AS_RANGE_HPP + + +#include <boost/type_traits.hpp> + +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/util/add_const_if_c.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template <typename GeometryTag, typename Geometry, typename Range, bool IsConst> +struct as_range +{ + static inline typename add_const_if_c<IsConst, Range>::type& get( + typename add_const_if_c<IsConst, Geometry>::type& input) + { + return input; + } +}; + + +template <typename Geometry, typename Range, bool IsConst> +struct as_range<polygon_tag, Geometry, Range, IsConst> +{ + static inline typename add_const_if_c<IsConst, Range>::type& get( + typename add_const_if_c<IsConst, Geometry>::type& input) + { + return exterior_ring(input); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +// Will probably be replaced by the more generic "view_as", therefore in detail +namespace detail +{ + +/*! +\brief Function getting either the range (ring, linestring) itself +or the outer ring (polygon) +\details Utility to handle polygon's outer ring as a range +\ingroup utility +*/ +template <typename Range, typename Geometry> +inline Range& as_range(Geometry& input) +{ + return dispatch::as_range + < + typename tag<Geometry>::type, + Geometry, + Range, + false + >::get(input); +} + + +/*! +\brief Function getting either the range (ring, linestring) itself +or the outer ring (polygon), const version +\details Utility to handle polygon's outer ring as a range +\ingroup utility +*/ +template <typename Range, typename Geometry> +inline Range const& as_range(Geometry const& input) +{ + return dispatch::as_range + < + typename tag<Geometry>::type, + Geometry, + Range, + true + >::get(input); +} + +} + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_AS_RANGE_HPP diff --git a/boost/geometry/algorithms/detail/assign_box_corners.hpp b/boost/geometry/algorithms/detail/assign_box_corners.hpp new file mode 100644 index 0000000000..1fd41733f2 --- /dev/null +++ b/boost/geometry/algorithms/detail/assign_box_corners.hpp @@ -0,0 +1,93 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_ASSIGN_BOX_CORNERS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_ASSIGN_BOX_CORNERS_HPP + + +#include <cstddef> + +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/algorithms/detail/assign_values.hpp> + + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ +// Note: this is moved to namespace detail because the names and parameter orders +// are not yet 100% clear. + +/*! +\brief Assign the four points of a 2D box +\ingroup assign +\note The order is crucial. Most logical is LOWER, UPPER and sub-order LEFT, RIGHT + so this is how it is implemented. +\tparam Box \tparam_box +\tparam Point \tparam_point +\param box \param_box +\param lower_left point being assigned to lower left coordinates of the box +\param lower_right point being assigned to lower right coordinates of the box +\param upper_left point being assigned to upper left coordinates of the box +\param upper_right point being assigned to upper right coordinates of the box + +\qbk{ +[heading Example] +[assign_box_corners] [assign_box_corners_output] +} +*/ +template <typename Box, typename Point> +inline void assign_box_corners(Box const& box, + Point& lower_left, Point& lower_right, + Point& upper_left, Point& upper_right) +{ + concept::check<Box const>(); + concept::check<Point>(); + + detail::assign::assign_box_2d_corner + <min_corner, min_corner>(box, lower_left); + detail::assign::assign_box_2d_corner + <max_corner, min_corner>(box, lower_right); + detail::assign::assign_box_2d_corner + <min_corner, max_corner>(box, upper_left); + detail::assign::assign_box_2d_corner + <max_corner, max_corner>(box, upper_right); +} + +template <bool Reverse, typename Box, typename Range> +inline void assign_box_corners_oriented(Box const& box, Range& corners) +{ + if (Reverse) + { + // make counterclockwise ll,lr,ur,ul + assign_box_corners(box, corners[0], corners[1], corners[3], corners[2]); + } + else + { + // make clockwise ll,ul,ur,lr + assign_box_corners(box, corners[0], corners[3], corners[1], corners[2]); + } +} + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_ASSIGN_BOX_CORNERS_HPP diff --git a/boost/geometry/algorithms/detail/assign_indexed_point.hpp b/boost/geometry/algorithms/detail/assign_indexed_point.hpp new file mode 100644 index 0000000000..a1cffb80a7 --- /dev/null +++ b/boost/geometry/algorithms/detail/assign_indexed_point.hpp @@ -0,0 +1,94 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_ASSIGN_INDEXED_POINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_ASSIGN_INDEXED_POINT_HPP + + +#include <cstddef> + +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/algorithms/detail/assign_values.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +/*! +\brief Assign a box or segment with the value of a point +\ingroup assign +\tparam Index indicates which box-corner, min_corner (0) or max_corner (1) + or which point of segment (0/1) +\tparam Point \tparam_point +\tparam Geometry \tparam_box_or_segment +\param point \param_point +\param geometry \param_box_or_segment + +\qbk{ +[heading Example] +[assign_point_to_index] [assign_point_to_index_output] +} +*/ +template <std::size_t Index, typename Geometry, typename Point> +inline void assign_point_to_index(Point const& point, Geometry& geometry) +{ + concept::check<Point const>(); + concept::check<Geometry>(); + + detail::assign::assign_point_to_index + < + Geometry, Point, Index, 0, dimension<Geometry>::type::value + >::apply(point, geometry); +} + + +/*! +\brief Assign a point with a point of a box or segment +\ingroup assign +\tparam Index indicates which box-corner, min_corner (0) or max_corner (1) + or which point of segment (0/1) +\tparam Geometry \tparam_box_or_segment +\tparam Point \tparam_point +\param geometry \param_box_or_segment +\param point \param_point + +\qbk{ +[heading Example] +[assign_point_from_index] [assign_point_from_index_output] +} +*/ +template <std::size_t Index, typename Point, typename Geometry> +inline void assign_point_from_index(Geometry const& geometry, Point& point) +{ + concept::check<Geometry const>(); + concept::check<Point>(); + + detail::assign::assign_point_from_index + < + Geometry, Point, Index, 0, dimension<Geometry>::type::value + >::apply(geometry, point); +} + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_ASSIGN_INDEXED_POINT_HPP diff --git a/boost/geometry/algorithms/detail/assign_values.hpp b/boost/geometry/algorithms/detail/assign_values.hpp new file mode 100644 index 0000000000..ed4713493f --- /dev/null +++ b/boost/geometry/algorithms/detail/assign_values.hpp @@ -0,0 +1,443 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_ASSIGN_VALUES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_ASSIGN_VALUES_HPP + + +#include <cstddef> + +#include <boost/concept/requires.hpp> +#include <boost/concept_check.hpp> +#include <boost/mpl/assert.hpp> +#include <boost/mpl/if.hpp> +#include <boost/numeric/conversion/bounds.hpp> +#include <boost/numeric/conversion/cast.hpp> +#include <boost/type_traits.hpp> + +#include <boost/geometry/arithmetic/arithmetic.hpp> +#include <boost/geometry/algorithms/append.hpp> +#include <boost/geometry/algorithms/clear.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + + +#include <boost/geometry/util/for_each_coordinate.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace assign +{ + + +template +< + typename Box, std::size_t Index, + std::size_t Dimension, std::size_t DimensionCount +> +struct initialize +{ + typedef typename coordinate_type<Box>::type coordinate_type; + + static inline void apply(Box& box, coordinate_type const& value) + { + geometry::set<Index, Dimension>(box, value); + initialize<Box, Index, Dimension + 1, DimensionCount>::apply(box, value); + } +}; + + +template <typename Box, std::size_t Index, std::size_t DimensionCount> +struct initialize<Box, Index, DimensionCount, DimensionCount> +{ + typedef typename coordinate_type<Box>::type coordinate_type; + + static inline void apply(Box&, coordinate_type const& ) + {} +}; + + +template <typename Point> +struct assign_zero_point +{ + static inline void apply(Point& point) + { + geometry::assign_value(point, 0); + } +}; + + +template <typename BoxOrSegment> +struct assign_inverse_box_or_segment +{ + typedef typename point_type<BoxOrSegment>::type point_type; + + static inline void apply(BoxOrSegment& geometry) + { + typedef typename coordinate_type<point_type>::type bound_type; + + initialize + < + BoxOrSegment, 0, 0, dimension<BoxOrSegment>::type::value + >::apply( + geometry, boost::numeric::bounds<bound_type>::highest()); + initialize + < + BoxOrSegment, 1, 0, dimension<BoxOrSegment>::type::value + >::apply( + geometry, boost::numeric::bounds<bound_type>::lowest()); + } +}; + + +template <typename BoxOrSegment> +struct assign_zero_box_or_segment +{ + static inline void apply(BoxOrSegment& geometry) + { + typedef typename coordinate_type<BoxOrSegment>::type coordinate_type; + + initialize + < + BoxOrSegment, 0, 0, dimension<BoxOrSegment>::type::value + >::apply(geometry, coordinate_type()); + initialize + < + BoxOrSegment, 1, 0, dimension<BoxOrSegment>::type::value + >::apply(geometry, coordinate_type()); + } +}; + + +template +< + std::size_t Corner1, std::size_t Corner2, + typename Box, typename Point +> +inline void assign_box_2d_corner(Box const& box, Point& point) +{ + // Be sure both are 2-Dimensional + assert_dimension<Box, 2>(); + assert_dimension<Point, 2>(); + + // Copy coordinates + typedef typename coordinate_type<Point>::type coordinate_type; + + geometry::set<0>(point, boost::numeric_cast<coordinate_type>(get<Corner1, 0>(box))); + geometry::set<1>(point, boost::numeric_cast<coordinate_type>(get<Corner2, 1>(box))); +} + + + +template +< + typename Geometry, typename Point, + std::size_t Index, + std::size_t Dimension, std::size_t DimensionCount +> +struct assign_point_to_index +{ + + static inline void apply(Point const& point, Geometry& geometry) + { + geometry::set<Index, Dimension>(geometry, boost::numeric_cast + < + typename coordinate_type<Geometry>::type + >(geometry::get<Dimension>(point))); + + assign_point_to_index + < + Geometry, Point, Index, Dimension + 1, DimensionCount + >::apply(point, geometry); + } +}; + +template +< + typename Geometry, typename Point, + std::size_t Index, + std::size_t DimensionCount +> +struct assign_point_to_index + < + Geometry, Point, + Index, + DimensionCount, DimensionCount + > +{ + static inline void apply(Point const& , Geometry& ) + { + } +}; + + +template +< + typename Geometry, typename Point, + std::size_t Index, + std::size_t Dimension, std::size_t DimensionCount +> +struct assign_point_from_index +{ + + static inline void apply(Geometry const& geometry, Point& point) + { + geometry::set<Dimension>( point, boost::numeric_cast + < + typename coordinate_type<Point>::type + >(geometry::get<Index, Dimension>(geometry))); + + assign_point_from_index + < + Geometry, Point, Index, Dimension + 1, DimensionCount + >::apply(geometry, point); + } +}; + +template +< + typename Geometry, typename Point, + std::size_t Index, + std::size_t DimensionCount +> +struct assign_point_from_index + < + Geometry, Point, + Index, + DimensionCount, DimensionCount + > +{ + static inline void apply(Geometry const&, Point&) + { + } +}; + + +template <typename Geometry> +struct assign_2d_box_or_segment +{ + typedef typename coordinate_type<Geometry>::type coordinate_type; + + // Here we assign 4 coordinates to a box of segment + // -> Most logical is: x1,y1,x2,y2 + // In case the user reverses x1/x2 or y1/y2, for a box, we could reverse them (THAT IS NOT IMPLEMENTED) + + template <typename Type> + static inline void apply(Geometry& geometry, + Type const& x1, Type const& y1, Type const& x2, Type const& y2) + { + geometry::set<0, 0>(geometry, boost::numeric_cast<coordinate_type>(x1)); + geometry::set<0, 1>(geometry, boost::numeric_cast<coordinate_type>(y1)); + geometry::set<1, 0>(geometry, boost::numeric_cast<coordinate_type>(x2)); + geometry::set<1, 1>(geometry, boost::numeric_cast<coordinate_type>(y2)); + } +}; + + +}} // namespace detail::assign +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template <typename GeometryTag, typename Geometry, std::size_t DimensionCount> +struct assign +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + +template <typename Point> +struct assign<point_tag, Point, 2> +{ + typedef typename coordinate_type<Point>::type coordinate_type; + + template <typename T> + static inline void apply(Point& point, T const& c1, T const& c2) + { + set<0>(point, boost::numeric_cast<coordinate_type>(c1)); + set<1>(point, boost::numeric_cast<coordinate_type>(c2)); + } +}; + +template <typename Point> +struct assign<point_tag, Point, 3> +{ + typedef typename coordinate_type<Point>::type coordinate_type; + + template <typename T> + static inline void apply(Point& point, T const& c1, T const& c2, T const& c3) + { + set<0>(point, boost::numeric_cast<coordinate_type>(c1)); + set<1>(point, boost::numeric_cast<coordinate_type>(c2)); + set<2>(point, boost::numeric_cast<coordinate_type>(c3)); + } +}; + +template <typename Box> +struct assign<box_tag, Box, 2> + : detail::assign::assign_2d_box_or_segment<Box> +{}; + +template <typename Segment> +struct assign<segment_tag, Segment, 2> + : detail::assign::assign_2d_box_or_segment<Segment> +{}; + + + +template <typename GeometryTag, typename Geometry> +struct assign_zero {}; + + +template <typename Point> +struct assign_zero<point_tag, Point> + : detail::assign::assign_zero_point<Point> +{}; + +template <typename Box> +struct assign_zero<box_tag, Box> + : detail::assign::assign_zero_box_or_segment<Box> +{}; + +template <typename Segment> +struct assign_zero<segment_tag, Segment> + : detail::assign::assign_zero_box_or_segment<Segment> +{}; + + +template <typename GeometryTag, typename Geometry> +struct assign_inverse {}; + +template <typename Box> +struct assign_inverse<box_tag, Box> + : detail::assign::assign_inverse_box_or_segment<Box> +{}; + +template <typename Segment> +struct assign_inverse<segment_tag, Segment> + : detail::assign::assign_inverse_box_or_segment<Segment> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Assign two coordinates to a geometry (usually a 2D point) +\ingroup assign +\tparam Geometry \tparam_geometry +\tparam Type \tparam_numeric to specify the coordinates +\param geometry \param_geometry +\param c1 \param_x +\param c2 \param_y + +\qbk{distinguish, 2 coordinate values} +\qbk{ +[heading Example] +[assign_2d_point] [assign_2d_point_output] + +[heading See also] +\* [link geometry.reference.algorithms.make.make_2_2_coordinate_values make] +} + */ +template <typename Geometry, typename Type> +inline void assign_values(Geometry& geometry, Type const& c1, Type const& c2) +{ + concept::check<Geometry>(); + + dispatch::assign + < + typename tag<Geometry>::type, + Geometry, + geometry::dimension<Geometry>::type::value + >::apply(geometry, c1, c2); +} + +/*! +\brief Assign three values to a geometry (usually a 3D point) +\ingroup assign +\tparam Geometry \tparam_geometry +\tparam Type \tparam_numeric to specify the coordinates +\param geometry \param_geometry +\param c1 \param_x +\param c2 \param_y +\param c3 \param_z + +\qbk{distinguish, 3 coordinate values} +\qbk{ +[heading Example] +[assign_3d_point] [assign_3d_point_output] + +[heading See also] +\* [link geometry.reference.algorithms.make.make_3_3_coordinate_values make] +} + */ +template <typename Geometry, typename Type> +inline void assign_values(Geometry& geometry, + Type const& c1, Type const& c2, Type const& c3) +{ + concept::check<Geometry>(); + + dispatch::assign + < + typename tag<Geometry>::type, + Geometry, + geometry::dimension<Geometry>::type::value + >::apply(geometry, c1, c2, c3); +} + +/*! +\brief Assign four values to a geometry (usually a box or segment) +\ingroup assign +\tparam Geometry \tparam_geometry +\tparam Type \tparam_numeric to specify the coordinates +\param geometry \param_geometry +\param c1 First coordinate (usually x1) +\param c2 Second coordinate (usually y1) +\param c3 Third coordinate (usually x2) +\param c4 Fourth coordinate (usually y2) + +\qbk{distinguish, 4 coordinate values} + */ +template <typename Geometry, typename Type> +inline void assign_values(Geometry& geometry, + Type const& c1, Type const& c2, Type const& c3, Type const& c4) +{ + concept::check<Geometry>(); + + dispatch::assign + < + typename tag<Geometry>::type, + Geometry, + geometry::dimension<Geometry>::type::value + >::apply(geometry, c1, c2, c3, c4); +} + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_ASSIGN_VALUES_HPP diff --git a/boost/geometry/algorithms/detail/calculate_null.hpp b/boost/geometry/algorithms/detail/calculate_null.hpp new file mode 100644 index 0000000000..4b48d62fc2 --- /dev/null +++ b/boost/geometry/algorithms/detail/calculate_null.hpp @@ -0,0 +1,38 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_NULL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_NULL_HPP + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template<typename ReturnType, typename Geometry, typename Strategy> +struct calculate_null +{ + static inline ReturnType apply(Geometry const& , Strategy const&) + { + return ReturnType(); + } +}; + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_NULL_HPP diff --git a/boost/geometry/algorithms/detail/calculate_sum.hpp b/boost/geometry/algorithms/detail/calculate_sum.hpp new file mode 100644 index 0000000000..2ad349080b --- /dev/null +++ b/boost/geometry/algorithms/detail/calculate_sum.hpp @@ -0,0 +1,64 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_SUM_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_SUM_HPP + + +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + + +template +< + typename ReturnType, + typename Polygon, + typename Strategy, + typename Policy +> +class calculate_polygon_sum +{ + template <typename Rings> + static inline ReturnType sum_interior_rings(Rings const& rings, Strategy const& strategy) + { + ReturnType sum = ReturnType(); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + sum += Policy::apply(*it, strategy); + } + return sum; + } + +public : + static inline ReturnType apply(Polygon const& poly, Strategy const& strategy) + { + return Policy::apply(exterior_ring(poly), strategy) + + sum_interior_rings(interior_rings(poly), strategy) + ; + } +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_SUM_HPP diff --git a/boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp b/boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp new file mode 100644 index 0000000000..d39824a61d --- /dev/null +++ b/boost/geometry/algorithms/detail/convert_indexed_to_indexed.hpp @@ -0,0 +1,80 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CONVERT_INDEXED_TO_INDEXED_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CONVERT_INDEXED_TO_INDEXED_HPP + + +#include <cstddef> + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/coordinate_type.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace conversion +{ + + +template +< + typename Source, + typename Destination, + std::size_t Dimension, + std::size_t DimensionCount +> +struct indexed_to_indexed +{ + static inline void apply(Source const& source, Destination& destination) + { + typedef typename coordinate_type<Destination>::type coordinate_type; + + geometry::set<min_corner, Dimension>(destination, + boost::numeric_cast<coordinate_type>( + geometry::get<min_corner, Dimension>(source))); + geometry::set<max_corner, Dimension>(destination, + boost::numeric_cast<coordinate_type>( + geometry::get<max_corner, Dimension>(source))); + + indexed_to_indexed + < + Source, Destination, + Dimension + 1, DimensionCount + >::apply(source, destination); + } +}; + +template +< + typename Source, + typename Destination, + std::size_t DimensionCount +> +struct indexed_to_indexed<Source, Destination, DimensionCount, DimensionCount> +{ + static inline void apply(Source const& , Destination& ) + {} +}; + + +}} // namespace detail::conversion +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CONVERT_INDEXED_TO_INDEXED_HPP diff --git a/boost/geometry/algorithms/detail/convert_point_to_point.hpp b/boost/geometry/algorithms/detail/convert_point_to_point.hpp new file mode 100644 index 0000000000..c7d37b6ca4 --- /dev/null +++ b/boost/geometry/algorithms/detail/convert_point_to_point.hpp @@ -0,0 +1,68 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CONVERT_POINT_TO_POINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CONVERT_POINT_TO_POINT_HPP + +// Note: extracted from "convert.hpp" to avoid circular references convert/append + +#include <cstddef> + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/coordinate_type.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace conversion +{ + + +template <typename Source, typename Destination, std::size_t Dimension, std::size_t DimensionCount> +struct point_to_point +{ + static inline void apply(Source const& source, Destination& destination) + { + typedef typename coordinate_type<Destination>::type coordinate_type; + + set<Dimension>(destination, boost::numeric_cast<coordinate_type>(get<Dimension>(source))); + point_to_point<Source, Destination, Dimension + 1, DimensionCount>::apply(source, destination); + } +}; + +template <typename Source, typename Destination, std::size_t DimensionCount> +struct point_to_point<Source, Destination, DimensionCount, DimensionCount> +{ + static inline void apply(Source const& , Destination& ) + {} +}; + + +template <typename Source, typename Destination> +inline void convert_point_to_point(Source const& source, Destination& destination) +{ + point_to_point<Source, Destination, 0, dimension<Destination>::value>::apply(source, destination); +} + + + +}} // namespace detail::conversion +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CONVERT_POINT_TO_POINT_HPP diff --git a/boost/geometry/algorithms/detail/disjoint.hpp b/boost/geometry/algorithms/detail/disjoint.hpp new file mode 100644 index 0000000000..2ced5b1ce3 --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint.hpp @@ -0,0 +1,225 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_HPP + +// Note: contrary to most files, the geometry::detail::disjoint namespace +// is partly implemented in a separate file, to avoid circular references +// disjoint -> get_turns -> disjoint + +#include <cstddef> + +#include <boost/range.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/reverse_dispatch.hpp> + + +#include <boost/geometry/util/math.hpp> + + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + + +struct disjoint_interrupt_policy +{ + static bool const enabled = true; + bool has_intersections; + + inline disjoint_interrupt_policy() + : has_intersections(false) + {} + + template <typename Range> + inline bool apply(Range const& range) + { + // If there is any IP in the range, it is NOT disjoint + if (boost::size(range) > 0) + { + has_intersections = true; + return true; + } + return false; + } +}; + + + +template +< + typename Point1, typename Point2, + std::size_t Dimension, std::size_t DimensionCount +> +struct point_point +{ + static inline bool apply(Point1 const& p1, Point2 const& p2) + { + if (! geometry::math::equals(get<Dimension>(p1), get<Dimension>(p2))) + { + return true; + } + return point_point + < + Point1, Point2, + Dimension + 1, DimensionCount + >::apply(p1, p2); + } +}; + + +template <typename Point1, typename Point2, std::size_t DimensionCount> +struct point_point<Point1, Point2, DimensionCount, DimensionCount> +{ + static inline bool apply(Point1 const& , Point2 const& ) + { + return false; + } +}; + + +template +< + typename Point, typename Box, + std::size_t Dimension, std::size_t DimensionCount +> +struct point_box +{ + static inline bool apply(Point const& point, Box const& box) + { + if (get<Dimension>(point) < get<min_corner, Dimension>(box) + || get<Dimension>(point) > get<max_corner, Dimension>(box)) + { + return true; + } + return point_box + < + Point, Box, + Dimension + 1, DimensionCount + >::apply(point, box); + } +}; + + +template <typename Point, typename Box, std::size_t DimensionCount> +struct point_box<Point, Box, DimensionCount, DimensionCount> +{ + static inline bool apply(Point const& , Box const& ) + { + return false; + } +}; + + +template +< + typename Box1, typename Box2, + std::size_t Dimension, std::size_t DimensionCount +> +struct box_box +{ + static inline bool apply(Box1 const& box1, Box2 const& box2) + { + if (get<max_corner, Dimension>(box1) < get<min_corner, Dimension>(box2)) + { + return true; + } + if (get<min_corner, Dimension>(box1) > get<max_corner, Dimension>(box2)) + { + return true; + } + return box_box + < + Box1, Box2, + Dimension + 1, DimensionCount + >::apply(box1, box2); + } +}; + + +template <typename Box1, typename Box2, std::size_t DimensionCount> +struct box_box<Box1, Box2, DimensionCount, DimensionCount> +{ + static inline bool apply(Box1 const& , Box2 const& ) + { + return false; + } +}; + + + +/*! + \brief Internal utility function to detect of boxes are disjoint + \note Is used from other algorithms, declared separately + to avoid circular references + */ +template <typename Box1, typename Box2> +inline bool disjoint_box_box(Box1 const& box1, Box2 const& box2) +{ + return box_box + < + Box1, Box2, + 0, dimension<Box1>::type::value + >::apply(box1, box2); +} + + + +/*! + \brief Internal utility function to detect of points are disjoint + \note To avoid circular references + */ +template <typename Point1, typename Point2> +inline bool disjoint_point_point(Point1 const& point1, Point2 const& point2) +{ + return point_point + < + Point1, Point2, + 0, dimension<Point1>::type::value + >::apply(point1, point2); +} + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace equals +{ + +/*! + \brief Internal utility function to detect of points are disjoint + \note To avoid circular references + */ +template <typename Point1, typename Point2> +inline bool equals_point_point(Point1 const& point1, Point2 const& point2) +{ + return ! detail::disjoint::disjoint_point_point(point1, point2); +} + + +}} // namespace detail::equals +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_HPP diff --git a/boost/geometry/algorithms/detail/equals/collect_vectors.hpp b/boost/geometry/algorithms/detail/equals/collect_vectors.hpp new file mode 100644 index 0000000000..9c2fe28057 --- /dev/null +++ b/boost/geometry/algorithms/detail/equals/collect_vectors.hpp @@ -0,0 +1,315 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP + + +#include <boost/numeric/conversion/cast.hpp> +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/multi/core/tags.hpp> + +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/util/math.hpp> + + + +namespace boost { namespace geometry +{ + +// TODO: if Boost.LA of Emil Dotchevski is accepted, adapt this +template <typename T> +struct collected_vector +{ + typedef T type; + + inline collected_vector() + {} + + inline collected_vector(T const& px, T const& py, + T const& pdx, T const& pdy) + : x(px) + , y(py) + , dx(pdx) + , dy(pdy) + , dx_0(T()) + , dy_0(T()) + {} + + T x, y; + T dx, dy; + T dx_0, dy_0; + + // For sorting + inline bool operator<(collected_vector<T> const& other) const + { + if (math::equals(x, other.x)) + { + if (math::equals(y, other.y)) + { + if (math::equals(dx, other.dx)) + { + return dy < other.dy; + } + return dx < other.dx; + } + return y < other.y; + } + return x < other.x; + } + + inline bool same_direction(collected_vector<T> const& other) const + { + // For high precision arithmetic, we have to be + // more relaxed then using == + // Because 2/sqrt( (0,0)<->(2,2) ) == 1/sqrt( (0,0)<->(1,1) ) + // is not always true (at least, it is not for ttmath) + return math::equals_with_epsilon(dx, other.dx) + && math::equals_with_epsilon(dy, other.dy); + } + + // For std::equals + inline bool operator==(collected_vector<T> const& other) const + { + return math::equals(x, other.x) + && math::equals(y, other.y) + && same_direction(other); + } +}; + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace collect_vectors +{ + + +template <typename Range, typename Collection> +struct range_collect_vectors +{ + typedef typename boost::range_value<Collection>::type item_type; + typedef typename item_type::type calculation_type; + + static inline void apply(Collection& collection, Range const& range) + { + if (boost::size(range) < 2) + { + return; + } + + typedef typename boost::range_iterator<Range const>::type iterator; + + bool first = true; + iterator it = boost::begin(range); + + for (iterator prev = it++; + it != boost::end(range); + prev = it++) + { + typename boost::range_value<Collection>::type v; + + v.x = get<0>(*prev); + v.y = get<1>(*prev); + v.dx = get<0>(*it) - v.x; + v.dy = get<1>(*it) - v.y; + v.dx_0 = v.dx; + v.dy_0 = v.dy; + + // Normalize the vector -> this results in points+direction + // and is comparible between geometries + calculation_type magnitude = sqrt( + boost::numeric_cast<calculation_type>(v.dx * v.dx + v.dy * v.dy)); + + // Avoid non-duplicate points (AND division by zero) + if (magnitude > 0) + { + v.dx /= magnitude; + v.dy /= magnitude; + + // Avoid non-direction changing points + if (first || ! v.same_direction(collection.back())) + { + collection.push_back(v); + } + first = false; + } + } + + // If first one has same direction as last one, remove first one + if (boost::size(collection) > 1 + && collection.front().same_direction(collection.back())) + { + collection.erase(collection.begin()); + } + } +}; + + +template <typename Box, typename Collection> +struct box_collect_vectors +{ + // Calculate on coordinate type, but if it is integer, + // then use double + typedef typename boost::range_value<Collection>::type item_type; + typedef typename item_type::type calculation_type; + + static inline void apply(Collection& collection, Box const& box) + { + typename point_type<Box>::type lower_left, lower_right, + upper_left, upper_right; + geometry::detail::assign_box_corners(box, lower_left, lower_right, + upper_left, upper_right); + + typedef typename boost::range_value<Collection>::type item; + + collection.push_back(item(get<0>(lower_left), get<1>(lower_left), 0, 1)); + collection.push_back(item(get<0>(upper_left), get<1>(upper_left), 1, 0)); + collection.push_back(item(get<0>(upper_right), get<1>(upper_right), 0, -1)); + collection.push_back(item(get<0>(lower_right), get<1>(lower_right), -1, 0)); + } +}; + + +template <typename Polygon, typename Collection> +struct polygon_collect_vectors +{ + static inline void apply(Collection& collection, Polygon const& polygon) + { + typedef typename geometry::ring_type<Polygon>::type ring_type; + + typedef range_collect_vectors<ring_type, Collection> per_range; + per_range::apply(collection, exterior_ring(polygon)); + + typename interior_return_type<Polygon const>::type rings + = interior_rings(polygon); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + per_range::apply(collection, *it); + } + } +}; + + +template <typename MultiGeometry, typename Collection, typename SinglePolicy> +struct multi_collect_vectors +{ + static inline void apply(Collection& collection, MultiGeometry const& multi) + { + for (typename boost::range_iterator<MultiGeometry const>::type + it = boost::begin(multi); + it != boost::end(multi); + ++it) + { + SinglePolicy::apply(collection, *it); + } + } +}; + + +}} // namespace detail::collect_vectors +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag, + typename Collection, + typename Geometry +> +struct collect_vectors +{ + static inline void apply(Collection&, Geometry const&) + {} +}; + + +template <typename Collection, typename Box> +struct collect_vectors<box_tag, Collection, Box> + : detail::collect_vectors::box_collect_vectors<Box, Collection> +{}; + + + +template <typename Collection, typename Ring> +struct collect_vectors<ring_tag, Collection, Ring> + : detail::collect_vectors::range_collect_vectors<Ring, Collection> +{}; + + +template <typename Collection, typename LineString> +struct collect_vectors<linestring_tag, Collection, LineString> + : detail::collect_vectors::range_collect_vectors<LineString, Collection> +{}; + + +template <typename Collection, typename Polygon> +struct collect_vectors<polygon_tag, Collection, Polygon> + : detail::collect_vectors::polygon_collect_vectors<Polygon, Collection> +{}; + + +template <typename Collection, typename MultiPolygon> +struct collect_vectors<multi_polygon_tag, Collection, MultiPolygon> + : detail::collect_vectors::multi_collect_vectors + < + MultiPolygon, + Collection, + detail::collect_vectors::polygon_collect_vectors + < + typename boost::range_value<MultiPolygon>::type, + Collection + > + > +{}; + + + +} // namespace dispatch +#endif + + +/*! + \ingroup collect_vectors + \tparam Collection Collection type, should be e.g. std::vector<> + \tparam Geometry geometry type + \param collection the collection of vectors + \param geometry the geometry to make collect_vectors +*/ +template <typename Collection, typename Geometry> +inline void collect_vectors(Collection& collection, Geometry const& geometry) +{ + concept::check<Geometry const>(); + + dispatch::collect_vectors + < + typename tag<Geometry>::type, + Collection, + Geometry + >::apply(collection, geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP diff --git a/boost/geometry/algorithms/detail/for_each_range.hpp b/boost/geometry/algorithms/detail/for_each_range.hpp new file mode 100644 index 0000000000..b10017c209 --- /dev/null +++ b/boost/geometry/algorithms/detail/for_each_range.hpp @@ -0,0 +1,148 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_FOR_EACH_RANGE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_FOR_EACH_RANGE_HPP + + +#include <boost/mpl/assert.hpp> +#include <boost/concept/requires.hpp> + +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tag_cast.hpp> + +#include <boost/geometry/util/add_const_if_c.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace for_each +{ + + +template <typename Range, typename Actor, bool IsConst> +struct fe_range_range +{ + static inline void apply( + typename add_const_if_c<IsConst, Range>::type& range, + Actor& actor) + { + actor.apply(range); + } +}; + + +template <typename Polygon, typename Actor, bool IsConst> +struct fe_range_polygon +{ + static inline void apply( + typename add_const_if_c<IsConst, Polygon>::type& polygon, + Actor& actor) + { + actor.apply(exterior_ring(polygon)); + + // TODO: If some flag says true, also do the inner rings. + // for convex hull, it's not necessary + } +}; + +template <typename Box, typename Actor, bool IsConst> +struct fe_range_box +{ + static inline void apply( + typename add_const_if_c<IsConst, Box>::type& box, + Actor& actor) + { + actor.apply(box_view<Box>(box)); + } +}; + + +}} // namespace detail::for_each +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag, + typename Geometry, + typename Actor, + bool IsConst +> +struct for_each_range +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + + +template <typename Linestring, typename Actor, bool IsConst> +struct for_each_range<linestring_tag, Linestring, Actor, IsConst> + : detail::for_each::fe_range_range<Linestring, Actor, IsConst> +{}; + + +template <typename Ring, typename Actor, bool IsConst> +struct for_each_range<ring_tag, Ring, Actor, IsConst> + : detail::for_each::fe_range_range<Ring, Actor, IsConst> +{}; + + +template <typename Polygon, typename Actor, bool IsConst> +struct for_each_range<polygon_tag, Polygon, Actor, IsConst> + : detail::for_each::fe_range_polygon<Polygon, Actor, IsConst> +{}; + +template <typename Box, typename Actor, bool IsConst> +struct for_each_range<box_tag, Box, Actor, IsConst> + : detail::for_each::fe_range_box<Box, Actor, IsConst> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +namespace detail +{ + +template <typename Geometry, typename Actor> +inline void for_each_range(Geometry const& geometry, Actor& actor) +{ + dispatch::for_each_range + < + typename tag<Geometry>::type, + Geometry, + Actor, + true + >::apply(geometry, actor); +} + + +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_FOR_EACH_RANGE_HPP diff --git a/boost/geometry/algorithms/detail/has_self_intersections.hpp b/boost/geometry/algorithms/detail/has_self_intersections.hpp new file mode 100644 index 0000000000..1e6215ed93 --- /dev/null +++ b/boost/geometry/algorithms/detail/has_self_intersections.hpp @@ -0,0 +1,120 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2011-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_HAS_SELF_INTERSECTIONS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_HAS_SELF_INTERSECTIONS_HPP + +#include <deque> + +#include <boost/range.hpp> +#include <boost/geometry/core/point_type.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> +#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp> + +#include <boost/geometry/multi/algorithms/detail/overlay/self_turn_points.hpp> + +#ifdef BOOST_GEOMETRY_DEBUG_HAS_SELF_INTERSECTIONS +# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> +# include <boost/geometry/io/dsv/write.hpp> +#endif + + +namespace boost { namespace geometry +{ + + +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + +/*! +\brief Overlay Invalid Input Exception +\ingroup overlay +\details The overlay_invalid_input_exception is thrown at invalid input + */ +class overlay_invalid_input_exception : public geometry::exception +{ +public: + + inline overlay_invalid_input_exception() {} + + virtual char const* what() const throw() + { + return "Boost.Geometry Overlay invalid input exception"; + } +}; + +#endif + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template <typename Geometry> +inline bool has_self_intersections(Geometry const& geometry) +{ + typedef typename point_type<Geometry>::type point_type; + typedef detail::overlay::turn_info<point_type> turn_info; + std::deque<turn_info> turns; + detail::disjoint::disjoint_interrupt_policy policy; + geometry::self_turns<detail::overlay::assign_null_policy>(geometry, turns, policy); + +#ifdef BOOST_GEOMETRY_DEBUG_HAS_SELF_INTERSECTIONS + bool first = true; +#endif + for(typename std::deque<turn_info>::const_iterator it = boost::begin(turns); + it != boost::end(turns); ++it) + { + turn_info const& info = *it; + bool const both_union_turn = + info.operations[0].operation == detail::overlay::operation_union + && info.operations[1].operation == detail::overlay::operation_union; + bool const both_intersection_turn = + info.operations[0].operation == detail::overlay::operation_intersection + && info.operations[1].operation == detail::overlay::operation_intersection; + + bool const valid = (both_union_turn || both_intersection_turn) + && (info.method == detail::overlay::method_touch + || info.method == detail::overlay::method_touch_interior); + + if (! valid) + { +#ifdef BOOST_GEOMETRY_DEBUG_HAS_SELF_INTERSECTIONS + if (first) + { + std::cout << "turn points: " << std::endl; + first = false; + } + std::cout << method_char(info.method); + for (int i = 0; i < 2; i++) + { + std::cout << " " << operation_char(info.operations[i].operation); + } + std::cout << " " << geometry::dsv(info.point) << std::endl; +#endif + +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + throw overlay_invalid_input_exception(); +#endif + } + + } + return false; +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_HAS_SELF_INTERSECTIONS_HPP + diff --git a/boost/geometry/algorithms/detail/not.hpp b/boost/geometry/algorithms/detail/not.hpp new file mode 100644 index 0000000000..abc3a4e168 --- /dev/null +++ b/boost/geometry/algorithms/detail/not.hpp @@ -0,0 +1,50 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_NOT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_NOT_HPP + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + + + +/*! +\brief Structure negating the result of specified policy +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Policy +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return Negation of the result of the policy + */ +template <typename Geometry1, typename Geometry2, typename Policy> +struct not_ +{ + static inline bool apply(Geometry1 const &geometry1, Geometry2 const& geometry2) + { + return ! Policy::apply(geometry1, geometry2); + } +}; + + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_NOT_HPP diff --git a/boost/geometry/algorithms/detail/overlay/add_rings.hpp b/boost/geometry/algorithms/detail/overlay/add_rings.hpp new file mode 100644 index 0000000000..eb3e60e483 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/add_rings.hpp @@ -0,0 +1,134 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ADD_RINGS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ADD_RINGS_HPP + +#include <boost/geometry/algorithms/detail/overlay/convert_ring.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename RingCollection +> +inline void convert_and_add(GeometryOut& result, + Geometry1 const& geometry1, Geometry2 const& geometry2, + RingCollection const& collection, + ring_identifier id, + bool reversed, bool append) +{ + typedef typename geometry::tag<Geometry1>::type tag1; + typedef typename geometry::tag<Geometry2>::type tag2; + typedef typename geometry::tag<GeometryOut>::type tag_out; + + if (id.source_index == 0) + { + convert_ring<tag_out>::apply(result, + get_ring<tag1>::apply(id, geometry1), + append, reversed); + } + else if (id.source_index == 1) + { + convert_ring<tag_out>::apply(result, + get_ring<tag2>::apply(id, geometry2), + append, reversed); + } + else if (id.source_index == 2) + { + convert_ring<tag_out>::apply(result, + get_ring<void>::apply(id, collection), + append, reversed); + } +} + +template +< + typename GeometryOut, + typename SelectionMap, + typename Geometry1, + typename Geometry2, + typename RingCollection, + typename OutputIterator +> +inline OutputIterator add_rings(SelectionMap const& map, + Geometry1 const& geometry1, Geometry2 const& geometry2, + RingCollection const& collection, + OutputIterator out) +{ + typedef typename SelectionMap::const_iterator iterator; + + for (iterator it = boost::begin(map); + it != boost::end(map); + ++it) + { + if (! it->second.discarded + && it->second.parent.source_index == -1) + { + GeometryOut result; + convert_and_add(result, geometry1, geometry2, collection, + it->first, it->second.reversed, false); + + // Add children + for (typename std::vector<ring_identifier>::const_iterator child_it + = it->second.children.begin(); + child_it != it->second.children.end(); + ++child_it) + { + iterator mit = map.find(*child_it); + if (mit != map.end() + && ! mit->second.discarded) + { + convert_and_add(result, geometry1, geometry2, collection, + *child_it, mit->second.reversed, true); + } + } + *out++ = result; + } + } + return out; +} + + +template +< + typename GeometryOut, + typename SelectionMap, + typename Geometry, + typename RingCollection, + typename OutputIterator +> +inline OutputIterator add_rings(SelectionMap const& map, + Geometry const& geometry, + RingCollection const& collection, + OutputIterator out) +{ + Geometry empty; + return add_rings<GeometryOut>(map, geometry, empty, collection, out); +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ADD_RINGS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp b/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp new file mode 100644 index 0000000000..2c0f88e2aa --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp @@ -0,0 +1,53 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPLICATES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPLICATES_HPP + + +#include <boost/range.hpp> + +#include <boost/geometry/algorithms/append.hpp> +#include <boost/geometry/algorithms/detail/disjoint.hpp> + + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template <typename Range, typename Point> +inline void append_no_duplicates(Range& range, Point const& point, bool force = false) +{ + if (boost::size(range) == 0 + || force + || ! geometry::detail::equals::equals_point_point(*(boost::end(range)-1), point)) + { +#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION + std::cout << " add: (" + << geometry::get<0>(point) << ", " << geometry::get<1>(point) << ")" + << std::endl; +#endif + geometry::append(range, point); + } +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_APPEND_NO_DUPLICATES_HPP diff --git a/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp new file mode 100644 index 0000000000..133530563e --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -0,0 +1,337 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP + +#include <boost/geometry/algorithms/area.hpp> +#include <boost/geometry/algorithms/envelope.hpp> +#include <boost/geometry/algorithms/expand.hpp> +#include <boost/geometry/algorithms/detail/partition.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_ring.hpp> +#include <boost/geometry/algorithms/within.hpp> + +#include <boost/geometry/geometries/box.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + + +template +< + typename Item, + typename Geometry1, typename Geometry2, + typename RingCollection +> +static inline bool within_selected_input(Item const& item2, ring_identifier const& ring_id, + Geometry1 const& geometry1, Geometry2 const& geometry2, + RingCollection const& collection) +{ + typedef typename geometry::tag<Geometry1>::type tag1; + typedef typename geometry::tag<Geometry2>::type tag2; + + switch (ring_id.source_index) + { + case 0 : + return geometry::within(item2.point, + get_ring<tag1>::apply(ring_id, geometry1)); + break; + case 1 : + return geometry::within(item2.point, + get_ring<tag2>::apply(ring_id, geometry2)); + break; + case 2 : + return geometry::within(item2.point, + get_ring<void>::apply(ring_id, collection)); + break; + } + return false; +} + + +template <typename Point> +struct ring_info_helper +{ + typedef typename geometry::default_area_result<Point>::type area_type; + + ring_identifier id; + area_type real_area; + area_type abs_area; + model::box<Point> envelope; + + inline ring_info_helper() + : real_area(0), abs_area(0) + {} + + inline ring_info_helper(ring_identifier i, area_type a) + : id(i), real_area(a), abs_area(geometry::math::abs(a)) + {} +}; + + +struct ring_info_helper_get_box +{ + template <typename Box, typename InputItem> + static inline void apply(Box& total, InputItem const& item) + { + geometry::expand(total, item.envelope); + } +}; + +struct ring_info_helper_ovelaps_box +{ + template <typename Box, typename InputItem> + static inline bool apply(Box const& box, InputItem const& item) + { + return ! geometry::detail::disjoint::disjoint_box_box(box, item.envelope); + } +}; + +template <typename Geometry1, typename Geometry2, typename Collection, typename RingMap> +struct assign_visitor +{ + typedef typename RingMap::mapped_type ring_info_type; + + Geometry1 const& m_geometry1; + Geometry2 const& m_geometry2; + Collection const& m_collection; + RingMap& m_ring_map; + bool m_check_for_orientation; + + + inline assign_visitor(Geometry1 const& g1, Geometry2 const& g2, Collection const& c, + RingMap& map, bool check) + : m_geometry1(g1) + , m_geometry2(g2) + , m_collection(c) + , m_ring_map(map) + , m_check_for_orientation(check) + {} + + template <typename Item> + inline void apply(Item const& outer, Item const& inner, bool first = true) + { + if (first && outer.real_area < 0) + { + // Reverse arguments + apply(inner, outer, false); + return; + } + + if (outer.real_area > 0) + { + if (inner.real_area < 0 || m_check_for_orientation) + { + ring_info_type& inner_in_map = m_ring_map[inner.id]; + + if (geometry::within(inner_in_map.point, outer.envelope) + && within_selected_input(inner_in_map, outer.id, m_geometry1, m_geometry2, m_collection) + ) + { + // Only assign parent if that parent is smaller (or if it is the first) + if (inner_in_map.parent.source_index == -1 + || outer.abs_area < inner_in_map.parent_area) + { + inner_in_map.parent = outer.id; + inner_in_map.parent_area = outer.abs_area; + } + } + } + } + } +}; + + + + +template +< + typename Geometry1, typename Geometry2, + typename RingCollection, + typename RingMap +> +inline void assign_parents(Geometry1 const& geometry1, + Geometry2 const& geometry2, + RingCollection const& collection, + RingMap& ring_map, + bool check_for_orientation = false) +{ + typedef typename geometry::tag<Geometry1>::type tag1; + typedef typename geometry::tag<Geometry2>::type tag2; + + typedef typename RingMap::mapped_type ring_info_type; + typedef typename ring_info_type::point_type point_type; + typedef model::box<point_type> box_type; + + typedef typename RingMap::iterator map_iterator_type; + + { + typedef ring_info_helper<point_type> helper; + typedef std::vector<helper> vector_type; + typedef typename boost::range_iterator<vector_type const>::type vector_iterator_type; + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + boost::timer timer; +#endif + + + std::size_t count_total = ring_map.size(); + std::size_t count_positive = 0; + std::size_t index_positive = 0; // only used if count_positive>0 + std::size_t index = 0; + + // Copy to vector (with new approach this might be obsolete as well, using the map directly) + vector_type vector(count_total); + + for (map_iterator_type it = boost::begin(ring_map); + it != boost::end(ring_map); ++it, ++index) + { + vector[index] = helper(it->first, it->second.get_area()); + helper& item = vector[index]; + switch(it->first.source_index) + { + case 0 : + geometry::envelope(get_ring<tag1>::apply(it->first, geometry1), + item.envelope); + break; + case 1 : + geometry::envelope(get_ring<tag2>::apply(it->first, geometry2), + item.envelope); + break; + case 2 : + geometry::envelope(get_ring<void>::apply(it->first, collection), + item.envelope); + break; + } + if (item.real_area > 0) + { + count_positive++; + index_positive = index; + } + } + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << " ap: created helper vector: " << timer.elapsed() << std::endl; +#endif + + if (! check_for_orientation) + { + if (count_positive == count_total) + { + // Optimization for only positive rings + // -> no assignment of parents or reversal necessary, ready here. + return; + } + + if (count_positive == 1) + { + // Optimization for one outer ring + // -> assign this as parent to all others (all interior rings) + // In unions, this is probably the most occuring case and gives + // a dramatic improvement (factor 5 for star_comb testcase) + ring_identifier id_of_positive = vector[index_positive].id; + ring_info_type& outer = ring_map[id_of_positive]; + std::size_t index = 0; + for (vector_iterator_type it = boost::begin(vector); + it != boost::end(vector); ++it, ++index) + { + if (index != index_positive) + { + ring_info_type& inner = ring_map[it->id]; + inner.parent = id_of_positive; + outer.children.push_back(it->id); + } + } + return; + } + } + + assign_visitor + < + Geometry1, Geometry2, + RingCollection, RingMap + > visitor(geometry1, geometry2, collection, ring_map, check_for_orientation); + + geometry::partition + < + box_type, ring_info_helper_get_box, ring_info_helper_ovelaps_box + >::apply(vector, visitor); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << " ap: quadradic loop: " << timer.elapsed() << std::endl; + std::cout << " ap: check_for_orientation " << check_for_orientation << std::endl; +#endif + } + + if (check_for_orientation) + { + for (map_iterator_type it = boost::begin(ring_map); + it != boost::end(ring_map); ++it) + { + if (geometry::math::equals(it->second.get_area(), 0)) + { + it->second.discarded = true; + } + else if (it->second.parent.source_index >= 0 && it->second.get_area() > 0) + { + // Discard positive inner ring with parent + it->second.discarded = true; + it->second.parent.source_index = -1; + } + else if (it->second.parent.source_index < 0 && it->second.get_area() < 0) + { + // Reverse negative ring without parent + it->second.reversed = true; + } + } + } + + // Assign childlist + for (map_iterator_type it = boost::begin(ring_map); + it != boost::end(ring_map); ++it) + { + if (it->second.parent.source_index >= 0) + { + ring_map[it->second.parent].children.push_back(it->first); + } + } +} + +template +< + typename Geometry, + typename RingCollection, + typename RingMap +> +inline void assign_parents(Geometry const& geometry, + RingCollection const& collection, + RingMap& ring_map) +{ + // Call it with an empty geometry + // (ring_map should be empty for source_id==1) + + Geometry empty; + assign_parents(geometry, empty, collection, ring_map, true); +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ASSIGN_PARENTS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp b/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp new file mode 100644 index 0000000000..012b3aca30 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp @@ -0,0 +1,170 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_BACKTRACK_CHECK_SI_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_BACKTRACK_CHECK_SI_HPP + +#include <cstddef> +#include <string> + +#include <boost/range.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/detail/has_self_intersections.hpp> +#if defined(BOOST_GEOMETRY_DEBUG_INTERSECTION) || defined(BOOST_GEOMETRY_OVERLAY_REPORT_WKT) +# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> +# include <boost/geometry/io/wkt/wkt.hpp> +#endif + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template <typename Turns> +inline void clear_visit_info(Turns& turns) +{ + typedef typename boost::range_value<Turns>::type tp_type; + + for (typename boost::range_iterator<Turns>::type + it = boost::begin(turns); + it != boost::end(turns); + ++it) + { + for (typename boost::range_iterator + < + typename tp_type::container_type + >::type op_it = boost::begin(it->operations); + op_it != boost::end(it->operations); + ++op_it) + { + op_it->visited.clear(); + } + it->discarded = false; + } +} + +struct backtrack_state +{ + bool m_good; + + inline backtrack_state() : m_good(true) {} + inline void reset() { m_good = true; } + inline bool good() const { return m_good; } +}; + + +template +< + typename Geometry1, + typename Geometry2 +> +class backtrack_check_self_intersections +{ + struct state : public backtrack_state + { + bool m_checked; + inline state() + : m_checked() + {} + }; +public : + typedef state state_type; + + template <typename Operation, typename Rings, typename Turns> + static inline void apply(std::size_t size_at_start, + Rings& rings, typename boost::range_value<Rings>::type& ring, + Turns& turns, Operation& operation, + std::string const& , + Geometry1 const& geometry1, + Geometry2 const& geometry2, + state_type& state + ) + { + state.m_good = false; + + // Check self-intersections and throw exception if appropriate + if (! state.m_checked) + { + state.m_checked = true; + has_self_intersections(geometry1); + has_self_intersections(geometry2); + } + + // Make bad output clean + rings.resize(size_at_start); + ring.clear(); + + // Reject this as a starting point + operation.visited.set_rejected(); + + // And clear all visit info + clear_visit_info(turns); + } +}; + +#ifdef BOOST_GEOMETRY_OVERLAY_REPORT_WKT +template +< + typename Geometry1, + typename Geometry2 +> +class backtrack_debug +{ +public : + typedef backtrack_state state_type; + + template <typename Operation, typename Rings, typename Turns> + static inline void apply(std::size_t size_at_start, + Rings& rings, typename boost::range_value<Rings>::type& ring, + Turns& turns, Operation& operation, + std::string const& reason, + Geometry1 const& geometry1, + Geometry2 const& geometry2, + state_type& state + ) + { + std::cout << " REJECT " << reason << std::endl; + + state.m_good = false; + + rings.resize(size_at_start); + ring.clear(); + operation.visited.set_rejected(); + clear_visit_info(turns); + + int c = 0; + for (int i = 0; i < turns.size(); i++) + { + for (int j = 0; j < 2; j++) + { + if (turns[i].operations[j].visited.rejected()) + { + c++; + } + } + } + std::cout << "BACKTRACK (" << reason << " )" + << " " << c << " of " << turns.size() << " rejected" + << std::endl; + std::cout + << geometry::wkt(geometry1) << std::endl + << geometry::wkt(geometry2) << std::endl; + } +}; +#endif // BOOST_GEOMETRY_OVERLAY_REPORT_WKT + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_BACKTRACK_CHECK_SI_HPP diff --git a/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp b/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp new file mode 100644 index 0000000000..3e6a8897f5 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp @@ -0,0 +1,55 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CALCULATE_DISTANCE_POLICY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CALCULATE_DISTANCE_POLICY_HPP + + +#include <boost/geometry/algorithms/comparable_distance.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +/*! + \brief Policy calculating distance + \details get_turn_info has an optional policy to get some + extra information. + This policy calculates the distance (using default distance strategy) + */ +struct calculate_distance_policy +{ + static bool const include_no_turn = false; + static bool const include_degenerate = false; + + template <typename Point1, typename Point2, typename Info> + static inline void apply(Info& info, Point1 const& p1, Point2 const& p2) + { + info.operations[0].enriched.distance + = geometry::comparable_distance(info.point, p1); + info.operations[1].enriched.distance + = geometry::comparable_distance(info.point, p2); + } + +}; + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CALCULATE_DISTANCE_POLICY_HPP diff --git a/boost/geometry/algorithms/detail/overlay/check_enrich.hpp b/boost/geometry/algorithms/detail/overlay/check_enrich.hpp new file mode 100644 index 0000000000..b210fd04b1 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/check_enrich.hpp @@ -0,0 +1,172 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CHECK_ENRICH_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CHECK_ENRICH_HPP + + +#include <cstddef> + + +#include <boost/assert.hpp> +#include <boost/range.hpp> + + + +#include <boost/geometry/algorithms/detail/ring_identifier.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template<typename Turn> +struct meta_turn +{ + int index; + Turn const* turn; + bool handled[2]; + + inline meta_turn(int i, Turn const& t) + : index(i), turn(&t) + { + handled[0] = false; + handled[1] = false; + } +}; + + +template <typename MetaTurn> +inline void display(MetaTurn const& meta_turn, std::string const& reason = "") +{ +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << meta_turn.index + << "\tMethods: " << method_char(meta_turn.turn->method) + << " operations: " << operation_char(meta_turn.turn->operations[0].operation) + << operation_char(meta_turn.turn->operations[1].operation) + << " travels to " << meta_turn.turn->operations[0].enriched.travels_to_ip_index + << " and " << meta_turn.turn->operations[1].enriched.travels_to_ip_index + //<< " -> " << op_index + << " " << reason + << std::endl; +#endif +} + + +template <typename MetaTurns, typename MetaTurn> +inline void check_detailed(MetaTurns& meta_turns, MetaTurn const& meta_turn, + int op_index, int cycle, int start, operation_type for_operation, + bool& error) +{ + display(meta_turn); + int const ip_index = meta_turn.turn->operations[op_index].enriched.travels_to_ip_index; + if (ip_index >= 0) + { + bool found = false; + + if (ip_index == start) + { + display(meta_turns[ip_index], " FINISH"); + return; + } + + // check on continuing, or on same-operation-on-same-geometry + if (! meta_turns[ip_index].handled[op_index] + && (meta_turns[ip_index].turn->operations[op_index].operation == operation_continue + || meta_turns[ip_index].turn->operations[op_index].operation == for_operation) + ) + { + meta_turns[ip_index].handled[op_index] = true; + check_detailed(meta_turns, meta_turns[ip_index], op_index, cycle, start, for_operation, error); + found = true; + } + // check on other geometry + if (! found) + { + int const other_index = 1 - op_index; + if (! meta_turns[ip_index].handled[other_index] + && meta_turns[ip_index].turn->operations[other_index].operation == for_operation) + { + meta_turns[ip_index].handled[other_index] = true; + check_detailed(meta_turns, meta_turns[ip_index], other_index, cycle, start, for_operation, error); + found = true; + } + } + + if (! found) + { + display(meta_turns[ip_index], " STOP"); + error = true; +#ifndef BOOST_GEOMETRY_DEBUG_ENRICH + //std::cout << " STOP"; +#endif + } + } +} + + +template <typename TurnPoints> +inline bool check_graph(TurnPoints& turn_points, operation_type for_operation) +{ + typedef typename boost::range_value<TurnPoints>::type turn_point_type; + + bool error = false; + int index = 0; + + std::vector<meta_turn<turn_point_type> > meta_turns; + for (typename boost::range_iterator<TurnPoints const>::type + it = boost::begin(turn_points); + it != boost::end(turn_points); + ++it, ++index) + { + meta_turns.push_back(meta_turn<turn_point_type>(index, *it)); + } + + int cycle = 0; + for (typename boost::range_iterator<std::vector<meta_turn<turn_point_type> > > ::type + it = boost::begin(meta_turns); + it != boost::end(meta_turns); + ++it) + { + if (! (it->turn->blocked() || it->turn->is_discarded())) + { + for (int i = 0 ; i < 2; i++) + { + if (! it->handled[i] + && it->turn->operations[i].operation == for_operation) + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << "CYCLE " << cycle << std::endl; +#endif + it->handled[i] = true; + check_detailed(meta_turns, *it, i, cycle++, it->index, for_operation, error); +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout <<" END CYCLE " << it->index << std::endl; +#endif + } + } + } + } + return error; +} + + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CHECK_ENRICH_HPP diff --git a/boost/geometry/algorithms/detail/overlay/clip_linestring.hpp b/boost/geometry/algorithms/detail/overlay/clip_linestring.hpp new file mode 100644 index 0000000000..fc4f657322 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/clip_linestring.hpp @@ -0,0 +1,242 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CLIP_LINESTRING_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CLIP_LINESTRING_HPP + +#include <boost/range.hpp> + +#include <boost/geometry/algorithms/clear.hpp> +#include <boost/geometry/algorithms/convert.hpp> + +#include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp> + +#include <boost/geometry/util/select_coordinate_type.hpp> +#include <boost/geometry/geometries/segment.hpp> + +namespace boost { namespace geometry +{ + +namespace strategy { namespace intersection +{ + +/*! + \brief Strategy: line clipping algorithm after Liang Barsky + \ingroup overlay + \details The Liang-Barsky line clipping algorithm clips a line with a clipping box. + It is slightly adapted in the sense that it returns which points are clipped + \tparam B input box type of clipping box + \tparam P input/output point-type of segments to be clipped + \note The algorithm is currently only implemented for 2D Cartesian points + \note Though it is implemented in namespace strategy, and theoretically another + strategy could be used, it is not (yet) updated to the general strategy concepts, + and not (yet) splitted into a file in folder strategies + \author Barend Gehrels, and the following recourses + - A tutorial: http://www.skytopia.com/project/articles/compsci/clipping.html + - a German applet (link broken): http://ls7-www.cs.uni-dortmund.de/students/projectgroups/acit/lineclip.shtml +*/ +template<typename Box, typename Point> +class liang_barsky +{ +private: + typedef model::referring_segment<Point> segment_type; + + template <typename T> + inline bool check_edge(T const& p, T const& q, T& t1, T& t2) const + { + bool visible = true; + + if(p < 0) + { + T const r = q / p; + if (r > t2) + visible = false; + else if (r > t1) + t1 = r; + } + else if(p > 0) + { + T const r = q / p; + if (r < t1) + visible = false; + else if (r < t2) + t2 = r; + } + else + { + if (q < 0) + visible = false; + } + + return visible; + } + +public: + + inline bool clip_segment(Box const& b, segment_type& s, bool& sp1_clipped, bool& sp2_clipped) const + { + typedef typename select_coordinate_type<Box, Point>::type coordinate_type; + + coordinate_type t1 = 0; + coordinate_type t2 = 1; + + coordinate_type const dx = get<1, 0>(s) - get<0, 0>(s); + coordinate_type const dy = get<1, 1>(s) - get<0, 1>(s); + + coordinate_type const p1 = -dx; + coordinate_type const p2 = dx; + coordinate_type const p3 = -dy; + coordinate_type const p4 = dy; + + coordinate_type const q1 = get<0, 0>(s) - get<min_corner, 0>(b); + coordinate_type const q2 = get<max_corner, 0>(b) - get<0, 0>(s); + coordinate_type const q3 = get<0, 1>(s) - get<min_corner, 1>(b); + coordinate_type const q4 = get<max_corner, 1>(b) - get<0, 1>(s); + + if (check_edge(p1, q1, t1, t2) // left + && check_edge(p2, q2, t1, t2) // right + && check_edge(p3, q3, t1, t2) // bottom + && check_edge(p4, q4, t1, t2)) // top + { + sp1_clipped = t1 > 0; + sp2_clipped = t2 < 1; + + if (sp2_clipped) + { + set<1, 0>(s, get<0, 0>(s) + t2 * dx); + set<1, 1>(s, get<0, 1>(s) + t2 * dy); + } + + if(sp1_clipped) + { + set<0, 0>(s, get<0, 0>(s) + t1 * dx); + set<0, 1>(s, get<0, 1>(s) + t1 * dy); + } + + return true; + } + + return false; + } + + template<typename Linestring, typename OutputIterator> + inline void apply(Linestring& line_out, OutputIterator out) const + { + if (!boost::empty(line_out)) + { + *out = line_out; + ++out; + geometry::clear(line_out); + } + } +}; + + +}} // namespace strategy::intersection + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersection +{ + +/*! + \brief Clips a linestring with a box + \details A linestring is intersected (clipped) by the specified box + and the resulting linestring, or pieces of linestrings, are sent to the specified output operator. + \tparam OutputLinestring type of the output linestrings + \tparam OutputIterator an output iterator which outputs linestrings + \tparam Linestring linestring-type, for example a vector of points, matching the output-iterator type, + the points should also match the input-iterator type + \tparam Box box type + \tparam Strategy strategy, a clipping strategy which should implement the methods "clip_segment" and "apply" +*/ +template +< + typename OutputLinestring, + typename OutputIterator, + typename Range, + typename Box, + typename Strategy +> +OutputIterator clip_range_with_box(Box const& b, Range const& range, + OutputIterator out, Strategy const& strategy) +{ + if (boost::begin(range) == boost::end(range)) + { + return out; + } + + typedef typename point_type<OutputLinestring>::type point_type; + + OutputLinestring line_out; + + typedef typename boost::range_iterator<Range const>::type iterator_type; + iterator_type vertex = boost::begin(range); + for(iterator_type previous = vertex++; + vertex != boost::end(range); + ++previous, ++vertex) + { + point_type p1, p2; + geometry::convert(*previous, p1); + geometry::convert(*vertex, p2); + + // Clip the segment. Five situations: + // 1. Segment is invisible, finish line if any (shouldn't occur) + // 2. Segment is completely visible. Add (p1)-p2 to line + // 3. Point 1 is invisible (clipped), point 2 is visible. Start new line from p1-p2... + // 4. Point 1 is visible, point 2 is invisible (clipped). End the line with ...p2 + // 5. Point 1 and point 2 are both invisible (clipped). Start/finish an independant line p1-p2 + // + // This results in: + // a. if p1 is clipped, start new line + // b. if segment is partly or completely visible, add the segment + // c. if p2 is clipped, end the line + + bool c1 = false; + bool c2 = false; + model::referring_segment<point_type> s(p1, p2); + + if (!strategy.clip_segment(b, s, c1, c2)) + { + strategy.apply(line_out, out); + } + else + { + // a. If necessary, finish the line and add a start a new one + if (c1) + { + strategy.apply(line_out, out); + } + + // b. Add p1 only if it is the first point, then add p2 + if (boost::empty(line_out)) + { + detail::overlay::append_no_duplicates(line_out, p1, true); + } + detail::overlay::append_no_duplicates(line_out, p2); + + // c. If c2 is clipped, finish the line + if (c2) + { + strategy.apply(line_out, out); + } + } + + } + + // Add last part + strategy.apply(line_out, out); + return out; +} + +}} // namespace detail::intersection +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CLIP_LINESTRING_HPP diff --git a/boost/geometry/algorithms/detail/overlay/convert_ring.hpp b/boost/geometry/algorithms/detail/overlay/convert_ring.hpp new file mode 100644 index 0000000000..05bd721e7f --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/convert_ring.hpp @@ -0,0 +1,99 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CONVERT_RING_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CONVERT_RING_HPP + + +#include <boost/mpl/assert.hpp> +#include <boost/range.hpp> +#include <boost/range/algorithm/reverse.hpp> + +#include <boost/geometry/core/tags.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/algorithms/detail/ring_identifier.hpp> + +#include <boost/geometry/algorithms/convert.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template<typename Tag> +struct convert_ring +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TAG + , (types<Tag>) + ); +}; + +template<> +struct convert_ring<ring_tag> +{ + template<typename Destination, typename Source> + static inline void apply(Destination& destination, Source const& source, + bool append, bool reverse) + { + if (! append) + { + geometry::convert(source, destination); + if (reverse) + { + boost::reverse(destination); + } + } + } +}; + + +template<> +struct convert_ring<polygon_tag> +{ + template<typename Destination, typename Source> + static inline void apply(Destination& destination, Source const& source, + bool append, bool reverse) + { + if (! append) + { + geometry::convert(source, exterior_ring(destination)); + if (reverse) + { + boost::reverse(exterior_ring(destination)); + } + } + else + { + interior_rings(destination).resize( + interior_rings(destination).size() + 1); + geometry::convert(source, interior_rings(destination).back()); + if (reverse) + { + boost::reverse(interior_rings(destination).back()); + } + } + } +}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_CONVERT_RING_HPP diff --git a/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp new file mode 100644 index 0000000000..379444428f --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp @@ -0,0 +1,295 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENT_POINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENT_POINT_HPP + + +#include <boost/array.hpp> +#include <boost/mpl/assert.hpp> +#include <boost/range.hpp> + +#include <boost/geometry/core/ring_type.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/views/reversible_view.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace copy_segments +{ + + +template <typename Range, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point_range +{ + typedef typename closeable_view + < + Range const, + closure<Range>::value + >::type cview_type; + + typedef typename reversible_view + < + cview_type const, + Reverse ? iterate_reverse : iterate_forward + >::type rview_type; + + static inline bool apply(Range const& range, + SegmentIdentifier const& seg_id, bool second, + PointOut& point) + { + int index = seg_id.segment_index; + if (second) + { + index++; + if (index >= boost::size(range)) + { + index = 0; + } + } + + // Exception? + if (index >= boost::size(range)) + { + return false; + } + + cview_type cview(range); + rview_type view(cview); + + + geometry::convert(*(boost::begin(view) + index), point); + return true; + } +}; + + +template <typename Polygon, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point_polygon +{ + static inline bool apply(Polygon const& polygon, + SegmentIdentifier const& seg_id, bool second, + PointOut& point) + { + // Call ring-version with the right ring + return copy_segment_point_range + < + typename geometry::ring_type<Polygon>::type, + Reverse, + SegmentIdentifier, + PointOut + >::apply + ( + seg_id.ring_index < 0 + ? geometry::exterior_ring(polygon) + : geometry::interior_rings(polygon)[seg_id.ring_index], + seg_id, second, + point + ); + } +}; + + +template <typename Box, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point_box +{ + static inline bool apply(Box const& box, + SegmentIdentifier const& seg_id, bool second, + PointOut& point) + { + int index = seg_id.segment_index; + if (second) + { + index++; + } + + boost::array<typename point_type<Box>::type, 4> bp; + assign_box_corners_oriented<Reverse>(box, bp); + point = bp[index % 4]; + return true; + } +}; + + + + +}} // namespace detail::copy_segments +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag, + typename GeometryIn, + bool Reverse, + typename SegmentIdentifier, + typename PointOut +> +struct copy_segment_point +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<GeometryIn>) + ); +}; + + +template <typename LineString, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point<linestring_tag, LineString, Reverse, SegmentIdentifier, PointOut> + : detail::copy_segments::copy_segment_point_range + < + LineString, Reverse, SegmentIdentifier, PointOut + > +{}; + + +template <typename Ring, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point<ring_tag, Ring, Reverse, SegmentIdentifier, PointOut> + : detail::copy_segments::copy_segment_point_range + < + Ring, Reverse, SegmentIdentifier, PointOut + > +{}; + +template <typename Polygon, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point<polygon_tag, Polygon, Reverse, SegmentIdentifier, PointOut> + : detail::copy_segments::copy_segment_point_polygon + < + Polygon, Reverse, SegmentIdentifier, PointOut + > +{}; + + +template <typename Box, bool Reverse, typename SegmentIdentifier, typename PointOut> +struct copy_segment_point<box_tag, Box, Reverse, SegmentIdentifier, PointOut> + : detail::copy_segments::copy_segment_point_box + < + Box, Reverse, SegmentIdentifier, PointOut + > +{}; + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + + + + +/*! + \brief Helper function, copies a point from a segment + \ingroup overlay + */ +template<bool Reverse, typename Geometry, typename SegmentIdentifier, typename PointOut> +inline bool copy_segment_point(Geometry const& geometry, + SegmentIdentifier const& seg_id, bool second, + PointOut& point_out) +{ + concept::check<Geometry const>(); + + return dispatch::copy_segment_point + < + typename tag<Geometry>::type, + Geometry, + Reverse, + SegmentIdentifier, + PointOut + >::apply(geometry, seg_id, second, point_out); +} + + +/*! + \brief Helper function, to avoid the same construct several times, + copies a point, based on a source-index and two geometries + \ingroup overlay + */ +template +< + bool Reverse1, bool Reverse2, + typename Geometry1, typename Geometry2, + typename SegmentIdentifier, + typename PointOut +> +inline bool copy_segment_point(Geometry1 const& geometry1, Geometry2 const& geometry2, + SegmentIdentifier const& seg_id, bool second, + PointOut& point_out) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + if (seg_id.source_index == 0) + { + return dispatch::copy_segment_point + < + typename tag<Geometry1>::type, + Geometry1, + Reverse1, + SegmentIdentifier, + PointOut + >::apply(geometry1, seg_id, second, point_out); + } + else if (seg_id.source_index == 1) + { + return dispatch::copy_segment_point + < + typename tag<Geometry2>::type, + Geometry2, + Reverse2, + SegmentIdentifier, + PointOut + >::apply(geometry2, seg_id, second, point_out); + } + // Exception? + return false; +} + + +/*! + \brief Helper function, to avoid the same construct several times, + copies a point, based on a source-index and two geometries + \ingroup overlay + */ +template +< + bool Reverse1, bool Reverse2, + typename Geometry1, typename Geometry2, + typename SegmentIdentifier, + typename PointOut +> +inline bool copy_segment_points(Geometry1 const& geometry1, Geometry2 const& geometry2, + SegmentIdentifier const& seg_id, + PointOut& point1, PointOut& point2) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + return copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, seg_id, false, point1) + && copy_segment_point<Reverse1, Reverse2>(geometry1, geometry2, seg_id, true, point2); +} + + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENT_POINT_HPP diff --git a/boost/geometry/algorithms/detail/overlay/copy_segments.hpp b/boost/geometry/algorithms/detail/overlay/copy_segments.hpp new file mode 100644 index 0000000000..b0183a3ac2 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/copy_segments.hpp @@ -0,0 +1,328 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENTS_HPP + + +#include <boost/array.hpp> +#include <boost/mpl/assert.hpp> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/range.hpp> + +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/core/ring_type.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/iterators/ever_circling_iterator.hpp> +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/views/reversible_view.hpp> + +#include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp> + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace copy_segments +{ + + +template +< + typename Ring, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments_ring +{ + typedef typename closeable_view + < + Ring const, + closure<Ring>::value + >::type cview_type; + + typedef typename reversible_view + < + cview_type const, + Reverse ? iterate_reverse : iterate_forward + >::type rview_type; + + typedef typename boost::range_iterator<rview_type const>::type iterator; + typedef geometry::ever_circling_iterator<iterator> ec_iterator; + + static inline void apply(Ring const& ring, + SegmentIdentifier const& seg_id, int to_index, + RangeOut& current_output) + { + cview_type cview(ring); + rview_type view(cview); + + // The problem: sometimes we want to from "3" to "2" + // -> end = "3" -> end == begin + // This is not convenient with iterators. + + // So we use the ever-circling iterator and determine when to step out + + int const from_index = seg_id.segment_index + 1; + + // Sanity check + BOOST_ASSERT(from_index < boost::size(view)); + + ec_iterator it(boost::begin(view), boost::end(view), + boost::begin(view) + from_index); + + // [2..4] -> 4 - 2 + 1 = 3 -> {2,3,4} -> OK + // [4..2],size=6 -> 6 - 4 + 2 + 1 = 5 -> {4,5,0,1,2} -> OK + // [1..1], travel the whole ring round + typedef typename boost::range_difference<Ring>::type size_type; + size_type const count = from_index <= to_index + ? to_index - from_index + 1 + : boost::size(view) - from_index + to_index + 1; + + for (size_type i = 0; i < count; ++i, ++it) + { + detail::overlay::append_no_duplicates(current_output, *it); + } + } +}; + +template +< + typename LineString, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments_linestring +{ + + typedef typename boost::range_iterator<LineString const>::type iterator; + + static inline void apply(LineString const& ls, + SegmentIdentifier const& seg_id, int to_index, + RangeOut& current_output) + { + int const from_index = seg_id.segment_index + 1; + + // Sanity check + if (from_index > to_index || from_index < 0 || to_index >= boost::size(ls)) + { + return; + } + + typedef typename boost::range_difference<LineString>::type size_type; + size_type const count = to_index - from_index + 1; + + typename boost::range_iterator<LineString const>::type it = boost::begin(ls) + from_index; + + for (size_type i = 0; i < count; ++i, ++it) + { + detail::overlay::append_no_duplicates(current_output, *it); + } + } +}; + +template +< + typename Polygon, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments_polygon +{ + static inline void apply(Polygon const& polygon, + SegmentIdentifier const& seg_id, int to_index, + RangeOut& current_output) + { + // Call ring-version with the right ring + copy_segments_ring + < + typename geometry::ring_type<Polygon>::type, + Reverse, + SegmentIdentifier, + RangeOut + >::apply + ( + seg_id.ring_index < 0 + ? geometry::exterior_ring(polygon) + : geometry::interior_rings(polygon)[seg_id.ring_index], + seg_id, to_index, + current_output + ); + } +}; + + +template +< + typename Box, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments_box +{ + static inline void apply(Box const& box, + SegmentIdentifier const& seg_id, int to_index, + RangeOut& current_output) + { + int index = seg_id.segment_index + 1; + BOOST_ASSERT(index < 5); + + int const count = index <= to_index + ? to_index - index + 1 + : 5 - index + to_index + 1; + + // Create array of points, the fifth one closes it + boost::array<typename point_type<Box>::type, 5> bp; + assign_box_corners_oriented<Reverse>(box, bp); + bp[4] = bp[0]; + + // (possibly cyclic) copy to output + // (see comments in ring-version) + for (int i = 0; i < count; i++, index++) + { + detail::overlay::append_no_duplicates(current_output, bp[index % 5]); + + } + } +}; + + +}} // namespace detail::copy_segments +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag, + typename GeometryIn, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<GeometryIn>) + ); +}; + + +template +< + typename Ring, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments<ring_tag, Ring, Reverse, SegmentIdentifier, RangeOut> + : detail::copy_segments::copy_segments_ring + < + Ring, Reverse, SegmentIdentifier, RangeOut + > +{}; + + + +template +< + typename LineString, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments<linestring_tag, LineString, Reverse, SegmentIdentifier, RangeOut> + : detail::copy_segments::copy_segments_linestring + < + LineString, Reverse, SegmentIdentifier, RangeOut + > +{}; + +template +< + typename Polygon, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments<polygon_tag, Polygon, Reverse, SegmentIdentifier, RangeOut> + : detail::copy_segments::copy_segments_polygon + < + Polygon, Reverse, SegmentIdentifier, RangeOut + > +{}; + + +template +< + typename Box, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments<box_tag, Box, Reverse, SegmentIdentifier, RangeOut> + : detail::copy_segments::copy_segments_box + < + Box, Reverse, SegmentIdentifier, RangeOut + > +{}; + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! + \brief Copy segments from a geometry, starting with the specified segment (seg_id) + until the specified index (to_index) + \ingroup overlay + */ +template +< + bool Reverse, + typename Geometry, + typename SegmentIdentifier, + typename RangeOut +> +inline void copy_segments(Geometry const& geometry, + SegmentIdentifier const& seg_id, int to_index, + RangeOut& range_out) +{ + concept::check<Geometry const>(); + + dispatch::copy_segments + < + typename tag<Geometry>::type, + Geometry, + Reverse, + SegmentIdentifier, + RangeOut + >::apply(geometry, seg_id, to_index, range_out); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENTS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp b/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp new file mode 100644 index 0000000000..0bec816946 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp @@ -0,0 +1,64 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DEBUG_TURN_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DEBUG_TURN_INFO_HPP + +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/visit_info.hpp> + + +namespace boost { namespace geometry +{ + +inline char method_char(detail::overlay::method_type const& method) +{ + using namespace detail::overlay; + switch(method) + { + case method_none : return '-'; + case method_disjoint : return 'd'; + case method_crosses : return 'i'; + case method_touch : return 't'; + case method_touch_interior : return 'm'; + case method_collinear : return 'c'; + case method_equal : return 'e'; + default : return '?'; + } +} + +inline char operation_char(detail::overlay::operation_type const& operation) +{ + using namespace detail::overlay; + switch(operation) + { + case operation_none : return '-'; + case operation_union : return 'u'; + case operation_intersection : return 'i'; + case operation_blocked : return 'x'; + case operation_continue : return 'c'; + default : return '?'; + } +} + +inline char visited_char(detail::overlay::visit_info const& v) +{ + if (v.rejected()) return 'R'; + if (v.started()) return 's'; + if (v.visited()) return 'v'; + if (v.none()) return '-'; + if (v.finished()) return 'f'; + return '?'; +} + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DEBUG_TURN_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp new file mode 100644 index 0000000000..e4842d35f1 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -0,0 +1,523 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICH_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICH_HPP + +#include <cstddef> +#include <algorithm> +#include <map> +#include <set> +#include <vector> + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +# include <iostream> +# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> +# include <boost/geometry/io/wkt/wkt.hpp> +# define BOOST_GEOMETRY_DEBUG_IDENTIFIER +#endif + +#include <boost/assert.hpp> +#include <boost/range.hpp> + +#include <boost/geometry/algorithms/detail/ring_identifier.hpp> +#include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_relative_order.hpp> +#include <boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp> +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +# include <boost/geometry/algorithms/detail/overlay/check_enrich.hpp> +#endif + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +// Wraps "turn_operation" from turn_info.hpp, +// giving it extra information +template <typename TurnOperation> +struct indexed_turn_operation +{ + typedef TurnOperation type; + + int index; + int operation_index; + bool discarded; + TurnOperation subject; + + inline indexed_turn_operation(int i, int oi, TurnOperation const& s) + : index(i) + , operation_index(oi) + , discarded(false) + , subject(s) + {} +}; + +template <typename IndexedTurnOperation> +struct remove_discarded +{ + inline bool operator()(IndexedTurnOperation const& operation) const + { + return operation.discarded; + } +}; + + +template +< + typename TurnPoints, + typename Indexed, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Strategy +> +struct sort_on_segment_and_distance +{ + inline sort_on_segment_and_distance(TurnPoints const& turn_points + , Geometry1 const& geometry1 + , Geometry2 const& geometry2 + , Strategy const& strategy + , bool* clustered) + : m_turn_points(turn_points) + , m_geometry1(geometry1) + , m_geometry2(geometry2) + , m_strategy(strategy) + , m_clustered(clustered) + { + } + +private : + + TurnPoints const& m_turn_points; + Geometry1 const& m_geometry1; + Geometry2 const& m_geometry2; + Strategy const& m_strategy; + mutable bool* m_clustered; + + inline bool consider_relative_order(Indexed const& left, + Indexed const& right) const + { + typedef typename geometry::point_type<Geometry1>::type point_type; + point_type pi, pj, ri, rj, si, sj; + + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + left.subject.seg_id, + pi, pj); + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + left.subject.other_id, + ri, rj); + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + right.subject.other_id, + si, sj); + + int const order = get_relative_order + < + point_type + >::apply(pi, pj,ri, rj, si, sj); + //debug("r/o", order == -1); + return order == -1; + } + +public : + + // Note that left/right do NOT correspond to m_geometry1/m_geometry2 + // but to the "indexed_turn_operation" + inline bool operator()(Indexed const& left, Indexed const& right) const + { + segment_identifier const& sl = left.subject.seg_id; + segment_identifier const& sr = right.subject.seg_id; + + if (sl == sr + && geometry::math::equals(left.subject.enriched.distance + , right.subject.enriched.distance)) + { + // Both left and right are located on the SAME segment. + + // First check "real" intersection (crosses) + // -> distance zero due to precision, solve it by sorting + if (m_turn_points[left.index].method == method_crosses + && m_turn_points[right.index].method == method_crosses) + { + return consider_relative_order(left, right); + } + + // If that is not the case, cluster it later on. + // Indicate that this is necessary. + *m_clustered = true; + + return left.index < right.index; + } + return sl == sr + ? left.subject.enriched.distance < right.subject.enriched.distance + : sl < sr; + + } +}; + + +template<typename Turns, typename Operations> +inline void update_discarded(Turns& turn_points, Operations& operations) +{ + // Vice-versa, set discarded to true for discarded operations; + // AND set discarded points to true + for (typename boost::range_iterator<Operations>::type it = boost::begin(operations); + it != boost::end(operations); + ++it) + { + if (turn_points[it->index].discarded) + { + it->discarded = true; + } + else if (it->discarded) + { + turn_points[it->index].discarded = true; + } + } +} + + +// Sorts IP-s of this ring on segment-identifier, and if on same segment, +// on distance. +// Then assigns for each IP which is the next IP on this segment, +// plus the vertex-index to travel to, plus the next IP +// (might be on another segment) +template +< + typename IndexType, + bool Reverse1, bool Reverse2, + typename Container, + typename TurnPoints, + typename Geometry1, typename Geometry2, + typename Strategy +> +inline void enrich_sort(Container& operations, + TurnPoints& turn_points, + operation_type for_operation, + Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + typedef typename IndexType::type operations_type; + + bool clustered = false; + std::sort(boost::begin(operations), + boost::end(operations), + sort_on_segment_and_distance + < + TurnPoints, + IndexType, + Geometry1, Geometry2, + Reverse1, Reverse2, + Strategy + >(turn_points, geometry1, geometry2, strategy, &clustered)); + + // DONT'T discard xx / (for union) ix / ii / (for intersection) ux / uu here + // It would give way to "lonely" ui turn points, traveling all + // the way round. See #105 + + if (clustered) + { + typedef typename boost::range_iterator<Container>::type nc_iterator; + nc_iterator it = boost::begin(operations); + nc_iterator begin_cluster = boost::end(operations); + for (nc_iterator prev = it++; + it != boost::end(operations); + prev = it++) + { + operations_type& prev_op = turn_points[prev->index] + .operations[prev->operation_index]; + operations_type& op = turn_points[it->index] + .operations[it->operation_index]; + + if (prev_op.seg_id == op.seg_id + && (turn_points[prev->index].method != method_crosses + || turn_points[it->index].method != method_crosses) + && geometry::math::equals(prev_op.enriched.distance, + op.enriched.distance)) + { + if (begin_cluster == boost::end(operations)) + { + begin_cluster = prev; + } + } + else if (begin_cluster != boost::end(operations)) + { + handle_cluster<IndexType, Reverse1, Reverse2>(begin_cluster, it, turn_points, + for_operation, geometry1, geometry2, strategy); + begin_cluster = boost::end(operations); + } + } + if (begin_cluster != boost::end(operations)) + { + handle_cluster<IndexType, Reverse1, Reverse2>(begin_cluster, it, turn_points, + for_operation, geometry1, geometry2, strategy); + } + } + + update_discarded(turn_points, operations); +} + + +template +< + typename IndexType, + typename Container, + typename TurnPoints +> +inline void enrich_discard(Container& operations, TurnPoints& turn_points) +{ + update_discarded(turn_points, operations); + + // Then delete discarded operations from vector + remove_discarded<IndexType> predicate; + operations.erase( + std::remove_if(boost::begin(operations), + boost::end(operations), + predicate), + boost::end(operations)); +} + +template +< + typename IndexType, + typename Container, + typename TurnPoints, + typename Geometry1, + typename Geometry2, + typename Strategy +> +inline void enrich_assign(Container& operations, + TurnPoints& turn_points, + operation_type , + Geometry1 const& , Geometry2 const& , + Strategy const& ) +{ + typedef typename IndexType::type operations_type; + typedef typename boost::range_iterator<Container const>::type iterator_type; + + + if (operations.size() > 0) + { + // Assign travel-to-vertex/ip index for each turning point. + // Because IP's are circular, PREV starts at the very last one, + // being assigned from the first one. + // "next ip on same segment" should not be considered circular. + bool first = true; + iterator_type it = boost::begin(operations); + for (iterator_type prev = it + (boost::size(operations) - 1); + it != boost::end(operations); + prev = it++) + { + operations_type& prev_op + = turn_points[prev->index].operations[prev->operation_index]; + operations_type& op + = turn_points[it->index].operations[it->operation_index]; + + prev_op.enriched.travels_to_ip_index + = it->index; + prev_op.enriched.travels_to_vertex_index + = it->subject.seg_id.segment_index; + + if (! first + && prev_op.seg_id.segment_index == op.seg_id.segment_index) + { + prev_op.enriched.next_ip_index = it->index; + } + first = false; + } + } + + // DEBUG +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + { + for (iterator_type it = boost::begin(operations); + it != boost::end(operations); + ++it) + { + operations_type& op = turn_points[it->index] + .operations[it->operation_index]; + + std::cout << it->index + << " meth: " << method_char(turn_points[it->index].method) + << " seg: " << op.seg_id + << " dst: " << boost::numeric_cast<double>(op.enriched.distance) + << " op: " << operation_char(turn_points[it->index].operations[0].operation) + << operation_char(turn_points[it->index].operations[1].operation) + << " dsc: " << (turn_points[it->index].discarded ? "T" : "F") + << " ->vtx " << op.enriched.travels_to_vertex_index + << " ->ip " << op.enriched.travels_to_ip_index + << " ->nxt ip " << op.enriched.next_ip_index + //<< " vis: " << visited_char(op.visited) + << std::endl; + ; + } + } +#endif + // END DEBUG + +} + + +template <typename IndexedType, typename TurnPoints, typename MappedVector> +inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vector) +{ + typedef typename boost::range_value<TurnPoints>::type turn_point_type; + typedef typename turn_point_type::container_type container_type; + + int index = 0; + for (typename boost::range_iterator<TurnPoints const>::type + it = boost::begin(turn_points); + it != boost::end(turn_points); + ++it, ++index) + { + // Add operations on this ring, but skip discarded ones + if (! it->discarded) + { + int op_index = 0; + for (typename boost::range_iterator<container_type const>::type + op_it = boost::begin(it->operations); + op_it != boost::end(it->operations); + ++op_it, ++op_index) + { + // We should NOT skip blocked operations here + // because they can be relevant for "the other side" + // NOT if (op_it->operation != operation_blocked) + + ring_identifier ring_id + ( + op_it->seg_id.source_index, + op_it->seg_id.multi_index, + op_it->seg_id.ring_index + ); + mapped_vector[ring_id].push_back + ( + IndexedType(index, op_index, *op_it) + ); + } + } + } +} + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + + +/*! +\brief All intersection points are enriched with successor information +\ingroup overlay +\tparam TurnPoints type of intersection container + (e.g. vector of "intersection/turn point"'s) +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Strategy side strategy type +\param turn_points container containing intersectionpoints +\param for_operation operation_type (union or intersection) +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param strategy strategy + */ +template +< + bool Reverse1, bool Reverse2, + typename TurnPoints, + typename Geometry1, typename Geometry2, + typename Strategy +> +inline void enrich_intersection_points(TurnPoints& turn_points, + detail::overlay::operation_type for_operation, + Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + typedef typename boost::range_value<TurnPoints>::type turn_point_type; + typedef typename turn_point_type::turn_operation_type turn_operation_type; + typedef detail::overlay::indexed_turn_operation + < + turn_operation_type + > indexed_turn_operation; + + typedef std::map + < + ring_identifier, + std::vector<indexed_turn_operation> + > mapped_vector_type; + + // DISCARD ALL UU + // #76 is the reason that this is necessary... + // With uu, at all points there is the risk that rings are being traversed twice or more. + // Without uu, all rings having only uu will be untouched and gathered by assemble + for (typename boost::range_iterator<TurnPoints>::type + it = boost::begin(turn_points); + it != boost::end(turn_points); + ++it) + { + if (it->both(detail::overlay::operation_union)) + { + it->discarded = true; + } + } + + + // Create a map of vectors of indexed operation-types to be able + // to sort intersection points PER RING + mapped_vector_type mapped_vector; + + detail::overlay::create_map<indexed_turn_operation>(turn_points, mapped_vector); + + + // No const-iterator; contents of mapped copy is temporary, + // and changed by enrich + for (typename mapped_vector_type::iterator mit + = mapped_vector.begin(); + mit != mapped_vector.end(); + ++mit) + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << "ENRICH-sort Ring " + << mit->first << std::endl; +#endif + detail::overlay::enrich_sort<indexed_turn_operation, Reverse1, Reverse2>(mit->second, turn_points, for_operation, + geometry1, geometry2, strategy); + } + + for (typename mapped_vector_type::iterator mit + = mapped_vector.begin(); + mit != mapped_vector.end(); + ++mit) + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << "ENRICH-discard Ring " + << mit->first << std::endl; +#endif + detail::overlay::enrich_discard<indexed_turn_operation>(mit->second, turn_points); + } + + for (typename mapped_vector_type::iterator mit + = mapped_vector.begin(); + mit != mapped_vector.end(); + ++mit) + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << "ENRICH-assign Ring " + << mit->first << std::endl; +#endif + detail::overlay::enrich_assign<indexed_turn_operation>(mit->second, turn_points, for_operation, + geometry1, geometry2, strategy); + } + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + //detail::overlay::check_graph(turn_points, for_operation); +#endif + +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICH_HPP diff --git a/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp b/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp new file mode 100644 index 0000000000..8c8ed96189 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp @@ -0,0 +1,76 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICHMENT_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICHMENT_INFO_HPP + + +#include <boost/geometry/strategies/distance.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +/*! +\brief Keeps info to enrich intersection info (per source) +\details Class to keep information necessary for traversal phase (a phase + of the overlay process). The information is gathered during the + enrichment phase + */ +template<typename P> +struct enrichment_info +{ + typedef typename strategy::distance::services::return_type + < + typename strategy::distance::services::comparable_type + < + typename strategy::distance::services::default_strategy + < + point_tag, + P + >::type + >::type + >::type distance_type; + + inline enrichment_info() + : travels_to_vertex_index(-1) + , travels_to_ip_index(-1) + , next_ip_index(-1) + , distance(distance_type()) + {} + + // vertex to which is free travel after this IP, + // so from "segment_index+1" to "travels_to_vertex_index", without IP-s, + // can be -1 + int travels_to_vertex_index; + + // same but now IP index, so "next IP index" but not on THIS segment + int travels_to_ip_index; + + // index of next IP on this segment, -1 if there is no one + int next_ip_index; + + distance_type distance; // distance-measurement from segment.first to IP +}; + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICHMENT_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/follow.hpp b/boost/geometry/algorithms/detail/overlay/follow.hpp new file mode 100644 index 0000000000..087092a5f2 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/follow.hpp @@ -0,0 +1,416 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP + +#include <cstddef> + +#include <boost/range.hpp> +#include <boost/mpl/assert.hpp> + +#include <boost/geometry/algorithms/detail/point_on_border.hpp> +#include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp> +#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> + +#include <boost/geometry/algorithms/covered_by.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +namespace following +{ + +template <typename Turn, typename Operation> +static inline bool is_entering(Turn const& /* TODO remove this parameter */, Operation const& op) +{ + // (Blocked means: blocked for polygon/polygon intersection, because + // they are reversed. But for polygon/line it is similar to continue) + return op.operation == operation_intersection + || op.operation == operation_continue + || op.operation == operation_blocked + ; +} + +template +< + typename Turn, + typename Operation, + typename LineString, + typename Polygon +> +static inline bool last_covered_by(Turn const& turn, Operation const& op, + LineString const& linestring, Polygon const& polygon) +{ + // Check any point between the this one and the first IP + typedef typename geometry::point_type<LineString>::type point_type; + point_type point_in_between; + detail::point_on_border::midpoint_helper + < + point_type, + 0, dimension<point_type>::value + >::apply(point_in_between, linestring[op.seg_id.segment_index], turn.point); + + return geometry::covered_by(point_in_between, polygon); +} + + +template +< + typename Turn, + typename Operation, + typename LineString, + typename Polygon +> +static inline bool is_leaving(Turn const& turn, Operation const& op, + bool entered, bool first, + LineString const& linestring, Polygon const& polygon) +{ + if (op.operation == operation_union) + { + return entered + || turn.method == method_crosses + || (first && last_covered_by(turn, op, linestring, polygon)) + ; + } + return false; +} + + +template +< + typename Turn, + typename Operation, + typename LineString, + typename Polygon +> +static inline bool is_staying_inside(Turn const& turn, Operation const& op, + bool entered, bool first, + LineString const& linestring, Polygon const& polygon) +{ + if (turn.method == method_crosses) + { + // The normal case, this is completely covered with entering/leaving + // so stay out of this time consuming "covered_by" + return false; + } + + if (is_entering(turn, op)) + { + return entered || (first && last_covered_by(turn, op, linestring, polygon)); + } + + return false; +} + +template +< + typename Turn, + typename Operation, + typename Linestring, + typename Polygon +> +static inline bool was_entered(Turn const& turn, Operation const& op, bool first, + Linestring const& linestring, Polygon const& polygon) +{ + if (first && (turn.method == method_collinear || turn.method == method_equal)) + { + return last_covered_by(turn, op, linestring, polygon); + } + return false; +} + + +// Template specialization structure to call the right actions for the right type +template<overlay_type OverlayType> +struct action_selector +{ + // If you get here the overlay type is not intersection or difference + // BOOST_MPL_ASSERT(false); +}; + +// Specialization for intersection, containing the implementation +template<> +struct action_selector<overlay_intersection> +{ + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void enter(LineStringOut& current_piece, + LineString const& , + segment_identifier& segment_id, + int , Point const& point, + Operation const& operation, OutputIterator& ) + { + // On enter, append the intersection point and remember starting point + detail::overlay::append_no_duplicates(current_piece, point); + segment_id = operation.seg_id; + } + + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void leave(LineStringOut& current_piece, + LineString const& linestring, + segment_identifier& segment_id, + int index, Point const& point, + Operation const& , OutputIterator& out) + { + // On leave, copy all segments from starting point, append the intersection point + // and add the output piece + geometry::copy_segments<false>(linestring, segment_id, index, current_piece); + detail::overlay::append_no_duplicates(current_piece, point); + if (current_piece.size() > 1) + { + *out++ = current_piece; + } + current_piece.clear(); + } + + static inline bool is_entered(bool entered) + { + return entered; + } + + template <typename Point, typename Geometry> + static inline bool included(Point const& point, Geometry const& geometry) + { + return geometry::covered_by(point, geometry); + } + +}; + +// Specialization for difference, which reverses these actions +template<> +struct action_selector<overlay_difference> +{ + typedef action_selector<overlay_intersection> normal_action; + + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void enter(LineStringOut& current_piece, + LineString const& linestring, + segment_identifier& segment_id, + int index, Point const& point, + Operation const& operation, OutputIterator& out) + { + normal_action::leave(current_piece, linestring, segment_id, index, + point, operation, out); + } + + template + < + typename OutputIterator, + typename LineStringOut, + typename LineString, + typename Point, + typename Operation + > + static inline void leave(LineStringOut& current_piece, + LineString const& linestring, + segment_identifier& segment_id, + int index, Point const& point, + Operation const& operation, OutputIterator& out) + { + normal_action::enter(current_piece, linestring, segment_id, index, + point, operation, out); + } + + static inline bool is_entered(bool entered) + { + return ! normal_action::is_entered(entered); + } + + template <typename Point, typename Geometry> + static inline bool included(Point const& point, Geometry const& geometry) + { + return ! normal_action::included(point, geometry); + } + +}; + +} + +/*! +\brief Follows a linestring from intersection point to intersection point, outputting which + is inside, or outside, a ring or polygon +\ingroup overlay + */ +template +< + typename LineStringOut, + typename LineString, + typename Polygon, + overlay_type OverlayType +> +class follow +{ + + template<typename Turn> + struct sort_on_segment + { + // In case of turn point at the same location, we want to have continue/blocked LAST + // because that should be followed (intersection) or skipped (difference). + // By chance the enumeration is ordered like that but we keep the safe way here. + inline int operation_order(Turn const& turn) const + { + operation_type const& operation = turn.operations[0].operation; + switch(operation) + { + case operation_none : return 0; + case operation_union : return 1; + case operation_intersection : return 2; + case operation_blocked : return 3; + case operation_continue : return 4; + } + return -1; + }; + + inline bool use_operation(Turn const& left, Turn const& right) const + { + // If they are the same, OK. + return operation_order(left) < operation_order(right); + } + + inline bool use_distance(Turn const& left, Turn const& right) const + { + return geometry::math::equals(left.operations[0].enriched.distance, right.operations[0].enriched.distance) + ? use_operation(left, right) + : left.operations[0].enriched.distance < right.operations[0].enriched.distance + ; + } + + inline bool operator()(Turn const& left, Turn const& right) const + { + segment_identifier const& sl = left.operations[0].seg_id; + segment_identifier const& sr = right.operations[0].seg_id; + + return sl == sr + ? use_distance(left, right) + : sl < sr + ; + + } + }; + + + +public : + + template <typename Point, typename Geometry> + static inline bool included(Point const& point, Geometry const& geometry) + { + return following::action_selector<OverlayType>::included(point, geometry); + } + + template<typename Turns, typename OutputIterator> + static inline OutputIterator apply(LineString const& linestring, Polygon const& polygon, + detail::overlay::operation_type , // TODO: this parameter might be redundant + Turns& turns, OutputIterator out) + { + typedef typename boost::range_iterator<Turns>::type turn_iterator; + typedef typename boost::range_value<Turns>::type turn_type; + typedef typename boost::range_iterator + < + typename turn_type::container_type + >::type turn_operation_iterator_type; + + typedef following::action_selector<OverlayType> action; + + // Sort intersection points on segments-along-linestring, and distance + // (like in enrich is done for poly/poly) + std::sort(boost::begin(turns), boost::end(turns), sort_on_segment<turn_type>()); + + LineStringOut current_piece; + geometry::segment_identifier current_segment_id(0, -1, -1, -1); + + // Iterate through all intersection points (they are ordered along the line) + bool entered = false; + bool first = true; + for (turn_iterator it = boost::begin(turns); it != boost::end(turns); ++it) + { + turn_operation_iterator_type iit = boost::begin(it->operations); + + if (following::was_entered(*it, *iit, first, linestring, polygon)) + { + debug_traverse(*it, *iit, "-> Was entered"); + entered = true; + } + + if (following::is_staying_inside(*it, *iit, entered, first, linestring, polygon)) + { + debug_traverse(*it, *iit, "-> Staying inside"); + + entered = true; + } + else if (following::is_entering(*it, *iit)) + { + debug_traverse(*it, *iit, "-> Entering"); + + entered = true; + action::enter(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, out); + } + else if (following::is_leaving(*it, *iit, entered, first, linestring, polygon)) + { + debug_traverse(*it, *iit, "-> Leaving"); + + entered = false; + action::leave(current_piece, linestring, current_segment_id, iit->seg_id.segment_index, it->point, *iit, out); + } + first = false; + } + + if (action::is_entered(entered)) + { + geometry::copy_segments<false>(linestring, current_segment_id, + boost::size(linestring) - 1, + current_piece); + } + + // Output the last one, if applicable + if (current_piece.size() > 1) + { + *out++ = current_piece; + } + return out; + } + +}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_FOLLOW_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp b/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp new file mode 100644 index 0000000000..019c3ba3f9 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp @@ -0,0 +1,146 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_INTERSECTION_POINTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_INTERSECTION_POINTS_HPP + + +#include <cstddef> + +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> + +#include <boost/geometry/geometries/segment.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace get_intersection_points +{ + + +template +< + typename Point1, + typename Point2, + typename TurnInfo +> +struct get_turn_without_info +{ + typedef strategy_intersection + < + typename cs_tag<typename TurnInfo::point_type>::type, + Point1, + Point2, + typename TurnInfo::point_type + > si; + + typedef typename si::segment_intersection_strategy_type strategy; + + + + template <typename OutputIterator> + static inline OutputIterator apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + TurnInfo const& , + OutputIterator out) + { + typedef model::referring_segment<Point1 const> segment_type1; + typedef model::referring_segment<Point1 const> segment_type2; + segment_type1 p1(pi, pj), p2(pj, pk); + segment_type2 q1(qi, qj), q2(qj, qk); + + // + typename strategy::return_type result = strategy::apply(p1, q1); + + for (std::size_t i = 0; i < result.template get<0>().count; i++) + { + + TurnInfo tp; + geometry::convert(result.template get<0>().intersections[i], tp.point); + *out++ = tp; + } + + return out; + } +}; + +}} // namespace detail::get_intersection_points +#endif // DOXYGEN_NO_DETAIL + + + + +template +< + typename Geometry1, + typename Geometry2, + typename Turns +> +inline void get_intersection_points(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Turns& turns) +{ + concept::check_concepts_and_equal_dimensions<Geometry1 const, Geometry2 const>(); + + typedef detail::get_intersection_points::get_turn_without_info + < + typename point_type<Geometry1>::type, + typename point_type<Geometry2>::type, + typename boost::range_value<Turns>::type + > TurnPolicy; + + typedef typename strategy_intersection + < + typename cs_tag<Geometry1>::type, + Geometry1, + Geometry2, + typename boost::range_value<Turns>::type + >::segment_intersection_strategy_type segment_intersection_strategy_type; + + detail::get_turns::no_interrupt_policy interrupt_policy; + + boost::mpl::if_c + < + reverse_dispatch<Geometry1, Geometry2>::type::value, + dispatch::get_turns_reversed + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, Geometry2, + false, false, + Turns, TurnPolicy, + //segment_intersection_strategy_type, + detail::get_turns::no_interrupt_policy + >, + dispatch::get_turns + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, Geometry2, + false, false, + Turns, TurnPolicy, + //segment_intersection_strategy_type, + detail::get_turns::no_interrupt_policy + > + >::type::apply( + 0, geometry1, + 1, geometry2, + turns, interrupt_policy); +} + + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_INTERSECTION_POINTS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp b/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp new file mode 100644 index 0000000000..522ef68382 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp @@ -0,0 +1,108 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RELATIVE_ORDER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RELATIVE_ORDER_HPP + + +#include <boost/geometry/algorithms/distance.hpp> + +#include <boost/geometry/strategies/intersection.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +/*! + \brief Get relative order + \details Can indicate which of two segments R and S, + both crossing a common segment P, comes first. + If the two segments cross P very close (e.g. in a spike), + the distance between the intersection points can be zero, + but we still need to know which comes first. + Therefore, it is useful that using sides we are able to discover this. + */ +template <typename Point1> +struct get_relative_order +{ + typedef strategy_intersection + < + typename cs_tag<Point1>::type, + Point1, + Point1, + Point1 + > si; + + typedef typename si::side_strategy_type strategy; + + template <typename Point> + static inline int value_via_product(Point const& ti, Point const& tj, + Point const& ui, Point const& uj, int factor) + { + int const side_ti_u = strategy::apply(ti, tj, ui); + int const side_tj_u = strategy::apply(ti, tj, uj); + +#ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER + std::cout << (factor == 1 ? " r//s " : " s//r ") + << side_ti_u << " / " << side_tj_u; +#endif + + return side_ti_u * side_tj_u >= 0 + ? factor * (side_ti_u != 0 ? side_ti_u : side_tj_u) + : 0; + } + + + static inline int apply( + Point1 const& pi, Point1 const& pj, + Point1 const& ri, Point1 const& rj, + Point1 const& si, Point1 const& sj) + { + int const side_ri_p = strategy::apply(pi, pj, ri); + int const side_si_p = strategy::apply(pi, pj, si); + +#ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER + int const side_rj_p = strategy::apply(pi, pj, rj); + int const side_sj_p = strategy::apply(pi, pj, sj); + std::cout << "r//p: " << side_ri_p << " / " << side_rj_p; + std::cout << " s//p: " << side_si_p << " / " << side_sj_p; +#endif + + int value = value_via_product(si, sj, ri, rj, 1); + if (value == 0) + { + value = value_via_product(ri, rj, si, sj, -1); + } + + int const order = side_ri_p * side_ri_p * side_si_p * value; + +#ifdef BOOST_GEOMETRY_DEBUG_RELATIVE_ORDER + std::cout + << " o: " << order + << std::endl << std::endl; +#endif + + return order; + } +}; + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RELATIVE_ORDER_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_ring.hpp b/boost/geometry/algorithms/detail/overlay/get_ring.hpp new file mode 100644 index 0000000000..ad47665500 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_ring.hpp @@ -0,0 +1,102 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RING_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RING_HPP + + +#include <boost/assert.hpp> +#include <boost/range.hpp> + + +#include <boost/geometry/core/tags.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/algorithms/detail/ring_identifier.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template<typename Tag> +struct get_ring +{}; + +// A container of rings (multi-ring but that does not exist) +// gets the "void" tag and is dispatched here. +template<> +struct get_ring<void> +{ + template<typename Container> + static inline typename boost::range_value<Container>::type const& + apply(ring_identifier const& id, Container const& container) + { + return container[id.multi_index]; + } +}; + + + + +template<> +struct get_ring<ring_tag> +{ + template<typename Ring> + static inline Ring const& apply(ring_identifier const& , Ring const& ring) + { + return ring; + } +}; + + +template<> +struct get_ring<box_tag> +{ + template<typename Box> + static inline Box const& apply(ring_identifier const& , + Box const& box) + { + return box; + } +}; + + +template<> +struct get_ring<polygon_tag> +{ + template<typename Polygon> + static inline typename ring_return_type<Polygon const>::type const apply( + ring_identifier const& id, + Polygon const& polygon) + { + BOOST_ASSERT + ( + id.ring_index >= -1 + && id.ring_index < boost::size(interior_rings(polygon)) + ); + return id.ring_index < 0 + ? exterior_ring(polygon) + : interior_rings(polygon)[id.ring_index]; + } +}; + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_RING_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp new file mode 100644 index 0000000000..663d70d9af --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -0,0 +1,956 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HPP + + +#include <boost/assert.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/strategies/intersection.hpp> + +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> + +#include <boost/geometry/geometries/segment.hpp> + + +namespace boost { namespace geometry +{ + +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) +class turn_info_exception : public geometry::exception +{ + std::string message; +public: + + // NOTE: "char" will be replaced by enum in future version + inline turn_info_exception(char const method) + { + message = "Boost.Geometry Turn exception: "; + message += method; + } + + virtual ~turn_info_exception() throw() + {} + + virtual char const* what() const throw() + { + return message.c_str(); + } +}; +#endif + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +struct base_turn_handler +{ + // Returns true if both sides are opposite + static inline bool opposite(int side1, int side2) + { + // We cannot state side1 == -side2, because 0 == -0 + // So either side1*side2==-1 or side1==-side2 && side1 != 0 + return side1 * side2 == -1; + } + + // Same side of a segment (not being 0) + static inline bool same(int side1, int side2) + { + return side1 * side2 == 1; + } + + // Both continue + template <typename TurnInfo> + static inline void both(TurnInfo& ti, operation_type const op) + { + ti.operations[0].operation = op; + ti.operations[1].operation = op; + } + + // If condition, first union/second intersection, else vice versa + template <typename TurnInfo> + static inline void ui_else_iu(bool condition, TurnInfo& ti) + { + ti.operations[0].operation = condition + ? operation_union : operation_intersection; + ti.operations[1].operation = condition + ? operation_intersection : operation_union; + } + + // If condition, both union, else both intersection + template <typename TurnInfo> + static inline void uu_else_ii(bool condition, TurnInfo& ti) + { + both(ti, condition ? operation_union : operation_intersection); + } +}; + + +template +< + typename TurnInfo, + typename SideStrategy +> +struct touch_interior : public base_turn_handler +{ + // Index: 0, P is the interior, Q is touching and vice versa + template + < + int Index, + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply( + Point1 const& pi, Point1 const& pj, Point1 const& , + Point2 const& qi, Point2 const& qj, Point2 const& qk, + TurnInfo& ti, + IntersectionInfo const& intersection_info, + DirInfo const& dir_info) + { + ti.method = method_touch_interior; + geometry::convert(intersection_info.intersections[0], ti.point); + + // Both segments of q touch segment p somewhere in its interior + // 1) We know: if q comes from LEFT or RIGHT + // (i.e. dir_info.sides.get<Index,0>() == 1 or -1) + // 2) Important is: if q_k goes to LEFT, RIGHT, COLLINEAR + // and, if LEFT/COLL, if it is lying LEFT or RIGHT w.r.t. q_i + + static int const index_p = Index; + static int const index_q = 1 - Index; + + int const side_qi_p = dir_info.sides.template get<index_q, 0>(); + int const side_qk_p = SideStrategy::apply(pi, pj, qk); + + if (side_qi_p == -side_qk_p) + { + // Q crosses P from left->right or from right->left (test "ML1") + // Union: folow P (left->right) or Q (right->left) + // Intersection: other turn + int index = side_qk_p == -1 ? index_p : index_q; + ti.operations[index].operation = operation_union; + ti.operations[1 - index].operation = operation_intersection; + return; + } + + int const side_qk_q = SideStrategy::apply(qi, qj, qk); + + if (side_qi_p == -1 && side_qk_p == -1 && side_qk_q == 1) + { + // Q turns left on the right side of P (test "MR3") + // Both directions for "intersection" + both(ti, operation_intersection); + } + else if (side_qi_p == 1 && side_qk_p == 1 && side_qk_q == -1) + { + // Q turns right on the left side of P (test "ML3") + // Union: take both operation + // Intersection: skip + both(ti, operation_union); + } + else if (side_qi_p == side_qk_p && side_qi_p == side_qk_q) + { + // Q turns left on the left side of P (test "ML2") + // or Q turns right on the right side of P (test "MR2") + // Union: take left turn (Q if Q turns left, P if Q turns right) + // Intersection: other turn + int index = side_qk_q == 1 ? index_q : index_p; + ti.operations[index].operation = operation_union; + ti.operations[1 - index].operation = operation_intersection; + } + else if (side_qk_p == 0) + { + // Q intersects on interior of P and continues collinearly + if (side_qk_q == side_qi_p) + { + // Collinearly in the same direction + // (Q comes from left of P and turns left, + // OR Q comes from right of P and turns right) + // Omit intersection point. + // Union: just continue + // Intersection: just continue + both(ti, operation_continue); + } + else + { + // Opposite direction, which is never travelled. + // If Q turns left, P continues for intersection + // If Q turns right, P continues for union + ti.operations[Index].operation = side_qk_q == 1 + ? operation_intersection + : operation_union; + ti.operations[1 - Index].operation = operation_blocked; + } + } + else + { + // Should not occur! + ti.method = method_error; + } + } +}; + + +template +< + typename TurnInfo, + typename SideStrategy +> +struct touch : public base_turn_handler +{ + static inline bool between(int side1, int side2, int turn) + { + return side1 == side2 && ! opposite(side1, turn); + } + + /*static inline void block_second(bool block, TurnInfo& ti) + { + if (block) + { + ti.operations[1].operation = operation_blocked; + } + }*/ + + + template + < + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + TurnInfo& ti, + IntersectionInfo const& intersection_info, + DirInfo const& dir_info) + { + ti.method = method_touch; + geometry::convert(intersection_info.intersections[0], ti.point); + + int const side_qi_p1 = dir_info.sides.template get<1, 0>(); + int const side_qk_p1 = SideStrategy::apply(pi, pj, qk); + + + // If Qi and Qk are both at same side of Pi-Pj, + // or collinear (so: not opposite sides) + if (! opposite(side_qi_p1, side_qk_p1)) + { + int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); + int const side_pk_p = SideStrategy::apply(pi, pj, pk); + int const side_qk_q = SideStrategy::apply(qi, qj, qk); + bool const q_turns_left = side_qk_q == 1; + bool const block_q = side_qk_p1 == 0 + && ! same(side_qi_p1, side_qk_q); + + // If Pk at same side as Qi/Qk + // (the "or" is for collinear case) + // or Q is fully collinear && P turns not to left + if (side_pk_p == side_qi_p1 + || side_pk_p == side_qk_p1 + || (side_qi_p1 == 0 && side_qk_p1 == 0 && side_pk_p != -1) + ) + { + // Collinear -> lines join, continue + // (#BRL2) + if (side_pk_q2 == 0 && ! block_q) + { + both(ti, operation_continue); + return; + } + + int const side_pk_q1 = SideStrategy::apply(qi, qj, pk); + + + // Collinear opposite case -> block P + // (#BRL4, #BLR8) + if (side_pk_q1 == 0) + { + ti.operations[0].operation = operation_blocked; + // Q turns right -> union (both independant), + // Q turns left -> intersection + ti.operations[1].operation = block_q ? operation_blocked + : q_turns_left ? operation_intersection + : operation_union; + return; + } + + // Pk between Qi and Qk + // (#BRL3, #BRL7) + if (between(side_pk_q1, side_pk_q2, side_qk_q)) + { + ui_else_iu(q_turns_left, ti); + if (block_q) + { + ti.operations[1].operation = operation_blocked; + } + //block_second(block_q, ti); + return; + } + + // Pk between Qk and P, so left of Qk (if Q turns right) and vv + // (#BRL1) + if (side_pk_q2 == -side_qk_q) + { + ui_else_iu(! q_turns_left, ti); + return; + } + + // + // (#BRL5, #BRL9) + if (side_pk_q1 == -side_qk_q) + { + uu_else_ii(! q_turns_left, ti); + if (block_q) + { + ti.operations[1].operation = operation_blocked; + } + //block_second(block_q, ti); + return; + } + } + else + { + // Pk at other side than Qi/Pk + int const side_qk_q = SideStrategy::apply(qi, qj, qk); + bool const q_turns_left = side_qk_q == 1; + + ti.operations[0].operation = q_turns_left + ? operation_intersection + : operation_union; + ti.operations[1].operation = block_q + ? operation_blocked + : side_qi_p1 == 1 || side_qk_p1 == 1 + ? operation_union + : operation_intersection; + + return; + } + } + else + { + // From left to right or from right to left + int const side_pk_p = SideStrategy::apply(pi, pj, pk); + bool const right_to_left = side_qk_p1 == 1; + + // If p turns into direction of qi (1,2) + if (side_pk_p == side_qi_p1) + { + int const side_pk_q1 = SideStrategy::apply(qi, qj, pk); + + // Collinear opposite case -> block P + if (side_pk_q1 == 0) + { + ti.operations[0].operation = operation_blocked; + ti.operations[1].operation = right_to_left + ? operation_union : operation_intersection; + return; + } + + if (side_pk_q1 == side_qk_p1) + { + uu_else_ii(right_to_left, ti); + return; + } + } + + // If p turns into direction of qk (4,5) + if (side_pk_p == side_qk_p1) + { + int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); + + // Collinear case -> lines join, continue + if (side_pk_q2 == 0) + { + both(ti, operation_continue); + return; + } + if (side_pk_q2 == side_qk_p1) + { + ui_else_iu(right_to_left, ti); + return; + } + } + // otherwise (3) + ui_else_iu(! right_to_left, ti); + return; + } + +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS + // Normally a robustness issue. + // TODO: more research if still occuring + std::cout << "Not yet handled" << std::endl + << "pi " << get<0>(pi) << " , " << get<1>(pi) + << " pj " << get<0>(pj) << " , " << get<1>(pj) + << " pk " << get<0>(pk) << " , " << get<1>(pk) + << std::endl + << "qi " << get<0>(qi) << " , " << get<1>(qi) + << " qj " << get<0>(qj) << " , " << get<1>(qj) + << " qk " << get<0>(qk) << " , " << get<1>(qk) + << std::endl; +#endif + + } +}; + + +template +< + typename TurnInfo, + typename SideStrategy +> +struct equal : public base_turn_handler +{ + template + < + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& , Point2 const& qj, Point2 const& qk, + TurnInfo& ti, + IntersectionInfo const& intersection_info, + DirInfo const& ) + { + ti.method = method_equal; + // Copy the SECOND intersection point + geometry::convert(intersection_info.intersections[1], ti.point); + + int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); + int const side_pk_p = SideStrategy::apply(pi, pj, pk); + int const side_qk_p = SideStrategy::apply(pi, pj, qk); + + // If pk is collinear with qj-qk, they continue collinearly. + // This can be on either side of p1 (== q1), or collinear + // The second condition checks if they do not continue + // oppositely + if (side_pk_q2 == 0 && side_pk_p == side_qk_p) + { + both(ti, operation_continue); + return; + } + + + // If they turn to same side (not opposite sides) + if (! opposite(side_pk_p, side_qk_p)) + { + int const side_pk_q2 = SideStrategy::apply(qj, qk, pk); + + // If pk is left of q2 or collinear: p: union, q: intersection + ui_else_iu(side_pk_q2 != -1, ti); + } + else + { + // They turn opposite sides. If p turns left (or collinear), + // p: union, q: intersection + ui_else_iu(side_pk_p != -1, ti); + } + } +}; + + +template +< + typename TurnInfo, + typename SideStrategy +> +struct collinear : public base_turn_handler +{ + /* + arrival P pk//p1 qk//q1 product* case result + 1 1 1 CLL1 ui + -1 1 -1 CLL2 iu + 1 1 1 CLR1 ui + -1 -1 1 CLR2 ui + + 1 -1 -1 CRL1 iu + -1 1 -1 CRL2 iu + 1 -1 -1 CRR1 iu + -1 -1 1 CRR2 ui + + 1 0 0 CC1 cc + -1 0 0 CC2 cc + + *product = arrival * (pk//p1 or qk//q1) + + Stated otherwise: + - if P arrives: look at turn P + - if Q arrives: look at turn Q + - if P arrives and P turns left: union for P + - if P arrives and P turns right: intersection for P + - if Q arrives and Q turns left: union for Q (=intersection for P) + - if Q arrives and Q turns right: intersection for Q (=union for P) + */ + template + < + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + TurnInfo& ti, + IntersectionInfo const& intersection_info, + DirInfo const& dir_info) + { + ti.method = method_collinear; + geometry::convert(intersection_info.intersections[1], ti.point); + + int const arrival = dir_info.arrival[0]; + // Should not be 0, this is checked before + BOOST_ASSERT(arrival != 0); + + // If p arrives, use p, else use q + int const side_p_or_q = arrival == 1 + ? SideStrategy::apply(pi, pj, pk) + : SideStrategy::apply(qi, qj, qk) + ; + + // See comments above, + // resulting in a strange sort of mathematic rule here: + // The arrival-info multiplied by the relevant side + // delivers a consistent result. + + int const product = arrival * side_p_or_q; + + if(product == 0) + { + both(ti, operation_continue); + } + else + { + ui_else_iu(product == 1, ti); + } + } +}; + +template +< + typename TurnInfo, + typename SideStrategy, + typename AssignPolicy +> +struct collinear_opposite : public base_turn_handler +{ +private : + /* + arrival P arrival Q pk//p1 qk//q1 case result2 result + -------------------------------------------------------------- + 1 1 1 -1 CLO1 ix xu + 1 1 1 0 CLO2 ix (xx) + 1 1 1 1 CLO3 ix xi + + 1 1 0 -1 CCO1 (xx) xu + 1 1 0 0 CCO2 (xx) (xx) + 1 1 0 1 CCO3 (xx) xi + + 1 1 -1 -1 CRO1 ux xu + 1 1 -1 0 CRO2 ux (xx) + 1 1 -1 1 CRO3 ux xi + + -1 1 -1 CXO1 xu + -1 1 0 CXO2 (xx) + -1 1 1 CXO3 xi + + 1 -1 1 CXO1 ix + 1 -1 0 CXO2 (xx) + 1 -1 -1 CXO3 ux + */ + + template + < + int Index, + typename Point, + typename IntersectionInfo + > + static inline bool set_tp(Point const& ri, Point const& rj, Point const& rk, + TurnInfo& tp, IntersectionInfo const& intersection_info) + { + int const side_rk_r = SideStrategy::apply(ri, rj, rk); + switch(side_rk_r) + { + + case 1 : + // Turning left on opposite collinear: intersection + tp.operations[Index].operation = operation_intersection; + break; + case -1 : + // Turning right on opposite collinear: union + tp.operations[Index].operation = operation_union; + break; + case 0 : + // No turn on opposite collinear: block, do not traverse + // But this "xx" is ignored here, it is useless to include + // two operation blocked, so the whole point does not need + // to be generated. + // So return false to indicate nothing is to be done. + return false; + } + + // The other direction is always blocked when collinear opposite + tp.operations[1 - Index].operation = operation_blocked; + + // If P arrives within Q, set info on P (which is done above, index=0), + // this turn-info belongs to the second intersection point, index=1 + // (see e.g. figure CLO1) + geometry::convert(intersection_info.intersections[1 - Index], tp.point); + return true; + } + +public: + template + < + typename Point1, + typename Point2, + typename OutputIterator, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + + // Opposite collinear can deliver 2 intersection points, + TurnInfo const& tp_model, + OutputIterator& out, + + IntersectionInfo const& intersection_info, + DirInfo const& dir_info) + { + /* + std::cout << "arrivals: " + << dir_info.arrival[0] + << "/" << dir_info.arrival[1] + << std::endl; + */ + + TurnInfo tp = tp_model; + + tp.method = method_collinear; + + // If P arrives within Q, there is a turn dependant on P + if (dir_info.arrival[0] == 1 + && set_tp<0>(pi, pj, pk, tp, intersection_info)) + { + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + + // If Q arrives within P, there is a turn dependant on Q + if (dir_info.arrival[1] == 1 + && set_tp<1>(qi, qj, qk, tp, intersection_info)) + { + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + } +}; + + +template +< + typename TurnInfo, + typename SideStrategy +> +struct crosses : public base_turn_handler +{ + template + < + typename Point1, + typename Point2, + typename IntersectionInfo, + typename DirInfo + > + static inline void apply( + Point1 const& , Point1 const& , Point1 const& , + Point2 const& , Point2 const& , Point2 const& , + TurnInfo& ti, + IntersectionInfo const& intersection_info, + DirInfo const& dir_info) + { + ti.method = method_crosses; + geometry::convert(intersection_info.intersections[0], ti.point); + + // In all casees: + // If Q crosses P from left to right + // Union: take P + // Intersection: take Q + // Otherwise: vice versa + int const side_qi_p1 = dir_info.sides.template get<1, 0>(); + int const index = side_qi_p1 == 1 ? 0 : 1; + ti.operations[index].operation = operation_union; + ti.operations[1 - index].operation = operation_intersection; + } +}; + +template<typename TurnInfo> +struct only_convert +{ + template<typename IntersectionInfo> + static inline void apply(TurnInfo& ti, IntersectionInfo const& intersection_info) + { + ti.method = method_collinear; + geometry::convert(intersection_info.intersections[0], ti.point); + ti.operations[0].operation = operation_continue; + ti.operations[1].operation = operation_continue; + } +}; + +/*! +\brief Policy doing nothing +\details get_turn_info can have an optional policy to get/assign some + extra information. By default it does not, and this class + is that default. + */ +struct assign_null_policy +{ + static bool const include_no_turn = false; + static bool const include_degenerate = false; + + template <typename Point1, typename Point2, typename Info> + static inline void apply(Info& , Point1 const& , Point2 const& ) + {} + +}; + + +/*! + \brief Turn information: intersection point, method, and turn information + \details Information necessary for traversal phase (a phase + of the overlay process). The information is gathered during the + get_turns (segment intersection) phase. + \tparam Point1 point type of first segment + \tparam Point2 point type of second segment + \tparam TurnInfo type of class getting intersection and turn info + \tparam AssignPolicy policy to assign extra info, + e.g. to calculate distance from segment's first points + to intersection points. + It also defines if a certain class of points + (degenerate, non-turns) should be included. + */ +template +< + typename Point1, + typename Point2, + typename TurnInfo, + typename AssignPolicy +> +struct get_turn_info +{ + typedef strategy_intersection + < + typename cs_tag<typename TurnInfo::point_type>::type, + Point1, + Point2, + typename TurnInfo::point_type + > si; + + typedef typename si::segment_intersection_strategy_type strategy; + + + + template <typename OutputIterator> + static inline OutputIterator apply( + Point1 const& pi, Point1 const& pj, Point1 const& pk, + Point2 const& qi, Point2 const& qj, Point2 const& qk, + TurnInfo const& tp_model, + OutputIterator out) + { + typedef model::referring_segment<Point1 const> segment_type1; + typedef model::referring_segment<Point1 const> segment_type2; + segment_type1 p1(pi, pj), p2(pj, pk); + segment_type2 q1(qi, qj), q2(qj, qk); + + typename strategy::return_type result = strategy::apply(p1, q1); + + char const method = result.template get<1>().how; + + // Copy, to copy possibly extended fields + TurnInfo tp = tp_model; + + + // Select method and apply + switch(method) + { + case 'a' : // collinear, "at" + case 'f' : // collinear, "from" + case 's' : // starts from the middle + if (AssignPolicy::include_no_turn + && result.template get<0>().count > 0) + { + only_convert<TurnInfo>::apply(tp, + result.template get<0>()); + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + break; + + case 'd' : // disjoint: never do anything + break; + + case 'm' : + { + typedef touch_interior + < + TurnInfo, + typename si::side_strategy_type + > policy; + + // If Q (1) arrives (1) + if (result.template get<1>().arrival[1] == 1) + { + policy::template apply<0>(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>()); + } + else + { + // Swap p/q + policy::template apply<1>(qi, qj, qk, pi, pj, pk, + tp, result.template get<0>(), result.template get<1>()); + } + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + break; + case 'i' : + { + typedef crosses + < + TurnInfo, + typename si::side_strategy_type + > policy; + + policy::apply(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>()); + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + break; + case 't' : + { + // Both touch (both arrive there) + typedef touch + < + TurnInfo, + typename si::side_strategy_type + > policy; + + policy::apply(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>()); + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + break; + case 'e': + { + if (! result.template get<1>().opposite) + { + // Both equal + // or collinear-and-ending at intersection point + typedef equal + < + TurnInfo, + typename si::side_strategy_type + > policy; + + policy::apply(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>()); + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + // If they ARE opposite, don't do anything. + } + break; + case 'c' : + { + // Collinear + if (! result.template get<1>().opposite) + { + + if (result.template get<1>().arrival[0] == 0) + { + // Collinear, but similar thus handled as equal + equal + < + TurnInfo, + typename si::side_strategy_type + >::apply(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>()); + + // override assigned method + tp.method = method_collinear; + } + else + { + collinear + < + TurnInfo, + typename si::side_strategy_type + >::apply(pi, pj, pk, qi, qj, qk, + tp, result.template get<0>(), result.template get<1>()); + } + + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + else + { + collinear_opposite + < + TurnInfo, + typename si::side_strategy_type, + AssignPolicy + >::apply(pi, pj, pk, qi, qj, qk, + tp, out, result.template get<0>(), result.template get<1>()); + } + } + break; + case '0' : + { + // degenerate points + if (AssignPolicy::include_degenerate) + { + only_convert<TurnInfo>::apply(tp, result.template get<0>()); + AssignPolicy::apply(tp, pi, qi); + *out++ = tp; + } + } + break; + default : + { +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + throw turn_info_exception(method); +#endif + } + break; + } + + return out; + } +}; + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/boost/geometry/algorithms/detail/overlay/get_turns.hpp new file mode 100644 index 0000000000..5740a8b7ba --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -0,0 +1,866 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURNS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURNS_HPP + + +#include <cstddef> +#include <map> + +#include <boost/array.hpp> +#include <boost/mpl/if.hpp> +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/tuple/tuple.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/reverse_dispatch.hpp> + +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/ring_type.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/util/math.hpp> +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/views/reversible_view.hpp> +#include <boost/geometry/views/detail/range_type.hpp> + +#include <boost/geometry/geometries/box.hpp> +#include <boost/geometry/geometries/segment.hpp> + +#include <boost/geometry/iterators/ever_circling_iterator.hpp> + +#include <boost/geometry/strategies/cartesian/cart_intersect.hpp> +#include <boost/geometry/strategies/intersection.hpp> +#include <boost/geometry/strategies/intersection_result.hpp> + +#include <boost/geometry/algorithms/detail/disjoint.hpp> +#include <boost/geometry/algorithms/detail/partition.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> + +#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp> + + +#include <boost/geometry/algorithms/detail/sections/range_by_section.hpp> + +#include <boost/geometry/algorithms/expand.hpp> +#include <boost/geometry/algorithms/detail/sections/sectionalize.hpp> + +#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION +# include <sstream> +# include <boost/geometry/io/dsv/write.hpp> +#endif + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace get_turns +{ + + +struct no_interrupt_policy +{ + static bool const enabled = false; + + template <typename Range> + static inline bool apply(Range const&) + { + return false; + } +}; + + +template +< + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Section1, typename Section2, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +class get_turns_in_sections +{ + typedef typename closeable_view + < + typename range_type<Geometry1>::type const, + closure<Geometry1>::value + >::type cview_type1; + typedef typename closeable_view + < + typename range_type<Geometry2>::type const, + closure<Geometry2>::value + >::type cview_type2; + + typedef typename reversible_view + < + cview_type1 const, + Reverse1 ? iterate_reverse : iterate_forward + >::type view_type1; + typedef typename reversible_view + < + cview_type2 const, + Reverse2 ? iterate_reverse : iterate_forward + >::type view_type2; + + typedef typename boost::range_iterator + < + view_type1 const + >::type range1_iterator; + + typedef typename boost::range_iterator + < + view_type2 const + >::type range2_iterator; + + + template <typename Geometry, typename Section> + static inline bool neighbouring(Section const& section, + int index1, int index2) + { + // About n-2: + // (square: range_count=5, indices 0,1,2,3 + // -> 0-3 are adjacent, don't check on intersections) + // Also tested for open polygons, and/or duplicates + // About first condition: will be optimized by compiler (static) + // It checks if it is areal (box,ring,(multi)polygon + int const n = int(section.range_count); + return boost::is_same + < + typename tag_cast + < + typename geometry::point_type<Geometry1>::type, + areal_tag + >::type, + areal_tag + >::value + && index1 == 0 + && index2 >= n - 2 + ; + } + + +public : + // Returns true if terminated, false if interrupted + static inline bool apply( + int source_id1, Geometry1 const& geometry1, Section1 const& sec1, + int source_id2, Geometry2 const& geometry2, Section2 const& sec2, + bool skip_larger, + Turns& turns, + InterruptPolicy& interrupt_policy) + { + cview_type1 cview1(range_by_section(geometry1, sec1)); + cview_type2 cview2(range_by_section(geometry2, sec2)); + view_type1 view1(cview1); + view_type2 view2(cview2); + + range1_iterator begin_range_1 = boost::begin(view1); + range1_iterator end_range_1 = boost::end(view1); + + range2_iterator begin_range_2 = boost::begin(view2); + range2_iterator end_range_2 = boost::end(view2); + + int const dir1 = sec1.directions[0]; + int const dir2 = sec2.directions[0]; + int index1 = sec1.begin_index; + int ndi1 = sec1.non_duplicate_index; + + bool const same_source = + source_id1 == source_id2 + && sec1.ring_id.multi_index == sec2.ring_id.multi_index + && sec1.ring_id.ring_index == sec2.ring_id.ring_index; + + range1_iterator prev1, it1, end1; + + get_start_point_iterator(sec1, view1, prev1, it1, end1, + index1, ndi1, dir1, sec2.bounding_box); + + // We need a circular iterator because it might run through the closing point. + // One circle is actually enough but this one is just convenient. + ever_circling_iterator<range1_iterator> next1(begin_range_1, end_range_1, it1, true); + next1++; + + // Walk through section and stop if we exceed the other box + // section 2: [--------------] + // section 1: |----|---|---|---|---| + for (prev1 = it1++, next1++; + it1 != end1 && ! exceeding<0>(dir1, *prev1, sec2.bounding_box); + ++prev1, ++it1, ++index1, ++next1, ++ndi1) + { + ever_circling_iterator<range1_iterator> nd_next1( + begin_range_1, end_range_1, next1, true); + advance_to_non_duplicate_next(nd_next1, it1, sec1); + + int index2 = sec2.begin_index; + int ndi2 = sec2.non_duplicate_index; + + range2_iterator prev2, it2, end2; + + get_start_point_iterator(sec2, view2, prev2, it2, end2, + index2, ndi2, dir2, sec1.bounding_box); + ever_circling_iterator<range2_iterator> next2(begin_range_2, end_range_2, it2, true); + next2++; + + for (prev2 = it2++, next2++; + it2 != end2 && ! exceeding<0>(dir2, *prev2, sec1.bounding_box); + ++prev2, ++it2, ++index2, ++next2, ++ndi2) + { + bool skip = same_source; + if (skip) + { + // If sources are the same (possibly self-intersecting): + // skip if it is a neighbouring segment. + // (including first-last segment + // and two segments with one or more degenerate/duplicate + // (zero-length) segments in between) + + // Also skip if index1 < index2 to avoid getting all + // intersections twice (only do this on same source!) + + skip = (skip_larger && index1 >= index2) + || ndi2 == ndi1 + 1 + || neighbouring<Geometry1>(sec1, index1, index2) + ; + } + + if (! skip) + { + // Move to the "non duplicate next" + ever_circling_iterator<range2_iterator> nd_next2( + begin_range_2, end_range_2, next2, true); + advance_to_non_duplicate_next(nd_next2, it2, sec2); + + typedef typename boost::range_value<Turns>::type turn_info; + typedef typename turn_info::point_type ip; + + turn_info ti; + ti.operations[0].seg_id = segment_identifier(source_id1, + sec1.ring_id.multi_index, sec1.ring_id.ring_index, index1), + ti.operations[1].seg_id = segment_identifier(source_id2, + sec2.ring_id.multi_index, sec2.ring_id.ring_index, index2), + + ti.operations[0].other_id = ti.operations[1].seg_id; + ti.operations[1].other_id = ti.operations[0].seg_id; + + std::size_t const size_before = boost::size(turns); + + TurnPolicy::apply(*prev1, *it1, *nd_next1, *prev2, *it2, *nd_next2, + ti, std::back_inserter(turns)); + + if (InterruptPolicy::enabled) + { + if (interrupt_policy.apply( + std::make_pair(boost::begin(turns) + size_before, + boost::end(turns)))) + { + return false; + } + } + } + } + } + return true; + } + + +private : + typedef typename geometry::point_type<Geometry1>::type point1_type; + typedef typename geometry::point_type<Geometry2>::type point2_type; + typedef typename model::referring_segment<point1_type const> segment1_type; + typedef typename model::referring_segment<point2_type const> segment2_type; + + + template <size_t Dim, typename Point, typename Box> + static inline bool preceding(int dir, Point const& point, Box const& box) + { + return (dir == 1 && get<Dim>(point) < get<min_corner, Dim>(box)) + || (dir == -1 && get<Dim>(point) > get<max_corner, Dim>(box)); + } + + template <size_t Dim, typename Point, typename Box> + static inline bool exceeding(int dir, Point const& point, Box const& box) + { + return (dir == 1 && get<Dim>(point) > get<max_corner, Dim>(box)) + || (dir == -1 && get<Dim>(point) < get<min_corner, Dim>(box)); + } + + template <typename Iterator, typename RangeIterator, typename Section> + static inline void advance_to_non_duplicate_next(Iterator& next, + RangeIterator const& it, Section const& section) + { + // To see where the next segments bend to, in case of touch/intersections + // on end points, we need (in case of degenerate/duplicate points) an extra + // iterator which moves to the REAL next point, so non duplicate. + // This needs an extra comparison (disjoint). + // (Note that within sections, non duplicate points are already asserted, + // by the sectionalize process). + + // So advance to the "non duplicate next" + // (the check is defensive, to avoid endless loops) + std::size_t check = 0; + while(! detail::disjoint::disjoint_point_point(*it, *next) + && check++ < section.range_count) + { + next++; + } + } + + // It is NOT possible to have section-iterators here + // because of the logistics of "index" (the section-iterator automatically + // skips to the begin-point, we loose the index or have to recalculate it) + // So we mimic it here + template <typename Range, typename Section, typename Box> + static inline void get_start_point_iterator(Section & section, + Range const& range, + typename boost::range_iterator<Range const>::type& it, + typename boost::range_iterator<Range const>::type& prev, + typename boost::range_iterator<Range const>::type& end, + int& index, int& ndi, + int dir, Box const& other_bounding_box) + { + it = boost::begin(range) + section.begin_index; + end = boost::begin(range) + section.end_index + 1; + + // Mimic section-iterator: + // Skip to point such that section interects other box + prev = it++; + for(; it != end && preceding<0>(dir, *it, other_bounding_box); + prev = it++, index++, ndi++) + {} + // Go back one step because we want to start completely preceding + it = prev; + } +}; + +struct get_section_box +{ + template <typename Box, typename InputItem> + static inline void apply(Box& total, InputItem const& item) + { + geometry::expand(total, item.bounding_box); + } +}; + +struct ovelaps_section_box +{ + template <typename Box, typename InputItem> + static inline bool apply(Box const& box, InputItem const& item) + { + return ! detail::disjoint::disjoint_box_box(box, item.bounding_box); + } +}; + +template +< + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct section_visitor +{ + int m_source_id1; + Geometry1 const& m_geometry1; + int m_source_id2; + Geometry2 const& m_geometry2; + Turns& m_turns; + InterruptPolicy& m_interrupt_policy; + + section_visitor(int id1, Geometry1 const& g1, + int id2, Geometry2 const& g2, + Turns& turns, InterruptPolicy& ip) + : m_source_id1(id1), m_geometry1(g1) + , m_source_id2(id2), m_geometry2(g2) + , m_turns(turns) + , m_interrupt_policy(ip) + {} + + template <typename Section> + inline bool apply(Section const& sec1, Section const& sec2) + { + if (! detail::disjoint::disjoint_box_box(sec1.bounding_box, sec2.bounding_box)) + { + return get_turns_in_sections + < + Geometry1, + Geometry2, + Reverse1, Reverse2, + Section, Section, + Turns, + TurnPolicy, + InterruptPolicy + >::apply( + m_source_id1, m_geometry1, sec1, + m_source_id2, m_geometry2, sec2, + false, + m_turns, m_interrupt_policy); + } + return true; + } + +}; + +template +< + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +class get_turns_generic +{ + +public: + static inline void apply( + int source_id1, Geometry1 const& geometry1, + int source_id2, Geometry2 const& geometry2, + Turns& turns, InterruptPolicy& interrupt_policy) + { + // First create monotonic sections... + typedef typename boost::range_value<Turns>::type ip_type; + typedef typename ip_type::point_type point_type; + typedef model::box<point_type> box_type; + typedef typename geometry::sections<box_type, 2> sections_type; + + sections_type sec1, sec2; + + geometry::sectionalize<Reverse1>(geometry1, sec1, 0); + geometry::sectionalize<Reverse2>(geometry2, sec2, 1); + + // ... and then partition them, intersecting overlapping sections in visitor method + section_visitor + < + Geometry1, Geometry2, + Reverse1, Reverse2, + Turns, TurnPolicy, InterruptPolicy + > visitor(source_id1, geometry1, source_id2, geometry2, turns, interrupt_policy); + + geometry::partition + < + box_type, get_section_box, ovelaps_section_box + >::apply(sec1, sec2, visitor); + } +}; + + +// Get turns for a range with a box, following Cohen-Sutherland (cs) approach +template +< + typename Range, typename Box, + bool ReverseRange, bool ReverseBox, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns_cs +{ + typedef typename boost::range_value<Turns>::type turn_info; + typedef typename geometry::point_type<Range>::type point_type; + typedef typename geometry::point_type<Box>::type box_point_type; + + typedef typename closeable_view + < + Range const, + closure<Range>::value + >::type cview_type; + + typedef typename reversible_view + < + cview_type const, + ReverseRange ? iterate_reverse : iterate_forward + >::type view_type; + + typedef typename boost::range_iterator + < + view_type const + >::type iterator_type; + + + static inline void apply( + int source_id1, Range const& range, + int source_id2, Box const& box, + Turns& turns, + InterruptPolicy& , + int multi_index = -1, int ring_index = -1) + { + if (boost::size(range) <= 1) + { + return; + } + + boost::array<box_point_type,4> bp; + assign_box_corners_oriented<ReverseBox>(box, bp); + + cview_type cview(range); + view_type view(cview); + + iterator_type it = boost::begin(view); + + ever_circling_iterator<iterator_type> next( + boost::begin(view), boost::end(view), it, true); + next++; + next++; + + //bool first = true; + + //char previous_side[2] = {0, 0}; + + int index = 0; + + for (iterator_type prev = it++; + it != boost::end(view); + prev = it++, next++, index++) + { + segment_identifier seg_id(source_id1, + multi_index, ring_index, index); + + /*if (first) + { + previous_side[0] = get_side<0>(box, *prev); + previous_side[1] = get_side<1>(box, *prev); + } + + char current_side[2]; + current_side[0] = get_side<0>(box, *it); + current_side[1] = get_side<1>(box, *it); + + // There can NOT be intersections if + // 1) EITHER the two points are lying on one side of the box (! 0 && the same) + // 2) OR same in Y-direction + // 3) OR all points are inside the box (0) + if (! ( + (current_side[0] != 0 && current_side[0] == previous_side[0]) + || (current_side[1] != 0 && current_side[1] == previous_side[1]) + || (current_side[0] == 0 + && current_side[1] == 0 + && previous_side[0] == 0 + && previous_side[1] == 0) + ) + )*/ + if (true) + { + get_turns_with_box(seg_id, source_id2, + *prev, *it, *next, + bp[0], bp[1], bp[2], bp[3], + turns); + // Future performance enhancement: + // return if told by the interrupt policy + } + } + } + +private: + template<std::size_t Index, typename Point> + static inline int get_side(Box const& box, Point const& point) + { + // Inside -> 0 + // Outside -> -1 (left/below) or 1 (right/above) + // On border -> -2 (left/lower) or 2 (right/upper) + // The only purpose of the value is to not be the same, + // and to denote if it is inside (0) + + typename coordinate_type<Point>::type const& c = get<Index>(point); + typename coordinate_type<Box>::type const& left = get<min_corner, Index>(box); + typename coordinate_type<Box>::type const& right = get<max_corner, Index>(box); + + if (geometry::math::equals(c, left)) return -2; + else if (geometry::math::equals(c, right)) return 2; + else if (c < left) return -1; + else if (c > right) return 1; + else return 0; + } + + static inline void get_turns_with_box(segment_identifier const& seg_id, int source_id2, + // Points from a range: + point_type const& rp0, + point_type const& rp1, + point_type const& rp2, + // Points from the box + box_point_type const& bp0, + box_point_type const& bp1, + box_point_type const& bp2, + box_point_type const& bp3, + // Output + Turns& turns) + { + // Depending on code some relations can be left out + + typedef typename boost::range_value<Turns>::type turn_info; + + turn_info ti; + ti.operations[0].seg_id = seg_id; + ti.operations[0].other_id = ti.operations[1].seg_id; + ti.operations[1].other_id = seg_id; + + ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 0); + TurnPolicy::apply(rp0, rp1, rp2, bp0, bp1, bp2, + ti, std::back_inserter(turns)); + + ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 1); + TurnPolicy::apply(rp0, rp1, rp2, bp1, bp2, bp3, + ti, std::back_inserter(turns)); + + ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 2); + TurnPolicy::apply(rp0, rp1, rp2, bp2, bp3, bp0, + ti, std::back_inserter(turns)); + + ti.operations[1].seg_id = segment_identifier(source_id2, -1, -1, 3); + TurnPolicy::apply(rp0, rp1, rp2, bp3, bp0, bp1, + ti, std::back_inserter(turns)); + } + +}; + + +template +< + typename Polygon, typename Box, + bool Reverse, bool ReverseBox, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns_polygon_cs +{ + static inline void apply( + int source_id1, Polygon const& polygon, + int source_id2, Box const& box, + Turns& turns, InterruptPolicy& interrupt_policy, + int multi_index = -1) + { + typedef typename geometry::ring_type<Polygon>::type ring_type; + + typedef detail::get_turns::get_turns_cs + < + ring_type, Box, + Reverse, ReverseBox, + Turns, + TurnPolicy, + InterruptPolicy + > intersector_type; + + intersector_type::apply( + source_id1, geometry::exterior_ring(polygon), + source_id2, box, turns, interrupt_policy, + multi_index, -1); + + int i = 0; + + typename interior_return_type<Polygon const>::type rings + = interior_rings(polygon); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); + ++it, ++i) + { + intersector_type::apply( + source_id1, *it, + source_id2, box, turns, interrupt_policy, + multi_index, i); + } + + } +}; + +}} // namespace detail::get_turns +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// Because this is "detail" method, and most implementations will use "generic", +// we take the freedom to derive it from "generic". +template +< + typename GeometryTag1, typename GeometryTag2, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns + : detail::get_turns::get_turns_generic + < + Geometry1, Geometry2, + Reverse1, Reverse2, + Turns, + TurnPolicy, + InterruptPolicy + > +{}; + + +template +< + typename Polygon, typename Box, + bool ReversePolygon, bool ReverseBox, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns + < + polygon_tag, box_tag, + Polygon, Box, + ReversePolygon, ReverseBox, + Turns, + TurnPolicy, + InterruptPolicy + > : detail::get_turns::get_turns_polygon_cs + < + Polygon, Box, + ReversePolygon, ReverseBox, + Turns, TurnPolicy, InterruptPolicy + > +{}; + + +template +< + typename Ring, typename Box, + bool ReverseRing, bool ReverseBox, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns + < + ring_tag, box_tag, + Ring, Box, + ReverseRing, ReverseBox, + Turns, + TurnPolicy, + InterruptPolicy + > : detail::get_turns::get_turns_cs + < + Ring, Box, ReverseRing, ReverseBox, + Turns, TurnPolicy, InterruptPolicy + > + +{}; + + +template +< + typename GeometryTag1, typename GeometryTag2, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns_reversed +{ + static inline void apply( + int source_id1, Geometry1 const& g1, + int source_id2, Geometry2 const& g2, + Turns& turns, InterruptPolicy& interrupt_policy) + { + get_turns + < + GeometryTag2, GeometryTag1, + Geometry2, Geometry1, + Reverse2, Reverse1, + Turns, TurnPolicy, + InterruptPolicy + >::apply(source_id2, g2, source_id1, g1, turns, interrupt_policy); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + + +/*! +\brief \brief_calc2{turn points} +\ingroup overlay +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Turns type of turn-container (e.g. vector of "intersection/turn point"'s) +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param turns container which will contain turn points +\param interrupt_policy policy determining if process is stopped + when intersection is found + */ +template +< + bool Reverse1, bool Reverse2, + typename AssignPolicy, + typename Geometry1, + typename Geometry2, + typename Turns, + typename InterruptPolicy +> +inline void get_turns(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Turns& turns, + InterruptPolicy& interrupt_policy) +{ + concept::check_concepts_and_equal_dimensions<Geometry1 const, Geometry2 const>(); + + typedef typename strategy_intersection + < + typename cs_tag<Geometry1>::type, + Geometry1, + Geometry2, + typename boost::range_value<Turns>::type + >::segment_intersection_strategy_type segment_intersection_strategy_type; + + typedef detail::overlay::get_turn_info + < + typename point_type<Geometry1>::type, + typename point_type<Geometry2>::type, + typename boost::range_value<Turns>::type, + AssignPolicy + > TurnPolicy; + + boost::mpl::if_c + < + reverse_dispatch<Geometry1, Geometry2>::type::value, + dispatch::get_turns_reversed + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, Geometry2, + Reverse1, Reverse2, + Turns, TurnPolicy, + InterruptPolicy + >, + dispatch::get_turns + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, Geometry2, + Reverse1, Reverse2, + Turns, TurnPolicy, + InterruptPolicy + > + >::type::apply( + 0, geometry1, + 1, geometry2, + turns, interrupt_policy); +} + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURNS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp b/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp new file mode 100644 index 0000000000..1e878ca525 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp @@ -0,0 +1,672 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_TANGENCIES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_TANGENCIES_HPP + +#include <algorithm> + +#include <boost/geometry/algorithms/detail/ring_identifier.hpp> +#include <boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> + +#include <boost/geometry/geometries/segment.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template +< + typename TurnPoints, + typename Indexed, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, + typename Strategy +> +struct sort_in_cluster +{ + inline sort_in_cluster(TurnPoints const& turn_points + , Geometry1 const& geometry1 + , Geometry2 const& geometry2 + , Strategy const& strategy) + : m_turn_points(turn_points) + , m_geometry1(geometry1) + , m_geometry2(geometry2) + , m_strategy(strategy) + {} + +private : + + TurnPoints const& m_turn_points; + Geometry1 const& m_geometry1; + Geometry2 const& m_geometry2; + Strategy const& m_strategy; + + typedef typename Indexed::type turn_operation_type; + typedef typename geometry::point_type<Geometry1>::type point_type; + typedef model::referring_segment<point_type const> segment_type; + + // Determine how p/r and p/s are located. + template <typename P> + static inline void overlap_info(P const& pi, P const& pj, + P const& ri, P const& rj, + P const& si, P const& sj, + bool& pr_overlap, bool& ps_overlap, bool& rs_overlap) + { + // Determine how p/r and p/s are located. + // One of them is coming from opposite direction. + + typedef strategy::intersection::relate_cartesian_segments + < + policies::relate::segments_intersection_points + < + segment_type, + segment_type, + segment_intersection_points<point_type> + > + > policy; + + segment_type p(pi, pj); + segment_type r(ri, rj); + segment_type s(si, sj); + + // Get the intersection point (or two points) + segment_intersection_points<point_type> pr = policy::apply(p, r); + segment_intersection_points<point_type> ps = policy::apply(p, s); + segment_intersection_points<point_type> rs = policy::apply(r, s); + + // Check on overlap + pr_overlap = pr.count == 2; + ps_overlap = ps.count == 2; + rs_overlap = rs.count == 2; + } + + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + inline void debug_consider(int order, Indexed const& left, + Indexed const& right, std::string const& header, + bool skip = true, + std::string const& extra = "", bool ret = false + ) const + { + if (skip) return; + + point_type pi, pj, ri, rj, si, sj; + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + left.subject.seg_id, + pi, pj); + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + left.subject.other_id, + ri, rj); + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + right.subject.other_id, + si, sj); + + bool prc = false, psc = false, rsc = false; + overlap_info(pi, pj, ri, rj, si, sj, prc, psc, rsc); + + int const side_ri_p = m_strategy.apply(pi, pj, ri); + int const side_rj_p = m_strategy.apply(pi, pj, rj); + int const side_si_p = m_strategy.apply(pi, pj, si); + int const side_sj_p = m_strategy.apply(pi, pj, sj); + int const side_si_r = m_strategy.apply(ri, rj, si); + int const side_sj_r = m_strategy.apply(ri, rj, sj); + + std::cout << "Case: " << header << " for " << left.index << " / " << right.index << std::endl; +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH_MORE + std::cout << " Segment p:" << geometry::wkt(pi) << " .. " << geometry::wkt(pj) << std::endl; + std::cout << " Segment r:" << geometry::wkt(ri) << " .. " << geometry::wkt(rj) << std::endl; + std::cout << " Segment s:" << geometry::wkt(si) << " .. " << geometry::wkt(sj) << std::endl; + + std::cout << " r//p: " << side_ri_p << " / " << side_rj_p << std::endl; + std::cout << " s//p: " << side_si_p << " / " << side_sj_p << std::endl; + std::cout << " s//r: " << side_si_r << " / " << side_sj_r << std::endl; +#endif + + std::cout << header + //<< " order: " << order + << " ops: " << operation_char(left.subject.operation) + << "/" << operation_char(right.subject.operation) + << " ri//p: " << side_ri_p + << " si//p: " << side_si_p + << " si//r: " << side_si_r + << " cnts: " << int(prc) << "," << int(psc) << "," << int(rsc) + //<< " idx: " << left.index << "/" << right.index + ; + + if (! extra.empty()) + { + std::cout << " " << extra << " " << (ret ? "true" : "false"); + } + std::cout << std::endl; + } +#else + inline void debug_consider(int, Indexed const& , + Indexed const& , std::string const& , + bool = true, + std::string const& = "", bool = false + ) const + {} +#endif + + + // ux/ux + inline bool consider_ux_ux(Indexed const& left, + Indexed const& right + , std::string const& // header + ) const + { + bool ret = left.index < right.index; + + // In combination of u/x, x/u: take first union, then blocked. + // Solves #88, #61, #56, #80 + if (left.subject.operation == operation_union + && right.subject.operation == operation_blocked) + { + ret = true; + } + else if (left.subject.operation == operation_blocked + && right.subject.operation == operation_union) + { + ret = false; + } + else + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << "ux/ux unhandled" << std::endl; +#endif + } + + //debug_consider(0, left, right, header, false, "-> return ", ret); + + return ret; + } + + inline bool consider_iu_ux(Indexed const& left, + Indexed const& right, + int order // 1: iu first, -1: ux first + , std::string const& // header + ) const + { + bool ret = false; + + if (left.subject.operation == operation_union + && right.subject.operation == operation_union) + { + ret = order == 1; + } + else if (left.subject.operation == operation_union + && right.subject.operation == operation_blocked) + { + ret = true; + } + else if (right.subject.operation == operation_union + && left.subject.operation == operation_blocked) + { + ret = false; + } + else if (left.subject.operation == operation_union) + { + ret = true; + } + else if (right.subject.operation == operation_union) + { + ret = false; + } + else + { +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + // this still happens in the traverse.cpp test + std::cout << " iu/ux unhandled" << std::endl; +#endif + ret = order == 1; + } + + //debug_consider(0, left, right, header, false, "-> return", ret); + return ret; + } + + inline bool consider_iu_ix(Indexed const& left, + Indexed const& right, + int order // 1: iu first, -1: ix first + , std::string const& // header + ) const + { + //debug_consider(order, left, right, header, false, "iu/ix"); + + return left.subject.operation == operation_intersection + && right.subject.operation == operation_intersection ? order == 1 + : left.subject.operation == operation_intersection ? false + : right.subject.operation == operation_intersection ? true + : order == 1; + } + + + inline bool consider_iu_iu(Indexed const& left, Indexed const& right, + std::string const& header) const + { + //debug_consider(0, left, right, header); + + // In general, order it like "union, intersection". + if (left.subject.operation == operation_intersection + && right.subject.operation == operation_union) + { + //debug_consider(0, left, right, header, false, "i,u", false); + return false; + } + else if (left.subject.operation == operation_union + && right.subject.operation == operation_intersection) + { + //debug_consider(0, left, right, header, false, "u,i", true); + return true; + } + + point_type pi, pj, ri, rj, si, sj; + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + left.subject.seg_id, + pi, pj); + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + left.subject.other_id, + ri, rj); + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + right.subject.other_id, + si, sj); + + int const side_ri_p = m_strategy.apply(pi, pj, ri); + int const side_si_p = m_strategy.apply(pi, pj, si); + int const side_si_r = m_strategy.apply(ri, rj, si); + + // Both located at same side (#58, pie_21_7_21_0_3) + if (side_ri_p * side_si_p == 1 && side_si_r != 0) + { + // Take the most left one + if (left.subject.operation == operation_union + && right.subject.operation == operation_union) + { + bool ret = side_si_r == 1; + //debug_consider(0, left, right, header, false, "same side", ret); + return ret; + } + } + + + // Coming from opposite sides (#59, #99) + if (side_ri_p * side_si_p == -1) + { + bool ret = false; + + { + ret = side_ri_p == 1; // #100 + debug_consider(0, left, right, header, false, "opp.", ret); + return ret; + } +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << " iu/iu coming from opposite unhandled" << std::endl; +#endif + } + + // We need EXTRA information here: are p/r/s overlapping? + bool pr_ov = false, ps_ov = false, rs_ov = false; + overlap_info(pi, pj, ri, rj, si, sj, pr_ov, ps_ov, rs_ov); + + // One coming from right (#83,#90) + // One coming from left (#90, #94, #95) + if (side_si_r != 0 && (side_ri_p != 0 || side_si_p != 0)) + { + bool ret = false; + + if (pr_ov || ps_ov) + { + int r = side_ri_p != 0 ? side_ri_p : side_si_p; + ret = r * side_si_r == 1; + } + else + { + ret = side_si_r == 1; + } + + debug_consider(0, left, right, header, false, "left or right", ret); + return ret; + } + + // All aligned (#92, #96) + if (side_ri_p == 0 && side_si_p == 0 && side_si_r == 0) + { + // One of them is coming from opposite direction. + + // Take the one NOT overlapping + bool ret = false; + bool found = false; + if (pr_ov && ! ps_ov) + { + ret = true; + found = true; + } + else if (!pr_ov && ps_ov) + { + ret = false; + found = true; + } + + debug_consider(0, left, right, header, false, "aligned", ret); + if (found) + { + return ret; + } + } + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << " iu/iu unhandled" << std::endl; + debug_consider(0, left, right, header, false, "unhandled", left.index < right.index); +#endif + return left.index < right.index; + } + + inline bool consider_ii(Indexed const& left, Indexed const& right, + std::string const& header) const + { + debug_consider(0, left, right, header); + + point_type pi, pj, ri, rj, si, sj; + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + left.subject.seg_id, + pi, pj); + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + left.subject.other_id, + ri, rj); + geometry::copy_segment_points<Reverse1, Reverse2>(m_geometry1, m_geometry2, + right.subject.other_id, + si, sj); + + int const side_ri_p = m_strategy.apply(pi, pj, ri); + int const side_si_p = m_strategy.apply(pi, pj, si); + + // Two other points are (mostly) lying both right of the considered segment + // Take the most left one + int const side_si_r = m_strategy.apply(ri, rj, si); + if (side_ri_p == -1 + && side_si_p == -1 + && side_si_r != 0) + { + bool const ret = side_si_r != 1; + return ret; + } + return left.index < right.index; + } + + +public : + inline bool operator()(Indexed const& left, Indexed const& right) const + { + bool const default_order = left.index < right.index; + + if ((m_turn_points[left.index].discarded || left.discarded) + && (m_turn_points[right.index].discarded || right.discarded)) + { + return default_order; + } + else if (m_turn_points[left.index].discarded || left.discarded) + { + // Be careful to sort discarded first, then all others + return true; + } + else if (m_turn_points[right.index].discarded || right.discarded) + { + // See above so return false here such that right (discarded) + // is sorted before left (not discarded) + return false; + } + else if (m_turn_points[left.index].combination(operation_blocked, operation_union) + && m_turn_points[right.index].combination(operation_blocked, operation_union)) + { + // ux/ux + return consider_ux_ux(left, right, "ux/ux"); + } + else if (m_turn_points[left.index].both(operation_union) + && m_turn_points[right.index].both(operation_union)) + { + // uu/uu, Order is arbitrary + // Note: uu/uu is discarded now before so this point will + // not be reached. + return default_order; + } + else if (m_turn_points[left.index].combination(operation_intersection, operation_union) + && m_turn_points[right.index].combination(operation_intersection, operation_union)) + { + return consider_iu_iu(left, right, "iu/iu"); + } + else if (m_turn_points[left.index].both(operation_intersection) + && m_turn_points[right.index].both(operation_intersection)) + { + return consider_ii(left, right, "ii/ii"); + } + else if (m_turn_points[left.index].combination(operation_union, operation_blocked) + && m_turn_points[right.index].combination(operation_intersection, operation_union)) + { + return consider_iu_ux(left, right, -1, "ux/iu"); + } + else if (m_turn_points[left.index].combination(operation_intersection, operation_union) + && m_turn_points[right.index].combination(operation_union, operation_blocked)) + { + return consider_iu_ux(left, right, 1, "iu/ux"); + } + else if (m_turn_points[left.index].combination(operation_intersection, operation_blocked) + && m_turn_points[right.index].combination(operation_intersection, operation_union)) + { + return consider_iu_ix(left, right, 1, "ix/iu"); + } + else if (m_turn_points[left.index].combination(operation_intersection, operation_union) + && m_turn_points[right.index].combination(operation_intersection, operation_blocked)) + { + return consider_iu_ix(left, right, -1, "iu/ix"); + } + else if (m_turn_points[left.index].method != method_equal + && m_turn_points[right.index].method == method_equal + ) + { + // If one of them was EQUAL or CONTINUES, it should always come first + return false; + } + else if (m_turn_points[left.index].method == method_equal + && m_turn_points[right.index].method != method_equal + ) + { + return true; + } + + // Now we have no clue how to sort. + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << " Consider: " << operation_char(m_turn_points[left.index].operations[0].operation) + << operation_char(m_turn_points[left.index].operations[1].operation) + << "/" << operation_char(m_turn_points[right.index].operations[0].operation) + << operation_char(m_turn_points[right.index].operations[1].operation) + << " " << " Take " << left.index << " < " << right.index + << std::cout; +#endif + + return default_order; + } +}; + + + +template +< + typename IndexType, + typename Iterator, + typename TurnPoints, + typename Geometry1, + typename Geometry2, + typename Strategy +> +inline void inspect_cluster(Iterator begin_cluster, Iterator end_cluster, + TurnPoints& turn_points, + operation_type , + Geometry1 const& , Geometry2 const& , + Strategy const& ) +{ + int count = 0; + + // Make an analysis about all occuring cases here. + std::map<std::pair<operation_type, operation_type>, int> inspection; + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + operation_type first = turn_points[it->index].operations[0].operation; + operation_type second = turn_points[it->index].operations[1].operation; + if (first > second) + { + std::swap(first, second); + } + inspection[std::make_pair(first, second)]++; + count++; + } + + + bool keep_cc = false; + + // Decide about which is going to be discarded here. + if (inspection[std::make_pair(operation_union, operation_union)] == 1 + && inspection[std::make_pair(operation_continue, operation_continue)] == 1) + { + // In case of uu/cc, discard the uu, that indicates a tangency and + // inclusion would disturb the (e.g.) cc-cc-cc ordering + // NOTE: uu is now discarded anyhow. + keep_cc = true; + } + else if (count == 2 + && inspection[std::make_pair(operation_intersection, operation_intersection)] == 1 + && inspection[std::make_pair(operation_union, operation_intersection)] == 1) + { + // In case of ii/iu, discard the iu. The ii should always be visited, + // Because (in case of not discarding iu) correctly ordering of ii/iu appears impossible + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + if (turn_points[it->index].combination(operation_intersection, operation_union)) + { + it->discarded = true; + } + } + } + + // Discard any continue turn, unless it is the only thing left + // (necessary to avoid cc-only rings, all being discarded + // e.g. traversal case #75) + int nd_count= 0, cc_count = 0; + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + if (! it->discarded) + { + nd_count++; + if (turn_points[it->index].both(operation_continue)) + { + cc_count++; + } + } + } + + if (nd_count == cc_count) + { + keep_cc = true; + } + + if (! keep_cc) + { + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + if (turn_points[it->index].both(operation_continue)) + { + it->discarded = true; + } + } + } +} + + +template +< + typename IndexType, + bool Reverse1, bool Reverse2, + typename Iterator, + typename TurnPoints, + typename Geometry1, + typename Geometry2, + typename Strategy +> +inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster, + TurnPoints& turn_points, + operation_type for_operation, + Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + // First inspect and (possibly) discard rows + inspect_cluster<IndexType>(begin_cluster, end_cluster, turn_points, + for_operation, geometry1, geometry2, strategy); + + + // Then sort this range (discard rows will be ordered first and will be removed in enrich_assign) + std::sort(begin_cluster, end_cluster, + sort_in_cluster + < + TurnPoints, + IndexType, + Geometry1, Geometry2, + Reverse1, Reverse2, + Strategy + >(turn_points, geometry1, geometry2, strategy)); + + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + typedef typename IndexType::type operations_type; + operations_type const& op = turn_points[begin_cluster->index].operations[begin_cluster->operation_index]; + std::cout << "Clustered points on equal distance " << op.enriched.distance << std::endl; + std::cout << "->Indexes "; + + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + std::cout << " " << it->index; + } + std::cout << std::endl << "->Methods: "; + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + std::cout << " " << method_char(turn_points[it->index].method); + } + std::cout << std::endl << "->Operations: "; + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + std::cout << " " << operation_char(turn_points[it->index].operations[0].operation) + << operation_char(turn_points[it->index].operations[1].operation); + } + std::cout << std::endl << "->Discarded: "; + for (Iterator it = begin_cluster; it != end_cluster; ++it) + { + std::cout << " " << (it->discarded ? "true" : "false"); + } + std::cout << std::endl; + //<< "\tOn segments: " << prev_op.seg_id << " / " << prev_op.other_id + //<< " and " << op.seg_id << " / " << op.other_id + //<< geometry::distance(turn_points[prev->index].point, turn_points[it->index].point) +#endif + +} + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_TANGENCIES_HPP diff --git a/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp new file mode 100644 index 0000000000..8bca790d74 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -0,0 +1,690 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_INSERT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_INSERT_HPP + + +#include <cstddef> + +#include <boost/mpl/if.hpp> +#include <boost/mpl/assert.hpp> +#include <boost/range/metafunctions.hpp> + + +#include <boost/geometry/core/is_areal.hpp> +#include <boost/geometry/core/point_order.hpp> +#include <boost/geometry/core/reverse_dispatch.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/detail/point_on_border.hpp> +#include <boost/geometry/algorithms/detail/overlay/clip_linestring.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp> +#include <boost/geometry/algorithms/detail/overlay/overlay.hpp> +#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> +#include <boost/geometry/algorithms/detail/overlay/follow.hpp> +#include <boost/geometry/views/segment_view.hpp> + +#if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) +#include <boost/foreach.hpp> +#endif + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersection +{ + +template +< + typename Segment1, typename Segment2, + typename OutputIterator, typename PointOut, + typename Strategy +> +struct intersection_segment_segment_point +{ + static inline OutputIterator apply(Segment1 const& segment1, + Segment2 const& segment2, OutputIterator out, + Strategy const& ) + { + typedef typename point_type<PointOut>::type point_type; + + // Get the intersection point (or two points) + segment_intersection_points<point_type> is + = strategy::intersection::relate_cartesian_segments + < + policies::relate::segments_intersection_points + < + Segment1, + Segment2, + segment_intersection_points<point_type> + > + >::apply(segment1, segment2); + + for (std::size_t i = 0; i < is.count; i++) + { + PointOut p; + geometry::convert(is.intersections[i], p); + *out++ = p; + } + return out; + } +}; + +template +< + typename Linestring1, typename Linestring2, + typename OutputIterator, typename PointOut, + typename Strategy +> +struct intersection_linestring_linestring_point +{ + static inline OutputIterator apply(Linestring1 const& linestring1, + Linestring2 const& linestring2, OutputIterator out, + Strategy const& ) + { + typedef typename point_type<PointOut>::type point_type; + + typedef detail::overlay::turn_info<point_type> turn_info; + std::deque<turn_info> turns; + + geometry::get_intersection_points(linestring1, linestring2, turns); + + for (typename boost::range_iterator<std::deque<turn_info> const>::type + it = boost::begin(turns); it != boost::end(turns); ++it) + { + PointOut p; + geometry::convert(it->point, p); + *out++ = p; + } + return out; + } +}; + +/*! +\brief Version of linestring with an areal feature (polygon or multipolygon) +*/ +template +< + typename LineString, typename Areal, + bool ReverseAreal, + typename OutputIterator, typename LineStringOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_of_linestring_with_areal +{ + typedef detail::overlay::follow + < + LineStringOut, + LineString, + Areal, + OverlayType + > follower; + +#if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) + template <typename Turn, typename Operation> + static inline void debug_follow(Turn const& turn, Operation op, + int index) + { + std::cout << index + << " at " << op.seg_id + << " meth: " << method_char(turn.method) + << " op: " << operation_char(op.operation) + << " vis: " << visited_char(op.visited) + << " of: " << operation_char(turn.operations[0].operation) + << operation_char(turn.operations[1].operation) + << " " << geometry::wkt(turn.point) + << std::endl; + } +#endif + + static inline OutputIterator apply(LineString const& linestring, Areal const& areal, + OutputIterator out, + Strategy const& ) + { + if (boost::size(linestring) == 0) + { + return out; + } + + typedef typename point_type<LineStringOut>::type point_type; + + typedef detail::overlay::traversal_turn_info<point_type> turn_info; + std::deque<turn_info> turns; + + detail::get_turns::no_interrupt_policy policy; + geometry::get_turns + < + false, + (OverlayType == overlay_intersection ? ReverseAreal : !ReverseAreal), + detail::overlay::calculate_distance_policy + >(linestring, areal, turns, policy); + + if (turns.empty()) + { + // No intersection points, it is either completely + // inside (interior + borders) + // or completely outside + + // Use border point (on a segment) to check this + // (because turn points might skip some cases) + point_type border_point; + if (! geometry::point_on_border(border_point, linestring, true)) + { + return out; + } + + + if (follower::included(border_point, areal)) + { + LineStringOut copy; + geometry::convert(linestring, copy); + *out++ = copy; + } + return out; + } + +#if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) + int index = 0; + BOOST_FOREACH(turn_info const& turn, turns) + { + debug_follow(turn, turn.operations[0], index++); + } +#endif + + return follower::apply + ( + linestring, areal, + geometry::detail::overlay::operation_intersection, + turns, out + ); + } +}; + + +}} // namespace detail::intersection +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + // tag dispatching: + typename TagIn1, typename TagIn2, typename TagOut, + // orientation + // metafunction finetuning helpers: + bool Areal1, bool Areal2, bool ArealOut, + // real types + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, + typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPES_OR_ORIENTATIONS + , (types<Geometry1, Geometry2, GeometryOut>) + ); +}; + + +template +< + typename TagIn1, typename TagIn2, typename TagOut, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, + typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + TagIn1, TagIn2, TagOut, + true, true, true, + Geometry1, Geometry2, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > : detail::overlay::overlay + <Geometry1, Geometry2, Reverse1, Reverse2, ReverseOut, OutputIterator, GeometryOut, OverlayType, Strategy> +{}; + + +// Any areal type with box: +template +< + typename TagIn, typename TagOut, + typename Geometry, typename Box, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, + typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + TagIn, box_tag, TagOut, + true, true, true, + Geometry, Box, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > : detail::overlay::overlay + <Geometry, Box, Reverse1, Reverse2, ReverseOut, OutputIterator, GeometryOut, OverlayType, Strategy> +{}; + + +template +< + typename Segment1, typename Segment2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + segment_tag, segment_tag, point_tag, + false, false, false, + Segment1, Segment2, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, Strategy + > : detail::intersection::intersection_segment_segment_point + < + Segment1, Segment2, + OutputIterator, GeometryOut, + Strategy + > +{}; + + +template +< + typename Linestring1, typename Linestring2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + linestring_tag, linestring_tag, point_tag, + false, false, false, + Linestring1, Linestring2, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, Strategy + > : detail::intersection::intersection_linestring_linestring_point + < + Linestring1, Linestring2, + OutputIterator, GeometryOut, + Strategy + > +{}; + + +template +< + typename Linestring, typename Box, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + linestring_tag, box_tag, linestring_tag, + false, true, false, + Linestring, Box, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > +{ + static inline OutputIterator apply(Linestring const& linestring, + Box const& box, OutputIterator out, Strategy const& ) + { + typedef typename point_type<GeometryOut>::type point_type; + strategy::intersection::liang_barsky<Box, point_type> lb_strategy; + return detail::intersection::clip_range_with_box + <GeometryOut>(box, linestring, out, lb_strategy); + } +}; + + +template +< + typename Linestring, typename Polygon, + bool ReverseLinestring, bool ReversePolygon, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + linestring_tag, polygon_tag, linestring_tag, + false, true, false, + Linestring, Polygon, + ReverseLinestring, ReversePolygon, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > : detail::intersection::intersection_of_linestring_with_areal + < + Linestring, Polygon, + ReversePolygon, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > +{}; + + +template +< + typename Linestring, typename Ring, + bool ReverseLinestring, bool ReverseRing, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + linestring_tag, ring_tag, linestring_tag, + false, true, false, + Linestring, Ring, + ReverseLinestring, ReverseRing, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > : detail::intersection::intersection_of_linestring_with_areal + < + Linestring, Ring, + ReverseRing, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > +{}; + +template +< + typename Segment, typename Box, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + segment_tag, box_tag, linestring_tag, + false, true, false, + Segment, Box, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > +{ + static inline OutputIterator apply(Segment const& segment, + Box const& box, OutputIterator out, Strategy const& ) + { + geometry::segment_view<Segment> range(segment); + + typedef typename point_type<GeometryOut>::type point_type; + strategy::intersection::liang_barsky<Box, point_type> lb_strategy; + return detail::intersection::clip_range_with_box + <GeometryOut>(box, range, out, lb_strategy); + } +}; + +template +< + typename Tag1, typename Tag2, + bool Areal1, bool Areal2, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename PointOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert + < + Tag1, Tag2, point_tag, + Areal1, Areal2, false, + Geometry1, Geometry2, + Reverse1, Reverse2, ReverseOut, + OutputIterator, PointOut, + OverlayType, + Strategy + > +{ + static inline OutputIterator apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, OutputIterator out, Strategy const& ) + { + + typedef detail::overlay::turn_info<PointOut> turn_info; + std::vector<turn_info> turns; + + detail::get_turns::no_interrupt_policy policy; + geometry::get_turns + < + false, false, detail::overlay::assign_null_policy + >(geometry1, geometry2, turns, policy); + for (typename std::vector<turn_info>::const_iterator it + = turns.begin(); it != turns.end(); ++it) + { + *out++ = it->point; + } + + return out; + } +}; + + +template +< + typename GeometryTag1, typename GeometryTag2, typename GeometryTag3, + bool Areal1, bool Areal2, bool ArealOut, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type OverlayType, + typename Strategy +> +struct intersection_insert_reversed +{ + static inline OutputIterator apply(Geometry1 const& g1, + Geometry2 const& g2, OutputIterator out, + Strategy const& strategy) + { + return intersection_insert + < + GeometryTag2, GeometryTag1, GeometryTag3, + Areal2, Areal1, ArealOut, + Geometry2, Geometry1, + Reverse2, Reverse1, ReverseOut, + OutputIterator, GeometryOut, + OverlayType, + Strategy + >::apply(g2, g1, out, strategy); + } +}; + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersection +{ + + +template +< + typename GeometryOut, + bool ReverseSecond, + overlay_type OverlayType, + typename Geometry1, typename Geometry2, + typename OutputIterator, + typename Strategy +> +inline OutputIterator insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, + OutputIterator out, + Strategy const& strategy) +{ + return boost::mpl::if_c + < + geometry::reverse_dispatch<Geometry1, Geometry2>::type::value, + geometry::dispatch::intersection_insert_reversed + < + typename geometry::tag<Geometry1>::type, + typename geometry::tag<Geometry2>::type, + typename geometry::tag<GeometryOut>::type, + geometry::is_areal<Geometry1>::value, + geometry::is_areal<Geometry2>::value, + geometry::is_areal<GeometryOut>::value, + Geometry1, Geometry2, + overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, + overlay::do_reverse<geometry::point_order<Geometry2>::value, ReverseSecond>::value, + overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value, + OutputIterator, GeometryOut, + OverlayType, + Strategy + >, + geometry::dispatch::intersection_insert + < + typename geometry::tag<Geometry1>::type, + typename geometry::tag<Geometry2>::type, + typename geometry::tag<GeometryOut>::type, + geometry::is_areal<Geometry1>::value, + geometry::is_areal<Geometry2>::value, + geometry::is_areal<GeometryOut>::value, + Geometry1, Geometry2, + geometry::detail::overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, + geometry::detail::overlay::do_reverse<geometry::point_order<Geometry2>::value, ReverseSecond>::value, + geometry::detail::overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value, + OutputIterator, GeometryOut, + OverlayType, + Strategy + > + >::type::apply(geometry1, geometry2, out, strategy); +} + + +/*! +\brief \brief_calc2{intersection} \brief_strategy +\ingroup intersection +\details \details_calc2{intersection_insert, spatial set theoretic intersection} + \brief_strategy. \details_insert{intersection} +\tparam GeometryOut \tparam_geometry{\p_l_or_c} +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam OutputIterator \tparam_out{\p_l_or_c} +\tparam Strategy \tparam_strategy_overlay +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param out \param_out{intersection} +\param strategy \param_strategy{intersection} +\return \return_out + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/intersection.qbk]} +*/ +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename OutputIterator, + typename Strategy +> +inline OutputIterator intersection_insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, + OutputIterator out, + Strategy const& strategy) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + return detail::intersection::insert + < + GeometryOut, false, overlay_intersection + >(geometry1, geometry2, out, strategy); +} + + +/*! +\brief \brief_calc2{intersection} +\ingroup intersection +\details \details_calc2{intersection_insert, spatial set theoretic intersection}. + \details_insert{intersection} +\tparam GeometryOut \tparam_geometry{\p_l_or_c} +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam OutputIterator \tparam_out{\p_l_or_c} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param out \param_out{intersection} +\return \return_out + +\qbk{[include reference/algorithms/intersection.qbk]} +*/ +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename OutputIterator +> +inline OutputIterator intersection_insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, + OutputIterator out) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + typedef strategy_intersection + < + typename cs_tag<GeometryOut>::type, + Geometry1, + Geometry2, + typename geometry::point_type<GeometryOut>::type + > strategy; + + return intersection_insert<GeometryOut>(geometry1, geometry2, out, + strategy()); +} + +}} // namespace detail::intersection +#endif // DOXYGEN_NO_DETAIL + + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_INTERSECTION_INSERT_HPP diff --git a/boost/geometry/algorithms/detail/overlay/overlay.hpp b/boost/geometry/algorithms/detail/overlay/overlay.hpp new file mode 100644 index 0000000000..ab5b6d123d --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -0,0 +1,301 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_HPP + + +#include <deque> +#include <map> + +#include <boost/range.hpp> +#include <boost/mpl/assert.hpp> + + +#include <boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp> +#include <boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp> +#include <boost/geometry/algorithms/detail/overlay/enrichment_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> +#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> +#include <boost/geometry/algorithms/detail/overlay/traverse.hpp> +#include <boost/geometry/algorithms/detail/overlay/traversal_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> + + +#include <boost/geometry/algorithms/num_points.hpp> +#include <boost/geometry/algorithms/reverse.hpp> + +#include <boost/geometry/algorithms/detail/overlay/add_rings.hpp> +#include <boost/geometry/algorithms/detail/overlay/assign_parents.hpp> +#include <boost/geometry/algorithms/detail/overlay/ring_properties.hpp> +#include <boost/geometry/algorithms/detail/overlay/select_rings.hpp> + + +#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE +# include <boost/geometry/io/dsv/write.hpp> +#endif + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +// Skip for assemble process +template <typename TurnInfo> +inline bool skip(TurnInfo const& turn_info) +{ + return (turn_info.discarded || turn_info.both(operation_union)) + && ! turn_info.any_blocked() + && ! turn_info.both(operation_intersection) + ; +} + + +template <typename TurnPoints, typename Map> +inline void map_turns(Map& map, TurnPoints const& turn_points) +{ + typedef typename boost::range_value<TurnPoints>::type turn_point_type; + typedef typename turn_point_type::container_type container_type; + + int index = 0; + for (typename boost::range_iterator<TurnPoints const>::type + it = boost::begin(turn_points); + it != boost::end(turn_points); + ++it, ++index) + { + if (! skip(*it)) + { + int op_index = 0; + for (typename boost::range_iterator<container_type const>::type + op_it = boost::begin(it->operations); + op_it != boost::end(it->operations); + ++op_it, ++op_index) + { + ring_identifier ring_id + ( + op_it->seg_id.source_index, + op_it->seg_id.multi_index, + op_it->seg_id.ring_index + ); + map[ring_id]++; + } + } + } +} + + +template +< + typename GeometryOut, overlay_type Direction, bool ReverseOut, + typename Geometry1, typename Geometry2, + typename OutputIterator +> +inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, + Geometry2 const& geometry2, + OutputIterator out) +{ + typedef std::deque + < + typename geometry::ring_type<GeometryOut>::type + > ring_container_type; + + typedef ring_properties<typename geometry::point_type<Geometry1>::type> properties; + + // Union: return either of them + // Intersection: return nothing + // Difference: return first of them + if (Direction == overlay_intersection + || (Direction == overlay_difference + && geometry::num_points(geometry1) == 0)) + { + return out; + } + + std::map<ring_identifier, int> empty; + std::map<ring_identifier, properties> all_of_one_of_them; + + select_rings<Direction>(geometry1, geometry2, empty, all_of_one_of_them, false); + ring_container_type rings; + assign_parents(geometry1, geometry2, rings, all_of_one_of_them); + return add_rings<GeometryOut>(all_of_one_of_them, geometry1, geometry2, rings, out); +} + + +template +< + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + overlay_type Direction, + typename Strategy +> +struct overlay +{ + static inline OutputIterator apply( + Geometry1 const& geometry1, Geometry2 const& geometry2, + OutputIterator out, + Strategy const& ) + { + if (geometry::num_points(geometry1) == 0 + && geometry::num_points(geometry2) == 0) + { + return out; + } + + if (geometry::num_points(geometry1) == 0 + || geometry::num_points(geometry2) == 0) + { + return return_if_one_input_is_empty + < + GeometryOut, Direction, ReverseOut + >(geometry1, geometry2, out); + } + + typedef typename geometry::point_type<GeometryOut>::type point_type; + typedef detail::overlay::traversal_turn_info<point_type> turn_info; + typedef std::deque<turn_info> container_type; + + typedef std::deque + < + typename geometry::ring_type<GeometryOut>::type + > ring_container_type; + + container_type turn_points; + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + boost::timer timer; +#endif + +#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE +std::cout << "get turns" << std::endl; +#endif + detail::get_turns::no_interrupt_policy policy; + geometry::get_turns + < + Reverse1, Reverse2, + detail::overlay::calculate_distance_policy + >(geometry1, geometry2, turn_points, policy); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "get_turns: " << timer.elapsed() << std::endl; +#endif + +#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE +std::cout << "enrich" << std::endl; +#endif + typename Strategy::side_strategy_type side_strategy; + geometry::enrich_intersection_points<Reverse1, Reverse2>(turn_points, + Direction == overlay_union + ? geometry::detail::overlay::operation_union + : geometry::detail::overlay::operation_intersection, + geometry1, geometry2, + side_strategy); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "enrich_intersection_points: " << timer.elapsed() << std::endl; +#endif + + +#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE +std::cout << "traverse" << std::endl; +#endif + // Traverse through intersection/turn points and create rings of them. + // Note that these rings are always in clockwise order, even in CCW polygons, + // and are marked as "to be reversed" below + ring_container_type rings; + traverse<Reverse1, Reverse2, Geometry1, Geometry2>::apply + ( + geometry1, geometry2, + Direction == overlay_union + ? geometry::detail::overlay::operation_union + : geometry::detail::overlay::operation_intersection, + turn_points, rings + ); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "traverse: " << timer.elapsed() << std::endl; +#endif + + + std::map<ring_identifier, int> map; + map_turns(map, turn_points); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "map_turns: " << timer.elapsed() << std::endl; +#endif + + typedef ring_properties<typename geometry::point_type<Geometry1>::type> properties; + + std::map<ring_identifier, properties> selected; + select_rings<Direction>(geometry1, geometry2, map, selected, ! turn_points.empty()); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "select_rings: " << timer.elapsed() << std::endl; +#endif + + + // Add rings created during traversal + { + ring_identifier id(2, 0, -1); + for (typename boost::range_iterator<ring_container_type>::type + it = boost::begin(rings); + it != boost::end(rings); + ++it) + { + selected[id] = properties(*it, true); + selected[id].reversed = ReverseOut; + id.multi_index++; + } + } + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "add traversal rings: " << timer.elapsed() << std::endl; +#endif + + + assign_parents(geometry1, geometry2, rings, selected); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "assign_parents: " << timer.elapsed() << std::endl; +#endif + + return add_rings<GeometryOut>(selected, geometry1, geometry2, rings, out); + } +}; + + +// Metafunction helper for intersection and union +template <order_selector Selector, bool Reverse = false> +struct do_reverse {}; + +template <> +struct do_reverse<clockwise, false> : boost::false_type {}; + +template <> +struct do_reverse<clockwise, true> : boost::true_type {}; + +template <> +struct do_reverse<counterclockwise, false> : boost::true_type {}; + +template <> +struct do_reverse<counterclockwise, true> : boost::false_type {}; + + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_HPP diff --git a/boost/geometry/algorithms/detail/overlay/overlay_type.hpp b/boost/geometry/algorithms/detail/overlay/overlay_type.hpp new file mode 100644 index 0000000000..af62131f0e --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/overlay_type.hpp @@ -0,0 +1,29 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_TYPE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_TYPE_HPP + + + +namespace boost { namespace geometry +{ + +enum overlay_type +{ + overlay_union, + overlay_intersection, + overlay_difference, + overlay_dissolve +}; + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_TYPE_HPP diff --git a/boost/geometry/algorithms/detail/overlay/ring_properties.hpp b/boost/geometry/algorithms/detail/overlay/ring_properties.hpp new file mode 100644 index 0000000000..a6088694da --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/ring_properties.hpp @@ -0,0 +1,78 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RING_PROPERTIES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RING_PROPERTIES_HPP + + +#include <boost/geometry/algorithms/area.hpp> +#include <boost/geometry/algorithms/within.hpp> +#include <boost/geometry/algorithms/detail/point_on_border.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template <typename Point> +struct ring_properties +{ + typedef Point point_type; + typedef typename default_area_result<Point>::type area_type; + + // Filled by "select_rings" + Point point; + area_type area; + + // Filled by "update_selection_map" + int within_code; + bool reversed; + + // Filled/used by "assign_rings" + bool discarded; + ring_identifier parent; + area_type parent_area; + std::vector<ring_identifier> children; + + inline ring_properties() + : area(area_type()) + , within_code(-1) + , reversed(false) + , discarded(false) + , parent_area(-1) + {} + + template <typename RingOrBox> + inline ring_properties(RingOrBox const& ring_or_box, bool midpoint) + : within_code(-1) + , reversed(false) + , discarded(false) + , parent_area(-1) + { + this->area = geometry::area(ring_or_box); + geometry::point_on_border(this->point, ring_or_box, midpoint); + } + + inline area_type get_area() const + { + return reversed ? -area : area; + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_RING_PROPERTIES_HPP diff --git a/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp b/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp new file mode 100644 index 0000000000..007113ffba --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp @@ -0,0 +1,91 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SEGMENT_IDENTIFIER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SEGMENT_IDENTIFIER_HPP + + +#if defined(BOOST_GEOMETRY_DEBUG_OVERLAY) +# define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER +#endif + + +#include <vector> + + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> + + + +namespace boost { namespace geometry +{ + + +// Internal struct to uniquely identify a segment +// on a linestring,ring +// or polygon (needs ring_index) +// or multi-geometry (needs multi_index) +struct segment_identifier +{ + inline segment_identifier() + : source_index(-1) + , multi_index(-1) + , ring_index(-1) + , segment_index(-1) + {} + + inline segment_identifier(int src, int mul, int rin, int seg) + : source_index(src) + , multi_index(mul) + , ring_index(rin) + , segment_index(seg) + {} + + inline bool operator<(segment_identifier const& other) const + { + return source_index != other.source_index ? source_index < other.source_index + : multi_index !=other.multi_index ? multi_index < other.multi_index + : ring_index != other.ring_index ? ring_index < other.ring_index + : segment_index < other.segment_index + ; + } + + inline bool operator==(segment_identifier const& other) const + { + return source_index == other.source_index + && segment_index == other.segment_index + && ring_index == other.ring_index + && multi_index == other.multi_index + ; + } + +#if defined(BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER) + friend std::ostream& operator<<(std::ostream &os, segment_identifier const& seg_id) + { + std::cout + << "s:" << seg_id.source_index + << ", v:" << seg_id.segment_index // ~vertex + ; + if (seg_id.ring_index >= 0) std::cout << ", r:" << seg_id.ring_index; + if (seg_id.multi_index >= 0) std::cout << ", m:" << seg_id.multi_index; + return os; + } +#endif + + int source_index; + int multi_index; + int ring_index; + int segment_index; +}; + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SEGMENT_IDENTIFIER_HPP diff --git a/boost/geometry/algorithms/detail/overlay/select_rings.hpp b/boost/geometry/algorithms/detail/overlay/select_rings.hpp new file mode 100644 index 0000000000..f664b19514 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/select_rings.hpp @@ -0,0 +1,295 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP + +#include <map> + + +#include <boost/geometry/algorithms/area.hpp> +#include <boost/geometry/algorithms/within.hpp> +#include <boost/geometry/algorithms/detail/point_on_border.hpp> +#include <boost/geometry/algorithms/detail/ring_identifier.hpp> +#include <boost/geometry/algorithms/detail/overlay/ring_properties.hpp> +#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +namespace dispatch +{ + + template <typename Tag, typename Geometry> + struct select_rings + {}; + + template <typename Box> + struct select_rings<box_tag, Box> + { + template <typename Geometry, typename Map> + static inline void apply(Box const& box, Geometry const& , + ring_identifier const& id, Map& map, bool midpoint) + { + map[id] = typename Map::mapped_type(box, midpoint); + } + + template <typename Map> + static inline void apply(Box const& box, + ring_identifier const& id, Map& map, bool midpoint) + { + map[id] = typename Map::mapped_type(box, midpoint); + } + }; + + template <typename Ring> + struct select_rings<ring_tag, Ring> + { + template <typename Geometry, typename Map> + static inline void apply(Ring const& ring, Geometry const& , + ring_identifier const& id, Map& map, bool midpoint) + { + if (boost::size(ring) > 0) + { + map[id] = typename Map::mapped_type(ring, midpoint); + } + } + + template <typename Map> + static inline void apply(Ring const& ring, + ring_identifier const& id, Map& map, bool midpoint) + { + if (boost::size(ring) > 0) + { + map[id] = typename Map::mapped_type(ring, midpoint); + } + } + }; + + + template <typename Polygon> + struct select_rings<polygon_tag, Polygon> + { + template <typename Geometry, typename Map> + static inline void apply(Polygon const& polygon, Geometry const& geometry, + ring_identifier id, Map& map, bool midpoint) + { + typedef typename geometry::ring_type<Polygon>::type ring_type; + typedef select_rings<ring_tag, ring_type> per_ring; + + per_ring::apply(exterior_ring(polygon), geometry, id, map, midpoint); + + typename interior_return_type<Polygon const>::type rings + = interior_rings(polygon); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + id.ring_index++; + per_ring::apply(*it, geometry, id, map, midpoint); + } + } + + template <typename Map> + static inline void apply(Polygon const& polygon, + ring_identifier id, Map& map, bool midpoint) + { + typedef typename geometry::ring_type<Polygon>::type ring_type; + typedef select_rings<ring_tag, ring_type> per_ring; + + per_ring::apply(exterior_ring(polygon), id, map, midpoint); + + typename interior_return_type<Polygon const>::type rings + = interior_rings(polygon); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + id.ring_index++; + per_ring::apply(*it, id, map, midpoint); + } + } + }; +} + + +template<overlay_type OverlayType> +struct decide +{}; + +template<> +struct decide<overlay_union> +{ + template <typename Code> + static bool include(ring_identifier const& , Code const& code) + { + return code.within_code * -1 == 1; + } + + template <typename Code> + static bool reversed(ring_identifier const& , Code const& ) + { + return false; + } +}; + +template<> +struct decide<overlay_difference> +{ + template <typename Code> + static bool include(ring_identifier const& id, Code const& code) + { + bool is_first = id.source_index == 0; + return code.within_code * -1 * (is_first ? 1 : -1) == 1; + } + + template <typename Code> + static bool reversed(ring_identifier const& id, Code const& code) + { + return include(id, code) && id.source_index == 1; + } +}; + +template<> +struct decide<overlay_intersection> +{ + template <typename Code> + static bool include(ring_identifier const& , Code const& code) + { + return code.within_code * 1 == 1; + } + + template <typename Code> + static bool reversed(ring_identifier const& , Code const& ) + { + return false; + } +}; + + +template +< + overlay_type OverlayType, + typename Geometry1, typename Geometry2, + typename IntersectionMap, typename SelectionMap +> +inline void update_selection_map(Geometry1 const& geometry1, + Geometry2 const& geometry2, + IntersectionMap const& intersection_map, + SelectionMap const& map_with_all, SelectionMap& selection_map) +{ + selection_map.clear(); + + for (typename SelectionMap::const_iterator it = boost::begin(map_with_all); + it != boost::end(map_with_all); + ++it) + { + /* + int union_code = it->second.within_code * -1; + bool is_first = it->first.source_index == 0; + std::cout << it->first << " " << it->second.area + << ": " << it->second.within_code + << " union: " << union_code + << " intersection: " << (it->second.within_code * 1) + << " G1-G2: " << (union_code * (is_first ? 1 : -1)) + << " G2-G1: " << (union_code * (is_first ? -1 : 1)) + << " -> " << (decide<OverlayType>::include(it->first, it->second) ? "INC" : "") + << decide<OverlayType>::reverse(it->first, it->second) + << std::endl; + */ + + bool found = intersection_map.find(it->first) != intersection_map.end(); + if (! found) + { + ring_identifier const id = it->first; + typename SelectionMap::mapped_type properties = it->second; // Copy by value + + // Calculate the "within code" (previously this was done earlier but is + // must efficienter here - it can be even more efficient doing it all at once, + // using partition, TODO) + // So though this is less elegant than before, it avoids many unused point-in-poly calculations + switch(id.source_index) + { + case 0 : + properties.within_code + = geometry::within(properties.point, geometry2) ? 1 : -1; + break; + case 1 : + properties.within_code + = geometry::within(properties.point, geometry1) ? 1 : -1; + break; + } + + if (decide<OverlayType>::include(id, properties)) + { + properties.reversed = decide<OverlayType>::reversed(id, properties); + selection_map[id] = properties; + } + } + } +} + + +/*! +\brief The function select_rings select rings based on the overlay-type (union,intersection) +*/ +template +< + overlay_type OverlayType, + typename Geometry1, typename Geometry2, + typename IntersectionMap, typename SelectionMap +> +inline void select_rings(Geometry1 const& geometry1, Geometry2 const& geometry2, + IntersectionMap const& intersection_map, + SelectionMap& selection_map, bool midpoint) +{ + typedef typename geometry::tag<Geometry1>::type tag1; + typedef typename geometry::tag<Geometry2>::type tag2; + + SelectionMap map_with_all; + dispatch::select_rings<tag1, Geometry1>::apply(geometry1, geometry2, + ring_identifier(0, -1, -1), map_with_all, midpoint); + dispatch::select_rings<tag2, Geometry2>::apply(geometry2, geometry1, + ring_identifier(1, -1, -1), map_with_all, midpoint); + + update_selection_map<OverlayType>(geometry1, geometry2, intersection_map, + map_with_all, selection_map); +} + +template +< + overlay_type OverlayType, + typename Geometry, + typename IntersectionMap, typename SelectionMap +> +inline void select_rings(Geometry const& geometry, + IntersectionMap const& intersection_map, + SelectionMap& selection_map, bool midpoint) +{ + typedef typename geometry::tag<Geometry>::type tag; + + SelectionMap map_with_all; + dispatch::select_rings<tag, Geometry>::apply(geometry, + ring_identifier(0, -1, -1), map_with_all, midpoint); + + update_selection_map<OverlayType>(geometry, geometry, intersection_map, + map_with_all, selection_map); +} + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELECT_RINGS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp new file mode 100644 index 0000000000..9c4c99394e --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -0,0 +1,308 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP + +#include <cstddef> + +#include <boost/range.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/detail/disjoint.hpp> +#include <boost/geometry/algorithms/detail/partition.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> + +#include <boost/geometry/geometries/box.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace self_get_turn_points +{ + +struct no_interrupt_policy +{ + static bool const enabled = false; + static bool const has_intersections = false; + + + template <typename Range> + static inline bool apply(Range const&) + { + return false; + } +}; + + + + +class self_ip_exception : public geometry::exception {}; + +template +< + typename Geometry, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct self_section_visitor +{ + Geometry const& m_geometry; + Turns& m_turns; + InterruptPolicy& m_interrupt_policy; + + inline self_section_visitor(Geometry const& g, + Turns& turns, InterruptPolicy& ip) + : m_geometry(g) + , m_turns(turns) + , m_interrupt_policy(ip) + {} + + template <typename Section> + inline bool apply(Section const& sec1, Section const& sec2) + { + if (! detail::disjoint::disjoint_box_box(sec1.bounding_box, sec2.bounding_box) + && ! sec1.duplicate + && ! sec2.duplicate) + { + detail::get_turns::get_turns_in_sections + < + Geometry, Geometry, + false, false, + Section, Section, + Turns, TurnPolicy, + InterruptPolicy + >::apply( + 0, m_geometry, sec1, + 0, m_geometry, sec2, + false, + m_turns, m_interrupt_policy); + } + if (m_interrupt_policy.has_intersections) + { + // TODO: we should give partition an interrupt policy. + // Now we throw, and catch below, to stop the partition loop. + throw self_ip_exception(); + } + return true; + } + +}; + + + +template +< + typename Geometry, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct get_turns +{ + static inline bool apply( + Geometry const& geometry, + Turns& turns, + InterruptPolicy& interrupt_policy) + { + typedef model::box + < + typename geometry::point_type<Geometry>::type + > box_type; + typedef typename geometry::sections + < + box_type, 1 + > sections_type; + + sections_type sec; + geometry::sectionalize<false>(geometry, sec); + + self_section_visitor + < + Geometry, + Turns, TurnPolicy, InterruptPolicy + > visitor(geometry, turns, interrupt_policy); + + try + { + geometry::partition + < + box_type, + detail::get_turns::get_section_box, + detail::get_turns::ovelaps_section_box + >::apply(sec, visitor); + } + catch(self_ip_exception const& ) + { + return false; + } + + return true; + } +}; + + +}} // namespace detail::self_get_turn_points +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename GeometryTag, + typename Geometry, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct self_get_turn_points +{ +}; + + +template +< + typename Ring, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct self_get_turn_points + < + ring_tag, Ring, + Turns, + TurnPolicy, + InterruptPolicy + > + : detail::self_get_turn_points::get_turns + < + Ring, + Turns, + TurnPolicy, + InterruptPolicy + > +{}; + + +template +< + typename Box, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct self_get_turn_points + < + box_tag, Box, + Turns, + TurnPolicy, + InterruptPolicy + > +{ + static inline bool apply( + Box const& , + Turns& , + InterruptPolicy& ) + { + return true; + } +}; + + +template +< + typename Polygon, + typename Turns, + typename TurnPolicy, + typename InterruptPolicy +> +struct self_get_turn_points + < + polygon_tag, Polygon, + Turns, + TurnPolicy, + InterruptPolicy + > + : detail::self_get_turn_points::get_turns + < + Polygon, + Turns, + TurnPolicy, + InterruptPolicy + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! + \brief Calculate self intersections of a geometry + \ingroup overlay + \tparam Geometry geometry type + \tparam Turns type of intersection container + (e.g. vector of "intersection/turn point"'s) + \param geometry geometry + \param turns container which will contain intersection points + \param interrupt_policy policy determining if process is stopped + when intersection is found + */ +template +< + typename AssignPolicy, + typename Geometry, + typename Turns, + typename InterruptPolicy +> +inline void self_turns(Geometry const& geometry, + Turns& turns, InterruptPolicy& interrupt_policy) +{ + concept::check<Geometry const>(); + + typedef typename strategy_intersection + < + typename cs_tag<Geometry>::type, + Geometry, + Geometry, + typename boost::range_value<Turns>::type + >::segment_intersection_strategy_type strategy_type; + + typedef detail::overlay::get_turn_info + < + typename point_type<Geometry>::type, + typename point_type<Geometry>::type, + typename boost::range_value<Turns>::type, + detail::overlay::assign_null_policy + > TurnPolicy; + + dispatch::self_get_turn_points + < + typename tag<Geometry>::type, + Geometry, + Turns, + TurnPolicy, + InterruptPolicy + >::apply(geometry, turns, interrupt_policy); +} + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_SELF_TURN_POINTS_HPP diff --git a/boost/geometry/algorithms/detail/overlay/stream_info.hpp b/boost/geometry/algorithms/detail/overlay/stream_info.hpp new file mode 100644 index 0000000000..eebe381944 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/stream_info.hpp @@ -0,0 +1,75 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_STREAM_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_STREAM_INFO_HPP + + +#include <string> + +#include <boost/array.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + + static inline std::string dir(int d) + { + return d == 0 ? "-" : (d == 1 ? "L" : d == -1 ? "R" : "#"); + } + static inline std::string how_str(int h) + { + return h == 0 ? "-" : (h == 1 ? "A" : "D"); + } + + template <typename P> + std::ostream& operator<<(std::ostream &os, turn_info<P> const& info) + { + typename geometry::coordinate_type<P>::type d = info.distance; + os << "\t" + << " src " << info.seg_id.source_index + << " seg " << info.seg_id.segment_index + << " (// " << info.other_id.source_index + << "." << info.other_id.segment_index << ")" + << " how " << info.how + << "[" << how_str(info.arrival) + << " " << dir(info.direction) + << (info.opposite ? " o" : "") + << "]" + << " sd " + << dir(info.sides.get<0,0>()) + << dir(info.sides.get<0,1>()) + << dir(info.sides.get<1,0>()) + << dir(info.sides.get<1,1>()) + << " nxt seg " << info.travels_to_vertex_index + << " , ip " << info.travels_to_ip_index + << " , or " << info.next_ip_index + << " dst " << double(d) + << info.visit_state; + if (info.flagged) + { + os << " FLAGGED"; + } + return os; + } + + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_STREAM_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/traversal_info.hpp b/boost/geometry/algorithms/detail/overlay/traversal_info.hpp new file mode 100644 index 0000000000..810a27af04 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/traversal_info.hpp @@ -0,0 +1,47 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INFO_HPP + + +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/enrichment_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/visit_info.hpp> +#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template <typename P> +struct traversal_turn_operation : public turn_operation +{ + enrichment_info<P> enriched; + visit_info visited; +}; + +template <typename P> +struct traversal_turn_info : public turn_info<P, traversal_turn_operation<P> > +{}; + + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/traverse.hpp b/boost/geometry/algorithms/detail/overlay/traverse.hpp new file mode 100644 index 0000000000..12daafa0cf --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/traverse.hpp @@ -0,0 +1,395 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP + +#include <cstddef> + +#include <boost/range.hpp> + +#include <boost/geometry/algorithms/detail/overlay/append_no_duplicates.hpp> +#include <boost/geometry/algorithms/detail/overlay/backtrack_check_si.hpp> +#include <boost/geometry/algorithms/detail/overlay/copy_segments.hpp> +#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> + +#if defined(BOOST_GEOMETRY_DEBUG_INTERSECTION) \ + || defined(BOOST_GEOMETRY_OVERLAY_REPORT_WKT) \ + || defined(BOOST_GEOMETRY_DEBUG_TRAVERSE) +# include <string> +# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> +# include <boost/geometry/io/wkt/wkt.hpp> +#endif + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template <typename Turn, typename Operation> +#ifdef BOOST_GEOMETRY_DEBUG_TRAVERSE +inline void debug_traverse(Turn const& turn, Operation op, + std::string const& header) +{ + std::cout << header + << " at " << op.seg_id + << " meth: " << method_char(turn.method) + << " op: " << operation_char(op.operation) + << " vis: " << visited_char(op.visited) + << " of: " << operation_char(turn.operations[0].operation) + << operation_char(turn.operations[1].operation) + << " " << geometry::wkt(turn.point) + << std::endl; + + if (boost::contains(header, "Finished")) + { + std::cout << std::endl; + } +} +#else +inline void debug_traverse(Turn const& , Operation, std::string const& ) +{ +} +#endif + + +template <typename Info, typename Turn> +inline void set_visited_for_continue(Info& info, Turn const& turn) +{ + // On "continue", set "visited" for ALL directions + if (turn.operation == detail::overlay::operation_continue) + { + for (typename boost::range_iterator + < + typename Info::container_type + >::type it = boost::begin(info.operations); + it != boost::end(info.operations); + ++it) + { + if (it->visited.none()) + { + it->visited.set_visited(); + } + } + } +} + + +template +< + bool Reverse1, bool Reverse2, + typename GeometryOut, + typename G1, + typename G2, + typename Turns, + typename IntersectionInfo +> +inline bool assign_next_ip(G1 const& g1, G2 const& g2, + Turns& turns, + typename boost::range_iterator<Turns>::type& ip, + GeometryOut& current_output, + IntersectionInfo& info, + segment_identifier& seg_id) +{ + info.visited.set_visited(); + set_visited_for_continue(*ip, info); + + // If there is no next IP on this segment + if (info.enriched.next_ip_index < 0) + { + if (info.enriched.travels_to_vertex_index < 0 + || info.enriched.travels_to_ip_index < 0) + { + return false; + } + + BOOST_ASSERT(info.enriched.travels_to_vertex_index >= 0); + BOOST_ASSERT(info.enriched.travels_to_ip_index >= 0); + + if (info.seg_id.source_index == 0) + { + geometry::copy_segments<Reverse1>(g1, info.seg_id, + info.enriched.travels_to_vertex_index, + current_output); + } + else + { + geometry::copy_segments<Reverse2>(g2, info.seg_id, + info.enriched.travels_to_vertex_index, + current_output); + } + seg_id = info.seg_id; + ip = boost::begin(turns) + info.enriched.travels_to_ip_index; + } + else + { + ip = boost::begin(turns) + info.enriched.next_ip_index; + seg_id = info.seg_id; + } + + detail::overlay::append_no_duplicates(current_output, ip->point); + return true; +} + + +inline bool select_source(operation_type operation, int source1, int source2) +{ + return (operation == operation_intersection && source1 != source2) + || (operation == operation_union && source1 == source2) + ; +} + + +template +< + typename Turn, + typename Iterator +> +inline bool select_next_ip(operation_type operation, + Turn& turn, + segment_identifier const& seg_id, + Iterator& selected) +{ + if (turn.discarded) + { + return false; + } + bool has_tp = false; + selected = boost::end(turn.operations); + for (Iterator it = boost::begin(turn.operations); + it != boost::end(turn.operations); + ++it) + { + if (it->visited.started()) + { + selected = it; + //std::cout << " RETURN"; + return true; + } + + // In some cases there are two alternatives. + // For "ii", take the other one (alternate) + // UNLESS the other one is already visited + // For "uu", take the same one (see above); + // For "cc", take either one, but if there is a starting one, + // take that one. + if ( (it->operation == operation_continue + && (! has_tp || it->visited.started() + ) + ) + || (it->operation == operation + && ! it->visited.finished() + && (! has_tp + || select_source(operation, + it->seg_id.source_index, seg_id.source_index) + ) + ) + ) + { + selected = it; + debug_traverse(turn, *it, " Candidate"); + has_tp = true; + } + } + + if (has_tp) + { + debug_traverse(turn, *selected, " Accepted"); + } + + + return has_tp; +} + + + +/*! + \brief Traverses through intersection points / geometries + \ingroup overlay + */ +template +< + bool Reverse1, bool Reverse2, + typename Geometry1, + typename Geometry2, + typename Backtrack = backtrack_check_self_intersections<Geometry1, Geometry2> +> +class traverse +{ +public : + template <typename Turns, typename Rings> + static inline void apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + detail::overlay::operation_type operation, + Turns& turns, Rings& rings) + { + typedef typename boost::range_iterator<Turns>::type turn_iterator; + typedef typename boost::range_value<Turns>::type turn_type; + typedef typename boost::range_iterator + < + typename turn_type::container_type + >::type turn_operation_iterator_type; + + std::size_t size_at_start = boost::size(rings); + + typename Backtrack::state_type state; + do + { + state.reset(); + + // Iterate through all unvisited points + for (turn_iterator it = boost::begin(turns); + state.good() && it != boost::end(turns); + ++it) + { + // Skip discarded ones + if (! (it->is_discarded() || it->blocked())) + { + for (turn_operation_iterator_type iit = boost::begin(it->operations); + state.good() && iit != boost::end(it->operations); + ++iit) + { + if (iit->visited.none() + && ! iit->visited.rejected() + && (iit->operation == operation + || iit->operation == detail::overlay::operation_continue) + ) + { + set_visited_for_continue(*it, *iit); + + typename boost::range_value<Rings>::type current_output; + detail::overlay::append_no_duplicates(current_output, + it->point, true); + + turn_iterator current = it; + turn_operation_iterator_type current_iit = iit; + segment_identifier current_seg_id; + + if (! detail::overlay::assign_next_ip<Reverse1, Reverse2>( + geometry1, geometry2, + turns, + current, current_output, + *iit, current_seg_id)) + { + Backtrack::apply( + size_at_start, + rings, current_output, turns, *current_iit, + "No next IP", + geometry1, geometry2, state); + } + + if (! detail::overlay::select_next_ip( + operation, + *current, + current_seg_id, + current_iit)) + { + Backtrack::apply( + size_at_start, + rings, current_output, turns, *iit, + "Dead end at start", + geometry1, geometry2, state); + } + else + { + + iit->visited.set_started(); + detail::overlay::debug_traverse(*it, *iit, "-> Started"); + detail::overlay::debug_traverse(*current, *current_iit, "Selected "); + + + unsigned int i = 0; + + while (current_iit != iit && state.good()) + { + if (current_iit->visited.visited()) + { + // It visits a visited node again, without passing the start node. + // This makes it suspicious for endless loops + Backtrack::apply( + size_at_start, + rings, current_output, turns, *iit, + "Visit again", + geometry1, geometry2, state); + } + else + { + + + // We assume clockwise polygons only, non self-intersecting, closed. + // However, the input might be different, and checking validity + // is up to the library user. + + // Therefore we make here some sanity checks. If the input + // violates the assumptions, the output polygon will not be correct + // but the routine will stop and output the current polygon, and + // will continue with the next one. + + // Below three reasons to stop. + detail::overlay::assign_next_ip<Reverse1, Reverse2>( + geometry1, geometry2, + turns, current, current_output, + *current_iit, current_seg_id); + + if (! detail::overlay::select_next_ip( + operation, + *current, + current_seg_id, + current_iit)) + { + // Should not occur in valid (non-self-intersecting) polygons + // Should not occur in self-intersecting polygons without spikes + // Might occur in polygons with spikes + Backtrack::apply( + size_at_start, + rings, current_output, turns, *iit, + "Dead end", + geometry1, geometry2, state); + } + detail::overlay::debug_traverse(*current, *current_iit, "Selected "); + + if (i++ > 2 + 2 * turns.size()) + { + // Sanity check: there may be never more loops + // than turn points. + // Turn points marked as "ii" can be visited twice. + Backtrack::apply( + size_at_start, + rings, current_output, turns, *iit, + "Endless loop", + geometry1, geometry2, state); + } + } + } + + if (state.good()) + { + iit->visited.set_finished(); + detail::overlay::debug_traverse(*current, *iit, "->Finished"); + rings.push_back(current_output); + } + } + } + } + } + } + } while (! state.good()); + } +}; + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP diff --git a/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/boost/geometry/algorithms/detail/overlay/turn_info.hpp new file mode 100644 index 0000000000..aa6b428f19 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -0,0 +1,142 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TURN_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TURN_INFO_HPP + + +#include <boost/array.hpp> + +#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp> + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +enum operation_type +{ + operation_none, + operation_union, + operation_intersection, + operation_blocked, + operation_continue +}; + + +enum method_type +{ + method_none, + method_disjoint, + method_crosses, + method_touch, + method_touch_interior, + method_collinear, + method_equal, + method_error +}; + + +/*! + \brief Turn operation: operation + \details Information necessary for traversal phase (a phase + of the overlay process). The information is gathered during the + get_turns (segment intersection) phase. + The class is to be included in the turn_info class, either direct + or a derived or similar class with more (e.g. enrichment) information. + */ +struct turn_operation +{ + operation_type operation; + segment_identifier seg_id; + segment_identifier other_id; + + inline turn_operation() + : operation(operation_none) + {} +}; + + +/*! + \brief Turn information: intersection point, method, and turn information + \details Information necessary for traversal phase (a phase + of the overlay process). The information is gathered during the + get_turns (segment intersection) phase. + \tparam Point point type of intersection point + \tparam Operation gives classes opportunity to add additional info + \tparam Container gives classes opportunity to define how operations are stored + */ +template +< + typename Point, + typename Operation = turn_operation, + typename Container = boost::array<Operation, 2> +> +struct turn_info +{ + typedef Point point_type; + typedef Operation turn_operation_type; + typedef Container container_type; + + Point point; + method_type method; + bool discarded; + + + Container operations; + + inline turn_info() + : method(method_none) + , discarded(false) + {} + + inline bool both(operation_type type) const + { + return has12(type, type); + } + + inline bool combination(operation_type type1, operation_type type2) const + { + return has12(type1, type2) || has12(type2, type1); + } + + + inline bool is_discarded() const { return discarded; } + inline bool blocked() const + { + return both(operation_blocked); + } + inline bool any_blocked() const + { + return this->operations[0].operation == operation_blocked + || this->operations[1].operation == operation_blocked; + } + + +private : + inline bool has12(operation_type type1, operation_type type2) const + { + return this->operations[0].operation == type1 + && this->operations[1].operation == type2 + ; + } + +}; + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TURN_INFO_HPP diff --git a/boost/geometry/algorithms/detail/overlay/visit_info.hpp b/boost/geometry/algorithms/detail/overlay/visit_info.hpp new file mode 100644 index 0000000000..6be63f42b4 --- /dev/null +++ b/boost/geometry/algorithms/detail/overlay/visit_info.hpp @@ -0,0 +1,136 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_VISIT_INFO_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_VISIT_INFO_HPP + + +#ifdef BOOST_GEOMETRY_USE_MSM +# include <boost/geometry/algorithms/detail/overlay/msm_state.hpp> +#endif + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +#if ! defined(BOOST_GEOMETRY_USE_MSM) + +class visit_info +{ +private : + static const int NONE = 0; + static const int STARTED = 1; + static const int VISITED = 2; + static const int FINISHED = 3; + static const int REJECTED = 4; + + int m_visit_code; + bool m_rejected; + +public: + inline visit_info() + : m_visit_code(0) + , m_rejected(false) + {} + + inline void set_visited() { m_visit_code = VISITED; } + inline void set_started() { m_visit_code = STARTED; } + inline void set_finished() { m_visit_code = FINISHED; } + inline void set_rejected() + { + m_visit_code = REJECTED; + m_rejected = true; + } + + inline bool none() const { return m_visit_code == NONE; } + inline bool visited() const { return m_visit_code == VISITED; } + inline bool started() const { return m_visit_code == STARTED; } + inline bool finished() const { return m_visit_code == FINISHED; } + inline bool rejected() const { return m_rejected; } + + inline void clear() + { + if (! rejected()) + { + m_visit_code = NONE; + } + } + + + +#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION + friend std::ostream& operator<<(std::ostream &os, visit_info const& v) + { + if (v.m_visit_code != 0) + { + os << " VIS: " << int(v.m_visit_code); + } + return os; + } +#endif + +}; + + +#else + + +class visit_info +{ + +private : + +#ifndef USE_MSM_MINI + mutable +#endif + traverse_state state; + +public : + inline visit_info() + { + state.start(); + } + + inline void set_none() { state.process_event(none()); } // Not Yet Implemented! + inline void set_visited() { state.process_event(visit()); } + inline void set_started() { state.process_event(starting()); } + inline void set_finished() { state.process_event(finish()); } + +#ifdef USE_MSM_MINI + inline bool none() const { return state.flag_none(); } + inline bool visited() const { return state.flag_visited(); } + inline bool started() const { return state.flag_started(); } +#else + inline bool none() const { return state.is_flag_active<is_init>(); } + inline bool visited() const { return state.is_flag_active<is_visited>(); } + inline bool started() const { return state.is_flag_active<is_started>(); } +#endif + +#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION + friend std::ostream& operator<<(std::ostream &os, visit_info const& v) + { + return os; + } +#endif +}; +#endif + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_VISIT_INFO_HPP diff --git a/boost/geometry/algorithms/detail/partition.hpp b/boost/geometry/algorithms/detail/partition.hpp new file mode 100644 index 0000000000..7a7de2cdd3 --- /dev/null +++ b/boost/geometry/algorithms/detail/partition.hpp @@ -0,0 +1,425 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2011-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP + +#include <vector> +#include <boost/range/algorithm/copy.hpp> +#include <boost/geometry/algorithms/assign.hpp> +#include <boost/geometry/core/coordinate_type.hpp> + +namespace boost { namespace geometry +{ + +namespace detail { namespace partition +{ + +typedef std::vector<std::size_t> index_vector_type; + +template <int Dimension, typename Box> +inline void divide_box(Box const& box, Box& lower_box, Box& upper_box) +{ + typedef typename coordinate_type<Box>::type ctype; + + // Divide input box into two parts, e.g. left/right + ctype two = 2; + ctype mid = (geometry::get<min_corner, Dimension>(box) + + geometry::get<max_corner, Dimension>(box)) / two; + + lower_box = box; + upper_box = box; + geometry::set<max_corner, Dimension>(lower_box, mid); + geometry::set<min_corner, Dimension>(upper_box, mid); +} + +// Divide collection into three subsets: lower, upper and oversized +// (not-fitting) +// (lower == left or bottom, upper == right or top) +template <typename OverlapsPolicy, typename InputCollection, typename Box> +static inline void divide_into_subsets(Box const& lower_box, + Box const& upper_box, + InputCollection const& collection, + index_vector_type const& input, + index_vector_type& lower, + index_vector_type& upper, + index_vector_type& exceeding) +{ + typedef boost::range_iterator + < + index_vector_type const + >::type index_iterator_type; + + for(index_iterator_type it = boost::begin(input); + it != boost::end(input); + ++it) + { + bool const lower_overlapping = OverlapsPolicy::apply(lower_box, + collection[*it]); + bool const upper_overlapping = OverlapsPolicy::apply(upper_box, + collection[*it]); + + if (lower_overlapping && upper_overlapping) + { + exceeding.push_back(*it); + } + else if (lower_overlapping) + { + lower.push_back(*it); + } + else if (upper_overlapping) + { + upper.push_back(*it); + } + else + { + // Is nowhere! Should not occur! + BOOST_ASSERT(true); + } + } +} + +// Match collection with itself +template <typename InputCollection, typename Policy> +static inline void handle_one(InputCollection const& collection, + index_vector_type const& input, + Policy& policy) +{ + typedef boost::range_iterator<index_vector_type const>::type + index_iterator_type; + // Quadratic behaviour at lowest level (lowest quad, or all exceeding) + for(index_iterator_type it1 = boost::begin(input); + it1 != boost::end(input); + ++it1) + { + index_iterator_type it2 = it1; + for(++it2; it2 != boost::end(input); ++it2) + { + policy.apply(collection[*it1], collection[*it2]); + } + } +} + +// Match collection 1 with collection 2 +template <typename InputCollection, typename Policy> +static inline void handle_two( + InputCollection const& collection1, index_vector_type const& input1, + InputCollection const& collection2, index_vector_type const& input2, + Policy& policy) +{ + typedef boost::range_iterator + < + index_vector_type const + >::type index_iterator_type; + + for(index_iterator_type it1 = boost::begin(input1); + it1 != boost::end(input1); + ++it1) + { + for(index_iterator_type it2 = boost::begin(input2); + it2 != boost::end(input2); + ++it2) + { + policy.apply(collection1[*it1], collection2[*it2]); + } + } +} + +template +< + int Dimension, + typename Box, + typename OverlapsPolicy, + typename VisitBoxPolicy +> +class partition_one_collection +{ + typedef std::vector<std::size_t> index_vector_type; + typedef typename coordinate_type<Box>::type ctype; + typedef partition_one_collection + < + 1 - Dimension, + Box, + OverlapsPolicy, + VisitBoxPolicy + > sub_divide; + + template <typename InputCollection, typename Policy> + static inline void next_level(Box const& box, + InputCollection const& collection, + index_vector_type const& input, + int level, int min_elements, + Policy& policy, VisitBoxPolicy& box_policy) + { + if (boost::size(input) > 0) + { + if (boost::size(input) > min_elements && level < 100) + { + sub_divide::apply(box, collection, input, level + 1, + min_elements, policy, box_policy); + } + else + { + handle_one(collection, input, policy); + } + } + } + +public : + template <typename InputCollection, typename Policy> + static inline void apply(Box const& box, + InputCollection const& collection, + index_vector_type const& input, + int level, + int min_elements, + Policy& policy, VisitBoxPolicy& box_policy) + { + box_policy.apply(box, level); + + Box lower_box, upper_box; + divide_box<Dimension>(box, lower_box, upper_box); + + index_vector_type lower, upper, exceeding; + divide_into_subsets<OverlapsPolicy>(lower_box, upper_box, collection, + input, lower, upper, exceeding); + + if (boost::size(exceeding) > 0) + { + // All what is not fitting a partition should be combined + // with each other, and with all which is fitting. + handle_one(collection, exceeding, policy); + handle_two(collection, exceeding, collection, lower, policy); + handle_two(collection, exceeding, collection, upper, policy); + } + + // Recursively call operation both parts + next_level(lower_box, collection, lower, level, min_elements, + policy, box_policy); + next_level(upper_box, collection, upper, level, min_elements, + policy, box_policy); + } +}; + +template +< + int Dimension, + typename Box, + typename OverlapsPolicy, + typename VisitBoxPolicy +> +class partition_two_collections +{ + typedef std::vector<std::size_t> index_vector_type; + typedef typename coordinate_type<Box>::type ctype; + typedef partition_two_collections + < + 1 - Dimension, + Box, + OverlapsPolicy, + VisitBoxPolicy + > sub_divide; + + template <typename InputCollection, typename Policy> + static inline void next_level(Box const& box, + InputCollection const& collection1, + index_vector_type const& input1, + InputCollection const& collection2, + index_vector_type const& input2, + int level, int min_elements, + Policy& policy, VisitBoxPolicy& box_policy) + { + if (boost::size(input1) > 0 && boost::size(input2) > 0) + { + if (boost::size(input1) > min_elements + && boost::size(input2) > min_elements + && level < 100) + { + sub_divide::apply(box, collection1, input1, collection2, + input2, level + 1, min_elements, + policy, box_policy); + } + else + { + box_policy.apply(box, level + 1); + handle_two(collection1, input1, collection2, input2, policy); + } + } + } + +public : + template <typename InputCollection, typename Policy> + static inline void apply(Box const& box, + InputCollection const& collection1, index_vector_type const& input1, + InputCollection const& collection2, index_vector_type const& input2, + int level, + int min_elements, + Policy& policy, VisitBoxPolicy& box_policy) + { + box_policy.apply(box, level); + + Box lower_box, upper_box; + divide_box<Dimension>(box, lower_box, upper_box); + + index_vector_type lower1, upper1, exceeding1; + index_vector_type lower2, upper2, exceeding2; + divide_into_subsets<OverlapsPolicy>(lower_box, upper_box, collection1, + input1, lower1, upper1, exceeding1); + divide_into_subsets<OverlapsPolicy>(lower_box, upper_box, collection2, + input2, lower2, upper2, exceeding2); + + if (boost::size(exceeding1) > 0) + { + // All exceeding from 1 with 2: + handle_two(collection1, exceeding1, collection2, exceeding2, + policy); + + // All exceeding from 1 with lower and upper of 2: + handle_two(collection1, exceeding1, collection2, lower2, policy); + handle_two(collection1, exceeding1, collection2, upper2, policy); + } + if (boost::size(exceeding2) > 0) + { + // All exceeding from 2 with lower and upper of 1: + handle_two(collection1, lower1, collection2, exceeding2, policy); + handle_two(collection1, upper1, collection2, exceeding2, policy); + } + + next_level(lower_box, collection1, lower1, collection2, lower2, level, + min_elements, policy, box_policy); + next_level(upper_box, collection1, upper1, collection2, upper2, level, + min_elements, policy, box_policy); + } +}; + +}} // namespace detail::partition + +struct visit_no_policy +{ + template <typename Box> + static inline void apply(Box const&, int ) + {} +}; + +template +< + typename Box, + typename ExpandPolicy, + typename OverlapsPolicy, + typename VisitBoxPolicy = visit_no_policy +> +class partition +{ + typedef std::vector<std::size_t> index_vector_type; + + template <typename InputCollection> + static inline void expand_to_collection(InputCollection const& collection, + Box& total, index_vector_type& index_vector) + { + std::size_t index = 0; + for(typename boost::range_iterator<InputCollection const>::type it + = boost::begin(collection); + it != boost::end(collection); + ++it, ++index) + { + ExpandPolicy::apply(total, *it); + index_vector.push_back(index); + } + } + +public : + template <typename InputCollection, typename VisitPolicy> + static inline void apply(InputCollection const& collection, + VisitPolicy& visitor, + int min_elements = 16, + VisitBoxPolicy box_visitor = visit_no_policy() + ) + { + if (boost::size(collection) > min_elements) + { + index_vector_type index_vector; + Box total; + assign_inverse(total); + expand_to_collection(collection, total, index_vector); + + detail::partition::partition_one_collection + < + 0, Box, + OverlapsPolicy, + VisitBoxPolicy + >::apply(total, collection, index_vector, 0, min_elements, + visitor, box_visitor); + } + else + { + typedef typename boost::range_iterator + < + InputCollection const + >::type iterator_type; + for(iterator_type it1 = boost::begin(collection); + it1 != boost::end(collection); + ++it1) + { + iterator_type it2 = it1; + for(++it2; it2 != boost::end(collection); ++it2) + { + visitor.apply(*it1, *it2); + } + } + } + } + + template <typename InputCollection, typename VisitPolicy> + static inline void apply(InputCollection const& collection1, + InputCollection const& collection2, + VisitPolicy& visitor, + int min_elements = 16, + VisitBoxPolicy box_visitor = visit_no_policy() + ) + { + if (boost::size(collection1) > min_elements + && boost::size(collection2) > min_elements) + { + index_vector_type index_vector1, index_vector2; + Box total; + assign_inverse(total); + expand_to_collection(collection1, total, index_vector1); + expand_to_collection(collection2, total, index_vector2); + + detail::partition::partition_two_collections + < + 0, Box, OverlapsPolicy, VisitBoxPolicy + >::apply(total, + collection1, index_vector1, + collection2, index_vector2, + 0, min_elements, visitor, box_visitor); + } + else + { + typedef typename boost::range_iterator + < + InputCollection const + >::type iterator_type; + for(iterator_type it1 = boost::begin(collection1); + it1 != boost::end(collection1); + ++it1) + { + for(iterator_type it2 = boost::begin(collection2); + it2 != boost::end(collection2); + ++it2) + { + visitor.apply(*it1, *it2); + } + } + } + } + +}; + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP diff --git a/boost/geometry/algorithms/detail/point_on_border.hpp b/boost/geometry/algorithms/detail/point_on_border.hpp new file mode 100644 index 0000000000..33177924aa --- /dev/null +++ b/boost/geometry/algorithms/detail/point_on_border.hpp @@ -0,0 +1,243 @@ +// 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. + +// 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.Dimension. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_POINT_ON_BORDER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_POINT_ON_BORDER_HPP + + +#include <cstddef> + +#include <boost/range.hpp> + +#include <boost/geometry/core/point_type.hpp> +#include <boost/geometry/core/ring_type.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/assign.hpp> +#include <boost/geometry/algorithms/detail/disjoint.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace point_on_border +{ + + +template<typename Point> +struct get_point +{ + static inline bool apply(Point& destination, Point const& source, bool) + { + destination = source; + return true; + } +}; + +template<typename Point, std::size_t Dimension, std::size_t DimensionCount> +struct midpoint_helper +{ + static inline bool apply(Point& p, Point const& p1, Point const& p2) + { + typename coordinate_type<Point>::type const two = 2; + set<Dimension>(p, + (get<Dimension>(p1) + get<Dimension>(p2)) / two); + return midpoint_helper<Point, Dimension + 1, DimensionCount>::apply(p, p1, p2); + } +}; + + +template <typename Point, std::size_t DimensionCount> +struct midpoint_helper<Point, DimensionCount, DimensionCount> +{ + static inline bool apply(Point& , Point const& , Point const& ) + { + return true; + } +}; + + +template<typename Point, typename Range> +struct point_on_range +{ + static inline bool apply(Point& point, Range const& range, bool midpoint) + { + const std::size_t n = boost::size(range); + if (midpoint && n > 1) + { + typedef typename boost::range_iterator + < + Range const + >::type iterator; + + iterator it = boost::begin(range); + iterator prev = it++; + while (it != boost::end(range) + && detail::equals::equals_point_point(*it, *prev)) + { + prev = it++; + } + if (it != boost::end(range)) + { + return midpoint_helper + < + Point, + 0, dimension<Point>::value + >::apply(point, *prev, *it); + } + } + + if (n > 0) + { + point = *boost::begin(range); + return true; + } + return false; + } +}; + + +template<typename Point, typename Polygon> +struct point_on_polygon +{ + static inline bool apply(Point& point, Polygon const& polygon, bool midpoint) + { + return point_on_range + < + Point, + typename ring_type<Polygon>::type + >::apply(point, exterior_ring(polygon), midpoint); + } +}; + + +template<typename Point, typename Box> +struct point_on_box +{ + static inline bool apply(Point& point, Box const& box, bool midpoint) + { + if (midpoint) + { + Point p1, p2; + detail::assign::assign_box_2d_corner<min_corner, min_corner>(box, p1); + detail::assign::assign_box_2d_corner<max_corner, min_corner>(box, p2); + midpoint_helper + < + Point, + 0, dimension<Point>::value + >::apply(point, p1, p2); + } + else + { + detail::assign::assign_box_2d_corner<min_corner, min_corner>(box, point); + } + + return true; + } +}; + + +}} // namespace detail::point_on_border +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename GeometryTag, + typename Point, + typename Geometry + +> +struct point_on_border +{}; + + +template<typename Point> +struct point_on_border<point_tag, Point, Point> + : detail::point_on_border::get_point<Point> +{}; + + +template<typename Point, typename Linestring> +struct point_on_border<linestring_tag, Point, Linestring> + : detail::point_on_border::point_on_range<Point, Linestring> +{}; + + +template<typename Point, typename Ring> +struct point_on_border<ring_tag, Point, Ring> + : detail::point_on_border::point_on_range<Point, Ring> +{}; + + +template<typename Point, typename Polygon> +struct point_on_border<polygon_tag, Point, Polygon> + : detail::point_on_border::point_on_polygon<Point, Polygon> +{}; + + +template<typename Point, typename Box> +struct point_on_border<box_tag, Point, Box> + : detail::point_on_border::point_on_box<Point, Box> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Take point on a border +\ingroup overlay +\tparam Geometry geometry type. This also defines the type of the output point +\param point to assign +\param geometry geometry to take point from +\param midpoint boolean flag, true if the point should not be a vertex, but some point + in between of two vertices +\return TRUE if successful, else false. + It is only false if polygon/line have no points +\note for a polygon, it is always a point on the exterior ring +\note for take_midpoint, it is not taken from two consecutive duplicate vertices, + (unless there are no other). + */ +template <typename Point, typename Geometry> +inline bool point_on_border(Point& point, + Geometry const& geometry, + bool midpoint = false) +{ + concept::check<Point>(); + concept::check<Geometry const>(); + + typedef typename point_type<Geometry>::type point_type; + + return dispatch::point_on_border + < + typename tag<Geometry>::type, + Point, + Geometry + >::apply(point, geometry, midpoint); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_POINT_ON_BORDER_HPP diff --git a/boost/geometry/algorithms/detail/ring_identifier.hpp b/boost/geometry/algorithms/detail/ring_identifier.hpp new file mode 100644 index 0000000000..9209ee0304 --- /dev/null +++ b/boost/geometry/algorithms/detail/ring_identifier.hpp @@ -0,0 +1,70 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RING_IDENTIFIER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RING_IDENTIFIER_HPP + + +namespace boost { namespace geometry +{ + + +// Ring Identifier. It is currently: source,multi,ring +struct ring_identifier +{ + + inline ring_identifier() + : source_index(-1) + , multi_index(-1) + , ring_index(-1) + {} + + inline ring_identifier(int src, int mul, int rin) + : source_index(src) + , multi_index(mul) + , ring_index(rin) + {} + + inline bool operator<(ring_identifier const& other) const + { + return source_index != other.source_index ? source_index < other.source_index + : multi_index !=other.multi_index ? multi_index < other.multi_index + : ring_index < other.ring_index + ; + } + + inline bool operator==(ring_identifier const& other) const + { + return source_index == other.source_index + && ring_index == other.ring_index + && multi_index == other.multi_index + ; + } + +#if defined(BOOST_GEOMETRY_DEBUG_IDENTIFIER) + friend std::ostream& operator<<(std::ostream &os, ring_identifier const& ring_id) + { + os << "(s:" << ring_id.source_index; + if (ring_id.ring_index >= 0) os << ", r:" << ring_id.ring_index; + if (ring_id.multi_index >= 0) os << ", m:" << ring_id.multi_index; + os << ")"; + return os; + } +#endif + + + int source_index; + int multi_index; + int ring_index; +}; + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_RING_IDENTIFIER_HPP diff --git a/boost/geometry/algorithms/detail/sections/range_by_section.hpp b/boost/geometry/algorithms/detail/sections/range_by_section.hpp new file mode 100644 index 0000000000..ad62f232bd --- /dev/null +++ b/boost/geometry/algorithms/detail/sections/range_by_section.hpp @@ -0,0 +1,131 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_RANGE_BY_SECTION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_RANGE_BY_SECTION_HPP + + +#include <boost/mpl/assert.hpp> +#include <boost/range.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> + + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace section +{ + + +template <typename Range, typename Section> +struct full_section_range +{ + static inline Range const& apply(Range const& range, Section const& ) + { + return range; + } +}; + + +template <typename Polygon, typename Section> +struct full_section_polygon +{ + static inline typename ring_return_type<Polygon const>::type apply(Polygon const& polygon, Section const& section) + { + return section.ring_id.ring_index < 0 + ? geometry::exterior_ring(polygon) + : geometry::interior_rings(polygon)[section.ring_id.ring_index]; + } +}; + + +}} // namespace detail::section +#endif + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag, + typename Geometry, + typename Section +> +struct range_by_section +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + + +template <typename LineString, typename Section> +struct range_by_section<linestring_tag, LineString, Section> + : detail::section::full_section_range<LineString, Section> +{}; + + +template <typename Ring, typename Section> +struct range_by_section<ring_tag, Ring, Section> + : detail::section::full_section_range<Ring, Section> +{}; + + +template <typename Polygon, typename Section> +struct range_by_section<polygon_tag, Polygon, Section> + : detail::section::full_section_polygon<Polygon, Section> +{}; + + +} // namespace dispatch +#endif + + +/*! + \brief Get full ring (exterior, one of interiors, one from multi) + indicated by the specified section + \ingroup sectionalize + \tparam Geometry type + \tparam Section type of section to get from + \param geometry geometry to take section of + \param section structure with section + */ +template <typename Geometry, typename Section> +inline typename ring_return_type<Geometry const>::type + range_by_section(Geometry const& geometry, Section const& section) +{ + concept::check<Geometry const>(); + + return dispatch::range_by_section + < + typename tag<Geometry>::type, + Geometry, + Section + >::apply(geometry, section); +} + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_RANGE_BY_SECTION_HPP diff --git a/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/boost/geometry/algorithms/detail/sections/sectionalize.hpp new file mode 100644 index 0000000000..36bcbdd6e7 --- /dev/null +++ b/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -0,0 +1,648 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP + +#include <cstddef> +#include <vector> + +#include <boost/mpl/assert.hpp> +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/algorithms/assign.hpp> +#include <boost/geometry/algorithms/expand.hpp> + +#include <boost/geometry/algorithms/detail/ring_identifier.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/point_order.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/util/math.hpp> +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/views/reversible_view.hpp> +#include <boost/geometry/geometries/segment.hpp> + + +namespace boost { namespace geometry +{ + + +/*! + \brief Structure containing section information + \details Section information consists of a bounding box, direction + information (if it is increasing or decreasing, per dimension), + index information (begin-end, ring, multi) and the number of + segments in this section + + \tparam Box box-type + \tparam DimensionCount number of dimensions for this section + \ingroup sectionalize + */ +template <typename Box, std::size_t DimensionCount> +struct section +{ + typedef Box box_type; + + int id; // might be obsolete now, BSG 14-03-2011 TODO decide about this + + int directions[DimensionCount]; + ring_identifier ring_id; + Box bounding_box; + + int begin_index; + int end_index; + std::size_t count; + std::size_t range_count; + bool duplicate; + int non_duplicate_index; + + inline section() + : id(-1) + , begin_index(-1) + , end_index(-1) + , count(0) + , range_count(0) + , duplicate(false) + , non_duplicate_index(-1) + { + assign_inverse(bounding_box); + for (register std::size_t i = 0; i < DimensionCount; i++) + { + directions[i] = 0; + } + } +}; + + +/*! + \brief Structure containing a collection of sections + \note Derived from a vector, proves to be faster than of deque + \note vector might be templated in the future + \ingroup sectionalize + */ +template <typename Box, std::size_t DimensionCount> +struct sections : std::vector<section<Box, DimensionCount> > +{ + typedef Box box_type; + static std::size_t const value = DimensionCount; +}; + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace sectionalize +{ + +template <typename Segment, std::size_t Dimension, std::size_t DimensionCount> +struct get_direction_loop +{ + typedef typename coordinate_type<Segment>::type coordinate_type; + + static inline void apply(Segment const& seg, + int directions[DimensionCount]) + { + coordinate_type const diff = + geometry::get<1, Dimension>(seg) - geometry::get<0, Dimension>(seg); + + coordinate_type zero = coordinate_type(); + directions[Dimension] = diff > zero ? 1 : diff < zero ? -1 : 0; + + get_direction_loop + < + Segment, Dimension + 1, DimensionCount + >::apply(seg, directions); + } +}; + +template <typename Segment, std::size_t DimensionCount> +struct get_direction_loop<Segment, DimensionCount, DimensionCount> +{ + static inline void apply(Segment const&, int [DimensionCount]) + {} +}; + +template <typename T, std::size_t Dimension, std::size_t DimensionCount> +struct copy_loop +{ + static inline void apply(T const source[DimensionCount], + T target[DimensionCount]) + { + target[Dimension] = source[Dimension]; + copy_loop<T, Dimension + 1, DimensionCount>::apply(source, target); + } +}; + +template <typename T, std::size_t DimensionCount> +struct copy_loop<T, DimensionCount, DimensionCount> +{ + static inline void apply(T const [DimensionCount], T [DimensionCount]) + {} +}; + +template <typename T, std::size_t Dimension, std::size_t DimensionCount> +struct compare_loop +{ + static inline bool apply(T const source[DimensionCount], + T const target[DimensionCount]) + { + bool const not_equal = target[Dimension] != source[Dimension]; + + return not_equal + ? false + : compare_loop + < + T, Dimension + 1, DimensionCount + >::apply(source, target); + } +}; + +template <typename T, std::size_t DimensionCount> +struct compare_loop<T, DimensionCount, DimensionCount> +{ + static inline bool apply(T const [DimensionCount], + T const [DimensionCount]) + { + + return true; + } +}; + + +template <typename Segment, std::size_t Dimension, std::size_t DimensionCount> +struct check_duplicate_loop +{ + typedef typename coordinate_type<Segment>::type coordinate_type; + + static inline bool apply(Segment const& seg) + { + if (! geometry::math::equals + ( + geometry::get<0, Dimension>(seg), + geometry::get<1, Dimension>(seg) + ) + ) + { + return false; + } + + return check_duplicate_loop + < + Segment, Dimension + 1, DimensionCount + >::apply(seg); + } +}; + +template <typename Segment, std::size_t DimensionCount> +struct check_duplicate_loop<Segment, DimensionCount, DimensionCount> +{ + static inline bool apply(Segment const&) + { + return true; + } +}; + +template <typename T, std::size_t Dimension, std::size_t DimensionCount> +struct assign_loop +{ + static inline void apply(T dims[DimensionCount], int const value) + { + dims[Dimension] = value; + assign_loop<T, Dimension + 1, DimensionCount>::apply(dims, value); + } +}; + +template <typename T, std::size_t DimensionCount> +struct assign_loop<T, DimensionCount, DimensionCount> +{ + static inline void apply(T [DimensionCount], int const) + { + } +}; + +/// @brief Helper class to create sections of a part of a range, on the fly +template +< + typename Range, // Can be closeable_view + typename Point, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize_part +{ + typedef model::referring_segment<Point const> segment_type; + typedef typename boost::range_value<Sections>::type section_type; + + typedef typename boost::range_iterator<Range const>::type iterator_type; + + static inline void apply(Sections& sections, section_type& section, + int& index, int& ndi, + Range const& range, + ring_identifier ring_id) + { + if (boost::size(range) <= index) + { + return; + } + + if (index == 0) + { + ndi = 0; + } + + iterator_type it = boost::begin(range); + it += index; + + for(iterator_type previous = it++; + it != boost::end(range); + ++previous, ++it, index++) + { + segment_type segment(*previous, *it); + + int direction_classes[DimensionCount] = {0}; + get_direction_loop + < + segment_type, 0, DimensionCount + >::apply(segment, direction_classes); + + // if "dir" == 0 for all point-dimensions, it is duplicate. + // Those sections might be omitted, if wished, lateron + bool duplicate = false; + + if (direction_classes[0] == 0) + { + // Recheck because ALL dimensions should be checked, + // not only first one. + // (DimensionCount might be < dimension<P>::value) + if (check_duplicate_loop + < + segment_type, 0, geometry::dimension<Point>::type::value + >::apply(segment) + ) + { + duplicate = true; + + // Change direction-info to force new section + // Note that wo consecutive duplicate segments will generate + // only one duplicate-section. + // Actual value is not important as long as it is not -1,0,1 + assign_loop + < + int, 0, DimensionCount + >::apply(direction_classes, -99); + } + } + + if (section.count > 0 + && (!compare_loop + < + int, 0, DimensionCount + >::apply(direction_classes, section.directions) + || section.count > MaxCount + ) + ) + { + sections.push_back(section); + section = section_type(); + } + + if (section.count == 0) + { + section.begin_index = index; + section.ring_id = ring_id; + section.duplicate = duplicate; + section.non_duplicate_index = ndi; + section.range_count = boost::size(range); + + copy_loop + < + int, 0, DimensionCount + >::apply(direction_classes, section.directions); + geometry::expand(section.bounding_box, *previous); + } + + geometry::expand(section.bounding_box, *it); + section.end_index = index + 1; + section.count++; + if (! duplicate) + { + ndi++; + } + } + } +}; + + +template +< + typename Range, closure_selector Closure, bool Reverse, + typename Point, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize_range +{ + typedef typename closeable_view<Range const, Closure>::type cview_type; + typedef typename reversible_view + < + cview_type const, + Reverse ? iterate_reverse : iterate_forward + >::type view_type; + + static inline void apply(Range const& range, Sections& sections, + ring_identifier ring_id) + { + typedef model::referring_segment<Point const> segment_type; + + cview_type cview(range); + view_type view(cview); + + std::size_t const n = boost::size(view); + if (n == 0) + { + // Zero points, no section + return; + } + + if (n == 1) + { + // Line with one point ==> no sections + return; + } + + int index = 0; + int ndi = 0; // non duplicate index + + typedef typename boost::range_value<Sections>::type section_type; + section_type section; + + sectionalize_part + < + view_type, Point, Sections, + DimensionCount, MaxCount + >::apply(sections, section, index, ndi, + view, ring_id); + + // Add last section if applicable + if (section.count > 0) + { + sections.push_back(section); + } + } +}; + +template +< + typename Polygon, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize_polygon +{ + static inline void apply(Polygon const& poly, Sections& sections, + ring_identifier ring_id) + { + typedef typename point_type<Polygon>::type point_type; + typedef typename ring_type<Polygon>::type ring_type; + typedef sectionalize_range + < + ring_type, closure<Polygon>::value, Reverse, + point_type, Sections, DimensionCount, MaxCount + > sectionalizer_type; + + ring_id.ring_index = -1; + sectionalizer_type::apply(exterior_ring(poly), sections, ring_id);//-1, multi_index); + + ring_id.ring_index++; + typename interior_return_type<Polygon const>::type rings + = interior_rings(poly); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); + ++it, ++ring_id.ring_index) + { + sectionalizer_type::apply(*it, sections, ring_id); + } + } +}; + +template +< + typename Box, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize_box +{ + static inline void apply(Box const& box, Sections& sections, ring_identifier const& ring_id) + { + typedef typename point_type<Box>::type point_type; + + assert_dimension<Box, 2>(); + + // Add all four sides of the 2D-box as separate section. + // Easiest is to convert it to a polygon. + // However, we don't have the polygon type + // (or polygon would be a helper-type). + // Therefore we mimic a linestring/std::vector of 5 points + + // TODO: might be replaced by assign_box_corners_oriented + // or just "convert" + point_type ll, lr, ul, ur; + geometry::detail::assign_box_corners(box, ll, lr, ul, ur); + + std::vector<point_type> points; + points.push_back(ll); + points.push_back(ul); + points.push_back(ur); + points.push_back(lr); + points.push_back(ll); + + sectionalize_range + < + std::vector<point_type>, closed, false, + point_type, + Sections, + DimensionCount, + MaxCount + >::apply(points, sections, ring_id); + } +}; + +template <typename Sections> +inline void set_section_unique_ids(Sections& sections) +{ + // Set ID's. + int index = 0; + for (typename boost::range_iterator<Sections>::type it = boost::begin(sections); + it != boost::end(sections); + ++it) + { + it->id = index++; + } +} + + +}} // namespace detail::sectionalize +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag, + typename Geometry, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + +template +< + typename Box, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize<box_tag, Box, Reverse, Sections, DimensionCount, MaxCount> + : detail::sectionalize::sectionalize_box + < + Box, + Sections, + DimensionCount, + MaxCount + > +{}; + +template +< + typename LineString, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize + < + linestring_tag, + LineString, + false, + Sections, + DimensionCount, + MaxCount + > + : detail::sectionalize::sectionalize_range + < + LineString, closed, false, + typename point_type<LineString>::type, + Sections, + DimensionCount, + MaxCount + > +{}; + +template +< + typename Ring, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize<ring_tag, Ring, Reverse, Sections, DimensionCount, MaxCount> + : detail::sectionalize::sectionalize_range + < + Ring, geometry::closure<Ring>::value, Reverse, + typename point_type<Ring>::type, + Sections, + DimensionCount, + MaxCount + > +{}; + +template +< + typename Polygon, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize<polygon_tag, Polygon, Reverse, Sections, DimensionCount, MaxCount> + : detail::sectionalize::sectionalize_polygon + < + Polygon, Reverse, Sections, DimensionCount, MaxCount + > +{}; + +} // namespace dispatch +#endif + + +/*! + \brief Split a geometry into monotonic sections + \ingroup sectionalize + \tparam Geometry type of geometry to check + \tparam Sections type of sections to create + \param geometry geometry to create sections from + \param sections structure with sections + \param source_index index to assign to the ring_identifiers + */ +template<bool Reverse, typename Geometry, typename Sections> +inline void sectionalize(Geometry const& geometry, Sections& sections, int source_index = 0) +{ + concept::check<Geometry const>(); + + // TODO: review use of this constant (see below) as causing problems with GCC 4.6 --mloskot + // A maximum of 10 segments per section seems to give the fastest results + //static std::size_t const max_segments_per_section = 10; + typedef dispatch::sectionalize + < + typename tag<Geometry>::type, + Geometry, + Reverse, + Sections, + Sections::value, + 10 // TODO: max_segments_per_section + > sectionalizer_type; + + sections.clear(); + ring_identifier ring_id; + ring_id.source_index = source_index; + sectionalizer_type::apply(geometry, sections, ring_id); + detail::sectionalize::set_section_unique_ids(sections); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP diff --git a/boost/geometry/algorithms/detail/throw_on_empty_input.hpp b/boost/geometry/algorithms/detail/throw_on_empty_input.hpp new file mode 100644 index 0000000000..62328a0d87 --- /dev/null +++ b/boost/geometry/algorithms/detail/throw_on_empty_input.hpp @@ -0,0 +1,53 @@ +// 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. + +// 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_THROW_ON_EMPTY_INPUT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_THROW_ON_EMPTY_INPUT_HPP + +#include <boost/geometry/core/exception.hpp> +#include <boost/geometry/algorithms/num_points.hpp> + +// BSG 2012-02-06: we use this currently only for distance. +// For other scalar results area,length,perimeter it is commented on purpose. +// Reason is that for distance there is no other choice. distance of two +// empty geometries (or one empty) should NOT return any value. +// But for area it is no problem to be 0. +// Suppose: area(intersection(a,b)). We (probably) don't want a throw there... + +// So decided that at least for Boost 1.49 this is commented for +// scalar results, except distance. + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template <typename Geometry> +inline void throw_on_empty_input(Geometry const& geometry) +{ +#if ! defined(BOOST_GEOMETRY_EMPTY_INPUT_NO_THROW) + if (geometry::num_points(geometry) == 0) + { + throw empty_input_exception(); + } +#endif +} + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_THROW_ON_EMPTY_INPUT_HPP + diff --git a/boost/geometry/algorithms/difference.hpp b/boost/geometry/algorithms/difference.hpp new file mode 100644 index 0000000000..480dd928de --- /dev/null +++ b/boost/geometry/algorithms/difference.hpp @@ -0,0 +1,160 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DIFFERENCE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DIFFERENCE_HPP + +#include <algorithm> + +#include <boost/geometry/algorithms/detail/overlay/intersection_insert.hpp> + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace difference +{ + +/*! +\brief_calc2{difference} \brief_strategy +\ingroup difference +\details \details_calc2{difference_insert, spatial set theoretic difference} + \brief_strategy. \details_inserter{difference} +\tparam GeometryOut output geometry type, must be specified +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam OutputIterator output iterator +\tparam Strategy \tparam_strategy_overlay +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param out \param_out{difference} +\param strategy \param_strategy{difference} +\return \return_out + +\qbk{distinguish,with strategy} +*/ +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename OutputIterator, + typename Strategy +> +inline OutputIterator difference_insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, OutputIterator out, + Strategy const& strategy) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + concept::check<GeometryOut>(); + + return geometry::dispatch::intersection_insert + < + typename geometry::tag<Geometry1>::type, + typename geometry::tag<Geometry2>::type, + typename geometry::tag<GeometryOut>::type, + geometry::is_areal<Geometry1>::value, + geometry::is_areal<Geometry2>::value, + geometry::is_areal<GeometryOut>::value, + Geometry1, Geometry2, + geometry::detail::overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, + geometry::detail::overlay::do_reverse<geometry::point_order<Geometry2>::value, true>::value, + geometry::detail::overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value, + OutputIterator, GeometryOut, + overlay_difference, + Strategy + >::apply(geometry1, geometry2, out, strategy); +} + +/*! +\brief_calc2{difference} +\ingroup difference +\details \details_calc2{difference_insert, spatial set theoretic difference}. + \details_insert{difference} +\tparam GeometryOut output geometry type, must be specified +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam OutputIterator output iterator +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param out \param_out{difference} +\return \return_out + +\qbk{[include reference/algorithms/difference_insert.qbk]} +*/ +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename OutputIterator +> +inline OutputIterator difference_insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, OutputIterator out) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + concept::check<GeometryOut>(); + + typedef strategy_intersection + < + typename cs_tag<GeometryOut>::type, + Geometry1, + Geometry2, + typename geometry::point_type<GeometryOut>::type + > strategy; + + return difference_insert<GeometryOut>(geometry1, geometry2, + out, strategy()); +} + + +}} // namespace detail::difference +#endif // DOXYGEN_NO_DETAIL + + + +/*! +\brief_calc2{difference} +\ingroup difference +\details \details_calc2{difference, spatial set theoretic difference}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Collection \tparam_output_collection +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param output_collection the output collection + +\qbk{[include reference/algorithms/difference.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename Collection +> +inline void difference(Geometry1 const& geometry1, + Geometry2 const& geometry2, Collection& output_collection) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + typedef typename boost::range_value<Collection>::type geometry_out; + concept::check<geometry_out>(); + + detail::difference::difference_insert<geometry_out>( + geometry1, geometry2, + std::back_inserter(output_collection)); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DIFFERENCE_HPP diff --git a/boost/geometry/algorithms/disjoint.hpp b/boost/geometry/algorithms/disjoint.hpp new file mode 100644 index 0000000000..f36b8dbdd0 --- /dev/null +++ b/boost/geometry/algorithms/disjoint.hpp @@ -0,0 +1,270 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DISJOINT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DISJOINT_HPP + +#include <cstddef> +#include <deque> + +#include <boost/mpl/if.hpp> +#include <boost/range.hpp> + +#include <boost/static_assert.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/reverse_dispatch.hpp> + +#include <boost/geometry/algorithms/detail/disjoint.hpp> +#include <boost/geometry/algorithms/detail/point_on_border.hpp> +#include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> +#include <boost/geometry/algorithms/within.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/util/math.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + +struct assign_disjoint_policy +{ + // We want to include all points: + static bool const include_no_turn = true; + static bool const include_degenerate = true; + + // We don't assign extra info: + template <typename Point1, typename Point2, typename Info> + static inline void apply(Info& , Point1 const& , Point2 const& ) + {} +}; + + +template <typename Geometry1, typename Geometry2> +struct disjoint_linear +{ + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + typedef typename geometry::point_type<Geometry1>::type point_type; + + typedef overlay::turn_info<point_type> turn_info; + std::deque<turn_info> turns; + + // Specify two policies: + // 1) Stop at any intersection + // 2) In assignment, include also degenerate points (which are normally skipped) + disjoint_interrupt_policy policy; + geometry::get_turns + < + false, false, + assign_disjoint_policy + >(geometry1, geometry2, turns, policy); + if (policy.has_intersections) + { + return false; + } + + return true; + } +}; + +template <typename Segment1, typename Segment2> +struct disjoint_segment +{ + static inline bool apply(Segment1 const& segment1, Segment2 const& segment2) + { + typedef typename point_type<Segment1>::type point_type; + + segment_intersection_points<point_type> is + = strategy::intersection::relate_cartesian_segments + < + policies::relate::segments_intersection_points + < + Segment1, + Segment2, + segment_intersection_points<point_type> + > + >::apply(segment1, segment2); + + return is.count == 0; + } +}; + + + +template <typename Geometry1, typename Geometry2> +struct general_areal +{ + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + if (! disjoint_linear<Geometry1, Geometry2>::apply(geometry1, geometry2)) + { + return false; + } + + typedef typename geometry::point_type<Geometry1>::type point_type; + + // If there is no intersection of segments, they might located + // inside each other + point_type p1; + geometry::point_on_border(p1, geometry1); + if (geometry::within(p1, geometry2)) + { + return false; + } + + typename geometry::point_type<Geometry1>::type p2; + geometry::point_on_border(p2, geometry2); + if (geometry::within(p2, geometry1)) + { + return false; + } + + return true; + } +}; + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename GeometryTag1, typename GeometryTag2, + typename Geometry1, typename Geometry2, + std::size_t DimensionCount +> +struct disjoint + : detail::disjoint::general_areal<Geometry1, Geometry2> +{}; + + +template <typename Point1, typename Point2, std::size_t DimensionCount> +struct disjoint<point_tag, point_tag, Point1, Point2, DimensionCount> + : detail::disjoint::point_point<Point1, Point2, 0, DimensionCount> +{}; + + +template <typename Box1, typename Box2, std::size_t DimensionCount> +struct disjoint<box_tag, box_tag, Box1, Box2, DimensionCount> + : detail::disjoint::box_box<Box1, Box2, 0, DimensionCount> +{}; + + +template <typename Point, typename Box, std::size_t DimensionCount> +struct disjoint<point_tag, box_tag, Point, Box, DimensionCount> + : detail::disjoint::point_box<Point, Box, 0, DimensionCount> +{}; + +template <typename Linestring1, typename Linestring2> +struct disjoint<linestring_tag, linestring_tag, Linestring1, Linestring2, 2> + : detail::disjoint::disjoint_linear<Linestring1, Linestring2> +{}; + +template <typename Linestring1, typename Linestring2> +struct disjoint<segment_tag, segment_tag, Linestring1, Linestring2, 2> + : detail::disjoint::disjoint_segment<Linestring1, Linestring2> +{}; + +template <typename Linestring, typename Segment> +struct disjoint<linestring_tag, segment_tag, Linestring, Segment, 2> + : detail::disjoint::disjoint_linear<Linestring, Segment> +{}; + + +template +< + typename GeometryTag1, typename GeometryTag2, + typename Geometry1, typename Geometry2, + std::size_t DimensionCount +> +struct disjoint_reversed +{ + static inline bool apply(Geometry1 const& g1, Geometry2 const& g2) + { + return disjoint + < + GeometryTag2, GeometryTag1, + Geometry2, Geometry1, + DimensionCount + >::apply(g2, g1); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + + +/*! +\brief \brief_check2{are disjoint} +\ingroup disjoint +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_check2{are disjoint} + +\qbk{[include reference/algorithms/disjoint.qbk]} +*/ +template <typename Geometry1, typename Geometry2> +inline bool disjoint(Geometry1 const& geometry1, + Geometry2 const& geometry2) +{ + concept::check_concepts_and_equal_dimensions + < + Geometry1 const, + Geometry2 const + >(); + + return boost::mpl::if_c + < + reverse_dispatch<Geometry1, Geometry2>::type::value, + dispatch::disjoint_reversed + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, + Geometry2, + dimension<Geometry1>::type::value + >, + dispatch::disjoint + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, + Geometry2, + dimension<Geometry1>::type::value + > + >::type::apply(geometry1, geometry2); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DISJOINT_HPP diff --git a/boost/geometry/algorithms/distance.hpp b/boost/geometry/algorithms/distance.hpp new file mode 100644 index 0000000000..eca3b03c7b --- /dev/null +++ b/boost/geometry/algorithms/distance.hpp @@ -0,0 +1,591 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DISTANCE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DISTANCE_HPP + + +#include <boost/mpl/if.hpp> +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/reverse_dispatch.hpp> +#include <boost/geometry/core/tag_cast.hpp> + +#include <boost/geometry/algorithms/not_implemented.hpp> +#include <boost/geometry/algorithms/detail/throw_on_empty_input.hpp> + +#include <boost/geometry/geometries/segment.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/strategies/distance.hpp> +#include <boost/geometry/strategies/default_distance_result.hpp> +#include <boost/geometry/algorithms/assign.hpp> +#include <boost/geometry/algorithms/within.hpp> + +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/util/math.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + +// To avoid spurious namespaces here: +using strategy::distance::services::return_type; + +template <typename P1, typename P2, typename Strategy> +struct point_to_point +{ + static inline typename return_type<Strategy>::type apply(P1 const& p1, + P2 const& p2, Strategy const& strategy) + { + return strategy.apply(p1, p2); + } +}; + + +template<typename Point, typename Segment, typename Strategy> +struct point_to_segment +{ + static inline typename return_type<Strategy>::type apply(Point const& point, + Segment const& segment, Strategy const& ) + { + typename strategy::distance::services::default_strategy + < + segment_tag, + Point, + typename point_type<Segment>::type, + typename cs_tag<Point>::type, + typename cs_tag<typename point_type<Segment>::type>::type, + Strategy + >::type segment_strategy; + + typename point_type<Segment>::type p[2]; + geometry::detail::assign_point_from_index<0>(segment, p[0]); + geometry::detail::assign_point_from_index<1>(segment, p[1]); + return segment_strategy.apply(point, p[0], p[1]); + } +}; + + +template +< + typename Point, + typename Range, + closure_selector Closure, + typename PPStrategy, + typename PSStrategy +> +struct point_to_range +{ + typedef typename return_type<PSStrategy>::type return_type; + + static inline return_type apply(Point const& point, Range const& range, + PPStrategy const& pp_strategy, PSStrategy const& ps_strategy) + { + return_type const zero = return_type(0); + + if (boost::size(range) == 0) + { + return zero; + } + + typedef typename closeable_view<Range const, Closure>::type view_type; + + view_type view(range); + + // line of one point: return point distance + typedef typename boost::range_iterator<view_type const>::type iterator_type; + iterator_type it = boost::begin(view); + iterator_type prev = it++; + if (it == boost::end(view)) + { + return pp_strategy.apply(point, *boost::begin(view)); + } + + // Create comparable (more efficient) strategy + typedef typename strategy::distance::services::comparable_type<PSStrategy>::type eps_strategy_type; + eps_strategy_type eps_strategy = strategy::distance::services::get_comparable<PSStrategy>::apply(ps_strategy); + + // start with first segment distance + return_type d = eps_strategy.apply(point, *prev, *it); + return_type rd = ps_strategy.apply(point, *prev, *it); + + // check if other segments are closer + for (++prev, ++it; it != boost::end(view); ++prev, ++it) + { + return_type const ds = ps_strategy.apply(point, *prev, *it); + if (geometry::math::equals(ds, zero)) + { + return ds; + } + else if (ds < d) + { + d = ds; + rd = ps_strategy.apply(point, *prev, *it); + } + } + + return rd; + } +}; + + +template +< + typename Point, + typename Ring, + closure_selector Closure, + typename PPStrategy, + typename PSStrategy +> +struct point_to_ring +{ + typedef std::pair + < + typename return_type<PPStrategy>::type, bool + > distance_containment; + + static inline distance_containment apply(Point const& point, + Ring const& ring, + PPStrategy const& pp_strategy, PSStrategy const& ps_strategy) + { + return distance_containment + ( + point_to_range + < + Point, + Ring, + Closure, + PPStrategy, + PSStrategy + >::apply(point, ring, pp_strategy, ps_strategy), + geometry::within(point, ring) + ); + } +}; + + + +template +< + typename Point, + typename Polygon, + closure_selector Closure, + typename PPStrategy, + typename PSStrategy +> +struct point_to_polygon +{ + typedef typename return_type<PPStrategy>::type return_type; + typedef std::pair<return_type, bool> distance_containment; + + static inline distance_containment apply(Point const& point, + Polygon const& polygon, + PPStrategy const& pp_strategy, PSStrategy const& ps_strategy) + { + // Check distance to all rings + typedef point_to_ring + < + Point, + typename ring_type<Polygon>::type, + Closure, + PPStrategy, + PSStrategy + > per_ring; + + distance_containment dc = per_ring::apply(point, + exterior_ring(polygon), pp_strategy, ps_strategy); + + typename interior_return_type<Polygon const>::type rings + = interior_rings(polygon); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + distance_containment dcr = per_ring::apply(point, + *it, pp_strategy, ps_strategy); + if (dcr.first < dc.first) + { + dc.first = dcr.first; + } + // If it was inside, and also inside inner ring, + // turn off the inside-flag, it is outside the polygon + if (dc.second && dcr.second) + { + dc.second = false; + } + } + return dc; + } +}; + + +// Helper metafunction for default strategy retrieval +template <typename Geometry1, typename Geometry2> +struct default_strategy + : strategy::distance::services::default_strategy + < + point_tag, + typename point_type<Geometry1>::type, + typename point_type<Geometry2>::type + > +{}; + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +using strategy::distance::services::return_type; + + +template +< + typename Geometry1, typename Geometry2, + typename Strategy = typename detail::distance::default_strategy<Geometry1, Geometry2>::type, + typename Tag1 = typename tag_cast<typename tag<Geometry1>::type, multi_tag>::type, + typename Tag2 = typename tag_cast<typename tag<Geometry2>::type, multi_tag>::type, + typename StrategyTag = typename strategy::distance::services::tag<Strategy>::type, + bool Reverse = reverse_dispatch<Geometry1, Geometry2>::type::value +> +struct distance: not_implemented<Tag1, Tag2> +{}; + + +// If reversal is needed, perform it +template +< + typename Geometry1, typename Geometry2, typename Strategy, + typename Tag1, typename Tag2, typename StrategyTag +> +struct distance +< + Geometry1, Geometry2, Strategy, + Tag1, Tag2, StrategyTag, + true +> + : distance<Geometry2, Geometry1, Strategy, Tag2, Tag1, StrategyTag, false> +{ + static inline typename return_type<Strategy>::type apply( + Geometry1 const& g1, + Geometry2 const& g2, + Strategy const& strategy) + { + return distance + < + Geometry2, Geometry1, Strategy, + Tag2, Tag1, StrategyTag, + false + >::apply(g2, g1, strategy); + } +}; + +// If reversal is needed and we got the strategy by default, invert it before +// proceeding to the reversal. +template +< + typename Geometry1, typename Geometry2, + typename Tag1, typename Tag2, typename StrategyTag +> +struct distance +< + Geometry1, Geometry2, + typename detail::distance::default_strategy<Geometry1, Geometry2>::type, + Tag1, Tag2, StrategyTag, + true +> + : distance + < + Geometry2, Geometry1, + typename detail::distance::default_strategy<Geometry2, Geometry1>::type, + Tag2, Tag1, StrategyTag, + false + > +{ + typedef typename detail::distance::default_strategy<Geometry2, Geometry1>::type reversed_strategy; + + static inline typename strategy::distance::services::return_type<reversed_strategy>::type apply( + Geometry1 const& g1, + Geometry2 const& g2, + typename detail::distance::default_strategy<Geometry1, Geometry2>::type const&) + { + return distance + < + Geometry2, Geometry1, reversed_strategy, + Tag2, Tag1, StrategyTag, + false + >::apply(g2, g1, reversed_strategy()); + } +}; + + +// Point-point +template <typename P1, typename P2, typename Strategy> +struct distance + < + P1, P2, Strategy, + point_tag, point_tag, strategy_tag_distance_point_point, + false + > + : detail::distance::point_to_point<P1, P2, Strategy> +{}; + + +// Point-line version 1, where point-point strategy is specified +template <typename Point, typename Linestring, typename Strategy> +struct distance +< + Point, Linestring, Strategy, + point_tag, linestring_tag, strategy_tag_distance_point_point, + false +> +{ + + static inline typename return_type<Strategy>::type apply(Point const& point, + Linestring const& linestring, + Strategy const& strategy) + { + typedef typename strategy::distance::services::default_strategy + < + segment_tag, + Point, + typename point_type<Linestring>::type + >::type ps_strategy_type; + + return detail::distance::point_to_range + < + Point, Linestring, closed, Strategy, ps_strategy_type + >::apply(point, linestring, strategy, ps_strategy_type()); + } +}; + + +// Point-line version 2, where point-segment strategy is specified +template <typename Point, typename Linestring, typename Strategy> +struct distance +< + Point, Linestring, Strategy, + point_tag, linestring_tag, strategy_tag_distance_point_segment, + false +> +{ + static inline typename return_type<Strategy>::type apply(Point const& point, + Linestring const& linestring, + Strategy const& strategy) + { + typedef typename Strategy::point_strategy_type pp_strategy_type; + return detail::distance::point_to_range + < + Point, Linestring, closed, pp_strategy_type, Strategy + >::apply(point, linestring, pp_strategy_type(), strategy); + } +}; + +// Point-ring , where point-segment strategy is specified +template <typename Point, typename Ring, typename Strategy> +struct distance +< + Point, Ring, Strategy, + point_tag, ring_tag, strategy_tag_distance_point_point, + false +> +{ + typedef typename return_type<Strategy>::type return_type; + + static inline return_type apply(Point const& point, + Ring const& ring, + Strategy const& strategy) + { + typedef typename strategy::distance::services::default_strategy + < + segment_tag, + Point, + typename point_type<Ring>::type + >::type ps_strategy_type; + + std::pair<return_type, bool> + dc = detail::distance::point_to_ring + < + Point, Ring, + geometry::closure<Ring>::value, + Strategy, ps_strategy_type + >::apply(point, ring, strategy, ps_strategy_type()); + + return dc.second ? return_type(0) : dc.first; + } +}; + + +// Point-polygon , where point-segment strategy is specified +template <typename Point, typename Polygon, typename Strategy> +struct distance +< + Point, Polygon, Strategy, + point_tag, polygon_tag, strategy_tag_distance_point_point, + false +> +{ + typedef typename return_type<Strategy>::type return_type; + + static inline return_type apply(Point const& point, + Polygon const& polygon, + Strategy const& strategy) + { + typedef typename strategy::distance::services::default_strategy + < + segment_tag, + Point, + typename point_type<Polygon>::type + >::type ps_strategy_type; + + std::pair<return_type, bool> + dc = detail::distance::point_to_polygon + < + Point, Polygon, + geometry::closure<Polygon>::value, + Strategy, ps_strategy_type + >::apply(point, polygon, strategy, ps_strategy_type()); + + return dc.second ? return_type(0) : dc.first; + } +}; + + + +// Point-segment version 1, with point-point strategy +template <typename Point, typename Segment, typename Strategy> +struct distance +< + Point, Segment, Strategy, + point_tag, segment_tag, strategy_tag_distance_point_point, + false +> : detail::distance::point_to_segment<Point, Segment, Strategy> +{}; + +// Point-segment version 2, with point-segment strategy +template <typename Point, typename Segment, typename Strategy> +struct distance +< + Point, Segment, Strategy, + point_tag, segment_tag, strategy_tag_distance_point_segment, + false +> +{ + static inline typename return_type<Strategy>::type apply(Point const& point, + Segment const& segment, Strategy const& strategy) + { + + typename point_type<Segment>::type p[2]; + geometry::detail::assign_point_from_index<0>(segment, p[0]); + geometry::detail::assign_point_from_index<1>(segment, p[1]); + return strategy.apply(point, p[0], p[1]); + } +}; + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +/*! +\brief \brief_calc2{distance} \brief_strategy +\ingroup distance +\details +\details \details_calc{area}. \brief_strategy. \details_strategy_reasons + +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Strategy \tparam_strategy{Distance} +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param strategy \param_strategy{distance} +\return \return_calc{distance} +\note The strategy can be a point-point strategy. In case of distance point-line/point-polygon + it may also be a point-segment strategy. + +\qbk{distinguish,with strategy} + +\qbk{ +[heading Available Strategies] +\* [link geometry.reference.strategies.strategy_distance_pythagoras Pythagoras (cartesian)] +\* [link geometry.reference.strategies.strategy_distance_haversine Haversine (spherical)] +\* [link geometry.reference.strategies.strategy_distance_cross_track Cross track (spherical\, point-to-segment)] +\* [link geometry.reference.strategies.strategy_distance_projected_point Projected point (cartesian\, point-to-segment)] +\* more (currently extensions): Vincenty\, Andoyer (geographic) +} + */ + +/* +Note, in case of a Compilation Error: +if you get: + - "Failed to specialize function template ..." + - "error: no matching function for call to ..." +for distance, it is probably so that there is no specialization +for return_type<...> for your strategy. +*/ +template <typename Geometry1, typename Geometry2, typename Strategy> +inline typename strategy::distance::services::return_type<Strategy>::type distance( + Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + detail::throw_on_empty_input(geometry1); + detail::throw_on_empty_input(geometry2); + + return dispatch::distance + < + Geometry1, + Geometry2, + Strategy + >::apply(geometry1, geometry2, strategy); +} + + +/*! +\brief \brief_calc2{distance} +\ingroup distance +\details The default strategy is used, corresponding to the coordinate system of the geometries +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_calc{distance} + +\qbk{[include reference/algorithms/distance.qbk]} + */ +template <typename Geometry1, typename Geometry2> +inline typename default_distance_result<Geometry1, Geometry2>::type distance( + Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + return distance(geometry1, geometry2, + typename detail::distance::default_strategy<Geometry1, Geometry2>::type()); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DISTANCE_HPP diff --git a/boost/geometry/algorithms/envelope.hpp b/boost/geometry/algorithms/envelope.hpp new file mode 100644 index 0000000000..da34f6a783 --- /dev/null +++ b/boost/geometry/algorithms/envelope.hpp @@ -0,0 +1,273 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_ENVELOPE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_ENVELOPE_HPP + +#include <boost/mpl/assert.hpp> +#include <boost/range.hpp> + +#include <boost/numeric/conversion/cast.hpp> + +#include <boost/geometry/algorithms/assign.hpp> +#include <boost/geometry/algorithms/expand.hpp> +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace envelope +{ + + +/// Calculate envelope of an 2D or 3D segment +template<typename Geometry, typename Box> +struct envelope_expand_one +{ + static inline void apply(Geometry const& geometry, Box& mbr) + { + assign_inverse(mbr); + geometry::expand(mbr, geometry); + } +}; + + +/// Iterate through range (also used in multi*) +template<typename Range, typename Box> +inline void envelope_range_additional(Range const& range, Box& mbr) +{ + typedef typename boost::range_iterator<Range const>::type iterator_type; + + for (iterator_type it = boost::begin(range); + it != boost::end(range); + ++it) + { + geometry::expand(mbr, *it); + } +} + + + +/// Generic range dispatching struct +template <typename Range, typename Box> +struct envelope_range +{ + /// Calculate envelope of range using a strategy + static inline void apply(Range const& range, Box& mbr) + { + assign_inverse(mbr); + envelope_range_additional(range, mbr); + } +}; + +}} // namespace detail::envelope +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +// Note, the strategy is for future use (less/greater -> compare spherical +// using other methods), defaults are OK for now. +// However, they are already in the template methods + +template +< + typename Tag1, typename Tag2, + typename Geometry, typename Box, + typename StrategyLess, typename StrategyGreater +> +struct envelope +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + + +template +< + typename Point, typename Box, + typename StrategyLess, typename StrategyGreater +> +struct envelope + < + point_tag, box_tag, + Point, Box, + StrategyLess, StrategyGreater + > + : detail::envelope::envelope_expand_one<Point, Box> +{}; + + +template +< + typename BoxIn, typename BoxOut, + typename StrategyLess, typename StrategyGreater +> +struct envelope + < + box_tag, box_tag, + BoxIn, BoxOut, + StrategyLess, StrategyGreater + > + : detail::envelope::envelope_expand_one<BoxIn, BoxOut> +{}; + + +template +< + typename Segment, typename Box, + typename StrategyLess, typename StrategyGreater +> +struct envelope + < + segment_tag, box_tag, + Segment, Box, + StrategyLess, StrategyGreater + > + : detail::envelope::envelope_expand_one<Segment, Box> +{}; + + +template +< + typename Linestring, typename Box, + typename StrategyLess, typename StrategyGreater +> +struct envelope + < + linestring_tag, box_tag, + Linestring, Box, + StrategyLess, StrategyGreater + > + : detail::envelope::envelope_range<Linestring, Box> +{}; + + +template +< + typename Ring, typename Box, + typename StrategyLess, typename StrategyGreater +> +struct envelope + < + ring_tag, box_tag, + Ring, Box, + StrategyLess, StrategyGreater + > + : detail::envelope::envelope_range<Ring, Box> +{}; + + +template +< + typename Polygon, typename Box, + typename StrategyLess, typename StrategyGreater +> +struct envelope + < + polygon_tag, box_tag, + Polygon, Box, + StrategyLess, StrategyGreater + > +{ + static inline void apply(Polygon const& poly, Box& mbr) + { + // For polygon, inspecting outer ring is sufficient + + detail::envelope::envelope_range + < + typename ring_type<Polygon>::type, + Box + >::apply(exterior_ring(poly), mbr); + } + +}; + + +} // namespace dispatch +#endif + + +/*! +\brief \brief_calc{envelope} +\ingroup envelope +\details \details_calc{envelope,\det_envelope}. +\tparam Geometry \tparam_geometry +\tparam Box \tparam_box +\param geometry \param_geometry +\param mbr \param_box \param_set{envelope} + +\qbk{[include reference/algorithms/envelope.qbk]} +\qbk{ +[heading Example] +[envelope] [envelope_output] +} +*/ +template<typename Geometry, typename Box> +inline void envelope(Geometry const& geometry, Box& mbr) +{ + concept::check<Geometry const>(); + concept::check<Box>(); + + dispatch::envelope + < + typename tag<Geometry>::type, typename tag<Box>::type, + Geometry, Box, + void, void + >::apply(geometry, mbr); +} + + +/*! +\brief \brief_calc{envelope} +\ingroup envelope +\details \details_calc{return_envelope,\det_envelope}. \details_return{envelope} +\tparam Box \tparam_box +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_calc{envelope} + +\qbk{[include reference/algorithms/envelope.qbk]} +\qbk{ +[heading Example] +[return_envelope] [return_envelope_output] +} +*/ +template<typename Box, typename Geometry> +inline Box return_envelope(Geometry const& geometry) +{ + concept::check<Geometry const>(); + concept::check<Box>(); + + Box mbr; + dispatch::envelope + < + typename tag<Geometry>::type, typename tag<Box>::type, + Geometry, Box, + void, void + >::apply(geometry, mbr); + return mbr; +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_ENVELOPE_HPP diff --git a/boost/geometry/algorithms/equals.hpp b/boost/geometry/algorithms/equals.hpp new file mode 100644 index 0000000000..6b094f76d0 --- /dev/null +++ b/boost/geometry/algorithms/equals.hpp @@ -0,0 +1,319 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_EQUALS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_EQUALS_HPP + + +#include <cstddef> +#include <vector> + +#include <boost/mpl/if.hpp> +#include <boost/static_assert.hpp> +#include <boost/range.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/core/reverse_dispatch.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/detail/disjoint.hpp> +#include <boost/geometry/algorithms/detail/not.hpp> + +// For trivial checks +#include <boost/geometry/algorithms/area.hpp> +#include <boost/geometry/algorithms/length.hpp> +#include <boost/geometry/util/math.hpp> +#include <boost/geometry/util/select_coordinate_type.hpp> +#include <boost/geometry/util/select_most_precise.hpp> + +#include <boost/geometry/algorithms/detail/equals/collect_vectors.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace equals +{ + + +template +< + typename Box1, + typename Box2, + std::size_t Dimension, + std::size_t DimensionCount +> +struct box_box +{ + static inline bool apply(Box1 const& box1, Box2 const& box2) + { + if (!geometry::math::equals(get<min_corner, Dimension>(box1), get<min_corner, Dimension>(box2)) + || !geometry::math::equals(get<max_corner, Dimension>(box1), get<max_corner, Dimension>(box2))) + { + return false; + } + return box_box<Box1, Box2, Dimension + 1, DimensionCount>::apply(box1, box2); + } +}; + +template <typename Box1, typename Box2, std::size_t DimensionCount> +struct box_box<Box1, Box2, DimensionCount, DimensionCount> +{ + static inline bool apply(Box1 const& , Box2 const& ) + { + return true; + } +}; + + +struct area_check +{ + template <typename Geometry1, typename Geometry2> + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + return geometry::math::equals( + geometry::area(geometry1), + geometry::area(geometry2)); + } +}; + + +struct length_check +{ + template <typename Geometry1, typename Geometry2> + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + return geometry::math::equals( + geometry::length(geometry1), + geometry::length(geometry2)); + } +}; + + +template <typename Geometry1, typename Geometry2, typename TrivialCheck> +struct equals_by_collection +{ + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + if (! TrivialCheck::apply(geometry1, geometry2)) + { + return false; + } + + typedef typename geometry::select_most_precise + < + typename select_coordinate_type + < + Geometry1, Geometry2 + >::type, + double + >::type calculation_type; + + typedef std::vector<collected_vector<calculation_type> > v; + v c1, c2; + + geometry::collect_vectors(c1, geometry1); + geometry::collect_vectors(c2, geometry2); + + if (boost::size(c1) != boost::size(c2)) + { + return false; + } + + std::sort(c1.begin(), c1.end()); + std::sort(c2.begin(), c2.end()); + + // Just check if these vectors are equal. + return std::equal(c1.begin(), c1.end(), c2.begin()); + } +}; + + +}} // namespace detail::equals +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag1, typename Tag2, + typename Geometry1, + typename Geometry2, + std::size_t DimensionCount +> +struct equals +{}; + + +template <typename P1, typename P2, std::size_t DimensionCount> +struct equals<point_tag, point_tag, P1, P2, DimensionCount> + : geometry::detail::not_ + < + P1, + P2, + detail::disjoint::point_point<P1, P2, 0, DimensionCount> + > +{}; + + +template <typename Box1, typename Box2, std::size_t DimensionCount> +struct equals<box_tag, box_tag, Box1, Box2, DimensionCount> + : detail::equals::box_box<Box1, Box2, 0, DimensionCount> +{}; + + +template <typename Ring1, typename Ring2> +struct equals<ring_tag, ring_tag, Ring1, Ring2, 2> + : detail::equals::equals_by_collection + < + Ring1, Ring2, + detail::equals::area_check + > +{}; + + +template <typename Polygon1, typename Polygon2> +struct equals<polygon_tag, polygon_tag, Polygon1, Polygon2, 2> + : detail::equals::equals_by_collection + < + Polygon1, Polygon2, + detail::equals::area_check + > +{}; + + +template <typename LineString1, typename LineString2> +struct equals<linestring_tag, linestring_tag, LineString1, LineString2, 2> + : detail::equals::equals_by_collection + < + LineString1, LineString2, + detail::equals::length_check + > +{}; + + +template <typename Polygon, typename Ring> +struct equals<polygon_tag, ring_tag, Polygon, Ring, 2> + : detail::equals::equals_by_collection + < + Polygon, Ring, + detail::equals::area_check + > +{}; + + +template <typename Ring, typename Box> +struct equals<ring_tag, box_tag, Ring, Box, 2> + : detail::equals::equals_by_collection + < + Ring, Box, + detail::equals::area_check + > +{}; + + +template <typename Polygon, typename Box> +struct equals<polygon_tag, box_tag, Polygon, Box, 2> + : detail::equals::equals_by_collection + < + Polygon, Box, + detail::equals::area_check + > +{}; + + +template +< + typename Tag1, typename Tag2, + typename Geometry1, + typename Geometry2, + std::size_t DimensionCount +> +struct equals_reversed +{ + static inline bool apply(Geometry1 const& g1, Geometry2 const& g2) + { + return equals + < + Tag2, Tag1, + Geometry2, Geometry1, + DimensionCount + >::apply(g2, g1); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brief_check{are spatially equal} +\details \details_check12{equals, is spatially equal}. Spatially equal means + that the same point set is included. A box can therefore be spatially equal + to a ring or a polygon, or a linestring can be spatially equal to a + multi-linestring or a segment. This only theoretically, not all combinations + are implemented yet. +\ingroup equals +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_check2{are spatially equal} + +\qbk{[include reference/algorithms/equals.qbk]} + + */ +template <typename Geometry1, typename Geometry2> +inline bool equals(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check_concepts_and_equal_dimensions + < + Geometry1 const, + Geometry2 const + >(); + + return boost::mpl::if_c + < + reverse_dispatch<Geometry1, Geometry2>::type::value, + dispatch::equals_reversed + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, + Geometry2, + dimension<Geometry1>::type::value + >, + dispatch::equals + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, + Geometry2, + dimension<Geometry1>::type::value + > + >::type::apply(geometry1, geometry2); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_EQUALS_HPP + diff --git a/boost/geometry/algorithms/expand.hpp b/boost/geometry/algorithms/expand.hpp new file mode 100644 index 0000000000..da7442b593 --- /dev/null +++ b/boost/geometry/algorithms/expand.hpp @@ -0,0 +1,319 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_EXPAND_HPP +#define BOOST_GEOMETRY_ALGORITHMS_EXPAND_HPP + + +#include <cstddef> + +#include <boost/numeric/conversion/cast.hpp> + +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/util/select_coordinate_type.hpp> + +#include <boost/geometry/strategies/compare.hpp> +#include <boost/geometry/policies/compare.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace expand +{ + + +template +< + typename Box, typename Point, + typename StrategyLess, typename StrategyGreater, + std::size_t Dimension, std::size_t DimensionCount +> +struct point_loop +{ + typedef typename strategy::compare::detail::select_strategy + < + StrategyLess, 1, Point, Dimension + >::type less_type; + + typedef typename strategy::compare::detail::select_strategy + < + StrategyGreater, -1, Point, Dimension + >::type greater_type; + + typedef typename select_coordinate_type<Point, Box>::type coordinate_type; + + static inline void apply(Box& box, Point const& source) + { + less_type less; + greater_type greater; + + coordinate_type const coord = get<Dimension>(source); + + if (less(coord, get<min_corner, Dimension>(box))) + { + set<min_corner, Dimension>(box, coord); + } + + if (greater(coord, get<max_corner, Dimension>(box))) + { + set<max_corner, Dimension>(box, coord); + } + + point_loop + < + Box, Point, + StrategyLess, StrategyGreater, + Dimension + 1, DimensionCount + >::apply(box, source); + } +}; + + +template +< + typename Box, typename Point, + typename StrategyLess, typename StrategyGreater, + std::size_t DimensionCount +> +struct point_loop + < + Box, Point, + StrategyLess, StrategyGreater, + DimensionCount, DimensionCount + > +{ + static inline void apply(Box&, Point const&) {} +}; + + +template +< + typename Box, typename Geometry, + typename StrategyLess, typename StrategyGreater, + std::size_t Index, + std::size_t Dimension, std::size_t DimensionCount +> +struct indexed_loop +{ + typedef typename strategy::compare::detail::select_strategy + < + StrategyLess, 1, Box, Dimension + >::type less_type; + + typedef typename strategy::compare::detail::select_strategy + < + StrategyGreater, -1, Box, Dimension + >::type greater_type; + + typedef typename select_coordinate_type + < + Box, + Geometry + >::type coordinate_type; + + + static inline void apply(Box& box, Geometry const& source) + { + less_type less; + greater_type greater; + + coordinate_type const coord = get<Index, Dimension>(source); + + if (less(coord, get<min_corner, Dimension>(box))) + { + set<min_corner, Dimension>(box, coord); + } + + if (greater(coord, get<max_corner, Dimension>(box))) + { + set<max_corner, Dimension>(box, coord); + } + + indexed_loop + < + Box, Geometry, + StrategyLess, StrategyGreater, + Index, Dimension + 1, DimensionCount + >::apply(box, source); + } +}; + + +template +< + typename Box, typename Geometry, + typename StrategyLess, typename StrategyGreater, + std::size_t Index, std::size_t DimensionCount +> +struct indexed_loop + < + Box, Geometry, + StrategyLess, StrategyGreater, + Index, DimensionCount, DimensionCount + > +{ + static inline void apply(Box&, Geometry const&) {} +}; + + + +// Changes a box such that the other box is also contained by the box +template +< + typename Box, typename Geometry, + typename StrategyLess, typename StrategyGreater +> +struct expand_indexed +{ + static inline void apply(Box& box, Geometry const& geometry) + { + indexed_loop + < + Box, Geometry, + StrategyLess, StrategyGreater, + 0, 0, dimension<Geometry>::type::value + >::apply(box, geometry); + + indexed_loop + < + Box, Geometry, + StrategyLess, StrategyGreater, + 1, 0, dimension<Geometry>::type::value + >::apply(box, geometry); + } +}; + +}} // namespace detail::expand +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag, + typename BoxOut, typename Geometry, + typename StrategyLess, typename StrategyGreater +> +struct expand +{}; + + +// Box + point -> new box containing also point +template +< + typename BoxOut, typename Point, + typename StrategyLess, typename StrategyGreater +> +struct expand<point_tag, BoxOut, Point, StrategyLess, StrategyGreater> + : detail::expand::point_loop + < + BoxOut, Point, + StrategyLess, StrategyGreater, + 0, dimension<Point>::type::value + > +{}; + + +// Box + box -> new box containing two input boxes +template +< + typename BoxOut, typename BoxIn, + typename StrategyLess, typename StrategyGreater +> +struct expand<box_tag, BoxOut, BoxIn, StrategyLess, StrategyGreater> + : detail::expand::expand_indexed + <BoxOut, BoxIn, StrategyLess, StrategyGreater> +{}; + +template +< + typename Box, typename Segment, + typename StrategyLess, typename StrategyGreater +> +struct expand<segment_tag, Box, Segment, StrategyLess, StrategyGreater> + : detail::expand::expand_indexed + <Box, Segment, StrategyLess, StrategyGreater> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*** +*! +\brief Expands a box using the extend (envelope) of another geometry (box, point) +\ingroup expand +\tparam Box type of the box +\tparam Geometry of second geometry, to be expanded with the box +\param box box to expand another geometry with, might be changed +\param geometry other geometry +\param strategy_less +\param strategy_greater +\note Strategy is currently ignored + * +template +< + typename Box, typename Geometry, + typename StrategyLess, typename StrategyGreater +> +inline void expand(Box& box, Geometry const& geometry, + StrategyLess const& strategy_less, + StrategyGreater const& strategy_greater) +{ + concept::check_concepts_and_equal_dimensions<Box, Geometry const>(); + + dispatch::expand + < + typename tag<Geometry>::type, + Box, + Geometry, + StrategyLess, StrategyGreater + >::apply(box, geometry); +} +***/ + + +/*! +\brief Expands a box using the bounding box (envelope) of another geometry (box, point) +\ingroup expand +\tparam Box type of the box +\tparam Geometry \tparam_geometry +\param box box to be expanded using another geometry, mutable +\param geometry \param_geometry geometry which envelope (bounding box) will be added to the box + +\qbk{[include reference/algorithms/expand.qbk]} + */ +template <typename Box, typename Geometry> +inline void expand(Box& box, Geometry const& geometry) +{ + concept::check_concepts_and_equal_dimensions<Box, Geometry const>(); + + dispatch::expand + < + typename tag<Geometry>::type, + Box, Geometry, + strategy::compare::default_strategy, + strategy::compare::default_strategy + >::apply(box, geometry); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_EXPAND_HPP diff --git a/boost/geometry/algorithms/for_each.hpp b/boost/geometry/algorithms/for_each.hpp new file mode 100644 index 0000000000..671f26a70d --- /dev/null +++ b/boost/geometry/algorithms/for_each.hpp @@ -0,0 +1,358 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_FOR_EACH_HPP +#define BOOST_GEOMETRY_ALGORITHMS_FOR_EACH_HPP + + +#include <algorithm> + +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/tag_cast.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/geometries/segment.hpp> + +#include <boost/geometry/util/add_const_if_c.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace for_each +{ + + +template <typename Point, typename Functor, bool IsConst> +struct fe_point_per_point +{ + static inline Functor apply( + typename add_const_if_c<IsConst, Point>::type& point, Functor f) + { + f(point); + return f; + } +}; + + +template <typename Point, typename Functor, bool IsConst> +struct fe_point_per_segment +{ + static inline Functor apply( + typename add_const_if_c<IsConst, Point>::type& , Functor f) + { + // TODO: if non-const, we should extract the points from the segment + // and call the functor on those two points + return f; + } +}; + + +template <typename Range, typename Functor, bool IsConst> +struct fe_range_per_point +{ + static inline Functor apply( + typename add_const_if_c<IsConst, Range>::type& range, + Functor f) + { + return (std::for_each(boost::begin(range), boost::end(range), f)); + } +}; + + +template <typename Range, typename Functor, bool IsConst> +struct fe_range_per_segment +{ + static inline Functor apply( + typename add_const_if_c<IsConst, Range>::type& range, + Functor f) + { + typedef typename add_const_if_c + < + IsConst, + typename point_type<Range>::type + >::type point_type; + + BOOST_AUTO_TPL(it, boost::begin(range)); + BOOST_AUTO_TPL(previous, it++); + while(it != boost::end(range)) + { + model::referring_segment<point_type> s(*previous, *it); + f(s); + previous = it++; + } + + return f; + } +}; + + +template <typename Polygon, typename Functor, bool IsConst> +struct fe_polygon_per_point +{ + typedef typename add_const_if_c<IsConst, Polygon>::type poly_type; + + static inline Functor apply(poly_type& poly, Functor f) + { + typedef fe_range_per_point + < + typename ring_type<Polygon>::type, + Functor, + IsConst + > per_ring; + + f = per_ring::apply(exterior_ring(poly), f); + + typename interior_return_type<poly_type>::type rings + = interior_rings(poly); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + f = per_ring::apply(*it, f); + } + + return f; + } + +}; + + +template <typename Polygon, typename Functor, bool IsConst> +struct fe_polygon_per_segment +{ + typedef typename add_const_if_c<IsConst, Polygon>::type poly_type; + + static inline Functor apply(poly_type& poly, Functor f) + { + typedef fe_range_per_segment + < + typename ring_type<Polygon>::type, + Functor, + IsConst + > per_ring; + + f = per_ring::apply(exterior_ring(poly), f); + + typename interior_return_type<poly_type>::type rings + = interior_rings(poly); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + f = per_ring::apply(*it, f); + } + + return f; + } + +}; + + +}} // namespace detail::for_each +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag, + typename Geometry, + typename Functor, + bool IsConst +> +struct for_each_point {}; + + +template <typename Point, typename Functor, bool IsConst> +struct for_each_point<point_tag, Point, Functor, IsConst> + : detail::for_each::fe_point_per_point<Point, Functor, IsConst> +{}; + + +template <typename Linestring, typename Functor, bool IsConst> +struct for_each_point<linestring_tag, Linestring, Functor, IsConst> + : detail::for_each::fe_range_per_point<Linestring, Functor, IsConst> +{}; + + +template <typename Ring, typename Functor, bool IsConst> +struct for_each_point<ring_tag, Ring, Functor, IsConst> + : detail::for_each::fe_range_per_point<Ring, Functor, IsConst> +{}; + + +template <typename Polygon, typename Functor, bool IsConst> +struct for_each_point<polygon_tag, Polygon, Functor, IsConst> + : detail::for_each::fe_polygon_per_point<Polygon, Functor, IsConst> +{}; + + +template +< + typename Tag, + typename Geometry, + typename Functor, + bool IsConst +> +struct for_each_segment {}; + +template <typename Point, typename Functor, bool IsConst> +struct for_each_segment<point_tag, Point, Functor, IsConst> + : detail::for_each::fe_point_per_segment<Point, Functor, IsConst> +{}; + + +template <typename Linestring, typename Functor, bool IsConst> +struct for_each_segment<linestring_tag, Linestring, Functor, IsConst> + : detail::for_each::fe_range_per_segment<Linestring, Functor, IsConst> +{}; + + +template <typename Ring, typename Functor, bool IsConst> +struct for_each_segment<ring_tag, Ring, Functor, IsConst> + : detail::for_each::fe_range_per_segment<Ring, Functor, IsConst> +{}; + + +template <typename Polygon, typename Functor, bool IsConst> +struct for_each_segment<polygon_tag, Polygon, Functor, IsConst> + : detail::for_each::fe_polygon_per_segment<Polygon, Functor, IsConst> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brf_for_each{point} +\details \det_for_each{point} +\ingroup for_each +\param geometry \param_geometry +\param f \par_for_each_f{const point} +\tparam Geometry \tparam_geometry +\tparam Functor \tparam_functor + +\qbk{distinguish,const version} +\qbk{[include reference/algorithms/for_each_point.qbk]} +\qbk{[heading Example]} +\qbk{[for_each_point_const] [for_each_point_const_output]} +*/ +template<typename Geometry, typename Functor> +inline Functor for_each_point(Geometry const& geometry, Functor f) +{ + concept::check<Geometry const>(); + + return dispatch::for_each_point + < + typename tag_cast<typename tag<Geometry>::type, multi_tag>::type, + Geometry, + Functor, + true + >::apply(geometry, f); +} + + +/*! +\brief \brf_for_each{point} +\details \det_for_each{point} +\ingroup for_each +\param geometry \param_geometry +\param f \par_for_each_f{point} +\tparam Geometry \tparam_geometry +\tparam Functor \tparam_functor + +\qbk{[include reference/algorithms/for_each_point.qbk]} +\qbk{[heading Example]} +\qbk{[for_each_point] [for_each_point_output]} +*/ +template<typename Geometry, typename Functor> +inline Functor for_each_point(Geometry& geometry, Functor f) +{ + concept::check<Geometry>(); + + return dispatch::for_each_point + < + typename tag_cast<typename tag<Geometry>::type, multi_tag>::type, + Geometry, + Functor, + false + >::apply(geometry, f); +} + + +/*! +\brief \brf_for_each{segment} +\details \det_for_each{segment} +\ingroup for_each +\param geometry \param_geometry +\param f \par_for_each_f{const segment} +\tparam Geometry \tparam_geometry +\tparam Functor \tparam_functor + +\qbk{distinguish,const version} +\qbk{[include reference/algorithms/for_each_segment.qbk]} +\qbk{[heading Example]} +\qbk{[for_each_segment_const] [for_each_segment_const_output]} +*/ +template<typename Geometry, typename Functor> +inline Functor for_each_segment(Geometry const& geometry, Functor f) +{ + concept::check<Geometry const>(); + + return dispatch::for_each_segment + < + typename tag_cast<typename tag<Geometry>::type, multi_tag>::type, + Geometry, + Functor, + true + >::apply(geometry, f); +} + + +/*! +\brief \brf_for_each{segment} +\details \det_for_each{segment} +\ingroup for_each +\param geometry \param_geometry +\param f \par_for_each_f{segment} +\tparam Geometry \tparam_geometry +\tparam Functor \tparam_functor + +\qbk{[include reference/algorithms/for_each_segment.qbk]} +*/ +template<typename Geometry, typename Functor> +inline Functor for_each_segment(Geometry& geometry, Functor f) +{ + concept::check<Geometry>(); + + return dispatch::for_each_segment + < + typename tag_cast<typename tag<Geometry>::type, multi_tag>::type, + Geometry, + Functor, + false + >::apply(geometry, f); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_FOR_EACH_HPP diff --git a/boost/geometry/algorithms/intersection.hpp b/boost/geometry/algorithms/intersection.hpp new file mode 100644 index 0000000000..8d3dd68b3a --- /dev/null +++ b/boost/geometry/algorithms/intersection.hpp @@ -0,0 +1,237 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_INTERSECTION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_INTERSECTION_HPP + + +#include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/algorithms/detail/overlay/intersection_insert.hpp> +#include <boost/geometry/algorithms/intersects.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace intersection +{ + +template +< + typename Box1, typename Box2, + typename BoxOut, + typename Strategy, + std::size_t Dimension, std::size_t DimensionCount +> +struct intersection_box_box +{ + static inline bool apply(Box1 const& box1, + Box2 const& box2, BoxOut& box_out, + Strategy const& strategy) + { + typedef typename coordinate_type<BoxOut>::type ct; + + ct min1 = get<min_corner, Dimension>(box1); + ct min2 = get<min_corner, Dimension>(box2); + ct max1 = get<max_corner, Dimension>(box1); + ct max2 = get<max_corner, Dimension>(box2); + + if (max1 < min2 || max2 < min1) + { + return false; + } + // Set dimensions of output coordinate + set<min_corner, Dimension>(box_out, min1 < min2 ? min2 : min1); + set<max_corner, Dimension>(box_out, max1 > max2 ? max2 : max1); + + return intersection_box_box + < + Box1, Box2, BoxOut, Strategy, + Dimension + 1, DimensionCount + >::apply(box1, box2, box_out, strategy); + } +}; + +template +< + typename Box1, typename Box2, + typename BoxOut, + typename Strategy, + std::size_t DimensionCount +> +struct intersection_box_box<Box1, Box2, BoxOut, Strategy, DimensionCount, DimensionCount> +{ + static inline bool apply(Box1 const&, Box2 const&, BoxOut&, Strategy const&) + { + return true; + } +}; + + +}} // namespace detail::intersection +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// By default, all is forwarded to the intersection_insert-dispatcher +template +< + typename Tag1, typename Tag2, typename TagOut, + typename Geometry1, typename Geometry2, + typename GeometryOut, + typename Strategy +> +struct intersection +{ + typedef std::back_insert_iterator<GeometryOut> output_iterator; + + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) + { + typedef typename boost::range_value<GeometryOut>::type OneOut; + + intersection_insert + < + Tag1, Tag2, typename geometry::tag<OneOut>::type, + geometry::is_areal<Geometry1>::value, + geometry::is_areal<Geometry2>::value, + geometry::is_areal<OneOut>::value, + Geometry1, Geometry2, + detail::overlay::do_reverse<geometry::point_order<Geometry1>::value, false>::value, + detail::overlay::do_reverse<geometry::point_order<Geometry2>::value, false>::value, + detail::overlay::do_reverse<geometry::point_order<OneOut>::value>::value, + output_iterator, OneOut, + overlay_intersection, + Strategy + >::apply(geometry1, geometry2, std::back_inserter(geometry_out), strategy); + + return true; + } + +}; + + +template +< + typename Box1, typename Box2, + typename BoxOut, + typename Strategy +> +struct intersection + < + box_tag, box_tag, box_tag, + Box1, Box2, BoxOut, + Strategy + > : public detail::intersection::intersection_box_box + < + Box1, Box2, BoxOut, + Strategy, + 0, geometry::dimension<Box1>::value + > +{}; + + +template +< + typename Tag1, typename Tag2, typename TagOut, + typename Geometry1, typename Geometry2, + typename GeometryOut, + typename Strategy +> +struct intersection_reversed +{ + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) + { + return intersection + < + Tag2, Tag1, TagOut, + Geometry2, Geometry1, + GeometryOut, Strategy + >::apply(geometry2, geometry1, geometry_out, strategy); + } +}; + + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brief_calc2{intersection} +\ingroup intersection +\details \details_calc2{intersection, spatial set theoretic intersection}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam GeometryOut Collection of geometries (e.g. std::vector, std::deque, boost::geometry::multi*) of which + the value_type fulfills a \p_l_or_c concept, or it is the output geometry (e.g. for a box) +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param geometry_out The output geometry, either a multi_point, multi_polygon, + multi_linestring, or a box (for intersection of two boxes) + +\qbk{[include reference/algorithms/intersection.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename GeometryOut +> +inline bool intersection(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + typedef strategy_intersection + < + typename cs_tag<Geometry1>::type, + Geometry1, + Geometry2, + typename geometry::point_type<Geometry1>::type + > strategy; + + + return boost::mpl::if_c + < + geometry::reverse_dispatch<Geometry1, Geometry2>::type::value, + dispatch::intersection_reversed + < + typename geometry::tag<Geometry1>::type, + typename geometry::tag<Geometry2>::type, + typename geometry::tag<GeometryOut>::type, + Geometry1, Geometry2, GeometryOut, strategy + >, + dispatch::intersection + < + typename geometry::tag<Geometry1>::type, + typename geometry::tag<Geometry2>::type, + typename geometry::tag<GeometryOut>::type, + Geometry1, Geometry2, GeometryOut, strategy + > + >::type::apply(geometry1, geometry2, geometry_out, strategy()); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_INTERSECTION_HPP diff --git a/boost/geometry/algorithms/intersects.hpp b/boost/geometry/algorithms/intersects.hpp new file mode 100644 index 0000000000..f367f2e258 --- /dev/null +++ b/boost/geometry/algorithms/intersects.hpp @@ -0,0 +1,106 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_INTERSECTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_INTERSECTS_HPP + + +#include <deque> + +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/algorithms/detail/overlay/self_turn_points.hpp> +#include <boost/geometry/algorithms/disjoint.hpp> + + +namespace boost { namespace geometry +{ + +/*! +\brief \brief_check{has at least one intersection (crossing or self-tangency)} +\note This function can be called for one geometry (self-intersection) and + also for two geometries (intersection) +\ingroup intersects +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_check{is self-intersecting} + +\qbk{distinguish,one geometry} +\qbk{[def __one_parameter__]} +\qbk{[include reference/algorithms/intersects.qbk]} +*/ +template <typename Geometry> +inline bool intersects(Geometry const& geometry) +{ + concept::check<Geometry const>(); + + + typedef detail::overlay::turn_info + < + typename geometry::point_type<Geometry>::type + > turn_info; + std::deque<turn_info> turns; + + typedef typename strategy_intersection + < + typename cs_tag<Geometry>::type, + Geometry, + Geometry, + typename geometry::point_type<Geometry>::type + >::segment_intersection_strategy_type segment_intersection_strategy_type; + + typedef detail::overlay::get_turn_info + < + typename point_type<Geometry>::type, + typename point_type<Geometry>::type, + turn_info, + detail::overlay::assign_null_policy + > TurnPolicy; + + detail::disjoint::disjoint_interrupt_policy policy; + detail::self_get_turn_points::get_turns + < + Geometry, + std::deque<turn_info>, + TurnPolicy, + detail::disjoint::disjoint_interrupt_policy + >::apply(geometry, turns, policy); + return policy.has_intersections; +} + + +/*! +\brief \brief_check2{have at least one intersection} +\ingroup intersects +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_check2{intersect each other} + +\qbk{distinguish,two geometries} +\qbk{[include reference/algorithms/intersects.qbk]} + */ +template <typename Geometry1, typename Geometry2> +inline bool intersects(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + return ! geometry::disjoint(geometry1, geometry2); +} + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_INTERSECTS_HPP diff --git a/boost/geometry/algorithms/length.hpp b/boost/geometry/algorithms/length.hpp new file mode 100644 index 0000000000..de53a39e8f --- /dev/null +++ b/boost/geometry/algorithms/length.hpp @@ -0,0 +1,204 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_LENGTH_HPP +#define BOOST_GEOMETRY_ALGORITHMS_LENGTH_HPP + +#include <iterator> + +#include <boost/range.hpp> + +#include <boost/mpl/if.hpp> +#include <boost/type_traits.hpp> + +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/closure.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +#include <boost/geometry/algorithms/assign.hpp> +#include <boost/geometry/algorithms/detail/calculate_null.hpp> +// #include <boost/geometry/algorithms/detail/throw_on_empty_input.hpp> +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/strategies/distance.hpp> +#include <boost/geometry/strategies/default_length_result.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace length +{ + + +template<typename Segment, typename Strategy> +struct segment_length +{ + static inline typename default_length_result<Segment>::type apply( + Segment const& segment, Strategy const& strategy) + { + typedef typename point_type<Segment>::type point_type; + point_type p1, p2; + geometry::detail::assign_point_from_index<0>(segment, p1); + geometry::detail::assign_point_from_index<1>(segment, p2); + return strategy.apply(p1, p2); + } +}; + +/*! +\brief Internal, calculates length of a linestring using iterator pairs and + specified strategy +\note for_each could be used here, now that point_type is changed by boost + range iterator +*/ +template<typename Range, typename Strategy, closure_selector Closure> +struct range_length +{ + typedef typename default_length_result<Range>::type return_type; + + static inline return_type apply( + Range const& range, Strategy const& strategy) + { + typedef typename closeable_view<Range const, Closure>::type view_type; + typedef typename boost::range_iterator + < + view_type const + >::type iterator_type; + + return_type sum = return_type(); + view_type view(range); + iterator_type it = boost::begin(view), end = boost::end(view); + if(it != end) + { + for(iterator_type previous = it++; + it != end; + ++previous, ++it) + { + // Add point-point distance using the return type belonging + // to strategy + sum += strategy.apply(*previous, *it); + } + } + + return sum; + } +}; + + +}} // namespace detail::length +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template <typename Tag, typename Geometry, typename Strategy> +struct length : detail::calculate_null + < + typename default_length_result<Geometry>::type, + Geometry, + Strategy + > +{}; + + +template <typename Geometry, typename Strategy> +struct length<linestring_tag, Geometry, Strategy> + : detail::length::range_length<Geometry, Strategy, closed> +{}; + + +// RING: length is currently 0; it might be argued that it is the "perimeter" + + +template <typename Geometry, typename Strategy> +struct length<segment_tag, Geometry, Strategy> + : detail::length::segment_length<Geometry, Strategy> +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brief_calc{length} +\ingroup length +\details \details_calc{length, length (the sum of distances between consecutive points)}. \details_default_strategy +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_calc{length} + +\qbk{[include reference/algorithms/length.qbk]} +\qbk{[length] [length_output]} + */ +template<typename Geometry> +inline typename default_length_result<Geometry>::type length( + Geometry const& geometry) +{ + concept::check<Geometry const>(); + + // detail::throw_on_empty_input(geometry); + + typedef typename strategy::distance::services::default_strategy + < + point_tag, typename point_type<Geometry>::type + >::type strategy_type; + + return dispatch::length + < + typename tag<Geometry>::type, + Geometry, + strategy_type + >::apply(geometry, strategy_type()); +} + + +/*! +\brief \brief_calc{length} \brief_strategy +\ingroup length +\details \details_calc{length, length (the sum of distances between consecutive points)} \brief_strategy. \details_strategy_reasons +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{distance} +\param geometry \param_geometry +\param strategy \param_strategy{distance} +\return \return_calc{length} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/length.qbk]} +\qbk{[length_with_strategy] [length_with_strategy_output]} + */ +template<typename Geometry, typename Strategy> +inline typename default_length_result<Geometry>::type length( + Geometry const& geometry, Strategy const& strategy) +{ + concept::check<Geometry const>(); + + // detail::throw_on_empty_input(geometry); + + return dispatch::length + < + typename tag<Geometry>::type, + Geometry, + Strategy + >::apply(geometry, strategy); +} + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_LENGTH_HPP diff --git a/boost/geometry/algorithms/make.hpp b/boost/geometry/algorithms/make.hpp new file mode 100644 index 0000000000..d0e3092492 --- /dev/null +++ b/boost/geometry/algorithms/make.hpp @@ -0,0 +1,200 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_MAKE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_MAKE_HPP + +#include <boost/geometry/algorithms/assign.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace make +{ + +/*! +\brief Construct a geometry +\ingroup make +\tparam Geometry \tparam_geometry +\tparam Range \tparam_range_point +\param range \param_range_point +\return The constructed geometry, here: a linestring or a ring + +\qbk{distinguish, with a range} +\qbk{ +[heading Example] +[make_with_range] [make_with_range_output] + +[heading See also] +\* [link geometry.reference.algorithms.assign.assign_points assign] +} + */ +template <typename Geometry, typename Range> +inline Geometry make_points(Range const& range) +{ + concept::check<Geometry>(); + + Geometry geometry; + geometry::append(geometry, range); + return geometry; +} + +}} // namespace detail::make +#endif // DOXYGEN_NO_DETAIL + +/*! +\brief Construct a geometry +\ingroup make +\details +\note It does not work with array-point types, like int[2] +\tparam Geometry \tparam_geometry +\tparam Type \tparam_numeric to specify the coordinates +\param c1 \param_x +\param c2 \param_y +\return The constructed geometry, here: a 2D point + +\qbk{distinguish, 2 coordinate values} +\qbk{ +[heading Example] +[make_2d_point] [make_2d_point_output] + +[heading See also] +\* [link geometry.reference.algorithms.assign.assign_values_3_2_coordinate_values assign] +} +*/ +template <typename Geometry, typename Type> +inline Geometry make(Type const& c1, Type const& c2) +{ + concept::check<Geometry>(); + + Geometry geometry; + dispatch::assign + < + typename tag<Geometry>::type, + Geometry, + geometry::dimension<Geometry>::type::value + >::apply(geometry, c1, c2); + return geometry; +} + +/*! +\brief Construct a geometry +\ingroup make +\tparam Geometry \tparam_geometry +\tparam Type \tparam_numeric to specify the coordinates +\param c1 \param_x +\param c2 \param_y +\param c3 \param_z +\return The constructed geometry, here: a 3D point + +\qbk{distinguish, 3 coordinate values} +\qbk{ +[heading Example] +[make_3d_point] [make_3d_point_output] + +[heading See also] +\* [link geometry.reference.algorithms.assign.assign_values_4_3_coordinate_values assign] +} + */ +template <typename Geometry, typename Type> +inline Geometry make(Type const& c1, Type const& c2, Type const& c3) +{ + concept::check<Geometry>(); + + Geometry geometry; + dispatch::assign + < + typename tag<Geometry>::type, + Geometry, + geometry::dimension<Geometry>::type::value + >::apply(geometry, c1, c2, c3); + return geometry; +} + +template <typename Geometry, typename Type> +inline Geometry make(Type const& c1, Type const& c2, Type const& c3, Type const& c4) +{ + concept::check<Geometry>(); + + Geometry geometry; + dispatch::assign + < + typename tag<Geometry>::type, + Geometry, + geometry::dimension<Geometry>::type::value + >::apply(geometry, c1, c2, c3, c4); + return geometry; +} + + + + + +/*! +\brief Construct a box with inverse infinite coordinates +\ingroup make +\details The make_inverse function initializes a 2D or 3D box with large coordinates, the + min corner is very large, the max corner is very small. This is useful e.g. in combination + with the expand function, to determine the bounding box of a series of geometries. +\tparam Geometry \tparam_geometry +\return The constructed geometry, here: a box + +\qbk{ +[heading Example] +[make_inverse] [make_inverse_output] + +[heading See also] +\* [link geometry.reference.algorithms.assign.assign_inverse assign_inverse] +} + */ +template <typename Geometry> +inline Geometry make_inverse() +{ + concept::check<Geometry>(); + + Geometry geometry; + dispatch::assign_inverse + < + typename tag<Geometry>::type, + Geometry + >::apply(geometry); + return geometry; +} + +/*! +\brief Construct a geometry with its coordinates initialized to zero +\ingroup make +\details The make_zero function initializes a 2D or 3D point or box with coordinates of zero +\tparam Geometry \tparam_geometry +\return The constructed and zero-initialized geometry + */ +template <typename Geometry> +inline Geometry make_zero() +{ + concept::check<Geometry>(); + + Geometry geometry; + dispatch::assign_zero + < + typename tag<Geometry>::type, + Geometry + >::apply(geometry); + return geometry; +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_MAKE_HPP diff --git a/boost/geometry/algorithms/not_implemented.hpp b/boost/geometry/algorithms/not_implemented.hpp new file mode 100644 index 0000000000..008f111cc8 --- /dev/null +++ b/boost/geometry/algorithms/not_implemented.hpp @@ -0,0 +1,117 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef BOOST_GEOMETRY_ALGORITHMS_NOT_IMPLEMENTED_HPP +#define BOOST_GEOMETRY_ALGORITHMS_NOT_IMPLEMENTED_HPP + + +#include <boost/mpl/assert.hpp> +#include <boost/geometry/core/tags.hpp> +#include <boost/geometry/multi/core/tags.hpp> + + +namespace boost { namespace geometry +{ + + +namespace info +{ + struct UNRECOGNIZED_GEOMETRY_TYPE {}; + struct POINT {}; + struct LINESTRING {}; + struct POLYGON {}; + struct RING {}; + struct BOX {}; + struct SEGMENT {}; + struct MULTI_POINT {}; + struct MULTI_LINESTRING {}; + struct MULTI_POLYGON {}; + struct GEOMETRY_COLLECTION {}; + template <size_t D> struct DIMENSION {}; +} + + +namespace nyi +{ + + +struct not_implemented_tag {}; + +template +< + typename Term1, + typename Term2, + typename Term3 +> +struct not_implemented_error +{ + +#ifndef BOOST_GEOMETRY_IMPLEMENTATION_STATUS_BUILD +# define BOOST_GEOMETRY_IMPLEMENTATION_STATUS_BUILD false +#endif + + BOOST_MPL_ASSERT_MSG + ( + BOOST_GEOMETRY_IMPLEMENTATION_STATUS_BUILD, + THIS_OPERATION_IS_NOT_OR_NOT_YET_IMPLEMENTED, + ( + types<Term1, Term2, Term3> + ) + ); +}; + +template <typename Tag> +struct tag_to_term +{ + typedef Tag type; +}; + +template <> struct tag_to_term<geometry_not_recognized_tag> { typedef info::UNRECOGNIZED_GEOMETRY_TYPE type; }; +template <> struct tag_to_term<point_tag> { typedef info::POINT type; }; +template <> struct tag_to_term<linestring_tag> { typedef info::LINESTRING type; }; +template <> struct tag_to_term<polygon_tag> { typedef info::POLYGON type; }; +template <> struct tag_to_term<ring_tag> { typedef info::RING type; }; +template <> struct tag_to_term<box_tag> { typedef info::BOX type; }; +template <> struct tag_to_term<segment_tag> { typedef info::SEGMENT type; }; +template <> struct tag_to_term<multi_point_tag> { typedef info::MULTI_POINT type; }; +template <> struct tag_to_term<multi_linestring_tag> { typedef info::MULTI_LINESTRING type; }; +template <> struct tag_to_term<multi_polygon_tag> { typedef info::MULTI_POLYGON type; }; +template <> struct tag_to_term<geometry_collection_tag> { typedef info::GEOMETRY_COLLECTION type; }; +template <int D> struct tag_to_term<mpl::int_<D> > { typedef info::DIMENSION<D> type; }; + + +} + + +template +< + typename Term1 = void, + typename Term2 = void, + typename Term3 = void +> +struct not_implemented + : nyi::not_implemented_tag, + nyi::not_implemented_error + < + typename mpl::identity<typename nyi::tag_to_term<Term1>::type>::type, + typename mpl::identity<typename nyi::tag_to_term<Term2>::type>::type, + typename mpl::identity<typename nyi::tag_to_term<Term3>::type>::type + > +{}; + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_NOT_IMPLEMENTED_HPP diff --git a/boost/geometry/algorithms/num_geometries.hpp b/boost/geometry/algorithms/num_geometries.hpp new file mode 100644 index 0000000000..20f35e90d2 --- /dev/null +++ b/boost/geometry/algorithms/num_geometries.hpp @@ -0,0 +1,95 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + + +#ifndef BOOST_GEOMETRY_ALGORITHMS_NUM_GEOMETRIES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_NUM_GEOMETRIES_HPP + +#include <cstddef> + +#include <boost/mpl/assert.hpp> + +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> +#include <boost/geometry/core/tag_cast.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template <typename Tag, typename Geometry> +struct num_geometries +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + + +template <typename Geometry> +struct num_geometries<single_tag, Geometry> +{ + static inline std::size_t apply(Geometry const&) + { + return 1; + } +}; + + + +} // namespace dispatch +#endif + + +/*! +\brief \brief_calc{number of geometries} +\ingroup num_geometries +\details \details_calc{num_geometries, number of geometries}. +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_calc{number of geometries} + +\qbk{[include reference/algorithms/num_geometries.qbk]} +*/ +template <typename Geometry> +inline std::size_t num_geometries(Geometry const& geometry) +{ + concept::check<Geometry const>(); + + return dispatch::num_geometries + < + typename tag_cast + < + typename tag<Geometry>::type, + single_tag, + multi_tag + >::type, + Geometry + >::apply(geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_NUM_GEOMETRIES_HPP diff --git a/boost/geometry/algorithms/num_interior_rings.hpp b/boost/geometry/algorithms/num_interior_rings.hpp new file mode 100644 index 0000000000..2149f46576 --- /dev/null +++ b/boost/geometry/algorithms/num_interior_rings.hpp @@ -0,0 +1,88 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_NUM_INTERIOR_RINGS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_NUM_INTERIOR_RINGS_HPP + +#include <cstddef> + +#include <boost/range.hpp> + +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/core/interior_rings.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template <typename Tag, typename Geometry> +struct num_interior_rings +{ + static inline std::size_t apply(Geometry const& ) + { + return 0; + } +}; + + + +template <typename Polygon> +struct num_interior_rings<polygon_tag, Polygon> +{ + static inline std::size_t apply(Polygon const& polygon) + { + return boost::size(geometry::interior_rings(polygon)); + } + +}; + + +} // namespace dispatch +#endif + + +/*! +\brief \brief_calc{number of interior rings} +\ingroup num_interior_rings +\details \details_calc{num_interior_rings, number of interior rings}. +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_calc{number of interior rings} + +\qbk{[include reference/algorithms/num_interior_rings.qbk]} + +\note Defined by OGC as "numInteriorRing". To be consistent with "numPoints" + letter "s" is appended +*/ +template <typename Geometry> +inline std::size_t num_interior_rings(Geometry const& geometry) +{ + return dispatch::num_interior_rings + < + typename tag<Geometry>::type, + Geometry + >::apply(geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_NUM_INTERIOR_RINGS_HPP diff --git a/boost/geometry/algorithms/num_points.hpp b/boost/geometry/algorithms/num_points.hpp new file mode 100644 index 0000000000..c480068f43 --- /dev/null +++ b/boost/geometry/algorithms/num_points.hpp @@ -0,0 +1,171 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_NUM_POINTS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_NUM_POINTS_HPP + +#include <cstddef> + +#include <boost/mpl/assert.hpp> +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/ring_type.hpp> +#include <boost/geometry/core/tag_cast.hpp> + +#include <boost/geometry/algorithms/disjoint.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace num_points +{ + + +template <typename Range> +struct range_count +{ + static inline std::size_t apply(Range const& range, bool add_for_open) + { + std::size_t n = boost::size(range); + if (add_for_open && n > 0) + { + closure_selector const s = geometry::closure<Range>::value; + + if (s == open) + { + if (geometry::disjoint(*boost::begin(range), *(boost::begin(range) + n - 1))) + { + return n + 1; + } + } + } + return n; + } +}; + +template <typename Geometry, std::size_t D> +struct other_count +{ + static inline std::size_t apply(Geometry const&, bool) + { + return D; + } +}; + +template <typename Polygon> +struct polygon_count +{ + static inline std::size_t apply(Polygon const& poly, bool add_for_open) + { + typedef typename geometry::ring_type<Polygon>::type ring_type; + + std::size_t n = range_count<ring_type>::apply( + exterior_ring(poly), add_for_open); + + typename interior_return_type<Polygon const>::type rings + = interior_rings(poly); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + n += range_count<ring_type>::apply(*it, add_for_open); + } + + return n; + } +}; + +}} // namespace detail::num_points +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template <typename GeometryTag, typename Geometry> +struct num_points +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry>) + ); +}; + +template <typename Geometry> +struct num_points<point_tag, Geometry> + : detail::num_points::other_count<Geometry, 1> +{}; + +template <typename Geometry> +struct num_points<box_tag, Geometry> + : detail::num_points::other_count<Geometry, 4> +{}; + +template <typename Geometry> +struct num_points<segment_tag, Geometry> + : detail::num_points::other_count<Geometry, 2> +{}; + +template <typename Geometry> +struct num_points<linestring_tag, Geometry> + : detail::num_points::range_count<Geometry> +{}; + +template <typename Geometry> +struct num_points<ring_tag, Geometry> + : detail::num_points::range_count<Geometry> +{}; + +template <typename Geometry> +struct num_points<polygon_tag, Geometry> + : detail::num_points::polygon_count<Geometry> +{}; + +} // namespace dispatch +#endif + + +/*! +\brief \brief_calc{number of points} +\ingroup num_points +\details \details_calc{num_points, number of points}. +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\param add_for_open add one for open geometries (i.e. polygon types which are not closed) +\return \return_calc{number of points} + +\qbk{[include reference/algorithms/num_points.qbk]} +*/ +template <typename Geometry> +inline std::size_t num_points(Geometry const& geometry, bool add_for_open = false) +{ + concept::check<Geometry const>(); + + return dispatch::num_points + < + typename tag_cast<typename tag<Geometry>::type, multi_tag>::type, + Geometry + >::apply(geometry, add_for_open); +} + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_NUM_POINTS_HPP diff --git a/boost/geometry/algorithms/overlaps.hpp b/boost/geometry/algorithms/overlaps.hpp new file mode 100644 index 0000000000..2f854b4fdd --- /dev/null +++ b/boost/geometry/algorithms/overlaps.hpp @@ -0,0 +1,202 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_OVERLAPS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_OVERLAPS_HPP + + +#include <cstddef> + +#include <boost/mpl/assert.hpp> + +#include <boost/geometry/core/access.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlaps +{ + +template +< + typename Box1, + typename Box2, + std::size_t Dimension, + std::size_t DimensionCount +> +struct box_box_loop +{ + static inline void apply(Box1 const& b1, Box2 const& b2, + bool& overlaps, bool& one_in_two, bool& two_in_one) + { + assert_dimension_equal<Box1, Box2>(); + + typedef typename coordinate_type<Box1>::type coordinate_type1; + typedef typename coordinate_type<Box2>::type coordinate_type2; + + coordinate_type1 const& min1 = get<min_corner, Dimension>(b1); + coordinate_type1 const& max1 = get<max_corner, Dimension>(b1); + coordinate_type2 const& min2 = get<min_corner, Dimension>(b2); + coordinate_type2 const& max2 = get<max_corner, Dimension>(b2); + + // We might use the (not yet accepted) Boost.Interval + // submission in the future + + // If: + // B1: |-------| + // B2: |------| + // in any dimension -> no overlap + if (max1 <= min2 || min1 >= max2) + { + overlaps = false; + return; + } + + // If: + // B1: |--------------------| + // B2: |-------------| + // in all dimensions -> within, then no overlap + // B1: |--------------------| + // B2: |-------------| + // this is "within-touch" -> then no overlap. So use < and > + if (min1 < min2 || max1 > max2) + { + one_in_two = false; + } + // Same other way round + if (min2 < min1 || max2 > max1) + { + two_in_one = false; + } + + box_box_loop + < + Box1, + Box2, + Dimension + 1, + DimensionCount + >::apply(b1, b2, overlaps, one_in_two, two_in_one); + } +}; + +template +< + typename Box1, + typename Box2, + std::size_t DimensionCount +> +struct box_box_loop<Box1, Box2, DimensionCount, DimensionCount> +{ + static inline void apply(Box1 const& , Box2 const&, bool&, bool&, bool&) + { + } +}; + +template +< + typename Box1, + typename Box2 +> +struct box_box +{ + static inline bool apply(Box1 const& b1, Box2 const& b2) + { + bool overlaps = true; + bool within1 = true; + bool within2 = true; + box_box_loop + < + Box1, + Box2, + 0, + dimension<Box1>::type::value + >::apply(b1, b2, overlaps, within1, within2); + + /* + \see http://docs.codehaus.org/display/GEOTDOC/02+Geometry+Relationships#02GeometryRelationships-Overlaps + where is stated that "inside" is not an "overlap", + this is true and is implemented as such. + */ + return overlaps && ! within1 && ! within2; + } +}; + + + +}} // namespace detail::overlaps +#endif // DOXYGEN_NO_DETAIL + +//struct not_implemented_for_this_geometry_type : public boost::false_type {}; + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag1, + typename Tag2, + typename Geometry1, + typename Geometry2 +> +struct overlaps +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types<Geometry1, Geometry2>) + ); +}; + + +template <typename Box1, typename Box2> +struct overlaps<box_tag, box_tag, Box1, Box2> + : detail::overlaps::box_box<Box1, Box2> +{}; + + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brief_check2{overlap} +\ingroup overlaps +\return \return_check2{overlap} + +\qbk{[include reference/algorithms/overlaps.qbk]} +*/ +template <typename Geometry1, typename Geometry2> +inline bool overlaps(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + return dispatch::overlaps + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + Geometry1, + Geometry2 + >::apply(geometry1, geometry2); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_OVERLAPS_HPP diff --git a/boost/geometry/algorithms/perimeter.hpp b/boost/geometry/algorithms/perimeter.hpp new file mode 100644 index 0000000000..adeb0b05b0 --- /dev/null +++ b/boost/geometry/algorithms/perimeter.hpp @@ -0,0 +1,144 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_PERIMETER_HPP +#define BOOST_GEOMETRY_ALGORITHMS_PERIMETER_HPP + + +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/strategies/default_length_result.hpp> +#include <boost/geometry/algorithms/length.hpp> +#include <boost/geometry/algorithms/detail/calculate_null.hpp> +#include <boost/geometry/algorithms/detail/calculate_sum.hpp> +// #include <boost/geometry/algorithms/detail/throw_on_empty_input.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// Default perimeter is 0.0, specializations implement calculated values +template <typename Tag, typename Geometry, typename Strategy> +struct perimeter : detail::calculate_null + < + typename default_length_result<Geometry>::type, + Geometry, + Strategy + > +{}; + +template <typename Geometry, typename Strategy> +struct perimeter<ring_tag, Geometry, Strategy> + : detail::length::range_length + < + Geometry, + Strategy, + closure<Geometry>::value + > +{}; + +template <typename Polygon, typename Strategy> +struct perimeter<polygon_tag, Polygon, Strategy> + : detail::calculate_polygon_sum + < + typename default_length_result<Polygon>::type, + Polygon, + Strategy, + detail::length::range_length + < + typename ring_type<Polygon>::type, + Strategy, + closure<Polygon>::value + > + > +{}; + + +// box,n-sphere: to be implemented + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brief_calc{perimeter} +\ingroup perimeter +\details The function perimeter returns the perimeter of a geometry, + using the default distance-calculation-strategy +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\return \return_calc{perimeter} + +\qbk{[include reference/algorithms/perimeter.qbk]} + */ +template<typename Geometry> +inline typename default_length_result<Geometry>::type perimeter( + Geometry const& geometry) +{ + concept::check<Geometry const>(); + + typedef typename point_type<Geometry>::type point_type; + typedef typename strategy::distance::services::default_strategy + < + point_tag, point_type + >::type strategy_type; + + // detail::throw_on_empty_input(geometry); + + return dispatch::perimeter + < + typename tag<Geometry>::type, + Geometry, + strategy_type + >::apply(geometry, strategy_type()); +} + +/*! +\brief \brief_calc{perimeter} \brief_strategy +\ingroup perimeter +\details The function perimeter returns the perimeter of a geometry, + using specified strategy +\tparam Geometry \tparam_geometry +\tparam Strategy \tparam_strategy{distance} +\param geometry \param_geometry +\param strategy strategy to be used for distance calculations. +\return \return_calc{perimeter} + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/perimeter.qbk]} + */ +template<typename Geometry, typename Strategy> +inline typename default_length_result<Geometry>::type perimeter( + Geometry const& geometry, Strategy const& strategy) +{ + concept::check<Geometry const>(); + + // detail::throw_on_empty_input(geometry); + + return dispatch::perimeter + < + typename tag<Geometry>::type, + Geometry, + Strategy + >::apply(geometry, strategy); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_PERIMETER_HPP + diff --git a/boost/geometry/algorithms/reverse.hpp b/boost/geometry/algorithms/reverse.hpp new file mode 100644 index 0000000000..bf0ef2d9a9 --- /dev/null +++ b/boost/geometry/algorithms/reverse.hpp @@ -0,0 +1,134 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_REVERSE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_REVERSE_HPP + +#include <algorithm> + +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace reverse +{ + + +template <typename Range> +struct range_reverse +{ + static inline void apply(Range& range) + { + std::reverse(boost::begin(range), boost::end(range)); + } +}; + + +template <typename Polygon> +struct polygon_reverse +{ + static inline void apply(Polygon& polygon) + { + typedef typename geometry::ring_type<Polygon>::type ring_type; + + typedef range_reverse<ring_type> per_range; + per_range::apply(exterior_ring(polygon)); + + typename interior_return_type<Polygon>::type rings + = interior_rings(polygon); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + per_range::apply(*it); + } + } +}; + + +}} // namespace detail::reverse +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag, + typename Geometry +> +struct reverse +{ + static inline void apply(Geometry&) + {} +}; + + +template <typename Ring> +struct reverse<ring_tag, Ring> + : detail::reverse::range_reverse<Ring> +{}; + + +template <typename LineString> +struct reverse<linestring_tag, LineString> + : detail::reverse::range_reverse<LineString> +{}; + + +template <typename Polygon> +struct reverse<polygon_tag, Polygon> + : detail::reverse::polygon_reverse<Polygon> +{}; + + +} // namespace dispatch +#endif + + +/*! +\brief Reverses the points within a geometry +\details Generic function to reverse a geometry. It resembles the std::reverse + functionality, but it takes the geometry type into account. Only for a ring + or for a linestring it is the same as the std::reverse. +\ingroup reverse +\tparam Geometry \tparam_geometry +\param geometry \param_geometry which will be reversed + +\qbk{[include reference/algorithms/reverse.qbk]} +*/ +template <typename Geometry> +inline void reverse(Geometry& geometry) +{ + concept::check<Geometry>(); + + dispatch::reverse + < + typename tag<Geometry>::type, + Geometry + >::apply(geometry); +} + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_REVERSE_HPP diff --git a/boost/geometry/algorithms/simplify.hpp b/boost/geometry/algorithms/simplify.hpp new file mode 100644 index 0000000000..225321d303 --- /dev/null +++ b/boost/geometry/algorithms/simplify.hpp @@ -0,0 +1,405 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_SIMPLIFY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_SIMPLIFY_HPP + + +#include <cstddef> + +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/ring_type.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/mutable_range.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/strategies/agnostic/simplify_douglas_peucker.hpp> +#include <boost/geometry/strategies/concepts/simplify_concept.hpp> + +#include <boost/geometry/algorithms/clear.hpp> +#include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/num_interior_rings.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace simplify +{ + +template<typename Range, typename Strategy> +struct simplify_range_insert +{ + template <typename OutputIterator, typename Distance> + static inline void apply(Range const& range, OutputIterator out, + Distance const& max_distance, Strategy const& strategy) + { + if (boost::size(range) <= 2 || max_distance < 0) + { + std::copy(boost::begin(range), boost::end(range), out); + } + else + { + strategy.apply(range, out, max_distance); + } + } +}; + + +template<typename Range, typename Strategy> +struct simplify_copy +{ + template <typename Distance> + static inline void apply(Range const& range, Range& out, + Distance const& , Strategy const& ) + { + std::copy + ( + boost::begin(range), boost::end(range), std::back_inserter(out) + ); + } +}; + + +template<typename Range, typename Strategy, std::size_t Minimum> +struct simplify_range +{ + template <typename Distance> + static inline void apply(Range const& range, Range& out, + Distance const& max_distance, Strategy const& strategy) + { + // Call do_container for a linestring / ring + + /* For a RING: + The first/last point (the closing point of the ring) should maybe + be excluded because it lies on a line with second/one but last. + Here it is never excluded. + + Note also that, especially if max_distance is too large, + the output ring might be self intersecting while the input ring is + not, although chances are low in normal polygons + + Finally the inputring might have 3 (open) or 4 (closed) points (=correct), + the output < 3 or 4(=wrong) + */ + + if (boost::size(range) <= int(Minimum) || max_distance < 0.0) + { + simplify_copy<Range, Strategy>::apply + ( + range, out, max_distance, strategy + ); + } + else + { + simplify_range_insert<Range, Strategy>::apply + ( + range, std::back_inserter(out), max_distance, strategy + ); + } + } +}; + +template<typename Polygon, typename Strategy> +struct simplify_polygon +{ + template <typename Distance> + static inline void apply(Polygon const& poly_in, Polygon& poly_out, + Distance const& max_distance, Strategy const& strategy) + { + typedef typename ring_type<Polygon>::type ring_type; + + int const Minimum = core_detail::closure::minimum_ring_size + < + geometry::closure<Polygon>::value + >::value; + + // Note that if there are inner rings, and distance is too large, + // they might intersect with the outer ring in the output, + // while it didn't in the input. + simplify_range<ring_type, Strategy, Minimum>::apply(exterior_ring(poly_in), + exterior_ring(poly_out), + max_distance, strategy); + + traits::resize + < + typename boost::remove_reference + < + typename traits::interior_mutable_type<Polygon>::type + >::type + >::apply(interior_rings(poly_out), num_interior_rings(poly_in)); + + typename interior_return_type<Polygon const>::type rings_in + = interior_rings(poly_in); + typename interior_return_type<Polygon>::type rings_out + = interior_rings(poly_out); + BOOST_AUTO_TPL(it_out, boost::begin(rings_out)); + for (BOOST_AUTO_TPL(it_in, boost::begin(rings_in)); + it_in != boost::end(rings_in); + ++it_in, ++it_out) + { + simplify_range<ring_type, Strategy, Minimum>::apply(*it_in, + *it_out, max_distance, strategy); + } + } +}; + + +}} // namespace detail::simplify +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template <typename Tag, typename Geometry, typename Strategy> +struct simplify +{ +}; + +template <typename Point, typename Strategy> +struct simplify<point_tag, Point, Strategy> +{ + template <typename Distance> + static inline void apply(Point const& point, Point& out, + Distance const& , Strategy const& ) + { + geometry::convert(point, out); + } +}; + + +template <typename Linestring, typename Strategy> +struct simplify<linestring_tag, Linestring, Strategy> + : detail::simplify::simplify_range + < + Linestring, + Strategy, + 2 + > +{}; + +template <typename Ring, typename Strategy> +struct simplify<ring_tag, Ring, Strategy> + : detail::simplify::simplify_range + < + Ring, + Strategy, + core_detail::closure::minimum_ring_size + < + geometry::closure<Ring>::value + >::value + > +{}; + +template <typename Polygon, typename Strategy> +struct simplify<polygon_tag, Polygon, Strategy> + : detail::simplify::simplify_polygon + < + Polygon, + Strategy + > +{}; + + +template <typename Tag, typename Geometry, typename Strategy> +struct simplify_insert +{ +}; + + +template <typename Linestring, typename Strategy> +struct simplify_insert<linestring_tag, Linestring, Strategy> + : detail::simplify::simplify_range_insert + < + Linestring, + Strategy + > +{}; + +template <typename Ring, typename Strategy> +struct simplify_insert<ring_tag, Ring, Strategy> + : detail::simplify::simplify_range_insert + < + Ring, + Strategy + > +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Simplify a geometry using a specified strategy +\ingroup simplify +\tparam Geometry \tparam_geometry +\tparam Distance A numerical distance measure +\tparam Strategy A type fulfilling a SimplifyStrategy concept +\param strategy A strategy to calculate simplification +\param geometry input geometry, to be simplified +\param out output geometry, simplified version of the input geometry +\param max_distance distance (in units of input coordinates) of a vertex + to other segments to be removed +\param strategy simplify strategy to be used for simplification, might + include point-distance strategy + +\image html svg_simplify_country.png "The image below presents the simplified country" +\qbk{distinguish,with strategy} +*/ +template<typename Geometry, typename Distance, typename Strategy> +inline void simplify(Geometry const& geometry, Geometry& out, + Distance const& max_distance, Strategy const& strategy) +{ + concept::check<Geometry>(); + + BOOST_CONCEPT_ASSERT( (geometry::concept::SimplifyStrategy<Strategy>) ); + + geometry::clear(out); + + dispatch::simplify + < + typename tag<Geometry>::type, + Geometry, + Strategy + >::apply(geometry, out, max_distance, strategy); +} + + + + +/*! +\brief Simplify a geometry +\ingroup simplify +\tparam Geometry \tparam_geometry +\tparam Distance \tparam_numeric +\note This version of simplify simplifies a geometry using the default + strategy (Douglas Peucker), +\param geometry input geometry, to be simplified +\param out output geometry, simplified version of the input geometry +\param max_distance distance (in units of input coordinates) of a vertex + to other segments to be removed + +\qbk{[include reference/algorithms/simplify.qbk]} + */ +template<typename Geometry, typename Distance> +inline void simplify(Geometry const& geometry, Geometry& out, + Distance const& max_distance) +{ + concept::check<Geometry>(); + + typedef typename point_type<Geometry>::type point_type; + typedef typename strategy::distance::services::default_strategy + < + segment_tag, point_type + >::type ds_strategy_type; + + typedef strategy::simplify::douglas_peucker + < + point_type, ds_strategy_type + > strategy_type; + + simplify(geometry, out, max_distance, strategy_type()); +} + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace simplify +{ + + +/*! +\brief Simplify a geometry, using an output iterator + and a specified strategy +\ingroup simplify +\tparam Geometry \tparam_geometry +\param geometry input geometry, to be simplified +\param out output iterator, outputs all simplified points +\param max_distance distance (in units of input coordinates) of a vertex + to other segments to be removed +\param strategy simplify strategy to be used for simplification, + might include point-distance strategy + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/simplify.qbk]} +*/ +template<typename Geometry, typename OutputIterator, typename Distance, typename Strategy> +inline void simplify_insert(Geometry const& geometry, OutputIterator out, + Distance const& max_distance, Strategy const& strategy) +{ + concept::check<Geometry const>(); + BOOST_CONCEPT_ASSERT( (geometry::concept::SimplifyStrategy<Strategy>) ); + + dispatch::simplify_insert + < + typename tag<Geometry>::type, + Geometry, + Strategy + >::apply(geometry, out, max_distance, strategy); +} + +/*! +\brief Simplify a geometry, using an output iterator +\ingroup simplify +\tparam Geometry \tparam_geometry +\param geometry input geometry, to be simplified +\param out output iterator, outputs all simplified points +\param max_distance distance (in units of input coordinates) of a vertex + to other segments to be removed + +\qbk{[include reference/algorithms/simplify_insert.qbk]} + */ +template<typename Geometry, typename OutputIterator, typename Distance> +inline void simplify_insert(Geometry const& geometry, OutputIterator out, + Distance const& max_distance) +{ + typedef typename point_type<Geometry>::type point_type; + + // Concept: output point type = point type of input geometry + concept::check<Geometry const>(); + concept::check<point_type>(); + + typedef typename strategy::distance::services::default_strategy + < + segment_tag, point_type + >::type ds_strategy_type; + + typedef strategy::simplify::douglas_peucker + < + point_type, ds_strategy_type + > strategy_type; + + dispatch::simplify_insert + < + typename tag<Geometry>::type, + Geometry, + strategy_type + >::apply(geometry, out, max_distance, strategy_type()); +} + +}} // namespace detail::simplify +#endif // DOXYGEN_NO_DETAIL + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_SIMPLIFY_HPP diff --git a/boost/geometry/algorithms/sym_difference.hpp b/boost/geometry/algorithms/sym_difference.hpp new file mode 100644 index 0000000000..6394576de4 --- /dev/null +++ b/boost/geometry/algorithms/sym_difference.hpp @@ -0,0 +1,177 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_SYM_DIFFERENCE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_SYM_DIFFERENCE_HPP + +#include <algorithm> + + +#include <boost/geometry/algorithms/intersection.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace sym_difference +{ + + + +/*! +\brief \brief_calc2{symmetric difference} \brief_strategy +\ingroup sym_difference +\details \details_calc2{symmetric difference, spatial set theoretic symmetric difference (XOR)} + \brief_strategy. \details_insert{sym_difference} +\tparam GeometryOut output geometry type, must be specified +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Strategy \tparam_strategy_overlay +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param out \param_out{difference} +\param strategy \param_strategy{difference} +\return \return_out + +\qbk{distinguish,with strategy} +*/ +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename OutputIterator, + typename Strategy +> +inline OutputIterator sym_difference_insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, OutputIterator out, + Strategy const& strategy) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + concept::check<GeometryOut>(); + + out = geometry::dispatch::intersection_insert + < + typename geometry::tag<Geometry1>::type, + typename geometry::tag<Geometry2>::type, + typename geometry::tag<GeometryOut>::type, + geometry::is_areal<Geometry1>::value, + geometry::is_areal<Geometry2>::value, + geometry::is_areal<GeometryOut>::value, + Geometry1, Geometry2, + geometry::detail::overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, + geometry::detail::overlay::do_reverse<geometry::point_order<Geometry2>::value, true>::value, + geometry::detail::overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value, + OutputIterator, GeometryOut, + overlay_difference, + Strategy + >::apply(geometry1, geometry2, out, strategy); + out = geometry::dispatch::intersection_insert + < + typename geometry::tag<Geometry2>::type, + typename geometry::tag<Geometry1>::type, + typename geometry::tag<GeometryOut>::type, + geometry::is_areal<Geometry2>::value, + geometry::is_areal<Geometry1>::value, + geometry::is_areal<GeometryOut>::value, + Geometry2, Geometry1, + geometry::detail::overlay::do_reverse<geometry::point_order<Geometry2>::value>::value, + geometry::detail::overlay::do_reverse<geometry::point_order<Geometry1>::value, true>::value, + geometry::detail::overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value, + OutputIterator, GeometryOut, + overlay_difference, + Strategy + >::apply(geometry2, geometry1, out, strategy); + return out; +} + + +/*! +\brief \brief_calc2{symmetric difference} +\ingroup sym_difference +\details \details_calc2{symmetric difference, spatial set theoretic symmetric difference (XOR)} + \details_insert{sym_difference} +\tparam GeometryOut output geometry type, must be specified +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param out \param_out{difference} +\return \return_out + +*/ +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename OutputIterator +> +inline OutputIterator sym_difference_insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, OutputIterator out) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + concept::check<GeometryOut>(); + + typedef strategy_intersection + < + typename cs_tag<GeometryOut>::type, + Geometry1, + Geometry2, + typename geometry::point_type<GeometryOut>::type + > strategy_type; + + return sym_difference_insert<GeometryOut>(geometry1, geometry2, out, strategy_type()); +} + +}} // namespace detail::sym_difference +#endif // DOXYGEN_NO_DETAIL + + +/*! +\brief \brief_calc2{symmetric difference} +\ingroup sym_difference +\details \details_calc2{symmetric difference, spatial set theoretic symmetric difference (XOR)}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Collection output collection, either a multi-geometry, + or a std::vector<Geometry> / std::deque<Geometry> etc +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param output_collection the output collection + +\qbk{[include reference/algorithms/sym_difference.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename Collection +> +inline void sym_difference(Geometry1 const& geometry1, + Geometry2 const& geometry2, Collection& output_collection) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + typedef typename boost::range_value<Collection>::type geometry_out; + concept::check<geometry_out>(); + + detail::sym_difference::sym_difference_insert<geometry_out>( + geometry1, geometry2, + std::back_inserter(output_collection)); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_SYM_DIFFERENCE_HPP diff --git a/boost/geometry/algorithms/transform.hpp b/boost/geometry/algorithms/transform.hpp new file mode 100644 index 0000000000..22b45dc77e --- /dev/null +++ b/boost/geometry/algorithms/transform.hpp @@ -0,0 +1,351 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_TRANSFORM_HPP +#define BOOST_GEOMETRY_ALGORITHMS_TRANSFORM_HPP + +#include <cmath> +#include <iterator> + +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/algorithms/assign.hpp> +#include <boost/geometry/algorithms/clear.hpp> +#include <boost/geometry/algorithms/num_interior_rings.hpp> + +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/mutable_range.hpp> +#include <boost/geometry/core/ring_type.hpp> +#include <boost/geometry/core/tag_cast.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/strategies/transform.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace transform +{ + +template <typename Point1, typename Point2, typename Strategy> +struct transform_point +{ + static inline bool apply(Point1 const& p1, Point2& p2, + Strategy const& strategy) + { + return strategy.apply(p1, p2); + } +}; + + +template <typename Box1, typename Box2, typename Strategy> +struct transform_box +{ + static inline bool apply(Box1 const& b1, Box2& b2, + Strategy const& strategy) + { + typedef typename point_type<Box1>::type point_type1; + typedef typename point_type<Box2>::type point_type2; + + point_type1 lower_left, upper_right; + detail::assign::assign_box_2d_corner<min_corner, min_corner>( + b1, lower_left); + detail::assign::assign_box_2d_corner<max_corner, max_corner>( + b1, upper_right); + + point_type2 p1, p2; + if (strategy.apply(lower_left, p1) && strategy.apply(upper_right, p2)) + { + // Create a valid box and therefore swap if necessary + typedef typename coordinate_type<point_type2>::type coordinate_type; + coordinate_type x1 = geometry::get<0>(p1) + , y1 = geometry::get<1>(p1) + , x2 = geometry::get<0>(p2) + , y2 = geometry::get<1>(p2); + + if (x1 > x2) { std::swap(x1, x2); } + if (y1 > y2) { std::swap(y1, y2); } + + set<min_corner, 0>(b2, x1); + set<min_corner, 1>(b2, y1); + set<max_corner, 0>(b2, x2); + set<max_corner, 1>(b2, y2); + + return true; + } + return false; + } +}; + +template <typename Geometry1, typename Geometry2, typename Strategy> +struct transform_box_or_segment +{ + static inline bool apply(Geometry1 const& source, Geometry2& target, + Strategy const& strategy) + { + typedef typename point_type<Geometry1>::type point_type1; + typedef typename point_type<Geometry2>::type point_type2; + + point_type1 source_point[2]; + geometry::detail::assign_point_from_index<0>(source, source_point[0]); + geometry::detail::assign_point_from_index<1>(source, source_point[1]); + + point_type2 target_point[2]; + if (strategy.apply(source_point[0], target_point[0]) + && strategy.apply(source_point[1], target_point[1])) + { + geometry::detail::assign_point_to_index<0>(target_point[0], target); + geometry::detail::assign_point_to_index<1>(target_point[1], target); + return true; + } + return false; + } +}; + + +template +< + typename PointOut, + typename OutputIterator, + typename Range, + typename Strategy +> +inline bool transform_range_out(Range const& range, + OutputIterator out, Strategy const& strategy) +{ + PointOut point_out; + for(typename boost::range_iterator<Range const>::type + it = boost::begin(range); + it != boost::end(range); + ++it) + { + if (! transform_point + < + typename point_type<Range>::type, + PointOut, + Strategy + >::apply(*it, point_out, strategy)) + { + return false; + } + *out++ = point_out; + } + return true; +} + + +template <typename Polygon1, typename Polygon2, typename Strategy> +struct transform_polygon +{ + static inline bool apply(Polygon1 const& poly1, Polygon2& poly2, + Strategy const& strategy) + { + typedef typename ring_type<Polygon1>::type ring1_type; + typedef typename ring_type<Polygon2>::type ring2_type; + typedef typename point_type<Polygon2>::type point2_type; + + geometry::clear(poly2); + + if (!transform_range_out<point2_type>(exterior_ring(poly1), + std::back_inserter(exterior_ring(poly2)), strategy)) + { + return false; + } + + // Note: here a resizeable container is assumed. + traits::resize + < + typename boost::remove_reference + < + typename traits::interior_mutable_type<Polygon2>::type + >::type + >::apply(interior_rings(poly2), num_interior_rings(poly1)); + + typename interior_return_type<Polygon1 const>::type rings1 + = interior_rings(poly1); + typename interior_return_type<Polygon2>::type rings2 + = interior_rings(poly2); + BOOST_AUTO_TPL(it1, boost::begin(rings1)); + BOOST_AUTO_TPL(it2, boost::begin(rings2)); + for ( ; it1 != boost::end(interior_rings(poly1)); ++it1, ++it2) + { + if (!transform_range_out<point2_type>(*it1, + std::back_inserter(*it2), strategy)) + { + return false; + } + } + + return true; + } +}; + + +template <typename Point1, typename Point2> +struct select_strategy +{ + typedef typename strategy::transform::services::default_strategy + < + typename cs_tag<Point1>::type, + typename cs_tag<Point2>::type, + typename coordinate_system<Point1>::type, + typename coordinate_system<Point2>::type, + dimension<Point1>::type::value, + dimension<Point2>::type::value, + typename point_type<Point1>::type, + typename point_type<Point2>::type + >::type type; +}; + +template <typename Range1, typename Range2, typename Strategy> +struct transform_range +{ + static inline bool apply(Range1 const& range1, + Range2& range2, Strategy const& strategy) + { + typedef typename point_type<Range2>::type point_type; + + // Should NOT be done here! + // geometry::clear(range2); + return transform_range_out<point_type>(range1, + std::back_inserter(range2), strategy); + } +}; + +}} // namespace detail::transform +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag1, typename Tag2, + typename Geometry1, typename Geometry2, + typename Strategy +> +struct transform {}; + +template <typename Point1, typename Point2, typename Strategy> +struct transform<point_tag, point_tag, Point1, Point2, Strategy> + : detail::transform::transform_point<Point1, Point2, Strategy> +{ +}; + + +template <typename Linestring1, typename Linestring2, typename Strategy> +struct transform + < + linestring_tag, linestring_tag, + Linestring1, Linestring2, Strategy + > + : detail::transform::transform_range<Linestring1, Linestring2, Strategy> +{ +}; + +template <typename Range1, typename Range2, typename Strategy> +struct transform<ring_tag, ring_tag, Range1, Range2, Strategy> + : detail::transform::transform_range<Range1, Range2, Strategy> +{ +}; + +template <typename Polygon1, typename Polygon2, typename Strategy> +struct transform<polygon_tag, polygon_tag, Polygon1, Polygon2, Strategy> + : detail::transform::transform_polygon<Polygon1, Polygon2, Strategy> +{ +}; + +template <typename Box1, typename Box2, typename Strategy> +struct transform<box_tag, box_tag, Box1, Box2, Strategy> + : detail::transform::transform_box<Box1, Box2, Strategy> +{ +}; + +template <typename Segment1, typename Segment2, typename Strategy> +struct transform<segment_tag, segment_tag, Segment1, Segment2, Strategy> + : detail::transform::transform_box_or_segment<Segment1, Segment2, Strategy> +{ +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Transforms from one geometry to another geometry \brief_strategy +\ingroup transform +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Strategy strategy +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param strategy The strategy to be used for transformation +\return True if the transformation could be done + +\qbk{distinguish,with strategy} + +\qbk{[include reference/algorithms/transform_with_strategy.qbk]} + */ +template <typename Geometry1, typename Geometry2, typename Strategy> +inline bool transform(Geometry1 const& geometry1, Geometry2& geometry2, + Strategy const& strategy) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2>(); + + typedef dispatch::transform + < + typename tag_cast<typename tag<Geometry1>::type, multi_tag>::type, + typename tag_cast<typename tag<Geometry2>::type, multi_tag>::type, + Geometry1, + Geometry2, + Strategy + > transform_type; + + return transform_type::apply(geometry1, geometry2, strategy); +} + + +/*! +\brief Transforms from one geometry to another geometry using a strategy +\ingroup transform +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return True if the transformation could be done + +\qbk{[include reference/algorithms/transform.qbk]} + */ +template <typename Geometry1, typename Geometry2> +inline bool transform(Geometry1 const& geometry1, Geometry2& geometry2) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2>(); + + typename detail::transform::select_strategy<Geometry1, Geometry2>::type strategy; + return transform(geometry1, geometry2, strategy); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_TRANSFORM_HPP diff --git a/boost/geometry/algorithms/union.hpp b/boost/geometry/algorithms/union.hpp new file mode 100644 index 0000000000..28d8e5dc0b --- /dev/null +++ b/boost/geometry/algorithms/union.hpp @@ -0,0 +1,284 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_UNION_HPP +#define BOOST_GEOMETRY_ALGORITHMS_UNION_HPP + + +#include <boost/mpl/if.hpp> + +#include <boost/range/metafunctions.hpp> + +#include <boost/geometry/core/is_areal.hpp> +#include <boost/geometry/core/point_order.hpp> +#include <boost/geometry/core/reverse_dispatch.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/algorithms/detail/overlay/overlay.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + // tag dispatching: + typename TagIn1, typename TagIn2, typename TagOut, + // metafunction finetuning helpers: + bool Areal1, bool Areal2, bool ArealOut, + // real types + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, + typename GeometryOut, + typename Strategy +> +struct union_insert +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPES + , (types<Geometry1, Geometry2, GeometryOut>) + ); +}; + + +template +< + typename TagIn1, typename TagIn2, typename TagOut, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, + typename GeometryOut, + typename Strategy +> +struct union_insert + < + TagIn1, TagIn2, TagOut, + true, true, true, + Geometry1, Geometry2, + Reverse1, Reverse2, ReverseOut, + OutputIterator, GeometryOut, + Strategy + > : detail::overlay::overlay + <Geometry1, Geometry2, Reverse1, Reverse2, ReverseOut, OutputIterator, GeometryOut, overlay_union, Strategy> +{}; + + + +template +< + typename GeometryTag1, typename GeometryTag2, typename GeometryTag3, + bool Areal1, bool Areal2, bool ArealOut, + typename Geometry1, typename Geometry2, + bool Reverse1, bool Reverse2, bool ReverseOut, + typename OutputIterator, typename GeometryOut, + typename Strategy +> +struct union_insert_reversed +{ + static inline OutputIterator apply(Geometry1 const& g1, + Geometry2 const& g2, OutputIterator out, + Strategy const& strategy) + { + return union_insert + < + GeometryTag2, GeometryTag1, GeometryTag3, + Areal2, Areal1, ArealOut, + Geometry2, Geometry1, + Reverse2, Reverse1, ReverseOut, + OutputIterator, GeometryOut, + Strategy + >::apply(g2, g1, out, strategy); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace union_ +{ + +template +< + typename GeometryOut, + typename Geometry1, typename Geometry2, + typename OutputIterator, + typename Strategy +> +inline OutputIterator insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, + OutputIterator out, + Strategy const& strategy) +{ + return boost::mpl::if_c + < + geometry::reverse_dispatch<Geometry1, Geometry2>::type::value, + dispatch::union_insert_reversed + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + typename tag<GeometryOut>::type, + geometry::is_areal<Geometry1>::value, + geometry::is_areal<Geometry2>::value, + geometry::is_areal<GeometryOut>::value, + Geometry1, Geometry2, + overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, + overlay::do_reverse<geometry::point_order<Geometry2>::value>::value, + overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value, + OutputIterator, GeometryOut, + Strategy + >, + dispatch::union_insert + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + typename tag<GeometryOut>::type, + geometry::is_areal<Geometry1>::value, + geometry::is_areal<Geometry2>::value, + geometry::is_areal<GeometryOut>::value, + Geometry1, Geometry2, + overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, + overlay::do_reverse<geometry::point_order<Geometry2>::value>::value, + overlay::do_reverse<geometry::point_order<GeometryOut>::value>::value, + OutputIterator, GeometryOut, + Strategy + > + >::type::apply(geometry1, geometry2, out, strategy); +} + +/*! +\brief_calc2{union} \brief_strategy +\ingroup union +\details \details_calc2{union_insert, spatial set theoretic union} + \brief_strategy. details_insert{union} +\tparam GeometryOut output geometry type, must be specified +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam OutputIterator output iterator +\tparam Strategy \tparam_strategy_overlay +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param out \param_out{union} +\param strategy \param_strategy{union} +\return \return_out + +\qbk{distinguish,with strategy} +*/ +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename OutputIterator, + typename Strategy +> +inline OutputIterator union_insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, + OutputIterator out, + Strategy const& strategy) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + concept::check<GeometryOut>(); + + return detail::union_::insert<GeometryOut>(geometry1, geometry2, out, strategy); +} + +/*! +\brief_calc2{union} +\ingroup union +\details \details_calc2{union_insert, spatial set theoretic union}. + \details_insert{union} +\tparam GeometryOut output geometry type, must be specified +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam OutputIterator output iterator +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param out \param_out{union} +\return \return_out +*/ +template +< + typename GeometryOut, + typename Geometry1, + typename Geometry2, + typename OutputIterator +> +inline OutputIterator union_insert(Geometry1 const& geometry1, + Geometry2 const& geometry2, + OutputIterator out) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + concept::check<GeometryOut>(); + + typedef strategy_intersection + < + typename cs_tag<GeometryOut>::type, + Geometry1, + Geometry2, + typename geometry::point_type<GeometryOut>::type + > strategy; + + return union_insert<GeometryOut>(geometry1, geometry2, out, strategy()); +} + + +}} // namespace detail::union_ +#endif // DOXYGEN_NO_DETAIL + + + + +/*! +\brief Combines two geometries which each other +\ingroup union +\details \details_calc2{union, spatial set theoretic union}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\tparam Collection output collection, either a multi-geometry, + or a std::vector<Geometry> / std::deque<Geometry> etc +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\param output_collection the output collection +\note Called union_ because union is a reserved word. + +\qbk{[include reference/algorithms/union.qbk]} +*/ +template +< + typename Geometry1, + typename Geometry2, + typename Collection +> +inline void union_(Geometry1 const& geometry1, + Geometry2 const& geometry2, + Collection& output_collection) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + + typedef typename boost::range_value<Collection>::type geometry_out; + concept::check<geometry_out>(); + + detail::union_::union_insert<geometry_out>(geometry1, geometry2, + std::back_inserter(output_collection)); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_UNION_HPP diff --git a/boost/geometry/algorithms/unique.hpp b/boost/geometry/algorithms/unique.hpp new file mode 100644 index 0000000000..3bbf479f9b --- /dev/null +++ b/boost/geometry/algorithms/unique.hpp @@ -0,0 +1,153 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_UNIQUE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_UNIQUE_HPP + +#include <algorithm> + +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/mutable_range.hpp> +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/policies/compare.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace unique +{ + + +template <typename Range, typename ComparePolicy> +struct range_unique +{ + static inline void apply(Range& range, ComparePolicy const& policy) + { + typename boost::range_iterator<Range>::type it + = std::unique + ( + boost::begin(range), + boost::end(range), + policy + ); + + traits::resize<Range>::apply(range, it - boost::begin(range)); + } +}; + + +template <typename Polygon, typename ComparePolicy> +struct polygon_unique +{ + static inline void apply(Polygon& polygon, ComparePolicy const& policy) + { + typedef typename geometry::ring_type<Polygon>::type ring_type; + + typedef range_unique<ring_type, ComparePolicy> per_range; + per_range::apply(exterior_ring(polygon), policy); + + typename interior_return_type<Polygon>::type rings + = interior_rings(polygon); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + per_range::apply(*it, policy); + } + } +}; + + + +}} // namespace detail::unique +#endif // DOXYGEN_NO_DETAIL + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag, + typename Geometry, + typename ComparePolicy +> +struct unique +{ + static inline void apply(Geometry&, ComparePolicy const& ) + {} +}; + + +template <typename Ring, typename ComparePolicy> +struct unique<ring_tag, Ring, ComparePolicy> + : detail::unique::range_unique<Ring, ComparePolicy> +{}; + + +template <typename LineString, typename ComparePolicy> +struct unique<linestring_tag, LineString, ComparePolicy> + : detail::unique::range_unique<LineString, ComparePolicy> +{}; + + +template <typename Polygon, typename ComparePolicy> +struct unique<polygon_tag, Polygon, ComparePolicy> + : detail::unique::polygon_unique<Polygon, ComparePolicy> +{}; + + +} // namespace dispatch +#endif + + +/*! +\brief \brief_calc{minimal set} +\ingroup unique +\details \details_calc{unique,minimal set (where duplicate consecutive points are removed)}. +\tparam Geometry \tparam_geometry +\param geometry \param_geometry which will be made unique + +\qbk{[include reference/algorithms/unique.qbk]} +*/ +template <typename Geometry> +inline void unique(Geometry& geometry) +{ + concept::check<Geometry>(); + + // Default strategy is the default point-comparison policy + typedef geometry::equal_to + < + typename geometry::point_type<Geometry>::type + > policy; + + + dispatch::unique + < + typename tag<Geometry>::type, + Geometry, + policy + >::apply(geometry, policy()); +} + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_UNIQUE_HPP diff --git a/boost/geometry/algorithms/within.hpp b/boost/geometry/algorithms/within.hpp new file mode 100644 index 0000000000..0e0cb68d53 --- /dev/null +++ b/boost/geometry/algorithms/within.hpp @@ -0,0 +1,344 @@ +// 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. + +// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library +// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_WITHIN_HPP +#define BOOST_GEOMETRY_ALGORITHMS_WITHIN_HPP + + +#include <cstddef> + +#include <boost/range.hpp> +#include <boost/typeof/typeof.hpp> + +#include <boost/geometry/algorithms/make.hpp> +#include <boost/geometry/algorithms/not_implemented.hpp> + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/closure.hpp> +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/exterior_ring.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/point_order.hpp> +#include <boost/geometry/core/ring_type.hpp> +#include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/geometries/concepts/check.hpp> +#include <boost/geometry/strategies/within.hpp> +#include <boost/geometry/strategies/concepts/within_concept.hpp> +#include <boost/geometry/util/math.hpp> +#include <boost/geometry/util/order_as_direction.hpp> +#include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/views/reversible_view.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace within +{ + + +template +< + typename Point, + typename Ring, + iterate_direction Direction, + closure_selector Closure, + typename Strategy +> +struct point_in_ring +{ + BOOST_CONCEPT_ASSERT( (geometry::concept::WithinStrategyPolygonal<Strategy>) ); + + static inline int apply(Point const& point, Ring const& ring, + Strategy const& strategy) + { + if (boost::size(ring) + < core_detail::closure::minimum_ring_size<Closure>::value) + { + return -1; + } + + typedef typename reversible_view<Ring const, Direction>::type rev_view_type; + typedef typename closeable_view + < + rev_view_type const, Closure + >::type cl_view_type; + typedef typename boost::range_iterator<cl_view_type const>::type iterator_type; + + rev_view_type rev_view(ring); + cl_view_type view(rev_view); + typename Strategy::state_type state; + iterator_type it = boost::begin(view); + iterator_type end = boost::end(view); + + bool stop = false; + for (iterator_type previous = it++; + it != end && ! stop; + ++previous, ++it) + { + if (! strategy.apply(point, *previous, *it, state)) + { + stop = true; + } + } + + return strategy.result(state); + } +}; + + +// Polygon: in exterior ring, and if so, not within interior ring(s) +template +< + typename Point, + typename Polygon, + iterate_direction Direction, + closure_selector Closure, + typename Strategy +> +struct point_in_polygon +{ + BOOST_CONCEPT_ASSERT( (geometry::concept::WithinStrategyPolygonal<Strategy>) ); + + static inline int apply(Point const& point, Polygon const& poly, + Strategy const& strategy) + { + int const code = point_in_ring + < + Point, + typename ring_type<Polygon>::type, + Direction, + Closure, + Strategy + >::apply(point, exterior_ring(poly), strategy); + + if (code == 1) + { + typename interior_return_type<Polygon const>::type rings + = interior_rings(poly); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); + it != boost::end(rings); + ++it) + { + int const interior_code = point_in_ring + < + Point, + typename ring_type<Polygon>::type, + Direction, + Closure, + Strategy + >::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; + } +}; + +}} // namespace detail::within +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Geometry1, + typename Geometry2, + typename Tag1 = typename tag<Geometry1>::type, + typename Tag2 = typename tag<Geometry2>::type +> +struct within: not_implemented<Tag1, Tag2> +{}; + + +template <typename Point, typename Box> +struct within<Point, Box, point_tag, box_tag> +{ + template <typename Strategy> + static inline bool apply(Point const& point, Box const& box, Strategy const& strategy) + { + return strategy.apply(point, box); + } +}; + +template <typename Box1, typename Box2> +struct within<Box1, Box2, box_tag, box_tag> +{ + template <typename Strategy> + static inline bool apply(Box1 const& box1, Box2 const& box2, Strategy const& strategy) + { + assert_dimension_equal<Box1, Box2>(); + return strategy.apply(box1, box2); + } +}; + + + +template <typename Point, typename Ring> +struct within<Point, Ring, point_tag, ring_tag> +{ + template <typename Strategy> + static inline bool apply(Point const& point, Ring const& ring, Strategy const& strategy) + { + return detail::within::point_in_ring + < + Point, + Ring, + order_as_direction<geometry::point_order<Ring>::value>::value, + geometry::closure<Ring>::value, + Strategy + >::apply(point, ring, strategy) == 1; + } +}; + +template <typename Point, typename Polygon> +struct within<Point, Polygon, point_tag, polygon_tag> +{ + template <typename Strategy> + static inline bool apply(Point const& point, Polygon const& polygon, Strategy const& strategy) + { + return detail::within::point_in_polygon + < + Point, + Polygon, + order_as_direction<geometry::point_order<Polygon>::value>::value, + geometry::closure<Polygon>::value, + Strategy + >::apply(point, polygon, strategy) == 1; + } +}; + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brief_check12{is completely inside} +\ingroup within +\details \details_check12{within, is completely inside}. +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry which might be within the second geometry +\param geometry2 \param_geometry which might contain the first geometry +\return true if geometry1 is completely contained within geometry2, + else false +\note The default strategy is used for within detection + + +\qbk{[include reference/algorithms/within.qbk]} + +\qbk{ +[heading Example] +[within] +[within_output] +} + */ +template<typename Geometry1, typename Geometry2> +inline bool within(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + assert_dimension_equal<Geometry1, Geometry2>(); + + typedef typename point_type<Geometry1>::type point_type1; + typedef typename point_type<Geometry2>::type point_type2; + + typedef typename strategy::within::services::default_strategy + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + typename tag<Geometry1>::type, + typename tag_cast<typename tag<Geometry2>::type, areal_tag>::type, + typename tag_cast + < + typename cs_tag<point_type1>::type, spherical_tag + >::type, + typename tag_cast + < + typename cs_tag<point_type2>::type, spherical_tag + >::type, + Geometry1, + Geometry2 + >::type strategy_type; + + return dispatch::within + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, strategy_type()); +} + +/*! +\brief \brief_check12{is completely inside} \brief_strategy +\ingroup within +\details \details_check12{within, is completely inside}, \brief_strategy. \details_strategy_reasons +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry which might be within the second geometry +\param geometry2 \param_geometry which might contain the first geometry +\param strategy strategy to be used +\return true if geometry1 is completely contained within geometry2, + else false + +\qbk{distinguish,with strategy} +\qbk{[include reference/algorithms/within.qbk]} +\qbk{ +[heading Available Strategies] +\* [link geometry.reference.strategies.strategy_within_winding Winding (coordinate system agnostic)] +\* [link geometry.reference.strategies.strategy_within_franklin Franklin (cartesian)] +\* [link geometry.reference.strategies.strategy_within_crossings_multiply Crossings Multiply (cartesian)] + +[heading Example] +[within_strategy] +[within_strategy_output] + +} +*/ +template<typename Geometry1, typename Geometry2, typename Strategy> +inline bool within(Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + concept::within::check + < + typename tag<Geometry1>::type, + typename tag<Geometry2>::type, + typename tag_cast<typename tag<Geometry2>::type, areal_tag>::type, + Strategy + >(); + concept::check<Geometry1 const>(); + concept::check<Geometry2 const>(); + assert_dimension_equal<Geometry1, Geometry2>(); + + return dispatch::within + < + Geometry1, + Geometry2 + >::apply(geometry1, geometry2, strategy); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_WITHIN_HPP |