summaryrefslogtreecommitdiff
path: root/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/geometry/algorithms/detail/overlay/handle_colocations.hpp')
-rw-r--r--boost/geometry/algorithms/detail/overlay/handle_colocations.hpp278
1 files changed, 278 insertions, 0 deletions
diff --git a/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp b/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp
new file mode 100644
index 0000000000..6f332ddff2
--- /dev/null
+++ b/boost/geometry/algorithms/detail/overlay/handle_colocations.hpp
@@ -0,0 +1,278 @@
+// 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_OVERLAY_HANDLE_COLOCATIONS_HPP
+#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP
+
+#include <cstddef>
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include <boost/range.hpp>
+#include <boost/geometry/algorithms/detail/overlay/overlay_type.hpp>
+#include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
+#include <boost/geometry/algorithms/detail/ring_identifier.hpp>
+#include <boost/geometry/algorithms/detail/overlay/segment_identifier.hpp>
+
+#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
+# include <iostream>
+# include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
+# include <boost/geometry/io/wkt/wkt.hpp>
+# define BOOST_GEOMETRY_DEBUG_IDENTIFIER
+#endif
+
+namespace boost { namespace geometry
+{
+
+#ifndef DOXYGEN_NO_DETAIL
+namespace detail { namespace overlay
+{
+
+struct turn_operation_index
+{
+ turn_operation_index(signed_size_type ti = -1,
+ signed_size_type oi = -1)
+ : turn_index(ti)
+ , op_index(oi)
+ {}
+
+ signed_size_type turn_index;
+ signed_size_type op_index; // basically only 0,1
+};
+
+
+template <typename TurnPoints>
+struct less_by_fraction_and_type
+{
+ inline less_by_fraction_and_type(TurnPoints const& turn_points)
+ : m_turns(turn_points)
+ {
+ }
+
+ inline bool operator()(turn_operation_index const& left,
+ turn_operation_index const& right) const
+ {
+ typedef typename boost::range_value<TurnPoints>::type turn_type;
+ typedef typename turn_type::turn_operation_type turn_operation_type;
+
+ turn_type const& left_turn = m_turns[left.turn_index];
+ turn_type const& right_turn = m_turns[right.turn_index];
+ turn_operation_type const& left_op
+ = left_turn.operations[left.op_index];
+
+ turn_operation_type const& right_op
+ = right_turn.operations[right.op_index];
+
+ if (! (left_op.fraction == right_op.fraction))
+ {
+ return left_op.fraction < right_op.fraction;
+ }
+
+ turn_operation_type const& left_other_op
+ = left_turn.operations[1 - left.op_index];
+
+ turn_operation_type const& right_other_op
+ = right_turn.operations[1 - right.op_index];
+
+ // Fraction is the same, now sort on ring id, first outer ring,
+ // then interior rings
+ return left_other_op.seg_id < right_other_op.seg_id;
+ }
+
+private:
+ TurnPoints const& m_turns;
+};
+
+template <overlay_type OverlayType, typename TurnPoints, typename OperationVector>
+inline void handle_colocation_cluster(TurnPoints& turn_points,
+ segment_identifier const& current_ring_seg_id,
+ OperationVector const& vec)
+{
+ typedef typename boost::range_value<TurnPoints>::type turn_type;
+ typedef typename turn_type::turn_operation_type turn_operation_type;
+
+ std::vector<turn_operation_index>::const_iterator vit = vec.begin();
+
+ turn_type cluster_turn = turn_points[vit->turn_index];
+ turn_operation_type cluster_op
+ = cluster_turn.operations[vit->op_index];
+ segment_identifier cluster_other_id
+ = cluster_turn.operations[1 - vit->op_index].seg_id;
+ bool const discard_colocated
+ = cluster_turn.both(operation_union)
+ || cluster_turn.combination(operation_blocked, operation_union);
+
+ for (++vit; vit != vec.end(); ++vit)
+ {
+ turn_operation_index const& toi = *vit;
+ turn_type& turn = turn_points[toi.turn_index];
+ turn_operation_type const& op = turn.operations[toi.op_index];
+ segment_identifier const& other_id
+ = turn.operations[1 - toi.op_index].seg_id;
+
+ if (cluster_op.fraction == op.fraction)
+ {
+ // Two turns of current ring with same source are colocated,
+ // one is from exterior ring, one from interior ring
+ bool const colocated_ext_int
+ = cluster_other_id.multi_index == other_id.multi_index
+ && cluster_other_id.ring_index == -1
+ && other_id.ring_index >= 0;
+
+ // Turn of current interior ring with other interior ring
+ bool const touch_int_int
+ = current_ring_seg_id.ring_index >= 0
+ && other_id.ring_index >= 0;
+
+ if (discard_colocated && colocated_ext_int)
+ {
+ // If the two turns on this same segment are a
+ // colocation with two different segments on the
+ // other geometry, of the same polygon but with
+ // the outer (u/u or u/x) and the inner ring (non u/u),
+ // that turn with inner ring should be discarded
+ turn.discarded = true;
+ turn.colocated = true;
+ }
+ else if (cluster_turn.colocated
+ && touch_int_int
+ && turn.both(operation_intersection))
+ {
+ // Two holes touch each other at a point where the
+ // exterior ring also touches
+ turn.discarded = true;
+ turn.colocated = true;
+ }
+ else if (OverlayType == overlay_difference
+ && turn.both(operation_intersection)
+ && colocated_ext_int)
+ {
+ // For difference (polygon inside out) we need to
+ // discard i/i instead, in case of colocations
+ turn.discarded = true;
+ turn.colocated = true;
+ }
+ }
+ else
+ {
+ // Not on same fraction on this segment
+ // assign for next potential cluster
+ cluster_turn = turn;
+ cluster_op = op;
+ cluster_other_id = other_id;
+ }
+
+ }
+}
+
+
+// Checks colocated turns and flags combinations of uu/other, possibly a
+// combination of a ring touching another geometry's interior ring which is
+// tangential to the exterior ring
+
+// This function can be extended to replace handle_tangencies: at each
+// colocation incoming and outgoing vectors should be inspected
+
+template <overlay_type OverlayType, typename TurnPoints>
+inline void handle_colocations(TurnPoints& turn_points)
+{
+ typedef std::map
+ <
+ segment_identifier,
+ std::vector<turn_operation_index>
+ > map_type;
+
+ // Create and fill map on segment-identifier Map is sorted on seg_id,
+ // meaning it is sorted on ring_identifier too. This means that exterior
+ // rings are handled first. If there is a colocation on the exterior ring,
+ // that information can be used for the interior ring too
+ map_type map;
+
+ int index = 0;
+ for (typename boost::range_iterator<TurnPoints>::type
+ it = boost::begin(turn_points);
+ it != boost::end(turn_points);
+ ++it, ++index)
+ {
+ map[it->operations[0].seg_id].push_back(turn_operation_index(index, 0));
+ map[it->operations[1].seg_id].push_back(turn_operation_index(index, 1));
+ }
+
+ // Check if there are multiple turns on one or more segments,
+ // if not then nothing is to be done
+ bool colocations = 0;
+ for (typename map_type::const_iterator it = map.begin();
+ it != map.end();
+ ++it)
+ {
+ if (it->second.size() > 1u)
+ {
+ colocations = true;
+ break;
+ }
+ }
+
+ if (! colocations)
+ {
+ return;
+ }
+
+ // Sort all vectors, per same segment
+ less_by_fraction_and_type<TurnPoints> less(turn_points);
+ for (typename map_type::iterator it = map.begin();
+ it != map.end(); ++it)
+ {
+ std::sort(it->second.begin(), it->second.end(), less);
+ }
+
+ for (typename map_type::const_iterator it = map.begin();
+ it != map.end(); ++it)
+ {
+ if (it->second.size() > 1)
+ {
+ handle_colocation_cluster<OverlayType>(turn_points,
+ it->first, it->second);
+ }
+ }
+
+#if defined(BOOST_GEOMETRY_DEBUG_HANDLE_COLOCATIONS)
+ std::cout << "*** Colocations " << map.size() << std::endl;
+ for (typename map_type::const_iterator it = map.begin();
+ it != map.end(); ++it)
+ {
+ std::cout << it->first << std::endl;
+ for (std::vector<turn_operation_index>::const_iterator vit
+ = it->second.begin(); vit != it->second.end(); ++vit)
+ {
+ turn_operation_index const& toi = *vit;
+ std::cout << geometry::wkt(turn_points[toi.turn_index].point)
+ << std::boolalpha
+ << " discarded=" << turn_points[toi.turn_index].discarded
+ << " colocated=" << turn_points[toi.turn_index].colocated
+ << " " << operation_char(turn_points[toi.turn_index].operations[0].operation)
+ << " " << turn_points[toi.turn_index].operations[0].seg_id
+ << " " << turn_points[toi.turn_index].operations[0].fraction
+ << " // " << operation_char(turn_points[toi.turn_index].operations[1].operation)
+ << " " << turn_points[toi.turn_index].operations[1].seg_id
+ << " " << turn_points[toi.turn_index].operations[1].fraction
+ << std::endl;
+ }
+ }
+#endif // DEBUG
+
+}
+
+
+}} // namespace detail::overlay
+#endif //DOXYGEN_NO_DETAIL
+
+
+}} // namespace boost::geometry
+
+#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_HANDLE_COLOCATIONS_HPP