1 // Boost.Geometry
2 // Unit Test
3 
4 // Copyright (c) 2018, 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 
12 #include <geometry_test_common.hpp>
13 
14 #include <boost/geometry.hpp>
15 #include <boost/geometry/geometries/geometries.hpp>
16 
17 #include <boost/geometry/algorithms/line_interpolate.hpp>
18 #include <boost/geometry/algorithms/length.hpp>
19 
20 #include <boost/geometry/iterators/segment_iterator.hpp>
21 
22 #include <boost/geometry/strategies/strategies.hpp>
23 
24 #include <boost/geometry/io/wkt/wkt.hpp>
25 
26 template <typename P, typename Tag = typename bg::tag<P>::type>
27 struct check_points: bg::not_implemented<Tag>
28 {};
29 
30 template <typename P>
31 struct check_points<P, bg::point_tag>
32 {
applycheck_points33     static void apply(P const& p0, P const& p1)
34     {
35         double p00 = bg::get<0>(p0);
36         double p10 = bg::get<0>(p1);
37 
38         BOOST_CHECK_CLOSE(p00, p10, 0.001);
39 
40         double p01 = bg::get<1>(p0);
41         double p11 = bg::get<1>(p1);
42 
43         BOOST_CHECK_CLOSE(p01, p11, 0.001);
44     }
45 };
46 
47 template <typename P>
48 struct check_points<P, bg::multi_point_tag>
49 {
50     template <typename Range>
applycheck_points51     static void apply(Range const& r0, Range const& r1)
52     {
53 
54         typedef typename boost::range_iterator<Range const>::type iterator_t;
55         typedef typename boost::range_value<Range const>::type point_t;
56 
57         std::size_t count0 = boost::size(r0);
58         std::size_t count1 = boost::size(r1);
59 
60         BOOST_CHECK_MESSAGE(count0 == count1, bg::wkt(r0) << " != " << bg::wkt(r1));
61 
62         if (count0 == count1)
63         {
64             for (iterator_t it0 = boost::begin(r0), it1 = boost::begin(r1);
65                  it0 < boost::end(r0); it0++, it1++)
66             {
67                 check_points<point_t>::apply(*it0, *it1);
68             }
69         }
70     }
71 };
72 
73 template <typename G, typename P, typename S>
test(std::string const & wkt1,double fraction,std::string const & wkt2,S str)74 inline void test(std::string const& wkt1,
75                  double fraction,
76                  std::string const& wkt2,
77                  S str)
78 {
79     G g;
80     bg::read_wkt(wkt1, g);
81 
82     P o;
83     bg::read_wkt(wkt2, o);
84 
85     P p1;
86     bg::line_interpolate(g, fraction * bg::length(g), p1, str);
87     check_points<P>::apply(p1, o);
88 
89 }
90 
91 template <typename G, typename P>
test(std::string const & wkt1,double fraction,std::string const & wkt2)92 inline void test(std::string const& wkt1,
93                  double fraction,
94                  std::string const& wkt2)
95 {
96     G g;
97     bg::read_wkt(wkt1, g);
98 
99     P o;
100     bg::read_wkt(wkt2, o);
101 
102     P p1;
103     bg::line_interpolate(g, fraction * bg::length(g), p1);
104     check_points<P>::apply(p1, o);
105 }
106 
107 template <typename G, typename P>
test_distance(std::string const & wkt1,double distance,std::string const & wkt2)108 inline void test_distance(std::string const& wkt1,
109                           double distance,
110                           std::string const& wkt2)
111 {
112     G g;
113     bg::read_wkt(wkt1, g);
114 
115     P o;
116     bg::read_wkt(wkt2, o);
117 
118     P p1;
119     bg::line_interpolate(g, distance, p1);
120     check_points<P>::apply(p1, o);
121 }
122 
123 template <typename G, typename P, typename S>
test_distance(std::string const & wkt1,double distance,std::string const & wkt2,S str)124 inline void test_distance(std::string const& wkt1,
125                           double distance,
126                           std::string const& wkt2,
127                           S str)
128 {
129     G g;
130     bg::read_wkt(wkt1, g);
131 
132     P o;
133     bg::read_wkt(wkt2, o);
134 
135     P p1;
136     bg::line_interpolate(g, distance, p1, str);
137     check_points<P>::apply(p1, o);
138 }
139 
140 std::string const s = "SEGMENT(1 1, 2 2)";
141 std::string const l1 = "LINESTRING(1 1, 2 1, 2 2, 1 2, 1 3)";
142 std::string const l2 = "LINESTRING(0 2, 5 2, 5 1, 20 1)";
143 std::string const l00 = "LINESTRING()";
144 std::string const l01 = "LINESTRING(1 1)";
145 std::string const l02 = "LINESTRING(1 1, 1 1)";
146 
test_car_edge_cases()147 void test_car_edge_cases()
148 {
149     typedef bg::model::point<double, 2, bg::cs::cartesian> P;
150     typedef bg::model::multi_point<P> MP;
151     typedef bg::model::linestring<P> LS;
152 
153     //negative input distance
154     test_distance<LS,P>(l1, -1, "POINT(1 1)");
155     test_distance<LS,MP>(l1, -1, "MULTIPOINT((1 1))");
156 
157     //input distance longer than total length
158     test_distance<LS,P>(l1, 5, "POINT(1 3)");
159     test_distance<LS,MP>(l1, 5, "MULTIPOINT((1 3))");
160 
161     //linestring with only one point
162     test_distance<LS,P>(l01, 1, "POINT(1 1)");
163     test_distance<LS,MP>(l01, 1, "MULTIPOINT((1 1))");
164 
165     //linestring with two same points
166     test_distance<LS,P>(l02, 1, "POINT(1 1)");
167     test_distance<LS,MP>(l02, 1, "MULTIPOINT((1 1))");
168 
169     //empty linestring
170     try
171     {
172         test_distance<LS,P>(l00, 1, "POINT(1 1)");
173     }
174     catch(bg::empty_input_exception const& )
175     {
176         return;
177     }
178     BOOST_CHECK_MESSAGE(false, "A empty_input_exception should have been thrown" );
179 }
180 
test_car()181 void test_car()
182 {
183     typedef bg::model::point<double, 2, bg::cs::cartesian> P;
184     typedef bg::model::multi_point<P> MP;
185     typedef bg::model::segment<P> S;
186     typedef bg::model::linestring<P> LS;
187 
188     test<S,P>(s, 0,   "POINT(1 1)");
189     test<S,P>(s, 0.5, "POINT(1.5 1.5)");
190     test<S,P>(s, 1,   "POINT(2 2)");
191 
192     test<LS,P>(l1, 0,   "POINT(1 1)");
193     test<LS,P>(l1, 0.1, "POINT(1.4 1)");
194     test<LS,P>(l1, 0.2, "POINT(1.8 1)");
195     test<LS,P>(l1, 0.3, "POINT(2 1.2)");
196     test<LS,P>(l1, 0.4, "POINT(2 1.6)");
197     test<LS,P>(l1, 0.5, "POINT(2 2)");
198     test<LS,P>(l1, 0.6, "POINT(1.6 2)");
199     test<LS,P>(l1, 0.7, "POINT(1.2 2)");
200     test<LS,P>(l1, 0.8, "POINT(1 2.2)");
201     test<LS,P>(l1, 0.9, "POINT(1 2.6)");
202     test<LS,P>(l1, 1,   "POINT(1 3)");
203 
204     test<LS,MP>(l1, 0, "MULTIPOINT((1 1))");
205     //(1 3) missing due to floating point round off errors
206     test<LS,MP>(l1, 0.1, "MULTIPOINT((1.4 1)(1.8 1)(2 1.2)(2 1.6)(2 2)(1.6 2)\
207                                     (1.2 2)(1 2.2)(1 2.6))");
208     //(1 3) is not missing if you directly pass the distance
209     test_distance<LS,MP>(l1, 0.4, "MULTIPOINT((1.4 1)(1.8 1)(2 1.2)(2 1.6)(2 2)(1.6 2)\
210                                              (1.2 2)(1 2.2)(1 2.6)(1 3))");
211     test<LS,MP>(l1, 0.2, "MULTIPOINT((1.8 1)(2 1.6)(1.6 2)(1 2.2))");//(1 3) missing
212     test<LS,MP>(l1, 0.4, "MULTIPOINT((2 1.6)(1 2.2))");
213     test<LS,MP>(l1, 0.5, "MULTIPOINT((2 2)(1 3))");
214     test<LS,MP>(l1, 0.6, "MULTIPOINT((1.6 2))");
215     test<LS,MP>(l1, 1, "MULTIPOINT((1 3))");
216 }
217 
test_sph()218 void test_sph()
219 {
220     typedef bg::model::point<double, 2, bg::cs::spherical_equatorial<bg::degree> > P;
221     typedef bg::model::multi_point<P> MP;
222     typedef bg::model::segment<P> S;
223     typedef bg::model::linestring<P> LS;
224 
225     test<S,P>(s, 0,   "POINT(1 1)");
226     test<S,P>(s, 0.5, "POINT(1.4998857365615981 1.5000570914791198)");
227     test<S,P>(s, 1,   "POINT(2 2)");
228 
229     test<LS,P>(l1, 0,   "POINT(1 1)");
230     test<LS,P>(l1, 0.1, "POINT(1.39998476912905323 1.0000365473536286)");
231     test<LS,P>(l1, 0.2, "POINT(1.79996953825810646 1.0000243679448551)");
232     test<LS,P>(l1, 0.3, "POINT(2 1.1999238595669637)");
233     test<LS,P>(l1, 0.4, "POINT(2 1.5998477098527744)");
234     test<LS,P>(l1, 0.5, "POINT(2 1.9997715601390484)");
235     test<LS,P>(l1, 0.6, "POINT(1.6000609543036084 2.0000730473928678)");
236     test<LS,P>(l1, 0.7, "POINT(1.1998933176222553 2.0000486811516014)");
237     test<LS,P>(l1, 0.8, "POINT(1 2.2001522994279883)");
238     test<LS,P>(l1, 0.9, "POINT(1 2.6000761497139444)");
239     test<LS,P>(l1, 1,   "POINT(1 3)");
240 
241     test<LS,MP>(l1, 0, "MULTIPOINT((1 1))");
242     test<LS,MP>(l1, 0.1, "MULTIPOINT((1.39998476912905323 1.0000365473536286)\
243                                     (1.79996953825810646 1.0000243679448551)\
244                                     (2 1.1999238595669637)\
245                                     (2 1.5998477098527744)\
246                                     (2 1.9997715601385837)\
247                                     (1.6000609543036084 2.0000730473928678)\
248                                     (1.1998933176222553 2.0000486811516014)\
249                                     (1 2.2001522994279883)\
250                                     (1 2.6000761497139444)\
251                                     )");//(1,3)
252     test<LS,MP>(l1, 0.2, "MULTIPOINT((1.79996953825810646 1.0000243679448551)\
253                                     (2 1.5998477098527744)\
254                                     (1.6000609543036084 2.0000730473928678)\
255                                     (1 2.2001522994279883)\
256                                     )");//(1,3)
257     test<LS,MP>(l1, 0.4, "MULTIPOINT((2 1.5998477098527744)(1 2.2001522994279883))");
258     test<LS,MP>(l1, 0.5, "MULTIPOINT((2 1.9997715601385837)(1 3))");
259     test<LS,MP>(l1, 0.6, "MULTIPOINT((1.6000609543036084 2.0000730473928678))");
260     test<LS,MP>(l1, 1, "MULTIPOINT((1 3))");
261 
262     test<LS,MP>(l2, 0.3, "MULTIPOINT((5.3014893312120446 1.0006787676128222)\
263                                      (11.600850053156366 1.0085030143490989)\
264                                      (17.9002174825842 1.0041514208039872))");
265 
266 }
267 
268 template <typename Strategy>
test_sph(Strategy str)269 void test_sph(Strategy str)
270 {
271     typedef bg::model::point<double, 2, bg::cs::spherical_equatorial<bg::degree> > P;
272     typedef bg::model::segment<P> S;
273 
274     test_distance<S,P>(s, 0,   "POINT(1 1)", str);
275     test_distance<S,P>(s, 0.01, "POINT(1.4051065077123643 1.405268220524982)");
276     test_distance<S,P>(s, 0.01, "POINT(1.0040505023484179 1.0040529633262307)", str);
277     test_distance<S,P>(s, 1,   "POINT(1.4051065077123015 1.405268220524919)", str);
278     test_distance<S,P>(s, 1,   "POINT(2 2)");
279     test_distance<S,P>(s, 10,   "POINT(2 2)");
280 }
281 
282 template <typename Strategy>
test_geo(Strategy str)283 void test_geo(Strategy str)
284 {
285     typedef bg::model::point<double, 2, bg::cs::geographic<bg::degree> > P;
286     typedef bg::model::multi_point<P> MP;
287     typedef bg::model::segment<P> S;
288     typedef bg::model::linestring<P> LS;
289 
290     test<S,P>(s, 0,   "POINT(1 1)", str);
291     test<S,P>(s, 0.5, "POINT(1.4998780900539985 1.5000558288006378)", str);
292     test<S,P>(s, 1,   "POINT(2 2)", str);
293 
294     test<LS,P>(l1, 0,   "POINT(1 1)", str);
295     test<LS,P>(l1, 0.1, "POINT(1.3986445638301882 1.0000367522730751)", str);
296     test<LS,P>(l1, 0.2, "POINT(1.79728912766037641 1.0000247772611039)", str);
297     test<LS,P>(l1, 0.3, "POINT(2 1.1972285554368427)", str);
298     test<LS,P>(l1, 0.4, "POINT(2 1.598498298996567)", str);
299     test<LS,P>(l1, 0.5, "POINT(2 1.9997664696834965)", str);
300     test<LS,P>(l1, 0.6, "POINT(1.6013936980010324 2.0000734568388099)", str);
301     test<LS,P>(l1, 0.7, "POINT(1.2025664628960846 2.0000494983098767)", str);
302     test<LS,P>(l1, 0.8, "POINT(1 2.1974612279909937)", str);
303     test<LS,P>(l1, 0.9, "POINT(1 2.5987263175375022)", str);
304     test<LS,P>(l1, 1,   "POINT(1 3)", str);
305 
306     test<LS,MP>(l1, 0, "MULTIPOINT((1 1))", str);
307 
308     //adnoyer is missing the last point in the following cases
309     // of linestrings due to inaccuracy
310     if (!boost::is_same<Strategy, bg::strategy::line_interpolate::geographic
311                                  <bg::strategy::andoyer> >::value)
312     {
313         test<LS,MP>(l1, 0.1, "MULTIPOINT((1.3986445638301882 1.0000367522730751)\
314                     (1.79728912766037641 1.0000247772582571)\
315                     (2 1.1972285554368427)\
316                     (2 1.598498298996567)\
317                     (2 1.9997664696834965)\
318                     (1.6013936980010324 2.0000734568388099)\
319                     (1.2025664628960846 2.0000495003440779)\
320                     (1 2.1974612279909937)\
321                     (1 2.5987263175375022)\
322                     (1 3))", str);
323 
324         test<LS,MP>(l1, 0.2, "MULTIPOINT((1.79728912766037641 1.0000247772613331)\
325                     (2 1.598498298996567)\
326                     (1.6013936980010324 2.0000734568388099)\
327                     (1 2.1974612279909937)\
328                     (1 3))", str);
329     }
330     test<LS,MP>(l1, 0.4, "MULTIPOINT((2 1.598498298996567)(1 2.1974612279909937))", str);
331     test<LS,MP>(l1, 0.5, "MULTIPOINT((2 1.9997664696834965)(1 3))", str);
332     test<LS,MP>(l1, 0.6, "MULTIPOINT((1.6013936980010324 2.0000734568388099))", str);
333     test<LS,MP>(l1, 1, "MULTIPOINT((1 3))", str);
334 
335     test<LS,MP>(l2, 0.3, "MULTIPOINT((5.306157814 1.0006937303)\
336                                      (11.60351281 1.0085614548123072)\
337                                      (17.90073492 1.004178475142552))", str);
338 }
339 
340 template <typename Strategy>
test_geo_non_standard_spheroid(Strategy str)341 void test_geo_non_standard_spheroid(Strategy str)
342 {
343     typedef bg::model::point<double, 2, bg::cs::geographic<bg::degree> > P;
344     typedef bg::model::segment<P> S;
345 
346     test<S,P>(s, 0,   "POINT(1 1)", str);
347     test<S,P>(s, 0.5, "POINT(1.5127731436886724 1.5129021873759412)", str);
348     test<S,P>(s, 1,   "POINT(2 2)", str);
349 }
350 
test_main(int,char * [])351 int test_main(int, char* [])
352 {
353     test_car();
354     test_car_edge_cases();
355 
356     test_sph();
357     test_sph(bg::strategy::line_interpolate::spherical<>(100));
358 
359     typedef bg::srs::spheroid<double> stype;
360 
361     test_geo(bg::strategy::line_interpolate::geographic<bg::strategy::andoyer>());
362     test_geo(bg::strategy::line_interpolate::geographic<bg::strategy::thomas>());
363     test_geo(bg::strategy::line_interpolate::geographic<bg::strategy::vincenty>());
364 
365     test_geo_non_standard_spheroid(bg::strategy::line_interpolate::geographic
366                                    <bg::strategy::vincenty>(stype(5000000,6000000)));
367 
368     return 0;
369 }
370 
371