diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2016-03-21 15:45:20 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2016-03-21 15:46:37 +0900 |
commit | 733b5d5ae2c5d625211e2985ac25728ac3f54883 (patch) | |
tree | a5b214744b256f07e1dc2bd7273035a7808c659f /boost/geometry/algorithms | |
parent | 08c1e93fa36a49f49325a07fe91ff92c964c2b6c (diff) | |
download | boost-733b5d5ae2c5d625211e2985ac25728ac3f54883.tar.gz boost-733b5d5ae2c5d625211e2985ac25728ac3f54883.tar.bz2 boost-733b5d5ae2c5d625211e2985ac25728ac3f54883.zip |
Imported Upstream version 1.58.0upstream/1.58.0
Change-Id: If0072143aa26874812e0db6872e1efb10a3e5e94
Signed-off-by: DongHun Kwak <dh0128.kwak@samsung.com>
Diffstat (limited to 'boost/geometry/algorithms')
118 files changed, 5306 insertions, 1921 deletions
diff --git a/boost/geometry/algorithms/append.hpp b/boost/geometry/algorithms/append.hpp index 1a8828ba4b..6ffb5f9587 100644 --- a/boost/geometry/algorithms/append.hpp +++ b/boost/geometry/algorithms/append.hpp @@ -22,8 +22,10 @@ #include <boost/range.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> +#include <boost/variant/variant_fwd.hpp> #include <boost/geometry/algorithms/num_interior_rings.hpp> #include <boost/geometry/algorithms/detail/convert_point_to_point.hpp> diff --git a/boost/geometry/algorithms/area.hpp b/boost/geometry/algorithms/area.hpp index 7377798719..cb1501d8c9 100644 --- a/boost/geometry/algorithms/area.hpp +++ b/boost/geometry/algorithms/area.hpp @@ -18,8 +18,9 @@ #include <boost/mpl/if.hpp> #include <boost/range/functions.hpp> #include <boost/range/metafunctions.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/core/closure.hpp> diff --git a/boost/geometry/algorithms/assign.hpp b/boost/geometry/algorithms/assign.hpp index 32f095b9ac..2f325448ad 100644 --- a/boost/geometry/algorithms/assign.hpp +++ b/boost/geometry/algorithms/assign.hpp @@ -26,6 +26,10 @@ #include <boost/numeric/conversion/cast.hpp> #include <boost/type_traits.hpp> +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> +#include <boost/variant/variant_fwd.hpp> + #include <boost/geometry/algorithms/detail/assign_box_corners.hpp> #include <boost/geometry/algorithms/detail/assign_indexed_point.hpp> #include <boost/geometry/algorithms/detail/assign_values.hpp> @@ -41,8 +45,6 @@ #include <boost/geometry/util/for_each_coordinate.hpp> -#include <boost/variant/variant_fwd.hpp> - namespace boost { namespace geometry { @@ -224,28 +226,27 @@ template <typename Geometry1, typename Geometry2> struct assign { static inline void - apply( - Geometry1& geometry1, - const Geometry2& geometry2) + apply(Geometry1& geometry1, const Geometry2& geometry2) { concept::check<Geometry1>(); concept::check<Geometry2 const>(); concept::check_concepts_and_equal_dimensions<Geometry1, Geometry2 const>(); - bool const same_point_order = - point_order<Geometry1>::value == point_order<Geometry2>::value; - bool const same_closure = - closure<Geometry1>::value == closure<Geometry2>::value; - + static bool const same_point_order + = point_order<Geometry1>::value == point_order<Geometry2>::value; BOOST_MPL_ASSERT_MSG ( - same_point_order, ASSIGN_IS_NOT_SUPPORTED_FOR_DIFFERENT_POINT_ORDER - , (types<Geometry1, Geometry2>) + (same_point_order), + ASSIGN_IS_NOT_SUPPORTED_FOR_DIFFERENT_POINT_ORDER, + (types<Geometry1, Geometry2>) ); + static bool const same_closure + = closure<Geometry1>::value == closure<Geometry2>::value; BOOST_MPL_ASSERT_MSG ( - same_closure, ASSIGN_IS_NOT_SUPPORTED_FOR_DIFFERENT_CLOSURE - , (types<Geometry1, Geometry2>) + (same_closure), + ASSIGN_IS_NOT_SUPPORTED_FOR_DIFFERENT_CLOSURE, + (types<Geometry1, Geometry2>) ); dispatch::convert<Geometry2, Geometry1>::apply(geometry2, geometry1); @@ -317,8 +318,8 @@ struct assign<Geometry1, variant<BOOST_VARIANT_ENUM_PARAMS(T)> > }; -template <BOOST_VARIANT_ENUM_PARAMS(typename A), BOOST_VARIANT_ENUM_PARAMS(typename B)> -struct assign<variant<BOOST_VARIANT_ENUM_PARAMS(A)>, variant<BOOST_VARIANT_ENUM_PARAMS(B)> > +template <BOOST_VARIANT_ENUM_PARAMS(typename T1), BOOST_VARIANT_ENUM_PARAMS(typename T2)> +struct assign<variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, variant<BOOST_VARIANT_ENUM_PARAMS(T2)> > { struct visitor: static_visitor<void> { @@ -337,8 +338,8 @@ struct assign<variant<BOOST_VARIANT_ENUM_PARAMS(A)>, variant<BOOST_VARIANT_ENUM_ }; static inline void - apply(variant<BOOST_VARIANT_ENUM_PARAMS(A)>& geometry1, - variant<BOOST_VARIANT_ENUM_PARAMS(B)> const& geometry2) + apply(variant<BOOST_VARIANT_ENUM_PARAMS(T1)>& geometry1, + variant<BOOST_VARIANT_ENUM_PARAMS(T2)> const& geometry2) { return apply_visitor(visitor(), geometry1, geometry2); } diff --git a/boost/geometry/algorithms/buffer.hpp b/boost/geometry/algorithms/buffer.hpp index b8b07ad4d9..d85d32a0c6 100644 --- a/boost/geometry/algorithms/buffer.hpp +++ b/boost/geometry/algorithms/buffer.hpp @@ -17,8 +17,9 @@ #include <cstddef> #include <boost/numeric/conversion/cast.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/algorithms/clear.hpp> diff --git a/boost/geometry/algorithms/centroid.hpp b/boost/geometry/algorithms/centroid.hpp index 65dc9c3753..67ed68ac03 100644 --- a/boost/geometry/algorithms/centroid.hpp +++ b/boost/geometry/algorithms/centroid.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2015. +// Modifications copyright (c) 2014-2015 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -23,9 +23,11 @@ #include <cstddef> +#include <boost/core/ignore_unused.hpp> #include <boost/range.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/core/closure.hpp> @@ -41,8 +43,9 @@ #include <boost/geometry/geometries/concepts/check.hpp> #include <boost/geometry/algorithms/assign.hpp> -#include <boost/geometry/algorithms/detail/interior_iterator.hpp> #include <boost/geometry/algorithms/convert.hpp> +#include <boost/geometry/algorithms/detail/interior_iterator.hpp> +#include <boost/geometry/algorithms/detail/point_on_border.hpp> #include <boost/geometry/algorithms/not_implemented.hpp> #include <boost/geometry/strategies/centroid.hpp> #include <boost/geometry/strategies/concepts/centroid_concept.hpp> @@ -116,8 +119,8 @@ template < typename Indexed, typename Point, - std::size_t Dimension, - std::size_t DimensionCount + std::size_t Dimension = 0, + std::size_t DimensionCount = dimension<Indexed>::type::value > struct centroid_indexed_calculator { @@ -137,8 +140,7 @@ struct centroid_indexed_calculator centroid_indexed_calculator < - Indexed, Point, - Dimension + 1, DimensionCount + Indexed, Point, Dimension + 1 >::apply(indexed, centroid); } }; @@ -161,8 +163,7 @@ struct centroid_indexed { centroid_indexed_calculator < - Indexed, Point, - 0, dimension<Indexed>::type::value + Indexed, Point >::apply(indexed, centroid); } }; @@ -183,8 +184,9 @@ inline bool range_ok(Range const& range, Point& centroid) { #if ! defined(BOOST_GEOMETRY_CENTROID_NO_THROW) throw centroid_exception(); -#endif +#else return false; +#endif } else // if (n == 1) { @@ -192,7 +194,7 @@ inline bool range_ok(Range const& range, Point& centroid) geometry::convert(*boost::begin(range), centroid); return false; } - return true; + //return true; // unreachable } /*! @@ -207,6 +209,8 @@ struct centroid_range_state Strategy const& strategy, typename Strategy::state_type& state) { + boost::ignore_unused(strategy); + typedef typename geometry::point_type<Ring const>::type point_type; typedef typename closeable_view<Ring const, Closure>::type view_type; @@ -237,7 +241,7 @@ template <closure_selector Closure> struct centroid_range { template<typename Range, typename Point, typename Strategy> - static inline void apply(Range const& range, Point& centroid, + static inline bool apply(Range const& range, Point& centroid, Strategy const& strategy) { if (range_ok(range, centroid)) @@ -248,11 +252,16 @@ struct centroid_range typename Strategy::state_type state; centroid_range_state<Closure>::apply(range, transformer, strategy, state); - strategy.result(state, centroid); - - // translate the result back - transformer.apply_reverse(centroid); + + if ( strategy.result(state, centroid) ) + { + // translate the result back + transformer.apply_reverse(centroid); + return true; + } } + + return false; } }; @@ -289,8 +298,8 @@ struct centroid_polygon_state struct centroid_polygon { template<typename Polygon, typename Point, typename Strategy> - static inline void apply(Polygon const& poly, Point& centroid, - Strategy const& strategy) + static inline bool apply(Polygon const& poly, Point& centroid, + Strategy const& strategy) { if (range_ok(exterior_ring(poly), centroid)) { @@ -300,11 +309,16 @@ struct centroid_polygon typename Strategy::state_type state; centroid_polygon_state::apply(poly, transformer, strategy, state); - strategy.result(state, centroid); - - // translate the result back - transformer.apply_reverse(centroid); + + if ( strategy.result(state, centroid) ) + { + // translate the result back + transformer.apply_reverse(centroid); + return true; + } } + + return false; } }; @@ -321,6 +335,7 @@ struct centroid_multi_point_state Strategy const& strategy, typename Strategy::state_type& state) { + boost::ignore_unused(strategy); strategy.apply(static_cast<Point const&>(transformer.apply(point)), state); } @@ -339,7 +354,7 @@ template <typename Policy> struct centroid_multi { template <typename Multi, typename Point, typename Strategy> - static inline void apply(Multi const& multi, + static inline bool apply(Multi const& multi, Point& centroid, Strategy const& strategy) { @@ -364,10 +379,31 @@ struct centroid_multi { Policy::apply(*it, transformer, strategy, state); } - Strategy::result(state, centroid); - // translate the result back - transformer.apply_reverse(centroid); + if ( strategy.result(state, centroid) ) + { + // translate the result back + transformer.apply_reverse(centroid); + return true; + } + + return false; + } +}; + + +template <typename Algorithm> +struct centroid_linear_areal +{ + template <typename Geometry, typename Point, typename Strategy> + static inline void apply(Geometry const& geom, + Point& centroid, + Strategy const& strategy) + { + if ( ! Algorithm::apply(geom, centroid, strategy) ) + { + geometry::point_on_border(centroid, geom); + } } }; @@ -405,32 +441,47 @@ struct centroid<Segment, segment_tag> template <typename Ring> struct centroid<Ring, ring_tag> - : detail::centroid::centroid_range<geometry::closure<Ring>::value> + : detail::centroid::centroid_linear_areal + < + detail::centroid::centroid_range<geometry::closure<Ring>::value> + > {}; template <typename Linestring> struct centroid<Linestring, linestring_tag> - : detail::centroid::centroid_range<closed> + : detail::centroid::centroid_linear_areal + < + detail::centroid::centroid_range<closed> + > {}; template <typename Polygon> struct centroid<Polygon, polygon_tag> - : detail::centroid::centroid_polygon + : detail::centroid::centroid_linear_areal + < + detail::centroid::centroid_polygon + > {}; template <typename MultiLinestring> struct centroid<MultiLinestring, multi_linestring_tag> - : detail::centroid::centroid_multi + : detail::centroid::centroid_linear_areal < - detail::centroid::centroid_range_state<closed> + detail::centroid::centroid_multi + < + detail::centroid::centroid_range_state<closed> + > > {}; template <typename MultiPolygon> struct centroid<MultiPolygon, multi_polygon_tag> - : detail::centroid::centroid_multi + : detail::centroid::centroid_linear_areal < - detail::centroid::centroid_polygon_state + detail::centroid::centroid_multi + < + detail::centroid::centroid_polygon_state + > > {}; diff --git a/boost/geometry/algorithms/clear.hpp b/boost/geometry/algorithms/clear.hpp index 1850816b1b..22b61e7c30 100644 --- a/boost/geometry/algorithms/clear.hpp +++ b/boost/geometry/algorithms/clear.hpp @@ -16,6 +16,7 @@ #include <boost/type_traits/remove_const.hpp> + #include <boost/variant/apply_visitor.hpp> #include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> diff --git a/boost/geometry/algorithms/convert.hpp b/boost/geometry/algorithms/convert.hpp index 914ef8f420..5d68854a45 100644 --- a/boost/geometry/algorithms/convert.hpp +++ b/boost/geometry/algorithms/convert.hpp @@ -22,8 +22,9 @@ #include <boost/range.hpp> #include <boost/type_traits/is_array.hpp> #include <boost/type_traits/remove_reference.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/arithmetic/arithmetic.hpp> @@ -276,7 +277,7 @@ template bool UseAssignment = boost::is_same<Geometry1, Geometry2>::value && !boost::is_array<Geometry1>::value > -struct convert: not_implemented<Tag1, Tag2, mpl::int_<DimensionCount> > +struct convert: not_implemented<Tag1, Tag2, boost::mpl::int_<DimensionCount> > {}; diff --git a/boost/geometry/algorithms/convex_hull.hpp b/boost/geometry/algorithms/convex_hull.hpp index 09f4c5142d..c6db004f3d 100644 --- a/boost/geometry/algorithms/convex_hull.hpp +++ b/boost/geometry/algorithms/convex_hull.hpp @@ -4,8 +4,8 @@ // Copyright (c) 2008-2012 Bruno Lalande, Paris, France. // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2015. +// Modifications copyright (c) 2014-2015 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -20,8 +20,9 @@ #define BOOST_GEOMETRY_ALGORITHMS_CONVEX_HULL_HPP #include <boost/array.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/core/cs.hpp> @@ -35,6 +36,8 @@ #include <boost/geometry/strategies/concepts/convex_hull_concept.hpp> #include <boost/geometry/strategies/default_strategy.hpp> +#include <boost/geometry/util/condition.hpp> + #include <boost/geometry/views/detail/range_type.hpp> #include <boost/geometry/algorithms/num_points.hpp> @@ -122,7 +125,7 @@ struct convex_hull<Box, box_tag> boost::array<typename point_type<Box>::type, 4> range; geometry::detail::assign_box_corners_oriented<Reverse>(box, range); geometry::append(out, range); - if (Close) + if (BOOST_GEOMETRY_CONDITION(Close)) { geometry::append(out, *boost::begin(range)); } diff --git a/boost/geometry/algorithms/correct.hpp b/boost/geometry/algorithms/correct.hpp index 3c61b2c0d2..11ed6ecff9 100644 --- a/boost/geometry/algorithms/correct.hpp +++ b/boost/geometry/algorithms/correct.hpp @@ -23,8 +23,9 @@ #include <boost/mpl/assert.hpp> #include <boost/range.hpp> #include <boost/type_traits/remove_reference.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/algorithms/detail/interior_iterator.hpp> diff --git a/boost/geometry/algorithms/covered_by.hpp b/boost/geometry/algorithms/covered_by.hpp index e50dc338af..eb8e732409 100644 --- a/boost/geometry/algorithms/covered_by.hpp +++ b/boost/geometry/algorithms/covered_by.hpp @@ -22,8 +22,8 @@ #include <cstddef> -#include <boost/variant/static_visitor.hpp> #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/algorithms/not_implemented.hpp> diff --git a/boost/geometry/algorithms/crosses.hpp b/boost/geometry/algorithms/crosses.hpp index 91ed3e0806..cec4931a64 100644 --- a/boost/geometry/algorithms/crosses.hpp +++ b/boost/geometry/algorithms/crosses.hpp @@ -21,6 +21,9 @@ #define BOOST_GEOMETRY_ALGORITHMS_CROSSES_HPP #include <cstddef> + +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/core/access.hpp> @@ -141,8 +144,8 @@ namespace resolve_variant }; - template <BOOST_VARIANT_ENUM_PARAMS(typename A), BOOST_VARIANT_ENUM_PARAMS(typename B)> - struct crosses<variant<BOOST_VARIANT_ENUM_PARAMS(A)>, variant<BOOST_VARIANT_ENUM_PARAMS(B)> > + template <BOOST_VARIANT_ENUM_PARAMS(typename T1), BOOST_VARIANT_ENUM_PARAMS(typename T2)> + struct crosses<variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, variant<BOOST_VARIANT_ENUM_PARAMS(T2)> > { struct visitor: static_visitor<bool> { @@ -162,8 +165,8 @@ namespace resolve_variant static inline bool apply( - const variant<BOOST_VARIANT_ENUM_PARAMS(A)>& geometry1, - const variant<BOOST_VARIANT_ENUM_PARAMS(B)>& geometry2) + const variant<BOOST_VARIANT_ENUM_PARAMS(T1)>& geometry1, + const variant<BOOST_VARIANT_ENUM_PARAMS(T2)>& geometry2) { return apply_visitor(visitor(), geometry1, geometry2); } diff --git a/boost/geometry/algorithms/detail/azimuth.hpp b/boost/geometry/algorithms/detail/azimuth.hpp new file mode 100644 index 0000000000..7810b4814e --- /dev/null +++ b/boost/geometry/algorithms/detail/azimuth.hpp @@ -0,0 +1,158 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_AZIMUTH_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_AZIMUTH_HPP + +#include <boost/geometry/core/cs.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/radian_access.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/util/math.hpp> + +#include <boost/geometry/algorithms/not_implemented.hpp> +#include <boost/geometry/algorithms/detail/vincenty_inverse.hpp> + +namespace boost { namespace geometry +{ + +// An azimuth is an angle between a vector/segment from origin to a point of +// interest and a reference vector. Typically north-based azimuth is used. +// North direction is used as a reference, angle is measured clockwise +// (North - 0deg, East - 90deg). For consistency in 2d cartesian CS +// the reference vector is Y axis, angle is measured clockwise. +// http://en.wikipedia.org/wiki/Azimuth + +#ifndef DOXYGEN_NO_DISPATCH +namespace detail_dispatch +{ + +template <typename ReturnType, typename Tag> +struct azimuth + : not_implemented<Tag> +{}; + +template <typename ReturnType> +struct azimuth<ReturnType, geographic_tag> +{ + template <typename P1, typename P2, typename Spheroid> + static inline ReturnType apply(P1 const& p1, P2 const& p2, Spheroid const& spheroid) + { + return geometry::detail::vincenty_inverse<ReturnType> + ( get_as_radian<0>(p1), get_as_radian<1>(p1), + get_as_radian<0>(p2), get_as_radian<1>(p2), + spheroid ).azimuth12(); + } + + template <typename P1, typename P2> + static inline ReturnType apply(P1 const& p1, P2 const& p2) + { + return apply(p1, p2, srs::spheroid<ReturnType>()); + } +}; + +template <typename ReturnType> +struct azimuth<ReturnType, spherical_equatorial_tag> +{ + template <typename P1, typename P2, typename Sphere> + static inline ReturnType apply(P1 const& p1, P2 const& p2, Sphere const& /*unused*/) + { + // http://williams.best.vwh.net/avform.htm#Crs + ReturnType dlon = get_as_radian<0>(p2) - get_as_radian<0>(p1); + ReturnType cos_p2lat = cos(get_as_radian<1>(p2)); + + // An optimization which should kick in often for Boxes + //if ( math::equals(dlon, ReturnType(0)) ) + //if ( get<0>(p1) == get<0>(p2) ) + //{ + // return - sin(get_as_radian<1>(p1)) * cos_p2lat); + //} + + // "An alternative formula, not requiring the pre-computation of d" + // In the formula below dlon is used as "d" + return atan2(sin(dlon) * cos_p2lat, + cos(get_as_radian<1>(p1)) * sin(get_as_radian<1>(p2)) + - sin(get_as_radian<1>(p1)) * cos_p2lat * cos(dlon)); + } + + template <typename P1, typename P2> + static inline ReturnType apply(P1 const& p1, P2 const& p2) + { + return apply(p1, p2, 0); // dummy model + } +}; + +template <typename ReturnType> +struct azimuth<ReturnType, spherical_polar_tag> + : azimuth<ReturnType, spherical_equatorial_tag> +{}; + +template <typename ReturnType> +struct azimuth<ReturnType, cartesian_tag> +{ + template <typename P1, typename P2, typename Plane> + static inline ReturnType apply(P1 const& p1, P2 const& p2, Plane const& /*unused*/) + { + ReturnType x = get<0>(p2) - get<0>(p1); + ReturnType y = get<1>(p2) - get<1>(p1); + + // NOTE: azimuth 0 is at Y axis, increasing right + // as in spherical/geographic where 0 is at North axis + return atan2(x, y); + } + + template <typename P1, typename P2> + static inline ReturnType apply(P1 const& p1, P2 const& p2) + { + return apply(p1, p2, 0); // dummy model + } +}; + +} // detail_dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +/// Calculate azimuth between two points. +/// The result is in radians. +template <typename ReturnType, typename Point1, typename Point2> +inline ReturnType azimuth(Point1 const& p1, Point2 const& p2) +{ + return detail_dispatch::azimuth + < + ReturnType, + typename geometry::cs_tag<Point1>::type + >::apply(p1, p2); +} + +/// Calculate azimuth between two points. +/// The result is in radians. +template <typename ReturnType, typename Point1, typename Point2, typename Model> +inline ReturnType azimuth(Point1 const& p1, Point2 const& p2, Model const& model) +{ + return detail_dispatch::azimuth + < + ReturnType, + typename geometry::cs_tag<Point1>::type + >::apply(p1, p2, model); +} + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_AZIMUTH_HPP diff --git a/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp b/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp index c959ee849b..127e4c3fb2 100644 --- a/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp +++ b/boost/geometry/algorithms/detail/buffer/buffer_inserter.hpp @@ -12,6 +12,7 @@ #include <cstddef> #include <iterator> +#include <boost/core/ignore_unused.hpp> #include <boost/numeric/conversion/cast.hpp> #include <boost/range.hpp> @@ -20,6 +21,7 @@ #include <boost/geometry/core/exterior_ring.hpp> #include <boost/geometry/core/interior_rings.hpp> +#include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/math.hpp> #include <boost/geometry/strategies/buffer.hpp> @@ -28,6 +30,7 @@ #include <boost/geometry/algorithms/detail/buffer/line_line_intersection.hpp> #include <boost/geometry/algorithms/detail/buffer/parallel_continue.hpp> +#include <boost/geometry/algorithms/num_interior_rings.hpp> #include <boost/geometry/algorithms/simplify.hpp> #include <boost/geometry/views/detail/normalized_view.hpp> @@ -144,15 +147,25 @@ struct buffer_range intersection_point); } + switch(join) { case strategy::buffer::join_continue : // No join, we get two consecutive sides - return; + break; case strategy::buffer::join_concave : - collection.add_piece(strategy::buffer::buffered_concave, - previous_input, prev_perp2, perp1); - return; + { + std::vector<output_point_type> range_out; + range_out.push_back(prev_perp2); + range_out.push_back(previous_input); + collection.add_piece(strategy::buffer::buffered_concave, previous_input, range_out); + + range_out.clear(); + range_out.push_back(previous_input); + range_out.push_back(perp1); + collection.add_piece(strategy::buffer::buffered_concave, previous_input, range_out); + } + break; case strategy::buffer::join_spike : { // For linestrings, only add spike at one side to avoid @@ -160,22 +173,24 @@ struct buffer_range std::vector<output_point_type> range_out; end_strategy.apply(penultimate_input, prev_perp2, previous_input, perp1, side, distance, range_out); collection.add_endcap(end_strategy, range_out, previous_input); + collection.set_current_ring_concave(); } - return; + break; case strategy::buffer::join_convex : - break; // All code below handles this - } - - // The corner is convex, we create a join - // TODO (future) - avoid a separate vector, add the piece directly - std::vector<output_point_type> range_out; - if (join_strategy.apply(intersection_point, - previous_input, prev_perp2, perp1, - distance.apply(previous_input, input, side), - range_out)) - { - collection.add_piece(strategy::buffer::buffered_join, - previous_input, range_out); + { + // The corner is convex, we create a join + // TODO (future) - avoid a separate vector, add the piece directly + std::vector<output_point_type> range_out; + if (join_strategy.apply(intersection_point, + previous_input, prev_perp2, perp1, + distance.apply(previous_input, input, side), + range_out)) + { + collection.add_piece(strategy::buffer::buffered_join, + previous_input, range_out); + } + } + break; } } @@ -225,18 +240,13 @@ struct buffer_range output_point_type& last_p1, output_point_type& last_p2) { + boost::ignore_unused(side_strategy); + typedef typename std::iterator_traits < Iterator >::value_type point_type; - typedef typename robust_point_type - < - point_type, - RobustPolicy - >::type robust_point_type; - - robust_point_type previous_robust_input; point_type second_point, penultimate_point, ultimate_point; // last two points from begin/end /* @@ -261,57 +271,50 @@ struct buffer_range Iterator it = begin; - geometry::recalculate(previous_robust_input, *begin, robust_policy); - std::vector<output_point_type> generated_side; generated_side.reserve(2); for (Iterator prev = it++; it != end; ++it) { - robust_point_type robust_input; - geometry::recalculate(robust_input, *it, robust_policy); - // Check on equality - however, if input is simplified, this is - // unlikely (though possible by rescaling or for degenerated pointlike polygons) - if (! detail::equals::equals_point_point(previous_robust_input, robust_input)) - { - generated_side.clear(); - side_strategy.apply(*prev, *it, side, - distance_strategy, generated_side); + generated_side.clear(); + side_strategy.apply(*prev, *it, side, + distance_strategy, generated_side); - if (generated_side.empty()) - { - break; - } + if (generated_side.empty()) + { + // Because input is simplified, this is improbable, + // but it can happen for degenerate geometries + // Further handling of this side is skipped + continue; + } - result = true; + result = true; - if (! first) - { - add_join(collection, - penultimate_point, - *prev, last_p1, last_p2, - *it, generated_side.front(), generated_side.back(), - side, - distance_strategy, join_strategy, end_strategy, - robust_policy); - } + if (! first) + { + add_join(collection, + penultimate_point, + *prev, last_p1, last_p2, + *it, generated_side.front(), generated_side.back(), + side, + distance_strategy, join_strategy, end_strategy, + robust_policy); + } - collection.add_side_piece(*prev, *it, generated_side, first); + collection.add_side_piece(*prev, *it, generated_side, first); - penultimate_point = *prev; - ultimate_point = *it; - last_p1 = generated_side.front(); - last_p2 = generated_side.back(); - prev = it; - if (first) - { - first = false; - second_point = *it; - first_p1 = generated_side.front(); - first_p2 = generated_side.back(); - } + penultimate_point = *prev; + ultimate_point = *it; + last_p1 = generated_side.front(); + last_p2 = generated_side.back(); + prev = it; + if (first) + { + first = false; + second_point = *it; + first_p1 = generated_side.front(); + first_p2 = generated_side.back(); } - previous_robust_input = robust_input; } return result; } @@ -775,7 +778,7 @@ public: distance, side_strategy, join_strategy, end_strategy, point_strategy, robust_policy); - collection.finish_ring(); + collection.finish_ring(false, geometry::num_interior_rings(polygon) > 0u); } apply_interior_rings(interior_rings(polygon), @@ -839,6 +842,8 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator VisitPiecesPolicy& visit_pieces_policy ) { + boost::ignore_unused(visit_pieces_policy); + typedef detail::buffer::buffered_piece_collection < typename geometry::ring_type<GeometryOutput>::type, @@ -852,6 +857,11 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator typename tag_cast<typename tag<GeometryInput>::type, areal_tag>::type, areal_tag >::type::value; + bool const linear = boost::is_same + < + typename tag_cast<typename tag<GeometryInput>::type, linear_tag>::type, + linear_tag + >::type::value; dispatch::buffer_inserter < @@ -868,9 +878,10 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator robust_policy); collection.get_turns(); - if (areal) + collection.classify_turns(linear); + if (BOOST_GEOMETRY_CONDITION(areal)) { - collection.check_remaining_points(distance_strategy.factor()); + collection.check_remaining_points(distance_strategy); } // Visit the piece collection. This does nothing (by default), but @@ -889,7 +900,8 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator // - the output is counter clockwise // and avoid reversing twice bool reverse = distance_strategy.negative() && areal; - if (geometry::point_order<GeometryOutput>::value == counterclockwise) + if (BOOST_GEOMETRY_CONDITION( + geometry::point_order<GeometryOutput>::value == counterclockwise)) { reverse = ! reverse; } @@ -898,6 +910,11 @@ inline void buffer_inserter(GeometryInput const& geometry_input, OutputIterator collection.reverse(); } + if (distance_strategy.negative() && areal) + { + collection.discard_nonintersecting_deflated_rings(); + } + collection.template assign<GeometryOutput>(out); // Visit collection again diff --git a/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp b/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp index 6a2e6b32c5..c5bb8acc05 100644 --- a/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp +++ b/boost/geometry/algorithms/detail/buffer/buffer_policies.hpp @@ -35,7 +35,7 @@ namespace detail { namespace buffer enum intersection_location_type { - location_ok, inside_buffer, inside_original + location_ok, inside_buffer, location_discard }; class backtrack_for_buffer @@ -120,10 +120,14 @@ struct buffer_turn_info return robust_point; } - intersection_location_type location; int count_within; + + bool within_original; + int count_on_original_boundary; + int count_in_original; // increased by +1 for in ext.ring, -1 for int.ring + int count_on_offsetted; int count_on_helper; int count_within_near_offsetted; @@ -138,6 +142,9 @@ struct buffer_turn_info : turn_index(-1) , location(location_ok) , count_within(0) + , within_original(false) + , count_on_original_boundary(0) + , count_in_original(0) , count_on_offsetted(0) , count_on_helper(0) , count_within_near_offsetted(0) diff --git a/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp b/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp index 558a61fcb4..a501e3f197 100644 --- a/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp +++ b/boost/geometry/algorithms/detail/buffer/buffered_piece_collection.hpp @@ -12,8 +12,9 @@ #include <algorithm> #include <cstddef> #include <set> -#include <boost/range.hpp> +#include <boost/core/ignore_unused.hpp> +#include <boost/range.hpp> #include <boost/geometry/core/coordinate_type.hpp> #include <boost/geometry/core/point_type.hpp> @@ -24,13 +25,14 @@ #include <boost/geometry/strategies/buffer.hpp> #include <boost/geometry/geometries/ring.hpp> -#include <boost/geometry/geometries/polygon.hpp> #include <boost/geometry/algorithms/detail/buffer/buffered_ring.hpp> #include <boost/geometry/algorithms/detail/buffer/buffer_policies.hpp> #include <boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp> #include <boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp> +#include <boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp> +#include <boost/geometry/algorithms/detail/disjoint/point_box.hpp> #include <boost/geometry/algorithms/detail/overlay/add_rings.hpp> #include <boost/geometry/algorithms/detail/overlay/assign_parents.hpp> #include <boost/geometry/algorithms/detail/overlay/enrichment_info.hpp> @@ -41,6 +43,8 @@ #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp> #include <boost/geometry/algorithms/detail/occupation_info.hpp> #include <boost/geometry/algorithms/detail/partition.hpp> +#include <boost/geometry/algorithms/detail/sections/sectionalize.hpp> +#include <boost/geometry/algorithms/detail/sections/section_box_policies.hpp> #include <boost/geometry/util/range.hpp> @@ -89,7 +93,7 @@ enum segment_relation_code * form together the offsetted ring (marked with o below) * The 8 pieces are part of the piece collection and use for inside-checks * The inner parts form (using 1 or 2 points per piece, often co-located) - * form together the robust_ring (marked with r below) + * form together the robust_polygons (marked with r below) * The remaining piece-segments are helper-segments (marked with h) * * ooooooooooooooooo @@ -120,7 +124,7 @@ struct buffered_piece_collection // Robust ring/polygon type, always clockwise typedef geometry::model::ring<robust_point_type> robust_ring_type; - typedef geometry::model::polygon<robust_point_type> robust_polygon_type; + typedef geometry::model::box<robust_point_type> robust_box_type; typedef typename strategy::side::services::default_strategy < @@ -164,6 +168,9 @@ struct buffered_piece_collection struct piece { + typedef robust_ring_type piece_robust_ring_type; + typedef geometry::section<robust_box_type, 1> section_type; + strategy::buffer::piece_type type; int index; @@ -181,18 +188,58 @@ struct buffered_piece_collection #if defined(BOOST_GEOMETRY_BUFFER_USE_HELPER_POINTS) // 2: half, not part of offsetted rings - part of robust ring - std::vector<point_type> helper_points; // 4 points for segment, 3 points for join - 0 points for flat-end + std::vector<point_type> helper_points; // 4 points for side, 3 points for join - 0 points for flat-end #endif + bool is_monotonic_increasing[2]; // 0=x, 1=y + bool is_monotonic_decreasing[2]; // 0=x, 1=y + + // Monotonic sections of pieces around points + std::vector<section_type> sections; + + // Robust representations // 3: complete ring robust_ring_type robust_ring; - geometry::model::box<robust_point_type> robust_envelope; + robust_box_type robust_envelope; + robust_box_type robust_offsetted_envelope; std::vector<robust_turn> robust_turns; // Used only in insert_rescaled_piece_turns - we might use a map instead }; + struct robust_original + { + typedef robust_ring_type original_robust_ring_type; + typedef geometry::sections<robust_box_type, 1> sections_type; + + inline robust_original() + : m_is_interior(false) + , m_has_interiors(true) + {} + + inline robust_original(robust_ring_type const& ring, + bool is_interior, bool has_interiors) + : m_ring(ring) + , m_is_interior(is_interior) + , m_has_interiors(has_interiors) + { + geometry::envelope(m_ring, m_box); + + // create monotonic sections in y-dimension + typedef boost::mpl::vector_c<std::size_t, 1> dimensions; + geometry::sectionalize<false, dimensions>(m_ring, + detail::no_rescale_policy(), m_sections); + } + + robust_ring_type m_ring; + robust_box_type m_box; + sections_type m_sections; + + bool m_is_interior; + bool m_has_interiors; + }; + typedef std::vector<piece> piece_vector_type; piece_vector_type m_pieces; @@ -200,11 +247,17 @@ struct buffered_piece_collection int m_first_piece_index; buffered_ring_collection<buffered_ring<Ring> > offsetted_rings; // indexed by multi_index - buffered_ring_collection<robust_polygon_type> robust_polygons; // robust representation of the original(s) + std::vector<robust_original> robust_originals; // robust representation of the original(s) robust_ring_type current_robust_ring; buffered_ring_collection<Ring> traversed_rings; segment_identifier current_segment_id; + // Specificly for offsetted rings around points + // but also for large joins with many points + typedef geometry::sections<robust_box_type, 2> sections_type; + sections_type monotonic_sections; + + RobustPolicy const& m_robust_policy; struct redundant_turn @@ -378,7 +431,7 @@ struct buffered_piece_collection } } - inline void classify_turns() + inline void classify_turns(bool linear) { for (typename boost::range_iterator<turn_vector_type>::type it = boost::begin(m_turns); it != boost::end(m_turns); ++it) @@ -387,6 +440,10 @@ struct buffered_piece_collection { it->location = inside_buffer; } + if (it->count_on_original_boundary > 0 && ! linear) + { + it->location = inside_buffer; + } if (it->count_within_near_offsetted > 0) { // Within can have in rare cases a rounding issue. We don't discard this @@ -394,31 +451,40 @@ struct buffered_piece_collection // will never start a new ring from this type of points. it->selectable_start = false; } - } } - inline void check_remaining_points(int factor) + template <typename DistanceStrategy> + inline void check_remaining_points(DistanceStrategy const& distance_strategy) { - // TODO: use partition + // Check if a turn is inside any of the originals + + turn_in_original_visitor<turn_vector_type> visitor(m_turns); + geometry::partition + < + robust_box_type, + turn_get_box, turn_in_original_ovelaps_box, + original_get_box, original_ovelaps_box, + include_turn_policy, detail::partition::include_all_policy + >::apply(m_turns, robust_originals, visitor); + + bool const deflate = distance_strategy.negative(); for (typename boost::range_iterator<turn_vector_type>::type it = boost::begin(m_turns); it != boost::end(m_turns); ++it) { - if (it->location == location_ok) + buffer_turn_info_type& turn = *it; + if (turn.location == location_ok) { - int code = -1; - for (std::size_t i = 0; i < robust_polygons.size(); i++) + if (deflate && turn.count_in_original <= 0) { - if (geometry::covered_by(it->robust_point, robust_polygons[i])) - { - code = 1; - break; - } + // For deflate: it is not in original, discard + turn.location = location_discard; } - if (code * factor == 1) + else if (! deflate && turn.count_in_original > 0) { - it->location = inside_original; + // For inflate: it is in original, discard + turn.location = location_discard; } } } @@ -474,6 +540,7 @@ struct buffered_piece_collection // Take into account for the box (intersection points should fall inside, // but in theory they can be one off because of rounding geometry::expand(pc.robust_envelope, it->robust_point); + geometry::expand(pc.robust_offsetted_envelope, it->robust_point); } } @@ -502,9 +569,10 @@ struct buffered_piece_collection { int const index_in_vector = 1 + rit->seg_id.segment_index - piece_segment_index; BOOST_ASSERT - ( - index_in_vector > 0 && index_in_vector < pc.offsetted_count - ); + ( + index_in_vector > 0 + && index_in_vector < pc.offsetted_count + ); pc.robust_ring.insert(boost::begin(pc.robust_ring) + index_in_vector, rit->point); pc.offsetted_count++; @@ -517,25 +585,132 @@ struct buffered_piece_collection BOOST_ASSERT(assert_indices_in_robust_rings()); } + template <std::size_t Dimension> + static inline void determine_monotonicity(piece& pc, + robust_point_type const& current, + robust_point_type const& next) + { + if (geometry::get<Dimension>(current) >= geometry::get<Dimension>(next)) + { + pc.is_monotonic_increasing[Dimension] = false; + } + if (geometry::get<Dimension>(current) <= geometry::get<Dimension>(next)) + { + pc.is_monotonic_decreasing[Dimension] = false; + } + } + + static inline void determine_properties(piece& pc) + { + pc.is_monotonic_increasing[0] = true; + pc.is_monotonic_increasing[1] = true; + pc.is_monotonic_decreasing[0] = true; + pc.is_monotonic_decreasing[1] = true; + + if (pc.offsetted_count < 2) + { + return; + } + + typename robust_ring_type::const_iterator current = pc.robust_ring.begin(); + typename robust_ring_type::const_iterator next = current + 1; + + for (int i = 1; i < pc.offsetted_count; i++) + { + determine_monotonicity<0>(pc, *current, *next); + determine_monotonicity<1>(pc, *current, *next); + current = next; + ++next; + } + + } + + void determine_properties() + { + for (typename piece_vector_type::iterator it = boost::begin(m_pieces); + it != boost::end(m_pieces); + ++it) + { + determine_properties(*it); + } + } + + inline void reverse_negative_robust_rings() + { + for (typename piece_vector_type::iterator it = boost::begin(m_pieces); + it != boost::end(m_pieces); + ++it) + { + piece& pc = *it; + if (geometry::area(pc.robust_ring) < 0) + { + // Rings can be ccw: + // - in a concave piece + // - in a line-buffer with a negative buffer-distance + std::reverse(pc.robust_ring.begin(), pc.robust_ring.end()); + } + } + } + + inline void prepare_buffered_point_piece(piece& pc) + { + // create monotonic sections in y-dimension + typedef boost::mpl::vector_c<std::size_t, 1> dimensions; + geometry::sectionalize<false, dimensions>(pc.robust_ring, + detail::no_rescale_policy(), pc.sections); + + // TODO (next phase) determine min/max radius + } + + inline void prepare_buffered_point_pieces() + { + for (typename piece_vector_type::iterator it = boost::begin(m_pieces); + it != boost::end(m_pieces); + ++it) + { + if (it->type == geometry::strategy::buffer::buffered_point) + { + prepare_buffered_point_piece(*it); + } + } + } + inline void get_turns() { + for(typename boost::range_iterator<sections_type>::type it + = boost::begin(monotonic_sections); + it != boost::end(monotonic_sections); + ++it) + { + enlarge_box(it->bounding_box, 1); + } + { // Calculate the turns piece_turn_visitor < + piece_vector_type, buffered_ring_collection<buffered_ring<Ring> >, turn_vector_type, RobustPolicy - > visitor(offsetted_rings, m_turns, m_robust_policy); + > visitor(m_pieces, offsetted_rings, m_turns, m_robust_policy); geometry::partition < - model::box<robust_point_type>, piece_get_box, piece_ovelaps_box - >::apply(m_pieces, visitor); + robust_box_type, + detail::section::get_section_box, + detail::section::overlaps_section_box + >::apply(monotonic_sections, visitor); } insert_rescaled_piece_turns(); + reverse_negative_robust_rings(); + + determine_properties(); + + prepare_buffered_point_pieces(); + { // Check if it is inside any of the pieces turn_in_piece_visitor @@ -545,16 +720,12 @@ struct buffered_piece_collection geometry::partition < - model::box<robust_point_type>, + robust_box_type, turn_get_box, turn_ovelaps_box, piece_get_box, piece_ovelaps_box >::apply(m_turns, m_pieces, visitor); } - - classify_turns(); - - //get_occupation(); } inline void start_new_ring() @@ -571,7 +742,38 @@ struct buffered_piece_collection m_first_piece_index = boost::size(m_pieces); } - inline void finish_ring(bool is_interior = false) + inline void update_closing_point() + { + BOOST_ASSERT(! offsetted_rings.empty()); + buffered_ring<Ring>& added = offsetted_rings.back(); + if (! boost::empty(added)) + { + range::back(added) = range::front(added); + } + } + + inline void update_last_point(point_type const& p, + buffered_ring<Ring>& ring) + { + // For the first point of a new piece, and there were already + // points in the offsetted ring, for some piece types the first point + // is a duplicate of the last point of the previous piece. + + // TODO: disable that, that point should not be added + + // For now, it is made equal because due to numerical instability, + // it can be a tiny bit off, possibly causing a self-intersection + + BOOST_ASSERT(boost::size(m_pieces) > 0); + if (! ring.empty() + && current_segment_id.segment_index + == m_pieces.back().first_seg_id.segment_index) + { + ring.back() = p; + } + } + + inline void finish_ring(bool is_interior = false, bool has_interiors = false) { if (m_first_piece_index == -1) { @@ -588,41 +790,50 @@ struct buffered_piece_collection } m_first_piece_index = -1; - if (!current_robust_ring.empty()) + update_closing_point(); + + if (! current_robust_ring.empty()) { - BOOST_ASSERT(geometry::equals(current_robust_ring.front(), current_robust_ring.back())); + BOOST_ASSERT + ( + geometry::equals(current_robust_ring.front(), + current_robust_ring.back()) + ); - if (is_interior) - { - if (!robust_polygons.empty()) - { - robust_polygons.back().inners().push_back(current_robust_ring); - } - } - else - { - robust_polygons.resize(robust_polygons.size() + 1); - robust_polygons.back().outer() = current_robust_ring; - } + robust_originals.push_back( + robust_original(current_robust_ring, + is_interior, has_interiors)); } } + inline void set_current_ring_concave() + { + BOOST_ASSERT(boost::size(offsetted_rings) > 0); + offsetted_rings.back().has_concave = true; + } + inline int add_point(point_type const& p) { - BOOST_ASSERT - ( - boost::size(offsetted_rings) > 0 - ); + BOOST_ASSERT(boost::size(offsetted_rings) > 0); + + buffered_ring<Ring>& current_ring = offsetted_rings.back(); + update_last_point(p, current_ring); current_segment_id.segment_index++; - offsetted_rings.back().push_back(p); - return offsetted_rings.back().size(); + current_ring.push_back(p); + return current_ring.size(); } //------------------------------------------------------------------------- - inline piece& create_piece(strategy::buffer::piece_type type, bool decrease_segment_index_by_one) + inline piece& create_piece(strategy::buffer::piece_type type, + bool decrease_segment_index_by_one) { + if (type == strategy::buffer::buffered_concave) + { + offsetted_rings.back().has_concave = true; + } + piece pc; pc.type = type; pc.index = boost::size(m_pieces); @@ -634,6 +845,7 @@ struct buffered_piece_collection std::size_t const n = boost::size(offsetted_rings.back()); pc.first_seg_id.segment_index = decrease_segment_index_by_one ? n - 1 : n; + pc.last_segment_index = pc.first_seg_id.segment_index; m_pieces.push_back(pc); return m_pieces.back(); @@ -641,6 +853,18 @@ struct buffered_piece_collection inline void init_rescale_piece(piece& pc, std::size_t helper_points_size) { + if (pc.first_seg_id.segment_index < 0) + { + // This indicates an error situation: an earlier piece was empty + // It currently does not happen + // std::cout << "EMPTY " << pc.type << " " << pc.index << " " << pc.first_seg_id.multi_index << std::endl; + pc.offsetted_count = 0; + return; + } + + BOOST_ASSERT(pc.first_seg_id.multi_index >= 0); + BOOST_ASSERT(pc.last_segment_index >= 0); + pc.offsetted_count = pc.last_segment_index - pc.first_seg_id.segment_index; BOOST_ASSERT(pc.offsetted_count >= 0); @@ -674,16 +898,65 @@ struct buffered_piece_collection return rob_point; } + // TODO: this is shared with sectionalize, move to somewhere else (assign?) + template <typename Box, typename Value> + inline void enlarge_box(Box& box, Value value) + { + geometry::set<0, 0>(box, geometry::get<0, 0>(box) - value); + geometry::set<0, 1>(box, geometry::get<0, 1>(box) - value); + geometry::set<1, 0>(box, geometry::get<1, 0>(box) + value); + geometry::set<1, 1>(box, geometry::get<1, 1>(box) + value); + } + inline void calculate_robust_envelope(piece& pc) { + if (pc.offsetted_count == 0) + { + return; + } + geometry::detail::envelope::envelope_range::apply(pc.robust_ring, pc.robust_envelope); + + geometry::assign_inverse(pc.robust_offsetted_envelope); + for (int i = 0; i < pc.offsetted_count; i++) + { + geometry::expand(pc.robust_offsetted_envelope, pc.robust_ring[i]); + } + + // Take roundings into account, enlarge boxes with 1 integer + enlarge_box(pc.robust_envelope, 1); + enlarge_box(pc.robust_offsetted_envelope, 1); + } + + inline void sectionalize(piece& pc) + { + + buffered_ring<Ring> const& ring = offsetted_rings.back(); + + typedef geometry::detail::sectionalize::sectionalize_part + < + point_type, + boost::mpl::vector_c<std::size_t, 0, 1> // x,y dimension + > sectionalizer; + + // Create a ring-identifier. The source-index is the piece index + // The multi_index is as in this collection (the ring), but not used here + // The ring_index is not used + ring_identifier ring_id(pc.index, pc.first_seg_id.multi_index, -1); + + sectionalizer::apply(monotonic_sections, + boost::begin(ring) + pc.first_seg_id.segment_index, + boost::begin(ring) + pc.last_segment_index, + m_robust_policy, + ring_id, 10); } inline void finish_piece(piece& pc) { init_rescale_piece(pc, 0u); calculate_robust_envelope(pc); + sectionalize(pc); } inline void finish_piece(piece& pc, @@ -692,10 +965,16 @@ struct buffered_piece_collection const point_type& point3) { init_rescale_piece(pc, 3u); + if (pc.offsetted_count == 0) + { + return; + } + add_helper_point(pc, point1); robust_point_type mid_point = add_helper_point(pc, point2); add_helper_point(pc, point3); calculate_robust_envelope(pc); + sectionalize(pc); current_robust_ring.push_back(mid_point); } @@ -711,6 +990,7 @@ struct buffered_piece_collection robust_point_type mid_point2 = add_helper_point(pc, point2); robust_point_type mid_point1 = add_helper_point(pc, point3); add_helper_point(pc, point4); + sectionalize(pc); calculate_robust_envelope(pc); // Add mid-points in other order to current helper_ring @@ -730,10 +1010,7 @@ struct buffered_piece_collection template <typename Range> inline void add_range_to_piece(piece& pc, Range const& range, bool add_front) { - if (boost::size(range) == 0u) - { - return; - } + BOOST_ASSERT(boost::size(range) != 0u); typename Range::const_iterator it = boost::begin(range); @@ -753,10 +1030,15 @@ struct buffered_piece_collection template <typename Range> - inline void add_piece(strategy::buffer::piece_type type, Range const& range, bool decrease_segment_index_by_one) + inline void add_piece(strategy::buffer::piece_type type, Range const& range, + bool decrease_segment_index_by_one) { piece& pc = create_piece(type, decrease_segment_index_by_one); - add_range_to_piece(pc, range, offsetted_rings.back().empty()); + + if (boost::size(range) > 0u) + { + add_range_to_piece(pc, range, offsetted_rings.back().empty()); + } finish_piece(pc); } @@ -772,13 +1054,14 @@ struct buffered_piece_collection } template <typename Range> - inline void add_piece(strategy::buffer::piece_type type, point_type const& p, Range const& range) + inline void add_piece(strategy::buffer::piece_type type, + point_type const& p, Range const& range) { piece& pc = create_piece(type, true); - add_range_to_piece(pc, range, offsetted_rings.back().empty()); - if (boost::size(range) > 0) + if (boost::size(range) > 0u) { + add_range_to_piece(pc, range, offsetted_rings.back().empty()); finish_piece(pc, range.back(), p, range.front()); } else @@ -788,8 +1071,11 @@ struct buffered_piece_collection } template <typename EndcapStrategy, typename Range> - inline void add_endcap(EndcapStrategy const& strategy, Range const& range, point_type const& end_point) + inline void add_endcap(EndcapStrategy const& strategy, Range const& range, + point_type const& end_point) { + boost::ignore_unused(strategy); + if (range.empty()) { return; @@ -842,6 +1128,80 @@ struct buffered_piece_collection } } + inline bool point_coveredby_original(point_type const& point) + { + robust_point_type any_point; + geometry::recalculate(any_point, point, m_robust_policy); + + int count_in_original = 0; + + // Check of the robust point of this outputted ring is in + // any of the robust original rings + // This can go quadratic if the input has many rings, and there + // are many untouched deflated rings around + for (typename std::vector<robust_original>::const_iterator it + = robust_originals.begin(); + it != robust_originals.end(); + ++it) + { + robust_original const& original = *it; + if (detail::disjoint::disjoint_point_box(any_point, + original.m_box)) + { + continue; + } + + int const geometry_code + = detail::within::point_in_geometry(any_point, + original.m_ring); + + if (geometry_code == -1) + { + // Outside, continue + continue; + } + + // Apply for possibly nested interior rings + if (original.m_is_interior) + { + count_in_original--; + } + else if (original.m_has_interiors) + { + count_in_original++; + } + else + { + // Exterior ring without interior rings + return true; + } + } + return count_in_original > 0; + } + + // For a deflate, all rings around inner rings which are untouched + // (no intersections/turns) and which are OUTSIDE the original should + // be discarded + inline void discard_nonintersecting_deflated_rings() + { + for(typename buffered_ring_collection<buffered_ring<Ring> >::iterator it + = boost::begin(offsetted_rings); + it != boost::end(offsetted_rings); + ++it) + { + buffered_ring<Ring>& ring = *it; + if (! ring.has_intersections() + && boost::size(ring) > 0u + && geometry::area(ring) < 0) + { + if (! point_coveredby_original(geometry::range::front(ring))) + { + ring.is_untouched_outside_original = true; + } + } + } + } + inline void block_turns() { // To fix left-turn issues like #rt_u13 @@ -876,7 +1236,8 @@ struct buffered_piece_collection typedef detail::overlay::traverse < false, false, - buffered_ring_collection<buffered_ring<Ring> >, buffered_ring_collection<buffered_ring<Ring > >, + buffered_ring_collection<buffered_ring<Ring> >, + buffered_ring_collection<buffered_ring<Ring > >, backtrack_for_buffer > traverser; @@ -914,16 +1275,20 @@ struct buffered_piece_collection std::map<ring_identifier, properties> selected; - // Select all rings which do not have any self-intersection (other ones should be traversed) + // Select all rings which do not have any self-intersection + // Inner rings, for deflate, which do not have intersections, and + // which are outside originals, are skipped + // (other ones should be traversed) int index = 0; for(typename buffered_ring_collection<buffered_ring<Ring> >::const_iterator it = boost::begin(offsetted_rings); it != boost::end(offsetted_rings); ++it, ++index) { - if (! it->has_intersections()) + if (! it->has_intersections() + && ! it->is_untouched_outside_original) { ring_identifier id(0, index, -1); - selected[id] = properties(*it, true); + selected[id] = properties(*it); } } @@ -935,7 +1300,7 @@ struct buffered_piece_collection ++it, ++index) { ring_identifier id(2, index, -1); - selected[id] = properties(*it, true); + selected[id] = properties(*it); } detail::overlay::assign_parents(offsetted_rings, traversed_rings, selected, true); diff --git a/boost/geometry/algorithms/detail/buffer/buffered_ring.hpp b/boost/geometry/algorithms/detail/buffer/buffered_ring.hpp index 03ec598c90..9ea8bc1e85 100644 --- a/boost/geometry/algorithms/detail/buffer/buffered_ring.hpp +++ b/boost/geometry/algorithms/detail/buffer/buffered_ring.hpp @@ -43,12 +43,16 @@ struct buffered_ring_collection_tag : polygonal_tag, multi_tag template <typename Ring> struct buffered_ring : public Ring { + bool has_concave; bool has_accepted_intersections; bool has_discarded_intersections; + bool is_untouched_outside_original; inline buffered_ring() - : has_accepted_intersections(false) + : has_concave(false) + , has_accepted_intersections(false) , has_discarded_intersections(false) + , is_untouched_outside_original(false) {} inline bool discarded() const diff --git a/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp b/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp index 395921ccaa..6a3daa282e 100644 --- a/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp +++ b/boost/geometry/algorithms/detail/buffer/get_piece_turns.hpp @@ -16,6 +16,7 @@ #include <boost/geometry/algorithms/detail/disjoint/box_box.hpp> #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp> #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> +#include <boost/geometry/algorithms/detail/sections/section_functions.hpp> namespace boost { namespace geometry @@ -27,32 +28,16 @@ namespace detail { namespace buffer { -struct piece_get_box -{ - template <typename Box, typename Piece> - static inline void apply(Box& total, Piece const& piece) - { - geometry::expand(total, piece.robust_envelope); - } -}; - -struct piece_ovelaps_box -{ - template <typename Box, typename Piece> - static inline bool apply(Box const& box, Piece const& piece) - { - return ! geometry::detail::disjoint::disjoint_box_box(box, piece.robust_envelope); - } -}; - template < + typename Pieces, typename Rings, typename Turns, typename RobustPolicy > class piece_turn_visitor { + Pieces const& m_pieces; Rings const& m_rings; Turns& m_turns; RobustPolicy const& m_robust_policy; @@ -69,6 +54,17 @@ class piece_turn_visitor || piece1.index == piece2.right_index; } + template <typename Piece> + inline bool is_on_same_convex_ring(Piece const& piece1, Piece const& piece2) const + { + if (piece1.first_seg_id.multi_index != piece2.first_seg_id.multi_index) + { + return false; + } + + return ! m_rings[piece1.first_seg_id.multi_index].has_concave; + } + template <typename Range, typename Iterator> inline void move_to_next_point(Range const& range, Iterator& next) const { @@ -93,46 +89,110 @@ class piece_turn_visitor return result; } - template <typename Piece> - inline void calculate_turns(Piece const& piece1, Piece const& piece2) + template <std::size_t Dimension, typename Iterator, typename Box> + inline void move_begin_iterator(Iterator& it_begin, Iterator it_beyond, + int& index, int dir, Box const& other_bounding_box) + { + for(; it_begin != it_beyond + && it_begin + 1 != it_beyond + && detail::section::preceding<Dimension>(dir, *(it_begin + 1), + other_bounding_box, m_robust_policy); + ++it_begin, index++) + {} + } + + template <std::size_t Dimension, typename Iterator, typename Box> + inline void move_end_iterator(Iterator it_begin, Iterator& it_beyond, + int dir, Box const& other_bounding_box) + { + while (it_beyond != it_begin + && it_beyond - 1 != it_begin + && it_beyond - 2 != it_begin) + { + if (detail::section::exceeding<Dimension>(dir, *(it_beyond - 2), + other_bounding_box, m_robust_policy)) + { + --it_beyond; + } + else + { + return; + } + } + } + + template <typename Piece, typename Section> + inline void calculate_turns(Piece const& piece1, Piece const& piece2, + Section const& section1, Section const& section2) { typedef typename boost::range_value<Rings const>::type ring_type; typedef typename boost::range_value<Turns const>::type turn_type; typedef typename boost::range_iterator<ring_type const>::type iterator; - segment_identifier seg_id1 = piece1.first_seg_id; - segment_identifier seg_id2 = piece2.first_seg_id; - - if (seg_id1.segment_index < 0 || seg_id2.segment_index < 0) + int const piece1_first_index = piece1.first_seg_id.segment_index; + int const piece2_first_index = piece2.first_seg_id.segment_index; + if (piece1_first_index < 0 || piece2_first_index < 0) { return; } - ring_type const& ring1 = m_rings[seg_id1.multi_index]; - iterator it1_first = boost::begin(ring1) + seg_id1.segment_index; - iterator it1_last = boost::begin(ring1) + piece1.last_segment_index; - - ring_type const& ring2 = m_rings[seg_id2.multi_index]; - iterator it2_first = boost::begin(ring2) + seg_id2.segment_index; - iterator it2_last = boost::begin(ring2) + piece2.last_segment_index; + // Get indices of part of offsetted_rings for this monotonic section: + int const sec1_first_index = piece1_first_index + section1.begin_index; + int const sec2_first_index = piece2_first_index + section2.begin_index; + + // index of last point in section, beyond-end is one further + int const sec1_last_index = piece1_first_index + section1.end_index; + int const sec2_last_index = piece2_first_index + section2.end_index; + + // get geometry and iterators over these sections + ring_type const& ring1 = m_rings[piece1.first_seg_id.multi_index]; + iterator it1_first = boost::begin(ring1) + sec1_first_index; + iterator it1_beyond = boost::begin(ring1) + sec1_last_index + 1; + + ring_type const& ring2 = m_rings[piece2.first_seg_id.multi_index]; + iterator it2_first = boost::begin(ring2) + sec2_first_index; + iterator it2_beyond = boost::begin(ring2) + sec2_last_index + 1; + + // Set begin/end of monotonic ranges, in both x/y directions + int index1 = sec1_first_index; + move_begin_iterator<0>(it1_first, it1_beyond, index1, + section1.directions[0], section2.bounding_box); + move_end_iterator<0>(it1_first, it1_beyond, + section1.directions[0], section2.bounding_box); + move_begin_iterator<1>(it1_first, it1_beyond, index1, + section1.directions[1], section2.bounding_box); + move_end_iterator<1>(it1_first, it1_beyond, + section1.directions[1], section2.bounding_box); + + int index2 = sec2_first_index; + move_begin_iterator<0>(it2_first, it2_beyond, index2, + section2.directions[0], section1.bounding_box); + move_end_iterator<0>(it2_first, it2_beyond, + section2.directions[0], section1.bounding_box); + move_begin_iterator<1>(it2_first, it2_beyond, index2, + section2.directions[1], section1.bounding_box); + move_end_iterator<1>(it2_first, it2_beyond, + section2.directions[1], section1.bounding_box); turn_type the_model; the_model.operations[0].piece_index = piece1.index; the_model.operations[0].seg_id = piece1.first_seg_id; + the_model.operations[0].seg_id.segment_index = index1; // override iterator it1 = it1_first; for (iterator prev1 = it1++; - it1 != it1_last; + it1 != it1_beyond; prev1 = it1++, the_model.operations[0].seg_id.segment_index++) { the_model.operations[1].piece_index = piece2.index; the_model.operations[1].seg_id = piece2.first_seg_id; + the_model.operations[1].seg_id.segment_index = index2; // override iterator next1 = next_point(ring1, it1); iterator it2 = it2_first; for (iterator prev2 = it2++; - it2 != it2_last; + it2 != it2_beyond; prev2 = it2++, the_model.operations[1].seg_id.segment_index++) { iterator next2 = next_point(ring2, it2); @@ -158,26 +218,36 @@ class piece_turn_visitor public: - piece_turn_visitor(Rings const& ring_collection, + piece_turn_visitor(Pieces const& pieces, + Rings const& ring_collection, Turns& turns, RobustPolicy const& robust_policy) - : m_rings(ring_collection) + : m_pieces(pieces) + , m_rings(ring_collection) , m_turns(turns) , m_robust_policy(robust_policy) {} - template <typename Piece> - inline void apply(Piece const& piece1, Piece const& piece2, + template <typename Section> + inline void apply(Section const& section1, Section const& section2, bool first = true) { boost::ignore_unused_variable_warning(first); - if ( is_adjacent(piece1, piece2) - || detail::disjoint::disjoint_box_box(piece1.robust_envelope, - piece2.robust_envelope)) + + typedef typename boost::range_value<Pieces const>::type piece_type; + piece_type const& piece1 = m_pieces[section1.ring_id.source_index]; + piece_type const& piece2 = m_pieces[section2.ring_id.source_index]; + + if ( piece1.index == piece2.index + || is_adjacent(piece1, piece2) + || is_on_same_convex_ring(piece1, piece2) + || detail::disjoint::disjoint_box_box(section1.bounding_box, + section2.bounding_box) ) { return; } - calculate_turns(piece1, piece2); + + calculate_turns(piece1, piece2, section1, section2); } }; diff --git a/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp b/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp deleted file mode 100644 index 2b1c33d291..0000000000 --- a/boost/geometry/algorithms/detail/buffer/turn_in_input.hpp +++ /dev/null @@ -1,98 +0,0 @@ -// Boost.Geometry (aka GGL, Generic Geometry Library) - -// Copyright (c) 2012-2014 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_BUFFER_TURN_IN_INPUT_HPP -#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_INPUT_HPP - -#include <boost/geometry/core/tags.hpp> -#include <boost/geometry/algorithms/covered_by.hpp> - - -namespace boost { namespace geometry -{ - -#ifndef DOXYGEN_NO_DETAIL -namespace detail { namespace buffer -{ - -// Checks if an turn/intersection point is inside (or covered by) the input geometry - -template <typename Tag> -struct turn_in_input -{ -}; - -template <> -struct turn_in_input<polygon_tag> -{ - template <typename Point, typename Geometry> - static inline int apply(Point const& point, Geometry const& geometry) - { - return geometry::covered_by(point, geometry) ? 1 : -1; - } -}; - -template <> -struct turn_in_input<linestring_tag> -{ - template <typename Point, typename Geometry> - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - -template <> -struct turn_in_input<point_tag> -{ - template <typename Point, typename Geometry> - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - -template <> -struct turn_in_input<multi_polygon_tag> -{ - template <typename Point, typename Geometry> - static inline int apply(Point const& point, Geometry const& geometry) - { - return geometry::covered_by(point, geometry) ? 1 : -1; - } -}; - -template <> -struct turn_in_input<multi_linestring_tag> -{ - template <typename Point, typename Geometry> - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - -template <> -struct turn_in_input<multi_point_tag> -{ - template <typename Point, typename Geometry> - static inline int apply(Point const& , Geometry const& ) - { - return 0; - } -}; - - -}} // namespace detail::buffer -#endif // DOXYGEN_NO_DETAIL - - - -}} // namespace boost::geometry - -#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_INPUT_HPP diff --git a/boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp b/boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp new file mode 100644 index 0000000000..05fc6df8ff --- /dev/null +++ b/boost/geometry/algorithms/detail/buffer/turn_in_original_visitor.hpp @@ -0,0 +1,268 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014 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_BUFFER_TURN_IN_ORIGINAL_VISITOR +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_ORIGINAL_VISITOR + + +#include <boost/core/ignore_unused.hpp> + +#include <boost/geometry/algorithms/expand.hpp> +#include <boost/geometry/strategies/agnostic/point_in_poly_winding.hpp> +#include <boost/geometry/strategies/buffer.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace buffer +{ + +struct original_get_box +{ + template <typename Box, typename Original> + static inline void apply(Box& total, Original const& original) + { + geometry::expand(total, original.m_box); + } +}; + +struct original_ovelaps_box +{ + template <typename Box, typename Original> + static inline bool apply(Box const& box, Original const& original) + { + return ! detail::disjoint::disjoint_box_box(box, original.m_box); + } +}; + +struct include_turn_policy +{ + template <typename Turn> + static inline bool apply(Turn const& turn) + { + return turn.location == location_ok; + } +}; + +struct turn_in_original_ovelaps_box +{ + template <typename Box, typename Turn> + static inline bool apply(Box const& box, Turn const& turn) + { + if (turn.location != location_ok || turn.within_original) + { + // Skip all points already processed + return false; + } + + return ! geometry::detail::disjoint::disjoint_point_box( + turn.robust_point, box); + } +}; + +//! Check if specified is in range of specified iterators +//! Return value of strategy (true if we can bail out) +template +< + typename Strategy, + typename State, + typename Point, + typename Iterator +> +inline bool point_in_range(Strategy& strategy, State& state, + Point const& point, Iterator begin, Iterator end) +{ + boost::ignore_unused(strategy); + + Iterator it = begin; + for (Iterator previous = it++; it != end; ++previous, ++it) + { + if (! strategy.apply(point, *previous, *it, state)) + { + // We're probably on the boundary + return false; + } + } + return true; +} + +template +< + typename Strategy, + typename State, + typename Point, + typename CoordinateType, + typename Iterator +> +inline bool point_in_section(Strategy& strategy, State& state, + Point const& point, CoordinateType const& point_y, + Iterator begin, Iterator end, + int direction) +{ + if (direction == 0) + { + // Not a monotonic section, or no change in Y-direction + return point_in_range(strategy, state, point, begin, end); + } + + // We're in a monotonic section in y-direction + Iterator it = begin; + + for (Iterator previous = it++; it != end; ++previous, ++it) + { + // Depending on sections.direction we can quit for this section + CoordinateType const previous_y = geometry::get<1>(*previous); + + if (direction == 1 && point_y < previous_y) + { + // Section goes upwards, y increases, point is is below section + return true; + } + else if (direction == -1 && point_y > previous_y) + { + // Section goes downwards, y decreases, point is above section + return true; + } + + if (! strategy.apply(point, *previous, *it, state)) + { + // We're probably on the boundary + return false; + } + } + return true; +} + + +template <typename Point, typename Original> +inline int point_in_original(Point const& point, Original const& original) +{ + typedef strategy::within::winding<Point> strategy_type; + + typename strategy_type::state_type state; + strategy_type strategy; + + if (boost::size(original.m_sections) == 0 + || boost::size(original.m_ring) - boost::size(original.m_sections) < 16) + { + // There are no sections, or it does not profit to walk over sections + // instead of over points. Boundary of 16 is arbitrary but can influence + // performance + point_in_range(strategy, state, point, + original.m_ring.begin(), original.m_ring.end()); + return strategy.result(state); + } + + typedef typename Original::sections_type sections_type; + typedef typename boost::range_iterator<sections_type const>::type iterator_type; + typedef typename boost::range_value<sections_type const>::type section_type; + typedef typename geometry::coordinate_type<Point>::type coordinate_type; + + coordinate_type const point_y = geometry::get<1>(point); + + // Walk through all monotonic sections of this original + for (iterator_type it = boost::begin(original.m_sections); + it != boost::end(original.m_sections); + ++it) + { + section_type const& section = *it; + + if (! section.duplicate + && section.begin_index < section.end_index + && point_y >= geometry::get<min_corner, 1>(section.bounding_box) + && point_y <= geometry::get<max_corner, 1>(section.bounding_box)) + { + // y-coordinate of point overlaps with section + if (! point_in_section(strategy, state, point, point_y, + boost::begin(original.m_ring) + section.begin_index, + boost::begin(original.m_ring) + section.end_index + 1, + section.directions[0])) + { + // We're probably on the boundary + break; + } + } + } + + return strategy.result(state); +} + + +template <typename Turns> +class turn_in_original_visitor +{ +public: + turn_in_original_visitor(Turns& turns) + : m_mutable_turns(turns) + {} + + template <typename Turn, typename Original> + inline void apply(Turn const& turn, Original const& original, bool first = true) + { + boost::ignore_unused_variable_warning(first); + + if (turn.location != location_ok || turn.within_original) + { + // Skip all points already processed + return; + } + + if (geometry::disjoint(turn.robust_point, original.m_box)) + { + // Skip all disjoint + return; + } + + int const code = point_in_original(turn.robust_point, original); + + if (code == -1) + { + return; + } + + Turn& mutable_turn = m_mutable_turns[turn.turn_index]; + + if (code == 0) + { + // On border of original: always discard + mutable_turn.location = location_discard; + } + + // Point is inside an original ring + if (original.m_is_interior) + { + mutable_turn.count_in_original--; + } + else if (original.m_has_interiors) + { + mutable_turn.count_in_original++; + } + else + { + // It is an exterior ring and there are no interior rings. + // Then we are completely ready with this turn + mutable_turn.within_original = true; + mutable_turn.count_in_original = 1; + } + } + +private : + Turns& m_mutable_turns; +}; + + +}} // namespace detail::buffer +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_ORIGINAL_VISITOR diff --git a/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp b/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp index c02f56de3f..8803efdec9 100644 --- a/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp +++ b/boost/geometry/algorithms/detail/buffer/turn_in_piece_visitor.hpp @@ -9,19 +9,23 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_BUFFER_TURN_IN_PIECE_VISITOR + +#include <boost/core/ignore_unused.hpp> + #include <boost/range.hpp> #include <boost/geometry/arithmetic/dot_product.hpp> +#include <boost/geometry/algorithms/assign.hpp> +#include <boost/geometry/algorithms/comparable_distance.hpp> #include <boost/geometry/algorithms/equals.hpp> #include <boost/geometry/algorithms/expand.hpp> #include <boost/geometry/algorithms/detail/disjoint/point_box.hpp> +#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp> #include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp> #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> #include <boost/geometry/policies/compare.hpp> #include <boost/geometry/strategies/buffer.hpp> -#include <boost/geometry/geometries/linestring.hpp> -#include <boost/geometry/algorithms/comparable_distance.hpp> namespace boost { namespace geometry { @@ -31,6 +35,33 @@ namespace boost { namespace geometry namespace detail { namespace buffer { +struct piece_get_box +{ + template <typename Box, typename Piece> + static inline void apply(Box& total, Piece const& piece) + { + geometry::expand(total, piece.robust_envelope); + } +}; + +struct piece_ovelaps_box +{ + template <typename Box, typename Piece> + static inline bool apply(Box const& box, Piece const& piece) + { + if (piece.type == strategy::buffer::buffered_flat_end + || piece.type == strategy::buffer::buffered_concave) + { + // Turns cannot be inside a flat end (though they can be on border) + // Neither we need to check if they are inside concave helper pieces + + // Skip all pieces not used as soon as possible + return false; + } + + return ! geometry::detail::disjoint::disjoint_box_box(box, piece.robust_envelope); + } +}; struct turn_get_box { @@ -46,96 +77,422 @@ struct turn_ovelaps_box template <typename Box, typename Turn> static inline bool apply(Box const& box, Turn const& turn) { - return ! dispatch::disjoint - < - typename Turn::robust_point_type, - Box - >::apply(turn.robust_point, box); + return ! geometry::detail::disjoint::disjoint_point_box(turn.robust_point, box); } }; -template <typename Turns, typename Pieces> -class turn_in_piece_visitor + +enum analyse_result { - Turns& m_turns; // because partition is currently operating on const input only - Pieces const& m_pieces; // to check for piece-type + analyse_unknown, + analyse_continue, + analyse_disjoint, + analyse_within, + analyse_on_original_boundary, + analyse_on_offsetted, + analyse_near_offsetted +}; - typedef boost::long_long_type calculation_type; +template <typename Point> +inline bool in_box(Point const& previous, + Point const& current, Point const& point) +{ + // Get its box (TODO: this can be prepared-on-demand later) + typedef geometry::model::box<Point> box_type; + box_type box; + geometry::assign_inverse(box); + geometry::expand(box, previous); + geometry::expand(box, current); + + return geometry::covered_by(point, box); +} + +template <typename Point, typename Turn> +inline analyse_result check_segment(Point const& previous, + Point const& current, Turn const& turn, + bool from_monotonic) +{ + typedef typename strategy::side::services::default_strategy + < + typename cs_tag<Point>::type + >::type side_strategy; + typedef typename geometry::coordinate_type<Point>::type coordinate_type; + + coordinate_type const twice_area + = side_strategy::template side_value + < + coordinate_type, + coordinate_type + >(previous, current, turn.robust_point); - template <typename Point> - static inline bool projection_on_segment(Point const& subject, Point const& p, Point const& q) + if (twice_area == 0) { - typedef Point vector_type; - typedef typename geometry::coordinate_type<Point>::type coordinate_type; + // Collinear, only on segment if it is covered by its bbox + if (in_box(previous, current, turn.robust_point)) + { + return analyse_on_offsetted; + } + } + else if (twice_area < 0) + { + // It is in the triangle right-of the segment where the + // segment is the hypothenusa. Check if it is close + // (within rounding-area) + if (twice_area * twice_area < geometry::comparable_distance(previous, current) + && in_box(previous, current, turn.robust_point)) + { + return analyse_near_offsetted; + } + else if (from_monotonic) + { + return analyse_within; + } + } + else if (twice_area > 0 && from_monotonic) + { + // Left of segment + return analyse_disjoint; + } + + // Not monotonic, on left or right side: continue analysing + return analyse_continue; +} - vector_type v = q; - vector_type w = subject; - subtract_point(v, p); - subtract_point(w, p); - coordinate_type const zero = coordinate_type(); - coordinate_type const c1 = dot_product(w, v); +class analyse_turn_wrt_point_piece +{ +public : + template <typename Turn, typename Piece> + static inline analyse_result apply(Turn const& turn, Piece const& piece) + { + typedef typename Piece::section_type section_type; + typedef typename Turn::robust_point_type point_type; + typedef typename geometry::coordinate_type<point_type>::type coordinate_type; - if (c1 < zero) + coordinate_type const point_y = geometry::get<1>(turn.robust_point); + + typedef strategy::within::winding<point_type> strategy_type; + + typename strategy_type::state_type state; + strategy_type strategy; + boost::ignore_unused(strategy); + + for (std::size_t s = 0; s < piece.sections.size(); s++) { - return false; + section_type const& section = piece.sections[s]; + // If point within vertical range of monotonic section: + if (! section.duplicate + && section.begin_index < section.end_index + && point_y >= geometry::get<min_corner, 1>(section.bounding_box) - 1 + && point_y <= geometry::get<max_corner, 1>(section.bounding_box) + 1) + { + for (int i = section.begin_index + 1; i <= section.end_index; i++) + { + point_type const& previous = piece.robust_ring[i - 1]; + point_type const& current = piece.robust_ring[i]; + + analyse_result code = check_segment(previous, current, turn, false); + if (code != analyse_continue) + { + return code; + } + + // Get the state (to determine it is within), we don't have + // to cover the on-segment case (covered above) + strategy.apply(turn.robust_point, previous, current, state); + } + } } - coordinate_type const c2 = dot_product(v, v); - if (c2 < c1) + + int const code = strategy.result(state); + if (code == 1) { - return false; + return analyse_within; + } + else if (code == -1) + { + return analyse_disjoint; } - return true; + // Should normally not occur - on-segment is covered + return analyse_unknown; } - template <typename Point, typename Piece> - inline bool on_offsetted(Point const& point, Piece const& piece) const +}; + +class analyse_turn_wrt_piece +{ + template <typename Point, typename Turn> + static inline analyse_result check_helper_segment(Point const& s1, + Point const& s2, Turn const& turn, + bool is_original, + Point const& offsetted) { typedef typename strategy::side::services::default_strategy < typename cs_tag<Point>::type >::type side_strategy; - geometry::equal_to<Point> comparator; + + switch(side_strategy::apply(s1, s2, turn.robust_point)) + { + case 1 : + return analyse_disjoint; // left of segment + case 0 : + { + // If is collinear, either on segment or before/after + typedef geometry::model::box<Point> box_type; + + box_type box; + geometry::assign_inverse(box); + geometry::expand(box, s1); + geometry::expand(box, s2); + + if (geometry::covered_by(turn.robust_point, box)) + { + // It is on the segment + if (! is_original + && geometry::comparable_distance(turn.robust_point, offsetted) <= 1) + { + // It is close to the offsetted-boundary, take + // any rounding-issues into account + return analyse_near_offsetted; + } + + // Points on helper-segments are considered as within + // Points on original boundary are processed differently + return is_original + ? analyse_on_original_boundary + : analyse_within; + } + + // It is collinear but not on the segment. Because these + // segments are convex, it is outside + // Unless the offsetted ring is collinear or concave w.r.t. + // helper-segment but that scenario is not yet supported + return analyse_disjoint; + } + break; + } + + // right of segment + return analyse_continue; + } + + template <typename Turn, typename Piece> + static inline analyse_result check_helper_segments(Turn const& turn, Piece const& piece) + { + typedef typename Turn::robust_point_type point_type; + geometry::equal_to<point_type> comparator; + + point_type points[4]; + + int helper_count = piece.robust_ring.size() - piece.offsetted_count; + if (helper_count == 4) + { + for (int i = 0; i < 4; i++) + { + points[i] = piece.robust_ring[piece.offsetted_count + i]; + } + } + else if (helper_count == 3) + { + // Triangular piece, assign points but assign second twice + for (int i = 0; i < 4; i++) + { + int index = i < 2 ? i : i - 1; + points[i] = piece.robust_ring[piece.offsetted_count + index]; + } + } + else + { + // Some pieces (e.g. around points) do not have helper segments. + // Others should have 3 (join) or 4 (side) + return analyse_continue; + } + + // First check point-equality + point_type const& point = turn.robust_point; + if (comparator(point, points[0]) || comparator(point, points[3])) + { + return analyse_on_offsetted; + } + if (comparator(point, points[1]) || comparator(point, points[2])) + { + return analyse_on_original_boundary; + } + + // Right side of the piece + analyse_result result + = check_helper_segment(points[0], points[1], turn, + false, points[0]); + if (result != analyse_continue) + { + return result; + } + + // Left side of the piece + result = check_helper_segment(points[2], points[3], turn, + false, points[3]); + if (result != analyse_continue) + { + return result; + } + + if (! comparator(points[1], points[2])) + { + // Side of the piece at side of original geometry + result = check_helper_segment(points[1], points[2], turn, + true, point); + if (result != analyse_continue) + { + return result; + } + } + + // We are within the \/ or |_| shaped piece, where the top is the + // offsetted ring. + if (! geometry::covered_by(point, piece.robust_offsetted_envelope)) + { + // Not in offsetted-area. This makes a cheap check possible + typedef typename strategy::side::services::default_strategy + < + typename cs_tag<point_type>::type + >::type side_strategy; + + switch(side_strategy::apply(points[3], points[0], point)) + { + case 1 : return analyse_disjoint; + case -1 : return analyse_within; + case 0 : return analyse_disjoint; + } + } + + return analyse_continue; + } + + template <typename Turn, typename Piece, typename Compare> + static inline analyse_result check_monotonic(Turn const& turn, Piece const& piece, Compare const& compare) + { + typedef typename Piece::piece_robust_ring_type ring_type; + typedef typename ring_type::const_iterator it_type; + it_type end = piece.robust_ring.begin() + piece.offsetted_count; + it_type it = std::lower_bound(piece.robust_ring.begin(), + end, + turn.robust_point, + compare); + + if (it != end + && it != piece.robust_ring.begin()) + { + // iterator points to point larger than point + // w.r.t. specified direction, and prev points to a point smaller + // We now know if it is inside/outside + it_type prev = it - 1; + return check_segment(*prev, *it, turn, true); + } + return analyse_continue; + } + +public : + template <typename Turn, typename Piece> + static inline analyse_result apply(Turn const& turn, Piece const& piece) + { + typedef typename Turn::robust_point_type point_type; + analyse_result code = check_helper_segments(turn, piece); + if (code != analyse_continue) + { + return code; + } + + geometry::equal_to<point_type> comparator; + + if (piece.offsetted_count > 8) + { + // If the offset contains some points and is monotonic, we try + // to avoid walking all points linearly. + // We try it only once. + if (piece.is_monotonic_increasing[0]) + { + code = check_monotonic(turn, piece, geometry::less<point_type, 0>()); + if (code != analyse_continue) return code; + } + else if (piece.is_monotonic_increasing[1]) + { + code = check_monotonic(turn, piece, geometry::less<point_type, 1>()); + if (code != analyse_continue) return code; + } + else if (piece.is_monotonic_decreasing[0]) + { + code = check_monotonic(turn, piece, geometry::greater<point_type, 0>()); + if (code != analyse_continue) return code; + } + else if (piece.is_monotonic_decreasing[1]) + { + code = check_monotonic(turn, piece, geometry::greater<point_type, 1>()); + if (code != analyse_continue) return code; + } + } + + // It is small or not monotonic, walk linearly through offset + // TODO: this will be combined with winding strategy for (int i = 1; i < piece.offsetted_count; i++) { - Point const& previous = piece.robust_ring[i - 1]; - Point const& current = piece.robust_ring[i]; + point_type const& previous = piece.robust_ring[i - 1]; + point_type const& current = piece.robust_ring[i]; - // The robust ring contains duplicates, avoid applying side on them (will be 0) + // The robust ring can contain duplicates + // (on which any side or side-value would return 0) if (! comparator(previous, current)) { - int const side = side_strategy::apply(previous, current, point); - if (side == 0) + code = check_segment(previous, current, turn, false); + if (code != analyse_continue) { - // Collinear, check if projection falls on it - if (projection_on_segment(point, previous, current)) - { - return true; - } + return code; } } } - return false; + + return analyse_unknown; } - template <typename Point, typename Piece> - static inline - calculation_type comparable_distance_from_offsetted(Point const& point, - Piece const& piece) +}; + + +template <typename Turns, typename Pieces> +class turn_in_piece_visitor +{ + Turns& m_turns; // because partition is currently operating on const input only + Pieces const& m_pieces; // to check for piece-type + + template <typename Operation, typename Piece> + inline bool skip(Operation const& op, Piece const& piece) const { - // TODO: pass subrange to dispatch to avoid making copy - geometry::model::linestring<Point> ls; - std::copy(piece.robust_ring.begin(), - piece.robust_ring.begin() + piece.offsetted_count, - std::back_inserter(ls)); - typename default_comparable_distance_result<Point, Point>::type - const comp = geometry::comparable_distance(point, ls); - - return static_cast<calculation_type>(comp); + if (op.piece_index == piece.index) + { + return true; + } + Piece const& pc = m_pieces[op.piece_index]; + if (pc.left_index == piece.index || pc.right_index == piece.index) + { + if (pc.type == strategy::buffer::buffered_flat_end) + { + // If it is a flat end, don't compare against its neighbor: + // it will always be located on one of the helper segments + return true; + } + if (pc.type == strategy::buffer::buffered_concave) + { + // If it is concave, the same applies: the IP will be + // located on one of the helper segments + return true; + } + } + + return false; } + public: inline turn_in_piece_visitor(Turns& turns, Pieces const& pieces) @@ -157,81 +514,55 @@ public: if (piece.type == strategy::buffer::buffered_flat_end || piece.type == strategy::buffer::buffered_concave) { - // Turns cannot be inside a flat end (though they can be on border) - // Neither we need to check if they are inside concave helper pieces + // Turns cannot be located within flat-end or concave pieces return; } - bool neighbour = false; - for (int i = 0; i < 2; i++) - { - // Don't compare against one of the two source-pieces - if (turn.operations[i].piece_index == piece.index) - { - return; - } - - typename boost::range_value<Pieces>::type const& pc - = m_pieces[turn.operations[i].piece_index]; - if (pc.left_index == piece.index - || pc.right_index == piece.index) - { - if (pc.type == strategy::buffer::buffered_flat_end) - { - // If it is a flat end, don't compare against its neighbor: - // it will always be located on one of the helper segments - return; - } - neighbour = true; - } - } - - int geometry_code = detail::within::point_in_geometry(turn.robust_point, piece.robust_ring); - - if (geometry_code == -1) + if (! geometry::covered_by(turn.robust_point, piece.robust_envelope)) { + // Easy check: if the turn is not in the envelope, we can safely return return; } - if (geometry_code == 0 && neighbour) + + if (skip(turn.operations[0], piece) || skip(turn.operations[1], piece)) { return; } + // TODO: mutable_piece to make some on-demand preparations in analyse + analyse_result analyse_code = + piece.type == geometry::strategy::buffer::buffered_point + ? analyse_turn_wrt_point_piece::apply(turn, piece) + : analyse_turn_wrt_piece::apply(turn, piece); + Turn& mutable_turn = m_turns[turn.turn_index]; - if (geometry_code == 0) + switch(analyse_code) { - // If it is on the border and they are not neighbours, it should be - // on the offsetted ring - - if (! on_offsetted(turn.robust_point, piece)) - { - // It is on the border but not on the offsetted ring. - // Then it is somewhere on the helper-segments - // Classify it as "within" - geometry_code = 1; - mutable_turn.count_on_helper++; // can still become "near_offsetted" - } - else - { + case analyse_disjoint : + return; + case analyse_on_offsetted : mutable_turn.count_on_offsetted++; // value is not used anymore - } + return; + case analyse_on_original_boundary : + mutable_turn.count_on_original_boundary++; + return; + case analyse_within : + mutable_turn.count_within++; + return; + case analyse_near_offsetted : + mutable_turn.count_within_near_offsetted++; + return; + default : + break; } + // TODO: this point_in_geometry is a performance-bottleneck here and + // will be replaced completely by extending analyse_piece functionality + int geometry_code = detail::within::point_in_geometry(turn.robust_point, piece.robust_ring); + if (geometry_code == 1) { - calculation_type const distance - = comparable_distance_from_offsetted(turn.robust_point, piece); - if (distance >= 4) - { - // This is too far from the border, it counts as "really within" - mutable_turn.count_within++; - } - else - { - // Other points count as still "on border" because they might be - // travelled through, but not used as starting point - mutable_turn.count_within_near_offsetted++; - } + mutable_turn.count_within++; } } }; diff --git a/boost/geometry/algorithms/detail/closest_feature/geometry_to_range.hpp b/boost/geometry/algorithms/detail/closest_feature/geometry_to_range.hpp index c7558b4ff1..04fa9ee86b 100644 --- a/boost/geometry/algorithms/detail/closest_feature/geometry_to_range.hpp +++ b/boost/geometry/algorithms/detail/closest_feature/geometry_to_range.hpp @@ -63,8 +63,7 @@ private: >::apply(geometry, *it_min, strategy); // check if other elements in the range are closer - RangeIterator it = first; - for (++it; it != last; ++it) + for (RangeIterator it = ++first; it != last; ++it) { Distance dist = dispatch::distance < diff --git a/boost/geometry/algorithms/detail/closest_feature/range_to_range.hpp b/boost/geometry/algorithms/detail/closest_feature/range_to_range.hpp index 90248767ec..ceba59b41a 100644 --- a/boost/geometry/algorithms/detail/closest_feature/range_to_range.hpp +++ b/boost/geometry/algorithms/detail/closest_feature/range_to_range.hpp @@ -60,6 +60,7 @@ private: BOOST_ASSERT( queries_first != queries_last ); Distance const zero = Distance(0); + dist_min = zero; // create -- packing algorithm rtree_type rt(rtree_first, rtree_last); diff --git a/boost/geometry/algorithms/detail/comparable_distance/interface.hpp b/boost/geometry/algorithms/detail/comparable_distance/interface.hpp index 1a57c8f4b3..c443a54e58 100644 --- a/boost/geometry/algorithms/detail/comparable_distance/interface.hpp +++ b/boost/geometry/algorithms/detail/comparable_distance/interface.hpp @@ -232,13 +232,13 @@ struct comparable_distance template < - BOOST_VARIANT_ENUM_PARAMS(typename A), - BOOST_VARIANT_ENUM_PARAMS(typename B) + BOOST_VARIANT_ENUM_PARAMS(typename T1), + BOOST_VARIANT_ENUM_PARAMS(typename T2) > struct comparable_distance < - boost::variant<BOOST_VARIANT_ENUM_PARAMS(A)>, - boost::variant<BOOST_VARIANT_ENUM_PARAMS(B)> + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> > { template <typename Strategy> @@ -246,8 +246,8 @@ struct comparable_distance < typename comparable_distance_result < - boost::variant<BOOST_VARIANT_ENUM_PARAMS(A)>, - boost::variant<BOOST_VARIANT_ENUM_PARAMS(B)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)>, Strategy >::type > @@ -279,12 +279,12 @@ struct comparable_distance template <typename Strategy> static inline typename comparable_distance_result < - boost::variant<BOOST_VARIANT_ENUM_PARAMS(A)>, - boost::variant<BOOST_VARIANT_ENUM_PARAMS(B)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)>, Strategy >::type - apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(A)> const& geometry1, - boost::variant<BOOST_VARIANT_ENUM_PARAMS(B)> const& geometry2, + apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)> const& geometry1, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> const& geometry2, Strategy const& strategy) { return apply_visitor(visitor<Strategy>(strategy), geometry1, geometry2); diff --git a/boost/geometry/algorithms/detail/course.hpp b/boost/geometry/algorithms/detail/course.hpp index e1a74c0fee..37424941bd 100644 --- a/boost/geometry/algorithms/detail/course.hpp +++ b/boost/geometry/algorithms/detail/course.hpp @@ -14,10 +14,7 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_COURSE_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_COURSE_HPP -#include <boost/geometry/core/cs.hpp> -#include <boost/geometry/core/access.hpp> -#include <boost/geometry/core/radian_access.hpp> -#include <boost/geometry/util/math.hpp> +#include <boost/geometry/algorithms/detail/azimuth.hpp> namespace boost { namespace geometry { @@ -27,25 +24,12 @@ namespace detail { /// Calculate course (bearing) between two points. +/// +/// NOTE: left for convenience and temporary backward compatibility template <typename ReturnType, typename Point1, typename Point2> inline ReturnType course(Point1 const& p1, Point2 const& p2) { - // http://williams.best.vwh.net/avform.htm#Crs - ReturnType dlon = get_as_radian<0>(p2) - get_as_radian<0>(p1); - ReturnType cos_p2lat = cos(get_as_radian<1>(p2)); - - // An optimization which should kick in often for Boxes - //if ( math::equals(dlon, ReturnType(0)) ) - //if ( get<0>(p1) == get<0>(p2) ) - //{ - // return - sin(get_as_radian<1>(p1)) * cos_p2lat); - //} - - // "An alternative formula, not requiring the pre-computation of d" - // In the formula below dlon is used as "d" - return atan2(sin(dlon) * cos_p2lat, - cos(get_as_radian<1>(p1)) * sin(get_as_radian<1>(p2)) - - sin(get_as_radian<1>(p1)) * cos_p2lat * cos(dlon)); + return azimuth<ReturnType>(p1, p2); } } // namespace detail diff --git a/boost/geometry/algorithms/detail/disjoint/box_box.hpp b/boost/geometry/algorithms/detail/disjoint/box_box.hpp index ccff9799fd..84671f257e 100644 --- a/boost/geometry/algorithms/detail/disjoint/box_box.hpp +++ b/boost/geometry/algorithms/detail/disjoint/box_box.hpp @@ -74,7 +74,7 @@ struct box_box<Box1, Box2, DimensionCount, DimensionCount> /*! - \brief Internal utility function to detect of boxes are disjoint + \brief Internal utility function to detect if boxes are disjoint \note Is used from other algorithms, declared separately to avoid circular references */ diff --git a/boost/geometry/algorithms/detail/disjoint/implementation.hpp b/boost/geometry/algorithms/detail/disjoint/implementation.hpp index 0c8079b8e4..d482c4a5a9 100644 --- a/boost/geometry/algorithms/detail/disjoint/implementation.hpp +++ b/boost/geometry/algorithms/detail/disjoint/implementation.hpp @@ -26,6 +26,7 @@ #include <boost/geometry/algorithms/detail/disjoint/linear_areal.hpp> #include <boost/geometry/algorithms/detail/disjoint/linear_linear.hpp> #include <boost/geometry/algorithms/detail/disjoint/point_geometry.hpp> +#include <boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp> #include <boost/geometry/algorithms/detail/disjoint/point_point.hpp> #include <boost/geometry/algorithms/detail/disjoint/point_box.hpp> #include <boost/geometry/algorithms/detail/disjoint/box_box.hpp> diff --git a/boost/geometry/algorithms/detail/disjoint/interface.hpp b/boost/geometry/algorithms/detail/disjoint/interface.hpp index ec9057ba0d..96d6881296 100644 --- a/boost/geometry/algorithms/detail/disjoint/interface.hpp +++ b/boost/geometry/algorithms/detail/disjoint/interface.hpp @@ -23,8 +23,8 @@ #include <cstddef> -#include <boost/variant/static_visitor.hpp> #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/geometries/concepts/check.hpp> diff --git a/boost/geometry/algorithms/detail/disjoint/linear_areal.hpp b/boost/geometry/algorithms/detail/disjoint/linear_areal.hpp index eefd351b8d..6a48b684a1 100644 --- a/boost/geometry/algorithms/detail/disjoint/linear_areal.hpp +++ b/boost/geometry/algorithms/detail/disjoint/linear_areal.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2014 Mateusz Loskot, London, UK. // Copyright (c) 2013-2014 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2013-2014. -// Modifications copyright (c) 2013-2014, Oracle and/or its affiliates. +// This file was modified by Oracle on 2013-2015. +// Modifications copyright (c) 2013-2015, Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -31,16 +31,20 @@ #include <boost/geometry/core/exterior_ring.hpp> #include <boost/geometry/core/interior_rings.hpp> #include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tag_cast.hpp> #include <boost/geometry/core/tags.hpp> #include <boost/geometry/algorithms/covered_by.hpp> #include <boost/geometry/algorithms/not_implemented.hpp> -#include <boost/geometry/algorithms/detail/point_on_border.hpp> #include <boost/geometry/algorithms/detail/assign_indexed_point.hpp> +#include <boost/geometry/algorithms/detail/check_iterator_range.hpp> +#include <boost/geometry/algorithms/detail/point_on_border.hpp> + +#include <boost/geometry/algorithms/detail/disjoint/multirange_geometry.hpp> +#include <boost/geometry/algorithms/detail/disjoint/linear_segment_or_box.hpp> #include <boost/geometry/algorithms/detail/disjoint/point_box.hpp> #include <boost/geometry/algorithms/detail/disjoint/segment_box.hpp> -#include <boost/geometry/algorithms/detail/disjoint/linear_segment_or_box.hpp> #include <boost/geometry/algorithms/dispatch/disjoint.hpp> @@ -48,25 +52,59 @@ namespace boost { namespace geometry { - #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace disjoint { +template <typename Geometry1, typename Geometry2, + typename Tag1 = typename tag<Geometry1>::type, + typename Tag1OrMulti = typename tag_cast<Tag1, multi_tag>::type> +struct disjoint_no_intersections_policy +{ + static inline bool apply(Geometry1 const& g1, Geometry2 const& g2) + { + typedef typename point_type<Geometry1>::type point1_type; + point1_type p; + geometry::point_on_border(p, g1); + return !geometry::covered_by(p, g2); + } +}; -template<typename Geometry1, typename Geometry2> +template <typename Geometry1, typename Geometry2, typename Tag1> +struct disjoint_no_intersections_policy<Geometry1, Geometry2, Tag1, multi_tag> +{ + static inline bool apply(Geometry1 const& g1, Geometry2 const& g2) + { + // TODO: use partition or rtree on g2 + typedef typename boost::range_iterator<Geometry1 const>::type iterator; + for ( iterator it = boost::begin(g1) ; it != boost::end(g1) ; ++it ) + { + typedef typename boost::range_value<Geometry1 const>::type value_type; + if ( ! disjoint_no_intersections_policy<value_type const, Geometry2> + ::apply(*it, g2) ) + { + return false; + } + } + return true; + } +}; + + +template<typename Geometry1, typename Geometry2, + typename NoIntersectionsPolicy + = disjoint_no_intersections_policy<Geometry1, Geometry2> > struct disjoint_linear_areal { static inline bool apply(Geometry1 const& g1, Geometry2 const& g2) { // if there are intersections - return false if ( !disjoint_linear<Geometry1, Geometry2>::apply(g1, g2) ) + { return false; + } - typedef typename point_type<Geometry1>::type point1_type; - point1_type p; - geometry::point_on_border(p, g1); - return !geometry::covered_by(p, g2); + return NoIntersectionsPolicy::apply(g1, g2); } }; @@ -88,38 +126,28 @@ template <typename Segment, typename Polygon> class disjoint_segment_areal<Segment, Polygon, polygon_tag> { private: - template <typename RingIterator> - static inline bool check_interior_rings(RingIterator first, - RingIterator beyond, - Segment const& segment) - { - for (RingIterator it = first; it != beyond; ++it) - { - if ( !disjoint_range_segment_or_box - < - typename std::iterator_traits - < - RingIterator - >::value_type, - closure<Polygon>::value, - Segment - >::apply(*it, segment) ) - { - return false; - } - } - return true; - } - - template <typename InteriorRings> static inline bool check_interior_rings(InteriorRings const& interior_rings, Segment const& segment) { - return check_interior_rings(boost::begin(interior_rings), - boost::end(interior_rings), - segment); + typedef typename boost::range_value<InteriorRings>::type ring_type; + + typedef unary_disjoint_geometry_to_query_geometry + < + Segment, + disjoint_range_segment_or_box + < + ring_type, closure<ring_type>::value, Segment + > + > unary_predicate_type; + + return check_iterator_range + < + unary_predicate_type + >::apply(boost::begin(interior_rings), + boost::end(interior_rings), + unary_predicate_type(segment)); } @@ -155,7 +183,7 @@ struct disjoint_segment_areal<Segment, MultiPolygon, multi_polygon_tag> static inline bool apply(Segment const& segment, MultiPolygon const& multipolygon) { - return disjoint_multirange_segment_or_box + return multirange_constant_size_geometry < MultiPolygon, Segment >::apply(multipolygon, segment); diff --git a/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp b/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp index ad84d7191d..91f985edb8 100644 --- a/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp +++ b/boost/geometry/algorithms/detail/disjoint/linear_linear.hpp @@ -98,11 +98,10 @@ struct assign_disjoint_policy typename Info, typename Point1, typename Point2, - typename IntersectionInfo, - typename DirInfo + typename IntersectionInfo > static inline void apply(Info& , Point1 const& , Point2 const&, - IntersectionInfo const&, DirInfo const&) + IntersectionInfo const&) {} }; @@ -115,28 +114,42 @@ struct disjoint_linear { typedef typename geometry::point_type<Geometry1>::type point_type; typedef detail::no_rescale_policy rescale_policy_type; + typedef typename geometry::segment_ratio_type + < + point_type, rescale_policy_type + >::type segment_ratio_type; typedef overlay::turn_info - < - point_type, - typename segment_ratio_type<point_type, rescale_policy_type>::type - > turn_info; - std::deque<turn_info> turns; + < + point_type, + segment_ratio_type, + typename detail::get_turns::turn_operation_type + < + Geometry1, Geometry2, segment_ratio_type + >::type + > turn_info_type; - static const bool reverse1 = overlay::do_reverse<geometry::point_order<Geometry1>::value>::value; // should be false - static const bool reverse2 = overlay::do_reverse<geometry::point_order<Geometry2>::value>::value; // should be false + std::deque<turn_info_type> turns; // Specify two policies: // 1) Stop at any intersection // 2) In assignment, include also degenerate points (which are normally skipped) - disjoint_interrupt_policy policy; - rescale_policy_type robust_policy; - geometry::get_turns + disjoint_interrupt_policy interrupt_policy; + dispatch::get_turns < - reverse1, reverse2, - assign_disjoint_policy - >(geometry1, geometry2, robust_policy, turns, policy); + typename geometry::tag<Geometry1>::type, + typename geometry::tag<Geometry2>::type, + Geometry1, + Geometry2, + overlay::do_reverse<geometry::point_order<Geometry1>::value>::value, // should be false + overlay::do_reverse<geometry::point_order<Geometry2>::value>::value, // should be false + detail::get_turns::get_turn_info_type + < + Geometry1, Geometry2, assign_disjoint_policy + > + >::apply(0, geometry1, 1, geometry2, + rescale_policy_type(), turns, interrupt_policy); - return !policy.has_intersections; + return !interrupt_policy.has_intersections; } }; diff --git a/boost/geometry/algorithms/detail/disjoint/linear_segment_or_box.hpp b/boost/geometry/algorithms/detail/disjoint/linear_segment_or_box.hpp index d181726e2e..8d82f7c911 100644 --- a/boost/geometry/algorithms/detail/disjoint/linear_segment_or_box.hpp +++ b/boost/geometry/algorithms/detail/disjoint/linear_segment_or_box.hpp @@ -32,6 +32,7 @@ #include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/algorithms/detail/disjoint/multirange_geometry.hpp> #include <boost/geometry/algorithms/dispatch/disjoint.hpp> @@ -44,34 +45,6 @@ namespace detail { namespace disjoint { -template <typename MultiRange, typename SegmentOrBox> -struct disjoint_multirange_segment_or_box -{ - static inline - bool apply(MultiRange const& multirange, SegmentOrBox const& segment_or_box) - { - typedef typename boost::range_iterator - < - MultiRange const - >::type const_iterator; - - for (const_iterator it = boost::begin(multirange); - it != boost::end(multirange); ++it) - { - if ( !dispatch::disjoint - < - typename boost::range_value<MultiRange>::type, - SegmentOrBox - >::apply(*it, segment_or_box) ) - { - return false; - } - } - return true; - } -}; - - template < typename Range, @@ -160,7 +133,7 @@ template <typename MultiLinestring, typename SegmentOrBox> struct disjoint_linear_segment_or_box < MultiLinestring, SegmentOrBox, multi_linestring_tag - > : disjoint_multirange_segment_or_box<MultiLinestring, SegmentOrBox> + > : multirange_constant_size_geometry<MultiLinestring, SegmentOrBox> {}; diff --git a/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp b/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp new file mode 100644 index 0000000000..7d1bb05242 --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/multipoint_geometry.hpp @@ -0,0 +1,162 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_MULTIPOINT_GEOMETRY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_MULTIPOINT_GEOMETRY_HPP + +#include <algorithm> +#include <vector> + +#include <boost/assert.hpp> +#include <boost/range.hpp> + +#include <boost/geometry/algorithms/detail/check_iterator_range.hpp> +#include <boost/geometry/algorithms/detail/disjoint/multirange_geometry.hpp> +#include <boost/geometry/algorithms/detail/disjoint/point_point.hpp> +#include <boost/geometry/algorithms/detail/disjoint/point_geometry.hpp> +#include <boost/geometry/algorithms/detail/relate/less.hpp> + +#include <boost/geometry/algorithms/dispatch/disjoint.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + +template<typename MultiPoint1, typename MultiPoint2> +class multipoint_multipoint +{ +private: + template <typename Iterator> + class unary_disjoint_predicate + : detail::relate::less + { + private: + typedef detail::relate::less base_type; + + public: + unary_disjoint_predicate(Iterator first, Iterator last) + : base_type(), m_first(first), m_last(last) + {} + + template <typename Point> + inline bool apply(Point const& point) const + { + return !std::binary_search(m_first, + m_last, + point, + static_cast<base_type const&>(*this)); + } + + private: + Iterator m_first, m_last; + }; + +public: + static inline bool apply(MultiPoint1 const& multipoint1, + MultiPoint2 const& multipoint2) + { + BOOST_ASSERT( boost::size(multipoint1) <= boost::size(multipoint2) ); + + typedef typename boost::range_value<MultiPoint1>::type point1_type; + + std::vector<point1_type> points1(boost::begin(multipoint1), + boost::end(multipoint1)); + + std::sort(points1.begin(), points1.end(), detail::relate::less()); + + typedef unary_disjoint_predicate + < + typename std::vector<point1_type>::const_iterator + > predicate_type; + + return check_iterator_range + < + predicate_type + >::apply(boost::begin(multipoint2), + boost::end(multipoint2), + predicate_type(points1.begin(), points1.end())); + } +}; + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + + + +#ifndef DOXYGEN_NO_DISPATCH +namespace dispatch +{ + + +template<typename Point, typename MultiPoint, std::size_t DimensionCount> +struct disjoint + < + Point, MultiPoint, DimensionCount, point_tag, multi_point_tag, false + > : detail::disjoint::multirange_constant_size_geometry<MultiPoint, Point> +{}; + + +template<typename MultiPoint, typename Segment, std::size_t DimensionCount> +struct disjoint + < + MultiPoint, Segment, DimensionCount, multi_point_tag, segment_tag, false + > : detail::disjoint::multirange_constant_size_geometry<MultiPoint, Segment> +{}; + + +template<typename MultiPoint, typename Box, std::size_t DimensionCount> +struct disjoint + < + MultiPoint, Box, DimensionCount, multi_point_tag, box_tag, false + > : detail::disjoint::multirange_constant_size_geometry<MultiPoint, Box> +{}; + + +template<typename MultiPoint1, typename MultiPoint2, std::size_t DimensionCount> +struct disjoint + < + MultiPoint1, MultiPoint2, DimensionCount, + multi_point_tag, multi_point_tag, false + > +{ + static inline bool apply(MultiPoint1 const& multipoint1, + MultiPoint2 const& multipoint2) + { + if ( boost::size(multipoint2) < boost::size(multipoint1) ) + { + return detail::disjoint::multipoint_multipoint + < + MultiPoint2, MultiPoint1 + >::apply(multipoint2, multipoint1); + } + + return detail::disjoint::multipoint_multipoint + < + MultiPoint1, MultiPoint2 + >::apply(multipoint1, multipoint2); + } +}; + + +} // namespace dispatch +#endif // DOXYGEN_NO_DISPATCH + + +}} // namespace boost::geometry + + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_MULTIPOINT_GEOMETRY_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/multirange_geometry.hpp b/boost/geometry/algorithms/detail/disjoint/multirange_geometry.hpp new file mode 100644 index 0000000000..78a683e46e --- /dev/null +++ b/boost/geometry/algorithms/detail/disjoint/multirange_geometry.hpp @@ -0,0 +1,85 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2014, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_MULTIRANGE_GEOMETRY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_MULTIRANGE_GEOMETRY_HPP + +#include <boost/range.hpp> + +#include <boost/geometry/algorithms/detail/check_iterator_range.hpp> +#include <boost/geometry/algorithms/dispatch/disjoint.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace disjoint +{ + + +template <typename Geometry, typename BinaryPredicate> +class unary_disjoint_geometry_to_query_geometry +{ +public: + unary_disjoint_geometry_to_query_geometry(Geometry const& geometry) + : m_geometry(geometry) + {} + + template <typename QueryGeometry> + inline bool apply(QueryGeometry const& query_geometry) const + { + return BinaryPredicate::apply(query_geometry, m_geometry); + } + +private: + Geometry const& m_geometry; +}; + + +template<typename MultiRange, typename ConstantSizeGeometry> +struct multirange_constant_size_geometry +{ + static inline bool apply(MultiRange const& multirange, + ConstantSizeGeometry const& constant_size_geometry) + { + typedef unary_disjoint_geometry_to_query_geometry + < + ConstantSizeGeometry, + dispatch::disjoint + < + typename boost::range_value<MultiRange>::type, + ConstantSizeGeometry + > + > unary_predicate_type; + + return detail::check_iterator_range + < + unary_predicate_type + >::apply(boost::begin(multirange), boost::end(multirange), + unary_predicate_type(constant_size_geometry)); + } + + static inline bool apply(ConstantSizeGeometry const& constant_size_geometry, + MultiRange const& multirange) + { + return apply(multirange, constant_size_geometry); + } +}; + + +}} // namespace detail::disjoint +#endif // DOXYGEN_NO_DETAIL + + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISJOINT_MULTIRANGE_GEOMETRY_HPP diff --git a/boost/geometry/algorithms/detail/disjoint/point_box.hpp b/boost/geometry/algorithms/detail/disjoint/point_box.hpp index ea6609a153..12213db056 100644 --- a/boost/geometry/algorithms/detail/disjoint/point_box.hpp +++ b/boost/geometry/algorithms/detail/disjoint/point_box.hpp @@ -70,8 +70,21 @@ struct point_box<Point, Box, DimensionCount, DimensionCount> } }; +/*! + \brief Internal utility function to detect if point/box are disjoint + */ +template <typename Point, typename Box> +inline bool disjoint_point_box(Point const& point, Box const& box) +{ + return detail::disjoint::point_box + < + Point, Box, + 0, dimension<Point>::type::value + >::apply(point, box); +} + -}} // namespace detail::equals +}} // namespace detail::disjoint #endif // DOXYGEN_NO_DETAIL diff --git a/boost/geometry/algorithms/detail/distance/geometry_to_segment_or_box.hpp b/boost/geometry/algorithms/detail/distance/geometry_to_segment_or_box.hpp index 04d1095cba..57257dbdcc 100644 --- a/boost/geometry/algorithms/detail/distance/geometry_to_segment_or_box.hpp +++ b/boost/geometry/algorithms/detail/distance/geometry_to_segment_or_box.hpp @@ -35,6 +35,8 @@ #include <boost/geometry/algorithms/detail/distance/is_comparable.hpp> +#include <boost/geometry/util/condition.hpp> + namespace boost { namespace geometry { @@ -215,7 +217,7 @@ public: // consider all distances of the points in the geometry to the // segment or box - comparable_return_type cd_min1; + comparable_return_type cd_min1(0); point_iterator_type pit_min; seg_or_box_iterator_type it_min1 = seg_or_box_points.begin(); seg_or_box_iterator_type it_min2 = ++seg_or_box_points.begin(); @@ -246,7 +248,7 @@ public: // consider all distances of the points in the segment or box to the // segments of the geometry - comparable_return_type cd_min2; + comparable_return_type cd_min2(0); segment_iterator_type sit_min; typename std::vector<segment_or_box_point>::const_iterator it_min; @@ -271,7 +273,7 @@ public: } } - if (is_comparable<Strategy>::value) + if (BOOST_GEOMETRY_CONDITION(is_comparable<Strategy>::value)) { return (std::min)(cd_min1, cd_min2); } diff --git a/boost/geometry/algorithms/detail/distance/interface.hpp b/boost/geometry/algorithms/detail/distance/interface.hpp index 9b377f524b..fa8cbd69ee 100644 --- a/boost/geometry/algorithms/detail/distance/interface.hpp +++ b/boost/geometry/algorithms/detail/distance/interface.hpp @@ -260,13 +260,13 @@ struct distance<Geometry1, variant<BOOST_VARIANT_ENUM_PARAMS(T)> > template < - BOOST_VARIANT_ENUM_PARAMS(typename A), - BOOST_VARIANT_ENUM_PARAMS(typename B) + BOOST_VARIANT_ENUM_PARAMS(typename T1), + BOOST_VARIANT_ENUM_PARAMS(typename T2) > struct distance < - boost::variant<BOOST_VARIANT_ENUM_PARAMS(A)>, - boost::variant<BOOST_VARIANT_ENUM_PARAMS(B)> + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> > { template <typename Strategy> @@ -274,8 +274,8 @@ struct distance < typename distance_result < - boost::variant<BOOST_VARIANT_ENUM_PARAMS(A)>, - boost::variant<BOOST_VARIANT_ENUM_PARAMS(B)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)>, Strategy >::type > @@ -304,12 +304,12 @@ struct distance template <typename Strategy> static inline typename distance_result < - boost::variant<BOOST_VARIANT_ENUM_PARAMS(A)>, - boost::variant<BOOST_VARIANT_ENUM_PARAMS(B)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)>, Strategy >::type - apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(A)> const& geometry1, - boost::variant<BOOST_VARIANT_ENUM_PARAMS(B)> const& geometry2, + apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T1)> const& geometry1, + boost::variant<BOOST_VARIANT_ENUM_PARAMS(T2)> const& geometry2, Strategy const& strategy) { return apply_visitor(visitor<Strategy>(strategy), geometry1, geometry2); diff --git a/boost/geometry/algorithms/detail/distance/multipoint_to_geometry.hpp b/boost/geometry/algorithms/detail/distance/multipoint_to_geometry.hpp index 5f2c6e34fb..ccd3aa462e 100644 --- a/boost/geometry/algorithms/detail/distance/multipoint_to_geometry.hpp +++ b/boost/geometry/algorithms/detail/distance/multipoint_to_geometry.hpp @@ -18,7 +18,7 @@ #include <boost/geometry/strategies/distance.hpp> #include <boost/geometry/strategies/tags.hpp> -#include <boost/geometry/algorithms/within.hpp> +#include <boost/geometry/algorithms/covered_by.hpp> #include <boost/geometry/algorithms/dispatch/distance.hpp> @@ -113,16 +113,16 @@ template <typename MultiPoint, typename Areal, typename Strategy> class multipoint_to_areal { private: - struct within_areal + struct not_covered_by_areal { - within_areal(Areal const& areal) + not_covered_by_areal(Areal const& areal) : m_areal(areal) {} template <typename Point> inline bool apply(Point const& point) const { - return geometry::within(point, m_areal); + return !geometry::covered_by(point, m_areal); } Areal const& m_areal; @@ -140,27 +140,26 @@ public: Areal const& areal, Strategy const& strategy) { - within_areal predicate(areal); + not_covered_by_areal predicate(areal); if (check_iterator_range < - within_areal, false + not_covered_by_areal, false >::apply(boost::begin(multipoint), boost::end(multipoint), predicate)) { - return 0; + return detail::distance::point_or_segment_range_to_geometry_rtree + < + typename boost::range_iterator<MultiPoint const>::type, + Areal, + Strategy + >::apply(boost::begin(multipoint), + boost::end(multipoint), + areal, + strategy); } - - return detail::distance::point_or_segment_range_to_geometry_rtree - < - typename boost::range_iterator<MultiPoint const>::type, - Areal, - Strategy - >::apply(boost::begin(multipoint), - boost::end(multipoint), - areal, - strategy); + return 0; } static inline return_type apply(Areal const& areal, diff --git a/boost/geometry/algorithms/detail/distance/segment_to_box.hpp b/boost/geometry/algorithms/detail/distance/segment_to_box.hpp index f64a3e9fca..79d9adb703 100644 --- a/boost/geometry/algorithms/detail/distance/segment_to_box.hpp +++ b/boost/geometry/algorithms/detail/distance/segment_to_box.hpp @@ -28,6 +28,7 @@ #include <boost/geometry/core/tags.hpp> #include <boost/geometry/util/calculation_type.hpp> +#include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/math.hpp> #include <boost/geometry/strategies/distance.hpp> @@ -151,7 +152,7 @@ public: } } - if (is_comparable<Strategy>::value) + if (BOOST_GEOMETRY_CONDITION(is_comparable<Strategy>::value)) { return cd[imin]; } diff --git a/boost/geometry/algorithms/detail/distance/segment_to_segment.hpp b/boost/geometry/algorithms/detail/distance/segment_to_segment.hpp index 2dcde64946..bdf056d76e 100644 --- a/boost/geometry/algorithms/detail/distance/segment_to_segment.hpp +++ b/boost/geometry/algorithms/detail/distance/segment_to_segment.hpp @@ -18,6 +18,8 @@ #include <boost/geometry/core/point_type.hpp> #include <boost/geometry/core/tags.hpp> +#include <boost/geometry/util/condition.hpp> + #include <boost/geometry/strategies/distance.hpp> #include <boost/geometry/strategies/tags.hpp> @@ -96,7 +98,7 @@ public: std::size_t imin = std::distance(boost::addressof(d[0]), std::min_element(d, d + 4)); - if (is_comparable<Strategy>::value) + if (BOOST_GEOMETRY_CONDITION(is_comparable<Strategy>::value)) { return d[imin]; } diff --git a/boost/geometry/algorithms/detail/equals/collect_vectors.hpp b/boost/geometry/algorithms/detail/equals/collect_vectors.hpp index 5bcb5ffaa0..dc1cd74900 100644 --- a/boost/geometry/algorithms/detail/equals/collect_vectors.hpp +++ b/boost/geometry/algorithms/detail/equals/collect_vectors.hpp @@ -17,7 +17,6 @@ #include <boost/numeric/conversion/cast.hpp> -#include <boost/range.hpp> #include <boost/geometry/algorithms/detail/interior_iterator.hpp> @@ -28,7 +27,7 @@ #include <boost/geometry/geometries/concepts/check.hpp> #include <boost/geometry/util/math.hpp> - +#include <boost/geometry/util/range.hpp> namespace boost { namespace geometry @@ -159,7 +158,7 @@ struct range_collect_vectors if ( collected_count > 1 ) { typedef typename boost::range_iterator<Collection>::type c_iterator; - c_iterator first = collection.begin() + c_old_size; + c_iterator first = range::pos(collection, c_old_size); if ( first->same_direction(collection.back()) ) { diff --git a/boost/geometry/algorithms/detail/flattening.hpp b/boost/geometry/algorithms/detail/flattening.hpp new file mode 100644 index 0000000000..8ed5fd9a89 --- /dev/null +++ b/boost/geometry/algorithms/detail/flattening.hpp @@ -0,0 +1,69 @@ +// Boost.Geometry + +// Copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_FLATTENING_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_FLATTENING_HPP + +#include <boost/geometry/core/radius.hpp> +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/algorithms/not_implemented.hpp> + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace detail_dispatch +{ + +template <typename ResultType, typename Geometry, typename Tag = typename tag<Geometry>::type> +struct flattening + : not_implemented<Tag> +{}; + +template <typename ResultType, typename Geometry> +struct flattening<ResultType, Geometry, srs_sphere_tag> +{ + static inline ResultType apply(Geometry const& /*geometry*/) + { + return ResultType(0); + } +}; + +template <typename ResultType, typename Geometry> +struct flattening<ResultType, Geometry, srs_spheroid_tag> +{ + static inline ResultType apply(Geometry const& geometry) + { + return ResultType(get_radius<0>(geometry) - get_radius<2>(geometry)) + / ResultType(get_radius<0>(geometry)); + } +}; + +} // namespace detail_dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace detail +{ + +template <typename ResultType, typename Geometry> +ResultType flattening(Geometry const& geometry) +{ + return detail_dispatch::flattening<ResultType, Geometry>::apply(geometry); +} + +} // namespace detail +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_FLATTENING_HPP diff --git a/boost/geometry/algorithms/detail/intersection/interface.hpp b/boost/geometry/algorithms/detail/intersection/interface.hpp index 323ab7c850..d57535e61f 100644 --- a/boost/geometry/algorithms/detail/intersection/interface.hpp +++ b/boost/geometry/algorithms/detail/intersection/interface.hpp @@ -223,8 +223,8 @@ struct intersection<Geometry1, variant<BOOST_VARIANT_ENUM_PARAMS(T)> > }; -template <BOOST_VARIANT_ENUM_PARAMS(typename A), BOOST_VARIANT_ENUM_PARAMS(typename B)> -struct intersection<variant<BOOST_VARIANT_ENUM_PARAMS(A)>, variant<BOOST_VARIANT_ENUM_PARAMS(B)> > +template <BOOST_VARIANT_ENUM_PARAMS(typename T1), BOOST_VARIANT_ENUM_PARAMS(typename T2)> +struct intersection<variant<BOOST_VARIANT_ENUM_PARAMS(T1)>, variant<BOOST_VARIANT_ENUM_PARAMS(T2)> > { template <typename GeometryOut> struct visitor: static_visitor<bool> @@ -255,8 +255,8 @@ struct intersection<variant<BOOST_VARIANT_ENUM_PARAMS(A)>, variant<BOOST_VARIANT template <typename GeometryOut> static inline bool apply( - const variant<BOOST_VARIANT_ENUM_PARAMS(A)>& geometry1, - const variant<BOOST_VARIANT_ENUM_PARAMS(B)>& geometry2, + const variant<BOOST_VARIANT_ENUM_PARAMS(T1)>& geometry1, + const variant<BOOST_VARIANT_ENUM_PARAMS(T2)>& geometry2, GeometryOut& geometry_out) { return apply_visitor(visitor<GeometryOut>(geometry_out), geometry1, geometry2); diff --git a/boost/geometry/algorithms/detail/is_simple/areal.hpp b/boost/geometry/algorithms/detail/is_simple/areal.hpp index 9a1a16507a..623632c44e 100644 --- a/boost/geometry/algorithms/detail/is_simple/areal.hpp +++ b/boost/geometry/algorithms/detail/is_simple/areal.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -19,6 +19,7 @@ #include <boost/geometry/core/tags.hpp> #include <boost/geometry/algorithms/detail/check_iterator_range.hpp> +#include <boost/geometry/algorithms/detail/is_simple/failure_policy.hpp> #include <boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp> #include <boost/geometry/algorithms/dispatch/is_simple.hpp> @@ -38,11 +39,12 @@ struct is_simple_ring { static inline bool apply(Ring const& ring) { + simplicity_failure_policy policy; return !detail::is_valid::has_duplicates < Ring, geometry::closure<Ring>::value - >::apply(ring); + >::apply(ring, policy); } }; diff --git a/boost/geometry/algorithms/detail/is_simple/debug_print_boundary_points.hpp b/boost/geometry/algorithms/detail/is_simple/debug_print_boundary_points.hpp index 75c37c68f8..196d89b925 100644 --- a/boost/geometry/algorithms/detail/is_simple/debug_print_boundary_points.hpp +++ b/boost/geometry/algorithms/detail/is_simple/debug_print_boundary_points.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -18,6 +18,8 @@ #include <boost/range.hpp> #include <boost/geometry/core/point_type.hpp> +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> #include <boost/geometry/util/range.hpp> @@ -26,7 +28,8 @@ #include <boost/geometry/policies/compare.hpp> #include <boost/geometry/algorithms/equals.hpp> -#endif +#include <boost/geometry/algorithms/not_implemented.hpp> +#endif // BOOST_GEOMETRY_TEST_DEBUG namespace boost { namespace geometry @@ -37,39 +40,67 @@ namespace detail { namespace is_simple #ifdef BOOST_GEOMETRY_TEST_DEBUG -template <typename MultiLinestring> -inline void debug_print_boundary_points(MultiLinestring const& multilinestring) +template <typename Linear, typename Tag = typename tag<Linear>::type> +struct debug_boundary_points_printer + : not_implemented<Linear> +{}; + +template <typename Linestring> +struct debug_boundary_points_printer<Linestring, linestring_tag> { - typedef typename point_type<MultiLinestring>::type point_type; - typedef std::vector<point_type> point_vector; + static inline void apply(Linestring const& linestring) + { + std::cout << "boundary points: "; + std::cout << " " << geometry::dsv(range::front(linestring)); + std::cout << " " << geometry::dsv(range::back(linestring)); + std::cout << std::endl << std::endl; + } +}; - point_vector boundary_points; - for (typename boost::range_iterator<MultiLinestring const>::type it - = boost::begin(multilinestring); - it != boost::end(multilinestring); ++it) +template <typename MultiLinestring> +struct debug_boundary_points_printer<MultiLinestring, multi_linestring_tag> +{ + static inline void apply(MultiLinestring const& multilinestring) { - if ( boost::size(*it) > 1 - && !geometry::equals(range::front(*it), range::back(*it)) ) + typedef typename point_type<MultiLinestring>::type point_type; + typedef std::vector<point_type> point_vector; + + point_vector boundary_points; + for (typename boost::range_iterator<MultiLinestring const>::type it + = boost::begin(multilinestring); + it != boost::end(multilinestring); ++it) { - boundary_points.push_back( range::front(*it) ); - boundary_points.push_back( range::back(*it) ); + if ( boost::size(*it) > 1 + && !geometry::equals(range::front(*it), range::back(*it)) ) + { + boundary_points.push_back( range::front(*it) ); + boundary_points.push_back( range::back(*it) ); + } } - } - std::sort(boundary_points.begin(), boundary_points.end(), - geometry::less<point_type>()); + std::sort(boundary_points.begin(), boundary_points.end(), + geometry::less<point_type>()); - std::cout << "boundary points: "; - for (typename point_vector::const_iterator pit = boundary_points.begin(); - pit != boundary_points.end(); ++pit) - { - std::cout << " " << geometry::dsv(*pit); + std::cout << "boundary points: "; + for (typename point_vector::const_iterator + pit = boundary_points.begin(); + pit != boundary_points.end(); ++pit) + { + std::cout << " " << geometry::dsv(*pit); + } + std::cout << std::endl << std::endl; } - std::cout << std::endl << std::endl; +}; + + +template <typename Linear> +inline void debug_print_boundary_points(Linear const& linear) +{ + debug_boundary_points_printer<Linear>::apply(linear); } #else -template <typename MultiLinestring> -inline void debug_print_boundary_points(MultiLinestring const&) +template <typename Linear> +inline void debug_print_boundary_points(Linear const&) { } #endif // BOOST_GEOMETRY_TEST_DEBUG diff --git a/boost/geometry/algorithms/detail/is_simple/failure_policy.hpp b/boost/geometry/algorithms/detail/is_simple/failure_policy.hpp new file mode 100644 index 0000000000..6504edd1d5 --- /dev/null +++ b/boost/geometry/algorithms/detail/is_simple/failure_policy.hpp @@ -0,0 +1,53 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2015, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_FAILURE_POLICY_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_FAILURE_POLICY_HPP + +#include <boost/geometry/algorithms/validity_failure_type.hpp> + + +namespace boost { namespace geometry +{ + + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace is_simple +{ + + +struct simplicity_failure_policy +{ + template <validity_failure_type Failure> + static inline bool apply() + { + return Failure == no_failure; + } + + template <validity_failure_type Failure, typename Data> + static inline bool apply(Data const&) + { + return apply<Failure>(); + } + + template <validity_failure_type Failure, typename Data1, typename Data2> + static inline bool apply(Data1 const&, Data2 const&) + { + return apply<Failure>(); + } +}; + + +}} // namespace detail::is_simple +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_FAILURE_POLICY_HPP diff --git a/boost/geometry/algorithms/detail/is_simple/interface.hpp b/boost/geometry/algorithms/detail/is_simple/interface.hpp index 4239664ed1..fd84826970 100644 --- a/boost/geometry/algorithms/detail/is_simple/interface.hpp +++ b/boost/geometry/algorithms/detail/is_simple/interface.hpp @@ -10,8 +10,8 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_INTERFACE_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_SIMPLE_INTERFACE_HPP -#include <boost/variant/static_visitor.hpp> #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/geometries/concepts/check.hpp> diff --git a/boost/geometry/algorithms/detail/is_simple/linear.hpp b/boost/geometry/algorithms/detail/is_simple/linear.hpp index f2efcb309d..4f3e875eef 100644 --- a/boost/geometry/algorithms/detail/is_simple/linear.hpp +++ b/boost/geometry/algorithms/detail/is_simple/linear.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -19,6 +19,7 @@ #include <boost/geometry/core/closure.hpp> #include <boost/geometry/core/coordinate_type.hpp> #include <boost/geometry/core/point_type.hpp> +#include <boost/geometry/core/tag.hpp> #include <boost/geometry/core/tags.hpp> #include <boost/geometry/util/range.hpp> @@ -29,8 +30,10 @@ #include <boost/geometry/algorithms/equals.hpp> #include <boost/geometry/algorithms/intersects.hpp> +#include <boost/geometry/algorithms/not_implemented.hpp> #include <boost/geometry/algorithms/detail/check_iterator_range.hpp> +#include <boost/geometry/algorithms/detail/signed_index_type.hpp> #include <boost/geometry/algorithms/detail/disjoint/linear_linear.hpp> #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> @@ -40,6 +43,7 @@ #include <boost/geometry/algorithms/detail/is_valid/has_spikes.hpp> #include <boost/geometry/algorithms/detail/is_simple/debug_print_boundary_points.hpp> +#include <boost/geometry/algorithms/detail/is_simple/failure_policy.hpp> #include <boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp> #include <boost/geometry/algorithms/dispatch/is_simple.hpp> @@ -54,108 +58,211 @@ namespace detail { namespace is_simple { -template <typename Linestring, bool CheckSelfIntersections = true> -struct is_simple_linestring +template <typename Turn> +inline bool check_segment_indices(Turn const& turn, + signed_index_type last_index) { - static inline bool apply(Linestring const& linestring) + return + (turn.operations[0].seg_id.segment_index == 0 + && turn.operations[1].seg_id.segment_index == last_index) + || + (turn.operations[0].seg_id.segment_index == 0 + && turn.operations[1].seg_id.segment_index == last_index); +} + + +template <typename Geometry, typename Tag = typename tag<Geometry>::type> +class is_acceptable_turn + : not_implemented<Geometry> +{}; + +template <typename Linestring> +class is_acceptable_turn<Linestring, linestring_tag> +{ +public: + is_acceptable_turn(Linestring const& linestring) + : m_linestring(linestring) + , m_is_closed(geometry::equals(range::front(linestring), + range::back(linestring))) + {} + + template <typename Turn> + inline bool apply(Turn const& turn) const { - return !detail::is_valid::has_duplicates - < - Linestring, closed - >::apply(linestring) - && !detail::is_valid::has_spikes - < - Linestring, closed - >::apply(linestring) - && !(CheckSelfIntersections && geometry::intersects(linestring)); + BOOST_ASSERT(boost::size(m_linestring) > 1); + return m_is_closed + && turn.method == overlay::method_none + && check_segment_indices(turn, boost::size(m_linestring) - 2) + && turn.operations[0].fraction.is_zero(); } -}; - +private: + Linestring const& m_linestring; + bool const m_is_closed; +}; template <typename MultiLinestring> -class is_simple_multilinestring +class is_acceptable_turn<MultiLinestring, multi_linestring_tag> { private: - class is_acceptable_turn + typedef typename boost::range_value<MultiLinestring>::type linestring_type; + typedef is_acceptable_turn<linestring_type> base_type; + + template <typename Point, typename Linestring> + static inline bool is_boundary_point_of(Point const& point, + Linestring const& linestring) { - private: - template <typename Point, typename Linestring> - static inline bool is_boundary_point_of(Point const& point, - Linestring const& linestring) - { - BOOST_ASSERT( boost::size(linestring) > 1 ); - return - !geometry::equals(range::front(linestring), - range::back(linestring)) - && - ( geometry::equals(point, range::front(linestring)) - || geometry::equals(point, range::back(linestring)) ); - } + BOOST_ASSERT(boost::size(linestring) > 1); + return + ! geometry::equals(range::front(linestring), + range::back(linestring)) + && + (geometry::equals(point, range::front(linestring)) + || geometry::equals(point, range::back(linestring))); + } + + template <typename Turn, typename Linestring> + static inline bool is_closing_point_of(Turn const& turn, + Linestring const& linestring) + { + BOOST_ASSERT(boost::size(linestring) > 1); + return + turn.method == overlay::method_none + && + check_segment_indices(turn, boost::size(linestring) - 2) + && + geometry::equals(range::front(linestring), range::back(linestring)) + && + turn.operations[0].fraction.is_zero(); + ; + } + + template <typename Linestring1, typename Linestring2> + static inline bool have_same_boundary_points(Linestring1 const& ls1, + Linestring2 const& ls2) + { + return + geometry::equals(range::front(ls1), range::front(ls2)) + ? + geometry::equals(range::back(ls1), range::back(ls2)) + : + (geometry::equals(range::front(ls1), range::back(ls2)) + && + geometry::equals(range::back(ls1), range::front(ls2))) + ; + } + +public: + is_acceptable_turn(MultiLinestring const& multilinestring) + : m_multilinestring(multilinestring) + {} + + template <typename Turn> + inline bool apply(Turn const& turn) const + { + linestring_type const& ls1 = + range::at(m_multilinestring, turn.operations[0].seg_id.multi_index); - template <typename Linestring1, typename Linestring2> - static inline bool have_same_boundary_points(Linestring1 const& ls1, - Linestring2 const& ls2) + linestring_type const& ls2 = + range::at(m_multilinestring, turn.operations[1].seg_id.multi_index); + + if (turn.operations[0].seg_id.multi_index + == turn.operations[1].seg_id.multi_index) { - return - geometry::equals(range::front(ls1), range::front(ls2)) - ? - geometry::equals(range::back(ls1), range::back(ls2)) - : - (geometry::equals(range::front(ls1), range::back(ls2)) - && - geometry::equals(range::back(ls1), range::front(ls2)) - ) - ; + return is_closing_point_of(turn, ls1); } - public: - is_acceptable_turn(MultiLinestring const& multilinestring) - : m_multilinestring(multilinestring) - {} + return + is_boundary_point_of(turn.point, ls1) + && is_boundary_point_of(turn.point, ls2) + && + ( boost::size(ls1) != 2 + || boost::size(ls2) != 2 + || ! have_same_boundary_points(ls1, ls2) ); + } - template <typename Turn> - inline bool apply(Turn const& turn) const - { - typedef typename boost::range_value +private: + MultiLinestring const& m_multilinestring; +}; + + +template <typename Linear> +inline bool has_self_intersections(Linear const& linear) +{ + typedef typename point_type<Linear>::type point_type; + + // compute self turns + typedef detail::overlay::turn_info + < + point_type, + geometry::segment_ratio < - MultiLinestring - >::type linestring; - - linestring const& ls1 = - range::at(m_multilinestring, - turn.operations[0].seg_id.multi_index); - - linestring const& ls2 = - range::at(m_multilinestring, - turn.operations[1].seg_id.multi_index); - - return - is_boundary_point_of(turn.point, ls1) - && is_boundary_point_of(turn.point, ls2) - && - ( boost::size(ls1) != 2 - || boost::size(ls2) != 2 - || !have_same_boundary_points(ls1, ls2) ); - } + typename geometry::coordinate_type<point_type>::type + > + > turn_info; - private: - MultiLinestring const& m_multilinestring; - }; + std::deque<turn_info> turns; + typedef detail::overlay::get_turn_info + < + detail::disjoint::assign_disjoint_policy + > turn_policy; -public: - static inline bool apply(MultiLinestring const& multilinestring) + is_acceptable_turn<Linear> predicate(linear); + detail::overlay::predicate_based_interrupt_policy + < + is_acceptable_turn<Linear> + > interrupt_policy(predicate); + + detail::self_get_turn_points::get_turns + < + turn_policy + >::apply(linear, + detail::no_rescale_policy(), + turns, + interrupt_policy); + + detail::is_valid::debug_print_turns(turns.begin(), turns.end()); + debug_print_boundary_points(linear); + + return interrupt_policy.has_intersections; +} + + +template <typename Linestring, bool CheckSelfIntersections = true> +struct is_simple_linestring +{ + static inline bool apply(Linestring const& linestring) { - typedef typename boost::range_value<MultiLinestring>::type linestring; - typedef typename point_type<MultiLinestring>::type point_type; - typedef point_type point; + simplicity_failure_policy policy; + return ! detail::is_valid::has_duplicates + < + Linestring, closed + >::apply(linestring, policy) + && ! detail::is_valid::has_spikes + < + Linestring, closed + >::apply(linestring, policy) + && ! (CheckSelfIntersections && has_self_intersections(linestring)); + } +}; +template <typename MultiLinestring> +struct is_simple_multilinestring +{ + static inline bool apply(MultiLinestring const& multilinestring) + { // check each of the linestrings for simplicity - if ( !detail::check_iterator_range + // but do not compute self-intersections yet; these will be + // computed for the entire multilinestring + if ( ! detail::check_iterator_range < - is_simple_linestring<linestring>, + is_simple_linestring + < + typename boost::range_value<MultiLinestring>::type, + false // do not compute self-intersections + >, false // do not allow empty multilinestring >::apply(boost::begin(multilinestring), boost::end(multilinestring)) @@ -164,44 +271,8 @@ public: return false; } - - // compute self turns - typedef detail::overlay::turn_info - < - point_type, - geometry::segment_ratio - < - typename geometry::coordinate_type<point>::type - > - > turn_info; - - std::deque<turn_info> turns; - - typedef detail::overlay::get_turn_info - < - detail::disjoint::assign_disjoint_policy - > turn_policy; - - is_acceptable_turn predicate(multilinestring); - detail::overlay::predicate_based_interrupt_policy - < - is_acceptable_turn - > interrupt_policy(predicate); - - detail::self_get_turn_points::get_turns - < - turn_policy - >::apply(multilinestring, - detail::no_rescale_policy(), - turns, - interrupt_policy); - - detail::is_valid::debug_print_turns(turns.begin(), turns.end()); - debug_print_boundary_points(multilinestring); - - return !interrupt_policy.has_intersections; + return ! has_self_intersections(multilinestring); } - }; diff --git a/boost/geometry/algorithms/detail/is_simple/multipoint.hpp b/boost/geometry/algorithms/detail/is_simple/multipoint.hpp index d996eb64e9..71c9e6ba90 100644 --- a/boost/geometry/algorithms/detail/is_simple/multipoint.hpp +++ b/boost/geometry/algorithms/detail/is_simple/multipoint.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -21,6 +21,7 @@ #include <boost/geometry/policies/compare.hpp> #include <boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp> +#include <boost/geometry/algorithms/detail/is_simple/failure_policy.hpp> #include <boost/geometry/algorithms/dispatch/is_simple.hpp> @@ -48,7 +49,11 @@ struct is_simple_multipoint std::sort(boost::begin(mp), boost::end(mp), geometry::less<typename point_type<MultiPoint>::type>()); - return !detail::is_valid::has_duplicates<MultiPoint, closed>::apply(mp); + simplicity_failure_policy policy; + return !detail::is_valid::has_duplicates + < + MultiPoint, closed + >::apply(mp, policy); } }; diff --git a/boost/geometry/algorithms/detail/is_valid/box.hpp b/boost/geometry/algorithms/detail/is_valid/box.hpp index f82b3f9bf1..139502af78 100644 --- a/boost/geometry/algorithms/detail/is_valid/box.hpp +++ b/boost/geometry/algorithms/detail/is_valid/box.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -16,6 +16,7 @@ #include <boost/geometry/core/tags.hpp> #include <boost/geometry/core/coordinate_dimension.hpp> +#include <boost/geometry/algorithms/validity_failure_type.hpp> #include <boost/geometry/algorithms/dispatch/is_valid.hpp> @@ -30,15 +31,22 @@ namespace detail { namespace is_valid template <typename Box, std::size_t I> struct has_valid_corners { - static inline bool apply(Box const& box) + template <typename VisitPolicy> + static inline bool apply(Box const& box, VisitPolicy& visitor) { - if ( geometry::get<geometry::max_corner, I-1>(box) - <= - geometry::get<geometry::min_corner, I-1>(box) ) + if (math::equals(geometry::get<geometry::min_corner, I-1>(box), + geometry::get<geometry::max_corner, I-1>(box))) { - return false; + return + visitor.template apply<failure_wrong_topological_dimension>(); } - return has_valid_corners<Box, I-1>::apply(box); + else if (geometry::get<geometry::min_corner, I-1>(box) + > + geometry::get<geometry::max_corner, I-1>(box)) + { + return visitor.template apply<failure_wrong_corner_order>(); + } + return has_valid_corners<Box, I-1>::apply(box, visitor); } }; @@ -46,9 +54,10 @@ struct has_valid_corners template <typename Box> struct has_valid_corners<Box, 0> { - static inline bool apply(Box const&) + template <typename VisitPolicy> + static inline bool apply(Box const&, VisitPolicy& visitor) { - return true; + return visitor.template apply<no_failure>(); } }; diff --git a/boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp b/boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp index 6824921b63..ab99a9921b 100644 --- a/boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp +++ b/boost/geometry/algorithms/detail/is_valid/debug_print_turns.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -25,33 +25,47 @@ namespace detail { namespace is_valid { #ifdef BOOST_GEOMETRY_TEST_DEBUG +template <typename Turn> +inline void debug_print_turn(Turn const& turn) +{ + std::cout << " [" + << geometry::method_char(turn.method) + << "," + << geometry::operation_char(turn.operations[0].operation) + << "/" + << geometry::operation_char(turn.operations[1].operation) + << " {" + << turn.operations[0].seg_id.multi_index + << ", " + << turn.operations[1].seg_id.multi_index + << "} {" + << turn.operations[0].seg_id.ring_index + << ", " + << turn.operations[1].seg_id.ring_index + << "} {" + << turn.operations[0].seg_id.segment_index + << ", " + << turn.operations[1].seg_id.segment_index + << "} " + << geometry::dsv(turn.point) + << "]"; +} + template <typename TurnIterator> inline void debug_print_turns(TurnIterator first, TurnIterator beyond) { std::cout << "turns:"; for (TurnIterator tit = first; tit != beyond; ++tit) { - std::cout << " [" - << geometry::method_char(tit->method) - << "," - << geometry::operation_char(tit->operations[0].operation) - << "/" - << geometry::operation_char(tit->operations[1].operation) - << " {" - << tit->operations[0].seg_id.multi_index - << ", " - << tit->operations[1].seg_id.multi_index - << "} {" - << tit->operations[0].seg_id.ring_index - << ", " - << tit->operations[1].seg_id.ring_index - << "} " - << geometry::dsv(tit->point) - << "]"; + debug_print_turn(*tit); } std::cout << std::endl << std::endl; } #else +template <typename Turn> +inline void debug_print_turn(Turn const&) +{} + template <typename TurnIterator> inline void debug_print_turns(TurnIterator, TurnIterator) {} diff --git a/boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp b/boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp index 6f1c263646..a10e0fe596 100644 --- a/boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp +++ b/boost/geometry/algorithms/detail/is_valid/debug_validity_phase.hpp @@ -50,8 +50,8 @@ struct debug_validity_phase<Polygon, polygon_tag> std::cout << "computing and analyzing turns..." << std::endl; break; case 4: - std::cout << "checking if holes are inside the exterior ring..." - << std::endl; + std::cout << "checking if interior rings are inside " + << "the exterior ring..." << std::endl; break; case 5: std::cout << "checking connectivity of interior..." << std::endl; diff --git a/boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp b/boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp index dd0922bb2b..5878841e70 100644 --- a/boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp +++ b/boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -15,8 +15,10 @@ #include <boost/geometry/core/closure.hpp> #include <boost/geometry/policies/compare.hpp> +#include <boost/geometry/policies/is_valid/default_policy.hpp> #include <boost/geometry/views/closeable_view.hpp> +#include <boost/geometry/algorithms/validity_failure_type.hpp> namespace boost { namespace geometry @@ -30,7 +32,8 @@ namespace detail { namespace is_valid template <typename Range, closure_selector Closure> struct has_duplicates { - static inline bool apply(Range const& range) + template <typename VisitPolicy> + static inline bool apply(Range const& range, VisitPolicy& visitor) { typedef typename closeable_view<Range const, Closure>::type view_type; typedef typename boost::range_iterator<view_type const>::type iterator; @@ -39,7 +42,7 @@ struct has_duplicates if ( boost::size(view) < 2 ) { - return false; + return ! visitor.template apply<no_failure>(); } geometry::equal_to<typename boost::range_value<Range>::type> equal; @@ -50,10 +53,10 @@ struct has_duplicates { if ( equal(*it, *next) ) { - return true; + return ! visitor.template apply<failure_duplicate_points>(*it); } } - return false; + return ! visitor.template apply<no_failure>(); } }; diff --git a/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp b/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp index 9b95017482..090c026e8b 100644 --- a/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp +++ b/boost/geometry/algorithms/detail/is_valid/has_spikes.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -13,15 +13,22 @@ #include <algorithm> #include <boost/range.hpp> +#include <boost/type_traits/is_same.hpp> #include <boost/geometry/core/point_type.hpp> +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/policies/is_valid/default_policy.hpp> #include <boost/geometry/util/range.hpp> #include <boost/geometry/views/closeable_view.hpp> #include <boost/geometry/algorithms/equals.hpp> +#include <boost/geometry/algorithms/validity_failure_type.hpp> #include <boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp> +#include <boost/geometry/io/dsv/write.hpp> namespace boost { namespace geometry @@ -60,7 +67,7 @@ struct not_equal_to template <typename OtherPoint> inline bool operator()(OtherPoint const& other) const { - return !geometry::equals(other, m_point); + return ! geometry::equals(other, m_point); } }; @@ -69,13 +76,17 @@ struct not_equal_to template <typename Range, closure_selector Closure> struct has_spikes { - static inline bool apply(Range const& range) + template <typename VisitPolicy> + static inline bool apply(Range const& range, VisitPolicy& visitor) { typedef not_equal_to<typename point_type<Range>::type> not_equal; typedef typename closeable_view<Range const, Closure>::type view_type; typedef typename boost::range_iterator<view_type const>::type iterator; + bool const is_linear + = boost::is_same<typename tag<Range>::type, linestring_tag>::value; + view_type const view(range); iterator prev = boost::begin(view); @@ -85,7 +96,7 @@ struct has_spikes { // the range has only one distinct point, so it // cannot have a spike - return false; + return ! visitor.template apply<no_failure>(); } iterator next = std::find_if(cur, boost::end(view), not_equal(*cur)); @@ -93,7 +104,7 @@ struct has_spikes { // the range has only two distinct points, so it // cannot have a spike - return false; + return ! visitor.template apply<no_failure>(); } while ( next != boost::end(view) ) @@ -102,7 +113,8 @@ struct has_spikes *next, *cur) ) { - return true; + return + ! visitor.template apply<failure_spikes>(is_linear, *cur); } prev = cur; cur = next; @@ -120,10 +132,18 @@ struct has_spikes not_equal(range::back(view))); iterator next = std::find_if(cur, boost::end(view), not_equal(*cur)); - return detail::point_is_spike_or_equal(*prev, *next, *cur); + if (detail::point_is_spike_or_equal(*prev, *next, *cur)) + { + return + ! visitor.template apply<failure_spikes>(is_linear, *cur); + } + else + { + return ! visitor.template apply<no_failure>(); + } } - return false; + return ! visitor.template apply<no_failure>(); } }; diff --git a/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp b/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp index 220a67bcd1..ecbc4782b2 100644 --- a/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp +++ b/boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -10,6 +10,11 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_VALID_SELF_TURNS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_VALID_SELF_TURNS_HPP +#include <vector> + +#include <boost/assert.hpp> +#include <boost/range.hpp> + #include <boost/geometry/core/point_type.hpp> #include <boost/geometry/policies/predicate_based_interrupt_policy.hpp> @@ -22,7 +27,6 @@ #include <boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp> - namespace boost { namespace geometry { @@ -64,8 +68,10 @@ public: > turn_type; // returns true if all turns are valid - template <typename Turns> - static inline bool apply(Geometry const& geometry, Turns& turns) + template <typename Turns, typename VisitPolicy> + static inline bool apply(Geometry const& geometry, + Turns& turns, + VisitPolicy& visitor) { rescale_policy_type robust_policy = geometry::get_rescale_policy<rescale_policy_type>(geometry); @@ -80,7 +86,23 @@ public: turns, interrupt_policy); - return !interrupt_policy.has_intersections; + if (interrupt_policy.has_intersections) + { + BOOST_ASSERT(! boost::empty(turns)); + return visitor.template apply<failure_self_intersections>(turns); + } + else + { + return visitor.template apply<no_failure>(); + } + } + + // returns true if all turns are valid + template <typename VisitPolicy> + static inline bool apply(Geometry const& geometry, VisitPolicy& visitor) + { + std::vector<turn_type> turns; + return apply(geometry, turns, visitor); } }; diff --git a/boost/geometry/algorithms/detail/is_valid/interface.hpp b/boost/geometry/algorithms/detail/is_valid/interface.hpp index 4b232fd436..f83b09c437 100644 --- a/boost/geometry/algorithms/detail/is_valid/interface.hpp +++ b/boost/geometry/algorithms/detail/is_valid/interface.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -10,13 +10,19 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_INTERFACE_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_INTERFACE_HPP -#include <boost/variant/static_visitor.hpp> +#include <sstream> +#include <string> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/geometries/concepts/check.hpp> #include <boost/geometry/algorithms/dispatch/is_valid.hpp> +#include <boost/geometry/policies/is_valid/default_policy.hpp> +#include <boost/geometry/policies/is_valid/failing_reason_policy.hpp> +#include <boost/geometry/policies/is_valid/failure_type_policy.hpp> namespace boost { namespace geometry @@ -28,48 +34,123 @@ namespace resolve_variant { template <typename Geometry> struct is_valid { - static inline bool apply(Geometry const& geometry) + template <typename VisitPolicy> + static inline bool apply(Geometry const& geometry, VisitPolicy& visitor) { concept::check<Geometry const>(); - return dispatch::is_valid<Geometry>::apply(geometry); + return dispatch::is_valid<Geometry>::apply(geometry, visitor); } }; template <BOOST_VARIANT_ENUM_PARAMS(typename T)> struct is_valid<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> > { + template <typename VisitPolicy> struct visitor : boost::static_visitor<bool> { + visitor(VisitPolicy& policy) : m_policy(policy) {} + template <typename Geometry> bool operator()(Geometry const& geometry) const { - return is_valid<Geometry>::apply(geometry); + return is_valid<Geometry>::apply(geometry, m_policy); } + + VisitPolicy& m_policy; }; + template <typename VisitPolicy> static inline bool - apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry) + apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry, + VisitPolicy& policy_visitor) { - return boost::apply_visitor(visitor(), geometry); + return boost::apply_visitor(visitor<VisitPolicy>(policy_visitor), + geometry); } }; } // namespace resolve_variant +// Undocumented for now +template <typename Geometry, typename VisitPolicy> +inline bool is_valid(Geometry const& geometry, VisitPolicy& visitor) +{ + return resolve_variant::is_valid<Geometry>::apply(geometry, visitor); +} + + /*! \brief \brief_check{is valid (in the OGC sense)} \ingroup is_valid \tparam Geometry \tparam_geometry \param geometry \param_geometry -\return \return_check{is valid (in the OGC sense)} +\return \return_check{is valid (in the OGC sense); + furthermore, the following geometries are considered valid: + multi-geometries with no elements, + linear geometries containing spikes, + areal geometries with duplicate (consecutive) points} \qbk{[include reference/algorithms/is_valid.qbk]} */ template <typename Geometry> inline bool is_valid(Geometry const& geometry) { - return resolve_variant::is_valid<Geometry>::apply(geometry); + is_valid_default_policy<> policy_visitor; + return is_valid(geometry, policy_visitor); +} + + +/*! +\brief \brief_check{is valid (in the OGC sense)} +\ingroup is_valid +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\param failure An enumeration value indicating that the geometry is + valid or not, and if not valid indicating the reason why +\return \return_check{is valid (in the OGC sense); + furthermore, the following geometries are considered valid: + multi-geometries with no elements, + linear geometries containing spikes, + areal geometries with duplicate (consecutive) points} + +\qbk{distinguish,with failure value} +\qbk{[include reference/algorithms/is_valid_with_failure.qbk]} +*/ +template <typename Geometry> +inline bool is_valid(Geometry const& geometry, validity_failure_type& failure) +{ + failure_type_policy<> policy_visitor; + bool result = is_valid(geometry, policy_visitor); + failure = policy_visitor.failure(); + return result; +} + + +/*! +\brief \brief_check{is valid (in the OGC sense)} +\ingroup is_valid +\tparam Geometry \tparam_geometry +\param geometry \param_geometry +\param message A string containing a message stating if the geometry + is valid or not, and if not valid a reason why +\return \return_check{is valid (in the OGC sense); + furthermore, the following geometries are considered valid: + multi-geometries with no elements, + linear geometries containing spikes, + areal geometries with duplicate (consecutive) points} + +\qbk{distinguish,with message} +\qbk{[include reference/algorithms/is_valid_with_message.qbk]} +*/ +template <typename Geometry> +inline bool is_valid(Geometry const& geometry, std::string& message) +{ + std::ostringstream stream; + failing_reason_policy<> policy_visitor(stream); + bool result = is_valid(geometry, policy_visitor); + message = stream.str(); + return result; } diff --git a/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp b/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp index f9d926770e..0d80d6f6c0 100644 --- a/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp +++ b/boost/geometry/algorithms/detail/is_valid/is_acceptable_turn.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -72,6 +72,16 @@ template <typename Geometry, typename Tag = typename tag<Geometry>::type> struct is_acceptable_turn {}; +template <typename Ring> +struct is_acceptable_turn<Ring, ring_tag> +{ + template <typename Turn> + static inline bool apply(Turn const&) + { + return false; + } +}; + template <typename Polygon> class is_acceptable_turn<Polygon, polygon_tag> { diff --git a/boost/geometry/algorithms/detail/is_valid/linear.hpp b/boost/geometry/algorithms/detail/is_valid/linear.hpp index 244df9b035..69243563ec 100644 --- a/boost/geometry/algorithms/detail/is_valid/linear.hpp +++ b/boost/geometry/algorithms/detail/is_valid/linear.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -21,6 +21,7 @@ #include <boost/geometry/util/range.hpp> #include <boost/geometry/algorithms/equals.hpp> +#include <boost/geometry/algorithms/validity_failure_type.hpp> #include <boost/geometry/algorithms/detail/check_iterator_range.hpp> #include <boost/geometry/algorithms/detail/is_valid/has_spikes.hpp> #include <boost/geometry/algorithms/detail/num_distinct_consecutive_points.hpp> @@ -36,11 +37,18 @@ namespace detail { namespace is_valid { -template <typename Linestring, bool AllowSpikes> +template <typename Linestring> struct is_valid_linestring { - static inline bool apply(Linestring const& linestring) + template <typename VisitPolicy> + static inline bool apply(Linestring const& linestring, + VisitPolicy& visitor) { + if (boost::size(linestring) < 2) + { + return visitor.template apply<failure_few_points>(); + } + std::size_t num_distinct = detail::num_distinct_consecutive_points < Linestring, @@ -49,14 +57,17 @@ struct is_valid_linestring not_equal_to<typename point_type<Linestring>::type> >::apply(linestring); - if ( num_distinct < 2u ) + if (num_distinct < 2u) { - return false; + return + visitor.template apply<failure_wrong_topological_dimension>(); } - return num_distinct == 2u - || AllowSpikes - || !has_spikes<Linestring, closed>::apply(linestring); + if (num_distinct == 2u) + { + return visitor.template apply<no_failure>(); + } + return ! has_spikes<Linestring, closed>::apply(linestring, visitor); } }; @@ -84,9 +95,11 @@ namespace dispatch // By default, spikes are disallowed // // Reference: OGC 06-103r4 (6.1.6.1) -template <typename Linestring, bool AllowSpikes> -struct is_valid<Linestring, linestring_tag, AllowSpikes> - : detail::is_valid::is_valid_linestring<Linestring, AllowSpikes> +template <typename Linestring, bool AllowEmptyMultiGeometries> +struct is_valid + < + Linestring, linestring_tag, AllowEmptyMultiGeometries + > : detail::is_valid::is_valid_linestring<Linestring> {}; @@ -96,21 +109,47 @@ struct is_valid<Linestring, linestring_tag, AllowSpikes> // are on the boundaries of both elements. // // Reference: OGC 06-103r4 (6.1.8.1; Fig. 9) -template <typename MultiLinestring, bool AllowSpikes> -struct is_valid<MultiLinestring, multi_linestring_tag, AllowSpikes> +template <typename MultiLinestring, bool AllowEmptyMultiGeometries> +class is_valid + < + MultiLinestring, multi_linestring_tag, AllowEmptyMultiGeometries + > { - static inline bool apply(MultiLinestring const& multilinestring) +private: + template <typename VisitPolicy> + struct per_linestring + { + per_linestring(VisitPolicy& policy) : m_policy(policy) {} + + template <typename Linestring> + inline bool apply(Linestring const& linestring) const + { + return detail::is_valid::is_valid_linestring + < + Linestring + >::apply(linestring, m_policy); + } + + VisitPolicy& m_policy; + }; + +public: + template <typename VisitPolicy> + static inline bool apply(MultiLinestring const& multilinestring, + VisitPolicy& visitor) { + if (AllowEmptyMultiGeometries && boost::empty(multilinestring)) + { + return visitor.template apply<no_failure>(); + } + return detail::check_iterator_range < - detail::is_valid::is_valid_linestring - < - typename boost::range_value<MultiLinestring>::type, - AllowSpikes - >, - false // do not allow empty multilinestring + per_linestring<VisitPolicy>, + false // do not check for empty multilinestring (done above) >::apply(boost::begin(multilinestring), - boost::end(multilinestring)); + boost::end(multilinestring), + per_linestring<VisitPolicy>(visitor)); } }; diff --git a/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp b/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp index 3d0ebb5f82..9362bfca0e 100644 --- a/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp +++ b/boost/geometry/algorithms/detail/is_valid/multipolygon.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -25,6 +25,7 @@ #include <boost/geometry/geometries/box.hpp> +#include <boost/geometry/algorithms/validity_failure_type.hpp> #include <boost/geometry/algorithms/within.hpp> #include <boost/geometry/algorithms/detail/check_iterator_range.hpp> @@ -49,12 +50,11 @@ namespace detail { namespace is_valid { -template <typename MultiPolygon, bool AllowDuplicates> +template <typename MultiPolygon, bool AllowEmptyMultiGeometries> class is_valid_multipolygon : is_valid_polygon < typename boost::range_value<MultiPolygon>::type, - AllowDuplicates, true // check only the validity of rings > { @@ -62,18 +62,23 @@ private: typedef is_valid_polygon < typename boost::range_value<MultiPolygon>::type, - AllowDuplicates, true > base; - template <typename PolygonIterator, typename TurnIterator> + template + < + typename PolygonIterator, + typename TurnIterator, + typename VisitPolicy + > static inline bool are_polygon_interiors_disjoint(PolygonIterator polygons_first, PolygonIterator polygons_beyond, TurnIterator turns_first, - TurnIterator turns_beyond) + TurnIterator turns_beyond, + VisitPolicy& visitor) { // collect all polygons that have turns std::set<signed_index_type> multi_indices; @@ -89,22 +94,29 @@ private: for (PolygonIterator it = polygons_first; it != polygons_beyond; ++it, ++multi_index) { - if ( multi_indices.find(multi_index) == multi_indices.end() ) + if (multi_indices.find(multi_index) == multi_indices.end()) { polygon_iterators.push_back(it); } } - typename base::item_visitor visitor; + typename base::item_visitor_type item_visitor; geometry::partition < geometry::model::box<typename point_type<MultiPolygon>::type>, typename base::expand_box, typename base::overlaps_box - >::apply(polygon_iterators, visitor); + >::apply(polygon_iterators, item_visitor); - return !visitor.items_overlap; + if (item_visitor.items_overlap) + { + return visitor.template apply<failure_intersecting_interiors>(); + } + else + { + return visitor.template apply<no_failure>(); + } } @@ -132,11 +144,17 @@ private: template <typename Predicate> struct has_property_per_polygon { - template <typename PolygonIterator, typename TurnIterator> + template + < + typename PolygonIterator, + typename TurnIterator, + typename VisitPolicy + > static inline bool apply(PolygonIterator polygons_first, PolygonIterator polygons_beyond, TurnIterator turns_first, - TurnIterator turns_beyond) + TurnIterator turns_beyond, + VisitPolicy& visitor) { signed_index_type multi_index = 0; for (PolygonIterator it = polygons_first; it != polygons_beyond; @@ -157,9 +175,10 @@ private: turns_beyond, turns_beyond); - if ( !Predicate::apply(*it, + if (! Predicate::apply(*it, filtered_turns_first, - filtered_turns_beyond) ) + filtered_turns_beyond, + visitor)) { return false; } @@ -170,49 +189,82 @@ private: - template <typename PolygonIterator, typename TurnIterator> + template + < + typename PolygonIterator, + typename TurnIterator, + typename VisitPolicy + > static inline bool have_holes_inside(PolygonIterator polygons_first, PolygonIterator polygons_beyond, TurnIterator turns_first, - TurnIterator turns_beyond) + TurnIterator turns_beyond, + VisitPolicy& visitor) { return has_property_per_polygon < typename base::has_holes_inside >::apply(polygons_first, polygons_beyond, - turns_first, turns_beyond); + turns_first, turns_beyond, visitor); } - template <typename PolygonIterator, typename TurnIterator> + template + < + typename PolygonIterator, + typename TurnIterator, + typename VisitPolicy + > static inline bool have_connected_interior(PolygonIterator polygons_first, PolygonIterator polygons_beyond, TurnIterator turns_first, - TurnIterator turns_beyond) + TurnIterator turns_beyond, + VisitPolicy& visitor) { return has_property_per_polygon < typename base::has_connected_interior >::apply(polygons_first, polygons_beyond, - turns_first, turns_beyond); + turns_first, turns_beyond, visitor); } + template <typename VisitPolicy> + struct per_polygon + { + per_polygon(VisitPolicy& policy) : m_policy(policy) {} + + template <typename Polygon> + inline bool apply(Polygon const& polygon) const + { + return base::apply(polygon, m_policy); + } + + VisitPolicy& m_policy; + }; public: - static inline bool apply(MultiPolygon const& multipolygon) + template <typename VisitPolicy> + static inline bool apply(MultiPolygon const& multipolygon, + VisitPolicy& visitor) { typedef debug_validity_phase<MultiPolygon> debug_phase; + if (AllowEmptyMultiGeometries && boost::empty(multipolygon)) + { + return visitor.template apply<no_failure>(); + } + // check validity of all polygons ring debug_phase::apply(1); - if ( !detail::check_iterator_range + if (! detail::check_iterator_range < - base, - false // do not allow empty multi-polygons + per_polygon<VisitPolicy>, + false // do not check for empty multipolygon (done above) >::apply(boost::begin(multipolygon), - boost::end(multipolygon)) ) + boost::end(multipolygon), + per_polygon<VisitPolicy>(visitor))) { return false; } @@ -224,10 +276,11 @@ public: typedef has_valid_self_turns<MultiPolygon> has_valid_turns; std::deque<typename has_valid_turns::turn_type> turns; - bool has_invalid_turns = !has_valid_turns::apply(multipolygon, turns); + bool has_invalid_turns = + ! has_valid_turns::apply(multipolygon, turns, visitor); debug_print_turns(turns.begin(), turns.end()); - if ( has_invalid_turns ) + if (has_invalid_turns) { return false; } @@ -237,10 +290,11 @@ public: // exterior and not one inside the other debug_phase::apply(3); - if ( !have_holes_inside(boost::begin(multipolygon), + if (! have_holes_inside(boost::begin(multipolygon), boost::end(multipolygon), turns.begin(), - turns.end()) ) + turns.end(), + visitor)) { return false; } @@ -249,10 +303,11 @@ public: // check that each polygon's interior is connected debug_phase::apply(4); - if ( !have_connected_interior(boost::begin(multipolygon), + if (! have_connected_interior(boost::begin(multipolygon), boost::end(multipolygon), turns.begin(), - turns.end()) ) + turns.end(), + visitor)) { return false; } @@ -263,7 +318,8 @@ public: return are_polygon_interiors_disjoint(boost::begin(multipolygon), boost::end(multipolygon), turns.begin(), - turns.end()); + turns.end(), + visitor); } }; @@ -282,9 +338,14 @@ namespace dispatch // that the MultiPolygon is also valid. // // Reference (for validity of MultiPolygons): OGC 06-103r4 (6.1.14) -template <typename MultiPolygon, bool AllowSpikes, bool AllowDuplicates> -struct is_valid<MultiPolygon, multi_polygon_tag, AllowSpikes, AllowDuplicates> - : detail::is_valid::is_valid_multipolygon<MultiPolygon, AllowDuplicates> +template <typename MultiPolygon, bool AllowEmptyMultiGeometries> +struct is_valid + < + MultiPolygon, multi_polygon_tag, AllowEmptyMultiGeometries + > : detail::is_valid::is_valid_multipolygon + < + MultiPolygon, AllowEmptyMultiGeometries + > {}; diff --git a/boost/geometry/algorithms/detail/is_valid/pointlike.hpp b/boost/geometry/algorithms/detail/is_valid/pointlike.hpp index 8a4818ef15..8e5ebaadcc 100644 --- a/boost/geometry/algorithms/detail/is_valid/pointlike.hpp +++ b/boost/geometry/algorithms/detail/is_valid/pointlike.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -14,6 +14,7 @@ #include <boost/geometry/core/tags.hpp> +#include <boost/geometry/algorithms/validity_failure_type.hpp> #include <boost/geometry/algorithms/dispatch/is_valid.hpp> @@ -30,9 +31,10 @@ namespace dispatch template <typename Point> struct is_valid<Point, point_tag> { - static inline bool apply(Point const&) + template <typename VisitPolicy> + static inline bool apply(Point const&, VisitPolicy& visitor) { - return true; + return visitor.template apply<no_failure>(); } }; @@ -42,12 +44,24 @@ struct is_valid<Point, point_tag> // (have identical coordinate values in X and Y) // // Reference: OGC 06-103r4 (6.1.5) -template <typename MultiPoint> -struct is_valid<MultiPoint, multi_point_tag> +template <typename MultiPoint, bool AllowEmptyMultiGeometries> +struct is_valid<MultiPoint, multi_point_tag, AllowEmptyMultiGeometries> { - static inline bool apply(MultiPoint const& multipoint) + template <typename VisitPolicy> + static inline bool apply(MultiPoint const& multipoint, + VisitPolicy& visitor) { - return boost::size(multipoint) > 0; + if (AllowEmptyMultiGeometries || boost::size(multipoint) > 0) + { + // we allow empty multi-geometries, so an empty multipoint + // is considered valid + return visitor.template apply<no_failure>(); + } + else + { + // we do not allow an empty multipoint + return visitor.template apply<failure_few_points>(); + } } }; diff --git a/boost/geometry/algorithms/detail/is_valid/polygon.hpp b/boost/geometry/algorithms/detail/is_valid/polygon.hpp index 3a91999208..17eefd226f 100644 --- a/boost/geometry/algorithms/detail/is_valid/polygon.hpp +++ b/boost/geometry/algorithms/detail/is_valid/polygon.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -26,6 +26,7 @@ #include <boost/geometry/core/ring_type.hpp> #include <boost/geometry/core/tags.hpp> +#include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/range.hpp> #include <boost/geometry/geometries/box.hpp> @@ -36,6 +37,7 @@ #include <boost/geometry/algorithms/disjoint.hpp> #include <boost/geometry/algorithms/expand.hpp> #include <boost/geometry/algorithms/num_interior_rings.hpp> +#include <boost/geometry/algorithms/validity_failure_type.hpp> #include <boost/geometry/algorithms/within.hpp> #include <boost/geometry/algorithms/detail/check_iterator_range.hpp> @@ -62,51 +64,58 @@ namespace detail { namespace is_valid { -template -< - typename Polygon, - bool AllowDuplicates, - bool CheckRingValidityOnly = false -> +template <typename Polygon, bool CheckRingValidityOnly = false> class is_valid_polygon { protected: typedef debug_validity_phase<Polygon> debug_phase; + template <typename VisitPolicy> + struct per_ring + { + per_ring(VisitPolicy& policy) : m_policy(policy) {} + + template <typename Ring> + inline bool apply(Ring const& ring) const + { + return detail::is_valid::is_valid_ring + < + Ring, false, true + >::apply(ring, m_policy); + } + VisitPolicy& m_policy; + }; - template <typename InteriorRings> - static bool has_valid_interior_rings(InteriorRings const& interior_rings) + template <typename InteriorRings, typename VisitPolicy> + static bool has_valid_interior_rings(InteriorRings const& interior_rings, + VisitPolicy& visitor) { return detail::check_iterator_range < - detail::is_valid::is_valid_ring - < - typename boost::range_value<InteriorRings>::type, - AllowDuplicates, - false, // do not check self-intersections - true // indicate that the ring is interior - > + per_ring<VisitPolicy>, + true // allow for empty interior ring range >::apply(boost::begin(interior_rings), - boost::end(interior_rings)); + boost::end(interior_rings), + per_ring<VisitPolicy>(visitor)); } struct has_valid_rings { - static inline bool apply(Polygon const& polygon) + template <typename VisitPolicy> + static inline bool apply(Polygon const& polygon, VisitPolicy& visitor) { typedef typename ring_type<Polygon>::type ring_type; // check validity of exterior ring debug_phase::apply(1); - if ( !detail::is_valid::is_valid_ring + if (! detail::is_valid::is_valid_ring < ring_type, - AllowDuplicates, false // do not check self intersections - >::apply(exterior_ring(polygon)) ) + >::apply(exterior_ring(polygon), visitor)) { return false; } @@ -114,12 +123,13 @@ protected: // check validity of interior rings debug_phase::apply(2); - return has_valid_interior_rings(geometry::interior_rings(polygon)); + return has_valid_interior_rings(geometry::interior_rings(polygon), + visitor); } }; - // structs from partition -- start + // structs for partition -- start struct expand_box { template <typename Box, typename Iterator> @@ -135,24 +145,24 @@ protected: template <typename Box, typename Iterator> static inline bool apply(Box const& box, Iterator const& it) { - return !geometry::disjoint(*it, box); + return ! geometry::disjoint(*it, box); } }; - struct item_visitor + struct item_visitor_type { bool items_overlap; - item_visitor() : items_overlap(false) {} + item_visitor_type() : items_overlap(false) {} template <typename Item1, typename Item2> inline void apply(Item1 const& item1, Item2 const& item2) { - if ( !items_overlap - && (geometry::within(*points_begin(*item1), *item2) - || geometry::within(*points_begin(*item2), *item1)) - ) + if (! items_overlap + && (geometry::within(*points_begin(*item1), *item2) + || geometry::within(*points_begin(*item2), *item1)) + ) { items_overlap = true; } @@ -165,27 +175,29 @@ protected: < typename RingIterator, typename ExteriorRing, - typename TurnIterator + typename TurnIterator, + typename VisitPolicy > static inline bool are_holes_inside(RingIterator rings_first, RingIterator rings_beyond, ExteriorRing const& exterior_ring, TurnIterator turns_first, - TurnIterator turns_beyond) + TurnIterator turns_beyond, + VisitPolicy& visitor) { // collect the interior ring indices that have turns with the // exterior ring std::set<signed_index_type> ring_indices; for (TurnIterator tit = turns_first; tit != turns_beyond; ++tit) { - if ( tit->operations[0].seg_id.ring_index == -1 ) + if (tit->operations[0].seg_id.ring_index == -1) { - BOOST_ASSERT( tit->operations[1].seg_id.ring_index != -1 ); + BOOST_ASSERT(tit->operations[1].seg_id.ring_index != -1); ring_indices.insert(tit->operations[1].seg_id.ring_index); } - else if ( tit->operations[1].seg_id.ring_index == -1 ) + else if (tit->operations[1].seg_id.ring_index == -1) { - BOOST_ASSERT( tit->operations[0].seg_id.ring_index != -1 ); + BOOST_ASSERT(tit->operations[0].seg_id.ring_index != -1); ring_indices.insert(tit->operations[0].seg_id.ring_index); } } @@ -196,10 +208,10 @@ protected: { // do not examine interior rings that have turns with the // exterior ring - if ( ring_indices.find(ring_index) == ring_indices.end() - && !geometry::covered_by(range::front(*it), exterior_ring) ) + if (ring_indices.find(ring_index) == ring_indices.end() + && ! geometry::covered_by(range::front(*it), exterior_ring)) { - return false; + return visitor.template apply<failure_interior_rings_outside>(); } } @@ -216,7 +228,7 @@ protected: for (RingIterator it = rings_first; it != rings_beyond; ++it, ++ring_index) { - if ( ring_indices.find(ring_index) == ring_indices.end() ) + if (ring_indices.find(ring_index) == ring_indices.end()) { ring_iterators.push_back(it); } @@ -224,47 +236,59 @@ protected: // call partition to check is interior rings are disjoint from // each other - item_visitor visitor; + item_visitor_type item_visitor; geometry::partition < geometry::model::box<typename point_type<Polygon>::type>, expand_box, overlaps_box - >::apply(ring_iterators, visitor); + >::apply(ring_iterators, item_visitor); - return !visitor.items_overlap; + if (item_visitor.items_overlap) + { + return visitor.template apply<failure_nested_interior_rings>(); + } + else + { + return visitor.template apply<no_failure>(); + } } template < typename InteriorRings, typename ExteriorRing, - typename TurnIterator + typename TurnIterator, + typename VisitPolicy > static inline bool are_holes_inside(InteriorRings const& interior_rings, ExteriorRing const& exterior_ring, TurnIterator first, - TurnIterator beyond) + TurnIterator beyond, + VisitPolicy& visitor) { return are_holes_inside(boost::begin(interior_rings), boost::end(interior_rings), exterior_ring, first, - beyond); + beyond, + visitor); } struct has_holes_inside { - template <typename TurnIterator> + template <typename TurnIterator, typename VisitPolicy> static inline bool apply(Polygon const& polygon, TurnIterator first, - TurnIterator beyond) + TurnIterator beyond, + VisitPolicy& visitor) { return are_holes_inside(geometry::interior_rings(polygon), geometry::exterior_ring(polygon), first, - beyond); + beyond, + visitor); } }; @@ -273,10 +297,11 @@ protected: struct has_connected_interior { - template <typename TurnIterator> + template <typename TurnIterator, typename VisitPolicy> static inline bool apply(Polygon const& polygon, TurnIterator first, - TurnIterator beyond) + TurnIterator beyond, + VisitPolicy& visitor) { typedef typename std::iterator_traits < @@ -299,19 +324,27 @@ protected: debug_print_complement_graph(std::cout, g); - return !g.has_cycles(); + if (g.has_cycles()) + { + return visitor.template apply<failure_disconnected_interior>(); + } + else + { + return visitor.template apply<no_failure>(); + } } }; public: - static inline bool apply(Polygon const& polygon) + template <typename VisitPolicy> + static inline bool apply(Polygon const& polygon, VisitPolicy& visitor) { - if ( !has_valid_rings::apply(polygon) ) + if (! has_valid_rings::apply(polygon, visitor)) { return false; } - if ( CheckRingValidityOnly ) + if (BOOST_GEOMETRY_CONDITION(CheckRingValidityOnly)) { return true; } @@ -322,10 +355,11 @@ public: typedef has_valid_self_turns<Polygon> has_valid_turns; std::deque<typename has_valid_turns::turn_type> turns; - bool has_invalid_turns = !has_valid_turns::apply(polygon, turns); + bool has_invalid_turns + = ! has_valid_turns::apply(polygon, turns, visitor); debug_print_turns(turns.begin(), turns.end()); - if ( has_invalid_turns ) + if (has_invalid_turns) { return false; } @@ -333,7 +367,9 @@ public: // check if all interior rings are inside the exterior ring debug_phase::apply(4); - if ( !has_holes_inside::apply(polygon, turns.begin(), turns.end()) ) + if (! has_holes_inside::apply(polygon, + turns.begin(), turns.end(), + visitor)) { return false; } @@ -343,7 +379,8 @@ public: return has_connected_interior::apply(polygon, turns.begin(), - turns.end()); + turns.end(), + visitor); } }; @@ -361,9 +398,11 @@ namespace dispatch // A Polygon is always a simple geometric object provided that it is valid. // // Reference (for validity of Polygons): OGC 06-103r4 (6.1.11.1) -template <typename Polygon, bool AllowSpikes, bool AllowDuplicates> -struct is_valid<Polygon, polygon_tag, AllowSpikes, AllowDuplicates> - : detail::is_valid::is_valid_polygon<Polygon, AllowDuplicates> +template <typename Polygon, bool AllowEmptyMultiGeometries> +struct is_valid + < + Polygon, polygon_tag, AllowEmptyMultiGeometries + > : detail::is_valid::is_valid_polygon<Polygon> {}; diff --git a/boost/geometry/algorithms/detail/is_valid/ring.hpp b/boost/geometry/algorithms/detail/is_valid/ring.hpp index c88df79b05..c663a96d28 100644 --- a/boost/geometry/algorithms/detail/is_valid/ring.hpp +++ b/boost/geometry/algorithms/detail/is_valid/ring.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -10,6 +10,8 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_RING_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_RING_HPP +#include <deque> + #include <boost/geometry/core/closure.hpp> #include <boost/geometry/core/cs.hpp> #include <boost/geometry/core/point_order.hpp> @@ -19,16 +21,22 @@ #include <boost/geometry/algorithms/equals.hpp> -#include <boost/geometry/views/reversible_view.hpp> #include <boost/geometry/views/closeable_view.hpp> #include <boost/geometry/algorithms/area.hpp> #include <boost/geometry/algorithms/intersects.hpp> +#include <boost/geometry/algorithms/validity_failure_type.hpp> +#include <boost/geometry/algorithms/detail/num_distinct_consecutive_points.hpp> #include <boost/geometry/algorithms/detail/is_valid/has_spikes.hpp> #include <boost/geometry/algorithms/detail/is_valid/has_duplicates.hpp> +#include <boost/geometry/algorithms/detail/is_valid/has_valid_self_turns.hpp> #include <boost/geometry/strategies/area.hpp> +#ifdef BOOST_GEOMETRY_TEST_DEBUG +#include <boost/geometry/io/dsv/write.hpp> +#endif + namespace boost { namespace geometry { @@ -42,18 +50,27 @@ namespace detail { namespace is_valid template <typename Ring, closure_selector Closure /* open */> struct is_topologically_closed { - static inline bool apply(Ring const&) + template <typename VisitPolicy> + static inline bool apply(Ring const&, VisitPolicy& visitor) { - return true; + return visitor.template apply<no_failure>(); } }; template <typename Ring> struct is_topologically_closed<Ring, closed> { - static inline bool apply(Ring const& ring) + template <typename VisitPolicy> + static inline bool apply(Ring const& ring, VisitPolicy& visitor) { - return geometry::equals(range::front(ring), range::back(ring)); + if (geometry::equals(range::front(ring), range::back(ring))) + { + return visitor.template apply<no_failure>(); + } + else + { + return visitor.template apply<failure_not_closed>(); + } } }; @@ -92,7 +109,8 @@ struct is_properly_oriented typedef typename default_area_result<Ring>::type area_result_type; - static inline bool apply(Ring const& ring) + template <typename VisitPolicy> + static inline bool apply(Ring const& ring, VisitPolicy& visitor) { typename ring_area_predicate < @@ -101,7 +119,14 @@ struct is_properly_oriented // Check area area_result_type const zero = area_result_type(); - return predicate(ring_area_type::apply(ring, strategy_type()), zero); + if (predicate(ring_area_type::apply(ring, strategy_type()), zero)) + { + return visitor.template apply<no_failure>(); + } + else + { + return visitor.template apply<failure_wrong_orientation>(); + } } }; @@ -110,41 +135,60 @@ struct is_properly_oriented template < typename Ring, - bool AllowDuplicates, bool CheckSelfIntersections = true, bool IsInteriorRing = false > struct is_valid_ring { - static inline bool apply(Ring const& ring) + template <typename VisitPolicy> + static inline bool apply(Ring const& ring, VisitPolicy& visitor) { // return invalid if any of the following condition holds: // (a) the ring's size is below the minimal one - // (b) the ring is not topologically closed - // (c) the ring has spikes - // (d) the ring has duplicate points (if AllowDuplicates is false) - // (e) the boundary of the ring has self-intersections - // (f) the order of the points is inconsistent with the defined order + // (b) the ring consists of at most two distinct points + // (c) the ring is not topologically closed + // (d) the ring has spikes + // (e) the ring has duplicate points (if AllowDuplicates is false) + // (f) the boundary of the ring has self-intersections + // (g) the order of the points is inconsistent with the defined order // // Note: no need to check if the area is zero. If this is the // case, then the ring must have at least two spikes, which is // checked by condition (c). closure_selector const closure = geometry::closure<Ring>::value; + typedef typename closeable_view<Ring const, closure>::type view_type; + + if (boost::size(ring) + < core_detail::closure::minimum_ring_size<closure>::value) + { + return visitor.template apply<failure_few_points>(); + } + + view_type const view(ring); + if (detail::num_distinct_consecutive_points + < + view_type, 4u, true, + not_equal_to<typename point_type<Ring>::type> + >::apply(view) + < 4u) + { + return + visitor.template apply<failure_wrong_topological_dimension>(); + } return - ( boost::size(ring) - >= core_detail::closure::minimum_ring_size<closure>::value ) - && is_topologically_closed<Ring, closure>::apply(ring) - && (AllowDuplicates || !has_duplicates<Ring, closure>::apply(ring)) - && !has_spikes<Ring, closure>::apply(ring) - && !(CheckSelfIntersections && geometry::intersects(ring)) - && is_properly_oriented<Ring, IsInteriorRing>::apply(ring); + is_topologically_closed<Ring, closure>::apply(ring, visitor) + && ! has_duplicates<Ring, closure>::apply(ring, visitor) + && ! has_spikes<Ring, closure>::apply(ring, visitor) + && (! CheckSelfIntersections + || has_valid_self_turns<Ring>::apply(ring, visitor)) + && is_properly_oriented<Ring, IsInteriorRing>::apply(ring, visitor); } }; -}} // namespace dispatch +}} // namespace detail::is_valid #endif // DOXYGEN_NO_DETAIL @@ -158,9 +202,11 @@ namespace dispatch // 6.1.7.1, for the definition of LinearRing) // // Reference (for polygon validity): OGC 06-103r4 (6.1.11.1) -template <typename Ring, bool AllowSpikes, bool AllowDuplicates> -struct is_valid<Ring, ring_tag, AllowSpikes, AllowDuplicates> - : detail::is_valid::is_valid_ring<Ring, AllowDuplicates> +template <typename Ring, bool AllowEmptyMultiGeometries> +struct is_valid + < + Ring, ring_tag, AllowEmptyMultiGeometries + > : detail::is_valid::is_valid_ring<Ring> {}; diff --git a/boost/geometry/algorithms/detail/is_valid/segment.hpp b/boost/geometry/algorithms/detail/is_valid/segment.hpp index 486289dabe..0b60890dc0 100644 --- a/boost/geometry/algorithms/detail/is_valid/segment.hpp +++ b/boost/geometry/algorithms/detail/is_valid/segment.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2014, Oracle and/or its affiliates. +// Copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle @@ -15,6 +15,7 @@ #include <boost/geometry/algorithms/assign.hpp> #include <boost/geometry/algorithms/equals.hpp> +#include <boost/geometry/algorithms/validity_failure_type.hpp> #include <boost/geometry/algorithms/dispatch/is_valid.hpp> @@ -40,13 +41,22 @@ namespace dispatch template <typename Segment> struct is_valid<Segment, segment_tag> { - static inline bool apply(Segment const& segment) + template <typename VisitPolicy> + static inline bool apply(Segment const& segment, VisitPolicy& visitor) { typename point_type<Segment>::type p[2]; detail::assign_point_from_index<0>(segment, p[0]); detail::assign_point_from_index<1>(segment, p[1]); - return !geometry::equals(p[0], p[1]); + if(! geometry::equals(p[0], p[1])) + { + return visitor.template apply<no_failure>(); + } + else + { + return + visitor.template apply<failure_wrong_topological_dimension>(); + } } }; diff --git a/boost/geometry/algorithms/detail/not.hpp b/boost/geometry/algorithms/detail/not.hpp index abc3a4e168..43e71e2e37 100644 --- a/boost/geometry/algorithms/detail/not.hpp +++ b/boost/geometry/algorithms/detail/not.hpp @@ -1,8 +1,13 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. -// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2015. +// Modifications copyright (c) 2015, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -32,10 +37,12 @@ namespace detail \param geometry2 \param_geometry \return Negation of the result of the policy */ -template <typename Geometry1, typename Geometry2, typename Policy> +template <typename Policy> struct not_ { - static inline bool apply(Geometry1 const &geometry1, Geometry2 const& geometry2) + template <typename Geometry1, typename Geometry2> + static inline bool apply(Geometry1 const& geometry1, + Geometry2 const& geometry2) { return ! Policy::apply(geometry1, geometry2); } diff --git a/boost/geometry/algorithms/detail/occupation_info.hpp b/boost/geometry/algorithms/detail/occupation_info.hpp index d90f3cf7cf..002c946170 100644 --- a/boost/geometry/algorithms/detail/occupation_info.hpp +++ b/boost/geometry/algorithms/detail/occupation_info.hpp @@ -57,6 +57,12 @@ class occupation_info public : typedef std::vector<AngleInfo> collection_type; + int count; + + inline occupation_info() + : count(0) + {} + template <typename RobustPoint> inline void add(RobustPoint const& incoming_point, RobustPoint const& outgoing_point, diff --git a/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp b/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp index d44db17ad3..285edfdd6c 100644 --- a/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp +++ b/boost/geometry/algorithms/detail/overlay/append_no_dups_or_spikes.hpp @@ -20,6 +20,7 @@ #include <boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp> #include <boost/geometry/algorithms/detail/equals/point_point.hpp> +#include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/range.hpp> @@ -42,7 +43,7 @@ inline bool points_equal_or_close(Point1 const& point1, return true; } - if (! RobustPolicy::enabled) + if (BOOST_GEOMETRY_CONDITION(! RobustPolicy::enabled)) { return false; } @@ -127,7 +128,7 @@ inline void clean_closing_dups_and_spikes(Range& range, iterator_type first = boost::begin(range); iterator_type second = first + 1; iterator_type ultimate = boost::end(range) - 1; - if (closed) + if (BOOST_GEOMETRY_CONDITION(closed)) { ultimate--; } @@ -137,7 +138,7 @@ inline void clean_closing_dups_and_spikes(Range& range, if (point_is_spike_or_equal(*second, *ultimate, *first, robust_policy)) { range::erase(range, first); - if (closed) + if (BOOST_GEOMETRY_CONDITION(closed)) { // Remove closing last point range::resize(range, boost::size(range) - 1); diff --git a/boost/geometry/algorithms/detail/overlay/assign_parents.hpp b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp index 67b48cc471..178f3825d7 100644 --- a/boost/geometry/algorithms/detail/overlay/assign_parents.hpp +++ b/boost/geometry/algorithms/detail/overlay/assign_parents.hpp @@ -18,11 +18,6 @@ #include <boost/geometry/geometries/box.hpp> -#ifdef BOOST_GEOMETRY_TIME_OVERLAY -# include <boost/timer.hpp> -#endif - - namespace boost { namespace geometry { @@ -186,11 +181,6 @@ inline void assign_parents(Geometry1 const& geometry1, typedef std::vector<helper> vector_type; typedef typename boost::range_iterator<vector_type const>::type vector_iterator_type; -#ifdef BOOST_GEOMETRY_TIME_OVERLAY - boost::timer timer; -#endif - - std::size_t count_total = ring_map.size(); std::size_t count_positive = 0; std::size_t index_positive = 0; // only used if count_positive>0 @@ -226,10 +216,6 @@ inline void assign_parents(Geometry1 const& geometry1, } } -#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) @@ -272,11 +258,6 @@ inline void assign_parents(Geometry1 const& geometry1, < 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) diff --git a/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp index 20a6d7f48d..13e0a5a51e 100644 --- a/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp +++ b/boost/geometry/algorithms/detail/overlay/copy_segment_point.hpp @@ -11,6 +11,7 @@ #include <boost/array.hpp> +#include <boost/assert.hpp> #include <boost/mpl/assert.hpp> #include <boost/range.hpp> @@ -21,8 +22,7 @@ #include <boost/geometry/algorithms/convert.hpp> #include <boost/geometry/geometries/concepts/check.hpp> #include <boost/geometry/util/range.hpp> -#include <boost/geometry/views/closeable_view.hpp> -#include <boost/geometry/views/reversible_view.hpp> +#include <boost/geometry/views/detail/normalized_view.hpp> namespace boost { namespace geometry @@ -37,41 +37,24 @@ namespace detail { namespace copy_segments template <typename Range, bool Reverse, typename SegmentIdentifier, typename PointOut> struct copy_segment_point_range { - typedef typename closeable_view - < - Range const, - closure<Range>::value - >::type cview_type; - - typedef typename reversible_view - < - cview_type const, - Reverse ? iterate_reverse : iterate_forward - >::type rview_type; - static inline bool apply(Range const& range, SegmentIdentifier const& seg_id, bool second, PointOut& point) { + detail::normalized_view<Range const> view(range); + + signed_index_type const n = boost::size(view); signed_index_type index = seg_id.segment_index; if (second) { index++; - if (index >= int(boost::size(range))) + if (index >= n) { index = 0; } } - // Exception? - if (index >= int(boost::size(range))) - { - return false; - } - - cview_type cview(range); - rview_type view(cview); - + BOOST_ASSERT(index >= 0 && index < n); geometry::convert(*(boost::begin(view) + index), point); return true; @@ -323,6 +306,8 @@ inline bool copy_segment_point(Geometry1 const& geometry1, Geometry2 const& geom concept::check<Geometry1 const>(); concept::check<Geometry2 const>(); + BOOST_ASSERT(seg_id.source_index == 0 || seg_id.source_index == 1); + if (seg_id.source_index == 0) { return dispatch::copy_segment_point diff --git a/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp index 9484479b45..7ed93f542a 100644 --- a/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp +++ b/boost/geometry/algorithms/detail/overlay/enrich_intersection_points.hpp @@ -56,13 +56,13 @@ struct indexed_turn_operation TurnOperation const* subject; inline indexed_turn_operation(std::size_t ti, std::size_t oi, - TurnOperation const& s, + TurnOperation const& sub, segment_identifier const& oid) : turn_index(ti) , operation_index(oi) , discarded(false) , other_seg_id(&oid) - , subject(&s) + , subject(boost::addressof(sub)) {} }; @@ -114,6 +114,12 @@ private : typedef typename geometry::point_type<Geometry1>::type point_type; + inline bool default_order(Indexed const& left, Indexed const& right) const + { + // We've nothing to sort on. Take the indexes + return left.turn_index < right.turn_index; + } + inline bool consider_relative_order(Indexed const& left, Indexed const& right) const { @@ -148,7 +154,12 @@ private : // If they both turn left: the most left as last // If they both turn right: this is not relevant, but take also here most left - return side_rj_s < side_sj_r; + if (side_rj_s != side_sj_r) + { + return side_rj_s < side_sj_r; + } + + return default_order(left, right); } public : @@ -157,30 +168,32 @@ public : // 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 (! (left.subject->seg_id == right.subject->seg_id)) + { + return left.subject->seg_id < right.subject->seg_id; + } + + // Both left and right are located on the SAME segment. - if (sl == sr) + if (! (left.subject->fraction == right.subject->fraction)) { - // Both left and right are located on the SAME segment. - if (left.subject->fraction == right.subject->fraction) - { - // First check "real" intersection (crosses) - // -> distance zero due to precision, solve it by sorting - if (m_turn_points[left.turn_index].method == method_crosses - && m_turn_points[right.turn_index].method == method_crosses) - { - return consider_relative_order(left, right); - } + return left.subject->fraction < right.subject->fraction; + } - // If that is not the case, cluster it later on. - // Indicate that this is necessary. - *m_clustered = true; - } + + // First check "real" intersection (crosses) + // -> distance zero due to precision, solve it by sorting + if (m_turn_points[left.turn_index].method == method_crosses + && m_turn_points[right.turn_index].method == method_crosses) + { + return consider_relative_order(left, right); } - return sl == sr - ? left.subject->fraction < right.subject->fraction - : sl < sr; + + // If that is not the case, cluster it later on. + // Indicate that this is necessary. + *m_clustered = true; + + return default_order(left, right); } }; diff --git a/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp b/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp index 85378e08b0..7bcc0b951e 100644 --- a/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp +++ b/boost/geometry/algorithms/detail/overlay/follow_linear_linear.hpp @@ -35,6 +35,23 @@ namespace boost { namespace geometry { +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) +class inconsistent_turns_exception : public geometry::exception +{ +public: + + inline inconsistent_turns_exception() {} + + virtual ~inconsistent_turns_exception() throw() + {} + + virtual char const* what() const throw() + { + return "Boost.Geometry Inconsistent Turns exception"; + } +}; +#endif + #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace overlay @@ -304,7 +321,14 @@ public: oit); } - BOOST_ASSERT( enter_count == 0 ); +#if ! defined(BOOST_GEOMETRY_OVERLAY_NO_THROW) + if (enter_count != 0) + { + throw inconsistent_turns_exception(); + } +#else + BOOST_ASSERT(enter_count == 0); +#endif return process_end(entered, linestring, current_segment_id, current_piece, diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp index 240b6de036..b3b1a06f68 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info.hpp @@ -2,6 +2,11 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// This file was modified by Oracle on 2015. +// Modifications copyright (c) 2015 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) @@ -11,6 +16,8 @@ #include <boost/assert.hpp> +#include <boost/core/ignore_unused.hpp> + #include <boost/geometry/core/access.hpp> #include <boost/geometry/strategies/intersection.hpp> @@ -104,17 +111,19 @@ struct base_turn_handler template <typename TurnInfo, typename IntersectionInfo> static inline void assign_point(TurnInfo& ti, method_type method, - IntersectionInfo const& info, int index) + IntersectionInfo const& info, unsigned int index) { ti.method = method; - BOOST_ASSERT(index >= 0 && unsigned(index) < info.count); // TODO remove this + + BOOST_ASSERT(index < info.count); + geometry::convert(info.intersections[index], ti.point); ti.operations[0].fraction = info.fractions[index].robust_ra; ti.operations[1].fraction = info.fractions[index].robust_rb; } template <typename IntersectionInfo> - static inline int non_opposite_to_index(IntersectionInfo const& info) + static inline unsigned int non_opposite_to_index(IntersectionInfo const& info) { return info.fractions[0].robust_rb < info.fractions[1].robust_rb ? 1 : 0; @@ -132,7 +141,7 @@ struct touch_interior : public base_turn_handler // Index: 0, P is the interior, Q is touching and vice versa template < - int Index, + unsigned int Index, typename Point1, typename Point2, typename IntersectionInfo, @@ -155,8 +164,9 @@ struct touch_interior : public base_turn_handler // 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; + BOOST_STATIC_ASSERT(Index <= 1); + static unsigned int const index_p = Index; + static unsigned int const index_q = 1 - Index; int const side_qi_p = dir_info.sides.template get<index_q, 0>(); int const side_qk_p = side.qk_wrt_p1(); @@ -166,7 +176,7 @@ struct touch_interior : public base_turn_handler // 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; + unsigned int index = side_qk_p == -1 ? index_p : index_q; ti.operations[index].operation = operation_union; ti.operations[1 - index].operation = operation_intersection; return; @@ -193,7 +203,7 @@ struct touch_interior : public base_turn_handler // 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; + unsigned int index = side_qk_q == 1 ? index_q : index_p; ti.operations[index].operation = operation_union; ti.operations[1 - index].operation = operation_intersection; } @@ -215,10 +225,10 @@ struct touch_interior : public base_turn_handler // 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 + ti.operations[index_p].operation = side_qk_q == 1 ? operation_intersection : operation_union; - ti.operations[1 - Index].operation = operation_blocked; + ti.operations[index_q].operation = operation_blocked; } } else @@ -503,27 +513,25 @@ struct equal_opposite : public base_turn_handler typename Point1, typename Point2, typename OutputIterator, - typename IntersectionInfo, - typename DirInfo + typename IntersectionInfo > static inline void apply(Point1 const& pi, Point2 const& qi, /* by value: */ TurnInfo tp, OutputIterator& out, - IntersectionInfo const& intersection_info, - DirInfo const& dir_info) + IntersectionInfo const& intersection_info) { // For equal-opposite segments, normally don't do anything. if (AssignPolicy::include_opposite) { tp.method = method_equal; - for (int i = 0; i < 2; i++) + for (unsigned int i = 0; i < 2; i++) { tp.operations[i].operation = operation_opposite; } - for (unsigned int i = 0; i < intersection_info.count; i++) + for (unsigned int i = 0; i < intersection_info.i_info().count; i++) { - assign_point(tp, method_none, intersection_info, i); - AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); + assign_point(tp, method_none, intersection_info.i_info(), i); + AssignPolicy::apply(tp, pi, qi, intersection_info); *out++ = tp; } } @@ -653,7 +661,7 @@ private : template < - int Index, + unsigned int Index, typename Point1, typename Point2, typename IntersectionInfo @@ -663,8 +671,9 @@ private : Point2 const& , Point2 const& , int side_rk_s, TurnInfo& tp, IntersectionInfo const& intersection_info) { - boost::ignore_unused_variable_warning(handle_robustness); - boost::ignore_unused_variable_warning(side_rk_s); + BOOST_STATIC_ASSERT(Index <= 1); + + boost::ignore_unused(handle_robustness, side_rk_s); operation_type blocked = operation_blocked; switch(side_rk_r) @@ -715,7 +724,6 @@ public: typename Point2, typename OutputIterator, typename IntersectionInfo, - typename DirInfo, typename SidePolicy > static inline void apply( @@ -727,10 +735,9 @@ public: OutputIterator& out, IntersectionInfo const& intersection_info, - DirInfo const& dir_info, SidePolicy const& side) { - apply(pi, pj, pk, qi, qj, qk, tp_model, out, intersection_info, dir_info, side, empty_transformer); + apply(pi, pj, pk, qi, qj, qk, tp_model, out, intersection_info, side, empty_transformer); } public: @@ -740,7 +747,6 @@ public: typename Point2, typename OutputIterator, typename IntersectionInfo, - typename DirInfo, typename SidePolicy, typename TurnTransformer > @@ -752,50 +758,52 @@ public: TurnInfo const& tp_model, OutputIterator& out, - IntersectionInfo const& intersection_info, - DirInfo const& dir_info, + IntersectionInfo const& info, SidePolicy const& side, TurnTransformer turn_transformer, bool const is_pk_valid = true, bool const is_qk_valid = true) { TurnInfo tp = tp_model; + int const p_arrival = info.d_info().arrival[0]; + int const q_arrival = info.d_info().arrival[1]; + // If P arrives within Q, there is a turn dependent on P - if ( dir_info.arrival[0] == 1 + if ( p_arrival == 1 && is_pk_valid - && set_tp<0>(pi, pj, pk, side.pk_wrt_p1(), true, qi, qj, side.pk_wrt_q1(), tp, intersection_info) ) + && set_tp<0>(pi, pj, pk, side.pk_wrt_p1(), true, qi, qj, side.pk_wrt_q1(), tp, info.i_info()) ) { turn_transformer(tp); - AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); + AssignPolicy::apply(tp, pi, qi, info); *out++ = tp; } // If Q arrives within P, there is a turn dependent on Q - if ( dir_info.arrival[1] == 1 + if ( q_arrival == 1 && is_qk_valid - && set_tp<1>(qi, qj, qk, side.qk_wrt_q1(), false, pi, pj, side.qk_wrt_p1(), tp, intersection_info) ) + && set_tp<1>(qi, qj, qk, side.qk_wrt_q1(), false, pi, pj, side.qk_wrt_p1(), tp, info.i_info()) ) { turn_transformer(tp); - AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); + AssignPolicy::apply(tp, pi, qi, info); *out++ = tp; } if (AssignPolicy::include_opposite) { // Handle cases not yet handled above - if ((dir_info.arrival[1] == -1 && dir_info.arrival[0] == 0) - || (dir_info.arrival[0] == -1 && dir_info.arrival[1] == 0)) + if ((q_arrival == -1 && p_arrival == 0) + || (p_arrival == -1 && q_arrival == 0)) { - for (int i = 0; i < 2; i++) + for (unsigned int i = 0; i < 2; i++) { tp.operations[i].operation = operation_opposite; } - for (unsigned int i = 0; i < intersection_info.count; i++) + for (unsigned int i = 0; i < info.i_info().count; i++) { - assign_point(tp, method_collinear, intersection_info, i); - AssignPolicy::apply(tp, pi, qi, intersection_info, dir_info); + assign_point(tp, method_collinear, info.i_info(), i); + AssignPolicy::apply(tp, pi, qi, info); *out++ = tp; } } @@ -833,7 +841,7 @@ struct crosses : public base_turn_handler // 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; + unsigned int const index = side_qi_p1 == 1 ? 0 : 1; ti.operations[index].operation = operation_union; ti.operations[1 - index].operation = operation_intersection; } @@ -867,10 +875,9 @@ struct assign_null_policy typename Info, typename Point1, typename Point2, - typename IntersectionInfo, - typename DirInfo + typename IntersectionInfo > - static inline void apply(Info& , Point1 const& , Point2 const&, IntersectionInfo const&, DirInfo const&) + static inline void apply(Info& , Point1 const& , Point2 const&, IntersectionInfo const&) {} }; @@ -933,7 +940,7 @@ struct get_turn_info && inters.i_info().count > 0) { only_convert::apply(tp, inters.i_info()); - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } break; @@ -968,7 +975,7 @@ struct get_turn_info tp, inters.i_info(), inters.d_info(), swapped_side_calc); } - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } break; @@ -976,7 +983,7 @@ struct get_turn_info { crosses<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, tp, inters.i_info(), inters.d_info()); - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } break; @@ -985,7 +992,7 @@ struct get_turn_info // Both touch (both arrive there) touch<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, tp, inters.i_info(), inters.d_info(), inters.sides()); - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } break; @@ -997,7 +1004,7 @@ struct get_turn_info // or collinear-and-ending at intersection point equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, tp, inters.i_info(), inters.d_info(), inters.sides()); - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } else @@ -1007,7 +1014,7 @@ struct get_turn_info TurnInfo, AssignPolicy >::apply(pi, qi, - tp, out, inters.i_info(), inters.d_info()); + tp, out, inters); } } break; @@ -1032,7 +1039,7 @@ struct get_turn_info tp, inters.i_info(), inters.d_info(), inters.sides()); } - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } else @@ -1042,7 +1049,7 @@ struct get_turn_info TurnInfo, AssignPolicy >::apply(pi, pj, pk, qi, qj, qk, - tp, out, inters.i_info(), inters.d_info(), inters.sides()); + tp, out, inters, inters.sides()); } } break; @@ -1052,7 +1059,7 @@ struct get_turn_info if (AssignPolicy::include_degenerate) { only_convert::apply(tp, inters.i_info()); - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } } diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp index ca1b9d9d0c..4a3cacbedd 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp @@ -290,7 +290,7 @@ struct get_turn_info_for_endpoint linear_intersections::ip_info const& ip_info, TurnInfo const& tp_model, IntersectionInfo const& inters, - int ip_index, + unsigned int ip_index, OutputIterator out) { #ifdef BOOST_GEOMETRY_DEBUG_GET_TURNS_LINEAR_LINEAR @@ -396,7 +396,7 @@ struct get_turn_info_for_endpoint RobustPoint2 const& ri2, RobustPoint2 const& rj2, RobustPoint2 const& rk2, bool first1, bool last1, bool first2, bool last2, bool ip_i2, bool ip_j2, TurnInfo const& tp_model, - IntersectionInfo const& inters, int ip_index, + IntersectionInfo const& inters, unsigned int ip_index, operation_type & op1, operation_type & op2) { boost::ignore_unused_variable_warning(i2); @@ -535,7 +535,7 @@ struct get_turn_info_for_endpoint typename OutputIterator> static inline void assign(Point1 const& pi, Point2 const& qi, IntersectionResult const& result, - int ip_index, + unsigned int ip_index, method_type method, operation_type op0, operation_type op1, turn_position pos0, turn_position pos1, @@ -585,7 +585,9 @@ struct get_turn_info_for_endpoint } } - AssignPolicy::apply(tp, pi, qi, result.template get<0>(), result.template get<1>()); + // TODO: this should get an intersection_info, which is unavailable here + // Because the assign_null policy accepts any structure, we pass the result instead for now + AssignPolicy::apply(tp, pi, qi, result); *out++ = tp; } diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp index eead0d719b..e0d75108b9 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_helpers.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014. -// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015. +// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp index 873567bbf5..71946543ee 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_la.hpp @@ -2,18 +2,20 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014. -// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015. +// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle - #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_GET_TURN_INFO_LA_HPP +#include <boost/geometry/util/condition.hpp> + #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> #include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp> @@ -28,6 +30,8 @@ namespace detail { namespace overlay { template<typename AssignPolicy> struct get_turn_info_linear_areal { + // Currently only Linear spikes are handled + // Areal spikes are ignored static const bool handle_spikes = true; template @@ -122,7 +126,7 @@ struct get_turn_info_linear_areal calculate_spike_operation(tp.operations[0].operation, inters, is_p_last); - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } @@ -135,7 +139,7 @@ struct get_turn_info_linear_areal replace_operations_i(tp.operations[0].operation, tp.operations[1].operation); - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } break; @@ -220,9 +224,9 @@ struct get_turn_info_linear_areal inters, is_p_last); // TODO: move this into the append_xxx and call for each turn? - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); - if ( ! handle_spikes + if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes) || ignore_spike || ! append_opposite_spikes<append_touches>( // for 'i' or 'c' i??? tp, inters, is_p_last, is_q_last, out) ) @@ -256,10 +260,10 @@ struct get_turn_info_linear_areal transformer(tp); // TODO: move this into the append_xxx and call for each turn? - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); // conditionally handle spikes - if ( ! handle_spikes + if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes) || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, method_touch, append_equal, out) ) { @@ -273,7 +277,7 @@ struct get_turn_info_linear_areal TurnInfo, AssignPolicy >::apply(pi, qi, - tp, out, inters.i_info(), inters.d_info()); + tp, out, inters); } } } @@ -319,10 +323,10 @@ struct get_turn_info_linear_areal transformer(tp); // TODO: move this into the append_xxx and call for each turn? - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); // conditionally handle spikes - if ( ! handle_spikes + if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes) || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, method_replace, version, out) ) { @@ -336,7 +340,7 @@ struct get_turn_info_linear_areal turn_transformer_ec<false> transformer(method_touch_interior); // conditionally handle spikes - if ( handle_spikes ) + if ( BOOST_GEOMETRY_CONDITION(handle_spikes) ) { append_opposite_spikes<append_collinear_opposite>( tp, inters, is_p_last, is_q_last, out); @@ -351,8 +355,9 @@ struct get_turn_info_linear_areal TurnInfo, AssignPolicy >::apply(pi, pj, pk, qi, qj, qk, - tp, out, inters.i_info(), inters.d_info(), - inters.sides(), transformer); + tp, out, inters, + inters.sides(), transformer, + !is_p_last, true); // qk is always valid } } } @@ -360,7 +365,7 @@ struct get_turn_info_linear_areal case '0' : { // degenerate points - if (AssignPolicy::include_degenerate) + if ( BOOST_GEOMETRY_CONDITION(AssignPolicy::include_degenerate) ) { only_convert::apply(tp, inters.i_info()); @@ -376,7 +381,7 @@ struct get_turn_info_linear_areal } // tp.operations[1].position = position_middle; - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } } @@ -408,20 +413,37 @@ struct get_turn_info_linear_areal if ( is_p_spike ) { - bool going_in = false, going_out = false; - int const pk_q1 = inters.sides().pk_wrt_q1(); - int const pk_q2 = inters.sides().pk_wrt_q2(); + + bool going_in = pk_q1 < 0; // Pk on the right + bool going_out = pk_q1 > 0; // Pk on the left - if ( inters.sides().qk_wrt_q1() <= 0 ) // Q turning R or C + int const qk_q1 = inters.sides().qk_wrt_q1(); + + // special cases + if ( qk_q1 < 0 ) // Q turning R { - going_in = pk_q1 < 0 && pk_q2 < 0; // Pk on the right of both - going_out = pk_q1 > 0 || pk_q2 > 0; // Pk on the left of one of them + // spike on the edge point + // if it's already known that the spike is going out this musn't be checked + if ( ! going_out + && equals::equals_point_point(inters.rpj(), inters.rqj()) ) + { + int const pk_q2 = inters.sides().pk_wrt_q2(); + going_in = pk_q1 < 0 && pk_q2 < 0; // Pk on the right of both + going_out = pk_q1 > 0 || pk_q2 > 0; // Pk on the left of one of them + } } - else + else if ( qk_q1 > 0 ) // Q turning L { - going_in = pk_q1 < 0 || pk_q2 < 0; // Pk on the right of one of them - going_out = pk_q1 > 0 && pk_q2 > 0; // Pk on the left of both + // spike on the edge point + // if it's already known that the spike is going in this musn't be checked + if ( ! going_in + && equals::equals_point_point(inters.rpj(), inters.rqj()) ) + { + int const pk_q2 = inters.sides().pk_wrt_q2(); + going_in = pk_q1 < 0 || pk_q2 < 0; // Pk on the right of one of them + going_out = pk_q1 > 0 && pk_q2 > 0; // Pk on the left of both + } } if ( going_in ) @@ -461,10 +483,16 @@ struct get_turn_info_linear_areal && inters.is_spike_p(); // TODO: throw an exception for spike in Areal? - /*bool is_q_spike = tp.operations[1].operation == spike_op - && ! is_q_last - && inters.is_spike_q();*/ + /*bool is_q_spike = tp.operations[1].operation == operation_continue + && inters.is_spike_q(); + // both are collinear spikes on the same IP, we can just follow both + if ( is_p_spike && is_q_spike ) + { + return false; + } + // spike on Linear - it's turning back on the boundary of Areal + else*/ if ( is_p_spike ) { tp.method = method; @@ -477,7 +505,18 @@ struct get_turn_info_linear_areal return true; } - + // spike on Areal - Linear is going outside + /*else if ( is_q_spike ) + { + tp.method = method; + tp.operations[0].operation = operation_union; + tp.operations[1].operation = operation_continue; + *out++ = tp; + *out++ = tp; + + return true; + }*/ + return false; } @@ -492,48 +531,71 @@ struct get_turn_info_linear_areal bool is_p_last, bool /*is_q_last*/, OutIt out) { - bool is_p_spike = ( Version == append_touches ? + static const bool is_version_touches = (Version == append_touches); + + bool is_p_spike = ( is_version_touches ? ( tp.operations[0].operation == operation_continue || tp.operations[0].operation == operation_intersection ) : // i ??? true ) && ! is_p_last && inters.is_spike_p(); + // TODO: throw an exception for spike in Areal? - /*bool is_q_spike = ( Version == append_touches ? - ( tp.operations[1].operation == operation_continue - || tp.operations[1].operation == operation_intersection ) : - true ) - && ! is_q_last - && inters.is_spike_q();*/ + /*bool is_q_spike = ( ( Version == append_touches + && tp.operations[1].operation == operation_continue ) + || ( Version == append_collinear_opposite + && tp.operations[1].operation == operation_none ) ) + && inters.is_spike_q(); - if ( is_p_spike && ( Version == append_touches || inters.d_info().arrival[0] == 1 ) ) + if ( is_p_spike && is_q_spike ) { - if ( Version == append_touches ) - { - tp.operations[0].is_collinear = true; - //tp.operations[1].is_collinear = false; - tp.method = method_touch; - } - else + // u/u or nothing? + return false; + } + else*/ + if ( is_p_spike ) + { + if ( BOOST_GEOMETRY_CONDITION(is_version_touches) + || inters.d_info().arrival[0] == 1 ) { - tp.operations[0].is_collinear = true; - //tp.operations[1].is_collinear = false; - - BOOST_ASSERT(inters.i_info().count > 1); - base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1); + if ( BOOST_GEOMETRY_CONDITION(is_version_touches) ) + { + tp.operations[0].is_collinear = true; + //tp.operations[1].is_collinear = false; + tp.method = method_touch; + } + else + { + tp.operations[0].is_collinear = true; + //tp.operations[1].is_collinear = false; - AssignPolicy::apply(tp, inters.pi(), inters.qi(), inters.i_info(), inters.d_info()); - } + BOOST_ASSERT(inters.i_info().count > 1); + base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1); - tp.operations[0].operation = operation_blocked; + AssignPolicy::apply(tp, inters.pi(), inters.qi(), inters); + } + + tp.operations[0].operation = operation_blocked; + tp.operations[1].operation = operation_continue; // boundary + *out++ = tp; + tp.operations[0].operation = operation_continue; // boundary + //tp.operations[1].operation = operation_continue; // boundary + *out++ = tp; + + return true; + } + } + /*else if ( is_q_spike ) + { + tp.operations[0].is_collinear = true; + tp.method = is_version_touches ? method_touch : method_touch_interior; + tp.operations[0].operation = operation_continue; tp.operations[1].operation = operation_continue; // boundary *out++ = tp; - tp.operations[0].operation = operation_continue; // boundary - //tp.operations[1].operation = operation_continue; // boundary *out++ = tp; return true; - } + }*/ return false; } @@ -587,7 +649,7 @@ struct get_turn_info_linear_areal operation_type & op1 = turn.operations[1].operation; // NOTE: probably only if methods are WRT IPs, not segments! - if ( IsFront + if ( BOOST_GEOMETRY_CONDITION(IsFront) || op0 == operation_intersection || op0 == operation_union || op1 == operation_intersection || op1 == operation_union ) { @@ -666,7 +728,9 @@ struct get_turn_info_linear_areal // ANALYSE AND ASSIGN FIRST // IP on the first point of Linear Geometry - if ( EnableFirst && is_p_first && ip0.is_pi && !ip0.is_qi ) // !q0i prevents duplication + bool was_first_point_handled = false; + if ( BOOST_GEOMETRY_CONDITION(EnableFirst) + && is_p_first && ip0.is_pi && !ip0.is_qi ) // !q0i prevents duplication { TurnInfo tp = tp_model; tp.operations[0].position = position_front; @@ -735,14 +799,16 @@ struct get_turn_info_linear_areal // here is_p_first_ip == true tp.operations[0].is_collinear = false; - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; + + was_first_point_handled = true; } // ANALYSE AND ASSIGN LAST // IP on the last point of Linear Geometry - if ( EnableLast + if ( BOOST_GEOMETRY_CONDITION(EnableLast) && is_p_last && ( ip_count > 1 ? (ip1.is_pj && !ip1.is_qi) : (ip0.is_pj && !ip0.is_qi) ) ) // prevents duplication { @@ -783,13 +849,14 @@ struct get_turn_info_linear_areal // equals<> or collinear<> will assign the second point, // we'd like to assign the first one - int ip_index = ip_count > 1 ? 1 : 0; + unsigned int ip_index = ip_count > 1 ? 1 : 0; base_turn_handler::assign_point(tp, tp.method, inters.i_info(), ip_index); - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; - return true; + // don't ignore the first IP if the segment is opposite + return !( opposite && ip_count > 1 ) || was_first_point_handled; } // don't ignore anything for now diff --git a/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp b/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp index 4f0384877f..1ec88e54a0 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014. -// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015. +// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -17,6 +17,8 @@ #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> #include <boost/geometry/algorithms/detail/overlay/get_turn_info_for_endpoint.hpp> +#include <boost/geometry/util/condition.hpp> + namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL @@ -120,7 +122,7 @@ struct get_turn_info_linear_linear tp.operations[0].operation, tp.operations[1].operation); - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } } @@ -132,7 +134,7 @@ struct get_turn_info_linear_linear replace_operations_i(tp.operations[0].operation, tp.operations[1].operation); - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } break; @@ -260,9 +262,9 @@ struct get_turn_info_linear_linear tp.operations[1].operation); // TODO: move this into the append_xxx and call for each turn? - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); - if ( ! handle_spikes + if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes) || ! append_opposite_spikes<append_touches>(tp, inters, is_p_last, is_q_last, out) ) @@ -293,18 +295,24 @@ struct get_turn_info_linear_linear equal<TurnInfo>::apply(pi, pj, pk, qi, qj, qk, tp, inters.i_info(), inters.d_info(), inters.sides()); + operation_type spike_op + = ( tp.operations[0].operation != operation_continue + || tp.operations[1].operation != operation_continue ) ? + operation_union : + operation_continue; + // transform turn turn_transformer_ec transformer(method_touch); transformer(tp); // TODO: move this into the append_xxx and call for each turn? - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); // conditionally handle spikes - if ( ! handle_spikes + if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes) || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, - method_touch, operation_union, + method_touch, spike_op, out) ) { *out++ = tp; // no spikes @@ -318,7 +326,7 @@ struct get_turn_info_linear_linear < TurnInfo, AssignPolicy - >::apply(pi, qi, tp, out, inters.i_info(), inters.d_info()); + >::apply(pi, qi, tp, out, inters); } } } @@ -351,7 +359,11 @@ struct get_turn_info_linear_linear tp, inters.i_info(), inters.d_info(), inters.sides()); method_replace = method_touch; - spike_op = operation_union; + if ( tp.operations[0].operation != operation_continue + || tp.operations[1].operation != operation_continue ) + { + spike_op = operation_union; + } } else { @@ -367,10 +379,10 @@ struct get_turn_info_linear_linear transformer(tp); // TODO: move this into the append_xxx and call for each turn? - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); // conditionally handle spikes - if ( ! handle_spikes + if ( ! BOOST_GEOMETRY_CONDITION(handle_spikes) || ! append_collinear_spikes(tp, inters, is_p_last, is_q_last, method_replace, spike_op, @@ -386,7 +398,7 @@ struct get_turn_info_linear_linear turn_transformer_ec transformer(method_touch_interior); // conditionally handle spikes - if ( handle_spikes ) + if ( BOOST_GEOMETRY_CONDITION(handle_spikes) ) { append_opposite_spikes<append_collinear_opposite>(tp, inters, is_p_last, is_q_last, @@ -402,7 +414,7 @@ struct get_turn_info_linear_linear TurnInfo, AssignPolicy >::apply(pi, pj, pk, qi, qj, qk, - tp, out, inters.i_info(), inters.d_info(), inters.sides(), + tp, out, inters, inters.sides(), transformer, !is_p_last, !is_q_last); } } @@ -411,7 +423,7 @@ struct get_turn_info_linear_linear case '0' : { // degenerate points - if (AssignPolicy::include_degenerate) + if ( BOOST_GEOMETRY_CONDITION(AssignPolicy::include_degenerate) ) { only_convert::apply(tp, inters.i_info()); @@ -437,7 +449,7 @@ struct get_turn_info_linear_linear tp.operations[1].position = position_back; } - AssignPolicy::apply(tp, pi, qi, inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, pi, qi, inters); *out++ = tp; } } @@ -478,6 +490,14 @@ struct get_turn_info_linear_linear if ( is_p_spike && is_q_spike ) { + if ( tp.method == method_equal + && tp.operations[0].operation == operation_continue + && tp.operations[1].operation == operation_continue ) + { + // treat both non-opposite collinear spikes as no-spikes + return false; + } + tp.method = method; tp.operations[0].operation = operation_blocked; tp.operations[1].operation = operation_blocked; @@ -527,13 +547,15 @@ struct get_turn_info_linear_linear bool is_p_last, bool is_q_last, OutIt out) { - bool is_p_spike = ( Version == append_touches ? + static const bool is_version_touches = (Version == append_touches); + + bool is_p_spike = ( is_version_touches ? ( tp.operations[0].operation == operation_continue || tp.operations[0].operation == operation_intersection ) : true ) && ! is_p_last && inters.is_spike_p(); - bool is_q_spike = ( Version == append_touches ? + bool is_q_spike = ( is_version_touches ? ( tp.operations[1].operation == operation_continue || tp.operations[1].operation == operation_intersection ) : true ) @@ -542,9 +564,11 @@ struct get_turn_info_linear_linear bool res = false; - if ( is_p_spike && ( Version == append_touches || inters.d_info().arrival[0] == 1 ) ) + if ( is_p_spike + && ( BOOST_GEOMETRY_CONDITION(is_version_touches) + || inters.d_info().arrival[0] == 1 ) ) { - if ( Version == append_touches ) + if ( BOOST_GEOMETRY_CONDITION(is_version_touches) ) { tp.operations[0].is_collinear = true; tp.operations[1].is_collinear = false; @@ -560,8 +584,7 @@ struct get_turn_info_linear_linear base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 1); - AssignPolicy::apply(tp, inters.pi(), inters.qi(), - inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, inters.pi(), inters.qi(), inters); } tp.operations[0].operation = operation_blocked; @@ -574,9 +597,11 @@ struct get_turn_info_linear_linear res = true; } - if ( is_q_spike && ( Version == append_touches || inters.d_info().arrival[1] == 1 ) ) + if ( is_q_spike + && ( BOOST_GEOMETRY_CONDITION(is_version_touches) + || inters.d_info().arrival[1] == 1 ) ) { - if ( Version == append_touches ) + if ( BOOST_GEOMETRY_CONDITION(is_version_touches) ) { tp.operations[0].is_collinear = false; tp.operations[1].is_collinear = true; @@ -591,8 +616,7 @@ struct get_turn_info_linear_linear base_turn_handler::assign_point(tp, method_touch_interior, inters.i_info(), 0); - AssignPolicy::apply(tp, inters.pi(), inters.qi(), - inters.i_info(), inters.d_info()); + AssignPolicy::apply(tp, inters.pi(), inters.qi(), inters); } tp.operations[0].operation = operation_intersection; diff --git a/boost/geometry/algorithms/detail/overlay/get_turns.hpp b/boost/geometry/algorithms/detail/overlay/get_turns.hpp index a96538c43a..a5d8f3f023 100644 --- a/boost/geometry/algorithms/detail/overlay/get_turns.hpp +++ b/boost/geometry/algorithms/detail/overlay/get_turns.hpp @@ -54,6 +54,7 @@ #include <boost/geometry/algorithms/detail/interior_iterator.hpp> #include <boost/geometry/algorithms/detail/partition.hpp> #include <boost/geometry/algorithms/detail/recalculate.hpp> +#include <boost/geometry/algorithms/detail/sections/section_box_policies.hpp> #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> #include <boost/geometry/algorithms/detail/overlay/get_turn_info_ll.hpp> @@ -62,8 +63,7 @@ #include <boost/geometry/algorithms/detail/sections/range_by_section.hpp> #include <boost/geometry/algorithms/detail/sections/sectionalize.hpp> - -#include <boost/geometry/algorithms/expand.hpp> +#include <boost/geometry/algorithms/detail/sections/section_functions.hpp> #ifdef BOOST_GEOMETRY_DEBUG_INTERSECTION # include <sstream> @@ -229,7 +229,7 @@ public : // section 2: [--------------] // section 1: |----|---|---|---|---| for (prev1 = it1++, next1++; - it1 != end1 && ! exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); + it1 != end1 && ! detail::section::exceeding<0>(dir1, *prev1, sec2.bounding_box, robust_policy); ++prev1, ++it1, ++index1, ++next1, ++ndi1) { ever_circling_iterator<range1_iterator> nd_next1( @@ -247,7 +247,7 @@ public : next2++; for (prev2 = it2++, next2++; - it2 != end2 && ! exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); + it2 != end2 && ! detail::section::exceeding<0>(dir2, *prev2, sec1.bounding_box, robust_policy); ++prev2, ++it2, ++index2, ++next2, ++ndi2) { bool skip = same_source; @@ -299,8 +299,8 @@ public : if (InterruptPolicy::enabled) { if (interrupt_policy.apply( - std::make_pair(boost::begin(turns) + size_before, - boost::end(turns)))) + std::make_pair(range::pos(turns, size_before), + boost::end(turns)))) { return false; } @@ -318,25 +318,6 @@ private : typedef typename model::referring_segment<point1_type const> segment1_type; typedef typename model::referring_segment<point2_type const> segment2_type; - - template <size_t Dim, typename Point, typename Box, typename RobustPolicy> - static inline bool preceding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) - { - typename robust_point_type<Point, RobustPolicy>::type robust_point; - geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get<Dim>(robust_point) < get<min_corner, Dim>(box)) - || (dir == -1 && get<Dim>(robust_point) > get<max_corner, Dim>(box)); - } - - template <size_t Dim, typename Point, typename Box, typename RobustPolicy> - static inline bool exceeding(int dir, Point const& point, Box const& box, RobustPolicy const& robust_policy) - { - typename robust_point_type<Point, RobustPolicy>::type robust_point; - geometry::recalculate(robust_point, point, robust_policy); - return (dir == 1 && get<Dim>(robust_point) > get<max_corner, Dim>(box)) - || (dir == -1 && get<Dim>(robust_point) < get<min_corner, Dim>(box)); - } - template <typename Iterator, typename RangeIterator, typename Section, typename RobustPolicy> static inline void advance_to_non_duplicate_next(Iterator& next, RangeIterator const& it, Section const& section, RobustPolicy const& robust_policy) @@ -387,7 +368,7 @@ private : // Mimic section-iterator: // Skip to point such that section interects other box prev = it++; - for(; it != end && preceding<0>(dir, *it, other_bounding_box, robust_policy); + for(; it != end && detail::section::preceding<0>(dir, *it, other_bounding_box, robust_policy); prev = it++, index++, ndi++) {} // Go back one step because we want to start completely preceding @@ -395,24 +376,6 @@ private : } }; -struct get_section_box -{ - template <typename Box, typename InputItem> - static inline void apply(Box& total, InputItem const& item) - { - geometry::expand(total, item.bounding_box); - } -}; - -struct ovelaps_section_box -{ - template <typename Box, typename InputItem> - static inline bool apply(Box const& box, InputItem const& item) - { - return ! detail::disjoint::disjoint_box_box(box, item.bounding_box); - } -}; - template < typename Geometry1, typename Geometry2, @@ -496,12 +459,15 @@ public: point_type, RobustPolicy >::type > box_type; - typedef typename geometry::sections<box_type, 2> sections_type; + typedef geometry::sections<box_type, 2> sections_type; sections_type sec1, sec2; + typedef boost::mpl::vector_c<std::size_t, 0, 1> dimensions; - geometry::sectionalize<Reverse1>(geometry1, robust_policy, true, sec1, 0); - geometry::sectionalize<Reverse2>(geometry2, robust_policy, true, sec2, 1); + geometry::sectionalize<Reverse1, dimensions>(geometry1, robust_policy, + sec1, 0); + geometry::sectionalize<Reverse2, dimensions>(geometry2, robust_policy, + sec2, 1); // ... and then partition them, intersecting overlapping sections in visitor method section_visitor @@ -513,7 +479,9 @@ public: geometry::partition < - box_type, get_section_box, ovelaps_section_box + box_type, + detail::section::get_section_box, + detail::section::overlaps_section_box >::apply(sec1, sec2, visitor); } }; @@ -556,7 +524,8 @@ struct get_turns_cs RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, - int multi_index = -1, int ring_index = -1) + signed_index_type multi_index = -1, + signed_index_type ring_index = -1) { if ( boost::size(range) <= 1) { @@ -569,7 +538,8 @@ struct get_turns_cs cview_type cview(range); view_type view(cview); - typename boost::range_size<view_type>::type segments_count1 = boost::size(view) - 1; + typedef typename boost::range_size<view_type>::type size_type; + size_type segments_count1 = boost::size(view) - 1; iterator_type it = boost::begin(view); @@ -582,7 +552,7 @@ struct get_turns_cs //char previous_side[2] = {0, 0}; - int index = 0; + signed_index_type index = 0; for (iterator_type prev = it++; it != boost::end(view); @@ -621,7 +591,7 @@ struct get_turns_cs bp[0], bp[1], bp[2], bp[3], // NOTE: some dummy values could be passed below since this would be called only for Polygons and Boxes index == 0, - unsigned(index) == segments_count1, + size_type(index) == segments_count1, robust_policy, turns, interrupt_policy); // Future performance enhancement: @@ -726,7 +696,7 @@ struct get_turns_polygon_cs int source_id2, Box const& box, RobustPolicy const& robust_policy, Turns& turns, InterruptPolicy& interrupt_policy, - int multi_index = -1) + signed_index_type multi_index = -1) { typedef typename geometry::ring_type<Polygon>::type ring_type; @@ -744,7 +714,7 @@ struct get_turns_polygon_cs turns, interrupt_policy, multi_index, -1); - int i = 0; + signed_index_type i = 0; typename interior_return_type<Polygon const>::type rings = interior_rings(polygon); @@ -783,7 +753,7 @@ struct get_turns_multi_polygon_cs Multi const >::type iterator_type; - int i = 0; + signed_index_type i = 0; for (iterator_type it = boost::begin(multi); it != boost::end(multi); ++it, ++i) diff --git a/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp b/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp index 085933dd7a..277a223d47 100644 --- a/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp +++ b/boost/geometry/algorithms/detail/overlay/handle_tangencies.hpp @@ -28,6 +28,11 @@ #include <boost/geometry/geometries/segment.hpp> +// TODO: the approach below should be completely replaced by the new +// get_left_turns, to keep the outgoing vector which has open space one of its +// sides. + + namespace boost { namespace geometry { @@ -76,6 +81,12 @@ private : RobustPolicy >::type robust_point_type; + inline bool default_order(Indexed const& left, Indexed const& right) const + { + // We've nothing to sort on. Take the indexes + return left.turn_index < right.turn_index; + } + // Still necessary in some situations, // for example #case_102_multi, #case_107_multi, #case_recursive_boxes_3 inline void get_situation_map(Indexed const& left, Indexed const& right, @@ -336,7 +347,7 @@ private : #endif //debug_consider(0, left, right, header, false, "-> return", ret); - return left.turn_index < right.turn_index; + return default_order(left, right); } @@ -369,11 +380,17 @@ private : // 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; + int const side_ri_s = m_strategy.apply(si, sj, ri); + if (side_si_r == side_ri_s) + { + return default_order(left, right); + } + + // Take the most left one + bool const ret = side_si_r == 1; //debug_consider(0, left, right, header, false, "same side", ret); return ret; } @@ -407,6 +424,12 @@ private : // One coming from left (#90, #94, #95) if (side_si_r != 0 && (side_ri_p != 0 || side_si_p != 0)) { + int const side_ri_s = m_strategy.apply(si, sj, ri); + if (side_si_r == side_ri_s) + { + return default_order(left, right); + } + bool ret = false; #if BOOST_GEOMETRY_HANDLE_TANGENCIES_WITH_OVERLAP_INFO @@ -464,7 +487,7 @@ private : return ! consider_iu_iu(right, left, header, true); } - return left.turn_index < right.turn_index; + return default_order(left, right); } inline bool consider_ii(Indexed const& left, Indexed const& right, @@ -488,19 +511,17 @@ private : bool const ret = side_si_r != 1; return ret; } - return left.turn_index < right.turn_index; + return default_order(left, right); } public : inline bool operator()(Indexed const& left, Indexed const& right) const { - bool const default_order = left.turn_index < right.turn_index; - if ((m_turn_points[left.turn_index].discarded || left.discarded) && (m_turn_points[right.turn_index].discarded || right.discarded)) { - return default_order; + return default_order(left, right); } else if (m_turn_points[left.turn_index].discarded || left.discarded) { @@ -525,7 +546,7 @@ public : // uu/uu, Order is arbitrary // Note: uu/uu is discarded now before so this point will // not be reached. - return default_order; + return default_order(left, right); } else if (m_turn_points[left.turn_index].combination(operation_intersection, operation_union) && m_turn_points[right.turn_index].combination(operation_intersection, operation_union)) @@ -587,7 +608,7 @@ public : << std::endl; #endif - return default_order; + return default_order(left, right); } }; @@ -708,7 +729,7 @@ inline void handle_cluster(Iterator begin_cluster, Iterator end_cluster, for_operation, geometry1, geometry2, strategy); - // Then sort this range (discard rows will be ordered first and will be removed in enrich_assign) + // Then sort this range (discarded rows will be ordered first and will be removed in enrich_assign) std::sort(begin_cluster, end_cluster, sort_in_cluster < diff --git a/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp index a13a627456..3101de8c35 100644 --- a/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp +++ b/boost/geometry/algorithms/detail/overlay/intersection_insert.hpp @@ -43,9 +43,9 @@ #include <boost/geometry/algorithms/detail/overlay/linear_linear.hpp> #include <boost/geometry/algorithms/detail/overlay/pointlike_pointlike.hpp> - #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) -#include <boost/foreach.hpp> +#include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp> +#include <boost/geometry/io/wkt/wkt.hpp> #endif namespace boost { namespace geometry @@ -254,9 +254,10 @@ struct intersection_of_linestring_with_areal #if defined(BOOST_GEOMETRY_DEBUG_FOLLOW) int index = 0; - BOOST_FOREACH(turn_info const& turn, turns) + for(typename std::deque<turn_info>::const_iterator + it = turns.begin(); it != turns.end(); ++it) { - debug_follow(turn, turn.operations[0], index++); + debug_follow(*it, it->operations[0], index++); } #endif diff --git a/boost/geometry/algorithms/detail/overlay/linear_linear.hpp b/boost/geometry/algorithms/detail/overlay/linear_linear.hpp index 3a7a7a7f3e..d4ebcf296b 100644 --- a/boost/geometry/algorithms/detail/overlay/linear_linear.hpp +++ b/boost/geometry/algorithms/detail/overlay/linear_linear.hpp @@ -144,11 +144,10 @@ protected: typename Info, typename Point1, typename Point2, - typename IntersectionInfo, - typename DirInfo + typename IntersectionInfo > static inline void apply(Info& , Point1 const& , Point2 const& , - IntersectionInfo const& , DirInfo const& ) + IntersectionInfo const& ) { } }; diff --git a/boost/geometry/algorithms/detail/overlay/overlay.hpp b/boost/geometry/algorithms/detail/overlay/overlay.hpp index 44b5a0df3c..a2f52848d1 100644 --- a/boost/geometry/algorithms/detail/overlay/overlay.hpp +++ b/boost/geometry/algorithms/detail/overlay/overlay.hpp @@ -44,10 +44,6 @@ # include <boost/geometry/io/dsv/write.hpp> #endif -#ifdef BOOST_GEOMETRY_TIME_OVERLAY -# include <boost/timer.hpp> -#endif - namespace boost { namespace geometry { @@ -57,19 +53,10 @@ namespace boost { namespace geometry namespace detail { namespace overlay { -// Skip for assemble process -template <typename TurnInfo> -inline bool skip(TurnInfo const& turn_info) -{ - return (turn_info.discarded || turn_info.both(operation_union)) - && ! turn_info.any_blocked() - && ! turn_info.both(operation_intersection) - ; -} - -template <typename TurnPoints, typename Map> -inline void map_turns(Map& map, TurnPoints const& turn_points) +template <typename TurnPoints, typename TurnInfoMap> +inline void get_ring_turn_info(TurnInfoMap& turn_info_map, + TurnPoints const& turn_points) { typedef typename boost::range_value<TurnPoints>::type turn_point_type; typedef typename turn_point_type::container_type container_type; @@ -79,20 +66,32 @@ inline void map_turns(Map& map, TurnPoints const& turn_points) it != boost::end(turn_points); ++it) { - if (! skip(*it)) + typename boost::range_value<TurnPoints>::type const& turn_info = *it; + bool both_uu = turn_info.both(operation_union); + bool skip = (turn_info.discarded || both_uu) + && ! turn_info.any_blocked() + && ! turn_info.both(operation_intersection) + ; + + for (typename boost::range_iterator<container_type const>::type + op_it = boost::begin(turn_info.operations); + op_it != boost::end(turn_info.operations); + ++op_it) { - for (typename boost::range_iterator<container_type const>::type - op_it = boost::begin(it->operations); - op_it != boost::end(it->operations); - ++op_it) + ring_identifier ring_id + ( + op_it->seg_id.source_index, + op_it->seg_id.multi_index, + op_it->seg_id.ring_index + ); + + if (! skip) { - 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]++; + turn_info_map[ring_id].has_normal_turn = true; + } + else if (both_uu) + { + turn_info_map[ring_id].has_uu_turn = true; } } } @@ -137,10 +136,10 @@ inline OutputIterator return_if_one_input_is_empty(Geometry1 const& geometry1, #endif - std::map<ring_identifier, int> empty; + std::map<ring_identifier, ring_turn_info> empty; std::map<ring_identifier, properties> all_of_one_of_them; - select_rings<Direction>(geometry1, geometry2, empty, all_of_one_of_them, false); + select_rings<Direction>(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<GeometryOut>(all_of_one_of_them, geometry1, geometry2, rings, out); @@ -193,10 +192,6 @@ struct overlay 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 @@ -207,10 +202,6 @@ std::cout << "get turns" << std::endl; detail::overlay::assign_null_policy >(geometry1, geometry2, robust_policy, 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 @@ -223,11 +214,6 @@ std::cout << "enrich" << std::endl; robust_policy, 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 @@ -245,27 +231,18 @@ std::cout << "traverse" << std::endl; turn_points, rings ); -#ifdef BOOST_GEOMETRY_TIME_OVERLAY - std::cout << "traverse: " << timer.elapsed() << std::endl; -#endif - - - std::map<ring_identifier, int> map; - map_turns(map, turn_points); - -#ifdef BOOST_GEOMETRY_TIME_OVERLAY - std::cout << "map_turns: " << timer.elapsed() << std::endl; -#endif - - typedef ring_properties<typename geometry::point_type<GeometryOut>::type> properties; - - std::map<ring_identifier, properties> selected; - select_rings<Direction>(geometry1, geometry2, map, selected, ! turn_points.empty()); + std::map<ring_identifier, ring_turn_info> turn_info_per_ring; + get_ring_turn_info(turn_info_per_ring, turn_points); -#ifdef BOOST_GEOMETRY_TIME_OVERLAY - std::cout << "select_rings: " << timer.elapsed() << std::endl; -#endif + typedef ring_properties + < + typename geometry::point_type<GeometryOut>::type + > properties; + // Select all rings which are NOT touched by any intersection point + std::map<ring_identifier, properties> selected_ring_properties; + select_rings<Direction>(geometry1, geometry2, turn_info_per_ring, + selected_ring_properties); // Add rings created during traversal { @@ -275,24 +252,15 @@ std::cout << "traverse" << std::endl; it != boost::end(rings); ++it) { - selected[id] = properties(*it, true); - selected[id].reversed = ReverseOut; + selected_ring_properties[id] = properties(*it); + selected_ring_properties[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 + assign_parents(geometry1, geometry2, rings, selected_ring_properties); - return add_rings<GeometryOut>(selected, geometry1, geometry2, rings, out); + return add_rings<GeometryOut>(selected_ring_properties, geometry1, geometry2, rings, out); } }; diff --git a/boost/geometry/algorithms/detail/overlay/ring_properties.hpp b/boost/geometry/algorithms/detail/overlay/ring_properties.hpp index a6088694da..b469052c84 100644 --- a/boost/geometry/algorithms/detail/overlay/ring_properties.hpp +++ b/boost/geometry/algorithms/detail/overlay/ring_properties.hpp @@ -33,8 +33,7 @@ struct ring_properties Point point; area_type area; - // Filled by "update_selection_map" - int within_code; + // Filled by "update_ring_selection" bool reversed; // Filled/used by "assign_rings" @@ -45,21 +44,22 @@ struct ring_properties inline ring_properties() : area(area_type()) - , within_code(-1) , reversed(false) , discarded(false) , parent_area(-1) {} template <typename RingOrBox> - inline ring_properties(RingOrBox const& ring_or_box, bool midpoint) - : within_code(-1) - , reversed(false) + inline ring_properties(RingOrBox const& ring_or_box) + : reversed(false) , discarded(false) , parent_area(-1) { this->area = geometry::area(ring_or_box); - geometry::point_on_border(this->point, ring_or_box, midpoint); + // We should take a point somewhere in the middle of the ring, + // to avoid taking a point on a (self)tangency, + // in cases where multiple points come together + geometry::point_on_border(this->point, ring_or_box, true); } inline area_type get_area() const diff --git a/boost/geometry/algorithms/detail/overlay/select_rings.hpp b/boost/geometry/algorithms/detail/overlay/select_rings.hpp index 385658a190..d18e012b2d 100644 --- a/boost/geometry/algorithms/detail/overlay/select_rings.hpp +++ b/boost/geometry/algorithms/detail/overlay/select_rings.hpp @@ -1,6 +1,6 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands. // Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. // Use, modification and distribution is subject to the Boost Software License, @@ -20,7 +20,6 @@ #include <boost/geometry/algorithms/area.hpp> #include <boost/geometry/algorithms/within.hpp> #include <boost/geometry/algorithms/detail/interior_iterator.hpp> -#include <boost/geometry/algorithms/detail/point_on_border.hpp> #include <boost/geometry/algorithms/detail/ring_identifier.hpp> #include <boost/geometry/algorithms/detail/overlay/ring_properties.hpp> #include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp> @@ -34,6 +33,18 @@ namespace boost { namespace geometry namespace detail { namespace overlay { +struct ring_turn_info +{ + bool has_uu_turn; + bool has_normal_turn; + bool within_other; + + ring_turn_info() + : has_uu_turn(false) + , has_normal_turn(false) + , within_other(false) + {} +}; namespace dispatch { @@ -45,41 +56,41 @@ namespace dispatch template <typename Box> struct select_rings<box_tag, Box> { - template <typename Geometry, typename Map> + template <typename Geometry, typename RingPropertyMap> static inline void apply(Box const& box, Geometry const& , - ring_identifier const& id, Map& map, bool midpoint) + ring_identifier const& id, RingPropertyMap& ring_properties) { - map[id] = typename Map::mapped_type(box, midpoint); + ring_properties[id] = typename RingPropertyMap::mapped_type(box); } - template <typename Map> + template <typename RingPropertyMap> static inline void apply(Box const& box, - ring_identifier const& id, Map& map, bool midpoint) + ring_identifier const& id, RingPropertyMap& ring_properties) { - map[id] = typename Map::mapped_type(box, midpoint); + ring_properties[id] = typename RingPropertyMap::mapped_type(box); } }; template <typename Ring> struct select_rings<ring_tag, Ring> { - template <typename Geometry, typename Map> + template <typename Geometry, typename RingPropertyMap> static inline void apply(Ring const& ring, Geometry const& , - ring_identifier const& id, Map& map, bool midpoint) + ring_identifier const& id, RingPropertyMap& ring_properties) { if (boost::size(ring) > 0) { - map[id] = typename Map::mapped_type(ring, midpoint); + ring_properties[id] = typename RingPropertyMap::mapped_type(ring); } } - template <typename Map> + template <typename RingPropertyMap> static inline void apply(Ring const& ring, - ring_identifier const& id, Map& map, bool midpoint) + ring_identifier const& id, RingPropertyMap& ring_properties) { if (boost::size(ring) > 0) { - map[id] = typename Map::mapped_type(ring, midpoint); + ring_properties[id] = typename RingPropertyMap::mapped_type(ring); } } }; @@ -88,14 +99,14 @@ namespace dispatch template <typename Polygon> struct select_rings<polygon_tag, Polygon> { - template <typename Geometry, typename Map> + template <typename Geometry, typename RingPropertyMap> static inline void apply(Polygon const& polygon, Geometry const& geometry, - ring_identifier id, Map& map, bool midpoint) + ring_identifier id, RingPropertyMap& ring_properties) { typedef typename geometry::ring_type<Polygon>::type ring_type; typedef select_rings<ring_tag, ring_type> per_ring; - per_ring::apply(exterior_ring(polygon), geometry, id, map, midpoint); + per_ring::apply(exterior_ring(polygon), geometry, id, ring_properties); typename interior_return_type<Polygon const>::type rings = interior_rings(polygon); @@ -103,18 +114,18 @@ namespace dispatch it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; - per_ring::apply(*it, geometry, id, map, midpoint); + per_ring::apply(*it, geometry, id, ring_properties); } } - template <typename Map> + template <typename RingPropertyMap> static inline void apply(Polygon const& polygon, - ring_identifier id, Map& map, bool midpoint) + ring_identifier id, RingPropertyMap& ring_properties) { typedef typename geometry::ring_type<Polygon>::type ring_type; typedef select_rings<ring_tag, ring_type> per_ring; - per_ring::apply(exterior_ring(polygon), id, map, midpoint); + per_ring::apply(exterior_ring(polygon), id, ring_properties); typename interior_return_type<Polygon const>::type rings = interior_rings(polygon); @@ -122,7 +133,7 @@ namespace dispatch it = boost::begin(rings); it != boost::end(rings); ++it) { id.ring_index++; - per_ring::apply(*it, id, map, midpoint); + per_ring::apply(*it, id, ring_properties); } } }; @@ -130,9 +141,9 @@ namespace dispatch template <typename Multi> struct select_rings<multi_polygon_tag, Multi> { - template <typename Geometry, typename Map> + template <typename Geometry, typename RingPropertyMap> static inline void apply(Multi const& multi, Geometry const& geometry, - ring_identifier id, Map& map, bool midpoint) + ring_identifier id, RingPropertyMap& ring_properties) { typedef typename boost::range_iterator < @@ -145,7 +156,7 @@ namespace dispatch for (iterator_type it = boost::begin(multi); it != boost::end(multi); ++it) { id.ring_index = -1; - per_polygon::apply(*it, geometry, id, map, midpoint); + per_polygon::apply(*it, geometry, id, ring_properties); id.multi_index++; } } @@ -161,14 +172,12 @@ struct decide template<> struct decide<overlay_union> { - template <typename Code> - static bool include(ring_identifier const& , Code const& code) + static bool include(ring_identifier const& , ring_turn_info const& info) { - return code.within_code * -1 == 1; + return info.has_uu_turn || ! info.within_other; } - template <typename Code> - static bool reversed(ring_identifier const& , Code const& ) + static bool reversed(ring_identifier const& , ring_turn_info const& ) { return false; } @@ -177,31 +186,43 @@ struct decide<overlay_union> template<> struct decide<overlay_difference> { - template <typename Code> - static bool include(ring_identifier const& id, Code const& code) + static bool include(ring_identifier const& id, ring_turn_info const& info) { - bool is_first = id.source_index == 0; - return code.within_code * -1 * (is_first ? 1 : -1) == 1; + // Difference: A - B + + // If this is A (source_index=0) and there is only a u/u turn, + // then the ring is inside B + // If this is B (source_index=1) and there is only a u/u turn, + // then the ring is NOT inside A + + // If this is A and the ring is within the other geometry, + // then we should NOT include it. + // If this is B then we SHOULD include it. + + bool const is_first = id.source_index == 0; + bool const within_other = info.within_other + || (is_first && info.has_uu_turn); + return is_first ? ! within_other : within_other; } - template <typename Code> - static bool reversed(ring_identifier const& id, Code const& code) + static bool reversed(ring_identifier const& id, ring_turn_info const& info) { - return include(id, code) && id.source_index == 1; + // Difference: A - B + // If this is B, and the ring is included, it should be reversed afterwards + + return id.source_index == 1 && include(id, info); } }; template<> struct decide<overlay_intersection> { - template <typename Code> - static bool include(ring_identifier const& , Code const& code) + static bool include(ring_identifier const& , ring_turn_info const& info) { - return code.within_code * 1 == 1; + return ! info.has_uu_turn && info.within_other; } - template <typename Code> - static bool reversed(ring_identifier const& , Code const& ) + static bool reversed(ring_identifier const& , ring_turn_info const& ) { return false; } @@ -211,61 +232,60 @@ struct decide<overlay_intersection> template < overlay_type OverlayType, - typename Geometry1, typename Geometry2, - typename IntersectionMap, typename SelectionMap + typename Geometry1, + typename Geometry2, + typename TurnInfoMap, + typename RingPropertyMap > -inline void update_selection_map(Geometry1 const& geometry1, +inline void update_ring_selection(Geometry1 const& geometry1, Geometry2 const& geometry2, - IntersectionMap const& intersection_map, - SelectionMap const& map_with_all, SelectionMap& selection_map) + TurnInfoMap const& turn_info_map, + RingPropertyMap const& all_ring_properties, + RingPropertyMap& selected_ring_properties) { - selection_map.clear(); + selected_ring_properties.clear(); - for (typename SelectionMap::const_iterator it = boost::begin(map_with_all); - it != boost::end(map_with_all); + for (typename RingPropertyMap::const_iterator it = boost::begin(all_ring_properties); + it != boost::end(all_ring_properties); ++it) { - /* - int union_code = it->second.within_code * -1; - bool is_first = it->first.source_index == 0; - std::cout << it->first << " " << it->second.area - << ": " << it->second.within_code - << " union: " << union_code - << " intersection: " << (it->second.within_code * 1) - << " G1-G2: " << (union_code * (is_first ? 1 : -1)) - << " G2-G1: " << (union_code * (is_first ? -1 : 1)) - << " -> " << (decide<OverlayType>::include(it->first, it->second) ? "INC" : "") - << decide<OverlayType>::reverse(it->first, it->second) - << std::endl; - */ - - bool found = intersection_map.find(it->first) != intersection_map.end(); - if (! found) + ring_identifier const& id = it->first; + + ring_turn_info info; + + typename TurnInfoMap::const_iterator tcit = turn_info_map.find(id); + if (tcit != turn_info_map.end()) { - ring_identifier const id = it->first; - typename SelectionMap::mapped_type properties = it->second; // Copy by value + info = tcit->second; // Copy by value + } - // Calculate the "within code" (previously this was done earlier but is - // much 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 + if (info.has_normal_turn) + { + // There are normal turns on this ring. It should be traversed, we + // don't include the original ring + continue; + } + + if (! info.has_uu_turn) + { + // Check if the ring is within the other geometry, by taking + // a point lying on the ring switch(id.source_index) { case 0 : - properties.within_code - = geometry::within(properties.point, geometry2) ? 1 : -1; + info.within_other = geometry::within(it->second.point, geometry2); break; case 1 : - properties.within_code - = geometry::within(properties.point, geometry1) ? 1 : -1; + info.within_other = geometry::within(it->second.point, geometry1); break; } + } - if (decide<OverlayType>::include(id, properties)) - { - properties.reversed = decide<OverlayType>::reversed(id, properties); - selection_map[id] = properties; - } + if (decide<OverlayType>::include(id, info)) + { + typename RingPropertyMap::mapped_type properties = it->second; // Copy by value + properties.reversed = decide<OverlayType>::reversed(id, info); + selected_ring_properties[id] = properties; } } } @@ -277,44 +297,47 @@ inline void update_selection_map(Geometry1 const& geometry1, template < overlay_type OverlayType, - typename Geometry1, typename Geometry2, - typename IntersectionMap, typename SelectionMap + typename Geometry1, + typename Geometry2, + typename RingTurnInfoMap, + typename RingPropertyMap > inline void select_rings(Geometry1 const& geometry1, Geometry2 const& geometry2, - IntersectionMap const& intersection_map, - SelectionMap& selection_map, bool midpoint) + RingTurnInfoMap const& turn_info_per_ring, + RingPropertyMap& selected_ring_properties) { typedef typename geometry::tag<Geometry1>::type tag1; typedef typename geometry::tag<Geometry2>::type tag2; - SelectionMap map_with_all; + RingPropertyMap all_ring_properties; dispatch::select_rings<tag1, Geometry1>::apply(geometry1, geometry2, - ring_identifier(0, -1, -1), map_with_all, midpoint); + ring_identifier(0, -1, -1), all_ring_properties); dispatch::select_rings<tag2, Geometry2>::apply(geometry2, geometry1, - ring_identifier(1, -1, -1), map_with_all, midpoint); + ring_identifier(1, -1, -1), all_ring_properties); - update_selection_map<OverlayType>(geometry1, geometry2, intersection_map, - map_with_all, selection_map); + update_ring_selection<OverlayType>(geometry1, geometry2, turn_info_per_ring, + all_ring_properties, selected_ring_properties); } template < overlay_type OverlayType, typename Geometry, - typename IntersectionMap, typename SelectionMap + typename RingTurnInfoMap, + typename RingPropertyMap > inline void select_rings(Geometry const& geometry, - IntersectionMap const& intersection_map, - SelectionMap& selection_map, bool midpoint) + RingTurnInfoMap const& turn_info_per_ring, + RingPropertyMap& selected_ring_properties) { typedef typename geometry::tag<Geometry>::type tag; - SelectionMap map_with_all; + RingPropertyMap all_ring_properties; dispatch::select_rings<tag, Geometry>::apply(geometry, - ring_identifier(0, -1, -1), map_with_all, midpoint); + ring_identifier(0, -1, -1), all_ring_properties); - update_selection_map<OverlayType>(geometry, geometry, intersection_map, - map_with_all, selection_map); + update_ring_selection<OverlayType>(geometry, geometry, turn_info_per_ring, + all_ring_properties, selected_ring_properties); } diff --git a/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp index 8dffeae283..b1f984ffe1 100644 --- a/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp +++ b/boost/geometry/algorithms/detail/overlay/self_turn_points.hpp @@ -23,9 +23,12 @@ #include <boost/geometry/algorithms/detail/disjoint/box_box.hpp> #include <boost/geometry/algorithms/detail/partition.hpp> #include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> +#include <boost/geometry/algorithms/detail/sections/section_box_policies.hpp> #include <boost/geometry/geometries/box.hpp> +#include <boost/geometry/util/condition.hpp> + namespace boost { namespace geometry { @@ -96,7 +99,7 @@ struct self_section_visitor m_rescale_policy, m_turns, m_interrupt_policy); } - if (m_interrupt_policy.has_intersections) + if (BOOST_GEOMETRY_CONDITION(m_interrupt_policy.has_intersections)) { // TODO: we should give partition an interrupt policy. // Now we throw, and catch below, to stop the partition loop. @@ -121,15 +124,19 @@ struct get_turns { typedef model::box < - typename geometry::point_type<Geometry>::type + typename geometry::robust_point_type + < + typename geometry::point_type<Geometry>::type, + RobustPolicy + >::type > box_type; - typedef typename geometry::sections - < - box_type, 1 - > sections_type; + + typedef geometry::sections<box_type, 1> sections_type; + + typedef boost::mpl::vector_c<std::size_t, 0> dimensions; sections_type sec; - geometry::sectionalize<false>(geometry, robust_policy, false, sec); + geometry::sectionalize<false, dimensions>(geometry, robust_policy, sec); self_section_visitor < @@ -142,8 +149,8 @@ struct get_turns geometry::partition < box_type, - detail::get_turns::get_section_box, - detail::get_turns::ovelaps_section_box + detail::section::get_section_box, + detail::section::overlaps_section_box >::apply(sec, visitor); } catch(self_ip_exception const& ) diff --git a/boost/geometry/algorithms/detail/partition.hpp b/boost/geometry/algorithms/detail/partition.hpp index a44d5637bc..25a34ba2ec 100644 --- a/boost/geometry/algorithms/detail/partition.hpp +++ b/boost/geometry/algorithms/detail/partition.hpp @@ -10,7 +10,6 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_PARTITION_HPP #include <vector> -#include <boost/assert.hpp> #include <boost/range/algorithm/copy.hpp> #include <boost/geometry/algorithms/assign.hpp> #include <boost/geometry/core/coordinate_type.hpp> @@ -24,7 +23,7 @@ namespace detail { namespace partition typedef std::vector<std::size_t> index_vector_type; template <int Dimension, typename Box> -void divide_box(Box const& box, Box& lower_box, Box& upper_box) +inline void divide_box(Box const& box, Box& lower_box, Box& upper_box) { typedef typename coordinate_type<Box>::type ctype; @@ -79,20 +78,39 @@ inline void divide_into_subsets(Box const& lower_box, } else { - // Is nowhere! Should not occur! - BOOST_ASSERT(false); + // Is nowhere. That is (since 1.58) possible, it might be + // skipped by the OverlapsPolicy to enhance performance } } } +template <typename ExpandPolicy, typename Box, typename InputCollection> +inline void expand_with_elements(Box& total, + InputCollection const& collection, + index_vector_type const& input) +{ + typedef boost::range_iterator<index_vector_type const>::type it_type; + for(it_type it = boost::begin(input); it != boost::end(input); ++it) + { + ExpandPolicy::apply(total, collection[*it]); + } +} + + // Match collection with itself template <typename InputCollection, typename Policy> inline void handle_one(InputCollection const& collection, index_vector_type const& input, Policy& policy) { + if (boost::size(input) == 0) + { + return; + } + typedef boost::range_iterator<index_vector_type const>::type index_iterator_type; + // Quadratic behaviour at lowest level (lowest quad, or all exceeding) for(index_iterator_type it1 = boost::begin(input); it1 != boost::end(input); @@ -118,6 +136,11 @@ inline void handle_two( InputCollection2 const& collection2, index_vector_type const& input2, Policy& policy) { + if (boost::size(input1) == 0 || boost::size(input2) == 0) + { + return; + } + typedef boost::range_iterator < index_vector_type const @@ -136,43 +159,116 @@ inline void handle_two( } } +inline bool recurse_ok(index_vector_type const& input, + std::size_t min_elements, std::size_t level) +{ + return boost::size(input) >= min_elements + && level < 100; +} + +inline bool recurse_ok(index_vector_type const& input1, + index_vector_type const& input2, + std::size_t min_elements, std::size_t level) +{ + return boost::size(input1) >= min_elements + && recurse_ok(input2, min_elements, level); +} + +inline bool recurse_ok(index_vector_type const& input1, + index_vector_type const& input2, + index_vector_type const& input3, + std::size_t min_elements, std::size_t level) +{ + return boost::size(input1) >= min_elements + && recurse_ok(input2, input3, min_elements, level); +} + +template +< + int Dimension, + typename Box, + typename OverlapsPolicy1, + typename OverlapsPolicy2, + typename ExpandPolicy1, + typename ExpandPolicy2, + typename VisitBoxPolicy +> +class partition_two_collections; + + template < int Dimension, typename Box, typename OverlapsPolicy, + typename ExpandPolicy, typename VisitBoxPolicy > class partition_one_collection { typedef std::vector<std::size_t> index_vector_type; - typedef typename coordinate_type<Box>::type ctype; - typedef partition_one_collection + + template <typename InputCollection> + static inline Box get_new_box(InputCollection const& collection, + index_vector_type const& input) + { + Box box; + geometry::assign_inverse(box); + expand_with_elements<ExpandPolicy>(box, collection, input); + return box; + } + + template <typename InputCollection, typename Policy> + static inline void next_level(Box const& box, + InputCollection const& collection, + index_vector_type const& input, + std::size_t level, std::size_t min_elements, + Policy& policy, VisitBoxPolicy& box_policy) + { + if (recurse_ok(input, min_elements, level)) + { + partition_one_collection < 1 - Dimension, Box, OverlapsPolicy, + ExpandPolicy, VisitBoxPolicy - > sub_divide; + >::apply(box, collection, input, + level + 1, min_elements, policy, box_policy); + } + else + { + handle_one(collection, input, policy); + } + } + // Function to switch to two collections if there are geometries exceeding + // the separation line template <typename InputCollection, typename Policy> - static inline void next_level(Box const& box, + static inline void next_level2(Box const& box, InputCollection const& collection, - index_vector_type const& input, - int level, std::size_t min_elements, + index_vector_type const& input1, + index_vector_type const& input2, + std::size_t level, std::size_t min_elements, Policy& policy, VisitBoxPolicy& box_policy) { - if (boost::size(input) > 0) + + if (recurse_ok(input1, input2, min_elements, level)) { - if (std::size_t(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); - } + partition_two_collections + < + 1 - Dimension, + Box, + OverlapsPolicy, OverlapsPolicy, + ExpandPolicy, ExpandPolicy, + VisitBoxPolicy + >::apply(box, collection, input1, collection, input2, + level + 1, min_elements, policy, box_policy); + } + else + { + handle_two(collection, input1, collection, input2, policy); } } @@ -181,7 +277,7 @@ public : static inline void apply(Box const& box, InputCollection const& collection, index_vector_type const& input, - int level, + std::size_t level, std::size_t min_elements, Policy& policy, VisitBoxPolicy& box_policy) { @@ -196,11 +292,20 @@ public : 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); + // Get the box of exceeding-only + Box exceeding_box = get_new_box(collection, exceeding); + + // Recursively do exceeding elements only, in next dimension they + // will probably be less exceeding within the new box + next_level(exceeding_box, collection, exceeding, level, + min_elements, policy, box_policy); + + // Switch to two collections, combine exceeding with lower resp upper + // but not lower/lower, upper/upper + next_level2(exceeding_box, collection, exceeding, lower, level, + min_elements, policy, box_policy); + next_level2(exceeding_box, collection, exceeding, upper, level, + min_elements, policy, box_policy); } // Recursively call operation both parts @@ -217,20 +322,13 @@ template typename Box, typename OverlapsPolicy1, typename OverlapsPolicy2, + typename ExpandPolicy1, + typename ExpandPolicy2, typename VisitBoxPolicy > class partition_two_collections { typedef std::vector<std::size_t> index_vector_type; - typedef typename coordinate_type<Box>::type ctype; - typedef partition_two_collections - < - 1 - Dimension, - Box, - OverlapsPolicy1, - OverlapsPolicy2, - VisitBoxPolicy - > sub_divide; template < @@ -243,25 +341,50 @@ class partition_two_collections index_vector_type const& input1, InputCollection2 const& collection2, index_vector_type const& input2, - int level, std::size_t min_elements, + std::size_t level, std::size_t min_elements, Policy& policy, VisitBoxPolicy& box_policy) { - if (boost::size(input1) > 0 && boost::size(input2) > 0) - { - if (std::size_t(boost::size(input1)) > min_elements - && std::size_t(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); - } - } + partition_two_collections + < + 1 - Dimension, + Box, + OverlapsPolicy1, + OverlapsPolicy2, + ExpandPolicy1, + ExpandPolicy2, + VisitBoxPolicy + >::apply(box, collection1, input1, collection2, input2, + level + 1, min_elements, + policy, box_policy); + } + + template + < + typename ExpandPolicy, + typename InputCollection + > + static inline Box get_new_box(InputCollection const& collection, + index_vector_type const& input) + { + Box box; + geometry::assign_inverse(box); + expand_with_elements<ExpandPolicy>(box, collection, input); + return box; + } + + template + < + typename InputCollection1, + typename InputCollection2 + > + static inline Box get_new_box(InputCollection1 const& collection1, + index_vector_type const& input1, + InputCollection2 const& collection2, + index_vector_type const& input2) + { + Box box = get_new_box<ExpandPolicy1>(collection1, input1); + expand_with_elements<ExpandPolicy2>(box, collection2, input2); + return box; } public : @@ -274,7 +397,7 @@ public : static inline void apply(Box const& box, InputCollection1 const& collection1, index_vector_type const& input1, InputCollection2 const& collection2, index_vector_type const& input2, - int level, + std::size_t level, std::size_t min_elements, Policy& policy, VisitBoxPolicy& box_policy) { @@ -293,36 +416,100 @@ public : if (boost::size(exceeding1) > 0) { // All exceeding from 1 with 2: - handle_two(collection1, exceeding1, collection2, exceeding2, - policy); + + if (recurse_ok(exceeding1, exceeding2, min_elements, level)) + { + Box exceeding_box = get_new_box(collection1, exceeding1, + collection2, exceeding2); + next_level(exceeding_box, collection1, exceeding1, + collection2, exceeding2, level, + min_elements, policy, box_policy); + } + else + { + 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); + + // (Check sizes of all three collections to avoid recurse into + // the same combinations again and again) + if (recurse_ok(lower2, upper2, exceeding1, min_elements, level)) + { + Box exceeding_box + = get_new_box<ExpandPolicy1>(collection1, exceeding1); + next_level(exceeding_box, collection1, exceeding1, + collection2, lower2, level, min_elements, policy, box_policy); + next_level(exceeding_box, collection1, exceeding1, + collection2, upper2, level, min_elements, policy, box_policy); + } + else + { + 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); + if (recurse_ok(lower1, upper1, exceeding2, min_elements, level)) + { + Box exceeding_box + = get_new_box<ExpandPolicy2>(collection2, exceeding2); + next_level(exceeding_box, collection1, lower1, + collection2, exceeding2, level, min_elements, policy, box_policy); + next_level(exceeding_box, collection1, upper1, + collection2, exceeding2, level, min_elements, policy, box_policy); + } + else + { + 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); + if (recurse_ok(lower1, lower2, min_elements, level)) + { + next_level(lower_box, collection1, lower1, collection2, lower2, level, + min_elements, policy, box_policy); + } + else + { + handle_two(collection1, lower1, collection2, lower2, policy); + } + if (recurse_ok(upper1, upper2, min_elements, level)) + { + next_level(upper_box, collection1, upper1, collection2, upper2, level, + min_elements, policy, box_policy); + } + else + { + handle_two(collection1, upper1, collection2, upper2, policy); + } } }; -}} // namespace detail::partition - struct visit_no_policy { template <typename Box> - static inline void apply(Box const&, int ) + static inline void apply(Box const&, std::size_t ) {} }; +struct include_all_policy +{ + template <typename Item> + static inline bool apply(Item const&) + { + return true; + } +}; + + +}} // namespace detail::partition + template < typename Box, @@ -330,13 +517,15 @@ template typename OverlapsPolicy1, typename ExpandPolicy2 = ExpandPolicy1, typename OverlapsPolicy2 = OverlapsPolicy1, - typename VisitBoxPolicy = visit_no_policy + typename IncludePolicy1 = detail::partition::include_all_policy, + typename IncludePolicy2 = detail::partition::include_all_policy, + typename VisitBoxPolicy = detail::partition::visit_no_policy > class partition { typedef std::vector<std::size_t> index_vector_type; - template <typename ExpandPolicy, typename InputCollection> + template <typename ExpandPolicy, typename IncludePolicy, typename InputCollection> static inline void expand_to_collection(InputCollection const& collection, Box& total, index_vector_type& index_vector) { @@ -346,8 +535,11 @@ class partition it != boost::end(collection); ++it, ++index) { - ExpandPolicy::apply(total, *it); - index_vector.push_back(index); + if (IncludePolicy::apply(*it)) + { + ExpandPolicy::apply(total, *it); + index_vector.push_back(index); + } } } @@ -356,7 +548,7 @@ public : static inline void apply(InputCollection const& collection, VisitPolicy& visitor, std::size_t min_elements = 16, - VisitBoxPolicy box_visitor = visit_no_policy() + VisitBoxPolicy box_visitor = detail::partition::visit_no_policy() ) { if (std::size_t(boost::size(collection)) > min_elements) @@ -364,12 +556,14 @@ public : index_vector_type index_vector; Box total; assign_inverse(total); - expand_to_collection<ExpandPolicy1>(collection, total, index_vector); + expand_to_collection<ExpandPolicy1, IncludePolicy1>(collection, + total, index_vector); detail::partition::partition_one_collection < 0, Box, OverlapsPolicy1, + ExpandPolicy1, VisitBoxPolicy >::apply(total, collection, index_vector, 0, min_elements, visitor, box_visitor); @@ -403,7 +597,7 @@ public : InputCollection2 const& collection2, VisitPolicy& visitor, std::size_t min_elements = 16, - VisitBoxPolicy box_visitor = visit_no_policy() + VisitBoxPolicy box_visitor = detail::partition::visit_no_policy() ) { if (std::size_t(boost::size(collection1)) > min_elements @@ -412,12 +606,15 @@ public : index_vector_type index_vector1, index_vector2; Box total; assign_inverse(total); - expand_to_collection<ExpandPolicy1>(collection1, total, index_vector1); - expand_to_collection<ExpandPolicy2>(collection2, total, index_vector2); + expand_to_collection<ExpandPolicy1, IncludePolicy1>(collection1, + total, index_vector1); + expand_to_collection<ExpandPolicy2, IncludePolicy2>(collection2, + total, index_vector2); detail::partition::partition_two_collections < - 0, Box, OverlapsPolicy1, OverlapsPolicy2, VisitBoxPolicy + 0, Box, OverlapsPolicy1, OverlapsPolicy2, + ExpandPolicy1, ExpandPolicy2, VisitBoxPolicy >::apply(total, collection1, index_vector1, collection2, index_vector2, diff --git a/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp b/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp index cd3acb5ba4..9db1ef8e0c 100644 --- a/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp +++ b/boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp @@ -17,6 +17,7 @@ #include <boost/geometry/algorithms/detail/recalculate.hpp> #include <boost/geometry/policies/robustness/robust_point_type.hpp> #include <boost/geometry/strategies/side.hpp> +#include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/math.hpp> namespace boost { namespace geometry @@ -91,7 +92,7 @@ static inline bool point_is_spike_or_equal(Point1 const& last_point, return true; } - if (! RobustPolicy::enabled) + if (BOOST_GEOMETRY_CONDITION(! RobustPolicy::enabled)) { return false; } diff --git a/boost/geometry/algorithms/detail/relate/areal_areal.hpp b/boost/geometry/algorithms/detail/relate/areal_areal.hpp index 31d206ac99..2859841de4 100644 --- a/boost/geometry/algorithms/detail/relate/areal_areal.hpp +++ b/boost/geometry/algorithms/detail/relate/areal_areal.hpp @@ -2,19 +2,21 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014. -// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015. +// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle - #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_AREAL_AREAL_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_AREAL_AREAL_HPP #include <boost/geometry/core/topological_dimension.hpp> + +#include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/range.hpp> #include <boost/geometry/algorithms/num_interior_rings.hpp> @@ -197,7 +199,7 @@ struct areal_areal // The result should be FFFFFFFFF relate::set<exterior, exterior, result_dimension<Geometry2>::value>(result);// FFFFFFFFd, d in [1,9] or T - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) return; // get and analyse turns @@ -207,17 +209,17 @@ struct areal_areal interrupt_policy_areal_areal<Result> interrupt_policy(geometry1, geometry2, result); turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2, interrupt_policy); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) return; no_turns_aa_pred<Geometry2, Result, false> pred1(geometry2, result); for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) return; no_turns_aa_pred<Geometry1, Result, true> pred2(geometry1, result); for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) return; if ( turns.empty() ) @@ -242,7 +244,7 @@ struct areal_areal turns_analyser<turn_type, 0> analyser; analyse_each_turn(result, analyser, turns.begin(), turns.end()); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) return; } @@ -257,7 +259,7 @@ struct areal_areal uncertain_rings_analyser<0, Result, Geometry1, Geometry2> rings_analyser(result, geometry1, geometry2); analyse_uncertain_rings<0>::apply(rings_analyser, turns.begin(), turns.end()); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) return; } } @@ -281,7 +283,7 @@ struct areal_areal turns_analyser<turn_type, 1> analyser; analyse_each_turn(result, analyser, turns.begin(), turns.end()); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) return; } @@ -549,7 +551,7 @@ struct areal_areal { analyser.apply(res, it); - if ( res.interrupt ) + if ( BOOST_GEOMETRY_CONDITION(res.interrupt) ) return; } @@ -573,8 +575,7 @@ struct areal_areal { // check which relations must be analysed - if ( ! may_update<interior, interior, '2', transpose_result>(m_result) - && ! may_update<boundary, interior, '1', transpose_result>(m_result) ) + if ( ! may_update<interior, interior, '2', transpose_result>(m_result) ) { m_flags |= 1; } @@ -595,7 +596,7 @@ struct areal_areal inline void no_turns(segment_identifier const& seg_id) { // if those flags are set nothing will change - if ( (m_flags & 3) == 3 ) + if ( m_flags == 7 ) { return; } @@ -614,15 +615,18 @@ struct areal_areal // to know which other single geometries should be checked // TODO: optimize! e.g. use spatial index - // O(N) - running it in a loop would gives O(NM) + // O(N) - running it in a loop gives O(NM) int const pig = detail::within::point_in_geometry(range::front(range_ref), other_geometry); //BOOST_ASSERT(pig != 0); if ( pig > 0 ) { - update<boundary, interior, '1', transpose_result>(m_result); update<interior, interior, '2', transpose_result>(m_result); m_flags |= 1; + + update<boundary, interior, '1', transpose_result>(m_result); + update<exterior, interior, '2', transpose_result>(m_result); + m_flags |= 4; } else { @@ -696,12 +700,6 @@ struct areal_areal update<boundary, exterior, '1', transpose_result>(m_result); update<interior, exterior, '2', transpose_result>(m_result); m_flags |= 2; - - // not necessary since this will be checked in the next iteration - // but increases the pruning strength - // WARNING: this is not reflected in flags - update<exterior, boundary, '1', transpose_result>(m_result); - update<exterior, interior, '2', transpose_result>(m_result); } interrupt = m_flags == 7 || m_result.interrupt; // interrupt if the result won't be changed in the future @@ -795,7 +793,8 @@ struct areal_areal { segment_identifier const& seg_id = turn.operations[OpId].seg_id; - int count = boost::numeric_cast<int>( + signed_index_type + count = boost::numeric_cast<signed_index_type>( geometry::num_interior_rings( detail::single_geometry(analyser.geometry, seg_id))); @@ -803,7 +802,10 @@ struct areal_areal } template <typename Analyser, typename Turn> - static inline void for_no_turns_rings(Analyser & analyser, Turn const& turn, int first, int last) + static inline void for_no_turns_rings(Analyser & analyser, + Turn const& turn, + signed_index_type first, + signed_index_type last) { segment_identifier seg_id = turn.operations[OpId].seg_id; diff --git a/boost/geometry/algorithms/detail/relate/follow_helpers.hpp b/boost/geometry/algorithms/detail/relate/follow_helpers.hpp index 78fa03798d..2c44b009e7 100644 --- a/boost/geometry/algorithms/detail/relate/follow_helpers.hpp +++ b/boost/geometry/algorithms/detail/relate/follow_helpers.hpp @@ -14,6 +14,7 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_FOLLOW_HELPERS_HPP +#include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/range.hpp> //#include <boost/geometry/algorithms/detail/sub_range.hpp> @@ -98,9 +99,9 @@ struct for_each_disjoint_geometry_if<OpId, Geometry, Tag, true> std::vector<bool> detected_intersections(count, false); for ( TurnIt it = first ; it != last ; ++it ) { - int multi_index = it->operations[OpId].seg_id.multi_index; + signed_index_type multi_index = it->operations[OpId].seg_id.multi_index; BOOST_ASSERT(multi_index >= 0); - std::size_t index = static_cast<std::size_t>(multi_index); + std::size_t const index = static_cast<std::size_t>(multi_index); BOOST_ASSERT(index < count); detected_intersections[index] = true; } @@ -116,8 +117,8 @@ struct for_each_disjoint_geometry_if<OpId, Geometry, Tag, true> if ( *it == false ) { found = true; - bool cont = pred(range::at(geometry, - std::distance(detected_intersections.begin(), it))); + std::size_t const index = std::size_t(std::distance(detected_intersections.begin(), it)); + bool cont = pred(range::at(geometry, index)); if ( !cont ) break; } @@ -375,14 +376,14 @@ static inline bool is_ip_on_boundary(IntersectionPoint const& ip, bool res = false; // IP on the last point of the linestring - if ( (BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) + if ( BOOST_GEOMETRY_CONDITION(BoundaryQuery == boundary_back || BoundaryQuery == boundary_any) && operation_info.position == overlay::position_back ) { // check if this point is a boundary res = boundary_checker.template is_endpoint_boundary<boundary_back>(ip); } // IP on the last point of the linestring - else if ( (BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) + else if ( BOOST_GEOMETRY_CONDITION(BoundaryQuery == boundary_front || BoundaryQuery == boundary_any) && operation_info.position == overlay::position_front ) { // check if this point is a boundary diff --git a/boost/geometry/algorithms/detail/relate/linear_areal.hpp b/boost/geometry/algorithms/detail/relate/linear_areal.hpp index 3159ab329d..7d85a1d9a1 100644 --- a/boost/geometry/algorithms/detail/relate/linear_areal.hpp +++ b/boost/geometry/algorithms/detail/relate/linear_areal.hpp @@ -2,21 +2,23 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014. -// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015. +// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle - #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_LINEAR_AREAL_HPP #include <boost/core/ignore_unused.hpp> #include <boost/geometry/core/topological_dimension.hpp> + +#include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/range.hpp> #include <boost/geometry/algorithms/num_interior_rings.hpp> @@ -193,6 +195,33 @@ struct linear_areal typedef typename geometry::point_type<Geometry1>::type point1_type; typedef typename geometry::point_type<Geometry2>::type point2_type; + + template <typename Geometry> + struct is_multi + : boost::is_base_of + < + multi_tag, + typename tag<Geometry>::type + > + {}; + + template <typename Geom1, typename Geom2> + struct multi_turn_info + : turns::get_turns<Geom1, Geom2>::turn_info + { + multi_turn_info() : priority(0) {} + int priority; // single-geometry sorting priority + }; + + template <typename Geom1, typename Geom2> + struct turn_info_type + : boost::mpl::if_c + < + is_multi<Geometry2>::value, + multi_turn_info<Geom1, Geom2>, + typename turns::get_turns<Geom1, Geom2>::turn_info + > + {}; template <typename Result> static inline void apply(Geometry1 const& geometry1, Geometry2 const& geometry2, Result & result) @@ -202,17 +231,17 @@ struct linear_areal // The result should be FFFFFFFFF relate::set<exterior, exterior, result_dimension<Geometry2>::value, TransposeResult>(result);// FFFFFFFFd, d in [1,9] or T - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; // get and analyse turns - typedef typename turns::get_turns<Geometry1, Geometry2>::turn_info turn_type; + typedef typename turn_info_type<Geometry1, Geometry2>::type turn_type; std::vector<turn_type> turns; interrupt_policy_linear_areal<Geometry2, Result> interrupt_policy(geometry2, result); turns::get_turns<Geometry1, Geometry2>::apply(turns, geometry1, geometry2, interrupt_policy); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; boundary_checker<Geometry1> boundary_checker1(geometry1); @@ -224,12 +253,12 @@ struct linear_areal TransposeResult > pred1(geometry2, result, boundary_checker1); for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; no_turns_la_areal_pred<Result, !TransposeResult> pred2(result); for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; if ( turns.empty() ) @@ -238,14 +267,11 @@ struct linear_areal // This is set here because in the case if empty Areal geometry were passed // those shouldn't be set relate::set<exterior, interior, '2', TransposeResult>(result);// FFFFFF2Fd - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; { - // for different multi or same ring id: x, u, i, c - // for same multi and different ring id: c, i, u, x - typedef turns::less<0, turns::less_op_linear_areal<0> > less; - std::sort(turns.begin(), turns.end(), less()); + sort_dispatch(turns.begin(), turns.end(), is_multi<Geometry2>()); turns_analyser<turn_type> analyser; analyse_each_turn(result, analyser, @@ -253,7 +279,7 @@ struct linear_areal geometry1, geometry2, boundary_checker1); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; } @@ -297,7 +323,7 @@ struct linear_areal // if there was some previous ring if ( prev_seg_id_ptr != NULL ) { - int const next_ring_index = prev_seg_id_ptr->ring_index + 1; + signed_index_type const next_ring_index = prev_seg_id_ptr->ring_index + 1; BOOST_ASSERT(next_ring_index >= 0); // if one of the last rings of previous single geometry was ommited @@ -368,7 +394,7 @@ struct linear_areal // if there was some previous ring if ( prev_seg_id_ptr != NULL ) { - int const next_ring_index = prev_seg_id_ptr->ring_index + 1; + signed_index_type const next_ring_index = prev_seg_id_ptr->ring_index + 1; BOOST_ASSERT(next_ring_index >= 0); // if one of the last rings of previous single geometry was ommited @@ -383,6 +409,127 @@ struct linear_areal } } + template <typename It, typename Pred, typename Comp> + static void for_each_equal_range(It first, It last, Pred pred, Comp comp) + { + if ( first == last ) + return; + + It first_equal = first; + It prev = first; + for ( ++first ; ; ++first, ++prev ) + { + if ( first == last || !comp(*prev, *first) ) + { + pred(first_equal, first); + first_equal = first; + } + + if ( first == last ) + break; + } + } + + struct same_ip + { + template <typename Turn> + bool operator()(Turn const& left, Turn const& right) const + { + return left.operations[0].seg_id == right.operations[0].seg_id + && left.operations[0].fraction == right.operations[0].fraction; + } + }; + + struct same_ip_and_multi_index + { + template <typename Turn> + bool operator()(Turn const& left, Turn const& right) const + { + return same_ip()(left, right) + && left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index; + } + }; + + template <typename OpToPriority> + struct set_turns_group_priority + { + template <typename TurnIt> + void operator()(TurnIt first, TurnIt last) const + { + BOOST_ASSERT(first != last); + static OpToPriority op_to_priority; + // find the operation with the least priority + int least_priority = op_to_priority(first->operations[0]); + for ( TurnIt it = first + 1 ; it != last ; ++it ) + { + int priority = op_to_priority(it->operations[0]); + if ( priority < least_priority ) + least_priority = priority; + } + // set the least priority for all turns of the group + for ( TurnIt it = first ; it != last ; ++it ) + { + it->priority = least_priority; + } + } + }; + + template <typename SingleLess> + struct sort_turns_group + { + struct less + { + template <typename Turn> + bool operator()(Turn const& left, Turn const& right) const + { + return left.operations[1].seg_id.multi_index == right.operations[1].seg_id.multi_index ? + SingleLess()(left, right) : + left.priority < right.priority; + } + }; + + template <typename TurnIt> + void operator()(TurnIt first, TurnIt last) const + { + std::sort(first, last, less()); + } + }; + + template <typename TurnIt> + static void sort_dispatch(TurnIt first, TurnIt last, boost::true_type const& /*is_multi*/) + { + // sort turns by Linear seg_id, then by fraction, then by other multi_index + typedef turns::less<0, turns::less_other_multi_index<0> > less; + std::sort(first, last, less()); + + // For the same IP and multi_index - the same other's single geometry + // set priorities as the least operation found for the whole single geometry + // so e.g. single geometries containing 'u' will always be before those only containing 'i' + typedef turns::op_to_int<0,2,3,1,4,0> op_to_int_xuic; + for_each_equal_range(first, last, + set_turns_group_priority<op_to_int_xuic>(), // least operation in xuic order + same_ip_and_multi_index()); // other's multi_index + + // When priorities for single geometries are set now sort turns for the same IP + // if multi_index is the same sort them according to the single-less + // else use priority of the whole single-geometry set earlier + typedef turns::less<0, turns::less_op_linear_areal_single<0> > single_less; + for_each_equal_range(first, last, + sort_turns_group<single_less>(), + same_ip()); + } + + template <typename TurnIt> + static void sort_dispatch(TurnIt first, TurnIt last, boost::false_type const& /*is_multi*/) + { + // sort turns by Linear seg_id, then by fraction, then + // for same ring id: x, u, i, c + // for different ring id: c, i, u, x + typedef turns::less<0, turns::less_op_linear_areal_single<0> > less; + std::sort(first, last, less()); + } + + // interrupt policy which may be passed to get_turns to interrupt the analysis // based on the info in the passed result/mask template <typename Areal, typename Result> @@ -458,6 +605,8 @@ struct linear_areal , m_boundary_counter(0) , m_interior_detected(false) , m_first_interior_other_id_ptr(NULL) + , m_first_from_unknown(false) + , m_first_from_unknown_boundary_detected(false) {} template <typename Result, @@ -485,6 +634,11 @@ struct linear_areal const bool first_in_range = m_seg_watcher.update(seg_id); + // TODO: should apply() for the post-last ip be called if first_in_range ? + // this would unify how last points in ranges are handled + // possibly replacing parts of the code below + // e.g. for is_multi and m_interior_detected + // handle possible exit bool fake_enter_detected = false; if ( m_exit_watcher.get_exit_operation() == overlay::operation_union ) @@ -495,8 +649,24 @@ struct linear_areal { m_exit_watcher.reset_detected_exit(); - // not the last IP update<interior, exterior, '1', TransposeResult>(res); + + // next single geometry + if ( first_in_range && m_previous_turn_ptr ) + { + // NOTE: similar code is in the post-last-ip-apply() + segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; + + bool const prev_back_b = is_endpoint_on_boundary<boundary_back>( + range::back(sub_range(geometry, prev_seg_id)), + boundary_checker); + + // if there is a boundary on the last point + if ( prev_back_b ) + { + update<boundary, exterior, '0', TransposeResult>(res); + } + } } // fake exit point, reset state else if ( op == overlay::operation_intersection @@ -508,9 +678,12 @@ struct linear_areal } else if ( m_exit_watcher.get_exit_operation() == overlay::operation_blocked ) { - // ignore multiple BLOCKs - if ( op == overlay::operation_blocked ) + // ignore multiple BLOCKs for this same single geometry1 + if ( op == overlay::operation_blocked + && seg_id.multi_index == m_previous_turn_ptr->operations[op_id].seg_id.multi_index ) + { return; + } if ( ( op == overlay::operation_intersection || op == overlay::operation_continue ) @@ -522,11 +695,39 @@ struct linear_areal m_exit_watcher.reset_detected_exit(); } + if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value ) + && m_first_from_unknown ) + { + // For MultiPolygon many x/u operations may be generated as a first IP + // if for all turns x/u was generated and any of the Polygons doesn't contain the LineString + // then we know that the LineString is outside + // Similar with the u/u turns, if it was the first one it doesn't mean that the + // Linestring came from the exterior + if ( ( m_previous_operation == overlay::operation_blocked + && ( op != overlay::operation_blocked // operation different than block + || seg_id.multi_index != m_previous_turn_ptr->operations[op_id].seg_id.multi_index ) ) // or the next single-geometry + || ( m_previous_operation == overlay::operation_union + && ! turn_on_the_same_ip<op_id>(*m_previous_turn_ptr, *it) ) + ) + { + update<interior, exterior, '1', TransposeResult>(res); + if ( m_first_from_unknown_boundary_detected ) + { + update<boundary, exterior, '0', TransposeResult>(res); + } + + m_first_from_unknown = false; + m_first_from_unknown_boundary_detected = false; + } + } + // NOTE: THE WHOLE m_interior_detected HANDLING IS HERE BECAUSE WE CAN'T EFFICIENTLY SORT TURNS (CORRECTLY) // BECAUSE THE SAME IP MAY BE REPRESENTED BY TWO SEGMENTS WITH DIFFERENT DISTANCES // IT WOULD REQUIRE THE CALCULATION OF MAX DISTANCE // TODO: WE COULD GET RID OF THE TEST IF THE DISTANCES WERE NORMALIZED +// UPDATE: THEY SHOULD BE NORMALIZED NOW + // TODO: THIS IS POTENTIALLY ERROREOUS! // THIS ALGORITHM DEPENDS ON SOME SPECIFIC SEQUENCE OF OPERATIONS // IT WOULD GIVE WRONG RESULTS E.G. @@ -540,6 +741,30 @@ struct linear_areal { update<interior, interior, '1', TransposeResult>(res); m_interior_detected = false; + + // new range detected - reset previous state and check the boundary + if ( first_in_range ) + { + // actually it should be != NULL if m_interior_detected + // so an assert could be checked here + if ( m_previous_turn_ptr ) + { + segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; + + bool const prev_back_b = is_endpoint_on_boundary<boundary_back>( + range::back(sub_range(geometry, prev_seg_id)), + boundary_checker); + + // if there is a boundary on the last point + if ( prev_back_b ) + { + update<boundary, interior, '0', TransposeResult>(res); + } + } + + // The exit_watcher is reset below + // m_exit_watcher.reset(); + } } // fake interior overlap else if ( op == overlay::operation_continue ) @@ -560,10 +785,20 @@ struct linear_areal } } + // NOTE: If post-last-ip apply() was called this wouldn't be needed + if ( first_in_range ) + { + m_exit_watcher.reset(); + m_boundary_counter = 0; + m_first_from_unknown = false; + m_first_from_unknown_boundary_detected = false; + } + // i/u, c/u if ( op == overlay::operation_intersection || op == overlay::operation_continue ) // operation_boundary/operation_boundary_intersection { + bool const first_point = first_in_range || m_first_from_unknown; bool no_enters_detected = m_exit_watcher.is_outside(); m_exit_watcher.enter(*it); @@ -592,7 +827,7 @@ struct linear_areal { // don't add to the count for all met boundaries // only if this is the "new" boundary - if ( first_in_range || !it->operations[op_id].is_collinear ) + if ( first_point || !it->operations[op_id].is_collinear ) ++m_boundary_counter; update<interior, boundary, '1', TransposeResult>(res); @@ -619,7 +854,7 @@ struct linear_areal && it->operations[op_id].position != overlay::position_front ) { // TODO: calculate_from_inside() is only needed if the current Linestring is not closed - bool const from_inside = first_in_range + bool const from_inside = first_point && calculate_from_inside(geometry, other_geometry, *it); @@ -630,7 +865,7 @@ struct linear_areal update<interior, exterior, '1', TransposeResult>(res); // if it's the first IP then the first point is outside - if ( first_in_range ) + if ( first_point ) { bool const front_b = is_endpoint_on_boundary<boundary_front>( range::front(sub_range(geometry, seg_id)), @@ -647,6 +882,12 @@ struct linear_areal } } } + + if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value ) ) + { + m_first_from_unknown = false; + m_first_from_unknown_boundary_detected = false; + } } // u/u, x/u else if ( op == overlay::operation_union || op == overlay::operation_blocked ) @@ -709,7 +950,11 @@ struct linear_areal if ( it->operations[op_id].position != overlay::position_front ) { // TODO: calculate_from_inside() is only needed if the current Linestring is not closed - bool const first_from_inside = first_in_range + // NOTE: this is not enough for MultiPolygon and operation_blocked + // For LS/MultiPolygon multiple x/u turns may be generated + // the first checked Polygon may be the one which LS is outside for. + bool const first_point = first_in_range || m_first_from_unknown; + bool const first_from_inside = first_point && calculate_from_inside(geometry, other_geometry, *it); @@ -719,14 +964,26 @@ struct linear_areal // notify the exit_watcher that we started inside m_exit_watcher.enter(*it); + // and reset unknown flags since we know that we started inside + m_first_from_unknown = false; + m_first_from_unknown_boundary_detected = false; } else { - update<interior, exterior, '1', TransposeResult>(res); + if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value ) + /*&& ( op == overlay::operation_blocked + || op == overlay::operation_union )*/ ) // if we're here it's u or x + { + m_first_from_unknown = true; + } + else + { + update<interior, exterior, '1', TransposeResult>(res); + } } // first IP on the last segment point - this means that the first point is outside or inside - if ( first_in_range && ( !this_b || op_blocked ) ) + if ( first_point && ( !this_b || op_blocked ) ) { bool const front_b = is_endpoint_on_boundary<boundary_front>( range::front(sub_range(geometry, seg_id)), @@ -736,9 +993,23 @@ struct linear_areal if ( front_b ) { if ( first_from_inside ) + { update<boundary, interior, '0', TransposeResult>(res); + } else - update<boundary, exterior, '0', TransposeResult>(res); + { + if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value ) + /*&& ( op == overlay::operation_blocked + || op == overlay::operation_union )*/ ) // if we're here it's u or x + { + BOOST_ASSERT(m_first_from_unknown); + m_first_from_unknown_boundary_detected = true; + } + else + { + update<boundary, exterior, '0', TransposeResult>(res); + } + } } } } @@ -774,6 +1045,23 @@ struct linear_areal boost::ignore_unused(first, last); //BOOST_ASSERT( first != last ); + // For MultiPolygon many x/u operations may be generated as a first IP + // if for all turns x/u was generated and any of the Polygons doesn't contain the LineString + // then we know that the LineString is outside + if ( BOOST_GEOMETRY_CONDITION( is_multi<OtherGeometry>::value ) + && m_first_from_unknown ) + { + update<interior, exterior, '1', TransposeResult>(res); + if ( m_first_from_unknown_boundary_detected ) + { + update<boundary, exterior, '0', TransposeResult>(res); + } + + // done below + //m_first_from_unknown = false; + //m_first_from_unknown_boundary_detected = false; + } + // here, the possible exit is the real one // we know that we entered and now we exit if ( /*m_exit_watcher.get_exit_operation() == overlay::operation_union // THIS CHECK IS REDUNDANT @@ -822,12 +1110,37 @@ struct linear_areal } } - BOOST_ASSERT_MSG(m_previous_operation != overlay::operation_continue, - "Unexpected operation! Probably the error in get_turns(L,A) or relate(L,A)"); + // This condition may be false if the Linestring is lying on the Polygon's collinear spike + // if Polygon's spikes are not handled in get_turns() or relate() (they currently aren't) + //BOOST_ASSERT_MSG(m_previous_operation != overlay::operation_continue, + // "Unexpected operation! Probably the error in get_turns(L,A) or relate(L,A)"); + // Currently one c/c turn is generated for the exit + // when a Linestring is going out on a collinear spike + // When a Linestring is going in on a collinear spike + // the turn is not generated for the entry + // So assume it's the former + if ( m_previous_operation == overlay::operation_continue ) + { + update<interior, exterior, '1', TransposeResult>(res); + + segment_identifier const& prev_seg_id = m_previous_turn_ptr->operations[op_id].seg_id; + + bool const prev_back_b = is_endpoint_on_boundary<boundary_back>( + range::back(sub_range(geometry, prev_seg_id)), + boundary_checker); + + // if there is a boundary on the last point + if ( prev_back_b ) + { + update<boundary, exterior, '0', TransposeResult>(res); + } + } // Reset exit watcher before the analysis of the next Linestring m_exit_watcher.reset(); m_boundary_counter = 0; + m_first_from_unknown = false; + m_first_from_unknown_boundary_detected = false; } // check if the passed turn's segment of Linear geometry arrived @@ -852,8 +1165,8 @@ struct linear_areal BOOST_ASSERT(s2 > 2); std::size_t const seg_count2 = s2 - 1; - std::size_t const p_seg_ij = turn.operations[op_id].seg_id.segment_index; - std::size_t const q_seg_ij = turn.operations[other_op_id].seg_id.segment_index; + std::size_t const p_seg_ij = static_cast<std::size_t>(turn.operations[op_id].seg_id.segment_index); + std::size_t const q_seg_ij = static_cast<std::size_t>(turn.operations[other_op_id].seg_id.segment_index); BOOST_ASSERT(p_seg_ij + 1 < boost::size(range1)); BOOST_ASSERT(q_seg_ij + 1 < s2); @@ -876,7 +1189,7 @@ struct linear_areal // TODO: the following function should return immediately, however the worst case complexity is O(N) // It would be good to replace it with some O(1) mechanism range2_iterator qk_it = find_next_non_duplicated(boost::begin(range2), - boost::begin(range2) + q_seg_jk, + range::pos(range2, q_seg_jk), boost::end(range2)); // Will this sequence of points be always correct? @@ -945,6 +1258,8 @@ struct linear_areal unsigned m_boundary_counter; bool m_interior_detected; const segment_identifier * m_first_interior_other_id_ptr; + bool m_first_from_unknown; + bool m_first_from_unknown_boundary_detected; }; // call analyser.apply() for each turn in range @@ -971,7 +1286,7 @@ struct linear_areal geometry, other_geometry, boundary_checker); - if ( res.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( res.interrupt ) ) return; } @@ -1017,8 +1332,8 @@ struct linear_areal if ( first == last ) return last; - int const multi_index = first->operations[1].seg_id.multi_index; - int const ring_index = first->operations[1].seg_id.ring_index; + signed_index_type const multi_index = first->operations[1].seg_id.multi_index; + signed_index_type const ring_index = first->operations[1].seg_id.ring_index; fun(*first); ++first; diff --git a/boost/geometry/algorithms/detail/relate/linear_linear.hpp b/boost/geometry/algorithms/detail/relate/linear_linear.hpp index 263c82de56..20a22c3018 100644 --- a/boost/geometry/algorithms/detail/relate/linear_linear.hpp +++ b/boost/geometry/algorithms/detail/relate/linear_linear.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014. -// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015. +// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -16,6 +16,7 @@ #include <boost/core/ignore_unused.hpp> +#include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/range.hpp> #include <boost/geometry/algorithms/detail/sub_range.hpp> @@ -117,7 +118,7 @@ struct linear_linear { // The result should be FFFFFFFFF relate::set<exterior, exterior, result_dimension<Geometry1>::value>(result);// FFFFFFFFd, d in [1,9] or T - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; // get and analyse turns @@ -133,19 +134,19 @@ struct linear_linear detail::get_turns::get_turn_info_type<Geometry1, Geometry2, turns::assign_policy<true> > >::apply(turns, geometry1, geometry2, interrupt_policy); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; boundary_checker<Geometry1> boundary_checker1(geometry1); disjoint_linestring_pred<Result, boundary_checker<Geometry1>, false> pred1(result, boundary_checker1); for_each_disjoint_geometry_if<0, Geometry1>::apply(turns.begin(), turns.end(), geometry1, pred1); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; boundary_checker<Geometry2> boundary_checker2(geometry2); disjoint_linestring_pred<Result, boundary_checker<Geometry2>, true> pred2(result, boundary_checker2); for_each_disjoint_geometry_if<1, Geometry2>::apply(turns.begin(), turns.end(), geometry2, pred2); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; if ( turns.empty() ) @@ -171,7 +172,7 @@ struct linear_linear boundary_checker1, boundary_checker2); } - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( result.interrupt ) ) return; if ( may_update<interior, interior, '1', true>(result) @@ -250,6 +251,7 @@ struct linear_linear : m_previous_turn_ptr(NULL) , m_previous_operation(overlay::operation_none) , m_degenerated_turn_ptr(NULL) + , m_collinear_spike_exit(false) {} template <typename Result, @@ -383,7 +385,8 @@ struct linear_linear // if we didn't enter in the past, we were outside if ( was_outside && ! fake_enter_detected - && it->operations[op_id].position != overlay::position_front ) + && it->operations[op_id].position != overlay::position_front + && ! m_collinear_spike_exit ) { update<interior, exterior, '1', transpose_result>(res); @@ -402,6 +405,8 @@ struct linear_linear } } } + + m_collinear_spike_exit = false; } // u/i, u/u, u/x, x/i, x/u, x/x else if ( op == overlay::operation_union || op == overlay::operation_blocked ) @@ -418,6 +423,11 @@ struct linear_linear if ( !was_outside && is_collinear ) { m_exit_watcher.exit(*it, false); + // if the position is not set to back it must be a spike + if ( it->operations[op_id].position != overlay::position_back ) + { + m_collinear_spike_exit = true; + } } bool const op_blocked = op == overlay::operation_blocked; @@ -456,6 +466,7 @@ struct linear_linear // if we are truly outside if ( was_outside && it->operations[op_id].position != overlay::position_front + && ! m_collinear_spike_exit /*&& !is_collinear*/ ) { update<interior, exterior, '1', transpose_result>(res); @@ -526,6 +537,7 @@ struct linear_linear && ( !this_b || op_blocked ) && was_outside && it->operations[op_id].position != overlay::position_front + && ! m_collinear_spike_exit /*&& !is_collinear*/ ) { bool const front_b = is_endpoint_on_boundary<boundary_front>( @@ -607,6 +619,10 @@ struct linear_linear m_previous_turn_ptr = NULL; m_previous_operation = overlay::operation_none; m_degenerated_turn_ptr = NULL; + + // actually if this is set to true here there is some error + // in get_turns_ll or relate_ll, an assert could be checked here + m_collinear_spike_exit = false; } template <typename Result, @@ -724,6 +740,7 @@ struct linear_linear const TurnInfo * m_previous_turn_ptr; overlay::operation_type m_previous_operation; const TurnInfo * m_degenerated_turn_ptr; + bool m_collinear_spike_exit; }; template <typename Result, @@ -750,7 +767,7 @@ struct linear_linear geometry, other_geometry, boundary_checker, other_boundary_checker); - if ( res.interrupt ) + if ( BOOST_GEOMETRY_CONDITION( res.interrupt ) ) return; } diff --git a/boost/geometry/algorithms/detail/relate/point_geometry.hpp b/boost/geometry/algorithms/detail/relate/point_geometry.hpp index 86c236d357..62ab100919 100644 --- a/boost/geometry/algorithms/detail/relate/point_geometry.hpp +++ b/boost/geometry/algorithms/detail/relate/point_geometry.hpp @@ -2,15 +2,15 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014. -// Modifications copyright (c) 2013, 2014, Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015. +// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle - #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_POINT_GEOMETRY_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_POINT_GEOMETRY_HPP @@ -20,6 +20,8 @@ #include <boost/geometry/algorithms/detail/relate/topology_check.hpp> +#include <boost/geometry/util/condition.hpp> + namespace boost { namespace geometry { @@ -54,7 +56,7 @@ struct point_geometry set<exterior, exterior, result_dimension<Point>::value, Transpose>(result); - if ( result.interrupt ) + if ( BOOST_GEOMETRY_CONDITION(result.interrupt) ) return; // the point is on the boundary diff --git a/boost/geometry/algorithms/detail/relate/result.hpp b/boost/geometry/algorithms/detail/relate/result.hpp index 1bcb5275d2..e26bda67f2 100644 --- a/boost/geometry/algorithms/detail/relate/result.hpp +++ b/boost/geometry/algorithms/detail/relate/result.hpp @@ -2,15 +2,15 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014. -// Modifications copyright (c) 2013, 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015. +// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle - #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_RESULT_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_RESULT_HPP @@ -24,6 +24,7 @@ #include <boost/mpl/vector_c.hpp> #include <boost/geometry/core/topological_dimension.hpp> +#include <boost/geometry/util/condition.hpp> // TEMP - move this header to geometry/detail #include <boost/geometry/index/detail/tuples.hpp> @@ -235,17 +236,17 @@ struct interrupt_dispatch<Mask, true> static inline bool apply(Mask const& mask) { char m = mask.template get<F1, F2>(); - return check<V>(m); + return check_element<V>(m); } template <char V> - static inline bool check(char m) + static inline bool check_element(char m) { - if ( V >= '0' && V <= '9' ) + if ( BOOST_GEOMETRY_CONDITION(V >= '0' && V <= '9') ) { return m == 'F' || ( m < V && m >= '0' && m <= '9' ); } - else if ( V == 'T' ) + else if ( BOOST_GEOMETRY_CONDITION(V == 'T') ) { return m == 'F'; } @@ -394,7 +395,7 @@ inline bool may_update(Mask const& mask, Matrix const& matrix) ::template apply<F1, F2, D>(mask, matrix); } -// check() +// check_matrix() template <typename Mask> struct check_dispatch @@ -485,7 +486,7 @@ struct check_dispatch< boost::tuples::cons<Head, Tail> > }; template <typename Mask, typename Matrix> -inline bool check(Mask const& mask, Matrix const& matrix) +inline bool check_matrix(Mask const& mask, Matrix const& matrix) { return check_dispatch<Mask>::apply(mask, matrix); } @@ -546,7 +547,7 @@ public: result_type result() const { return !interrupt - && check(m_mask, static_cast<base_t const&>(*this)); + && check_matrix(m_mask, static_cast<base_t const&>(*this)); } template <field F1, field F2, char D> @@ -964,7 +965,7 @@ struct static_check_dispatch<StaticMask, true> }; template <typename StaticMask> -struct static_check +struct static_check_matrix { template <typename Matrix> static inline bool apply(Matrix const& matrix) @@ -997,7 +998,7 @@ public: result_type result() const { return (!Interrupt || !interrupt) - && static_check<StaticMask>:: + && static_check_matrix<StaticMask>:: apply(static_cast<base_t const&>(*this)); } diff --git a/boost/geometry/algorithms/detail/relate/turns.hpp b/boost/geometry/algorithms/detail/relate/turns.hpp index a2e56a8882..636c9756d8 100644 --- a/boost/geometry/algorithms/detail/relate/turns.hpp +++ b/boost/geometry/algorithms/detail/relate/turns.hpp @@ -1,16 +1,17 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2013, 2014. -// Modifications copyright (c) 2013-2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015. +// Modifications copyright (c) 2013-2015 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle - #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TURNS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_RELATE_TURNS_HPP @@ -19,8 +20,12 @@ #include <boost/geometry/algorithms/detail/overlay/get_turns.hpp> #include <boost/geometry/algorithms/detail/overlay/get_turn_info.hpp> +#include <boost/geometry/policies/robustness/get_rescale_policy.hpp> +#include <boost/geometry/policies/robustness/no_rescale_policy.hpp> + #include <boost/type_traits/is_base_of.hpp> + namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL @@ -35,10 +40,16 @@ struct assign_policy // GET_TURNS -template <typename Geometry1, - typename Geometry2, - typename GetTurnPolicy - = detail::get_turns::get_turn_info_type<Geometry1, Geometry2, assign_policy<> > > +template +< + typename Geometry1, + typename Geometry2, + typename GetTurnPolicy = detail::get_turns::get_turn_info_type + < + Geometry1, Geometry2, assign_policy<> + >, + typename RobustPolicy = detail::no_rescale_policy +> struct get_turns { typedef typename geometry::point_type<Geometry1>::type point1_type; @@ -46,11 +57,14 @@ struct get_turns typedef overlay::turn_info < point1_type, - typename segment_ratio_type<point1_type, detail::no_rescale_policy>::type, + typename segment_ratio_type<point1_type, RobustPolicy>::type, typename detail::get_turns::turn_operation_type < Geometry1, Geometry2, - typename segment_ratio_type<point1_type, detail::no_rescale_policy>::type + typename segment_ratio_type + < + point1_type, RobustPolicy + >::type >::type > turn_info; @@ -73,6 +87,12 @@ struct get_turns static const bool reverse1 = detail::overlay::do_reverse<geometry::point_order<Geometry1>::value>::value; static const bool reverse2 = detail::overlay::do_reverse<geometry::point_order<Geometry2>::value>::value; + RobustPolicy robust_policy = geometry::get_rescale_policy + < + RobustPolicy + >(geometry1, geometry2); + + dispatch::get_turns < typename geometry::tag<Geometry1>::type, @@ -83,7 +103,7 @@ struct get_turns reverse2, GetTurnPolicy >::apply(0, geometry1, 1, geometry2, - detail::no_rescale_policy(), turns, interrupt_policy); + robust_policy, turns, interrupt_policy); } }; @@ -125,7 +145,7 @@ struct less_op_linear_linear {}; template <std::size_t OpId> -struct less_op_linear_areal +struct less_op_linear_areal_single { template <typename Turn> inline bool operator()(Turn const& left, Turn const& right) const @@ -137,27 +157,19 @@ struct less_op_linear_areal segment_identifier const& left_other_seg_id = left.operations[other_op_id].seg_id; segment_identifier const& right_other_seg_id = right.operations[other_op_id].seg_id; - if ( left_other_seg_id.multi_index == right_other_seg_id.multi_index ) - { - typedef typename Turn::turn_operation_type operation_type; - operation_type const& left_operation = left.operations[OpId]; - operation_type const& right_operation = right.operations[OpId]; + typedef typename Turn::turn_operation_type operation_type; + operation_type const& left_operation = left.operations[OpId]; + operation_type const& right_operation = right.operations[OpId]; - if ( left_other_seg_id.ring_index == right_other_seg_id.ring_index ) - { - return op_to_int_xuic(left_operation) - < op_to_int_xuic(right_operation); - } - else - { - return op_to_int_xiuc(left_operation) - < op_to_int_xiuc(right_operation); - } + if ( left_other_seg_id.ring_index == right_other_seg_id.ring_index ) + { + return op_to_int_xuic(left_operation) + < op_to_int_xuic(right_operation); } else { - //return op_to_int_xuic(left.operations[OpId]) < op_to_int_xuic(right.operations[OpId]); - return left_other_seg_id.multi_index < right_other_seg_id.multi_index; + return op_to_int_xiuc(left_operation) + < op_to_int_xiuc(right_operation); } } }; @@ -217,6 +229,19 @@ struct less_op_areal_areal } }; +template <std::size_t OpId> +struct less_other_multi_index +{ + static const std::size_t other_op_id = (OpId + 1) % 2; + + template <typename Turn> + inline bool operator()(Turn const& left, Turn const& right) const + { + return left.operations[other_op_id].seg_id.multi_index + < right.operations[other_op_id].seg_id.multi_index; + } +}; + // sort turns by G1 - source_index == 0 by: // seg_id -> distance -> operation template <std::size_t OpId = 0, diff --git a/boost/geometry/algorithms/detail/sections/range_by_section.hpp b/boost/geometry/algorithms/detail/sections/range_by_section.hpp index 63feb12a71..d139a3fdd2 100644 --- a/boost/geometry/algorithms/detail/sections/range_by_section.hpp +++ b/boost/geometry/algorithms/detail/sections/range_by_section.hpp @@ -58,7 +58,8 @@ struct full_section_polygon { return section.ring_id.ring_index < 0 ? geometry::exterior_ring(polygon) - : range::at(geometry::interior_rings(polygon), section.ring_id.ring_index); + : range::at(geometry::interior_rings(polygon), + static_cast<std::size_t>(section.ring_id.ring_index)); } }; @@ -74,13 +75,15 @@ struct full_section_multi static inline typename ring_return_type<MultiGeometry const>::type apply( MultiGeometry const& multi, Section const& section) { + typedef typename boost::range_size<MultiGeometry>::type size_type; + BOOST_ASSERT ( section.ring_id.multi_index >= 0 - && section.ring_id.multi_index < int(boost::size(multi)) + && size_type(section.ring_id.multi_index) < boost::size(multi) ); - return Policy::apply(range::at(multi, section.ring_id.multi_index), section); + return Policy::apply(range::at(multi, size_type(section.ring_id.multi_index)), section); } }; diff --git a/boost/geometry/algorithms/detail/sections/section_box_policies.hpp b/boost/geometry/algorithms/detail/sections/section_box_policies.hpp new file mode 100644 index 0000000000..cf06700306 --- /dev/null +++ b/boost/geometry/algorithms/detail/sections/section_box_policies.hpp @@ -0,0 +1,49 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2015 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_SECTIONS_SECTION_BOX_POLICIES_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTION_BOX_POLICIES_HPP + + +#include <boost/geometry/algorithms/detail/disjoint/box_box.hpp> +#include <boost/geometry/algorithms/expand.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace section +{ + +struct get_section_box +{ + template <typename Box, typename Section> + static inline void apply(Box& total, Section const& section) + { + geometry::expand(total, section.bounding_box); + } +}; + +struct overlaps_section_box +{ + template <typename Box, typename Section> + static inline bool apply(Box const& box, Section const& section) + { + return ! detail::disjoint::disjoint_box_box(box, section.bounding_box); + } +}; + + +}} // namespace detail::section +#endif + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTION_BOX_POLICIES_HPP diff --git a/boost/geometry/algorithms/detail/sections/section_functions.hpp b/boost/geometry/algorithms/detail/sections/section_functions.hpp new file mode 100644 index 0000000000..ba1cf931b2 --- /dev/null +++ b/boost/geometry/algorithms/detail/sections/section_functions.hpp @@ -0,0 +1,66 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2015 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_SECTIONS_FUNCTIONS_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_FUNCTIONS_HPP + + +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/algorithms/detail/recalculate.hpp> +#include <boost/geometry/policies/robustness/robust_point_type.hpp> + + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DETAIL +namespace detail { namespace section +{ + +template +< + std::size_t Dimension, + typename Point, + typename RobustBox, + typename RobustPolicy +> +static inline bool preceding(int dir, Point const& point, + RobustBox const& robust_box, + RobustPolicy const& robust_policy) +{ + typename geometry::robust_point_type<Point, RobustPolicy>::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get<Dimension>(robust_point) < get<min_corner, Dimension>(robust_box)) + || (dir == -1 && get<Dimension>(robust_point) > get<max_corner, Dimension>(robust_box)); +} + +template +< + std::size_t Dimension, + typename Point, + typename RobustBox, + typename RobustPolicy +> +static inline bool exceeding(int dir, Point const& point, + RobustBox const& robust_box, + RobustPolicy const& robust_policy) +{ + typename geometry::robust_point_type<Point, RobustPolicy>::type robust_point; + geometry::recalculate(robust_point, point, robust_policy); + return (dir == 1 && get<Dimension>(robust_point) > get<max_corner, Dimension>(robust_box)) + || (dir == -1 && get<Dimension>(robust_point) < get<min_corner, Dimension>(robust_box)); +} + + +}} // namespace detail::section +#endif + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_FUNCTIONS_HPP diff --git a/boost/geometry/algorithms/detail/sections/sectionalize.hpp b/boost/geometry/algorithms/detail/sections/sectionalize.hpp index 250577c0c2..a744ea0a34 100644 --- a/boost/geometry/algorithms/detail/sections/sectionalize.hpp +++ b/boost/geometry/algorithms/detail/sections/sectionalize.hpp @@ -8,6 +8,9 @@ // This file was modified by Oracle on 2013, 2014. // Modifications copyright (c) 2013, 2014 Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -15,8 +18,6 @@ // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) -// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle - #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_SECTIONS_SECTIONALIZE_HPP @@ -25,7 +26,9 @@ #include <boost/concept/requires.hpp> #include <boost/mpl/assert.hpp> +#include <boost/mpl/vector_c.hpp> #include <boost/range.hpp> +#include <boost/static_assert.hpp> #include <boost/geometry/algorithms/assign.hpp> #include <boost/geometry/algorithms/expand.hpp> @@ -63,12 +66,15 @@ namespace boost { namespace geometry \tparam DimensionCount number of dimensions for this section \ingroup sectionalize */ -template <typename Box, std::size_t DimensionCount> +template +< + typename Box, + std::size_t DimensionCount +> struct section { typedef Box box_type; - - int id; // might be obsolete now, BSG 14-03-2011 TODO decide about this + static std::size_t const dimension_count = DimensionCount; int directions[DimensionCount]; ring_identifier ring_id; @@ -85,8 +91,7 @@ struct section bool is_non_duplicate_last; inline section() - : id(-1) - , begin_index(-1) + : begin_index(-1) , end_index(-1) , count(0) , range_count(0) @@ -122,75 +127,83 @@ struct sections : std::vector<section<Box, DimensionCount> > namespace detail { namespace sectionalize { -template <std::size_t Dimension, std::size_t DimensionCount> +template +< + typename DimensionVector, + std::size_t Index, + std::size_t Count +> struct get_direction_loop { + typedef typename boost::mpl::at_c<DimensionVector, Index>::type dimension; + template <typename Segment> static inline void apply(Segment const& seg, - int directions[DimensionCount]) + int directions[Count]) { typedef typename coordinate_type<Segment>::type coordinate_type; + coordinate_type const diff = - geometry::get<1, Dimension>(seg) - geometry::get<0, Dimension>(seg); + geometry::get<1, dimension::value>(seg) + - geometry::get<0, dimension::value>(seg); coordinate_type zero = coordinate_type(); - directions[Dimension] = diff > zero ? 1 : diff < zero ? -1 : 0; + directions[Index] = diff > zero ? 1 : diff < zero ? -1 : 0; get_direction_loop - < - Dimension + 1, DimensionCount - >::apply(seg, directions); + < + DimensionVector, + Index + 1, + Count + >::apply(seg, directions); } }; -template <std::size_t DimensionCount> -struct get_direction_loop<DimensionCount, DimensionCount> +template <typename DimensionVector, std::size_t Count> +struct get_direction_loop<DimensionVector, Count, Count> { template <typename Segment> - static inline void apply(Segment const&, int [DimensionCount]) + static inline void apply(Segment const&, int [Count]) {} }; -template <typename T, std::size_t Dimension, std::size_t DimensionCount> +//! Copy one static array to another +template <typename T, std::size_t Index, std::size_t Count> struct copy_loop { - static inline void apply(T const source[DimensionCount], - T target[DimensionCount]) + static inline void apply(T const source[Count], T target[Count]) { - target[Dimension] = source[Dimension]; - copy_loop<T, Dimension + 1, DimensionCount>::apply(source, target); + target[Index] = source[Index]; + copy_loop<T, Index + 1, Count>::apply(source, target); } }; -template <typename T, std::size_t DimensionCount> -struct copy_loop<T, DimensionCount, DimensionCount> +template <typename T, std::size_t Count> +struct copy_loop<T, Count, Count> { - static inline void apply(T const [DimensionCount], T [DimensionCount]) + static inline void apply(T const [Count], T [Count]) {} }; -template <typename T, std::size_t Dimension, std::size_t DimensionCount> +//! Compare two static arrays +template <typename T, std::size_t Index, std::size_t Count> struct compare_loop { - static inline bool apply(T const source[DimensionCount], - T const target[DimensionCount]) + static inline bool apply(T const array1[Count], T const array2[Count]) { - bool const not_equal = target[Dimension] != source[Dimension]; - - return not_equal + return array1[Index] != array2[Index] ? false : compare_loop < - T, Dimension + 1, DimensionCount - >::apply(source, target); + T, Index + 1, Count + >::apply(array1, array2); } }; -template <typename T, std::size_t DimensionCount> -struct compare_loop<T, DimensionCount, DimensionCount> +template <typename T, std::size_t Count> +struct compare_loop<T, Count, Count> { - static inline bool apply(T const [DimensionCount], - T const [DimensionCount]) + static inline bool apply(T const [Count], T const [Count]) { return true; @@ -215,9 +228,9 @@ struct check_duplicate_loop } return check_duplicate_loop - < + < Dimension + 1, DimensionCount - >::apply(seg); + >::apply(seg); } }; @@ -231,20 +244,21 @@ struct check_duplicate_loop<DimensionCount, DimensionCount> } }; -template <typename T, std::size_t Dimension, std::size_t DimensionCount> +//! Assign a value to a static array +template <typename T, std::size_t Index, std::size_t Count> struct assign_loop { - static inline void apply(T dims[DimensionCount], int const value) + static inline void apply(T dims[Count], int const value) { - dims[Dimension] = value; - assign_loop<T, Dimension + 1, DimensionCount>::apply(dims, value); + dims[Index] = value; + assign_loop<T, Index + 1, Count>::apply(dims, value); } }; -template <typename T, std::size_t DimensionCount> -struct assign_loop<T, DimensionCount, DimensionCount> +template <typename T, std::size_t Count> +struct assign_loop<T, Count, Count> { - static inline void apply(T [DimensionCount], int const) + static inline void apply(T [Count], int const) { } }; @@ -253,35 +267,42 @@ struct assign_loop<T, DimensionCount, DimensionCount> template < typename Point, - std::size_t DimensionCount + typename DimensionVector > struct sectionalize_part { + static const std::size_t dimension_count + = boost::mpl::size<DimensionVector>::value; + template < - typename Range, // Can be closeable_view + typename Iterator, typename RobustPolicy, typename Sections > static inline void apply(Sections& sections, - Range const& range, + Iterator begin, Iterator end, RobustPolicy const& robust_policy, - bool make_rescaled_boxes, ring_identifier ring_id, std::size_t max_count) { boost::ignore_unused_variable_warning(robust_policy); - boost::ignore_unused_variable_warning(make_rescaled_boxes); - typedef model::referring_segment<Point const> segment_type; typedef typename boost::range_value<Sections>::type section_type; - typedef model::segment - < - typename robust_point_type<Point, RobustPolicy>::type - > robust_segment_type; - typedef typename boost::range_iterator<Range const>::type iterator_type; + BOOST_STATIC_ASSERT + ( + (static_cast<int>(section_type::dimension_count) + == static_cast<int>(boost::mpl::size<DimensionVector>::value)) + ); - if ( boost::empty(range) ) + typedef typename geometry::robust_point_type + < + Point, + RobustPolicy + >::type robust_point_type; + + std::size_t const count = std::distance(begin, end); + if (count == 0) { return; } @@ -293,21 +314,24 @@ struct sectionalize_part bool mark_first_non_duplicated = true; std::size_t last_non_duplicate_index = sections.size(); - iterator_type it = boost::begin(range); + Iterator it = begin; + robust_point_type previous_robust_point; + geometry::recalculate(previous_robust_point, *it, robust_policy); - for(iterator_type previous = it++; - it != boost::end(range); + for(Iterator previous = it++; + it != end; ++previous, ++it, index++) { - segment_type segment(*previous, *it); - robust_segment_type robust_segment; - geometry::recalculate(robust_segment, segment, robust_policy); + robust_point_type current_robust_point; + geometry::recalculate(current_robust_point, *it, robust_policy); + model::referring_segment<robust_point_type> robust_segment( + previous_robust_point, current_robust_point); - int direction_classes[DimensionCount] = {0}; + int direction_classes[dimension_count] = {0}; get_direction_loop - < - 0, DimensionCount - >::apply(robust_segment, direction_classes); + < + DimensionVector, 0, dimension_count + >::apply(robust_segment, direction_classes); // if "dir" == 0 for all point-dimensions, it is duplicate. // Those sections might be omitted, if wished, lateron @@ -317,7 +341,7 @@ struct sectionalize_part { // Recheck because ALL dimensions should be checked, // not only first one. - // (DimensionCount might be < dimension<P>::value) + // (dimension_count might be < dimension<P>::value) if (check_duplicate_loop < 0, geometry::dimension<Point>::type::value @@ -332,21 +356,20 @@ struct sectionalize_part // Actual value is not important as long as it is not -1,0,1 assign_loop < - int, 0, DimensionCount + int, 0, dimension_count >::apply(direction_classes, -99); } } if (section.count > 0 - && (!compare_loop + && (! compare_loop < - int, 0, DimensionCount + int, 0, dimension_count >::apply(direction_classes, section.directions) - || section.count > max_count - ) + || section.count > max_count) ) { - if ( !section.duplicate ) + if (! section.duplicate) { last_non_duplicate_index = sections.size(); } @@ -361,9 +384,9 @@ struct sectionalize_part section.ring_id = ring_id; section.duplicate = duplicate; section.non_duplicate_index = ndi; - section.range_count = boost::size(range); + section.range_count = count; - if ( mark_first_non_duplicated && !duplicate ) + if (mark_first_non_duplicated && ! duplicate) { section.is_non_duplicate_first = true; mark_first_non_duplicated = false; @@ -371,25 +394,26 @@ struct sectionalize_part copy_loop < - int, 0, DimensionCount + int, 0, dimension_count >::apply(direction_classes, section.directions); - expand_box(*previous, robust_policy, section); + geometry::expand(section.bounding_box, previous_robust_point); } - expand_box(*it, robust_policy, section); + geometry::expand(section.bounding_box, current_robust_point); section.end_index = index + 1; section.count++; if (! duplicate) { ndi++; } + previous_robust_point = current_robust_point; } // Add last section if applicable if (section.count > 0) { - if ( !section.duplicate ) + if (! section.duplicate) { last_non_duplicate_index = sections.size(); } @@ -397,30 +421,21 @@ struct sectionalize_part sections.push_back(section); } - if ( last_non_duplicate_index < sections.size() - && !sections[last_non_duplicate_index].duplicate ) + if (last_non_duplicate_index < sections.size() + && ! sections[last_non_duplicate_index].duplicate) { sections[last_non_duplicate_index].is_non_duplicate_last = true; } } - - template <typename InputPoint, typename RobustPolicy, typename Section> - static inline void expand_box(InputPoint const& point, - RobustPolicy const& robust_policy, - Section& section) - { - typename geometry::point_type<typename Section::box_type>::type robust_point; - geometry::recalculate(robust_point, point, robust_policy); - geometry::expand(section.bounding_box, robust_point); - } }; template < - closure_selector Closure, bool Reverse, + closure_selector Closure, + bool Reverse, typename Point, - std::size_t DimensionCount + typename DimensionVector > struct sectionalize_range { @@ -432,13 +447,12 @@ struct sectionalize_range > static inline void apply(Range const& range, RobustPolicy const& robust_policy, - bool make_rescaled_boxes, Sections& sections, ring_identifier ring_id, std::size_t max_count) { - typedef typename closeable_view<Range const, Closure>::type cview_type; - typedef typename reversible_view + typedef typename closeable_view<Range const, Closure>::type cview_type; + typedef typename reversible_view < cview_type const, Reverse ? iterate_reverse : iterate_forward @@ -460,15 +474,16 @@ struct sectionalize_range return; } - sectionalize_part<Point, DimensionCount> - ::apply(sections, view, robust_policy, make_rescaled_boxes, ring_id, max_count); + sectionalize_part<Point, DimensionVector>::apply(sections, + boost::begin(view), boost::end(view), + robust_policy, ring_id, max_count); } }; template < bool Reverse, - std::size_t DimensionCount + typename DimensionVector > struct sectionalize_polygon { @@ -480,20 +495,18 @@ struct sectionalize_polygon > static inline void apply(Polygon const& poly, RobustPolicy const& robust_policy, - bool make_rescaled_boxes, Sections& sections, ring_identifier ring_id, std::size_t max_count) { typedef typename point_type<Polygon>::type point_type; - //typedef typename ring_type<Polygon>::type ring_type; typedef sectionalize_range - < + < closure<Polygon>::value, Reverse, - point_type, DimensionCount - > per_range; + point_type, DimensionVector + > per_range; ring_id.ring_index = -1; - per_range::apply(exterior_ring(poly), robust_policy, make_rescaled_boxes, sections, ring_id, max_count); + per_range::apply(exterior_ring(poly), robust_policy, sections, ring_id, max_count); ring_id.ring_index++; typename interior_return_type<Polygon const>::type @@ -501,15 +514,12 @@ struct sectionalize_polygon for (typename detail::interior_iterator<Polygon const>::type it = boost::begin(rings); it != boost::end(rings); ++it, ++ring_id.ring_index) { - per_range::apply(*it, robust_policy, make_rescaled_boxes, sections, ring_id, max_count); + per_range::apply(*it, robust_policy, sections, ring_id, max_count); } } }; -template -< - std::size_t DimensionCount -> +template <typename DimensionVector> struct sectionalize_box { template @@ -520,7 +530,6 @@ struct sectionalize_box > static inline void apply(Box const& box, RobustPolicy const& robust_policy, - bool make_rescaled_boxes, Sections& sections, ring_identifier const& ring_id, std::size_t max_count) { @@ -547,16 +556,16 @@ struct sectionalize_box points.push_back(ll); sectionalize_range - < + < closed, false, - point_type, - DimensionCount - >::apply(points, robust_policy, make_rescaled_boxes, sections, - ring_id, max_count); + point_type, + DimensionVector + >::apply(points, robust_policy, sections, + ring_id, max_count); } }; -template <std::size_t DimensionCount, typename Policy> +template <typename DimensionVector, typename Policy> struct sectionalize_multi { template @@ -567,7 +576,6 @@ struct sectionalize_multi > static inline void apply(MultiGeometry const& multi, RobustPolicy const& robust_policy, - bool make_rescaled_boxes, Sections& sections, ring_identifier ring_id, std::size_t max_count) { ring_id.multi_index = 0; @@ -576,25 +584,12 @@ struct sectionalize_multi it != boost::end(multi); ++it, ++ring_id.multi_index) { - Policy::apply(*it, robust_policy, make_rescaled_boxes, sections, ring_id, max_count); + Policy::apply(*it, robust_policy, sections, ring_id, max_count); } } }; template <typename Sections> -inline void set_section_unique_ids(Sections& sections) -{ - // Set ID's. - int index = 0; - for (typename boost::range_iterator<Sections>::type it = boost::begin(sections); - it != boost::end(sections); - ++it) - { - it->id = index++; - } -} - -template <typename Sections> inline void enlarge_sections(Sections& sections) { // Robustness issue. Increase sections a tiny bit such that all points are really within (and not on border) @@ -631,7 +626,7 @@ template typename Tag, typename Geometry, bool Reverse, - std::size_t DimensionCount + typename DimensionVector > struct sectionalize { @@ -646,29 +641,29 @@ template < typename Box, bool Reverse, - std::size_t DimensionCount + typename DimensionVector > -struct sectionalize<box_tag, Box, Reverse, DimensionCount> - : detail::sectionalize::sectionalize_box<DimensionCount> +struct sectionalize<box_tag, Box, Reverse, DimensionVector> + : detail::sectionalize::sectionalize_box<DimensionVector> {}; template < typename LineString, - std::size_t DimensionCount + typename DimensionVector > struct sectionalize < linestring_tag, LineString, false, - DimensionCount + DimensionVector > : detail::sectionalize::sectionalize_range < closed, false, typename point_type<LineString>::type, - DimensionCount + DimensionVector > {}; @@ -676,14 +671,14 @@ template < typename Ring, bool Reverse, - std::size_t DimensionCount + typename DimensionVector > -struct sectionalize<ring_tag, Ring, Reverse, DimensionCount> +struct sectionalize<ring_tag, Ring, Reverse, DimensionVector> : detail::sectionalize::sectionalize_range < geometry::closure<Ring>::value, Reverse, typename point_type<Ring>::type, - DimensionCount + DimensionVector > {}; @@ -691,12 +686,12 @@ template < typename Polygon, bool Reverse, - std::size_t DimensionCount + typename DimensionVector > -struct sectionalize<polygon_tag, Polygon, Reverse, DimensionCount> +struct sectionalize<polygon_tag, Polygon, Reverse, DimensionVector> : detail::sectionalize::sectionalize_polygon < - Reverse, DimensionCount + Reverse, DimensionVector > {}; @@ -704,16 +699,16 @@ template < typename MultiPolygon, bool Reverse, - std::size_t DimensionCount + typename DimensionVector > -struct sectionalize<multi_polygon_tag, MultiPolygon, Reverse, DimensionCount> +struct sectionalize<multi_polygon_tag, MultiPolygon, Reverse, DimensionVector> : detail::sectionalize::sectionalize_multi < - DimensionCount, + DimensionVector, detail::sectionalize::sectionalize_polygon < Reverse, - DimensionCount + DimensionVector > > @@ -723,17 +718,17 @@ template < typename MultiLinestring, bool Reverse, - std::size_t DimensionCount + typename DimensionVector > -struct sectionalize<multi_linestring_tag, MultiLinestring, Reverse, DimensionCount> +struct sectionalize<multi_linestring_tag, MultiLinestring, Reverse, DimensionVector> : detail::sectionalize::sectionalize_multi < - DimensionCount, + DimensionVector, detail::sectionalize::sectionalize_range < closed, false, typename point_type<MultiLinestring>::type, - DimensionCount + DimensionVector > > @@ -750,54 +745,61 @@ struct sectionalize<multi_linestring_tag, MultiLinestring, Reverse, DimensionCou \tparam Sections type of sections to create \param geometry geometry to create sections from \param robust_policy policy to handle robustness issues - \param enlarge_secion_boxes if true, boxes are enlarged a tiny bit to be sure - they really contain all geometries (w.r.t. robustness) \param sections structure with sections \param source_index index to assign to the ring_identifiers + \param max_count maximal number of points per section + (defaults to 10, this seems to give the fastest results) + */ -template<bool Reverse, typename Geometry, typename Sections, typename RobustPolicy> +template +< + bool Reverse, + typename DimensionVector, + typename Geometry, + typename Sections, + typename RobustPolicy +> inline void sectionalize(Geometry const& geometry, RobustPolicy const& robust_policy, - bool enlarge_secion_boxes, Sections& sections, - int source_index = 0) + int source_index = 0, + std::size_t max_count = 10) { concept::check<Geometry const>(); + typedef typename boost::range_value<Sections>::type section_type; + + // Compiletime check for point type of section boxes + // and point type related to robust policy + typedef typename geometry::coordinate_type + < + typename section_type::box_type + >::type ctype1; + typedef typename geometry::coordinate_type + < + typename geometry::robust_point_type + < + typename geometry::point_type<Geometry>::type, + RobustPolicy + >::type + >::type ctype2; + + BOOST_MPL_ASSERT((boost::is_same<ctype1, ctype2>)); + + sections.clear(); ring_identifier ring_id; ring_id.source_index = source_index; - // A maximum of 10 segments per section seems to give the fastest results dispatch::sectionalize < typename tag<Geometry>::type, Geometry, Reverse, - Sections::value - >::apply(geometry, robust_policy, enlarge_secion_boxes, sections, ring_id, 10); - - detail::sectionalize::set_section_unique_ids(sections); - if (! enlarge_secion_boxes) - { - detail::sectionalize::enlarge_sections(sections); - } -} - - -#if defined(BOOST_GEOMETRY_UNIT_TEST_SECTIONALIZE) -// Backwards compatibility -template<bool Reverse, typename Geometry, typename Sections> -inline void sectionalize(Geometry const& geometry, - Sections& sections, - int source_index = 0) -{ - return geometry::sectionalize<Reverse>(geometry, detail::no_rescale_policy(), - false, sections, - source_index); + DimensionVector + >::apply(geometry, robust_policy, sections, ring_id, max_count); } -#endif }} // namespace boost::geometry diff --git a/boost/geometry/algorithms/detail/single_geometry.hpp b/boost/geometry/algorithms/detail/single_geometry.hpp index c65ff8bf84..f38295ada6 100644 --- a/boost/geometry/algorithms/detail/single_geometry.hpp +++ b/boost/geometry/algorithms/detail/single_geometry.hpp @@ -55,7 +55,8 @@ struct single_geometry<Geometry, true> static inline return_type apply(Geometry & g, Id const& id) { BOOST_ASSERT(id.multi_index >= 0); - return range::at(g, id.multi_index); + typedef typename boost::range_size<Geometry>::type size_type; + return range::at(g, static_cast<size_type>(id.multi_index)); } }; diff --git a/boost/geometry/algorithms/detail/sub_range.hpp b/boost/geometry/algorithms/detail/sub_range.hpp index a68f31362a..eda3ce58ba 100644 --- a/boost/geometry/algorithms/detail/sub_range.hpp +++ b/boost/geometry/algorithms/detail/sub_range.hpp @@ -44,7 +44,7 @@ struct sub_range<Geometry, Tag, false> template <typename Geometry> struct sub_range<Geometry, polygon_tag, false> { - typedef typename geometry::ring_type<Geometry>::type & return_type; + typedef typename geometry::ring_return_type<Geometry>::type return_type; template <typename Id> static inline return_type apply(Geometry & geometry, Id const& id) @@ -55,7 +55,11 @@ struct sub_range<Geometry, polygon_tag, false> } else { - std::size_t ri = static_cast<std::size_t>(id.ring_index); + typedef typename boost::range_size + < + typename geometry::interior_type<Geometry>::type + >::type size_type; + size_type const ri = static_cast<size_type>(id.ring_index); return range::at(geometry::interior_rings(geometry), ri); } } @@ -81,7 +85,9 @@ struct sub_range<Geometry, Tag, true> return_type apply(Geometry & geometry, Id const& id) { BOOST_ASSERT(0 <= id.multi_index); - return sub_sub_range::apply(range::at(geometry, id.multi_index), id); + typedef typename boost::range_size<Geometry>::type size_type; + size_type const mi = static_cast<size_type>(id.multi_index); + return sub_sub_range::apply(range::at(geometry, mi), id); } }; diff --git a/boost/geometry/algorithms/detail/turns/print_turns.hpp b/boost/geometry/algorithms/detail/turns/print_turns.hpp index b339e11c94..9d4e45c41f 100644 --- a/boost/geometry/algorithms/detail/turns/print_turns.hpp +++ b/boost/geometry/algorithms/detail/turns/print_turns.hpp @@ -10,9 +10,9 @@ #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_TURNS_PRINT_TURNS_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_TURNS_PRINT_TURNS_HPP +#include <algorithm> #include <iostream> -#include <boost/foreach.hpp> #include <boost/range.hpp> #include <boost/geometry/algorithms/detail/overlay/traversal_info.hpp> @@ -27,21 +27,16 @@ namespace boost { namespace geometry namespace detail { namespace turns { - - -template <typename Geometry1, typename Geometry2, typename Turns> -static inline void print_turns(Geometry1 const& g1, - Geometry2 const& g2, - Turns const& turns) +struct turn_printer { - typedef typename boost::range_value<Turns>::type turn_info; + turn_printer(std::ostream & os) + : index(0) + , out(os) + {} - std::cout << geometry::wkt(g1) << std::endl; - std::cout << geometry::wkt(g2) << std::endl; - int index = 0; - BOOST_FOREACH(turn_info const& turn, turns) + template <typename Turn> + void operator()(Turn const& turn) { - std::ostream& out = std::cout; out << index << ": " << geometry::method_char(turn.method); @@ -81,8 +76,22 @@ static inline void print_turns(Geometry1 const& g1, out << ' ' << geometry::dsv(turn.point) << ' '; ++index; - std::cout << std::endl; + out << std::endl; } + + int index; + std::ostream & out; +}; + +template <typename Geometry1, typename Geometry2, typename Turns> +static inline void print_turns(Geometry1 const& g1, + Geometry2 const& g2, + Turns const& turns) +{ + std::cout << geometry::wkt(g1) << std::endl; + std::cout << geometry::wkt(g2) << std::endl; + + std::for_each(boost::begin(turns), boost::end(turns), turn_printer(std::cout)); } diff --git a/boost/geometry/algorithms/detail/vincenty_direct.hpp b/boost/geometry/algorithms/detail/vincenty_direct.hpp new file mode 100644 index 0000000000..775687cfdb --- /dev/null +++ b/boost/geometry/algorithms/detail/vincenty_direct.hpp @@ -0,0 +1,190 @@ +// Boost.Geometry + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_DIRECT_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_DIRECT_HPP + + +#include <boost/math/constants/constants.hpp> + +#include <boost/geometry/core/radius.hpp> +#include <boost/geometry/core/srs.hpp> + +#include <boost/geometry/util/math.hpp> + +#include <boost/geometry/algorithms/detail/flattening.hpp> + + +#ifndef BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS +#define BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS 1000 +#endif + + +namespace boost { namespace geometry { namespace detail +{ + +/*! +\brief The solution of the direct problem of geodesics on latlong coordinates, after Vincenty, 1975 +\author See + - http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf + - http://www.icsm.gov.au/gda/gdav2.3.pdf +\author Adapted from various implementations to get it close to the original document + - http://www.movable-type.co.uk/scripts/LatLongVincenty.html + - http://exogen.case.edu/projects/geopy/source/geopy.distance.html + - http://futureboy.homeip.net/fsp/colorize.fsp?fileName=navigation.frink + +*/ +template <typename CT> +class vincenty_direct +{ +public: + template <typename T, typename Dist, typename Azi, typename Spheroid> + vincenty_direct(T const& lo1, + T const& la1, + Dist const& distance, + Azi const& azimuth12, + Spheroid const& spheroid) + : lon1(lo1) + , lat1(la1) + , is_distance_zero(false) + { + if ( math::equals(distance, Dist(0)) || distance < Dist(0) ) + { + is_distance_zero = true; + return; + } + + CT const radius_a = CT(get_radius<0>(spheroid)); + CT const radius_b = CT(get_radius<2>(spheroid)); + flattening = geometry::detail::flattening<CT>(spheroid); + + sin_azimuth12 = sin(azimuth12); + cos_azimuth12 = cos(azimuth12); + + // U: reduced latitude, defined by tan U = (1-f) tan phi + one_min_f = CT(1) - flattening; + CT const tan_U1 = one_min_f * tan(lat1); + CT const sigma1 = atan2(tan_U1, cos_azimuth12); // (1) + + // may be calculated from tan using 1 sqrt() + CT const U1 = atan(tan_U1); + sin_U1 = sin(U1); + cos_U1 = cos(U1); + + sin_alpha = cos_U1 * sin_azimuth12; // (2) + sin_alpha_sqr = math::sqr(sin_alpha); + cos_alpha_sqr = CT(1) - sin_alpha_sqr; + + CT const b_sqr = radius_b * radius_b; + CT const u_sqr = cos_alpha_sqr * (radius_a * radius_a - b_sqr) / b_sqr; + CT const A = CT(1) + (u_sqr/CT(16384)) * (CT(4096) + u_sqr*(CT(-768) + u_sqr*(CT(320) - u_sqr*CT(175)))); // (3) + CT const B = (u_sqr/CT(1024))*(CT(256) + u_sqr*(CT(-128) + u_sqr*(CT(74) - u_sqr*CT(47)))); // (4) + + CT s_div_bA = distance / (radius_b * A); + sigma = s_div_bA; // (7) + + CT previous_sigma; + + int counter = 0; // robustness + + do + { + previous_sigma = sigma; + + CT const two_sigma_m = CT(2) * sigma1 + sigma; // (5) + + sin_sigma = sin(sigma); + cos_sigma = cos(sigma); + CT const sin_sigma_sqr = math::sqr(sin_sigma); + cos_2sigma_m = cos(two_sigma_m); + cos_2sigma_m_sqr = math::sqr(cos_2sigma_m); + + CT const delta_sigma = B * sin_sigma * (cos_2sigma_m + + (B/CT(4)) * ( cos_sigma * (CT(-1) + CT(2)*cos_2sigma_m_sqr) + - (B/CT(6) * cos_2sigma_m * (CT(-3)+CT(4)*sin_sigma_sqr) * (CT(-3)+CT(4)*cos_2sigma_m_sqr)) )); // (6) + + sigma = s_div_bA + delta_sigma; // (7) + + ++counter; // robustness + + } while ( geometry::math::abs(previous_sigma - sigma) > CT(1e-12) + //&& geometry::math::abs(sigma) < pi + && counter < BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS ); // robustness + } + + inline CT lat2() const + { + if ( is_distance_zero ) + { + return lat1; + } + + return atan2( sin_U1 * cos_sigma + cos_U1 * sin_sigma * cos_azimuth12, + one_min_f * math::sqrt(sin_alpha_sqr + math::sqr(sin_U1 * sin_sigma - cos_U1 * cos_sigma * cos_azimuth12))); // (8) + } + + inline CT lon2() const + { + if ( is_distance_zero ) + { + return lon1; + } + + CT const lambda = atan2( sin_sigma * sin_azimuth12, + cos_U1 * cos_sigma - sin_U1 * sin_sigma * cos_azimuth12); // (9) + CT const C = (flattening/CT(16)) * cos_alpha_sqr * ( CT(4) + flattening * ( CT(4) - CT(3) * cos_alpha_sqr ) ); // (10) + CT const L = lambda - (CT(1) - C) * flattening * sin_alpha + * ( sigma + C * sin_sigma * ( cos_2sigma_m + C * cos_sigma * ( CT(-1) + CT(2) * cos_2sigma_m_sqr ) ) ); // (11) + + return lon1 + L; + } + + inline CT azimuth21() const + { + // NOTE: signs of X and Y are different than in the original paper + return is_distance_zero ? + CT(0) : + atan2(-sin_alpha, sin_U1 * sin_sigma - cos_U1 * cos_sigma * cos_azimuth12); // (12) + } + +private: + CT sigma; + CT sin_sigma; + CT cos_sigma; + + CT cos_2sigma_m; + CT cos_2sigma_m_sqr; + + CT sin_alpha; + CT sin_alpha_sqr; + CT cos_alpha_sqr; + + CT sin_azimuth12; + CT cos_azimuth12; + + CT sin_U1; + CT cos_U1; + + CT flattening; + CT one_min_f; + + CT const lon1; + CT const lat1; + + bool is_distance_zero; +}; + +}}} // namespace boost::geometry::detail + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_DIRECT_HPP diff --git a/boost/geometry/algorithms/detail/vincenty_inverse.hpp b/boost/geometry/algorithms/detail/vincenty_inverse.hpp new file mode 100644 index 0000000000..861452af00 --- /dev/null +++ b/boost/geometry/algorithms/detail/vincenty_inverse.hpp @@ -0,0 +1,218 @@ +// Boost.Geometry + +// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. + +// This file was modified by Oracle on 2014. +// Modifications copyright (c) 2014 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_INVERSE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_INVERSE_HPP + + +#include <boost/math/constants/constants.hpp> + +#include <boost/geometry/core/radius.hpp> +#include <boost/geometry/core/srs.hpp> + +#include <boost/geometry/util/math.hpp> + +#include <boost/geometry/algorithms/detail/flattening.hpp> + + +#ifndef BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS +#define BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS 1000 +#endif + + +namespace boost { namespace geometry { namespace detail +{ + +/*! +\brief The solution of the inverse problem of geodesics on latlong coordinates, after Vincenty, 1975 +\author See + - http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf + - http://www.icsm.gov.au/gda/gdav2.3.pdf +\author Adapted from various implementations to get it close to the original document + - http://www.movable-type.co.uk/scripts/LatLongVincenty.html + - http://exogen.case.edu/projects/geopy/source/geopy.distance.html + - http://futureboy.homeip.net/fsp/colorize.fsp?fileName=navigation.frink + +*/ +template <typename CT> +class vincenty_inverse +{ +public: + template <typename T1, typename T2, typename Spheroid> + vincenty_inverse(T1 const& lon1, + T1 const& lat1, + T2 const& lon2, + T2 const& lat2, + Spheroid const& spheroid) + : is_result_zero(false) + { + if (math::equals(lat1, lat2) && math::equals(lon1, lon2)) + { + is_result_zero = true; + return; + } + + CT const c1 = 1; + CT const c2 = 2; + CT const c3 = 3; + CT const c4 = 4; + CT const c16 = 16; + CT const c_e_12 = CT(1e-12); + + CT const pi = geometry::math::pi<CT>(); + CT const two_pi = c2 * pi; + + // lambda: difference in longitude on an auxiliary sphere + CT L = lon2 - lon1; + CT lambda = L; + + if (L < -pi) L += two_pi; + if (L > pi) L -= two_pi; + + radius_a = CT(get_radius<0>(spheroid)); + radius_b = CT(get_radius<2>(spheroid)); + CT const flattening = geometry::detail::flattening<CT>(spheroid); + + // U: reduced latitude, defined by tan U = (1-f) tan phi + CT const one_min_f = c1 - flattening; + CT const tan_U1 = one_min_f * tan(lat1); // above (1) + CT const tan_U2 = one_min_f * tan(lat2); // above (1) + + // calculate sin U and cos U using trigonometric identities + CT const temp_den_U1 = math::sqrt(c1 + math::sqr(tan_U1)); + CT const temp_den_U2 = math::sqrt(c1 + math::sqr(tan_U2)); + // cos = 1 / sqrt(1 + tan^2) + cos_U1 = c1 / temp_den_U1; + cos_U2 = c1 / temp_den_U2; + // sin = tan / sqrt(1 + tan^2) + sin_U1 = tan_U1 / temp_den_U1; + sin_U2 = tan_U2 / temp_den_U2; + + // calculate sin U and cos U directly + //CT const U1 = atan(tan_U1); + //CT const U2 = atan(tan_U2); + //cos_U1 = cos(U1); + //cos_U2 = cos(U2); + //sin_U1 = tan_U1 * cos_U1; // sin(U1); + //sin_U2 = tan_U2 * cos_U2; // sin(U2); + + CT previous_lambda; + + int counter = 0; // robustness + + do + { + previous_lambda = lambda; // (13) + sin_lambda = sin(lambda); + cos_lambda = cos(lambda); + sin_sigma = math::sqrt(math::sqr(cos_U2 * sin_lambda) + math::sqr(cos_U1 * sin_U2 - sin_U1 * cos_U2 * cos_lambda)); // (14) + CT cos_sigma = sin_U1 * sin_U2 + cos_U1 * cos_U2 * cos_lambda; // (15) + sin_alpha = cos_U1 * cos_U2 * sin_lambda / sin_sigma; // (17) + cos2_alpha = c1 - math::sqr(sin_alpha); + cos2_sigma_m = math::equals(cos2_alpha, 0) ? 0 : cos_sigma - c2 * sin_U1 * sin_U2 / cos2_alpha; // (18) + + CT C = flattening/c16 * cos2_alpha * (c4 + flattening * (c4 - c3 * cos2_alpha)); // (10) + sigma = atan2(sin_sigma, cos_sigma); // (16) + lambda = L + (c1 - C) * flattening * sin_alpha * + (sigma + C * sin_sigma * ( cos2_sigma_m + C * cos_sigma * (-c1 + c2 * math::sqr(cos2_sigma_m)))); // (11) + + ++counter; // robustness + + } while ( geometry::math::abs(previous_lambda - lambda) > c_e_12 + && geometry::math::abs(lambda) < pi + && counter < BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS ); // robustness + } + + inline CT distance() const + { + if ( is_result_zero ) + { + return CT(0); + } + + // Oops getting hard here + // (again, problem is that ttmath cannot divide by doubles, which is OK) + CT const c1 = 1; + CT const c2 = 2; + CT const c3 = 3; + CT const c4 = 4; + CT const c6 = 6; + CT const c47 = 47; + CT const c74 = 74; + CT const c128 = 128; + CT const c256 = 256; + CT const c175 = 175; + CT const c320 = 320; + CT const c768 = 768; + CT const c1024 = 1024; + CT const c4096 = 4096; + CT const c16384 = 16384; + + //CT sqr_u = cos2_alpha * (math::sqr(radius_a) - math::sqr(radius_b)) / math::sqr(radius_b); // above (1) + CT sqr_u = cos2_alpha * ( math::sqr(radius_a / radius_b) - c1 ); // above (1) + + CT A = c1 + sqr_u/c16384 * (c4096 + sqr_u * (-c768 + sqr_u * (c320 - c175 * sqr_u))); // (3) + CT B = sqr_u/c1024 * (c256 + sqr_u * ( -c128 + sqr_u * (c74 - c47 * sqr_u))); // (4) + CT delta_sigma = B * sin_sigma * ( cos2_sigma_m + (B/c4) * (cos(sigma)* (-c1 + c2 * cos2_sigma_m) + - (B/c6) * cos2_sigma_m * (-c3 + c4 * math::sqr(sin_sigma)) * (-c3 + c4 * cos2_sigma_m))); // (6) + + return radius_b * A * (sigma - delta_sigma); // (19) + } + + inline CT azimuth12() const + { + return is_result_zero ? + CT(0) : + atan2(cos_U2 * sin_lambda, cos_U1 * sin_U2 - sin_U1 * cos_U2 * cos_lambda); // (20) + } + + inline CT azimuth21() const + { + // NOTE: signs of X and Y are different than in the original paper + return is_result_zero ? + CT(0) : + atan2(-cos_U1 * sin_lambda, sin_U1 * cos_U2 - cos_U1 * sin_U2 * cos_lambda); // (21) + } + +private: + // alpha: azimuth of the geodesic at the equator + CT cos2_alpha; + CT sin_alpha; + + // sigma: angular distance p1,p2 on the sphere + // sigma1: angular distance on the sphere from the equator to p1 + // sigma_m: angular distance on the sphere from the equator to the midpoint of the line + CT sigma; + CT sin_sigma; + CT cos2_sigma_m; + + CT sin_lambda; + CT cos_lambda; + + // set only once + CT cos_U1; + CT cos_U2; + CT sin_U1; + CT sin_U2; + + // set only once + CT radius_a; + CT radius_b; + + bool is_result_zero; +}; + +}}} // namespace boost::geometry::detail + + +#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_INVERSE_HPP diff --git a/boost/geometry/algorithms/detail/within/point_in_geometry.hpp b/boost/geometry/algorithms/detail/within/point_in_geometry.hpp index 6f1c1816cb..e7486f0e45 100644 --- a/boost/geometry/algorithms/detail/within/point_in_geometry.hpp +++ b/boost/geometry/algorithms/detail/within/point_in_geometry.hpp @@ -5,8 +5,8 @@ // Copyright (c) 2009-2012 Mateusz Loskot, London, UK. // Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2013, 2014. -// Modifications copyright (c) 2013, 2014, Oracle and/or its affiliates. +// This file was modified by Oracle on 2013, 2014, 2015. +// Modifications copyright (c) 2013-2015, Oracle and/or its affiliates. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -21,6 +21,7 @@ #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_WITHIN_POINT_IN_GEOMETRY_HPP #include <boost/assert.hpp> +#include <boost/core/ignore_unused.hpp> #include <boost/mpl/assert.hpp> #include <boost/range.hpp> #include <boost/type_traits/is_same.hpp> @@ -35,6 +36,7 @@ #include <boost/geometry/strategies/within.hpp> #include <boost/geometry/strategies/covered_by.hpp> +#include <boost/geometry/util/range.hpp> #include <boost/geometry/views/detail/normalized_view.hpp> namespace boost { namespace geometry { @@ -58,7 +60,7 @@ inline T check_result_type(T result) template <typename Point, typename Range, typename Strategy> inline int point_in_range(Point const& point, Range const& range, Strategy const& strategy) { - boost::ignore_unused_variable_warning(strategy); + boost::ignore_unused(strategy); typedef typename boost::range_iterator<Range const>::type iterator_type; typename Strategy::state_type state; @@ -147,7 +149,7 @@ struct point_in_geometry<Point2, point_tag> template <typename Point1, typename Strategy> static inline int apply(Point1 const& point1, Point2 const& point2, Strategy const& strategy) { - boost::ignore_unused_variable_warning(strategy); + boost::ignore_unused(strategy); return strategy.apply(point1, point2) ? 1 : -1; } }; @@ -158,6 +160,8 @@ struct point_in_geometry<Segment, segment_tag> template <typename Point, typename Strategy> static inline int apply(Point const& point, Segment const& segment, Strategy const& strategy) { + boost::ignore_unused(strategy); + typedef typename geometry::point_type<Segment>::type point_type; point_type p0, p1; // TODO: don't copy points @@ -194,11 +198,11 @@ struct point_in_geometry<Linestring, linestring_tag> return -1; // exterior // if the linestring doesn't have a boundary - if ( detail::equals::equals_point_point(*boost::begin(linestring), *(--boost::end(linestring))) ) + if (detail::equals::equals_point_point(range::front(linestring), range::back(linestring))) return 1; // interior // else if the point is equal to the one of the terminal points - else if ( detail::equals::equals_point_point(point, *boost::begin(linestring)) - || detail::equals::equals_point_point(point, *(--boost::end(linestring))) ) + else if (detail::equals::equals_point_point(point, range::front(linestring)) + || detail::equals::equals_point_point(point, range::back(linestring))) return 0; // boundary else return 1; // interior @@ -207,7 +211,7 @@ struct point_in_geometry<Linestring, linestring_tag> // throw an exception here? /*else if ( count == 1 ) { - if ( detail::equals::equals_point_point(point, *boost::begin(linestring)) ) + if ( detail::equals::equals_point_point(point, range::front(linestring)) ) return 1; }*/ @@ -333,8 +337,8 @@ struct point_in_geometry<Geometry, multi_linestring_tag> if ( boost::size(*it) < 2 ) continue; - point_type const& front = *boost::begin(*it); - point_type const& back = *(--boost::end(*it)); + point_type const& front = range::front(*it); + point_type const& back = range::back(*it); // is closed_ring - no boundary if ( detail::equals::equals_point_point(front, back) ) diff --git a/boost/geometry/algorithms/dispatch/is_valid.hpp b/boost/geometry/algorithms/dispatch/is_valid.hpp index 266bab9181..5d8108655c 100644 --- a/boost/geometry/algorithms/dispatch/is_valid.hpp +++ b/boost/geometry/algorithms/dispatch/is_valid.hpp @@ -27,10 +27,8 @@ template < typename Geometry, typename Tag = typename tag<Geometry>::type, - // for linear geometries: determines if spikes are allowed - bool AllowSpikes = true, - // for areal geometries: determines if duplicate points are allowed - bool AllowDuplicates = true + // for multi-geometries: determines if empty multi-geometries are allowed + bool AllowEmptyMultiGeometries = true > struct is_valid : not_implemented<Geometry> diff --git a/boost/geometry/algorithms/envelope.hpp b/boost/geometry/algorithms/envelope.hpp index e06ed71e81..bd1ea9cb7a 100644 --- a/boost/geometry/algorithms/envelope.hpp +++ b/boost/geometry/algorithms/envelope.hpp @@ -18,8 +18,9 @@ #include <boost/numeric/conversion/cast.hpp> #include <boost/range.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/algorithms/assign.hpp> diff --git a/boost/geometry/algorithms/equals.hpp b/boost/geometry/algorithms/equals.hpp index c6b718da1b..7c709c808d 100644 --- a/boost/geometry/algorithms/equals.hpp +++ b/boost/geometry/algorithms/equals.hpp @@ -1,14 +1,15 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. -// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. -// Copyright (c) 2014 Adam Wulkiewicz, Lodz, Poland. +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. +// Copyright (c) 2014-2015 Adam Wulkiewicz, Lodz, Poland. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2015. +// Modifications copyright (c) 2014-2015 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -26,6 +27,10 @@ #include <boost/range.hpp> +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> +#include <boost/variant/variant_fwd.hpp> + #include <boost/geometry/core/access.hpp> #include <boost/geometry/core/coordinate_dimension.hpp> #include <boost/geometry/core/geometry_id.hpp> @@ -50,8 +55,6 @@ #include <boost/geometry/views/detail/indexed_point_view.hpp> -#include <boost/variant/static_visitor.hpp> -#include <boost/variant/apply_visitor.hpp> namespace boost { namespace geometry { @@ -234,8 +237,6 @@ template <typename P1, typename P2, std::size_t DimensionCount, bool Reverse> struct equals<P1, P2, point_tag, point_tag, DimensionCount, Reverse> : geometry::detail::not_ < - P1, - P2, detail::disjoint::point_point<P1, P2, 0, DimensionCount> > {}; diff --git a/boost/geometry/algorithms/expand.hpp b/boost/geometry/algorithms/expand.hpp index 19e40aa2d0..9dc0a48e06 100644 --- a/boost/geometry/algorithms/expand.hpp +++ b/boost/geometry/algorithms/expand.hpp @@ -20,6 +20,10 @@ #include <boost/numeric/conversion/cast.hpp> +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> +#include <boost/variant/variant_fwd.hpp> + #include <boost/geometry/algorithms/not_implemented.hpp> #include <boost/geometry/core/coordinate_dimension.hpp> #include <boost/geometry/geometries/concepts/check.hpp> @@ -29,9 +33,6 @@ #include <boost/geometry/strategies/compare.hpp> #include <boost/geometry/policies/compare.hpp> -#include <boost/variant/static_visitor.hpp> -#include <boost/variant/apply_visitor.hpp> - namespace boost { namespace geometry { diff --git a/boost/geometry/algorithms/length.hpp b/boost/geometry/algorithms/length.hpp index 6cbec5303e..fad17ade46 100644 --- a/boost/geometry/algorithms/length.hpp +++ b/boost/geometry/algorithms/length.hpp @@ -4,10 +4,11 @@ // Copyright (c) 2008-2014 Bruno Lalande, Paris, France. // Copyright (c) 2009-2014 Mateusz Loskot, London, UK. -// This file was modified by Oracle on 2014. -// Modifications copyright (c) 2014, Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2015. +// Modifications copyright (c) 2014-2015, Oracle and/or its affiliates. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -22,6 +23,7 @@ #include <iterator> #include <boost/concept_check.hpp> +#include <boost/core/ignore_unused.hpp> #include <boost/range.hpp> #include <boost/mpl/fold.hpp> @@ -69,6 +71,7 @@ struct segment_length static inline typename default_length_result<Segment>::type apply( Segment const& segment, Strategy const& strategy) { + boost::ignore_unused(strategy); typedef typename point_type<Segment>::type point_type; point_type p1, p2; geometry::detail::assign_point_from_index<0>(segment, p1); @@ -92,7 +95,7 @@ struct range_length static inline return_type apply( Range const& range, Strategy const& strategy) { - boost::ignore_unused_variable_warning(strategy); + boost::ignore_unused(strategy); typedef typename closeable_view<Range const, Closure>::type view_type; typedef typename boost::range_iterator < diff --git a/boost/geometry/algorithms/not_implemented.hpp b/boost/geometry/algorithms/not_implemented.hpp index cd40a2772f..9c416074ed 100644 --- a/boost/geometry/algorithms/not_implemented.hpp +++ b/boost/geometry/algorithms/not_implemented.hpp @@ -1,8 +1,13 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2009-2015 Mateusz Loskot, London, UK. + +// This file was modified by Oracle on 2015. +// Modifications copyright (c) 2015, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands. @@ -17,6 +22,7 @@ #include <boost/mpl/assert.hpp> +#include <boost/mpl/identity.hpp> #include <boost/geometry/core/tags.hpp> @@ -87,7 +93,7 @@ template <> struct tag_to_term<multi_point_tag> { typedef info::MULT template <> struct tag_to_term<multi_linestring_tag> { typedef info::MULTI_LINESTRING type; }; template <> struct tag_to_term<multi_polygon_tag> { typedef info::MULTI_POLYGON type; }; template <> struct tag_to_term<geometry_collection_tag> { typedef info::GEOMETRY_COLLECTION type; }; -template <int D> struct tag_to_term<mpl::int_<D> > { typedef info::DIMENSION<D> type; }; +template <int D> struct tag_to_term<boost::mpl::int_<D> > { typedef info::DIMENSION<D> type; }; } @@ -103,9 +109,18 @@ struct not_implemented : nyi::not_implemented_tag, nyi::not_implemented_error < - typename mpl::identity<typename nyi::tag_to_term<Term1>::type>::type, - typename mpl::identity<typename nyi::tag_to_term<Term2>::type>::type, - typename mpl::identity<typename nyi::tag_to_term<Term3>::type>::type + typename boost::mpl::identity + < + typename nyi::tag_to_term<Term1>::type + >::type, + typename boost::mpl::identity + < + typename nyi::tag_to_term<Term2>::type + >::type, + typename boost::mpl::identity + < + typename nyi::tag_to_term<Term3>::type + >::type > {}; diff --git a/boost/geometry/algorithms/num_geometries.hpp b/boost/geometry/algorithms/num_geometries.hpp index d37d0bfabe..8144c22ab0 100644 --- a/boost/geometry/algorithms/num_geometries.hpp +++ b/boost/geometry/algorithms/num_geometries.hpp @@ -23,8 +23,9 @@ #include <cstddef> #include <boost/range.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/algorithms/not_implemented.hpp> diff --git a/boost/geometry/algorithms/num_interior_rings.hpp b/boost/geometry/algorithms/num_interior_rings.hpp index e198b37a75..04b4eb2a7c 100644 --- a/boost/geometry/algorithms/num_interior_rings.hpp +++ b/boost/geometry/algorithms/num_interior_rings.hpp @@ -24,8 +24,8 @@ #include <boost/range.hpp> -#include <boost/variant/static_visitor.hpp> #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/core/tag.hpp> diff --git a/boost/geometry/algorithms/num_points.hpp b/boost/geometry/algorithms/num_points.hpp index 4c2ad3b08c..214fe9c8b0 100644 --- a/boost/geometry/algorithms/num_points.hpp +++ b/boost/geometry/algorithms/num_points.hpp @@ -26,8 +26,8 @@ #include <boost/range.hpp> -#include <boost/variant/static_visitor.hpp> #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/core/closure.hpp> diff --git a/boost/geometry/algorithms/num_segments.hpp b/boost/geometry/algorithms/num_segments.hpp index cbb5685967..08af226caf 100644 --- a/boost/geometry/algorithms/num_segments.hpp +++ b/boost/geometry/algorithms/num_segments.hpp @@ -17,8 +17,8 @@ #include <boost/range.hpp> -#include <boost/variant/static_visitor.hpp> #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/core/closure.hpp> diff --git a/boost/geometry/algorithms/perimeter.hpp b/boost/geometry/algorithms/perimeter.hpp index 0ec153c1f4..1b5ccacf9c 100644 --- a/boost/geometry/algorithms/perimeter.hpp +++ b/boost/geometry/algorithms/perimeter.hpp @@ -20,8 +20,9 @@ #define BOOST_GEOMETRY_ALGORITHMS_PERIMETER_HPP #include <boost/range/metafunctions.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/algorithms/length.hpp> diff --git a/boost/geometry/algorithms/remove_spikes.hpp b/boost/geometry/algorithms/remove_spikes.hpp index e62ea9fe3d..a96a2c29d0 100644 --- a/boost/geometry/algorithms/remove_spikes.hpp +++ b/boost/geometry/algorithms/remove_spikes.hpp @@ -16,8 +16,9 @@ #include <boost/range.hpp> #include <boost/type_traits/remove_reference.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/core/closure.hpp> @@ -26,11 +27,15 @@ #include <boost/geometry/core/interior_rings.hpp> #include <boost/geometry/core/point_order.hpp> #include <boost/geometry/core/tags.hpp> + #include <boost/geometry/geometries/concepts/check.hpp> + #include <boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp> #include <boost/geometry/algorithms/detail/interior_iterator.hpp> #include <boost/geometry/algorithms/clear.hpp> +#include <boost/geometry/util/condition.hpp> + /* Remove spikes from a ring/polygon. @@ -94,7 +99,7 @@ struct range_remove_spikes } // For a closed-polygon, remove closing point, this makes checking first point(s) easier and consistent - if (geometry::closure<Range>::value == geometry::closed) + if ( BOOST_GEOMETRY_CONDITION(geometry::closure<Range>::value == geometry::closed) ) { cleaned.pop_back(); } @@ -127,7 +132,7 @@ struct range_remove_spikes } // Close if necessary - if (geometry::closure<Range>::value == geometry::closed) + if ( BOOST_GEOMETRY_CONDITION(geometry::closure<Range>::value == geometry::closed) ) { cleaned.push_back(cleaned.front()); } diff --git a/boost/geometry/algorithms/reverse.hpp b/boost/geometry/algorithms/reverse.hpp index 17b23ffdfb..578771bfe3 100644 --- a/boost/geometry/algorithms/reverse.hpp +++ b/boost/geometry/algorithms/reverse.hpp @@ -19,8 +19,9 @@ #include <boost/range.hpp> #include <boost/type_traits/remove_reference.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/algorithms/detail/interior_iterator.hpp> diff --git a/boost/geometry/algorithms/simplify.hpp b/boost/geometry/algorithms/simplify.hpp index 101b1324e1..0047546718 100644 --- a/boost/geometry/algorithms/simplify.hpp +++ b/boost/geometry/algorithms/simplify.hpp @@ -1,8 +1,8 @@ // Boost.Geometry (aka GGL, Generic Geometry Library) -// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// Copyright (c) 2008-2012 Bruno Lalande, Paris, France. -// Copyright (c) 2009-2012 Mateusz Loskot, London, UK. +// Copyright (c) 2007-2015 Barend Gehrels, Amsterdam, the Netherlands. +// Copyright (c) 2008-2015 Bruno Lalande, Paris, France. +// Copyright (c) 2009-2015 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. @@ -16,9 +16,11 @@ #include <cstddef> +#include <boost/core/ignore_unused.hpp> #include <boost/range.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/core/cs.hpp> @@ -32,6 +34,7 @@ #include <boost/geometry/strategies/agnostic/simplify_douglas_peucker.hpp> #include <boost/geometry/strategies/concepts/simplify_concept.hpp> #include <boost/geometry/strategies/default_strategy.hpp> +#include <boost/geometry/strategies/distance.hpp> #include <boost/geometry/algorithms/clear.hpp> #include <boost/geometry/algorithms/convert.hpp> @@ -52,6 +55,8 @@ struct simplify_range_insert static inline void apply(Range const& range, OutputIterator out, Distance const& max_distance, Strategy const& strategy) { + boost::ignore_unused(strategy); + if (boost::size(range) <= 2 || max_distance < 0) { std::copy(boost::begin(range), boost::end(range), out); diff --git a/boost/geometry/algorithms/touches.hpp b/boost/geometry/algorithms/touches.hpp index a06071d428..48334d7255 100644 --- a/boost/geometry/algorithms/touches.hpp +++ b/boost/geometry/algorithms/touches.hpp @@ -23,6 +23,10 @@ #include <deque> +#include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> +#include <boost/variant/variant_fwd.hpp> + #include <boost/geometry/geometries/concepts/check.hpp> #include <boost/geometry/algorithms/detail/for_each_range.hpp> #include <boost/geometry/algorithms/detail/overlay/overlay.hpp> @@ -32,9 +36,6 @@ #include <boost/geometry/algorithms/num_geometries.hpp> #include <boost/geometry/algorithms/detail/sub_range.hpp> #include <boost/geometry/policies/robustness/no_rescale_policy.hpp> -#include <boost/variant/static_visitor.hpp> -#include <boost/variant/apply_visitor.hpp> -#include <boost/variant/variant_fwd.hpp> #include <boost/geometry/algorithms/detail/relate/relate.hpp> diff --git a/boost/geometry/algorithms/transform.hpp b/boost/geometry/algorithms/transform.hpp index 1d6e8d0a35..357263a4ea 100644 --- a/boost/geometry/algorithms/transform.hpp +++ b/boost/geometry/algorithms/transform.hpp @@ -20,8 +20,9 @@ #include <boost/range.hpp> #include <boost/type_traits/remove_reference.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/algorithms/assign.hpp> diff --git a/boost/geometry/algorithms/validity_failure_type.hpp b/boost/geometry/algorithms/validity_failure_type.hpp new file mode 100644 index 0000000000..42de99c585 --- /dev/null +++ b/boost/geometry/algorithms/validity_failure_type.hpp @@ -0,0 +1,87 @@ +// Boost.Geometry (aka GGL, Generic Geometry Library) + +// Copyright (c) 2015, Oracle and/or its affiliates. + +// Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle + +// Licensed under the Boost Software License version 1.0. +// http://www.boost.org/users/license.html + +#ifndef BOOST_GEOMETRY_ALGORITHMS_VALIDITY_FAILURE_TYPE_HPP +#define BOOST_GEOMETRY_ALGORITHMS_VALIDITY_FAILURE_TYPE_HPP + + +namespace boost { namespace geometry +{ + + +/*! +\brief Enumerates the possible validity failure types for a geometry +\ingroup enum +\details The enumeration validity_failure_type enumerates the possible + reasons for which a geometry may be found as invalid by the + is_valid algorithm. + Besides the values that indicate invalidity, there is an + additional value (no_failure) that indicates validity. + +\qbk{ +[heading See also] +[link geometry.reference.algorithms.is_valid The is_valid +algorithm taking a reference to validity_failure_type as second argument] +} +*/ +enum validity_failure_type +{ + /// The geometry is valid + /// + no_failure = 0, + /// The geometry has a very small number of points, e.g., less + /// than 2 for linestrings, less than 3 for open rings, a closed + /// multi-polygon that contains a polygon with less than 4 points, etc. + /// (applies to linestrings, rings, polygons, multi-linestrings + /// and multi-polygons) + failure_few_points = 10, + /// The topological dimension of the geometry is smaller than its + /// dimension, e.g., a linestring with 3 identical points, an open + /// polygon with an interior ring consisting of 3 collinear points, etc. + /// (applies to linear and areal geometries, including segments + /// and boxes) + failure_wrong_topological_dimension = 11, + /// The geometry contains spikes + /// (applies to linear and areal geometries) + failure_spikes = 12, + /// The geometry has (consecutive) duplicate points + /// (applies to areal geometries only) + failure_duplicate_points = 13, + /// The geometry is defined as closed, the starting/ending points + /// are not equal + /// (applies to areal geometries only) + failure_not_closed = 20, // for areal geometries + /// The geometry has invalid self-intersections. + /// (applies to areal geometries only) + failure_self_intersections = 21, // for areal geometries + /// The actual orientation of the geometry is different from the one defined + /// (applies to areal geometries only) + failure_wrong_orientation = 22, // for areal geometries + /// The geometry contains interior rings that lie outside the exterior ring + /// (applies to polygons and multi-polygons only) + failure_interior_rings_outside = 30, // for (multi-)polygons + /// The geometry has nested interior rings + /// (applies to polygons and multi-polygons only) + failure_nested_interior_rings = 31, // for (multi-)polygons + /// The interior of the geometry is disconnected + /// (applies to polygons and multi-polygons only) + failure_disconnected_interior = 32, // for (multi-)polygons + /// The multi-polygon contains polygons whose interiors are not disjoint + /// (applies to multi-polygons only) + failure_intersecting_interiors = 40, // for multi-polygons + /// The top-right corner of the box is lexicographically smaller + /// than its bottom-left corner + /// (applies to boxes only) + failure_wrong_corner_order = 50 // for boxes +}; + + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_ALGORITHMS_VALIDITY_FAILURE_TYPE_HPP diff --git a/boost/geometry/algorithms/within.hpp b/boost/geometry/algorithms/within.hpp index f66b1ed1c6..9f2b6fedf7 100644 --- a/boost/geometry/algorithms/within.hpp +++ b/boost/geometry/algorithms/within.hpp @@ -24,8 +24,9 @@ #include <boost/concept_check.hpp> #include <boost/range.hpp> -#include <boost/variant/static_visitor.hpp> + #include <boost/variant/apply_visitor.hpp> +#include <boost/variant/static_visitor.hpp> #include <boost/variant/variant_fwd.hpp> #include <boost/geometry/algorithms/make.hpp> |