// Boost.Geometry (aka GGL, Generic Geometry Library) // Copyright (c) 2007-2011 Barend Gehrels, Amsterdam, the Netherlands. // Use, modification and distribution is subject to the Boost Software License, // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICH_HPP #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICH_HPP #include #include #include #include #include #ifdef BOOST_GEOMETRY_DEBUG_ENRICH # include # include # include # define BOOST_GEOMETRY_DEBUG_IDENTIFIER #endif #include #include #include #include #include #include #ifdef BOOST_GEOMETRY_DEBUG_ENRICH # include #endif namespace boost { namespace geometry { #ifndef DOXYGEN_NO_DETAIL namespace detail { namespace overlay { // Wraps "turn_operation" from turn_info.hpp, // giving it extra information template struct indexed_turn_operation { typedef TurnOperation type; int index; int operation_index; bool discarded; TurnOperation subject; inline indexed_turn_operation(int i, int oi, TurnOperation const& s) : index(i) , operation_index(oi) , discarded(false) , subject(s) {} }; template struct remove_discarded { inline bool operator()(IndexedTurnOperation const& operation) const { return operation.discarded; } }; template < typename TurnPoints, typename Indexed, typename Geometry1, typename Geometry2, bool Reverse1, bool Reverse2, typename Strategy > struct sort_on_segment_and_distance { inline sort_on_segment_and_distance(TurnPoints const& turn_points , Geometry1 const& geometry1 , Geometry2 const& geometry2 , Strategy const& strategy , bool* clustered) : m_turn_points(turn_points) , m_geometry1(geometry1) , m_geometry2(geometry2) , m_strategy(strategy) , m_clustered(clustered) { } private : TurnPoints const& m_turn_points; Geometry1 const& m_geometry1; Geometry2 const& m_geometry2; Strategy const& m_strategy; mutable bool* m_clustered; inline bool consider_relative_order(Indexed const& left, Indexed const& right) const { typedef typename geometry::point_type::type point_type; point_type pi, pj, ri, rj, si, sj; geometry::copy_segment_points(m_geometry1, m_geometry2, left.subject.seg_id, pi, pj); geometry::copy_segment_points(m_geometry1, m_geometry2, left.subject.other_id, ri, rj); geometry::copy_segment_points(m_geometry1, m_geometry2, right.subject.other_id, si, sj); int const order = get_relative_order < point_type >::apply(pi, pj,ri, rj, si, sj); //debug("r/o", order == -1); return order == -1; } public : // Note that left/right do NOT correspond to m_geometry1/m_geometry2 // but to the "indexed_turn_operation" inline bool operator()(Indexed const& left, Indexed const& right) const { segment_identifier const& sl = left.subject.seg_id; segment_identifier const& sr = right.subject.seg_id; if (sl == sr && geometry::math::equals(left.subject.enriched.distance , right.subject.enriched.distance)) { // Both left and right are located on the SAME segment. // First check "real" intersection (crosses) // -> distance zero due to precision, solve it by sorting if (m_turn_points[left.index].method == method_crosses && m_turn_points[right.index].method == method_crosses) { return consider_relative_order(left, right); } // If that is not the case, cluster it later on. // Indicate that this is necessary. *m_clustered = true; return left.index < right.index; } return sl == sr ? left.subject.enriched.distance < right.subject.enriched.distance : sl < sr; } }; template inline void update_discarded(Turns& turn_points, Operations& operations) { // Vice-versa, set discarded to true for discarded operations; // AND set discarded points to true for (typename boost::range_iterator::type it = boost::begin(operations); it != boost::end(operations); ++it) { if (turn_points[it->index].discarded) { it->discarded = true; } else if (it->discarded) { turn_points[it->index].discarded = true; } } } // Sorts IP-s of this ring on segment-identifier, and if on same segment, // on distance. // Then assigns for each IP which is the next IP on this segment, // plus the vertex-index to travel to, plus the next IP // (might be on another segment) template < typename IndexType, bool Reverse1, bool Reverse2, typename Container, typename TurnPoints, typename Geometry1, typename Geometry2, typename Strategy > inline void enrich_sort(Container& operations, TurnPoints& turn_points, operation_type for_operation, Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const& strategy) { typedef typename IndexType::type operations_type; bool clustered = false; std::sort(boost::begin(operations), boost::end(operations), sort_on_segment_and_distance < TurnPoints, IndexType, Geometry1, Geometry2, Reverse1, Reverse2, Strategy >(turn_points, geometry1, geometry2, strategy, &clustered)); // DONT'T discard xx / (for union) ix / ii / (for intersection) ux / uu here // It would give way to "lonely" ui turn points, traveling all // the way round. See #105 if (clustered) { typedef typename boost::range_iterator::type nc_iterator; nc_iterator it = boost::begin(operations); nc_iterator begin_cluster = boost::end(operations); for (nc_iterator prev = it++; it != boost::end(operations); prev = it++) { operations_type& prev_op = turn_points[prev->index] .operations[prev->operation_index]; operations_type& op = turn_points[it->index] .operations[it->operation_index]; if (prev_op.seg_id == op.seg_id && (turn_points[prev->index].method != method_crosses || turn_points[it->index].method != method_crosses) && geometry::math::equals(prev_op.enriched.distance, op.enriched.distance)) { if (begin_cluster == boost::end(operations)) { begin_cluster = prev; } } else if (begin_cluster != boost::end(operations)) { handle_cluster(begin_cluster, it, turn_points, for_operation, geometry1, geometry2, strategy); begin_cluster = boost::end(operations); } } if (begin_cluster != boost::end(operations)) { handle_cluster(begin_cluster, it, turn_points, for_operation, geometry1, geometry2, strategy); } } update_discarded(turn_points, operations); } template < typename IndexType, typename Container, typename TurnPoints > inline void enrich_discard(Container& operations, TurnPoints& turn_points) { update_discarded(turn_points, operations); // Then delete discarded operations from vector remove_discarded predicate; operations.erase( std::remove_if(boost::begin(operations), boost::end(operations), predicate), boost::end(operations)); } template < typename IndexType, typename Container, typename TurnPoints, typename Geometry1, typename Geometry2, typename Strategy > inline void enrich_assign(Container& operations, TurnPoints& turn_points, operation_type for_operation, Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const& strategy) { typedef typename IndexType::type operations_type; typedef typename boost::range_iterator::type iterator_type; if (operations.size() > 0) { // Assign travel-to-vertex/ip index for each turning point. // Because IP's are circular, PREV starts at the very last one, // being assigned from the first one. // "next ip on same segment" should not be considered circular. bool first = true; iterator_type it = boost::begin(operations); for (iterator_type prev = it + (boost::size(operations) - 1); it != boost::end(operations); prev = it++) { operations_type& prev_op = turn_points[prev->index].operations[prev->operation_index]; operations_type& op = turn_points[it->index].operations[it->operation_index]; prev_op.enriched.travels_to_ip_index = it->index; prev_op.enriched.travels_to_vertex_index = it->subject.seg_id.segment_index; if (! first && prev_op.seg_id.segment_index == op.seg_id.segment_index) { prev_op.enriched.next_ip_index = it->index; } first = false; } } // DEBUG #ifdef BOOST_GEOMETRY_DEBUG_ENRICH { for (iterator_type it = boost::begin(operations); it != boost::end(operations); ++it) { operations_type& op = turn_points[it->index] .operations[it->operation_index]; std::cout << it->index << " meth: " << method_char(turn_points[it->index].method) << " seg: " << op.seg_id << " dst: " << boost::numeric_cast(op.enriched.distance) << " op: " << operation_char(turn_points[it->index].operations[0].operation) << operation_char(turn_points[it->index].operations[1].operation) << " dsc: " << (turn_points[it->index].discarded ? "T" : "F") << " ->vtx " << op.enriched.travels_to_vertex_index << " ->ip " << op.enriched.travels_to_ip_index << " ->nxt ip " << op.enriched.next_ip_index //<< " vis: " << visited_char(op.visited) << std::endl; ; } } #endif // END DEBUG } template inline void create_map(TurnPoints const& turn_points, MappedVector& mapped_vector) { typedef typename boost::range_value::type turn_point_type; typedef typename turn_point_type::container_type container_type; int index = 0; for (typename boost::range_iterator::type it = boost::begin(turn_points); it != boost::end(turn_points); ++it, ++index) { // Add operations on this ring, but skip discarded ones if (! it->discarded) { int op_index = 0; for (typename boost::range_iterator::type op_it = boost::begin(it->operations); op_it != boost::end(it->operations); ++op_it, ++op_index) { // We should NOT skip blocked operations here // because they can be relevant for "the other side" // NOT if (op_it->operation != operation_blocked) ring_identifier ring_id ( op_it->seg_id.source_index, op_it->seg_id.multi_index, op_it->seg_id.ring_index ); mapped_vector[ring_id].push_back ( IndexedType(index, op_index, *op_it) ); } } } } }} // namespace detail::overlay #endif //DOXYGEN_NO_DETAIL /*! \brief All intersection points are enriched with successor information \ingroup overlay \tparam TurnPoints type of intersection container (e.g. vector of "intersection/turn point"'s) \tparam Geometry1 \tparam_geometry \tparam Geometry2 \tparam_geometry \tparam Strategy side strategy type \param turn_points container containing intersectionpoints \param for_operation operation_type (union or intersection) \param geometry1 \param_geometry \param geometry2 \param_geometry \param strategy strategy */ template < bool Reverse1, bool Reverse2, typename TurnPoints, typename Geometry1, typename Geometry2, typename Strategy > inline void enrich_intersection_points(TurnPoints& turn_points, detail::overlay::operation_type for_operation, Geometry1 const& geometry1, Geometry2 const& geometry2, Strategy const& strategy) { typedef typename boost::range_value::type turn_point_type; typedef typename turn_point_type::turn_operation_type turn_operation_type; typedef detail::overlay::indexed_turn_operation < turn_operation_type > indexed_turn_operation; typedef std::map < ring_identifier, std::vector > mapped_vector_type; // DISCARD ALL UU // #76 is the reason that this is necessary... // With uu, at all points there is the risk that rings are being traversed twice or more. // Without uu, all rings having only uu will be untouched and gathered by assemble for (typename boost::range_iterator::type it = boost::begin(turn_points); it != boost::end(turn_points); ++it) { if (it->both(detail::overlay::operation_union)) { it->discarded = true; } } // Create a map of vectors of indexed operation-types to be able // to sort intersection points PER RING mapped_vector_type mapped_vector; detail::overlay::create_map(turn_points, mapped_vector); // No const-iterator; contents of mapped copy is temporary, // and changed by enrich for (typename mapped_vector_type::iterator mit = mapped_vector.begin(); mit != mapped_vector.end(); ++mit) { #ifdef BOOST_GEOMETRY_DEBUG_ENRICH std::cout << "ENRICH-sort Ring " << mit->first << std::endl; #endif detail::overlay::enrich_sort(mit->second, turn_points, for_operation, geometry1, geometry2, strategy); } for (typename mapped_vector_type::iterator mit = mapped_vector.begin(); mit != mapped_vector.end(); ++mit) { #ifdef BOOST_GEOMETRY_DEBUG_ENRICH std::cout << "ENRICH-discard Ring " << mit->first << std::endl; #endif detail::overlay::enrich_discard(mit->second, turn_points); } for (typename mapped_vector_type::iterator mit = mapped_vector.begin(); mit != mapped_vector.end(); ++mit) { #ifdef BOOST_GEOMETRY_DEBUG_ENRICH std::cout << "ENRICH-assign Ring " << mit->first << std::endl; #endif detail::overlay::enrich_assign(mit->second, turn_points, for_operation, geometry1, geometry2, strategy); } #ifdef BOOST_GEOMETRY_DEBUG_ENRICH //detail::overlay::check_graph(turn_points, for_operation); #endif } }} // namespace boost::geometry #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_ENRICH_HPP