]>
Commit | Line | Data |
---|---|---|
b32b8144 FG |
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 | ||
92f5a8d4 TL |
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 | |
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 | |
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 | |
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 | ||
11fdf7f2 | 191 | //=================================================================== |
11fdf7f2 TL |
192 | |
193 | template <typename Tag> struct dispatch | |
194 | { | |
92f5a8d4 | 195 | //tag dispatching for swaping arguments in segments |
11fdf7f2 TL |
196 | template <typename T> |
197 | static inline T swap(T const& t) | |
198 | { | |
199 | return t; | |
200 | } | |
92f5a8d4 TL |
201 | |
202 | // mirror geometry w.r.t. equator | |
203 | template <typename T> | |
204 | static inline T mirror(T const& t) | |
205 | { | |
206 | return t; | |
207 | } | |
11fdf7f2 TL |
208 | }; |
209 | ||
210 | // Specialization for segments | |
211 | template <> struct dispatch<boost::geometry::segment_tag> | |
212 | { | |
213 | template <typename Segment> | |
214 | 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 | } | |
92f5a8d4 TL |
225 | |
226 | template <typename Segment> | |
227 | 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> | |
244 | static inline T swap(T const& t) | |
245 | { | |
246 | return t; | |
247 | } | |
248 | ||
249 | template <typename Box> | |
250 | 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> | |
268 | static inline T swap(T const& t) | |
269 | { | |
270 | return t; | |
271 | } | |
272 | ||
273 | template <typename Point> | |
274 | 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 | } | |
11fdf7f2 TL |
283 | }; |
284 | ||
b32b8144 FG |
285 | //======================================================================== |
286 | ||
287 | ||
288 | template <typename T> | |
289 | struct check_equal | |
290 | { | |
291 | template <typename Value, typename = void> | |
292 | struct equal_to | |
293 | { | |
294 | 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 | { | |
303 | 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> | |
310 | 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 | |
339 | 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, | |
11fdf7f2 | 344 | bool test_reversed = true, |
92f5a8d4 TL |
345 | bool swap_geometry_args = false, |
346 | bool mirror_geometry = true) | |
b32b8144 FG |
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, | |
92f5a8d4 TL |
353 | strategy, test_reversed, swap_geometry_args, |
354 | mirror_geometry); | |
b32b8144 FG |
355 | } |
356 | ||
357 | ||
358 | template | |
359 | < | |
360 | typename DistanceType, | |
361 | typename Strategy | |
362 | > | |
363 | static inline | |
364 | void apply(std::string const& case_id, | |
365 | Geometry1 const& geometry1, | |
366 | Geometry2 const& geometry2, | |
367 | DistanceType const& expected_distance, | |
368 | Strategy const& strategy, | |
11fdf7f2 | 369 | bool test_reversed = true, |
92f5a8d4 TL |
370 | bool swap_geometry_args = false, |
371 | bool mirror_geometry = true) | |
b32b8144 FG |
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; | |
92f5a8d4 | 429 | std::cout << "expected distance = " << std::setprecision(10) |
b32b8144 FG |
430 | << expected_distance << " ; " |
431 | << std::endl; | |
92f5a8d4 | 432 | std::cout << "distance = " << std::setprecision(10) |
b32b8144 FG |
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 | |
92f5a8d4 | 454 | std::cout << "distance[reversed args] = " << std::setprecision(10) |
11fdf7f2 | 455 | << dist |
b32b8144 | 456 | << std::endl; |
b32b8144 FG |
457 | #endif |
458 | } | |
459 | ||
11fdf7f2 TL |
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 | |
92f5a8d4 | 482 | std::cout << "distance[swap geometry args] = " << std::setprecision(10) |
11fdf7f2 TL |
483 | << dist |
484 | << std::endl; | |
485 | std::cout << std::endl; | |
486 | #endif | |
92f5a8d4 TL |
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 | } | |
11fdf7f2 TL |
516 | #ifdef BOOST_GEOMETRY_TEST_DEBUG |
517 | std::cout << std::endl; | |
518 | #endif | |
519 | ||
b32b8144 FG |
520 | } |
521 | }; | |
522 | ||
523 | ||
524 | //======================================================================== | |
525 | ||
526 | ||
527 | template <typename Geometry1, typename Geometry2, typename 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 |