1 // Boost.Geometry
2 
3 // Copyright (c) 2018 Yaghyavardhan Singh Khangarot, Hyderabad, India.
4 // Contributed and/or modified by Yaghyavardhan Singh Khangarot,
5 //   as part of Google Summer of Code 2018 program.
6 
7 // This file was modified by Oracle on 2018-2021.
8 // Modifications copyright (c) 2018-2021, Oracle and/or its affiliates.
9 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
10 
11 // Use, modification and distribution is subject to the Boost Software License,
12 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
13 // http://www.boost.org/LICENSE_1_0.txt)
14 
15 #ifndef BOOST_GEOMETRY_ALGORITHMS_DISCRETE_HAUSDORFF_DISTANCE_HPP
16 #define BOOST_GEOMETRY_ALGORITHMS_DISCRETE_HAUSDORFF_DISTANCE_HPP
17 
18 #include <algorithm>
19 
20 #ifdef BOOST_GEOMETRY_DEBUG_HAUSDORFF_DISTANCE
21 #include <iostream>
22 #endif
23 
24 #include <iterator>
25 #include <utility>
26 #include <vector>
27 #include <limits>
28 
29 #include <boost/geometry/algorithms/detail/dummy_geometries.hpp>
30 #include <boost/geometry/algorithms/detail/throw_on_empty_input.hpp>
31 #include <boost/geometry/algorithms/not_implemented.hpp>
32 #include <boost/geometry/core/point_type.hpp>
33 #include <boost/geometry/core/tag.hpp>
34 #include <boost/geometry/core/tags.hpp>
35 #include <boost/geometry/strategies/detail.hpp>
36 #include <boost/geometry/strategies/discrete_distance/cartesian.hpp>
37 #include <boost/geometry/strategies/discrete_distance/geographic.hpp>
38 #include <boost/geometry/strategies/discrete_distance/spherical.hpp>
39 #include <boost/geometry/strategies/distance_result.hpp>
40 #include <boost/geometry/util/range.hpp>
41 
42 // Note that in order for this to work umbrella strategy has to contain
43 // index strategies.
44 #ifdef BOOST_GEOMETRY_ENABLE_SIMILARITY_RTREE
45 #include <boost/geometry/index/rtree.hpp>
46 #endif // BOOST_GEOMETRY_ENABLE_SIMILARITY_RTREE
47 
48 namespace boost { namespace geometry
49 {
50 
51 #ifndef DOXYGEN_NO_DETAIL
52 namespace detail { namespace discrete_hausdorff_distance
53 {
54 
55 // TODO: The implementation should calculate comparable distances
56 
57 struct point_range
58 {
59     template <typename Point, typename Range, typename Strategies>
applyboost::geometry::detail::discrete_hausdorff_distance::point_range60     static inline auto apply(Point const& pnt, Range const& rng,
61                              Strategies const& strategies)
62     {
63         typedef typename distance_result
64             <
65                 Point,
66                 typename point_type<Range>::type,
67                 Strategies
68             >::type result_type;
69 
70         typedef typename boost::range_size<Range>::type size_type;
71 
72         boost::geometry::detail::throw_on_empty_input(rng);
73 
74         auto const strategy = strategies.distance(dummy_point(), dummy_point());
75 
76         size_type const n = boost::size(rng);
77         result_type dis_min = 0;
78         bool is_dis_min_set = false;
79 
80         for (size_type i = 0 ; i < n ; i++)
81         {
82             result_type dis_temp = strategy.apply(pnt, range::at(rng, i));
83             if (! is_dis_min_set || dis_temp < dis_min)
84             {
85                 dis_min = dis_temp;
86                 is_dis_min_set = true;
87             }
88         }
89         return dis_min;
90     }
91 };
92 
93 struct range_range
94 {
95     template <typename Range1, typename Range2, typename Strategies>
applyboost::geometry::detail::discrete_hausdorff_distance::range_range96     static inline auto apply(Range1 const& r1, Range2 const& r2,
97                              Strategies const& strategies)
98     {
99         typedef typename distance_result
100             <
101                 typename point_type<Range1>::type,
102                 typename point_type<Range2>::type,
103                 Strategies
104             >::type result_type;
105 
106         typedef typename boost::range_size<Range1>::type size_type;
107 
108         boost::geometry::detail::throw_on_empty_input(r1);
109         boost::geometry::detail::throw_on_empty_input(r2);
110 
111         size_type const n = boost::size(r1);
112         result_type dis_max = 0;
113 
114 #ifdef BOOST_GEOMETRY_ENABLE_SIMILARITY_RTREE
115         namespace bgi = boost::geometry::index;
116         typedef typename point_type<Range1>::type point_t;
117         typedef bgi::rtree<point_t, bgi::linear<4> > rtree_type;
118         rtree_type rtree(boost::begin(r2), boost::end(r2));
119         point_t res;
120 #endif
121 
122         for (size_type i = 0 ; i < n ; i++)
123         {
124 #ifdef BOOST_GEOMETRY_ENABLE_SIMILARITY_RTREE
125             size_type found = rtree.query(bgi::nearest(range::at(r1, i), 1), &res);
126             result_type dis_min = strategy.apply(range::at(r1,i), res);
127 #else
128             result_type dis_min = point_range::apply(range::at(r1, i), r2, strategies);
129 #endif
130             if (dis_min > dis_max )
131             {
132                 dis_max = dis_min;
133             }
134         }
135         return dis_max;
136     }
137 };
138 
139 
140 struct range_multi_range
141 {
142     template <typename Range, typename Multi_range, typename Strategies>
applyboost::geometry::detail::discrete_hausdorff_distance::range_multi_range143     static inline auto apply(Range const& rng, Multi_range const& mrng,
144                              Strategies const& strategies)
145     {
146         typedef typename distance_result
147             <
148                 typename point_type<Range>::type,
149                 typename point_type<Multi_range>::type,
150                 Strategies
151             >::type result_type;
152         typedef typename boost::range_size<Multi_range>::type size_type;
153 
154         boost::geometry::detail::throw_on_empty_input(rng);
155         boost::geometry::detail::throw_on_empty_input(mrng);
156 
157         size_type b = boost::size(mrng);
158         result_type haus_dis = 0;
159 
160         for (size_type j = 0 ; j < b ; j++)
161         {
162             result_type dis_max = range_range::apply(rng, range::at(mrng, j), strategies);
163             if (dis_max > haus_dis)
164             {
165                 haus_dis = dis_max;
166             }
167         }
168 
169         return haus_dis;
170     }
171 };
172 
173 
174 struct multi_range_multi_range
175 {
176     template <typename Multi_Range1, typename Multi_range2, typename Strategies>
applyboost::geometry::detail::discrete_hausdorff_distance::multi_range_multi_range177     static inline auto apply(Multi_Range1 const& mrng1, Multi_range2 const& mrng2,
178                              Strategies const& strategies)
179     {
180         typedef typename distance_result
181             <
182                 typename point_type<Multi_Range1>::type,
183                 typename point_type<Multi_range2>::type,
184                 Strategies
185             >::type result_type;
186         typedef typename boost::range_size<Multi_Range1>::type size_type;
187 
188         boost::geometry::detail::throw_on_empty_input(mrng1);
189         boost::geometry::detail::throw_on_empty_input(mrng2);
190 
191         size_type n = boost::size(mrng1);
192         result_type haus_dis = 0;
193 
194         for (size_type i = 0 ; i < n ; i++)
195         {
196             result_type dis_max = range_multi_range::apply(range::at(mrng1, i), mrng2, strategies);
197             if (dis_max > haus_dis)
198             {
199                 haus_dis = dis_max;
200             }
201         }
202         return haus_dis;
203     }
204 };
205 
206 }} // namespace detail::hausdorff_distance
207 #endif // DOXYGEN_NO_DETAIL
208 
209 #ifndef DOXYGEN_NO_DISPATCH
210 namespace dispatch
211 {
212 template
213 <
214     typename Geometry1,
215     typename Geometry2,
216     typename Tag1 = typename tag<Geometry1>::type,
217     typename Tag2 = typename tag<Geometry2>::type
218 >
219 struct discrete_hausdorff_distance : not_implemented<Tag1, Tag2>
220 {};
221 
222 // Specialization for point and multi_point
223 template <typename Point, typename MultiPoint>
224 struct discrete_hausdorff_distance<Point, MultiPoint, point_tag, multi_point_tag>
225     : detail::discrete_hausdorff_distance::point_range
226 {};
227 
228 // Specialization for linestrings
229 template <typename Linestring1, typename Linestring2>
230 struct discrete_hausdorff_distance<Linestring1, Linestring2, linestring_tag, linestring_tag>
231     : detail::discrete_hausdorff_distance::range_range
232 {};
233 
234 // Specialization for multi_point-multi_point
235 template <typename MultiPoint1, typename MultiPoint2>
236 struct discrete_hausdorff_distance<MultiPoint1, MultiPoint2, multi_point_tag, multi_point_tag>
237     : detail::discrete_hausdorff_distance::range_range
238 {};
239 
240 // Specialization for Linestring and MultiLinestring
241 template <typename Linestring, typename MultiLinestring>
242 struct discrete_hausdorff_distance<Linestring, MultiLinestring, linestring_tag, multi_linestring_tag>
243     : detail::discrete_hausdorff_distance::range_multi_range
244 {};
245 
246 // Specialization for MultiLinestring and MultiLinestring
247 template <typename MultiLinestring1, typename MultiLinestring2>
248 struct discrete_hausdorff_distance<MultiLinestring1, MultiLinestring2, multi_linestring_tag, multi_linestring_tag>
249     : detail::discrete_hausdorff_distance::multi_range_multi_range
250 {};
251 
252 } // namespace dispatch
253 #endif // DOXYGEN_NO_DISPATCH
254 
255 
256 namespace resolve_strategy {
257 
258 template
259 <
260     typename Strategies,
261     bool IsUmbrella = strategies::detail::is_umbrella_strategy<Strategies>::value
262 >
263 struct discrete_hausdorff_distance
264 {
265     template <typename Geometry1, typename Geometry2>
applyboost::geometry::resolve_strategy::discrete_hausdorff_distance266     static inline auto apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
267                              Strategies const& strategies)
268     {
269         return dispatch::discrete_hausdorff_distance
270             <
271                 Geometry1, Geometry2
272             >::apply(geometry1, geometry2, strategies);
273     }
274 };
275 
276 template <typename Strategy>
277 struct discrete_hausdorff_distance<Strategy, false>
278 {
279     template <typename Geometry1, typename Geometry2>
applyboost::geometry::resolve_strategy::discrete_hausdorff_distance280     static inline auto apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
281                              Strategy const& strategy)
282     {
283         using strategies::discrete_distance::services::strategy_converter;
284         return dispatch::discrete_hausdorff_distance
285             <
286                 Geometry1, Geometry2
287             >::apply(geometry1, geometry2,
288                      strategy_converter<Strategy>::get(strategy));
289     }
290 };
291 
292 template <>
293 struct discrete_hausdorff_distance<default_strategy, false>
294 {
295     template <typename Geometry1, typename Geometry2>
applyboost::geometry::resolve_strategy::discrete_hausdorff_distance296     static inline auto apply(Geometry1 const& geometry1, Geometry2 const& geometry2,
297                              default_strategy const&)
298     {
299         typedef typename strategies::discrete_distance::services::default_strategy
300             <
301                 Geometry1, Geometry2
302             >::type strategies_type;
303 
304         return dispatch::discrete_hausdorff_distance
305             <
306                 Geometry1, Geometry2
307             >::apply(geometry1, geometry2, strategies_type());
308     }
309 };
310 
311 } // namespace resolve_strategy
312 
313 
314 /*!
315 \brief Calculate discrete Hausdorff distance between two geometries (currently
316     works for LineString-LineString, MultiPoint-MultiPoint, Point-MultiPoint,
317     MultiLineString-MultiLineString) using specified strategy.
318 \ingroup discrete_hausdorff_distance
319 \tparam Geometry1 \tparam_geometry
320 \tparam Geometry2 \tparam_geometry
321 \tparam Strategy A type fulfilling a DistanceStrategy concept
322 \param geometry1 Input geometry
323 \param geometry2 Input geometry
324 \param strategy Distance strategy to be used to calculate Pt-Pt distance
325 
326 \qbk{distinguish,with strategy}
327 \qbk{[include reference/algorithms/discrete_hausdorff_distance.qbk]}
328 
329 \qbk{
330 [heading Available Strategies]
331 \* [link geometry.reference.strategies.strategy_distance_pythagoras Pythagoras (cartesian)]
332 \* [link geometry.reference.strategies.strategy_distance_haversine Haversine (spherical)]
333 [/ \* more (currently extensions): Vincenty\, Andoyer (geographic) ]
334 
335 [heading Example]
336 [discrete_hausdorff_distance_strategy]
337 [discrete_hausdorff_distance_strategy_output]
338 }
339 */
340 template <typename Geometry1, typename Geometry2, typename Strategy>
discrete_hausdorff_distance(Geometry1 const & geometry1,Geometry2 const & geometry2,Strategy const & strategy)341 inline auto discrete_hausdorff_distance(Geometry1 const& geometry1,
342                                         Geometry2 const& geometry2,
343                                         Strategy const& strategy)
344 {
345     return resolve_strategy::discrete_hausdorff_distance
346         <
347             Strategy
348         >::apply(geometry1, geometry2, strategy);
349 }
350 
351 /*!
352 \brief Calculate discrete Hausdorff distance between two geometries (currently
353     works for LineString-LineString, MultiPoint-MultiPoint, Point-MultiPoint,
354     MultiLineString-MultiLineString).
355 \ingroup discrete_hausdorff_distance
356 \tparam Geometry1 \tparam_geometry
357 \tparam Geometry2 \tparam_geometry
358 \param geometry1 Input geometry
359 \param geometry2 Input geometry
360 
361 \qbk{[include reference/algorithms/discrete_hausdorff_distance.qbk]}
362 
363 \qbk{
364 [heading Example]
365 [discrete_hausdorff_distance]
366 [discrete_hausdorff_distance_output]
367 }
368 */
369 template <typename Geometry1, typename Geometry2>
discrete_hausdorff_distance(Geometry1 const & geometry1,Geometry2 const & geometry2)370 inline auto discrete_hausdorff_distance(Geometry1 const& geometry1,
371                                         Geometry2 const& geometry2)
372 {
373     return resolve_strategy::discrete_hausdorff_distance
374         <
375             default_strategy
376         >::apply(geometry1, geometry2, default_strategy());
377 }
378 
379 }} // namespace boost::geometry
380 
381 #endif // BOOST_GEOMETRY_ALGORITHMS_DISCRETE_HAUSDORFF_DISTANCE_HPP
382