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