123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- // Boost.Geometry
- // Copyright (c) 2021 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_DISTANCE_GEOMETRY_COLLECTION_HPP
- #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_GEOMETRY_COLLECTION_HPP
- #include <vector>
- #include <boost/range/size.hpp>
- #include <boost/geometry/algorithms/dispatch/distance.hpp>
- #include <boost/geometry/algorithms/detail/visit.hpp>
- #include <boost/geometry/core/assert.hpp>
- #include <boost/geometry/core/point_type.hpp>
- #include <boost/geometry/index/rtree.hpp>
- #include <boost/geometry/strategies/distance.hpp>
- #include <boost/geometry/strategies/distance_result.hpp>
- namespace boost { namespace geometry
- {
- #ifndef DOXYGEN_NO_DETAIL
- namespace detail { namespace distance
- {
- template <typename Geometry, typename GeometryCollection, typename Strategies>
- inline auto geometry_to_collection(Geometry const& geometry,
- GeometryCollection const& collection,
- Strategies const& strategies)
- {
- using result_t = typename geometry::distance_result<Geometry, GeometryCollection, Strategies>::type;
- result_t result = 0;
- bool is_first = true;
- detail::visit_breadth_first([&](auto const& g)
- {
- result_t r = dispatch::distance
- <
- Geometry, util::remove_cref_t<decltype(g)>, Strategies
- >::apply(geometry, g, strategies);
- if (is_first)
- {
- result = r;
- is_first = false;
- }
- else if (r < result)
- {
- result = r;
- }
- return result > result_t(0);
- }, collection);
- return result;
- }
- template <typename GeometryCollection1, typename GeometryCollection2, typename Strategies>
- inline auto collection_to_collection(GeometryCollection1 const& collection1,
- GeometryCollection2 const& collection2,
- Strategies const& strategies)
- {
- using result_t = typename geometry::distance_result<GeometryCollection1, GeometryCollection2, Strategies>::type;
- using point1_t = typename geometry::point_type<GeometryCollection1>::type;
- using box1_t = model::box<point1_t>;
- using point2_t = typename geometry::point_type<GeometryCollection2>::type;
- using box2_t = model::box<point2_t>;
- using rtree_value_t = std::pair<box1_t, typename boost::range_iterator<GeometryCollection1 const>::type>;
- using rtree_params_t = index::parameters<index::rstar<4>, Strategies>;
- using rtree_t = index::rtree<rtree_value_t, rtree_params_t>;
- rtree_params_t rtree_params(index::rstar<4>(), strategies);
- rtree_t rtree(rtree_params);
- // Build rtree of boxes and iterators of elements of GC1
- // TODO: replace this with visit_breadth_first_iterator to avoid creating an unnecessary container?
- {
- std::vector<rtree_value_t> values;
- visit_breadth_first_impl<true>::apply([&](auto & g1, auto it)
- {
- box1_t b1 = geometry::return_envelope<box1_t>(g1, strategies);
- geometry::detail::expand_by_epsilon(b1);
- values.emplace_back(b1, it);
- return true;
- }, collection1);
- rtree_t rt(values.begin(), values.end(), rtree_params);
- rtree = std::move(rt);
- }
- result_t const zero = 0;
- auto const rtree_qend = rtree.qend();
- result_t result = 0;
- bool is_first = true;
- visit_breadth_first([&](auto const& g2)
- {
- box2_t b2 = geometry::return_envelope<box2_t>(g2, strategies);
- geometry::detail::expand_by_epsilon(b2);
- for (auto it = rtree.qbegin(index::nearest(b2, rtree.size())) ; it != rtree_qend ; ++it)
- {
- // If the distance between boxes is greater than or equal to previously found
- // distance between geometries then stop processing the current b2 because no
- // closer b1 will be found
- if (! is_first)
- {
- result_t const bd = dispatch::distance
- <
- box1_t, box2_t, Strategies
- >::apply(it->first, b2, strategies);
- if (bd >= result)
- {
- break;
- }
- }
- // Boxes are closer than the previously found distance (or it's the first time),
- // calculate the new distance between geometries and check if it's closer (or assign it).
- traits::iter_visit<GeometryCollection1>::apply([&](auto const& g1)
- {
- result_t const d = dispatch::distance
- <
- util::remove_cref_t<decltype(g1)>, util::remove_cref_t<decltype(g2)>,
- Strategies
- >::apply(g1, g2, strategies);
- if (is_first)
- {
- result = d;
- is_first = false;
- }
- else if (d < result)
- {
- result = d;
- }
- }, it->second);
- // The smallest possible distance found, end searching.
- if (! is_first && result <= zero)
- {
- return false;
- }
- }
- // Just in case
- return is_first || result > zero;
- }, collection2);
- return result;
- }
- }} // namespace detail::distance
- #endif // DOXYGEN_NO_DETAIL
- #ifndef DOXYGEN_NO_DISPATCH
- namespace dispatch
- {
- template
- <
- typename Geometry, typename GeometryCollection, typename Strategies, typename Tag1
- >
- struct distance
- <
- Geometry, GeometryCollection, Strategies,
- Tag1, geometry_collection_tag, void, false
- >
- {
- static inline auto apply(Geometry const& geometry,
- GeometryCollection const& collection,
- Strategies const& strategies)
- {
- assert_dimension_equal<Geometry, GeometryCollection>();
- return detail::distance::geometry_to_collection(geometry, collection, strategies);
- }
- };
- template
- <
- typename GeometryCollection, typename Geometry, typename Strategies, typename Tag2
- >
- struct distance
- <
- GeometryCollection, Geometry, Strategies,
- geometry_collection_tag, Tag2, void, false
- >
- {
- static inline auto apply(GeometryCollection const& collection,
- Geometry const& geometry,
- Strategies const& strategies)
- {
- assert_dimension_equal<Geometry, GeometryCollection>();
- return detail::distance::geometry_to_collection(geometry, collection, strategies);
- }
- };
- template
- <
- typename GeometryCollection1, typename GeometryCollection2, typename Strategies
- >
- struct distance
- <
- GeometryCollection1, GeometryCollection2, Strategies,
- geometry_collection_tag, geometry_collection_tag, void, false
- >
- {
- static inline auto apply(GeometryCollection1 const& collection1,
- GeometryCollection2 const& collection2,
- Strategies const& strategies)
- {
- assert_dimension_equal<GeometryCollection1, GeometryCollection2>();
- // Build the rtree for the smaller GC (ignoring recursive GCs)
- return boost::size(collection1) <= boost::size(collection2)
- ? detail::distance::collection_to_collection(collection1, collection2, strategies)
- : detail::distance::collection_to_collection(collection2, collection1, strategies);
- }
- };
- } // namespace dispatch
- #endif // DOXYGEN_NO_DISPATCH
- }} // namespace boost::geometry
- #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_DISTANCE_SEGMENT_TO_BOX_HPP
|