• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 // Unit Test
3 
4 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
5 // Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
6 // Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
7 
8 // This file was modified by Oracle on 2014, 2015, 2016, 2017.
9 // Modifications copyright (c) 2014-2017 Oracle and/or its affiliates.
10 
11 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
12 
13 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
14 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
15 
16 // Use, modification and distribution is subject to the Boost Software License,
17 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
18 // http://www.boost.org/LICENSE_1_0.txt)
19 
20 
21 #include <geometry_test_common.hpp>
22 
23 #include <boost/concept_check.hpp>
24 
25 #include <boost/geometry/algorithms/assign.hpp>
26 #include <boost/geometry/algorithms/distance.hpp>
27 #include <boost/geometry/formulas/vincenty_inverse.hpp>
28 #include <boost/geometry/formulas/vincenty_direct.hpp>
29 #include <boost/geometry/geometries/point.hpp>
30 #include <boost/geometry/srs/spheroid.hpp>
31 #include <boost/geometry/strategies/concepts/distance_concept.hpp>
32 #include <boost/geometry/strategies/geographic/distance_vincenty.hpp>
33 #include <boost/geometry/strategies/geographic/side_vincenty.hpp>
34 
35 #include <test_common/test_point.hpp>
36 
37 #ifdef HAVE_TTMATH
38 #  include <boost/geometry/extensions/contrib/ttmath_stub.hpp>
39 #endif
40 
41 template <typename T>
normalize_deg(T & deg)42 void normalize_deg(T & deg)
43 {
44     while ( deg > T(180) )
45         deg -= T(360);
46     while ( deg <= T(-180) )
47         deg += T(360);
48 }
49 
50 template <typename T>
difference_deg(T const & a1,T const & a2)51 T difference_deg(T const& a1, T const& a2)
52 {
53     T d = a1 - a2;
54     normalize_deg(d);
55     return d;
56 }
57 
58 template <typename T>
check_deg(std::string const & name,T const & a1,T const & a2,T const & percent,T const & error)59 void check_deg(std::string const& name, T const& a1, T const& a2, T const& percent, T const& error)
60 {
61     T diff = bg::math::abs(difference_deg(a1, a2));
62 
63     if ( bg::math::equals(a1, T(0)) || bg::math::equals(a2, T(0)) )
64     {
65         if ( diff > error )
66         {
67             BOOST_ERROR(name << " - the difference {" << diff << "} between {" << a1 << "} and {" << a2 << "} exceeds {" << error << "}");
68         }
69     }
70     else
71     {
72         T greater = (std::max)(bg::math::abs(a1), bg::math::abs(a2));
73 
74         if ( diff > greater * percent / T(100) )
75         {
76             BOOST_ERROR(name << " the difference {" << diff << "} between {" << a1 << "} and {" << a2 << "} exceeds {" << percent << "}%");
77         }
78     }
79 }
80 
azimuth(double deg,double min,double sec)81 double azimuth(double deg, double min, double sec)
82 {
83     min = fabs(min);
84     sec = fabs(sec);
85 
86     if ( deg < 0 )
87     {
88         min = -min;
89         sec = -sec;
90     }
91 
92     return deg + min/60.0 + sec/3600.0;
93 }
94 
azimuth(double deg,double min)95 double azimuth(double deg, double min)
96 {
97     return azimuth(deg, min, 0.0);
98 }
99 
100 template <typename P>
non_precise_ct()101 bool non_precise_ct()
102 {
103     typedef typename bg::coordinate_type<P>::type ct;
104     return boost::is_integral<ct>::value || boost::is_float<ct>::value;
105 }
106 
107 template <typename P1, typename P2, typename Spheroid>
test_vincenty(double lon1,double lat1,double lon2,double lat2,double expected_distance,double expected_azimuth_12,double,Spheroid const & spheroid)108 void test_vincenty(double lon1, double lat1, double lon2, double lat2,
109                    double expected_distance,
110                    double expected_azimuth_12,
111                    double /*expected_azimuth_21*/,
112                    Spheroid const& spheroid)
113 {
114     typedef typename bg::promote_floating_point
115         <
116             typename bg::select_calculation_type<P1, P2, void>::type
117         >::type calc_t;
118 
119     calc_t tolerance = non_precise_ct<P1>() || non_precise_ct<P2>() ?
120                        5.0 : 0.001;
121     calc_t error = non_precise_ct<P1>() || non_precise_ct<P2>() ?
122                    1e-5 : 1e-12;
123 
124     // formula
125     {
126         double const d2r = bg::math::d2r<double>();
127         double const r2d = bg::math::r2d<double>();
128 
129         typedef bg::formula::vincenty_inverse<calc_t, true, true> inverse_formula;
130         typename inverse_formula::result_type
131             result_i = inverse_formula::apply(lon1 * d2r,
132                                              lat1 * d2r,
133                                              lon2 * d2r,
134                                              lat2 * d2r,
135                                              spheroid);
136         calc_t dist = result_i.distance;
137         calc_t az12 = result_i.azimuth;
138         //calc_t az21 = vi.azimuth21();
139 
140         calc_t az12_deg = az12 * r2d;
141         //calc_t az21_deg = az21 * r2d;
142 
143         BOOST_CHECK_CLOSE(dist, calc_t(expected_distance), tolerance);
144         check_deg("az12_deg", az12_deg, calc_t(expected_azimuth_12), tolerance, error);
145         //check_deg("az21_deg", az21_deg, calc_t(expected_azimuth_21), tolerance, error);
146 
147         typedef bg::formula::vincenty_direct<calc_t> direct_formula;
148         typename direct_formula::result_type
149             result_d = direct_formula::apply(lon1 * d2r,
150                                              lat1 * d2r,
151                                              dist,
152                                              az12,
153                                              spheroid);
154         calc_t direct_lon2 = result_d.lon2;
155         calc_t direct_lat2 = result_d.lat2;
156         //calc_t direct_az21 = vd.azimuth21();
157 
158         calc_t direct_lon2_deg = direct_lon2 * r2d;
159         calc_t direct_lat2_deg = direct_lat2 * r2d;
160         //calc_t direct_az21_deg = direct_az21 * r2d;
161 
162         check_deg("direct_lon2_deg", direct_lon2_deg, calc_t(lon2), tolerance, error);
163         check_deg("direct_lat2_deg", direct_lat2_deg, calc_t(lat2), tolerance, error);
164         //check_deg("direct_az21_deg", direct_az21_deg, az21_deg, tolerance, error);
165     }
166 
167     // distance strategies
168     {
169         typedef bg::strategy::distance::vincenty<Spheroid> vincenty_type;
170         typedef bg::strategy::distance::geographic<bg::strategy::vincenty, Spheroid> geographic_type;
171 
172         BOOST_CONCEPT_ASSERT(
173             (
174                 bg::concepts::PointDistanceStrategy<vincenty_type, P1, P2>)
175             );
176 
177         vincenty_type vincenty(spheroid);
178         geographic_type geographic(spheroid);
179         typedef typename bg::strategy::distance::services::return_type<vincenty_type, P1, P2>::type return_type;
180 
181         P1 p1;
182         P2 p2;
183 
184         bg::assign_values(p1, lon1, lat1);
185         bg::assign_values(p2, lon2, lat2);
186 
187         BOOST_CHECK_CLOSE(vincenty.apply(p1, p2), return_type(expected_distance), tolerance);
188         BOOST_CHECK_CLOSE(geographic.apply(p1, p2), return_type(expected_distance), tolerance);
189         BOOST_CHECK_CLOSE(bg::distance(p1, p2, vincenty), return_type(expected_distance), tolerance);
190     }
191 }
192 
193 template <typename P1, typename P2>
test_vincenty(double lon1,double lat1,double lon2,double lat2,double expected_distance,double expected_azimuth_12,double expected_azimuth_21)194 void test_vincenty(double lon1, double lat1, double lon2, double lat2,
195                    double expected_distance,
196                    double expected_azimuth_12,
197                    double expected_azimuth_21)
198 {
199     test_vincenty<P1, P2>(lon1, lat1, lon2, lat2,
200                           expected_distance, expected_azimuth_12, expected_azimuth_21,
201                           bg::srs::spheroid<double>());
202 }
203 
204 template <typename PS, typename P>
test_side(double lon1,double lat1,double lon2,double lat2,double lon,double lat,int expected_side)205 void test_side(double lon1, double lat1,
206                double lon2, double lat2,
207                double lon, double lat,
208                int expected_side)
209 {
210     // Set radius type, but for integer coordinates we want to have floating point radius type
211     typedef typename bg::promote_floating_point
212         <
213             typename bg::coordinate_type<PS>::type
214         >::type rtype;
215 
216     typedef bg::srs::spheroid<rtype> stype;
217 
218     typedef bg::strategy::side::vincenty<stype> strategy_type;
219     typedef bg::strategy::side::geographic<bg::strategy::vincenty, stype> strategy2_type;
220 
221     strategy_type strategy;
222     strategy2_type strategy2;
223 
224     PS p1, p2;
225     P p;
226 
227     bg::assign_values(p1, lon1, lat1);
228     bg::assign_values(p2, lon2, lat2);
229     bg::assign_values(p, lon, lat);
230 
231     int side = strategy.apply(p1, p2, p);
232     int side2 = strategy2.apply(p1, p2, p);
233 
234     BOOST_CHECK_EQUAL(side, expected_side);
235     BOOST_CHECK_EQUAL(side2, expected_side);
236 }
237 
238 template <typename P1, typename P2>
test_all()239 void test_all()
240 {
241     // See:
242     //  - http://www.ga.gov.au/geodesy/datums/vincenty_inverse.jsp
243     //  - http://www.ga.gov.au/geodesy/datums/vincenty_direct.jsp
244     // Values in the comments below was calculated using the above pages
245     // in some cases distances may be different, previously used values was left
246 
247     // use km
248     double gda_a = 6378.1370;
249     double gda_f = 1.0 / 298.25722210;
250     double gda_b = gda_a * ( 1.0 - gda_f );
251     bg::srs::spheroid<double> gda_spheroid(gda_a, gda_b);
252 
253     // Test fractional coordinates only for non-integral types
254     if ( BOOST_GEOMETRY_CONDITION(
255             ! boost::is_integral<typename bg::coordinate_type<P1>::type>::value
256          && ! boost::is_integral<typename bg::coordinate_type<P2>::type>::value  ) )
257     {
258         // Flinders Peak -> Buninyong
259         test_vincenty<P1, P2>(azimuth(144,25,29.52440), azimuth(-37,57,3.72030),
260                               azimuth(143,55,35.38390), azimuth(-37,39,10.15610),
261                               54.972271, azimuth(306,52,5.37), azimuth(127,10,25.07),
262                               gda_spheroid);
263     }
264 
265     // Lodz -> Trondheim
266     test_vincenty<P1, P2>(azimuth(19,28), azimuth(51,47),
267                           azimuth(10,21), azimuth(63,23),
268                           1399.032724, azimuth(340,54,25.14), azimuth(153,10,0.19),
269                           gda_spheroid);
270     // London -> New York
271     test_vincenty<P1, P2>(azimuth(0,7,39), azimuth(51,30,26),
272                           azimuth(-74,0,21), azimuth(40,42,46),
273                           5602.044851, azimuth(288,31,36.82), azimuth(51,10,33.43),
274                           gda_spheroid);
275 
276     // Shanghai -> San Francisco
277     test_vincenty<P1, P2>(azimuth(121,30), azimuth(31,12),
278                           azimuth(-122,25), azimuth(37,47),
279                           9899.698550, azimuth(45,12,44.76), azimuth(309,50,20.88),
280                           gda_spheroid);
281 
282     test_vincenty<P1, P2>(0, 0, 0, 50, 5540.847042, 0, 180, gda_spheroid); // N
283     test_vincenty<P1, P2>(0, 0, 0, -50, 5540.847042, 180, 0, gda_spheroid); // S
284     test_vincenty<P1, P2>(0, 0, 50, 0, 5565.974540, 90, -90, gda_spheroid); // E
285     test_vincenty<P1, P2>(0, 0, -50, 0, 5565.974540, -90, 90, gda_spheroid); // W
286 
287     test_vincenty<P1, P2>(0, 0, 50, 50, 7284.879297, azimuth(32,51,55.87), azimuth(237,24,50.12), gda_spheroid); // NE
288 
289     // The original distance values, azimuths calculated using the web form mentioned above
290     // Using default spheroid units (meters)
291     test_vincenty<P1, P2>(0, 89, 1, 80, 1005153.5769, azimuth(178,53,23.85), azimuth(359,53,18.35)); // sub-polar
292     test_vincenty<P1, P2>(4, 52, 4, 52, 0.0, 0, 0); // no point difference
293     test_vincenty<P1, P2>(4, 52, 3, 40, 1336039.890, azimuth(183,41,29.08), azimuth(2,58,5.13)); // normal case
294 
295     test_side<P1, P2>(0, 0, 0, 1, 0, 2, 0);
296     test_side<P1, P2>(0, 0, 0, 1, 0, -2, 0);
297     test_side<P1, P2>(10, 0, 10, 1, 10, 2, 0);
298     test_side<P1, P2>(10, 0, 10, -1, 10, 2, 0);
299 
300     test_side<P1, P2>(10, 0, 10, 1, 0, 2, 1); // left
301     test_side<P1, P2>(10, 0, 10, -1, 0, 2, -1); // right
302 
303     test_side<P1, P2>(-10, -10, 10, 10, 10, 0, -1); // right
304     test_side<P1, P2>(-10, -10, 10, 10, -10, 0, 1); // left
305     test_side<P1, P2>(170, -10, -170, 10, -170, 0, -1); // right
306     test_side<P1, P2>(170, -10, -170, 10, 170, 0, 1); // left
307 }
308 
309 template <typename P>
test_all()310 void test_all()
311 {
312     test_all<P, P>();
313 }
314 
test_main(int,char * [])315 int test_main(int, char* [])
316 {
317     //test_all<float[2]>();
318     //test_all<double[2]>();
319     test_all<bg::model::point<double, 2, bg::cs::geographic<bg::degree> > >();
320     test_all<bg::model::point<float, 2, bg::cs::geographic<bg::degree> > >();
321     test_all<bg::model::point<int, 2, bg::cs::geographic<bg::degree> > >();
322 
323 #if defined(HAVE_TTMATH)
324     test_all<bg::model::point<ttmath::Big<1,4>, 2, bg::cs::geographic<bg::degree> > >();
325     test_all<bg::model::point<ttmath_big, 2, bg::cs::geographic<bg::degree> > >();
326 #endif
327 
328 
329     return 0;
330 }
331