1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 // Unit Test
3 
4 // Copyright (c) 2016-2017, Oracle and/or its affiliates.
5 
6 // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
7 
8 // Licensed under the Boost Software License version 1.0.
9 // http://www.boost.org/users/license.html
10 
11 #ifndef BOOST_GEOMETRY_TEST_DISTANCE_GEO_COMMON_HPP
12 #define BOOST_GEOMETRY_TEST_DISTANCE_GEO_COMMON_HPP
13 
14 #include <iostream>
15 #include <string>
16 
17 #include <boost/mpl/assert.hpp>
18 #include <boost/type_traits/is_integral.hpp>
19 #include <boost/type_traits/is_same.hpp>
20 
21 #include <boost/geometry/geometries/point.hpp>
22 #include <boost/geometry/geometries/point_xy.hpp>
23 #include <boost/geometry/geometries/segment.hpp>
24 #include <boost/geometry/geometries/linestring.hpp>
25 #include <boost/geometry/geometries/polygon.hpp>
26 #include <boost/geometry/geometries/ring.hpp>
27 #include <boost/geometry/geometries/box.hpp>
28 #include <boost/geometry/geometries/multi_point.hpp>
29 #include <boost/geometry/geometries/multi_linestring.hpp>
30 #include <boost/geometry/geometries/multi_polygon.hpp>
31 
32 #include <boost/geometry/io/wkt/write.hpp>
33 #include <boost/geometry/io/dsv/write.hpp>
34 
35 #include <boost/geometry/algorithms/num_interior_rings.hpp>
36 #include <boost/geometry/algorithms/distance.hpp>
37 
38 #include <boost/geometry/strategies/strategies.hpp>
39 
40 #include <from_wkt.hpp>
41 #include <string_from_type.hpp>
42 
43 #include "distance_brute_force.hpp"
44 
45 namespace bg = ::boost::geometry;
46 
47 typedef bg::srs::spheroid<double> stype;
48 
49 // Spherical strategy  for point-point distance
50 
51 typedef bg::strategy::distance::haversine<double> spherical_pp;
52 
53 // Geo strategies for point-point distance
54 
55 typedef bg::strategy::distance::andoyer<stype> andoyer_pp;
56 typedef bg::strategy::distance::thomas<stype> thomas_pp;
57 typedef bg::strategy::distance::vincenty<stype> vincenty_pp;
58 
59 // Spherical strategy  for point-segment distance
60 
61 typedef bg::strategy::distance::cross_track<> spherical_ps;
62 
63 // Geo strategies for point-segment distance
64 
65 typedef bg::strategy::distance::geographic_cross_track<bg::strategy::andoyer, stype, double>
66         andoyer_ps;
67 
68 typedef bg::strategy::distance::geographic_cross_track<bg::strategy::thomas, stype, double>
69         thomas_ps;
70 
71 typedef bg::strategy::distance::geographic_cross_track<bg::strategy::vincenty, stype, double>
72         vincenty_ps;
73 
74 typedef bg::strategy::distance::detail::geographic_cross_track<bg::strategy::vincenty, stype, double, true>
75         vincenty_ps_bisection;
76 
77 // Spherical strategy  for point-box distance
78 
79 typedef bg::strategy::distance::cross_track_point_box<> spherical_pb;
80 
81 // Geo strategies for point-box distance
82 
83 typedef bg::strategy::distance::geographic_cross_track_point_box
84 <
85     bg::strategy::andoyer,
86     stype,
87     double
88 > andoyer_pb;
89 
90 typedef bg::strategy::distance::geographic_cross_track_point_box
91 <
92     bg::strategy::thomas,
93     stype,
94     double
95 > thomas_pb;
96 
97 typedef bg::strategy::distance::geographic_cross_track_point_box
98 <
99     bg::strategy::vincenty,
100     stype,
101     double
102 > vincenty_pb;
103 
104 // Spherical strategy  for segment-box distance
105 
106 typedef bg::strategy::distance::spherical_segment_box<> spherical_sb;
107 
108 // Geo strategies for segment-box distance
109 
110 typedef bg::strategy::distance::geographic_segment_box<bg::strategy::andoyer, stype, double>
111         andoyer_sb;
112 
113 typedef bg::strategy::distance::geographic_segment_box<bg::strategy::thomas, stype, double>
114         thomas_sb;
115 
116 typedef bg::strategy::distance::geographic_segment_box<bg::strategy::vincenty, stype, double>
117         vincenty_sb;
118 
119 // Strategies for box-box distance
120 
121 typedef bg::strategy::distance::cross_track_box_box<> spherical_bb;
122 
123 typedef bg::strategy::distance::geographic_cross_track_box_box
124         <
125             bg::strategy::andoyer,
126             stype,
127             double
128         > andoyer_bb;
129 
130 typedef bg::strategy::distance::geographic_cross_track_box_box
131         <
132             bg::strategy::thomas,
133             stype,
134             double
135         > thomas_bb;
136 
137 typedef bg::strategy::distance::geographic_cross_track_box_box
138         <
139             bg::strategy::vincenty,
140             stype,
141             double
142         > vincenty_bb;
143 
144 //===========================================================================
145 
146 template <typename Point, typename Strategy>
147 inline typename bg::default_distance_result<Point>::type
pp_distance(std::string const & wkt1,std::string const & wkt2,Strategy const & strategy)148 pp_distance(std::string const& wkt1,
149             std::string const& wkt2,
150             Strategy const& strategy)
151 {
152     Point p1, p2;
153     bg::read_wkt(wkt1, p1);
154     bg::read_wkt(wkt2, p2);
155     return bg::distance(p1, p2, strategy);
156 }
157 
158 //===========================================================================
159 
160 template <typename Point, typename Strategy>
161 inline typename bg::default_distance_result<Point>::type
ps_distance(std::string const & wkt1,std::string const & wkt2,Strategy const & strategy)162 ps_distance(std::string const& wkt1,
163             std::string const& wkt2,
164             Strategy const& strategy)
165 {
166     Point p;
167     typedef bg::model::segment<Point> segment_type;
168     segment_type s;
169     bg::read_wkt(wkt1, p);
170     bg::read_wkt(wkt2, s);
171     return bg::distance(p, s, strategy);
172 }
173 
174 //===========================================================================
175 
176 template <typename Point, typename Strategy>
177 inline typename bg::default_distance_result<Point>::type
sb_distance(std::string const & wkt1,std::string const & wkt2,Strategy const & strategy)178 sb_distance(std::string const& wkt1,
179             std::string const& wkt2,
180             Strategy const& strategy)
181 {
182     typedef bg::model::segment<Point> segment_type;
183     typedef bg::model::box<Point> box_type;
184     segment_type s;
185     box_type b;
186     bg::read_wkt(wkt1, s);
187     bg::read_wkt(wkt2, b);
188     return bg::distance(s, b, strategy);
189 }
190 
191 //===================================================================
192 
193 template <typename Tag> struct dispatch
194 {
195     //tag dispatching for swaping arguments in segments
196     template <typename T>
swapdispatch197     static inline T swap(T const& t)
198     {
199         return t;
200     }
201 
202     // mirror geometry w.r.t. equator
203     template <typename T>
mirrordispatch204     static inline T mirror(T const& t)
205     {
206         return t;
207     }
208 };
209 
210 // Specialization for segments
211 template <> struct dispatch<boost::geometry::segment_tag>
212 {
213     template <typename Segment>
swapdispatch214     static inline Segment swap(Segment const& s)
215     {
216         Segment s_swaped;
217 
218         bg::set<0, 0>(s_swaped, bg::get<1, 0>(s));
219         bg::set<0, 1>(s_swaped, bg::get<1, 1>(s));
220         bg::set<1, 0>(s_swaped, bg::get<0, 0>(s));
221         bg::set<1, 1>(s_swaped, bg::get<0, 1>(s));
222 
223         return s_swaped;
224     }
225 
226     template <typename Segment>
mirrordispatch227     static inline Segment mirror(Segment const& s)
228     {
229         Segment s_mirror;
230 
231         bg::set<0, 0>(s_mirror, bg::get<0, 0>(s));
232         bg::set<0, 1>(s_mirror, bg::get<0, 1>(s) * -1);
233         bg::set<1, 0>(s_mirror, bg::get<1, 0>(s));
234         bg::set<1, 1>(s_mirror, bg::get<1, 1>(s) * -1);
235 
236         return s_mirror;
237     }
238 };
239 
240 // Specialization for boxes
241 template <> struct dispatch<boost::geometry::box_tag>
242 {
243     template <typename T>
swapdispatch244     static inline T swap(T const& t)
245     {
246         return t;
247     }
248 
249     template <typename Box>
mirrordispatch250     static inline Box mirror(Box const& b)
251     {
252         Box b_mirror;
253 
254         bg::set<0, 0>(b_mirror, bg::get<bg::min_corner, 0>(b));
255         bg::set<0, 1>(b_mirror, bg::get<bg::max_corner, 1>(b) * -1);
256         bg::set<1, 0>(b_mirror, bg::get<bg::max_corner, 0>(b));
257         bg::set<1, 1>(b_mirror, bg::get<bg::min_corner, 1>(b) * -1);
258 
259         return b_mirror;
260     }
261 };
262 
263 
264 // Specialization for points
265 template <> struct dispatch<boost::geometry::point_tag>
266 {
267     template <typename T>
swapdispatch268     static inline T swap(T const& t)
269     {
270         return t;
271     }
272 
273     template <typename Point>
mirrordispatch274     static inline Point mirror(Point const& p)
275     {
276         Point p_mirror;
277 
278         bg::set<0>(p_mirror, bg::get<0>(p));
279         bg::set<1>(p_mirror, bg::get<1>(p) * -1);
280 
281         return p_mirror;
282     }
283 };
284 
285 //========================================================================
286 
287 
288 template <typename T>
289 struct check_equal
290 {
291     template <typename Value, typename = void>
292     struct equal_to
293     {
applycheck_equal::equal_to294         static inline void apply(Value const& x, Value const& y)
295         {
296             BOOST_CHECK(x == y);
297         }
298     };
299 
300     template <typename Dummy>
301     struct equal_to<double, Dummy>
302     {
applycheck_equal::equal_to303         static inline void apply(double x, double y)
304         {
305             BOOST_CHECK_CLOSE(x, y, 0.001);
306         }
307     };
308 
309     template <typename Geometry1, typename Geometry2>
applycheck_equal310     static inline void apply(std::string const& /*case_id*/,
311                              std::string const& /*subcase_id*/,
312                              Geometry1 const& /*geometry1*/,
313                              Geometry2 const& /*geometry2*/,
314                              T const& detected,
315                              T const& expected)
316     {
317         equal_to<T>::apply(expected, detected);
318     }
319 };
320 
321 //========================================================================
322 
323 template
324 <
325     typename Geometry1, typename Geometry2,
326     int id1 = bg::geometry_id<Geometry1>::value,
327     int id2 = bg::geometry_id<Geometry2>::value
328 >
329 struct test_distance_of_geometries
330     : public test_distance_of_geometries<Geometry1, Geometry2, 0, 0>
331 {};
332 
333 
334 template <typename Geometry1, typename Geometry2>
335 struct test_distance_of_geometries<Geometry1, Geometry2, 0, 0>
336 {
337     template <typename DistanceType, typename Strategy>
338     static inline
applytest_distance_of_geometries339     void apply(std::string const& case_id,
340                std::string const& wkt1,
341                std::string const& wkt2,
342                DistanceType const& expected_distance,
343                Strategy const& strategy,
344                bool test_reversed = true,
345                bool swap_geometry_args = false,
346                bool mirror_geometry = true)
347     {
348         Geometry1 geometry1 = from_wkt<Geometry1>(wkt1);
349         Geometry2 geometry2 = from_wkt<Geometry2>(wkt2);
350 
351         apply(case_id, geometry1, geometry2,
352               expected_distance,
353               strategy, test_reversed, swap_geometry_args,
354               mirror_geometry);
355     }
356 
357 
358     template
359     <
360         typename DistanceType,
361         typename Strategy
362     >
363     static inline
applytest_distance_of_geometries364     void apply(std::string const& case_id,
365                Geometry1 const& geometry1,
366                Geometry2 const& geometry2,
367                DistanceType const& expected_distance,
368                Strategy const& strategy,
369                bool test_reversed = true,
370                bool swap_geometry_args = false,
371                bool mirror_geometry = true)
372     {
373 #ifdef BOOST_GEOMETRY_TEST_DEBUG
374         std::cout << "case ID: " << case_id << "; "
375                   << "G1: " << bg::wkt(geometry1)
376                   << " - "
377                   << "G2: " << bg::wkt(geometry2)
378                   << std::endl;
379 #endif
380         namespace services = bg::strategy::distance::services;
381 
382         using bg::unit_test::distance_brute_force;
383 
384         typedef typename bg::default_distance_result
385             <
386                 Geometry1, Geometry2
387             >::type default_distance_result;
388 
389         typedef typename services::return_type
390             <
391                 Strategy, Geometry1, Geometry2
392             >::type distance_result_from_strategy;
393 
394         static const bool same_regular = boost::is_same
395             <
396                 default_distance_result,
397                 distance_result_from_strategy
398             >::type::value;
399 
400         BOOST_CHECK(same_regular);
401 
402         // check distance with passed strategy
403         distance_result_from_strategy dist =
404             bg::distance(geometry1, geometry2, strategy);
405 
406         check_equal
407             <
408                 distance_result_from_strategy
409             >::apply(case_id, "a", geometry1, geometry2,
410                      dist, expected_distance);
411 
412         // check against the comparable distance computed in a
413         // brute-force manner
414         default_distance_result dist_brute_force
415             = distance_brute_force(geometry1, geometry2, strategy);
416 
417         check_equal
418             <
419                 default_distance_result
420             >::apply(case_id, "b", geometry1, geometry2,
421                      dist_brute_force, expected_distance);
422 
423 #ifdef BOOST_GEOMETRY_TEST_DEBUG
424         std::cout << string_from_type<typename bg::coordinate_type<Geometry1>::type>::name()
425                   << string_from_type<typename bg::coordinate_type<Geometry2>::type>::name()
426                   << " -> "
427                   << string_from_type<default_distance_result>::name()
428                   << std::endl;
429         std::cout << "expected distance = " << std::setprecision(10)
430                   << expected_distance << " ; "
431                   << std::endl;
432         std::cout << "distance = " << std::setprecision(10)
433                   << dist << " ; "
434                   << std::endl;
435 
436         if ( !test_reversed )
437         {
438             std::cout << std::endl;
439         }
440 #endif
441 
442         if ( test_reversed )
443         {
444             // check distance with given strategy
445             dist = bg::distance(geometry2, geometry1, strategy);
446 
447             check_equal
448                 <
449                     default_distance_result
450                 >::apply(case_id, "ra", geometry2, geometry1,
451                          dist, expected_distance);
452 
453 #ifdef BOOST_GEOMETRY_TEST_DEBUG
454             std::cout << "distance[reversed args] = "  << std::setprecision(10)
455                       << dist
456                       << std::endl;
457 #endif
458         }
459 
460         if (swap_geometry_args)
461         {
462             Geometry1 g1 = dispatch
463                 <
464                     typename boost::geometry::tag<Geometry1>::type
465                 >::swap(geometry1);
466 
467             Geometry2 g2 = dispatch
468                 <
469                     typename boost::geometry::tag<Geometry2>::type
470                 >::swap(geometry2);
471 
472             // check distance with given strategy
473             dist = bg::distance(g1, g2, strategy);
474 
475             check_equal
476                 <
477                     default_distance_result
478                 >::apply(case_id, "swap", g1, g2,
479                          dist, expected_distance);
480 
481 #ifdef BOOST_GEOMETRY_TEST_DEBUG
482             std::cout << "distance[swap geometry args] = "  << std::setprecision(10)
483                       << dist
484                       << std::endl;
485             std::cout << std::endl;
486 #endif
487         }
488         if (mirror_geometry)
489         {
490             Geometry1 g1 = dispatch
491                 <
492                     typename boost::geometry::tag<Geometry1>::type
493                 >::mirror(geometry1);
494 
495             Geometry2 g2 = dispatch
496                 <
497                     typename boost::geometry::tag<Geometry2>::type
498                 >::mirror(geometry2);
499 
500             // check distance with given strategy
501             dist = bg::distance(g1, g2, strategy);
502 
503             check_equal
504                 <
505                     default_distance_result
506                 >::apply(case_id, "mirror", g1, g2,
507                          dist, expected_distance);
508 
509 #ifdef BOOST_GEOMETRY_TEST_DEBUG
510             std::cout << "distance[mirror geometries] = "  << std::setprecision(10)
511                       << dist
512                       << std::endl;
513             std::cout << std::endl;
514 #endif
515         }
516 #ifdef BOOST_GEOMETRY_TEST_DEBUG
517             std::cout << std::endl;
518 #endif
519 
520     }
521 };
522 
523 
524 //========================================================================
525 
526 
527 template <typename Geometry1, typename Geometry2, typename Strategy>
test_empty_input(Geometry1 const & geometry1,Geometry2 const & geometry2,Strategy const & strategy)528 void test_empty_input(Geometry1 const& geometry1,
529                       Geometry2 const& geometry2,
530                       Strategy const& strategy)
531 {
532     try
533     {
534         bg::distance(geometry1, geometry2);
535     }
536     catch(bg::empty_input_exception const& )
537     {
538         return;
539     }
540     BOOST_CHECK_MESSAGE(false,
541                         "A empty_input_exception should have been thrown");
542 
543     try
544     {
545         bg::distance(geometry2, geometry1);
546     }
547     catch(bg::empty_input_exception const& )
548     {
549         return;
550     }
551     BOOST_CHECK_MESSAGE(false,
552                         "A empty_input_exception should have been thrown");
553 
554     try
555     {
556         bg::distance(geometry1, geometry2, strategy);
557     }
558     catch(bg::empty_input_exception const& )
559     {
560         return;
561     }
562     BOOST_CHECK_MESSAGE(false,
563                         "A empty_input_exception should have been thrown");
564 
565     try
566     {
567         bg::distance(geometry2, geometry1, strategy);
568     }
569     catch(bg::empty_input_exception const& )
570     {
571         return;
572     }
573     BOOST_CHECK_MESSAGE(false,
574                         "A empty_input_exception should have been thrown");
575 }
576 
577 #endif // BOOST_GEOMETRY_TEST_DISTANCE_GEO_COMMON_HPP
578