197 lines
6.2 KiB
C++
197 lines
6.2 KiB
C++
// Boost.Geometry
|
|
|
|
// Copyright (c) 2022, Oracle and/or its affiliates.
|
|
// Contributed and/or modified by Adam Wulkiewicz, 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_GC_GROUP_ELEMENTS_HPP
|
|
#define BOOST_GEOMETRY_ALGORITHMS_DETAIL_GC_GROUP_ELEMENTS_HPP
|
|
|
|
|
|
#include <array>
|
|
#include <deque>
|
|
#include <map>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
#include <boost/range/begin.hpp>
|
|
#include <boost/range/size.hpp>
|
|
|
|
#include <boost/geometry/algorithms/detail/gc_make_rtree.hpp>
|
|
#include <boost/geometry/algorithms/detail/visit.hpp>
|
|
#include <boost/geometry/algorithms/is_empty.hpp>
|
|
#include <boost/geometry/core/topological_dimension.hpp>
|
|
#include <boost/geometry/views/detail/random_access_view.hpp>
|
|
|
|
|
|
|
|
namespace boost { namespace geometry
|
|
{
|
|
|
|
|
|
#ifndef DOXYGEN_NO_DETAIL
|
|
namespace detail
|
|
{
|
|
|
|
|
|
struct gc_element_id
|
|
{
|
|
gc_element_id(unsigned int source_id_, std::size_t gc_id_)
|
|
: source_id(source_id_), gc_id(gc_id_)
|
|
{}
|
|
|
|
unsigned int source_id;
|
|
std::size_t gc_id;
|
|
|
|
friend bool operator<(gc_element_id const& left, gc_element_id const& right)
|
|
{
|
|
return left.source_id < right.source_id
|
|
|| (left.source_id == right.source_id && left.gc_id < right.gc_id);
|
|
}
|
|
};
|
|
|
|
template <typename GC1View, typename GC2View, typename Strategy, typename IntersectingFun, typename DisjointFun>
|
|
inline void gc_group_elements(GC1View const& gc1_view, GC2View const& gc2_view, Strategy const& strategy,
|
|
IntersectingFun&& intersecting_fun,
|
|
DisjointFun&& disjoint_fun,
|
|
bool const group_self = false)
|
|
{
|
|
// NOTE: gc_make_rtree_indexes() already checks for random-access,
|
|
// non-recursive geometry collections.
|
|
|
|
// NOTE: could be replaced with unordered_map and unordered_set
|
|
std::map<gc_element_id, std::set<gc_element_id>> adjacent;
|
|
|
|
// Create adjacency list based on intersecting envelopes of GC elements
|
|
auto const rtree2 = gc_make_rtree_indexes(gc2_view, strategy);
|
|
if (! group_self) // group only elements from the other GC?
|
|
{
|
|
for (std::size_t i = 0; i < boost::size(gc1_view); ++i)
|
|
{
|
|
traits::iter_visit<GC1View>::apply([&](auto const& g1)
|
|
{
|
|
using g1_t = util::remove_cref_t<decltype(g1)>;
|
|
using box1_t = gc_make_rtree_box_t<g1_t>;
|
|
box1_t b1 = geometry::return_envelope<box1_t>(g1, strategy);
|
|
detail::expand_by_epsilon(b1);
|
|
|
|
gc_element_id id1 = {0, i};
|
|
for (auto qit = rtree2.qbegin(index::intersects(b1)); qit != rtree2.qend(); ++qit)
|
|
{
|
|
gc_element_id id2 = {1, qit->second};
|
|
adjacent[id1].insert(id2);
|
|
adjacent[id2].insert(id1);
|
|
}
|
|
}, boost::begin(gc1_view) + i);
|
|
}
|
|
}
|
|
else // group elements from the same GCs and the other GC
|
|
{
|
|
auto const rtree1 = gc_make_rtree_indexes(gc1_view, strategy);
|
|
for (auto it1 = rtree1.begin() ; it1 != rtree1.end() ; ++it1)
|
|
{
|
|
auto const b1 = it1->first;
|
|
gc_element_id id1 = {0, it1->second};
|
|
for (auto qit2 = rtree2.qbegin(index::intersects(b1)); qit2 != rtree2.qend(); ++qit2)
|
|
{
|
|
gc_element_id id2 = {1, qit2->second};
|
|
adjacent[id1].insert(id2);
|
|
adjacent[id2].insert(id1);
|
|
}
|
|
for (auto qit1 = rtree1.qbegin(index::intersects(b1)); qit1 != rtree1.qend(); ++qit1)
|
|
{
|
|
if (id1.gc_id != qit1->second)
|
|
{
|
|
gc_element_id id11 = {0, qit1->second};
|
|
adjacent[id1].insert(id11);
|
|
adjacent[id11].insert(id1);
|
|
}
|
|
}
|
|
}
|
|
for (auto it2 = rtree2.begin(); it2 != rtree2.end(); ++it2)
|
|
{
|
|
auto const b2 = it2->first;
|
|
gc_element_id id2 = {1, it2->second};
|
|
for (auto qit2 = rtree2.qbegin(index::intersects(b2)); qit2 != rtree2.qend(); ++qit2)
|
|
{
|
|
if (id2.gc_id != qit2->second)
|
|
{
|
|
gc_element_id id22 = {1, qit2->second};
|
|
adjacent[id2].insert(id22);
|
|
adjacent[id22].insert(id2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Traverse the graph and build connected groups i.e. groups of intersecting envelopes
|
|
std::deque<gc_element_id> queue;
|
|
std::array<std::vector<bool>, 2> visited = {
|
|
std::vector<bool>(boost::size(gc1_view), false),
|
|
std::vector<bool>(boost::size(gc2_view), false)
|
|
};
|
|
for (auto const& elem : adjacent)
|
|
{
|
|
std::vector<gc_element_id> group;
|
|
if (! visited[elem.first.source_id][elem.first.gc_id])
|
|
{
|
|
queue.push_back(elem.first);
|
|
visited[elem.first.source_id][elem.first.gc_id] = true;
|
|
group.push_back(elem.first);
|
|
while (! queue.empty())
|
|
{
|
|
gc_element_id e = queue.front();
|
|
queue.pop_front();
|
|
for (auto const& n : adjacent[e])
|
|
{
|
|
if (! visited[n.source_id][n.gc_id])
|
|
{
|
|
queue.push_back(n);
|
|
visited[n.source_id][n.gc_id] = true;
|
|
group.push_back(n);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (! group.empty())
|
|
{
|
|
if (! intersecting_fun(group))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
std::vector<gc_element_id> group;
|
|
for (std::size_t i = 0; i < visited[0].size(); ++i)
|
|
{
|
|
if (! visited[0][i])
|
|
{
|
|
group.emplace_back(0, i);
|
|
}
|
|
}
|
|
for (std::size_t i = 0; i < visited[1].size(); ++i)
|
|
{
|
|
if (! visited[1][i])
|
|
{
|
|
group.emplace_back(1, i);
|
|
}
|
|
}
|
|
if (! group.empty())
|
|
{
|
|
disjoint_fun(group);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
} // namespace detail
|
|
#endif // DOXYGEN_NO_DETAIL
|
|
|
|
}} // namespace boost::geometry
|
|
|
|
#endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_GC_GROUP_ELEMENTS_HPP
|