diff --git a/include/boost/geometry/algorithms/append.hpp b/include/boost/geometry/algorithms/append.hpp new file mode 100644 index 000000000..0557d21f5 --- /dev/null +++ b/include/boost/geometry/algorithms/append.hpp @@ -0,0 +1,230 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + + +#include +#include +#include +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace append +{ + +template +struct append_no_action +{ + static inline void apply(Geometry& geometry, Point const& point, + int = 0, int = 0) + { + } +}; + +template +struct append_point +{ + static inline void apply(Geometry& geometry, Point const& point, + int = 0, int = 0) + { + typename geometry::point_type::type copy; + geometry::detail::convert::convert_point_to_point(point, copy); + traits::push_back::apply(geometry, copy); + } +}; + + +template +struct append_range +{ + typedef typename boost::range_value::type point_type; + + static inline void apply(Geometry& geometry, Range const& range, + int = 0, int = 0) + { + for (typename boost::range_iterator::type + it = boost::begin(range); + it != boost::end(range); + ++it) + { + append_point::apply(geometry, *it); + } + } +}; + + +template +struct point_to_polygon +{ + typedef typename ring_type::type range_type; + + static inline void apply(Polygon& polygon, Point const& point, + int ring_index, int = 0) + { + if (ring_index == -1) + { + append_point::apply( + exterior_ring(polygon), point); + } + else if (ring_index < int(num_interior_rings(polygon))) + { + append_point::apply( + interior_rings(polygon)[ring_index], point); + } + } +}; + + +template +struct range_to_polygon +{ + typedef typename ring_type::type ring_type; + + static inline void apply(Polygon& polygon, Range const& range, + int ring_index, int ) + { + if (ring_index == -1) + { + append_range::apply( + exterior_ring(polygon), range); + } + else if (ring_index < int(num_interior_rings(polygon))) + { + append_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 +struct append_point + : detail::append::append_no_action +{}; + +template +struct append_point + : detail::append::append_point +{}; + +template +struct append_point + : detail::append::append_point +{}; + + +template +struct append_point + : detail::append::point_to_polygon +{}; + + +template +struct append_range + : detail::append::append_no_action +{}; + +template +struct append_range + : detail::append::append_range +{}; + +template +struct append_range + : detail::append::append_range +{}; + + +template +struct append_range + : detail::append::range_to_polygon +{}; + +} + + +// Default: append a range (or linestring or ring or whatever) to any geometry +template +struct append + : splitted_dispatch::append_range::type, Geometry, RangeOrPoint> +{}; + +// Specialization for point to append a point to any geometry +template +struct append + : splitted_dispatch::append_point::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 +inline void append(Geometry& geometry, RangeOrPoint const& range_or_point, + int ring_index = -1, int multi_index = 0) +{ + concept::check(); + + dispatch::append + < + typename tag::type, + Geometry, + RangeOrPoint + >::apply(geometry, range_or_point, ring_index, multi_index); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_APPEND_HPP diff --git a/include/boost/geometry/algorithms/area.hpp b/include/boost/geometry/algorithms/area.hpp new file mode 100644 index 000000000..641d9adfb --- /dev/null +++ b/include/boost/geometry/algorithms/area.hpp @@ -0,0 +1,287 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace area +{ + +template +struct box_area +{ + typedef typename coordinate_type::type return_type; + + static inline return_type apply(Box const& box, Strategy const&) + { + // Currently only works for 2D Cartesian boxes + assert_dimension(); + + return_type const dx = get(box) + - get(box); + return_type const dy = get(box) + - get(box); + + return dx * dy; + } +}; + + +template +< + typename Ring, + iterate_direction Direction, + closure_selector Closure, + typename Strategy +> +struct ring_area +{ + BOOST_CONCEPT_ASSERT( (geometry::concept::AreaStrategy) ); + + typedef typename Strategy::return_type type; + + static inline type apply(Ring const& ring, Strategy const& strategy) + { + assert_dimension(); + + // 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::value) + { + return type(); + } + + typedef typename reversible_view::type rview_type; + typedef typename closeable_view + < + rview_type const, Closure + >::type view_type; + typedef typename boost::range_iterator::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 Tag, + typename Geometry, + typename Strategy +> +struct area + : detail::calculate_null + < + typename Strategy::return_type, + Geometry, + Strategy + > {}; + + +template +< + typename Geometry, + typename Strategy +> +struct area + : detail::area::box_area +{}; + + +template +< + typename Ring, + typename Strategy +> +struct area + : detail::area::ring_area + < + Ring, + order_as_direction::value>::value, + geometry::closure::value, + Strategy + > +{}; + + +template +< + typename Polygon, + typename Strategy +> +struct area + : detail::calculate_polygon_sum + < + typename Strategy::return_type, + Polygon, + Strategy, + detail::area::ring_area + < + typename ring_type::type, + order_as_direction::value>::value, + geometry::closure::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 +inline typename default_area_result::type area(Geometry const& geometry) +{ + concept::check(); + + typedef typename point_type::type point_type; + typedef typename strategy::area::services::default_strategy + < + typename cs_tag::type, + point_type + >::type strategy_type; + + return dispatch::area + < + typename tag::type, + Geometry, + strategy_type + >::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 +inline typename Strategy::return_type area( + Geometry const& geometry, Strategy const& strategy) +{ + concept::check(); + + return dispatch::area + < + typename tag::type, + Geometry, + Strategy + >::apply(geometry, strategy); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_AREA_HPP diff --git a/include/boost/geometry/algorithms/assign.hpp b/include/boost/geometry/algorithms/assign.hpp new file mode 100644 index 000000000..4df4e2d6f --- /dev/null +++ b/include/boost/geometry/algorithms/assign.hpp @@ -0,0 +1,673 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace assign +{ + +template +struct assign_operation +{ + inline assign_operation(CoordinateType const& value) + : m_value(value) + {} + + template + inline void apply(P& point) const + { + geometry::set(point, m_value); + } + +private: + + CoordinateType m_value; +}; + + +/*! + \brief Assigns all coordinates of a specific point to a value + \ingroup assign + \details + \param p Point + \param value Value which is assigned to all coordinates of point p + */ +template +inline void assign_value(Point& p, + typename coordinate_type::type const& value) +{ + for_each_coordinate(p, + assign_operation::type>(value)); +} + + + +template +< + typename Box, std::size_t Index, + std::size_t Dimension, std::size_t DimensionCount +> +struct initialize +{ + typedef typename coordinate_type::type coordinate_type; + + static inline void apply(Box& box, coordinate_type const& value) + { + geometry::set(box, value); + initialize::apply(box, value); + } +}; + + +template +struct initialize +{ + typedef typename coordinate_type::type coordinate_type; + + static inline void apply(Box&, coordinate_type const& ) + {} +}; + + +template +struct assign_zero_point +{ + static inline void apply(Point& point) + { + typedef typename coordinate_type::type coordinate_type; + assign_value(point, 0); + } +}; + + +template +struct assign_inverse_box_or_segment +{ + typedef typename point_type::type point_type; + + static inline void apply(BoxOrSegment& geometry) + { + typedef typename coordinate_type::type coordinate_type; + + typedef typename boost::mpl::if_ + < + typename boost::is_arithmetic::type, + coordinate_type, + double + >::type bound_type; + + initialize + < + BoxOrSegment, 0, 0, dimension::type::value + >::apply( + geometry, boost::numeric::bounds::highest()); + initialize + < + BoxOrSegment, 1, 0, dimension::type::value + >::apply( + geometry, boost::numeric::bounds::lowest()); + } +}; + + +template +struct assign_zero_box_or_segment +{ + static inline void apply(BoxOrSegment& geometry) + { + typedef typename coordinate_type::type coordinate_type; + + initialize + < + BoxOrSegment, 0, 0, dimension::type::value + >::apply(geometry, coordinate_type()); + initialize + < + BoxOrSegment, 1, 0, dimension::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(); + assert_dimension(); + + // Copy coordinates + typedef typename coordinate_type::type coordinate_type; + + geometry::set<0>(point, boost::numeric_cast(get(box))); + geometry::set<1>(point, boost::numeric_cast(get(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(geometry, boost::numeric_cast + < + typename coordinate_type::type + >(geometry::get(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( point, boost::numeric_cast + < + typename coordinate_type::type + >(geometry::get(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 +struct assign_2d_box_or_segment +{ + typedef typename coordinate_type::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 + 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(x1)); + geometry::set<0, 1>(geometry, boost::numeric_cast(y1)); + geometry::set<1, 0>(geometry, boost::numeric_cast(x2)); + geometry::set<1, 1>(geometry, boost::numeric_cast(y2)); + } +}; + + +}} // namespace detail::assign +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct assign +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types) + ); +}; + +template +struct assign +{ + typedef typename coordinate_type::type coordinate_type; + + template + static inline void apply(Point& point, T const& c1, T const& c2) + { + set<0>(point, boost::numeric_cast(c1)); + set<1>(point, boost::numeric_cast(c2)); + } +}; + +template +struct assign +{ + typedef typename coordinate_type::type coordinate_type; + + template + static inline void apply(Point& point, T const& c1, T const& c2, T const& c3) + { + set<0>(point, boost::numeric_cast(c1)); + set<1>(point, boost::numeric_cast(c2)); + set<2>(point, boost::numeric_cast(c3)); + } +}; + +template +struct assign + : detail::assign::assign_2d_box_or_segment +{}; + +template +struct assign + : detail::assign::assign_2d_box_or_segment +{}; + + + +template +struct assign_zero {}; + + +template +struct assign_zero + : detail::assign::assign_zero_point +{}; + +template +struct assign_zero + : detail::assign::assign_zero_box_or_segment +{}; + +template +struct assign_zero + : detail::assign::assign_zero_box_or_segment +{}; + + +template +struct assign_inverse {}; + +template +struct assign_inverse + : detail::assign::assign_inverse_box_or_segment +{}; + +template +struct assign_inverse + : detail::assign::assign_inverse_box_or_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 +inline void assign(Geometry& geometry, Type const& c1, Type const& c2) +{ + concept::check(); + + dispatch::assign + < + typename tag::type, + Geometry, + geometry::dimension::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 +inline void assign(Geometry& geometry, + Type const& c1, Type const& c2, Type const& c3) +{ + concept::check(); + + dispatch::assign + < + typename tag::type, + Geometry, + geometry::dimension::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 +inline void assign(Geometry& geometry, + Type const& c1, Type const& c2, Type const& c3, Type const& c4) +{ + concept::check(); + + dispatch::assign + < + typename tag::type, + Geometry, + geometry::dimension::type::value + >::apply(geometry, c1, c2, c3, c4); +} + + +/*! +\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{distinguish, with a range} +\qbk{ +[heading Notes] +[note Assign automatically clears the geometry before assigning (use append if you don't want that)] +[heading Example] +[assign_with_range] [assign_with_range_output] + +[heading See also] +\* [link geometry.reference.algorithms.make.make_1_with_a_range make] +\* [link geometry.reference.algorithms.append.append append] +} + */ +template +inline void assign(Geometry& geometry, Range const& range) +{ + concept::check(); + + 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 +inline void assign_inverse(Geometry& geometry) +{ + concept::check(); + + dispatch::assign_inverse + < + typename tag::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 +inline void assign_zero(Geometry& geometry) +{ + concept::check(); + + dispatch::assign_zero + < + typename tag::type, + Geometry + >::apply(geometry); +} + + +/*! +\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 +inline void assign_box_corners(Box const& box, + Point& lower_left, Point& lower_right, + Point& upper_left, Point& upper_right) +{ + concept::check(); + concept::check(); + + detail::assign::assign_box_2d_corner + (box, lower_left); + detail::assign::assign_box_2d_corner + (box, lower_right); + detail::assign::assign_box_2d_corner + (box, upper_left); + detail::assign::assign_box_2d_corner + (box, upper_right); +} + +template +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]); + } +} + + +/*! +\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 +inline void assign_point_to_index(Point const& point, Geometry& geometry) +{ + concept::check(); + concept::check(); + + detail::assign::assign_point_to_index + < + Geometry, Point, Index, 0, dimension::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 +inline void assign_point_from_index(Geometry const& geometry, Point& point) +{ + concept::check(); + concept::check(); + + detail::assign::assign_point_from_index + < + Geometry, Point, Index, 0, dimension::type::value + >::apply(geometry, point); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_ASSIGN_HPP diff --git a/include/boost/geometry/algorithms/buffer.hpp b/include/boost/geometry/algorithms/buffer.hpp new file mode 100644 index 000000000..543d27335 --- /dev/null +++ b/include/boost/geometry/algorithms/buffer.hpp @@ -0,0 +1,167 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include + + +#include +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + +template +struct box_loop +{ + typedef typename coordinate_type::type coordinate_type; + + static inline void apply(BoxIn const& box_in, T const& distance, BoxOut& box_out) + { + coordinate_type d = distance; + set(box_out, get(box_in) + d); + box_loop::apply(box_in, distance, box_out); + } +}; + +template +struct box_loop +{ + static inline void apply(BoxIn const&, T const&, BoxOut&) {} +}; + +// Extends a box with the same amount in all directions +template +inline void buffer_box(BoxIn const& box_in, T const& distance, BoxOut& box_out) +{ + assert_dimension_equal(); + + static const std::size_t N = dimension::value; + + box_loop::apply(box_in, -distance, box_out); + box_loop::apply(box_in, distance, box_out); +} + + + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct buffer {}; + + +template +struct buffer +{ + static inline void apply(BoxIn const& box_in, T const& distance, + T const& chord_length, 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 +inline void buffer(Input const& geometry_in, Output& geometry_out, + Distance const& distance, Distance const& chord_length = -1) +{ + concept::check(); + concept::check(); + + dispatch::buffer + < + typename tag::type, + typename tag::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 +Output return_buffer(Input const& geometry, T const& distance, T const& chord_length = -1) +{ + concept::check(); + concept::check(); + + Output geometry_out; + + dispatch::buffer + < + typename tag::type, + typename tag::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/include/boost/geometry/algorithms/centroid.hpp b/include/boost/geometry/algorithms/centroid.hpp new file mode 100644 index 000000000..a400e2e0b --- /dev/null +++ b/include/boost/geometry/algorithms/centroid.hpp @@ -0,0 +1,470 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + + +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 +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(box); + coordinate_type const c2 = get(box); + coordinate_type m = c1 + c2; + m /= 2.0; + + set(centroid, m); + + centroid_box_calculator + < + Box, Point, + Dimension + 1, DimensionCount + >::apply(box, centroid); + } +}; + + +template +struct centroid_box_calculator +{ + static inline void apply(Box const& , Point& ) + { + } +}; + + +template +struct centroid_box +{ + static inline void apply(Box const& box, Point& centroid, + Strategy const&) + { + centroid_box_calculator + < + Box, Point, + 0, dimension::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 +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 +struct centroid_range_state +{ + static inline void apply(Ring const& ring, + Strategy const& strategy, typename Strategy::state_type& state) + { + typedef typename closeable_view::type view_type; + + typedef typename boost::range_iterator::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 +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 +struct centroid_polygon_state +{ + typedef typename ring_type::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::value, + Strategy + > per_ring; + + per_ring::apply(exterior_ring(poly), strategy, state); + + typename interior_return_type::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 +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 + : detail::centroid::centroid_point +{}; + +template +< + typename Box, + typename Point, + typename Strategy +> +struct centroid + : detail::centroid::centroid_box +{}; + +template +struct centroid + : detail::centroid::centroid_range + < + Ring, + Point, + geometry::closure::value, + Strategy + > +{}; + +template +struct centroid + : detail::centroid::centroid_range + < + Linestring, + Point, + closed, + Strategy + > + {}; + +template +struct centroid + : detail::centroid::centroid_polygon + {}; + +} // 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 +inline void centroid(Geometry const& geometry, Point& c, + Strategy const& strategy) +{ + //BOOST_CONCEPT_ASSERT( (geometry::concept::CentroidStrategy) ); + + concept::check_concepts_and_equal_dimensions(); + + typedef typename point_type::type point_type; + + // Call dispatch apply method. That one returns true if centroid + // should be taken from state. + dispatch::centroid + < + typename tag::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 +inline void centroid(Geometry const& geometry, Point& c) +{ + concept::check_concepts_and_equal_dimensions(); + + typedef typename strategy::centroid::services::default_strategy + < + typename cs_tag::type, + typename tag_cast + < + typename tag::type, + pointlike_tag, + linear_tag, + areal_tag + >::type, + dimension::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 +inline Point return_centroid(Geometry const& geometry) +{ + concept::check_concepts_and_equal_dimensions(); + + 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 +inline Point return_centroid(Geometry const& geometry, Strategy const& strategy) +{ + //BOOST_CONCEPT_ASSERT( (geometry::concept::CentroidStrategy) ); + + concept::check_concepts_and_equal_dimensions(); + + Point c; + centroid(geometry, c, strategy); + return c; +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_CENTROID_HPP diff --git a/include/boost/geometry/algorithms/clear.hpp b/include/boost/geometry/algorithms/clear.hpp new file mode 100644 index 000000000..5635710d7 --- /dev/null +++ b/include/boost/geometry/algorithms/clear.hpp @@ -0,0 +1,155 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include + +#include +#include +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace clear +{ + +template +struct collection_clear +{ + static inline void apply(Geometry& geometry) + { + traits::clear::apply(geometry); + } +}; + +template +struct polygon_clear +{ + static inline void apply(Polygon& polygon) + { + traits::clear + < + typename boost::remove_reference + < + typename traits::interior_mutable_type::type + >::type + >::apply(interior_rings(polygon)); + traits::clear + < + typename boost::remove_reference + < + typename traits::ring_mutable_type::type + >::type + >::apply(exterior_ring(polygon)); + } +}; + +template +struct no_action +{ + static inline void apply(Geometry& ) + { + } +}; + +}} // namespace detail::clear +#endif // DOXYGEN_NO_DETAIL + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct clear +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types) + ); +}; + +// Point/box/segment do not have clear. So specialize to do nothing. +template +struct clear + : detail::clear::no_action +{}; + +template +struct clear + : detail::clear::no_action +{}; + +template +struct clear + : detail::clear::no_action +{}; + +template +struct clear + : detail::clear::collection_clear +{}; + +template +struct clear + : detail::clear::collection_clear +{}; + + +// Polygon can (indirectly) use std for clear +template +struct clear + : detail::clear::polygon_clear +{}; + + +} // 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 +\note points and boxes cannot be cleared, instead they can be set to zero by "assign_zero" +*/ +template +inline void clear(Geometry& geometry) +{ + concept::check(); + + dispatch::clear + < + typename tag_cast::type, multi_tag>::type, + Geometry + >::apply(geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_CLEAR_HPP diff --git a/include/boost/geometry/algorithms/comparable_distance.hpp b/include/boost/geometry/algorithms/comparable_distance.hpp new file mode 100644 index 000000000..57837a5ed --- /dev/null +++ b/include/boost/geometry/algorithms/comparable_distance.hpp @@ -0,0 +1,69 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + + +namespace boost { namespace geometry +{ + + +/*! +\brief \brief_calc2{comparable distance measurement} +\ingroup distance +\details The default strategy is used, belonging to the corresponding coordinate system of the geometries + and the comparable strategy is used +\tparam Geometry1 first geometry type +\tparam Geometry2 second geometry type +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_calc{comparable distance} + */ +template +inline typename default_distance_result::type comparable_distance( + Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check(); + concept::check(); + + typedef typename point_type::type point1_type; + typedef typename point_type::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 + ::type::value, + typename strategy::distance::services::default_strategy + ::type, + typename strategy::distance::services::default_strategy + ::type + >::type + >::type strategy_type; + + return distance(geometry1, geometry2, strategy_type()); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_COMPARABLE_DISTANCE_HPP diff --git a/include/boost/geometry/algorithms/convert.hpp b/include/boost/geometry/algorithms/convert.hpp new file mode 100644 index 000000000..607898a3f --- /dev/null +++ b/include/boost/geometry/algorithms/convert.hpp @@ -0,0 +1,264 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include +#include + +#include +#include +#include +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace convert +{ + +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::type coordinate_type; + + set(box, + boost::numeric_cast(get(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 +{ + static inline void apply(Point const& , Box& ) + {} +}; + + +}} // namespace detail::convert +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +< + typename Tag1, typename Tag2, + std::size_t DimensionCount, + typename Geometry1, typename Geometry2 +> +struct convert +{ +}; + + +template +< + typename Tag, + std::size_t DimensionCount, + typename Geometry1, typename Geometry2 +> +struct convert +{ + // Same geometry type -> copy coordinates from G1 to G2 + // Actually: we try now to just copy it + static inline void apply(Geometry1 const& source, Geometry2& destination) + { + destination = source; + } +}; + + +template +< + std::size_t DimensionCount, + typename Geometry1, typename Geometry2 +> +struct convert + : detail::convert::point_to_point +{}; + + +template +struct convert +{ + static inline void apply(Ring1 const& source, Ring2& destination) + { + geometry::clear(destination); + for (typename boost::range_iterator::type it + = boost::begin(source); + it != boost::end(source); + ++it) + { + geometry::append(destination, *it); + } + } +}; + + +template +struct convert +{ + static inline void apply(Box const& box, Ring& ring) + { + // go from box to ring -> add coordinates in correct order + geometry::clear(ring); + typename point_type::type point; + + geometry::assign(point, get(box), get(box)); + geometry::append(ring, point); + + geometry::assign(point, get(box), get(box)); + geometry::append(ring, point); + + geometry::assign(point, get(box), get(box)); + geometry::append(ring, point); + + geometry::assign(point, get(box), get(box)); + geometry::append(ring, point); + + geometry::assign(point, get(box), get(box)); + geometry::append(ring, point); + } +}; + + +template +struct convert +{ + static inline void apply(Box const& box, Polygon& polygon) + { + typedef typename ring_type::type ring_type; + + convert + < + box_tag, ring_tag, + 2, Box, ring_type + >::apply(box, exterior_ring(polygon)); + } +}; + + +template +struct convert +{ + static inline void apply(Point const& point, Box& box) + { + detail::convert::point_to_box + < + Point, Box, min_corner, 0, DimensionCount + >::apply(point, box); + detail::convert::point_to_box + < + Point, Box, max_corner, 0, DimensionCount + >::apply(point, box); + } +}; + + +template +struct convert +{ + static inline void apply(Ring const& ring, Polygon& polygon) + { + typedef typename ring_type::type ring_type; + convert + < + ring_tag, ring_tag, DimensionCount, + Ring, ring_type + >::apply(ring, exterior_ring(polygon)); + } +}; + + +template +struct convert +{ + static inline void apply(Polygon const& polygon, Ring& ring) + { + typedef typename ring_type::type ring_type; + + convert + < + ring_tag, ring_tag, DimensionCount, + ring_type, Ring + >::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. +\ingroup convert +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry (source) +\param geometry2 \param_geometry (target) + */ +template +inline void convert(Geometry1 const& geometry1, Geometry2& geometry2) +{ + concept::check_concepts_and_equal_dimensions(); + + dispatch::convert + < + typename tag::type, + typename tag::type, + dimension::type::value, + Geometry1, + Geometry2 + >::apply(geometry1, geometry2); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_CONVERT_HPP diff --git a/include/boost/geometry/algorithms/convex_hull.hpp b/include/boost/geometry/algorithms/convex_hull.hpp new file mode 100644 index 000000000..bde0f6fbd --- /dev/null +++ b/include/boost/geometry/algorithms/convex_hull.hpp @@ -0,0 +1,249 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include +#include + +#include + +#include + +#include +#include + +#include + + +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 + 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 OutputGeometry, + typename Strategy +> +struct hull_to_geometry +{ + static inline void apply(Geometry const& geometry, OutputGeometry& out, + Strategy const& strategy) + { + hull_insert + < + Geometry, + geometry::point_order::value, + Strategy + >::apply(geometry, + std::back_inserter( + // Handle both ring and polygon the same: + geometry::as_range + < + typename geometry::range_type::type + >(out)), strategy); + } +}; + + +}} // namespace detail::convex_hull +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +< + typename Tag1, + typename Geometry, + typename Output, + typename Strategy +> +struct convex_hull + : detail::convex_hull::hull_to_geometry +{}; + + +template +< + typename GeometryTag, + order_selector Order, + typename GeometryIn, typename Strategy + > +struct convex_hull_insert + : detail::convex_hull::hull_insert +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +template +inline void convex_hull(Geometry1 const& geometry, + Geometry2& out, Strategy const& strategy) +{ + concept::check_concepts_and_equal_dimensions + < + const Geometry1, + Geometry2 + >(); + + BOOST_CONCEPT_ASSERT( (geometry::concept::ConvexHullStrategy) ); + + + dispatch::convex_hull + < + typename tag::type, + Geometry1, + Geometry2, + 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, used for input +\param hull \param_geometry \param_set{convex hull} + */ +template +inline void convex_hull(Geometry1 const& geometry, + Geometry2& hull) +{ + concept::check_concepts_and_equal_dimensions + < + const Geometry1, + Geometry2 + >(); + + //typedef typename range_type::type range_type; + typedef typename point_type::type point_type; + + typedef typename strategy_convex_hull + < + typename cs_tag::type, + Geometry1, + point_type + >::type strategy_type; + + convex_hull(geometry, hull, strategy_type()); +} + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace convex_hull +{ + + +template +inline OutputIterator convex_hull_insert(Geometry const& geometry, + OutputIterator out, Strategy const& strategy) +{ + // Concept: output point type = point type of input geometry + concept::check(); + concept::check::type>(); + + BOOST_CONCEPT_ASSERT( (geometry::concept::ConvexHullStrategy) ); + + return dispatch::convex_hull_insert + < + typename tag::type, + geometry::point_order::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 +inline OutputIterator convex_hull_insert(Geometry const& geometry, + OutputIterator out) +{ + // Concept: output point type = point type of input geometry + concept::check(); + concept::check::type>(); + + typedef typename range_type::type range_type; + typedef typename point_type::type point_type; + + typedef typename strategy_convex_hull + < + typename cs_tag::type, + Geometry, + point_type + >::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/include/boost/geometry/algorithms/correct.hpp b/include/boost/geometry/algorithms/correct.hpp new file mode 100644 index 000000000..79e47c1e9 --- /dev/null +++ b/include/boost/geometry/algorithms/correct.hpp @@ -0,0 +1,265 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace correct +{ + +template +struct correct_nop +{ + static inline void apply(Geometry& ) + {} +}; + + +template +struct correct_box_loop +{ + typedef typename coordinate_type::type coordinate_type; + + static inline void apply(Box& box) + { + if (get(box) > get(box)) + { + // Swap the coordinates + coordinate_type max_value = get(box); + coordinate_type min_value = get(box); + set(box, min_value); + set(box, max_value); + } + + correct_box_loop + < + Box, Dimension + 1, DimensionCount + >::apply(box); + } +}; + + + +template +struct correct_box_loop +{ + static inline void apply(Box& box) + {} + +}; + + +// Correct a box: make min/max correct +template +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::type::value + >::apply(box); + } +}; + + +// Close a ring, if not closed +template +struct correct_ring +{ + typedef typename point_type::type point_type; + typedef typename coordinate_type::type coordinate_type; + + typedef typename strategy::area::services::default_strategy + < + typename cs_tag::type, + point_type + >::type strategy_type; + + typedef detail::area::ring_area + < + Ring, + order_as_direction::value>::value, + geometry::closure::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::value; + + if (disjoint && (s == closed)) + { + geometry::append(r, *boost::begin(r)); + } + if (! disjoint && geometry::closure::value != closed) + { + // Open it by removing last point + geometry::traits::resize::apply(r, boost::size(r) - 1); + } + } + // Check area + Predicate predicate; + coordinate_type const zero = 0; + 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 +struct correct_polygon +{ + typedef typename ring_type::type ring_type; + typedef typename coordinate_type::type coordinate_type; + + static inline void apply(Polygon& poly) + { + correct_ring + < + ring_type, + std::less + >::apply(exterior_ring(poly)); + + typename interior_return_type::type rings + = interior_rings(poly); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + correct_ring + < + ring_type, + std::greater + >::apply(*it); + } + } +}; + + +}} // namespace detail::correct +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct correct +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types) + ); +}; + +template +struct correct + : detail::correct::correct_nop +{}; + +template +struct correct + : detail::correct::correct_nop +{}; + +template +struct correct + : detail::correct::correct_nop +{}; + + +template +struct correct + : detail::correct::correct_box +{}; + +template +struct correct + : detail::correct::correct_ring + < + Ring, + std::less::type> + > +{}; + +template +struct correct + : detail::correct::correct_polygon +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Corrects a geometry +\details Corrects a geometry +\ingroup correct +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +*/ +template +inline void correct(Geometry& geometry) +{ + concept::check(); + + dispatch::correct::type, Geometry>::apply(geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_CORRECT_HPP diff --git a/include/boost/geometry/algorithms/detail/calculate_null.hpp b/include/boost/geometry/algorithms/detail/calculate_null.hpp new file mode 100644 index 000000000..aa64a5e92 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/calculate_null.hpp @@ -0,0 +1,38 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +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/include/boost/geometry/algorithms/detail/calculate_sum.hpp b/include/boost/geometry/algorithms/detail/calculate_sum.hpp new file mode 100644 index 000000000..a95fb0122 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/calculate_sum.hpp @@ -0,0 +1,64 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + + +template +< + typename ReturnType, + typename Polygon, + typename Strategy, + typename Policy +> +class calculate_polygon_sum +{ + template + 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/include/boost/geometry/algorithms/detail/convert_point_to_point.hpp b/include/boost/geometry/algorithms/detail/convert_point_to_point.hpp new file mode 100644 index 000000000..48981ce09 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/convert_point_to_point.hpp @@ -0,0 +1,68 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace convert +{ + + +template +struct point_to_point +{ + static inline void apply(Source const& source, Destination& destination) + { + typedef typename coordinate_type::type coordinate_type; + + set(destination, boost::numeric_cast(get(source))); + point_to_point::apply(source, destination); + } +}; + +template +struct point_to_point +{ + static inline void apply(Source const& , Destination& ) + {} +}; + + +template +inline void convert_point_to_point(Source const& source, Destination& destination) +{ + point_to_point::value>::apply(source, destination); +} + + + +}} // namespace detail::convert +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CONVERT_POINT_TO_POINT_HPP diff --git a/include/boost/geometry/algorithms/detail/disjoint.hpp b/include/boost/geometry/algorithms/detail/disjoint.hpp new file mode 100644 index 000000000..8d565bc0f --- /dev/null +++ b/include/boost/geometry/algorithms/detail/disjoint.hpp @@ -0,0 +1,225 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include + +#include +#include +#include + + +#include + + + +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 + 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(p1), get(p2))) + { + return true; + } + return point_point + < + Point1, Point2, + Dimension + 1, DimensionCount + >::apply(p1, p2); + } +}; + + +template +struct point_point +{ + 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(point) < get(box) + || get(point) > get(box)) + { + return true; + } + return point_box + < + Point, Box, + Dimension + 1, DimensionCount + >::apply(point, box); + } +}; + + +template +struct point_box +{ + 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(box1) < get(box2)) + { + return true; + } + if (get(box1) > get(box2)) + { + return true; + } + return box_box + < + Box1, Box2, + Dimension + 1, DimensionCount + >::apply(box1, box2); + } +}; + + +template +struct box_box +{ + 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 +inline bool disjoint_box_box(Box1 const& box1, Box2 const& box2) +{ + return box_box + < + Box1, Box2, + 0, dimension::type::value + >::apply(box1, box2); +} + + + +/*! + \brief Internal utility function to detect of points are disjoint + \note To avoid circular references + */ +template +inline bool disjoint_point_point(Point1 const& point1, Point2 const& point2) +{ + return point_point + < + Point1, Point2, + 0, dimension::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 +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/include/boost/geometry/algorithms/detail/equals/collect_vectors.hpp b/include/boost/geometry/algorithms/detail/equals/collect_vectors.hpp new file mode 100644 index 000000000..4c67dfe27 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/equals/collect_vectors.hpp @@ -0,0 +1,306 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include + + + +namespace boost { namespace geometry +{ + +// TODO: if Boost.LA of Emil Dotchevski is accepted, adapt this +template +struct collected_vector +{ + typedef T type; + + collected_vector() + {} + + 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; + + bool operator<(collected_vector 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 const& other) const + { + return math::equals(dx, other.dx) + && math::equals(dy, other.dy); + } + + inline bool operator==(collected_vector 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 +struct range_collect_vectors +{ + typedef typename boost::range_value::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::type iterator; + + bool first = true; + iterator it = boost::begin(range); + + for (iterator prev = it++; + it != boost::end(range); + prev = it++) + { + typename boost::range_value::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(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; + } + } + // TODO: if first one has same direction as last one, remove first one... + } +}; + + +template +struct box_collect_vectors +{ + // Calculate on coordinate type, but if it is integer, + // then use double + typedef typename boost::range_value::type item_type; + typedef typename item_type::type calculation_type; + + static inline void apply(Collection& collection, Box const& box) + { + typename point_type::type lower_left, lower_right, + upper_left, upper_right; + assign_box_corners(box, lower_left, lower_right, + upper_left, upper_right); + + typedef typename boost::range_value::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 +struct polygon_collect_vectors +{ + static inline void apply(Collection& collection, Polygon const& polygon) + { + typedef typename geometry::ring_type::type ring_type; + + typedef range_collect_vectors per_range; + per_range::apply(collection, exterior_ring(polygon)); + + typename interior_return_type::type rings + = interior_rings(polygon); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + per_range::apply(collection, *it); + } + } +}; + + +template +struct multi_collect_vectors +{ + static inline void apply(Collection& collection, MultiGeometry const& multi) + { + for (typename boost::range_iterator::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 +struct collect_vectors + : detail::collect_vectors::box_collect_vectors +{}; + + + +template +struct collect_vectors + : detail::collect_vectors::range_collect_vectors +{}; + + +template +struct collect_vectors + : detail::collect_vectors::range_collect_vectors +{}; + + +template +struct collect_vectors + : detail::collect_vectors::polygon_collect_vectors +{}; + + +template +struct collect_vectors + : detail::collect_vectors::multi_collect_vectors + < + MultiPolygon, + Collection, + detail::collect_vectors::polygon_collect_vectors + < + typename boost::range_value::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 +inline void collect_vectors(Collection& collection, Geometry const& geometry) +{ + concept::check(); + + dispatch::collect_vectors + < + typename tag::type, + Collection, + Geometry + >::apply(collection, geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP diff --git a/include/boost/geometry/algorithms/detail/for_each_range.hpp b/include/boost/geometry/algorithms/detail/for_each_range.hpp new file mode 100644 index 000000000..461bb17ce --- /dev/null +++ b/include/boost/geometry/algorithms/detail/for_each_range.hpp @@ -0,0 +1,125 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace for_each +{ + + +template +struct fe_range_range +{ + static inline void apply( + typename add_const_if_c::type& range, + Actor& actor) + { + actor.apply(range); + } +}; + + +template +struct fe_range_polygon +{ + static inline void apply( + typename add_const_if_c::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 + } +}; + + +}} // 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 {}; + + +template +struct for_each_range + : detail::for_each::fe_range_range +{}; + + +template +struct for_each_range + : detail::for_each::fe_range_range +{}; + + +template +struct for_each_range + : detail::for_each::fe_range_polygon +{}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + +namespace detail +{ + +template +inline void for_each_range(Geometry const& geometry, Actor& actor) +{ + dispatch::for_each_range + < + typename tag::type, + Geometry, + Actor, + true + >::apply(geometry, actor); +} + + +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_FOR_EACH_RANGE_HPP diff --git a/include/boost/geometry/algorithms/detail/not.hpp b/include/boost/geometry/algorithms/detail/not.hpp new file mode 100644 index 000000000..cd50f28f4 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/not.hpp @@ -0,0 +1,50 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +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/include/boost/geometry/algorithms/detail/overlay/add_rings.hpp b/include/boost/geometry/algorithms/detail/overlay/add_rings.hpp new file mode 100644 index 000000000..12248393f --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/add_rings.hpp @@ -0,0 +1,134 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include + + +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::type tag1; + typedef typename geometry::tag::type tag2; + typedef typename geometry::tag::type tag_out; + + if (id.source_index == 0) + { + convert_ring::apply(result, + get_ring::apply(id, geometry1), + append, reversed); + } + else if (id.source_index == 1) + { + convert_ring::apply(result, + get_ring::apply(id, geometry2), + append, reversed); + } + else if (id.source_index == 2) + { + convert_ring::apply(result, + get_ring::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::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(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/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp new file mode 100644 index 000000000..90ba4164b --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -0,0 +1,336 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include +#include +#include +#include +#include + + +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::type tag1; + typedef typename geometry::tag::type tag2; + + int code = -1; + switch (ring_id.source_index) + { + case 0 : + code = point_in_ring(item2.point, + get_ring::apply(ring_id, geometry1)); + break; + case 1 : + code = point_in_ring(item2.point, + get_ring::apply(ring_id, geometry2)); + break; + case 2 : + code = point_in_ring(item2.point, + get_ring::apply(ring_id, collection)); + break; + } + return code == 1; +} + + +template +struct ring_info_helper +{ + typedef typename geometry::default_area_result::type area_type; + + ring_identifier id; + area_type real_area; + area_type abs_area; + model::box 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(abs(a)) + {} +}; + + +struct ring_info_helper_get_box +{ + template + static inline void apply(Box& total, InputItem const& item) + { + geometry::expand(total, item.envelope); + } +}; + +struct ring_info_helper_ovelaps_box +{ + template + static inline bool apply(Box const& box, InputItem const& item) + { + return ! geometry::detail::disjoint::disjoint_box_box(box, item.envelope); + } +}; + +template +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 + 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::type tag1; + typedef typename geometry::tag::type tag2; + + typedef typename RingMap::mapped_type ring_info_type; + typedef typename ring_info_type::point_type point_type; + typedef model::box box_type; + + typedef typename RingMap::iterator map_iterator_type; + + { + typedef ring_info_helper helper; + typedef std::vector vector_type; + typedef typename boost::range_iterator::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::apply(it->first, geometry1), + item.envelope); + break; + case 1 : + geometry::envelope(get_ring::apply(it->first, geometry2), + item.envelope); + break; + case 2 : + geometry::envelope(get_ring::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/include/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp b/include/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp new file mode 100644 index 000000000..a88060efb --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/calculate_distance_policy.hpp @@ -0,0 +1,52 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + + +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 +{ + template + static inline void apply(Info& info, Point1 const& p1, Point2 const& p2) + { + info.operations[0].enriched.distance + = geometry::distance(info.point, p1); + info.operations[1].enriched.distance + = geometry::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/include/boost/geometry/algorithms/detail/overlay/check_enrich.hpp b/include/boost/geometry/algorithms/detail/overlay/check_enrich.hpp new file mode 100644 index 000000000..ddb3394c0 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/check_enrich.hpp @@ -0,0 +1,172 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + + +#include +#include + + + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template +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 +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 +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 +inline bool check_graph(TurnPoints& turn_points, operation_type for_operation) +{ + typedef typename boost::range_value::type turn_point_type; + + bool error = false; + int index = 0; + + std::vector > meta_turns; + for (typename boost::range_iterator::type + it = boost::begin(turn_points); + it != boost::end(turn_points); + ++it, ++index) + { + meta_turns.push_back(meta_turn(index, *it)); + } + + int cycle = 0; + for (typename boost::range_iterator > > ::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/include/boost/geometry/algorithms/detail/overlay/clip_linestring.hpp b/include/boost/geometry/algorithms/detail/overlay/clip_linestring.hpp new file mode 100644 index 000000000..6e8a7f6c3 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/clip_linestring.hpp @@ -0,0 +1,241 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + +#include +#include +#include + +#include +#include + +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 +class liang_barsky +{ +private: + typedef model::referring_segment segment_type; + + template + 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::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(b); + coordinate_type const q2 = get(b) - get<0, 0>(s); + coordinate_type const q3 = get<0, 1>(s) - get(b); + coordinate_type const q4 = get(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 + 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::type point_type; + + OutputLinestring line_out; + + typedef typename boost::range_iterator::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 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)) + { + geometry::append(line_out, p1); + } + geometry::append(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/include/boost/geometry/algorithms/detail/overlay/convert_ring.hpp b/include/boost/geometry/algorithms/detail/overlay/convert_ring.hpp new file mode 100644 index 000000000..d81826f51 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/convert_ring.hpp @@ -0,0 +1,99 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include +#include + +#include +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template +struct convert_ring +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TAG + , (types) + ); +}; + +template<> +struct convert_ring +{ + template + 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 +{ + template + 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/include/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp b/include/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp new file mode 100644 index 000000000..ead786280 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp @@ -0,0 +1,295 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace copy_segments +{ + + +template +struct copy_segment_point_range +{ + typedef typename closeable_view + < + Range const, + closure::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 +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::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 +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::type, 4> bp; + assign_box_corners_oriented(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) + ); +}; + + +template +struct copy_segment_point + : detail::copy_segments::copy_segment_point_range + < + LineString, Reverse, SegmentIdentifier, PointOut + > +{}; + + +template +struct copy_segment_point + : detail::copy_segments::copy_segment_point_range + < + Ring, Reverse, SegmentIdentifier, PointOut + > +{}; + +template +struct copy_segment_point + : detail::copy_segments::copy_segment_point_polygon + < + Polygon, Reverse, SegmentIdentifier, PointOut + > +{}; + + +template +struct copy_segment_point + : 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 +inline bool copy_segment_point(Geometry const& geometry, + SegmentIdentifier const& seg_id, bool second, + PointOut& point_out) +{ + concept::check(); + + return dispatch::copy_segment_point + < + typename tag::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(); + concept::check(); + + if (seg_id.source_index == 0) + { + return dispatch::copy_segment_point + < + typename tag::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::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(); + concept::check(); + + return copy_segment_point(geometry1, geometry2, seg_id, false, point1) + && copy_segment_point(geometry1, geometry2, seg_id, true, point2); +} + + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_COPY_SEGMENT_POINT_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp b/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp new file mode 100644 index 000000000..0b54de39f --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/copy_segments.hpp @@ -0,0 +1,282 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +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::value + >::type cview_type; + + typedef typename reversible_view + < + cview_type const, + Reverse ? iterate_reverse : iterate_forward + >::type rview_type; + + typedef typename boost::range_iterator::type iterator; + typedef geometry::ever_circling_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::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) + { +#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION + std::cout << " add: (" + << geometry::get<0>(*it) << ", " << geometry::get<1>(*it) << ")" + << std::endl; +#endif + geometry::append(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::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::type, 5> bp; + assign_box_corners_oriented(box, bp); + bp[4] = bp[0]; + + // (possibly cyclic) copy to output + // (see comments in ring-version) + for (int i = 0; i < count; i++, index++) + { + geometry::append(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) + ); +}; + + +template +< + typename Ring, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments + : detail::copy_segments::copy_segments_ring + < + Ring, Reverse, SegmentIdentifier, RangeOut + > +{}; + + +template +< + typename Polygon, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments + : detail::copy_segments::copy_segments_polygon + < + Polygon, Reverse, SegmentIdentifier, RangeOut + > +{}; + + +template +< + typename Box, + bool Reverse, + typename SegmentIdentifier, + typename RangeOut +> +struct copy_segments + : 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(); + + dispatch::copy_segments + < + typename tag::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/include/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp new file mode 100644 index 000000000..9359c8f65 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp @@ -0,0 +1,64 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include + + +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/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp new file mode 100644 index 000000000..5a13b5240 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -0,0 +1,531 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include +#include +#include +#include + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +# include +# include +# include +# define BOOST_GEOMETRY_DEBUG_IDENTIFIER +#endif + +#include +#include + + + +#include +#include +#include + +#include + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH +# include +#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 +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 +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::type point_type; + point_type pi, pj, ri, rj, si, sj; + + geometry::copy_segment_points(m_geometry1, m_geometry2, + left.subject.seg_id, + pi, pj); + geometry::copy_segment_points(m_geometry1, m_geometry2, + left.subject.other_id, + ri, rj); + geometry::copy_segment_points(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 +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::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::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(begin_cluster, it, turn_points, + for_operation, geometry1, geometry2, strategy); + begin_cluster = boost::end(operations); + } + } + if (begin_cluster != boost::end(operations)) + { + handle_cluster(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 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 for_operation, + Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + typedef typename IndexType::type operations_type; + typedef typename boost::range_iterator::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(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 +inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vector) +{ + typedef typename boost::range_value::type turn_point_type; + typedef typename turn_point_type::container_type container_type; + + int index = 0; + for (typename boost::range_iterator::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::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::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 + > 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::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(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(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(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(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/include/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp b/include/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp new file mode 100644 index 000000000..132b2dbf1 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/enrichment_info.hpp @@ -0,0 +1,66 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + + +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 +struct enrichment_info +{ + typedef typename default_distance_result::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/include/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp b/include/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp new file mode 100644 index 000000000..4a0fdc952 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/get_intersection_points.hpp @@ -0,0 +1,144 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + +#include +#include + + +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::type, + Point1, + Point2, + typename TurnInfo::point_type + > si; + + typedef typename si::segment_intersection_strategy_type strategy; + + + + template + 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 segment_type1; + typedef model::referring_segment 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(); + + typedef detail::get_intersection_points::get_turn_without_info + < + typename point_type::type, + typename point_type::type, + typename boost::range_value::type + > TurnPolicy; + + typedef typename strategy_intersection + < + typename cs_tag::type, + Geometry1, + Geometry2, + typename boost::range_value::type + >::segment_intersection_strategy_type segment_intersection_strategy_type; + + detail::get_turns::no_interrupt_policy interrupt_policy; + + boost::mpl::if_c + < + reverse_dispatch::type::value, + dispatch::get_turns_reversed + < + typename tag::type, + typename tag::type, + Geometry1, Geometry2, + false, false, + Turns, TurnPolicy, + //segment_intersection_strategy_type, + detail::get_turns::no_interrupt_policy + >, + dispatch::get_turns + < + typename tag::type, + typename tag::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/include/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp b/include/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp new file mode 100644 index 000000000..60744dbe1 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/get_relative_order.hpp @@ -0,0 +1,108 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + +#include + + +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 +struct get_relative_order +{ + typedef strategy_intersection + < + typename cs_tag::type, + Point1, + Point1, + Point1 + > si; + + typedef typename si::side_strategy_type strategy; + + template + 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/include/boost/geometry/algorithms/detail/overlay/get_ring.hpp b/include/boost/geometry/algorithms/detail/overlay/get_ring.hpp new file mode 100644 index 000000000..346a927d5 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/get_ring.hpp @@ -0,0 +1,102 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include + + +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template +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 +{ + template + static inline typename boost::range_value::type const& + apply(ring_identifier const& id, Container const& container) + { + return container[id.multi_index]; + } +}; + + + + +template<> +struct get_ring +{ + template + static inline Ring const& apply(ring_identifier const& , Ring const& ring) + { + return ring; + } +}; + + +template<> +struct get_ring +{ + template + static inline Box const& apply(ring_identifier const& , + Box const& box) + { + return box; + } +}; + + +template<> +struct get_ring +{ + template + static inline typename ring_return_type::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/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp new file mode 100644 index 000000000..2e7195310 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -0,0 +1,895 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +#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 + 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 + 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 + 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& pk, + 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() == 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(); + 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& qi, Point2 const& qj, Point2 const& qk, + TurnInfo& ti, + IntersectionInfo const& intersection_info, + DirInfo const& dir_info) + { + 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& 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_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; + } +}; + + +/*! +\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 +{ + template + static inline void apply(Info& info, Point1 const& p1, Point2 const& p2) + {} + +}; + + +/*! + \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 + */ +template +< + typename Point1, + typename Point2, + typename TurnInfo, + typename AssignPolicy = assign_null_policy +> +struct get_turn_info +{ + typedef strategy_intersection + < + typename cs_tag::type, + Point1, + Point2, + typename TurnInfo::point_type + > si; + + typedef typename si::segment_intersection_strategy_type strategy; + + + + template + 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 segment_type1; + typedef model::referring_segment 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' : + case 'f' : + case 's' : + case 'd' : + 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 + break; + default : +#ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS + std::cout << "get_turns, nyi: " << method << std::endl; +#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/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp new file mode 100644 index 000000000..78d32a428 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -0,0 +1,841 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + + +#include + +#include +#include + +#ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION +# include +# include +#endif + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace get_turns +{ + + +struct no_interrupt_policy +{ + static bool const enabled = false; + + template + 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::type const, + closure::value + >::type cview_type1; + typedef typename closeable_view + < + typename range_type::type const, + closure::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; + + + +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, + 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 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 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 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 sement. + // (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!) + + // About n-2: + // (square: range_count=5, indices 0,1,2,3 + // -> 0-3 are adjacent) + skip = index2 >= index1 + || ndi1 == ndi2 + 1 + || (index2 == 0 && index1 >= int(sec1.range_count) - 2) + ; + } + + if (! skip) + { + // Move to the "non duplicate next" + ever_circling_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::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::type point1_type; + typedef typename geometry::point_type::type point2_type; + typedef typename model::referring_segment segment1_type; + typedef typename model::referring_segment segment2_type; + + + template + static inline bool preceding(int dir, Point const& point, Box const& box) + { + return (dir == 1 && get(point) < get(box)) + || (dir == -1 && get(point) > get(box)); + } + + template + static inline bool exceeding(int dir, Point const& point, Box const& box) + { + return (dir == 1 && get(point) > get(box)) + || (dir == -1 && get(point) < get(box)); + } + + template + 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 + static inline void get_start_point_iterator(Section & section, + Range const& range, + typename boost::range_iterator::type& it, + typename boost::range_iterator::type& prev, + typename boost::range_iterator::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 + static inline void apply(Box& total, InputItem const& item) + { + geometry::expand(total, item.bounding_box); + } +}; + +struct ovelaps_section_box +{ + template + 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 + 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, + 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::type ip_type; + typedef typename ip_type::point_type point_type; + typedef model::box box_type; + typedef typename geometry::sections sections_type; + + sections_type sec1, sec2; + + geometry::sectionalize(geometry1, sec1, 0); + geometry::sectionalize(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::type turn_info; + typedef typename geometry::point_type::type point_type; + typedef typename geometry::point_type::type box_point_type; + + typedef typename closeable_view + < + Range const, + closure::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 bp; + assign_box_corners_oriented(box, bp); + + cview_type cview(range); + view_type view(cview); + + iterator_type it = boost::begin(view); + + ever_circling_iterator 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 + 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::type const& c = get(point); + typename coordinate_type::type const& left = get(box); + typename coordinate_type::type const& right = get(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::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::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::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(); + + typedef typename strategy_intersection + < + typename cs_tag::type, + Geometry1, + Geometry2, + typename boost::range_value::type + >::segment_intersection_strategy_type segment_intersection_strategy_type; + + typedef detail::overlay::get_turn_info + < + typename point_type::type, + typename point_type::type, + typename boost::range_value::type, + AssignPolicy + > TurnPolicy; + + boost::mpl::if_c + < + reverse_dispatch::type::value, + dispatch::get_turns_reversed + < + typename tag::type, + typename tag::type, + Geometry1, Geometry2, + Reverse1, Reverse2, + Turns, TurnPolicy, + InterruptPolicy + >, + dispatch::get_turns + < + typename tag::type, + typename tag::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/include/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp b/include/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp new file mode 100644 index 000000000..6c1ac0a4e --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp @@ -0,0 +1,665 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + + +#include +#include +#include + + +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::type point_type; + typedef model::referring_segment segment_type; + + // Determine how p/r and p/s are located. + template + 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 + > + > 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 pr = policy::apply(p, r); + segment_intersection_points ps = policy::apply(p, s); + segment_intersection_points rs = policy::apply(r, s); + + // Check on overlap + pr_overlap = pr.count == 2; + ps_overlap = ps.count == 2; + rs_overlap = rs.count == 2; + } + + + 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; + + +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + point_type pi, pj, ri, rj, si, sj; + geometry::copy_segment_points(m_geometry1, m_geometry2, + left.subject.seg_id, + pi, pj); + geometry::copy_segment_points(m_geometry1, m_geometry2, + left.subject.other_id, + ri, rj); + geometry::copy_segment_points(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; +#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(m_geometry1, m_geometry2, + left.subject.seg_id, + pi, pj); + geometry::copy_segment_points(m_geometry1, m_geometry2, + left.subject.other_id, + ri, rj); + geometry::copy_segment_points(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(m_geometry1, m_geometry2, + left.subject.seg_id, + pi, pj); + geometry::copy_segment_points(m_geometry1, m_geometry2, + left.subject.other_id, + ri, rj); + geometry::copy_segment_points(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 for_operation, + Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + int count = 0; + + // Make an analysis about all occuring cases here. + std::map, 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(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/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp new file mode 100644 index 000000000..75d240bc2 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -0,0 +1,485 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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& strategy) + { + typedef typename point_type::type point_type; + + // Get the intersection point (or two points) + segment_intersection_points is + = strategy::intersection::relate_cartesian_segments + < + policies::relate::segments_intersection_points + < + Segment1, + Segment2, + segment_intersection_points + > + >::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& strategy) + { + typedef typename point_type::type point_type; + + typedef detail::overlay::turn_info turn_info; + std::deque turns; + + geometry::get_intersection_points(linestring1, linestring2, turns); + + for (typename boost::range_iterator const>::type + it = boost::begin(turns); it != boost::end(turns); ++it) + { + PointOut p; + geometry::convert(it->point, p); + *out++ = p; + } + return 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) + ); +}; + + +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 + +{}; + + +// 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 + +{}; + + +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& strategy) + { + typedef typename point_type::type point_type; + strategy::intersection::liang_barsky lb_strategy; + return detail::intersection::clip_range_with_box + (box, linestring, out, lb_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& strategy) + { + typedef geometry::segment_range range_type; + range_type range(segment); + + typedef typename point_type::type point_type; + strategy::intersection::liang_barsky lb_strategy; + return detail::intersection::clip_range_with_box + (box, range, out, lb_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, + 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::type::value, + geometry::dispatch::intersection_insert_reversed + < + typename geometry::tag::type, + typename geometry::tag::type, + typename geometry::tag::type, + geometry::is_areal::value, + geometry::is_areal::value, + geometry::is_areal::value, + Geometry1, Geometry2, + overlay::do_reverse::value>::value, + overlay::do_reverse::value, ReverseSecond>::value, + overlay::do_reverse::value>::value, + OutputIterator, GeometryOut, + OverlayType, + Strategy + >, + geometry::dispatch::intersection_insert + < + typename geometry::tag::type, + typename geometry::tag::type, + typename geometry::tag::type, + geometry::is_areal::value, + geometry::is_areal::value, + geometry::is_areal::value, + Geometry1, Geometry2, + geometry::detail::overlay::do_reverse::value>::value, + geometry::detail::overlay::do_reverse::value, ReverseSecond>::value, + geometry::detail::overlay::do_reverse::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(); + concept::check(); + + 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(); + concept::check(); + + typedef strategy_intersection + < + typename cs_tag::type, + Geometry1, + Geometry2, + typename geometry::point_type::type + > strategy; + + return intersection_insert(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/include/boost/geometry/algorithms/detail/overlay/overlay.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp new file mode 100644 index 000000000..a469dd93a --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -0,0 +1,298 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include + +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include + +#include +#include +#include +#include + + +#ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE +# include +#endif + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +// Skip for assemble process +template +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 +inline void map_turns(Map& map, TurnPoints const& turn_points) +{ + typedef typename boost::range_value::type turn_point_type; + typedef typename turn_point_type::container_type container_type; + + int index = 0; + for (typename boost::range_iterator::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::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 typename geometry::range_type::type ring_type; + typedef std::deque ring_container_type; + + typedef ring_properties::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 empty; + std::map all_of_one_of_them; + + select_rings(geometry1, geometry2, empty, all_of_one_of_them); + ring_container_type rings; + assign_parents(geometry1, geometry2, rings, all_of_one_of_them); + return add_rings(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; + } + + typedef typename geometry::point_type::type point_type; + typedef detail::overlay::traversal_turn_info turn_info; + typedef std::deque container_type; + + // "Use" rangetype for ringtype: + // -> for polygon, it is the type of the exterior ring. + // -> for ring, it is the ring itself. + // -> for multi-polygon, it is also the type of the ring. + typedef typename geometry::range_type::type ring_type; + typedef std::deque ring_container_type; + + if (geometry::num_points(geometry1) == 0 + || geometry::num_points(geometry2) == 0) + { + return return_if_one_input_is_empty + < + GeometryOut, Direction, ReverseOut + >(geometry1, geometry2, out); + } + + 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(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; + geometry::traverse(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 map; + map_turns(map, turn_points); + +#ifdef BOOST_GEOMETRY_TIME_OVERLAY + std::cout << "map_turns: " << timer.elapsed() << std::endl; +#endif + + typedef ring_properties::type> properties; + + std::map selected; + select_rings(geometry1, geometry2, map, selected); + +#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::type + it = boost::begin(rings); + it != boost::end(rings); + ++it) + { + selected[id] = properties(*it); + 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(selected, geometry1, geometry2, rings, out); + } +}; + + +// Metafunction helper for intersection and union +template +struct do_reverse {}; + +template <> +struct do_reverse : boost::false_type {}; + +template <> +struct do_reverse : boost::true_type {}; + +template <> +struct do_reverse : boost::true_type {}; + +template <> +struct do_reverse : boost::false_type {}; + + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_OVERLAY_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/overlay_type.hpp b/include/boost/geometry/algorithms/detail/overlay/overlay_type.hpp new file mode 100644 index 000000000..d239993c4 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/overlay_type.hpp @@ -0,0 +1,29 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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/include/boost/geometry/algorithms/detail/overlay/ring_properties.hpp b/include/boost/geometry/algorithms/detail/overlay/ring_properties.hpp new file mode 100644 index 000000000..c6bcaf10b --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/ring_properties.hpp @@ -0,0 +1,78 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template +struct ring_properties +{ + typedef Point point_type; + typedef typename default_area_result::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 children; + + inline ring_properties() + : area(area_type()) + , within_code(-1) + , reversed(false) + , discarded(false) + , parent_area(-1) + {} + + template + inline ring_properties(RingOrBox const& ring_or_box) + : 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, true); + } + + 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/include/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp b/include/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp new file mode 100644 index 000000000..2bd10c494 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/segment_identifier.hpp @@ -0,0 +1,91 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + + +#include +#include + + + +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/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp b/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp new file mode 100644 index 000000000..60ec4c88a --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/select_rings.hpp @@ -0,0 +1,282 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + + +#include +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +namespace dispatch +{ + + template + struct select_rings + {}; + + template + struct select_rings + { + template + static inline void apply(Box const& box, Geometry const& geometry, ring_identifier const& id, Map& map) + { + map[id] = typename Map::mapped_type(box); + } + + template + static inline void apply(Box const& box, ring_identifier const& id, Map& map) + { + map[id] = typename Map::mapped_type(box); + } + }; + + template + struct select_rings + { + template + static inline void apply(Ring const& ring, Geometry const& geometry, ring_identifier const& id, Map& map) + { + if (boost::size(ring) > 0) + { + map[id] = typename Map::mapped_type(ring); + } + } + + template + static inline void apply(Ring const& ring, ring_identifier const& id, Map& map) + { + if (boost::size(ring) > 0) + { + map[id] = typename Map::mapped_type(ring); + } + } + }; + + + template + struct select_rings + { + template + static inline void apply(Polygon const& polygon, Geometry const& geometry, ring_identifier id, Map& map) + { + typedef typename geometry::ring_type::type ring_type; + typedef select_rings per_ring; + + per_ring::apply(exterior_ring(polygon), geometry, id, map); + + typename interior_return_type::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); + } + } + + template + static inline void apply(Polygon const& polygon, ring_identifier id, Map& map) + { + typedef typename geometry::ring_type::type ring_type; + typedef select_rings per_ring; + + per_ring::apply(exterior_ring(polygon), id, map); + + typename interior_return_type::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); + } + } + }; +} + + +template +struct decide +{}; + +template<> +struct decide +{ + template + static bool include(ring_identifier const& id, Code const& code) + { + return code.within_code * -1 == 1; + } + + template + static bool reversed(ring_identifier const& , Code const& ) + { + return false; + } +}; + +template<> +struct decide +{ + template + 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 + static bool reversed(ring_identifier const& id, Code const& code) + { + return include(id, code) && id.source_index == 1; + } +}; + +template<> +struct decide +{ + template + static bool include(ring_identifier const& id, Code const& code) + { + return code.within_code * 1 == 1; + } + + template + 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::include(it->first, it->second) ? "INC" : "") + << decide::reverse(it->first, it->second) + << std::endl; + */ + + bool found = intersection_map.find(it->first) != intersection_map.end(); + if (! found) + { + ring_identifier 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::include(id, properties)) + { + properties.reversed = decide::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) +{ + typedef typename geometry::tag::type tag1; + typedef typename geometry::tag::type tag2; + + SelectionMap map_with_all; + dispatch::select_rings::apply(geometry1, geometry2, ring_identifier(0, -1, -1), map_with_all); + dispatch::select_rings::apply(geometry2, geometry1, ring_identifier(1, -1, -1), map_with_all); + + update_selection_map(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) +{ + typedef typename geometry::tag::type tag; + + SelectionMap map_with_all; + dispatch::select_rings::apply(geometry, ring_identifier(0, -1, -1), map_with_all); + + update_selection_map(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/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp b/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp new file mode 100644 index 000000000..3efe5c7d7 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -0,0 +1,220 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + +#include + +#include +#include + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace self_get_turn_points +{ + +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 typename geometry::sections + < + model::box ::type>, + 1 + > sections_type; + + sections_type sec; + geometry::sectionalize(geometry, sec); + + for (typename boost::range_iterator::type + it1 = sec.begin(); + it1 != sec.end(); + ++it1) + { + for (typename boost::range_iterator::type + it2 = sec.begin(); + it2 != sec.end(); + ++it2) + { + if (! geometry::detail::disjoint::disjoint_box_box( + it1->bounding_box, it2->bounding_box) + && ! it1->duplicate + && ! it2->duplicate + ) + { + if (! geometry::detail::get_turns::get_turns_in_sections + < + Geometry, Geometry, + false, false, + typename boost::range_value::type, + typename boost::range_value::type, + Turns, TurnPolicy, + InterruptPolicy + >::apply( + 0, geometry, *it1, + 0, geometry, *it2, + turns, interrupt_policy)) + { + 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 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 get_turns(Geometry const& geometry, + Turns& turns, InterruptPolicy& interrupt_policy) +{ + concept::check(); + + typedef typename strategy_intersection + < + typename cs_tag::type, + Geometry, + Geometry, + typename boost::range_value::type + >::segment_intersection_strategy_type strategy_type; + + typedef detail::overlay::get_turn_info + < + typename point_type::type, + typename point_type::type, + typename boost::range_value::type, + detail::overlay::assign_null_policy + > TurnPolicy; + + dispatch::self_get_turn_points + < + typename tag::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/include/boost/geometry/algorithms/detail/overlay/stream_info.hpp b/include/boost/geometry/algorithms/detail/overlay/stream_info.hpp new file mode 100644 index 000000000..ef1c836d7 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/stream_info.hpp @@ -0,0 +1,75 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + +#include + + +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 + std::ostream& operator<<(std::ostream &os, turn_info

const& info) + { + typename geometry::coordinate_type

::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/include/boost/geometry/algorithms/detail/overlay/traversal_info.hpp b/include/boost/geometry/algorithms/detail/overlay/traversal_info.hpp new file mode 100644 index 000000000..d57fb9dca --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/traversal_info.hpp @@ -0,0 +1,47 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template +struct traversal_turn_operation : public turn_operation +{ + enrichment_info

enriched; + visit_info visited; +}; + +template +struct traversal_turn_info : public turn_info > +{}; + + + +}} // namespace detail::overlay +#endif //DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSAL_INFO_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/traverse.hpp b/include/boost/geometry/algorithms/detail/overlay/traverse.hpp new file mode 100644 index 000000000..d91efa71c --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/traverse.hpp @@ -0,0 +1,472 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + +#include + +#include +#include +#include +#include +#include + + +#if defined(BOOST_GEOMETRY_DEBUG_INTERSECTION) || defined(BOOST_GEOMETRY_OVERLAY_REPORT_WKT) +# include +# include +# include +#endif + + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + +template +inline void debug_traverse(Turn const& turn, Operation op, std::string const& header) +{ +#ifdef BOOST_GEOMETRY_DEBUG_TRAVERSE + std::cout << header + << " at " << op.seg_id + << " op: " << operation_char(op.operation) + << " vis: " << visited_char(op.visited) + << " of: " << operation_char(turn.operations[0].operation) + << operation_char(turn.operations[1].operation) + << std::endl; + + if (boost::contains(header, "Finished")) + { + std::cout << std::endl; + } +#endif +} + + +template +inline void clear_visit_info(Turns& turns) +{ + typedef typename boost::range_value::type tp_type; + + for (typename boost::range_iterator::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; + } +} + + +template +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::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(g1, info.seg_id, + info.enriched.travels_to_vertex_index, + current_output); + } + else + { + geometry::copy_segments(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; + } + + geometry::append(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; +} + + + +template +< + typename Rings, + typename Turns, + typename Operation, + typename Geometry1, + typename Geometry2 +> +inline void backtrack(std::size_t size_at_start, bool& fail, + Rings& rings, typename boost::range_value::type& ring, + Turns& turns, Operation& operation, + +#ifdef BOOST_GEOMETRY_OVERLAY_REPORT_WKT + std::string const& reason, + Geometry1 const& geometry1, + Geometry2 const& geometry2 +#else + std::string const& reason, + Geometry1 const& , + Geometry2 const& +#endif + ) +{ +#ifdef BOOST_GEOMETRY_DEBUG_ENRICH + std::cout << " REJECT " << reason << std::endl; +#endif + fail = true; + + // 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); + + /*** + 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; + ***/ + + + +#ifdef BOOST_GEOMETRY_OVERLAY_REPORT_WKT + std::cout << " BT (" << reason << " )"; + std::cout + << geometry::wkt(geometry1) << std::endl + << geometry::wkt(geometry2) << std::endl; +#endif + +} + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +/*! + \brief Traverses through intersection points / geometries + \ingroup overlay + */ +template +< + bool Reverse1, bool Reverse2, + typename Geometry1, + typename Geometry2, + typename Turns, + typename Rings +> +inline void traverse(Geometry1 const& geometry1, + Geometry2 const& geometry2, + detail::overlay::operation_type operation, + Turns& turns, Rings& rings) +{ + typedef typename boost::range_iterator::type turn_iterator; + typedef typename boost::range_value::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); + + bool fail = false; + do + { + fail = false; + // Iterate through all unvisited points + for (turn_iterator it = boost::begin(turns); + ! fail && it != boost::end(turns); + ++it) + { + // Skip discarded ones + if (! (it->is_discarded() || it->blocked())) + { + for (turn_operation_iterator_type iit = boost::begin(it->operations); + ! fail && 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::type current_output; + geometry::append(current_output, it->point); + + turn_iterator current = it; + turn_operation_iterator_type current_iit = iit; + segment_identifier current_seg_id; + + if (! detail::overlay::assign_next_ip( + geometry1, geometry2, + turns, + current, current_output, + *iit, current_seg_id)) + { + detail::overlay::backtrack( + size_at_start, fail, + rings, current_output, turns, *current_iit, + "No next IP", + geometry1, geometry2); + } + + if (! detail::overlay::select_next_ip( + operation, + *current, + current_seg_id, + current_iit)) + { + detail::overlay::backtrack( + size_at_start, fail, + rings, current_output, turns, *iit, + "Dead end at start", + geometry1, geometry2); + } + 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 && ! fail) + { + if (current_iit->visited.visited()) + { + // It visits a visited node again, without passing the start node. + // This makes it suspicious for endless loops + detail::overlay::backtrack( + size_at_start, fail, + rings, current_output, turns, *iit, + "Visit again", + geometry1, geometry2); + } + 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( + 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 + detail::overlay::backtrack( + size_at_start, fail, + rings, current_output, turns, *iit, + "Dead end", + geometry1, geometry2); + } + 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. + detail::overlay::backtrack( + size_at_start, fail, + rings, current_output, turns, *iit, + "Endless loop", + geometry1, geometry2); + } + } + } + + if (! fail) + { + iit->visited.set_finished(); + detail::overlay::debug_traverse(*current, *iit, "->Finished"); + rings.push_back(current_output); + } + } + } + } + } + } + } while (fail); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_TRAVERSE_HPP diff --git a/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp new file mode 100644 index 000000000..7d3ef4fc6 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/turn_info.hpp @@ -0,0 +1,142 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + +#include + +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 +> +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/include/boost/geometry/algorithms/detail/overlay/visit_info.hpp b/include/boost/geometry/algorithms/detail/overlay/visit_info.hpp new file mode 100644 index 000000000..43d265014 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/visit_info.hpp @@ -0,0 +1,136 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#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(); } + inline bool visited() const { return state.is_flag_active(); } + inline bool started() const { return state.is_flag_active(); } +#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/include/boost/geometry/algorithms/detail/overlay/within_util.hpp b/include/boost/geometry/algorithms/detail/overlay/within_util.hpp new file mode 100644 index 000000000..8618b81fe --- /dev/null +++ b/include/boost/geometry/algorithms/detail/overlay/within_util.hpp @@ -0,0 +1,98 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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_WITHIN_UTIL_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_WITHIN_UTIL_HPP + + + +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace overlay +{ + + +template +struct within_code +{}; + +template +struct within_code +{ + static inline int apply(Point const& point, Box const& box) + { + // 1. Check outside + if (get<0>(point) < get(box) + || get<0>(point) > get(box) + || get<1>(point) < get(box) + || get<1>(point) > get(box)) + { + return -1; + } + // 2. Check border + if (geometry::math::equals(get<0>(point), get(box)) + || geometry::math::equals(get<0>(point), get(box)) + || geometry::math::equals(get<1>(point), get(box)) + || geometry::math::equals(get<1>(point), get(box))) + { + return 0; + } + return 1; + } +}; +template +struct within_code +{ + static inline int apply(Point const& point, Ring const& ring) + { + // Same as point_in_ring but here ALWAYS with winding. + typedef strategy::within::winding strategy_type; + + return detail::within::point_in_ring + < + Point, + Ring, + order_as_direction::value>::value, + geometry::closure::value, + strategy_type + >::apply(point, ring, strategy_type()); + } +}; + + +template +inline int point_in_ring(Point const& point, Geometry const& geometry) +{ + return within_code::type, Point, Geometry> + ::apply(point, geometry); +} + +template +inline bool within_or_touch(Point const& point, Geometry const& geometry) +{ + return within_code::type, Point, Geometry> + ::apply(point, geometry) >= 0; +} + + + +}} // namespace detail::overlay +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_WITHIN_UTIL_HPP diff --git a/include/boost/geometry/algorithms/detail/partition.hpp b/include/boost/geometry/algorithms/detail/partition.hpp new file mode 100644 index 000000000..3fbd98b77 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/partition.hpp @@ -0,0 +1,388 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2011 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 +#include +#include + +namespace boost { namespace geometry +{ + + +namespace detail { namespace partition +{ + +typedef std::vector index_vector_type; + +template +inline void divide_box(Box const& box, Box& lower_box, Box& upper_box) +{ + typedef typename coordinate_type::type ctype; + + // Divide input box into two parts, e.g. left/right + ctype two = 2; + ctype mid = (geometry::get(box) + + geometry::get(box)) / two; + + lower_box = box; + upper_box = box; + geometry::set(lower_box, mid); + geometry::set(upper_box, mid); +} + +// Divide collection into three subsets: lower, upper and oversized (not-fitting) +// (lower == left or bottom, upper == right or top) +template +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::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 1 with collection 2 +template +static inline void handle_one(InputCollection const& collection, + index_vector_type const& input, + Policy& policy) +{ + typedef boost::range_iterator::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 +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::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 index_vector_type; + typedef typename coordinate_type::type ctype; + typedef partition_one_collection + < + 1 - Dimension, + Box, + OverlapsPolicy, + VisitBoxPolicy + > sub_divide; + + template + 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 + 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(box, lower_box, upper_box); + + index_vector_type lower, upper, exceeding; + divide_into_subsets(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 index_vector_type; + typedef typename coordinate_type::type ctype; + typedef partition_two_collections + < + 1 - Dimension, + Box, + OverlapsPolicy, + VisitBoxPolicy + > sub_divide; + + template + 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 + 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(box, lower_box, upper_box); + + index_vector_type lower1, upper1, exceeding1; + index_vector_type lower2, upper2, exceeding2; + divide_into_subsets(lower_box, upper_box, collection1, input1, lower1, upper1, exceeding1); + divide_into_subsets(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 + static inline void apply(Box const&, int ) + {} +}; + + +template +< + typename Box, + typename ExpandPolicy, + typename OverlapsPolicy, + typename VisitBoxPolicy = visit_no_policy +> +class partition +{ + typedef std::vector index_vector_type; + + template + 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::type it + = boost::begin(collection); + it != boost::end(collection); + ++it, ++index) + { + ExpandPolicy::apply(total, *it); + index_vector.push_back(index); + } + } + + +public : + template + 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::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 + 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::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_RING_IDENTIFIER_HPP diff --git a/include/boost/geometry/algorithms/detail/point_on_border.hpp b/include/boost/geometry/algorithms/detail/point_on_border.hpp new file mode 100644 index 000000000..0cc6f42b0 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/point_on_border.hpp @@ -0,0 +1,243 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include + +#include +#include + +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace point_on_border +{ + + +template +struct get_point +{ + static inline bool apply(Point& destination, Point const& source, bool) + { + destination = source; + return true; + } +}; + +template +struct midpoint_helper +{ + static inline bool apply(Point& p, Point const& p1, Point const& p2) + { + typename coordinate_type::type const two = 2; + set(p, + (get(p1) + get(p2)) / two); + return midpoint_helper::apply(p, p1, p2); + } +}; + + +template +struct midpoint_helper +{ + static inline bool apply(Point& , Point const& , Point const& ) + { + return true; + } +}; + + +template +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::value + >::apply(point, *prev, *it); + } + } + + if (n > 0) + { + point = *boost::begin(range); + return true; + } + return false; + } +}; + + +template +struct point_on_polygon +{ + static inline bool apply(Point& point, Polygon const& polygon, bool midpoint) + { + return point_on_range + < + Point, + typename ring_type::type + >::apply(point, exterior_ring(polygon), midpoint); + } +}; + + +template +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(box, p1); + detail::assign::assign_box_2d_corner(box, p2); + midpoint_helper + < + Point, + 0, dimension::value + >::apply(point, p1, p2); + } + else + { + detail::assign::assign_box_2d_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 +struct point_on_border + : detail::point_on_border::get_point +{}; + + +template +struct point_on_border + : detail::point_on_border::point_on_range +{}; + + +template +struct point_on_border + : detail::point_on_border::point_on_range +{}; + + +template +struct point_on_border + : detail::point_on_border::point_on_polygon +{}; + + +template +struct point_on_border + : detail::point_on_border::point_on_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 +inline bool point_on_border(Point& point, + Geometry const& geometry, + bool midpoint = false) +{ + concept::check(); + concept::check(); + + typedef typename point_type::type point_type; + + return dispatch::point_on_border + < + typename tag::type, + Point, + Geometry + >::apply(point, geometry, midpoint); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_POINT_ON_BORDER_HPP diff --git a/include/boost/geometry/algorithms/detail/ring_identifier.hpp b/include/boost/geometry/algorithms/detail/ring_identifier.hpp new file mode 100644 index 000000000..2d01db4e0 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/ring_identifier.hpp @@ -0,0 +1,70 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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/include/boost/geometry/algorithms/detail/sections/range_by_section.hpp b/include/boost/geometry/algorithms/detail/sections/range_by_section.hpp new file mode 100644 index 000000000..3bfcaf660 --- /dev/null +++ b/include/boost/geometry/algorithms/detail/sections/range_by_section.hpp @@ -0,0 +1,131 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include + +#include +#include +#include +#include + + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace section +{ + + +template +struct full_section_range +{ + static inline Range const& apply(Range const& range, Section const& section) + { + return range; + } +}; + + +template +struct full_section_polygon +{ + static inline typename ring_return_type::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) + ); +}; + + +template +struct range_by_section + : detail::section::full_section_range +{}; + + +template +struct range_by_section + : detail::section::full_section_range +{}; + + +template +struct range_by_section + : detail::section::full_section_polygon +{}; + + +} // 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 +inline typename ring_return_type::type + range_by_section(Geometry const& geometry, Section const& section) +{ + concept::check(); + + return dispatch::range_by_section + < + typename tag::type, + Geometry, + Section + >::apply(geometry, section); +} + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_RANGE_BY_SECTION_HPP diff --git a/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp new file mode 100644 index 000000000..775e8582e --- /dev/null +++ b/include/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -0,0 +1,645 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +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 +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 +struct sections : std::vector > +{ + typedef Box box_type; + static std::size_t const value = DimensionCount; +}; + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace sectionalize +{ + +template +struct get_direction_loop +{ + typedef typename coordinate_type::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 +struct get_direction_loop +{ + static inline void apply(Segment const&, int [DimensionCount]) + {} +}; + +template +struct copy_loop +{ + static inline void apply(T const source[DimensionCount], + T target[DimensionCount]) + { + target[Dimension] = source[Dimension]; + copy_loop::apply(source, target); + } +}; + +template +struct copy_loop +{ + static inline void apply(T const [DimensionCount], T [DimensionCount]) + {} +}; + +template +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 +struct compare_loop +{ + static inline bool apply(T const [DimensionCount], + T const [DimensionCount]) + { + + return true; + } +}; + + +template +struct check_duplicate_loop +{ + typedef typename coordinate_type::type coordinate_type; + + static inline bool apply(Segment const& seg) + { + coordinate_type const diff = + geometry::get<1, Dimension>(seg) - geometry::get<0, Dimension>(seg); + + coordinate_type const zero = 0; + if (! geometry::math::equals(diff, zero)) + { + return false; + } + + return check_duplicate_loop + < + Segment, Dimension + 1, DimensionCount + >::apply(seg); + } +}; + +template +struct check_duplicate_loop +{ + static inline bool apply(Segment const&) + { + return true; + } +}; + +template +struct assign_loop +{ + static inline void apply(T dims[DimensionCount], int const value) + { + dims[Dimension] = value; + assign_loop::apply(dims, value); + } +}; + +template +struct assign_loop +{ + 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 segment_type; + typedef typename boost::range_value::type section_type; + + typedef typename boost::range_iterator::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

::value) + if (check_duplicate_loop + < + segment_type, 0, geometry::dimension::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::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 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::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::type point_type; + typedef typename ring_type::type ring_type; + typedef sectionalize_range + < + ring_type, closure::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::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::type point_type; + + assert_dimension(); + + // 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 + + point_type ll, lr, ul, ur; + assign_box_corners(box, ll, lr, ul, ur); + + std::vector 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, closed, false, + point_type, + Sections, + DimensionCount, + MaxCount + >::apply(points, sections, ring_id); + } +}; + +template +inline void set_section_unique_ids(Sections& sections) +{ + // Set ID's. + int index = 0; + for (typename boost::range_iterator::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) + ); +}; + +template +< + typename Box, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize + : 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::type, + Sections, + DimensionCount, + MaxCount + > +{}; + +template +< + typename Ring, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize + : detail::sectionalize::sectionalize_range + < + Ring, geometry::closure::value, Reverse, + typename point_type::type, + Sections, + DimensionCount, + MaxCount + > +{}; + +template +< + typename Polygon, + bool Reverse, + typename Sections, + std::size_t DimensionCount, + std::size_t MaxCount +> +struct sectionalize + : 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 +inline void sectionalize(Geometry const& geometry, Sections& sections, int source_index = 0) +{ + concept::check(); + + // 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::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/include/boost/geometry/algorithms/difference.hpp b/include/boost/geometry/algorithms/difference.hpp new file mode 100644 index 000000000..230ef512e --- /dev/null +++ b/include/boost/geometry/algorithms/difference.hpp @@ -0,0 +1,148 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + +#include + +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(); + concept::check(); + concept::check(); + + return detail::intersection::insert( + 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(); + concept::check(); + concept::check(); + + typedef strategy_intersection + < + typename cs_tag::type, + Geometry1, + Geometry2, + typename geometry::point_type::type + > strategy; + + return difference_insert(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(); + concept::check(); + + typedef typename boost::range_value::type geometry_out; + concept::check(); + + detail::difference::difference_insert( + geometry1, geometry2, + std::back_inserter(output_collection)); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DIFFERENCE_HPP diff --git a/include/boost/geometry/algorithms/disjoint.hpp b/include/boost/geometry/algorithms/disjoint.hpp new file mode 100644 index 000000000..697d49f63 --- /dev/null +++ b/include/boost/geometry/algorithms/disjoint.hpp @@ -0,0 +1,253 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + +template +struct disjoint_linear +{ + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + typedef typename geometry::point_type::type point_type; + + typedef overlay::turn_info turn_info; + std::deque turns; + + // Get (and stop on) any intersection + disjoint_interrupt_policy policy; + geometry::get_turns + < + false, false, + overlay::assign_null_policy + >(geometry1, geometry2, turns, policy); + if (policy.has_intersections) + { + return false; + } + + return true; + } +}; + +template +struct disjoint_segment +{ + static inline bool apply(Segment1 const& segment1, Segment2 const& segment2) + { + typedef typename point_type::type point_type; + + segment_intersection_points is + = strategy::intersection::relate_cartesian_segments + < + policies::relate::segments_intersection_points + < + Segment1, + Segment2, + segment_intersection_points + > + >::apply(segment1, segment2); + + return is.count == 0; + } +}; + + + +template +struct general_areal +{ + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + if (! disjoint_linear::apply(geometry1, geometry2)) + { + return false; + } + + typedef typename geometry::point_type::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::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 +{}; + + +template +struct disjoint + : detail::disjoint::point_point +{}; + + +template +struct disjoint + : detail::disjoint::box_box +{}; + + +template +struct disjoint + : detail::disjoint::point_box +{}; + +template +struct disjoint + : detail::disjoint::disjoint_linear +{}; + +template +struct disjoint + : detail::disjoint::disjoint_segment +{}; + +template +struct disjoint + : detail::disjoint::disjoint_linear +{}; + + +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} + */ +template +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::type::value, + dispatch::disjoint_reversed + < + typename tag::type, + typename tag::type, + Geometry1, + Geometry2, + dimension::type::value + >, + dispatch::disjoint + < + typename tag::type, + typename tag::type, + Geometry1, + Geometry2, + dimension::type::value + > + >::type::apply(geometry1, geometry2); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DISJOINT_HPP diff --git a/include/boost/geometry/algorithms/distance.hpp b/include/boost/geometry/algorithms/distance.hpp new file mode 100644 index 000000000..f2b6c7a9c --- /dev/null +++ b/include/boost/geometry/algorithms/distance.hpp @@ -0,0 +1,567 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace distance +{ + +// To avoid spurious namespaces here: +using strategy::distance::services::return_type; + +template +struct point_to_point +{ + static inline typename return_type::type apply(P1 const& p1, + P2 const& p2, Strategy const& strategy) + { + return strategy.apply(p1, p2); + } +}; + + +template +struct point_to_segment +{ + static inline typename return_type::type apply(Point const& point, + Segment const& segment, Strategy const& strategy) + { + typename strategy::distance::services::default_strategy + < + segment_tag, + Point, + typename point_type::type, + typename cs_tag::type, + typename cs_tag::type>::type, + Strategy + >::type segment_strategy; + + typename point_type::type p[2]; + geometry::assign_point_from_index<0>(segment, p[0]); + geometry::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::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::type view_type; + + view_type view(range); + + // line of one point: return point distance + typedef typename boost::range_iterator::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::type eps_strategy_type; + eps_strategy_type eps_strategy = strategy::distance::services::get_comparable::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::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::type return_type; + typedef std::pair 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::type, + Closure, + PPStrategy, + PSStrategy + > per_ring; + + distance_containment dc = per_ring::apply(point, + exterior_ring(polygon), pp_strategy, ps_strategy); + + typename interior_return_type::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; + } +}; + + +}} // namespace detail::distance +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +using strategy::distance::services::return_type; + + +template +< + typename GeometryTag1, typename GeometryTag2, + typename Geometry1, typename Geometry2, + typename StrategyTag, typename Strategy +> +struct distance +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types) + ); +}; + + +template +struct distance + < + point_tag, point_tag, + P1, P2, + strategy_tag_distance_point_point, Strategy + > + : detail::distance::point_to_point +{}; + + +// Point-line version 1, where point-point strategy is specified +template +struct distance +< + point_tag, linestring_tag, + Point, Linestring, + strategy_tag_distance_point_point, Strategy +> +{ + + static inline typename return_type::type apply(Point const& point, + Linestring const& linestring, + Strategy const& strategy) + { + typedef typename strategy::distance::services::default_strategy + < + segment_tag, + Point, + typename point_type::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 +struct distance +< + point_tag, linestring_tag, + Point, Linestring, + strategy_tag_distance_point_segment, Strategy +> +{ + static inline typename return_type::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 +struct distance +< + point_tag, ring_tag, + Point, Ring, + strategy_tag_distance_point_point, Strategy +> +{ + typedef typename return_type::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::type + >::type ps_strategy_type; + + std::pair + dc = detail::distance::point_to_ring + < + Point, Ring, + geometry::closure::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 +struct distance +< + point_tag, polygon_tag, + Point, Polygon, + strategy_tag_distance_point_point, Strategy +> +{ + typedef typename return_type::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::type + >::type ps_strategy_type; + + std::pair + dc = detail::distance::point_to_polygon + < + Point, Polygon, + geometry::closure::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 +struct distance +< + point_tag, segment_tag, + Point, Segment, + strategy_tag_distance_point_point, Strategy +> : detail::distance::point_to_segment +{}; + +// Point-segment version 2, with point-segment strategy +template +struct distance +< + point_tag, segment_tag, + Point, Segment, + strategy_tag_distance_point_segment, Strategy +> +{ + static inline typename return_type::type apply(Point const& point, + Segment const& segment, Strategy const& strategy) + { + + typename point_type::type p[2]; + geometry::assign_point_from_index<0>(segment, p[0]); + geometry::assign_point_from_index<1>(segment, p[1]); + return strategy.apply(point, p[0], p[1]); + } +}; + + + +// Strictly spoken this might be in namespace again +template +< + typename GeometryTag1, typename GeometryTag2, + typename G1, typename G2, + typename StrategyTag, typename Strategy +> +struct distance_reversed +{ + static inline typename return_type::type apply(G1 const& g1, + G2 const& g2, Strategy const& strategy) + { + return distance + < + GeometryTag2, GeometryTag1, + G2, G1, + StrategyTag, Strategy + >::apply(g2, g1, strategy); + } +}; + +} // 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 +inline typename strategy::distance::services::return_type::type distance( + Geometry1 const& geometry1, + Geometry2 const& geometry2, Strategy const& strategy) +{ + concept::check(); + concept::check(); + + return boost::mpl::if_ + < + typename geometry::reverse_dispatch::type, + dispatch::distance_reversed + < + typename tag_cast::type, multi_tag>::type, + typename tag_cast::type, multi_tag>::type, + Geometry1, + Geometry2, + typename strategy::distance::services::tag::type, + Strategy + >, + dispatch::distance + < + typename tag_cast::type, multi_tag>::type, + typename tag_cast::type, multi_tag>::type, + Geometry1, + Geometry2, + typename strategy::distance::services::tag::type, + Strategy + > + >::type::apply(geometry1, geometry2, strategy); +} + + +/*! +\brief \brief_calc2{distance} +\ingroup distance +\details The default strategy is used, belonging to the corresponding 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} + */ +template +inline typename default_distance_result::type distance( + Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check(); + concept::check(); + + typedef typename point_type::type point1_type; + typedef typename point_type::type point2_type; + + // Define a point-point-distance-strategy + // for either the normal case, either the reversed case + typedef typename boost::mpl::if_c + < + geometry::reverse_dispatch::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 strategy; + + return distance(geometry1, geometry2, strategy()); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DISTANCE_HPP diff --git a/include/boost/geometry/algorithms/envelope.hpp b/include/boost/geometry/algorithms/envelope.hpp new file mode 100644 index 000000000..cae9fa0e1 --- /dev/null +++ b/include/boost/geometry/algorithms/envelope.hpp @@ -0,0 +1,279 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include + +#include + +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace envelope +{ + + +/// Calculate envelope of an 2D or 3D segment +template +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 +inline void envelope_range_additional(Range const& range, Box& mbr) +{ + typedef typename boost::range_iterator::type iterator_type; + + for (iterator_type it = boost::begin(range); + it != boost::end(range); + ++it) + { + geometry::expand(mbr, *it); + } +} + + + +/// Generic range dispatching struct +template +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) + ); +}; + + +template +< + typename Point, typename Box, + typename StrategyLess, typename StrategyGreater +> +struct envelope + < + point_tag, box_tag, + Point, Box, + StrategyLess, StrategyGreater + > + : detail::envelope::envelope_expand_one +{}; + + +template +< + typename BoxIn, typename BoxOut, + typename StrategyLess, typename StrategyGreater +> +struct envelope + < + box_tag, box_tag, + BoxIn, BoxOut, + StrategyLess, StrategyGreater + > + : detail::envelope::envelope_expand_one +{}; + + +template +< + typename Segment, typename Box, + typename StrategyLess, typename StrategyGreater +> +struct envelope + < + segment_tag, box_tag, + Segment, Box, + StrategyLess, StrategyGreater + > + : detail::envelope::envelope_expand_one +{}; + + +template +< + typename Linestring, typename Box, + typename StrategyLess, typename StrategyGreater +> +struct envelope + < + linestring_tag, box_tag, + Linestring, Box, + StrategyLess, StrategyGreater + > + : detail::envelope::envelope_range +{}; + + +template +< + typename Ring, typename Box, + typename StrategyLess, typename StrategyGreater +> +struct envelope + < + ring_tag, box_tag, + Ring, Box, + StrategyLess, StrategyGreater + > + : detail::envelope::envelope_range +{}; + + +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::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} + +\par Example: +Example showing envelope calculation, using point_ll latlong points +\dontinclude doxygen_1.cpp +\skip example_envelope_polygon +\line { +\until } + + +\qbk{ +[heading Example] +[envelope] [envelope_output] +} +*/ +template +inline void envelope(Geometry const& geometry, Box& mbr) +{ + concept::check(); + concept::check(); + + dispatch::envelope + < + typename tag::type, typename tag::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{ +[heading Example] +[return_envelope] [return_envelope_output] +} +*/ +template +inline Box return_envelope(Geometry const& geometry) +{ + concept::check(); + concept::check(); + + Box mbr; + dispatch::envelope + < + typename tag::type, typename tag::type, + Geometry, Box, + void, void + >::apply(geometry, mbr); + return mbr; +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_ENVELOPE_HPP diff --git a/include/boost/geometry/algorithms/equals.hpp b/include/boost/geometry/algorithms/equals.hpp new file mode 100644 index 000000000..d80406327 --- /dev/null +++ b/include/boost/geometry/algorithms/equals.hpp @@ -0,0 +1,317 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +// For trivial checks +#include +#include +#include +#include +#include + +#include + + +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(box1), get(box2)) + || !geometry::math::equals(get(box1), get(box2))) + { + return false; + } + return box_box::apply(box1, box2); + } +}; + +template +struct box_box +{ + static inline bool apply(Box1 const& , Box2 const& ) + { + return true; + } +}; + + +struct area_check +{ + template + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + return geometry::math::equals( + geometry::area(geometry1), + geometry::area(geometry1)); + } +}; + + +struct length_check +{ + template + static inline bool apply(Geometry1 const& geometry1, Geometry2 const& geometry2) + { + return geometry::math::equals( + geometry::length(geometry1), + geometry::length(geometry1)); + } +}; + + +template +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 > v; + v c1, c2; + + geometry::collect_vectors(c1, geometry1); + geometry::collect_vectors(c2, geometry2); + + if (boost::size(c1) != boost::size(c2)) + { + return false; + } + + // Check where direction is NOT changing + + std::sort(c1.begin(), c1.end()); + std::sort(c2.begin(), c2.end()); + + // Just check if these vectors are equal. + return c1.size() == c2.size() + && 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 +struct equals + : geometry::detail::not_ + < + P1, + P2, + detail::disjoint::point_point + > +{}; + + +template +struct equals + : detail::equals::box_box +{}; + + +template +struct equals + : detail::equals::equals_by_collection + < + Ring1, Ring2, + detail::equals::area_check + > +{}; + + +template +struct equals + : detail::equals::equals_by_collection + < + Polygon1, Polygon2, + detail::equals::area_check + > +{}; + + +template +struct equals + : detail::equals::equals_by_collection + < + LineString1, LineString2, + detail::equals::length_check + > +{}; + + +template +struct equals + : detail::equals::equals_by_collection + < + Polygon, Ring, + detail::equals::area_check + > +{}; + + +template +struct equals + : detail::equals::equals_by_collection + < + Ring, Box, + detail::equals::area_check + > +{}; + + +template +struct equals + : 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} +\ingroup equals +\tparam Geometry1 \tparam_geometry +\tparam Geometry2 \tparam_geometry +\param geometry1 \param_geometry +\param geometry2 \param_geometry +\return \return_check2{are spatially disjoint} + */ +template +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::type::value, + dispatch::equals_reversed + < + typename tag::type, + typename tag::type, + Geometry1, + Geometry2, + dimension::type::value + >, + dispatch::equals + < + typename tag::type, + typename tag::type, + Geometry1, + Geometry2, + dimension::type::value + > + >::type::apply(geometry1, geometry2); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_EQUALS_HPP + diff --git a/include/boost/geometry/algorithms/expand.hpp b/include/boost/geometry/algorithms/expand.hpp new file mode 100644 index 000000000..81217b23c --- /dev/null +++ b/include/boost/geometry/algorithms/expand.hpp @@ -0,0 +1,317 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include + +#include +#include + +#include + +#include +#include + + +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::type coordinate_type; + + static inline void apply(Box& box, Point const& source) + { + less_type less; + greater_type greater; + + coordinate_type const coord = get(source); + + if (less(coord, get(box))) + { + set(box, coord); + } + + if (greater(coord, get(box))) + { + set(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(source); + + if (less(coord, get(box))) + { + set(box, coord); + } + + if (greater(coord, get(box))) + { + set(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::type::value + >::apply(box, geometry); + + indexed_loop + < + Box, Geometry, + StrategyLess, StrategyGreater, + 1, 0, dimension::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 + : detail::expand::point_loop + < + BoxOut, Point, + StrategyLess, StrategyGreater, + 0, dimension::type::value + > +{}; + + +// Box + box -> new box containing two input boxes +template +< + typename BoxOut, typename BoxIn, + typename StrategyLess, typename StrategyGreater +> +struct expand + : detail::expand::expand_indexed + +{}; + +template +< + typename Box, typename Segment, + typename StrategyLess, typename StrategyGreater +> +struct expand + : detail::expand::expand_indexed + +{}; + + +} // 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(); + + dispatch::expand + < + typename tag::type, + Box, + Geometry, + StrategyLess, StrategyGreater + >::apply(box, geometry); +} +***/ + + +/*! +\brief Expands a box using the extend (envelope) of another geometry (box, point) +\ingroup expand +\tparam Box type of the box +\tparam Geometry \tparam_geometry +\param box box to expand another geometry with, might be changed +\param geometry \param_geometry + */ +template +inline void expand(Box& box, Geometry const& geometry) +{ + concept::check_concepts_and_equal_dimensions(); + + dispatch::expand + < + typename tag::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/include/boost/geometry/algorithms/for_each.hpp b/include/boost/geometry/algorithms/for_each.hpp new file mode 100644 index 000000000..7993644fa --- /dev/null +++ b/include/boost/geometry/algorithms/for_each.hpp @@ -0,0 +1,351 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include +#include + +#include +#include +#include + +#include + +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace for_each +{ + + +template +struct fe_point_per_point +{ + static inline Functor apply( + typename add_const_if_c::type& point, Functor f) + { + f(point); + return f; + } +}; + + +template +struct fe_point_per_segment +{ + static inline Functor apply( + typename add_const_if_c::type& point, Functor f) + { + return f; + } +}; + + +template +struct fe_range_per_point +{ + static inline Functor apply( + typename add_const_if_c::type& range, + Functor f) + { + return (std::for_each(boost::begin(range), boost::end(range), f)); + } +}; + + +template +struct fe_range_per_segment +{ + static inline Functor apply( + typename add_const_if_c::type& range, + Functor f) + { + typedef typename add_const_if_c + < + IsConst, + typename point_type::type + >::type point_type; + + BOOST_AUTO_TPL(it, boost::begin(range)); + BOOST_AUTO_TPL(previous, it++); + while(it != boost::end(range)) + { + model::referring_segment s(*previous, *it); + f(s); + previous = it++; + } + + return f; + } +}; + + +template +struct fe_polygon_per_point +{ + typedef typename add_const_if_c::type poly_type; + + static inline Functor apply(poly_type& poly, Functor f) + { + typedef fe_range_per_point + < + typename ring_type::type, + Functor, + IsConst + > per_ring; + + f = per_ring::apply(exterior_ring(poly), f); + + typename interior_return_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 +struct fe_polygon_per_segment +{ + typedef typename add_const_if_c::type poly_type; + + static inline Functor apply(poly_type& poly, Functor f) + { + typedef fe_range_per_segment + < + typename ring_type::type, + Functor, + IsConst + > per_ring; + + f = per_ring::apply(exterior_ring(poly), f); + + typename interior_return_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 +struct for_each_point + : detail::for_each::fe_point_per_point +{}; + + +template +struct for_each_point + : detail::for_each::fe_range_per_point +{}; + + +template +struct for_each_point + : detail::for_each::fe_range_per_point +{}; + + +template +struct for_each_point + : detail::for_each::fe_polygon_per_point +{}; + + +template +< + typename Tag, + typename Geometry, + typename Functor, + bool IsConst +> +struct for_each_segment {}; + +template +struct for_each_segment + : detail::for_each::fe_point_per_segment +{}; + + +template +struct for_each_segment + : detail::for_each::fe_range_per_segment +{}; + + +template +struct for_each_segment + : detail::for_each::fe_range_per_segment +{}; + + +template +struct for_each_segment + : detail::for_each::fe_polygon_per_segment +{}; + + +} // 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{[heading Example]} +\qbk{[for_each_point_const] [for_each_point_const_output]} +*/ +template +inline Functor for_each_point(Geometry const& geometry, Functor f) +{ + concept::check(); + + return dispatch::for_each_point + < + typename tag_cast::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{[heading Example]} +\qbk{[for_each_point] [for_each_point_output]} +*/ +template +inline Functor for_each_point(Geometry& geometry, Functor f) +{ + concept::check(); + + return dispatch::for_each_point + < + typename tag_cast::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{[heading Example]} +\qbk{[for_each_segment_const] [for_each_segment_const_output]} +*/ +template +inline Functor for_each_segment(Geometry const& geometry, Functor f) +{ + concept::check(); + + return dispatch::for_each_segment + < + typename tag_cast::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 +*/ +template +inline Functor for_each_segment(Geometry& geometry, Functor f) +{ + concept::check(); + + return dispatch::for_each_segment + < + typename tag_cast::type, multi_tag>::type, + Geometry, + Functor, + false + >::apply(geometry, f); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_FOR_EACH_HPP diff --git a/include/boost/geometry/algorithms/intersection.hpp b/include/boost/geometry/algorithms/intersection.hpp new file mode 100644 index 000000000..be9fff982 --- /dev/null +++ b/include/boost/geometry/algorithms/intersection.hpp @@ -0,0 +1,237 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 +#include +#include + + +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::type ct; + + ct min1 = get(box1); + ct min2 = get(box2); + ct max1 = get(box1); + ct max2 = get(box2); + + if (max1 < min2 || max2 < min1) + { + return false; + } + // Set dimensions of output coordinate + set(box_out, min1 < min2 ? min2 : min1); + set(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 +{ + 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 output_iterator; + + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2, + GeometryOut& geometry_out, + Strategy const& strategy) + { + typedef typename boost::range_value::type OneOut; + + intersection_insert + < + Tag1, Tag2, typename geometry::tag::type, + geometry::is_areal::value, + geometry::is_areal::value, + geometry::is_areal::value, + Geometry1, Geometry2, + detail::overlay::do_reverse::value, false>::value, + detail::overlay::do_reverse::value, false>::value, + detail::overlay::do_reverse::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::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(); + concept::check(); + + typedef strategy_intersection + < + typename cs_tag::type, + Geometry1, + Geometry2, + typename geometry::point_type::type + > strategy; + + + return boost::mpl::if_c + < + geometry::reverse_dispatch::type::value, + dispatch::intersection_reversed + < + typename geometry::tag::type, + typename geometry::tag::type, + typename geometry::tag::type, + Geometry1, Geometry2, GeometryOut, strategy + >, + dispatch::intersection + < + typename geometry::tag::type, + typename geometry::tag::type, + typename geometry::tag::type, + Geometry1, Geometry2, GeometryOut, strategy + > + >::type::apply(geometry1, geometry2, geometry_out, strategy()); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_INTERSECTION_HPP diff --git a/include/boost/geometry/algorithms/intersects.hpp b/include/boost/geometry/algorithms/intersects.hpp new file mode 100644 index 000000000..0463b9dab --- /dev/null +++ b/include/boost/geometry/algorithms/intersects.hpp @@ -0,0 +1,105 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include +#include +#include + + +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{[include reference/algorithms/intersects.qbk]} +*/ +template +inline bool intersects(Geometry const& geometry) +{ + concept::check(); + + + typedef detail::overlay::turn_info + < + typename geometry::point_type::type + > turn_info; + std::deque turns; + + typedef typename strategy_intersection + < + typename cs_tag::type, + Geometry, + Geometry, + typename geometry::point_type::type + >::segment_intersection_strategy_type segment_intersection_strategy_type; + + typedef detail::overlay::get_turn_info + < + typename point_type::type, + typename point_type::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, + 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 +inline bool intersects(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check(); + concept::check(); + + return ! geometry::disjoint(geometry1, geometry2); +} + + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_INTERSECTS_HPP diff --git a/include/boost/geometry/algorithms/length.hpp b/include/boost/geometry/algorithms/length.hpp new file mode 100644 index 000000000..434ea865f --- /dev/null +++ b/include/boost/geometry/algorithms/length.hpp @@ -0,0 +1,199 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace length +{ + + +template +struct segment_length +{ + static inline typename default_length_result::type apply( + Segment const& segment, Strategy const& strategy) + { + typedef typename point_type::type point_type; + point_type p1, p2; + assign_point_from_index<0>(segment, p1); + 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 +struct range_length +{ + typedef typename default_length_result::type return_type; + + static inline return_type apply( + Range const& range, Strategy const& strategy) + { + typedef typename closeable_view::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 +struct length : detail::calculate_null + < + typename default_length_result::type, + Geometry, + Strategy + > +{}; + + +template +struct length + : detail::length::range_length +{}; + + +// RING: length is currently 0; it might be argued that it is the "perimeter" + + +template +struct length + : detail::length::segment_length +{}; + + +} // 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 +inline typename default_length_result::type length( + Geometry const& geometry) +{ + concept::check(); + + typedef typename strategy::distance::services::default_strategy + < + point_tag, typename point_type::type + >::type strategy_type; + + return dispatch::length + < + typename tag::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 +inline typename default_length_result::type length( + Geometry const& geometry, Strategy const& strategy) +{ + concept::check(); + + return dispatch::length + < + typename tag::type, + Geometry, + Strategy + >::apply(geometry, strategy); +} + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_LENGTH_HPP diff --git a/include/boost/geometry/algorithms/make.hpp b/include/boost/geometry/algorithms/make.hpp new file mode 100644 index 000000000..06b962b1a --- /dev/null +++ b/include/boost/geometry/algorithms/make.hpp @@ -0,0 +1,192 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include + +namespace boost { namespace geometry +{ + +/*! +\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_3_2_coordinate_values assign] +} +*/ +template +inline Geometry make(Type const& c1, Type const& c2) +{ + concept::check(); + + Geometry geometry; + dispatch::assign + < + typename tag::type, + Geometry, + geometry::dimension::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_4_3_coordinate_values assign] +} + */ +template +inline Geometry make(Type const& c1, Type const& c2, Type const& c3) +{ + concept::check(); + + Geometry geometry; + dispatch::assign + < + typename tag::type, + Geometry, + geometry::dimension::type::value + >::apply(geometry, c1, c2, c3); + return geometry; +} + +template +inline Geometry make(Type const& c1, Type const& c2, Type const& c3, Type const& c4) +{ + concept::check(); + + Geometry geometry; + dispatch::assign + < + typename tag::type, + Geometry, + geometry::dimension::type::value + >::apply(geometry, c1, c2, c3, c4); + return geometry; +} + + + +/*! +\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_2_with_a_range assign] +} + */ +template +inline Geometry make(Range const& range) +{ + concept::check(); + + Geometry geometry; + append(geometry, range); + 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 +inline Geometry make_inverse() +{ + concept::check(); + + Geometry geometry; + dispatch::assign_inverse + < + typename tag::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 +inline Geometry make_zero() +{ + concept::check(); + + Geometry geometry; + dispatch::assign_zero + < + typename tag::type, + Geometry + >::apply(geometry); + return geometry; +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_MAKE_HPP diff --git a/include/boost/geometry/algorithms/num_geometries.hpp b/include/boost/geometry/algorithms/num_geometries.hpp new file mode 100644 index 000000000..b05092a13 --- /dev/null +++ b/include/boost/geometry/algorithms/num_geometries.hpp @@ -0,0 +1,95 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include + +#include +#include +#include + +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct num_geometries +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types) + ); +}; + + +template +struct num_geometries +{ + 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 +inline std::size_t num_geometries(Geometry const& geometry) +{ + concept::check(); + + return dispatch::num_geometries + < + typename tag_cast + < + typename tag::type, + single_tag, + multi_tag + >::type, + Geometry + >::apply(geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_NUM_GEOMETRIES_HPP diff --git a/include/boost/geometry/algorithms/num_interior_rings.hpp b/include/boost/geometry/algorithms/num_interior_rings.hpp new file mode 100644 index 000000000..0e4681aff --- /dev/null +++ b/include/boost/geometry/algorithms/num_interior_rings.hpp @@ -0,0 +1,88 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include + +#include +#include + +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template +struct num_interior_rings +{ + static inline std::size_t apply(Geometry const& ) + { + return 0; + } +}; + + + +template +struct num_interior_rings +{ + 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 +inline std::size_t num_interior_rings(Geometry const& geometry) +{ + return dispatch::num_interior_rings + < + typename tag::type, + Geometry + >::apply(geometry); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_NUM_INTERIOR_RINGS_HPP diff --git a/include/boost/geometry/algorithms/num_points.hpp b/include/boost/geometry/algorithms/num_points.hpp new file mode 100644 index 000000000..da42f38a9 --- /dev/null +++ b/include/boost/geometry/algorithms/num_points.hpp @@ -0,0 +1,171 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace num_points +{ + + +template +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::value; + + if (s == open) + { + if (geometry::disjoint(*boost::begin(range), *(boost::begin(range) + n - 1))) + { + return n + 1; + } + } + } + return n; + } +}; + +template +struct other_count +{ + static inline std::size_t apply(Geometry const&, bool) + { + return D; + } +}; + +template +struct polygon_count +{ + static inline std::size_t apply(Polygon const& poly, bool add_for_open) + { + typedef typename geometry::ring_type::type ring_type; + + std::size_t n = range_count::apply( + exterior_ring(poly), add_for_open); + + typename interior_return_type::type rings + = interior_rings(poly); + for (BOOST_AUTO_TPL(it, boost::begin(rings)); it != boost::end(rings); ++it) + { + n += range_count::apply(*it, add_for_open); + } + + return n; + } +}; + +}} // namespace detail::num_points +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct num_points +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types) + ); +}; + +template +struct num_points + : detail::num_points::other_count +{}; + +template +struct num_points + : detail::num_points::other_count +{}; + +template +struct num_points + : detail::num_points::other_count +{}; + +template +struct num_points + : detail::num_points::range_count +{}; + +template +struct num_points + : detail::num_points::range_count +{}; + +template +struct num_points + : detail::num_points::polygon_count +{}; + +} // 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 +inline std::size_t num_points(Geometry const& geometry, bool add_for_open = false) +{ + concept::check(); + + return dispatch::num_points + < + typename tag_cast::type, multi_tag>::type, + Geometry + >::apply(geometry, add_for_open); +} + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_NUM_POINTS_HPP diff --git a/include/boost/geometry/algorithms/overlaps.hpp b/include/boost/geometry/algorithms/overlaps.hpp new file mode 100644 index 000000000..3adf9cb5e --- /dev/null +++ b/include/boost/geometry/algorithms/overlaps.hpp @@ -0,0 +1,200 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include + +#include + +#include + +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(); + + typedef typename coordinate_type::type coordinate_type1; + typedef typename coordinate_type::type coordinate_type2; + + coordinate_type1 const& min1 = get(b1); + coordinate_type1 const& max1 = get(b1); + coordinate_type2 const& min2 = get(b2); + coordinate_type2 const& max2 = get(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 +{ + 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::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) + ); +}; + + +template +struct overlaps + : detail::overlaps::box_box +{}; + + + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief \brief_check2{overlap} +\ingroup overlaps +\return \return_check2{overlap} + */ +template +inline bool overlaps(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check(); + concept::check(); + + return dispatch::overlaps + < + typename tag::type, + typename tag::type, + Geometry1, + Geometry2 + >::apply(geometry1, geometry2); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_OVERLAPS_HPP diff --git a/include/boost/geometry/algorithms/perimeter.hpp b/include/boost/geometry/algorithms/perimeter.hpp new file mode 100644 index 000000000..a0536d1da --- /dev/null +++ b/include/boost/geometry/algorithms/perimeter.hpp @@ -0,0 +1,139 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +// Default perimeter is 0.0, specializations implement calculated values +template +struct perimeter : detail::calculate_null + < + typename default_length_result::type, + Geometry, + Strategy + > +{}; + +template +struct perimeter + : detail::length::range_length + < + Geometry, + Strategy, + closure::value + > +{}; + +template +struct perimeter + : detail::calculate_polygon_sum + < + typename default_length_result::type, + Polygon, + Strategy, + detail::length::range_length + < + typename ring_type::type, + Strategy, + closure::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 +inline typename default_length_result::type perimeter( + Geometry const& geometry) +{ + concept::check(); + + typedef typename point_type::type point_type; + typedef typename strategy::distance::services::default_strategy + < + point_tag, point_type + >::type strategy_type; + + return dispatch::perimeter + < + typename tag::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 +inline typename default_length_result::type perimeter( + Geometry const& geometry, Strategy const& strategy) +{ + concept::check(); + + return dispatch::perimeter + < + typename tag::type, + Geometry, + Strategy + >::apply(geometry, strategy); +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_PERIMETER_HPP + diff --git a/include/boost/geometry/algorithms/reverse.hpp b/include/boost/geometry/algorithms/reverse.hpp new file mode 100644 index 000000000..b8cf28d8a --- /dev/null +++ b/include/boost/geometry/algorithms/reverse.hpp @@ -0,0 +1,129 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include +#include + +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace reverse +{ + + +template +struct range_reverse +{ + static inline void apply(Range& range) + { + std::reverse(boost::begin(range), boost::end(range)); + } +}; + + +template +struct polygon_reverse +{ + static inline void apply(Polygon& polygon) + { + typedef typename geometry::ring_type::type ring_type; + + typedef range_reverse per_range; + per_range::apply(exterior_ring(polygon)); + + typename interior_return_type::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 +struct reverse + : detail::reverse::range_reverse +{}; + + +template +struct reverse + : detail::reverse::range_reverse +{}; + + +template +struct reverse + : detail::reverse::polygon_reverse +{}; + + +} // namespace dispatch +#endif + + +/*! +\brief Reverses a geometry +\ingroup reverse +\tparam Geometry \tparam_geometry +\param geometry \param_geometry which will be reversed +*/ +template +inline void reverse(Geometry& geometry) +{ + concept::check(); + + dispatch::reverse + < + typename tag::type, + Geometry + >::apply(geometry); +} + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_REVERSE_HPP diff --git a/include/boost/geometry/algorithms/simplify.hpp b/include/boost/geometry/algorithms/simplify.hpp new file mode 100644 index 000000000..e6a75cc77 --- /dev/null +++ b/include/boost/geometry/algorithms/simplify.hpp @@ -0,0 +1,405 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace simplify +{ + +template +struct simplify_range_insert +{ + template + 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 +struct simplify_copy +{ + template + static inline void apply(Range const& range, Range& out, + Distance const& max_distance, Strategy const& strategy) + { + std::copy + ( + boost::begin(range), boost::end(range), std::back_inserter(out) + ); + } +}; + + +template +struct simplify_range +{ + template + 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::apply + ( + range, out, max_distance, strategy + ); + } + else + { + simplify_range_insert::apply + ( + range, std::back_inserter(out), max_distance, strategy + ); + } + } +}; + +template +struct simplify_polygon +{ + template + static inline void apply(Polygon const& poly_in, Polygon& poly_out, + Distance const& max_distance, Strategy const& strategy) + { + typedef typename ring_type::type ring_type; + + int const Minimum = core_detail::closure::minimum_ring_size + < + geometry::closure::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::apply(exterior_ring(poly_in), + exterior_ring(poly_out), + max_distance, strategy); + + traits::resize + < + typename boost::remove_reference + < + typename traits::interior_mutable_type::type + >::type + >::apply(interior_rings(poly_out), num_interior_rings(poly_in)); + + typename interior_return_type::type rings_in + = interior_rings(poly_in); + typename interior_return_type::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::apply(*it_in, + *it_out, max_distance, strategy); + } + } +}; + + +}} // namespace detail::simplify +#endif // DOXYGEN_NO_DETAIL + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + +template +struct simplify +{ +}; + +template +struct simplify +{ + template + static inline void apply(Point const& point, Point& out, + Distance const& max_distance, Strategy const& strategy) + { + geometry::convert(point, out); + } +}; + + +template +struct simplify + : detail::simplify::simplify_range + < + Linestring, + Strategy, + 2 + > +{}; + +template +struct simplify + : detail::simplify::simplify_range + < + Ring, + Strategy, + core_detail::closure::minimum_ring_size + < + geometry::closure::value + >::value + > +{}; + +template +struct simplify + : detail::simplify::simplify_polygon + < + Polygon, + Strategy + > +{}; + + +template +struct simplify_insert +{ +}; + + +template +struct simplify_insert + : detail::simplify::simplify_range_insert + < + Linestring, + Strategy + > +{}; + +template +struct simplify_insert + : 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 +inline void simplify(Geometry const& geometry, Geometry& out, + Distance const& max_distance, Strategy const& strategy) +{ + concept::check(); + + BOOST_CONCEPT_ASSERT( (geometry::concept::SimplifyStrategy) ); + + geometry::clear(out); + + dispatch::simplify + < + typename tag::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 +inline void simplify(Geometry const& geometry, Geometry& out, + Distance const& max_distance) +{ + concept::check(); + + typedef typename point_type::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 +inline void simplify_insert(Geometry const& geometry, OutputIterator out, + Distance const& max_distance, Strategy const& strategy) +{ + concept::check(); + BOOST_CONCEPT_ASSERT( (geometry::concept::SimplifyStrategy) ); + + dispatch::simplify_insert + < + typename tag::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 +inline void simplify_insert(Geometry const& geometry, OutputIterator out, + Distance const& max_distance) +{ + typedef typename point_type::type point_type; + + // Concept: output point type = point type of input geometry + concept::check(); + concept::check(); + + 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::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/include/boost/geometry/algorithms/sym_difference.hpp b/include/boost/geometry/algorithms/sym_difference.hpp new file mode 100644 index 000000000..d228f056a --- /dev/null +++ b/include/boost/geometry/algorithms/sym_difference.hpp @@ -0,0 +1,149 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + + +#include + + +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(); + concept::check(); + concept::check(); + + out = detail::intersection::insert( + geometry1, geometry2, out, strategy); + out = detail::intersection::insert( + 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(); + concept::check(); + concept::check(); + + typedef strategy_intersection + < + typename cs_tag::type, + Geometry1, + Geometry2, + typename geometry::point_type::type + > strategy_type; + + return sym_difference_insert(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 / std::deque 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(); + concept::check(); + + typedef typename boost::range_value::type geometry_out; + concept::check(); + + detail::sym_difference::sym_difference_insert( + geometry1, geometry2, + std::back_inserter(output_collection)); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_SYM_DIFFERENCE_HPP diff --git a/include/boost/geometry/algorithms/transform.hpp b/include/boost/geometry/algorithms/transform.hpp new file mode 100644 index 000000000..47d142ad0 --- /dev/null +++ b/include/boost/geometry/algorithms/transform.hpp @@ -0,0 +1,344 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace transform +{ + +template +struct transform_point +{ + static inline bool apply(Point1 const& p1, Point2& p2, + Strategy const& strategy) + { + return strategy.apply(p1, p2); + } +}; + + +template +struct transform_box +{ + static inline bool apply(Box1 const& b1, Box2& b2, + Strategy const& strategy) + { + typedef typename point_type::type point_type1; + typedef typename point_type::type point_type2; + + point_type1 lower_left, upper_right; + detail::assign::assign_box_2d_corner( + b1, lower_left); + detail::assign::assign_box_2d_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::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(b2, x1); + set(b2, y1); + set(b2, x2); + set(b2, y2); + + return true; + } + return false; + } +}; + +template +struct transform_box_or_segment +{ + static inline bool apply(Geometry1 const& source, Geometry2& target, + Strategy const& strategy) + { + typedef typename point_type::type point_type1; + typedef typename point_type::type point_type2; + + point_type1 source_point[2]; + assign_point_from_index<0>(source, source_point[0]); + 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])) + { + assign_point_to_index<0>(target_point[0], target); + 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::type + it = boost::begin(range); + it != boost::end(range); + ++it) + { + if (! transform_point + < + typename point_type::type, + PointOut, + Strategy + >::apply(*it, point_out, strategy)) + { + return false; + } + *out++ = point_out; + } + return true; +} + + +template +struct transform_polygon +{ + static inline bool apply(Polygon1 const& poly1, Polygon2& poly2, + Strategy const& strategy) + { + typedef typename ring_type::type ring1_type; + typedef typename ring_type::type ring2_type; + typedef typename point_type::type point2_type; + + geometry::clear(poly2); + + if (!transform_range_out(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::type + >::type + >::apply(interior_rings(poly2), num_interior_rings(poly1)); + + typename interior_return_type::type rings1 + = interior_rings(poly1); + typename interior_return_type::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(*it1, + std::back_inserter(*it2), strategy)) + { + return false; + } + } + + return true; + } +}; + + +template +struct select_strategy +{ + typedef typename strategy::transform::services::default_strategy + < + typename cs_tag::type, + typename cs_tag::type, + typename coordinate_system::type, + typename coordinate_system::type, + dimension::type::value, + dimension::type::value, + typename point_type::type, + typename point_type::type + >::type type; +}; + +template +struct transform_range +{ + static inline bool apply(Range1 const& range1, + Range2& range2, Strategy const& strategy) + { + typedef typename point_type::type point_type; + + // Should NOT be done here! + // geometry::clear(range2); + return transform_range_out(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 +struct transform + : detail::transform::transform_point +{ +}; + + +template +struct transform + < + linestring_tag, linestring_tag, + Linestring1, Linestring2, Strategy + > + : detail::transform::transform_range +{ +}; + +template +struct transform + : detail::transform::transform_range +{ +}; + +template +struct transform + : detail::transform::transform_polygon +{ +}; + +template +struct transform + : detail::transform::transform_box +{ +}; + +template +struct transform + : detail::transform::transform_box_or_segment +{ +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +/*! +\brief Transforms from one geometry to another geometry using a 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 + */ +template +inline bool transform(Geometry1 const& geometry1, Geometry2& geometry2, + Strategy const& strategy) +{ + concept::check(); + concept::check(); + + typedef dispatch::transform + < + typename tag_cast::type, multi_tag>::type, + typename tag_cast::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 + */ +template +inline bool transform(Geometry1 const& geometry1, Geometry2& geometry2) +{ + concept::check(); + concept::check(); + + typename detail::transform::select_strategy::type strategy; + return transform(geometry1, geometry2, strategy); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_TRANSFORM_HPP diff --git a/include/boost/geometry/algorithms/union.hpp b/include/boost/geometry/algorithms/union.hpp new file mode 100644 index 000000000..2b73b7d7f --- /dev/null +++ b/include/boost/geometry/algorithms/union.hpp @@ -0,0 +1,284 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 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 + +#include + +#include +#include +#include +#include +#include + + +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) + ); +}; + + +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 + +{}; + + + +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::type::value, + dispatch::union_insert_reversed + < + typename tag::type, + typename tag::type, + typename tag::type, + geometry::is_areal::value, + geometry::is_areal::value, + geometry::is_areal::value, + Geometry1, Geometry2, + overlay::do_reverse::value>::value, + overlay::do_reverse::value>::value, + overlay::do_reverse::value>::value, + OutputIterator, GeometryOut, + Strategy + >, + dispatch::union_insert + < + typename tag::type, + typename tag::type, + typename tag::type, + geometry::is_areal::value, + geometry::is_areal::value, + geometry::is_areal::value, + Geometry1, Geometry2, + overlay::do_reverse::value>::value, + overlay::do_reverse::value>::value, + overlay::do_reverse::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(); + concept::check(); + concept::check(); + + return detail::union_::insert(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(); + concept::check(); + concept::check(); + + typedef strategy_intersection + < + typename cs_tag::type, + Geometry1, + Geometry2, + typename geometry::point_type::type + > strategy; + + return union_insert(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 / std::deque 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(); + concept::check(); + + typedef typename boost::range_value::type geometry_out; + concept::check(); + + detail::union_::union_insert(geometry1, geometry2, + std::back_inserter(output_collection)); +} + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_UNION_HPP diff --git a/include/boost/geometry/algorithms/unique.hpp b/include/boost/geometry/algorithms/unique.hpp new file mode 100644 index 000000000..1f15f39d1 --- /dev/null +++ b/include/boost/geometry/algorithms/unique.hpp @@ -0,0 +1,151 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include +#include + +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace unique +{ + + +template +struct range_unique +{ + static inline void apply(Range& range, ComparePolicy const& policy) + { + typename boost::range_iterator::type it + = std::unique + ( + boost::begin(range), + boost::end(range), + policy + ); + + traits::resize::apply(range, it - boost::begin(range)); + } +}; + + +template +struct polygon_unique +{ + static inline void apply(Polygon& polygon, ComparePolicy const& policy) + { + typedef typename geometry::ring_type::type ring_type; + + typedef range_unique per_range; + per_range::apply(exterior_ring(polygon), policy); + + typename interior_return_type::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 +struct unique + : detail::unique::range_unique +{}; + + +template +struct unique + : detail::unique::range_unique +{}; + + +template +struct unique + : detail::unique::polygon_unique +{}; + + +} // 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 +*/ +template +inline void unique(Geometry& geometry) +{ + concept::check(); + + // Default strategy is the default point-comparison policy + typedef geometry::equal_to + < + typename geometry::point_type::type + > policy; + + + dispatch::unique + < + typename tag::type, + Geometry, + policy + >::apply(geometry, policy()); +} + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_UNIQUE_HPP diff --git a/include/boost/geometry/algorithms/within.hpp b/include/boost/geometry/algorithms/within.hpp new file mode 100644 index 000000000..4c3edfb38 --- /dev/null +++ b/include/boost/geometry/algorithms/within.hpp @@ -0,0 +1,438 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2011 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2011 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 + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace within +{ + + +/*! + \brief Implementation for boxes + \ingroup boolean_relations + \note Should have strategy for e.g. Wrangel + */ +template +< + typename Point, + typename Box, + typename Strategy, + std::size_t Dimension, + std::size_t DimensionCount +> +struct point_in_box +{ + static inline int apply(Point const& p, Box const& b, Strategy const& s) + { + assert_dimension_equal(); + + if (get(p) <= get(b) + || get(p) >= get(b)) + { + return -1; + } + + return point_in_box + < + Point, + Box, + Strategy, + Dimension + 1, + DimensionCount + >::apply(p, b, s); + } +}; + +template +< + typename Point, + typename Box, + typename Strategy, + std::size_t DimensionCount +> +struct point_in_box +{ + static inline int apply(Point const& , Box const& , Strategy const& ) + { + return 1; + } +}; + + +template +< + typename Box1, + typename Box2, + typename Strategy, + std::size_t Dimension, + std::size_t DimensionCount +> +struct box_in_box +{ + static inline int apply(Box1 const& b1, Box2 const& b2, Strategy const& s) + { + assert_dimension_equal(); + + if (get(b1) <= get(b2) + || get(b1) >= get(b2)) + { + return -1; + } + + return box_in_box + < + Box1, + Box2, + Strategy, + Dimension + 1, + DimensionCount + >::apply(b1, b2, s); + } +}; + +template +< + typename Box1, + typename Box2, + typename Strategy, + std::size_t DimensionCount +> +struct box_in_box +{ + static inline int apply(Box1 const& , Box2 const& , Strategy const&) + { + return 1; + } +}; + + +template +< + typename Point, + typename Ring, + iterate_direction Direction, + closure_selector Closure, + typename Strategy +> +struct point_in_ring +{ + BOOST_CONCEPT_ASSERT( (geometry::concept::WithinStrategy) ); + + static inline int apply(Point const& point, Ring const& ring, + Strategy const& strategy) + { + if (boost::size(ring) + < core_detail::closure::minimum_ring_size::value) + { + return -1; + } + + typedef typename reversible_view::type rev_view_type; + typedef typename closeable_view + < + rev_view_type const, Closure + >::type cl_view_type; + typedef typename boost::range_iterator::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); + + for (iterator_type previous = it++; + it != end; + ++previous, ++it) + { + if (! strategy.apply(point, *previous, *it, state)) + { + return false; + } + } + + 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::WithinStrategy) ); + + static inline int apply(Point const& point, Polygon const& poly, + Strategy const& strategy) + { + int const code = point_in_ring + < + Point, + typename ring_type::type, + Direction, + Closure, + Strategy + >::apply(point, exterior_ring(poly), strategy); + + if (code == 1) + { + typename interior_return_type::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::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 Tag1, + typename Tag2, + typename Geometry1, + typename Geometry2, + typename Strategy +> +struct within +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_OR_NOT_YET_IMPLEMENTED_FOR_THIS_GEOMETRY_TYPE + , (types) + ); +}; + + +template +struct within + : detail::within::point_in_box + < + Point, + Box, + Strategy, + 0, + dimension::type::value + > +{}; + +template +struct within + : detail::within::box_in_box + < + Box1, + Box2, + Strategy, + 0, + dimension::type::value + > +{}; + + + +template +struct within + : detail::within::point_in_ring + < + Point, + Ring, + order_as_direction::value>::value, + geometry::closure::value, + Strategy + > +{}; + +template +struct within + : detail::within::point_in_polygon + < + Point, + Polygon, + order_as_direction::value>::value, + geometry::closure::value, + Strategy + > +{}; + +} // 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 +\param geometry2 \param_geometry +\param geometry1 geometry which might be within the second geometry +\param geometry2 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 +inline bool within(Geometry1 const& geometry1, Geometry2 const& geometry2) +{ + concept::check(); + concept::check(); + + typedef typename point_type::type point_type1; + typedef typename point_type::type point_type2; + + typedef typename strategy::within::services::default_strategy + < + typename tag::type, + typename tag_cast::type, areal_tag>::type, + typename cs_tag::type, + typename cs_tag::type, + point_type1, + point_type2 + >::type strategy_type; + + return dispatch::within + < + typename tag::type, + typename tag::type, + Geometry1, + Geometry2, + strategy_type + >::apply(geometry1, geometry2, strategy_type()) == 1; +} + +/*! +\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 +\param geometry2 \param_geometry +\param geometry1 \param_geometry 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 +inline bool within(Geometry1 const& geometry1, Geometry2 const& geometry2, + Strategy const& strategy) +{ + // Always assume a point-in-polygon strategy here. + // Because for point-in-box, it makes no sense to specify one. + BOOST_CONCEPT_ASSERT( (geometry::concept::WithinStrategy) ); + + concept::check(); + concept::check(); + + return dispatch::within + < + typename tag::type, + typename tag::type, + Geometry1, + Geometry2, + Strategy + >::apply(geometry1, geometry2, strategy) == 1; +} + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_WITHIN_HPP