]>
Commit | Line | Data |
---|---|---|
1 | // Boost.Geometry (aka GGL, Generic Geometry Library) | |
2 | // Unit Test | |
3 | ||
4 | // Copyright (c) 2015-2021, Oracle and/or its affiliates. | |
5 | // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle | |
6 | // Contributed and/or modified by Adam Wulkiewicz, 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_TEST_MODULE | |
12 | #define BOOST_TEST_MODULE test_douglas_peucker | |
13 | #endif | |
14 | ||
15 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
16 | #ifndef BOOST_GEOMETRY_DEBUG_DOUGLAS_PEUCKER | |
17 | #define BOOST_GEOMETRY_DEBUG_DOUGLAS_PEUCKER | |
18 | #endif | |
19 | #endif | |
20 | ||
21 | #include <iostream> | |
22 | #include <iterator> | |
23 | #include <sstream> | |
24 | #include <string> | |
25 | ||
26 | #include <boost/core/ignore_unused.hpp> | |
27 | #include <boost/test/included/unit_test.hpp> | |
28 | #include <boost/tuple/tuple.hpp> | |
29 | ||
30 | #include <boost/geometry/algorithms/comparable_distance.hpp> | |
31 | #include <boost/geometry/algorithms/equals.hpp> | |
32 | #include <boost/geometry/algorithms/simplify.hpp> | |
33 | ||
34 | #include <boost/geometry/core/point_type.hpp> | |
35 | #include <boost/geometry/core/tags.hpp> | |
36 | ||
37 | #include <boost/geometry/io/wkt/wkt.hpp> | |
38 | #include <boost/geometry/io/dsv/write.hpp> | |
39 | ||
40 | #include <boost/geometry/strategies/distance.hpp> | |
41 | #include <boost/geometry/strategies/strategies.hpp> | |
42 | ||
43 | #include <boost/geometry/geometries/geometries.hpp> | |
44 | #include <boost/geometry/geometries/adapted/boost_tuple.hpp> | |
45 | #include <boost/geometry/geometries/register/multi_point.hpp> | |
46 | ||
47 | ||
48 | namespace bg = ::boost::geometry; | |
49 | namespace services = bg::strategy::distance::services; | |
50 | ||
51 | typedef boost::tuple<double, double> tuple_point_type; | |
52 | typedef std::vector<tuple_point_type> tuple_multi_point_type; | |
53 | ||
54 | BOOST_GEOMETRY_REGISTER_BOOST_TUPLE_CS(cs::cartesian) | |
55 | BOOST_GEOMETRY_REGISTER_MULTI_POINT(tuple_multi_point_type) | |
56 | BOOST_GEOMETRY_REGISTER_MULTI_POINT_TEMPLATED(std::vector) | |
57 | ||
58 | template <typename CoordinateType> | |
59 | struct simplify_default_strategy | |
60 | { | |
61 | typedef bg::model::point<CoordinateType, 2, bg::cs::cartesian> point_type; | |
62 | typedef typename bg::strategies::simplify::services::default_strategy | |
63 | < | |
64 | point_type | |
65 | >::type type; | |
66 | }; | |
67 | ||
68 | template <typename CoordinateType> | |
69 | struct simplify_regular_strategy | |
70 | { | |
71 | typedef bg::strategies::simplify::cartesian<> type; | |
72 | }; | |
73 | ||
74 | template <typename CoordinateType> | |
75 | struct simplify_comparable_strategy | |
76 | { | |
77 | typedef bg::strategies::distance::detail::comparable | |
78 | < | |
79 | bg::strategies::simplify::cartesian<> | |
80 | > type; | |
81 | }; | |
82 | ||
83 | ||
84 | ||
85 | template <typename Geometry> | |
86 | inline Geometry from_wkt(std::string const& wkt) | |
87 | { | |
88 | Geometry geometry; | |
89 | bg::read_wkt(wkt, geometry); | |
90 | return geometry; | |
91 | } | |
92 | ||
93 | template <typename Iterator> | |
94 | inline std::ostream& print_point_range(std::ostream& os, | |
95 | Iterator first, | |
96 | Iterator beyond, | |
97 | std::string const& header) | |
98 | { | |
99 | os << header << "("; | |
100 | for (Iterator it = first; it != beyond; ++it) | |
101 | { | |
102 | os << " " << bg::dsv(*it); | |
103 | } | |
104 | os << " )"; | |
105 | return os; | |
106 | } | |
107 | ||
108 | ||
109 | struct equals | |
110 | { | |
111 | template <typename Iterator1, typename Iterator2> | |
112 | static inline bool apply(Iterator1 begin1, Iterator1 end1, | |
113 | Iterator2 begin2, Iterator2 end2) | |
114 | { | |
115 | std::size_t num_points1 = std::distance(begin1, end1); | |
116 | std::size_t num_points2 = std::distance(begin2, end2); | |
117 | ||
118 | if ( num_points1 != num_points2 ) | |
119 | { | |
120 | return false; | |
121 | } | |
122 | ||
123 | Iterator1 it1 = begin1; | |
124 | Iterator2 it2 = begin2; | |
125 | for (; it1 != end1; ++it1, ++it2) | |
126 | { | |
127 | if ( !bg::equals(*it1, *it2) ) | |
128 | { | |
129 | return false; | |
130 | } | |
131 | } | |
132 | return true; | |
133 | } | |
134 | ||
135 | template <typename Range1, typename Range2> | |
136 | static inline bool apply(Range1 const& range1, Range2 const& range2) | |
137 | { | |
138 | return apply(boost::begin(range1), boost::end(range1), | |
139 | boost::begin(range2), boost::end(range2)); | |
140 | } | |
141 | }; | |
142 | ||
143 | ||
144 | ||
145 | ||
146 | template <typename Geometry> | |
147 | struct test_one_case | |
148 | { | |
149 | using point_type = typename bg::point_type<Geometry>::type; | |
150 | ||
151 | template <typename Strategy> | |
152 | static inline void apply(std::string const& case_id, | |
153 | std::string const& wkt, | |
154 | double max_distance, | |
155 | Strategy const& strategy, | |
156 | std::initializer_list<point_type> const& expected_result) | |
157 | { | |
158 | std::vector<point_type> result; | |
159 | ||
160 | Geometry geometry = from_wkt<Geometry>(wkt); | |
161 | ||
162 | std::string typeid_name | |
163 | = typeid(typename bg::coordinate_type<point_type>::type).name(); | |
164 | ||
165 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
166 | std::cout << case_id << " - " << typeid_name | |
167 | << std::endl; | |
168 | std::cout << wkt << std::endl; | |
169 | #endif | |
170 | ||
171 | bg::detail::simplify::douglas_peucker::apply( | |
172 | geometry, std::back_inserter(result), max_distance, strategy); | |
173 | ||
174 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
175 | print_point_range(std::cout, boost::begin(result), boost::end(result), | |
176 | "output: "); | |
177 | std::cout << std::endl << std::endl; | |
178 | #endif | |
179 | std::stringstream stream_expected; | |
180 | print_point_range(stream_expected, boost::begin(expected_result), | |
181 | boost::end(expected_result), | |
182 | ""); | |
183 | std::stringstream stream_detected; | |
184 | print_point_range(stream_detected, boost::begin(result), | |
185 | boost::end(result), | |
186 | ""); | |
187 | ||
188 | BOOST_CHECK_MESSAGE(equals::apply(result, expected_result), | |
189 | "case id: " << case_id << " - " << typeid_name | |
190 | << ", geometry: " << wkt | |
191 | << ", Expected: " << stream_expected.str() | |
192 | << " - Detected: " << stream_detected.str()); | |
193 | ||
194 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
195 | std::cout << "---------------" << std::endl; | |
196 | std::cout << "---------------" << std::endl; | |
197 | std::cout << std::endl << std::endl; | |
198 | #endif | |
199 | } | |
200 | }; | |
201 | ||
202 | ||
203 | template <typename CoordinateType, typename Strategy> | |
204 | inline void test_with_strategy(std::string label) | |
205 | { | |
206 | std::cout.precision(20); | |
207 | Strategy strategy; | |
208 | ||
209 | typedef bg::model::point<CoordinateType, 2, bg::cs::cartesian> point_type; | |
210 | typedef bg::model::linestring<point_type> linestring_type; | |
211 | typedef bg::model::segment<point_type> segment_type; | |
212 | typedef test_one_case<linestring_type> tester; | |
213 | ||
214 | label = " (" + label + ")"; | |
215 | ||
216 | { | |
217 | point_type const p1(-6,-13), p2(0,-15); | |
218 | segment_type const s(point_type(12,-3), point_type(-12,5)); | |
219 | ||
220 | if (bg::comparable_distance(p1, s) >= bg::comparable_distance(p2, s)) | |
221 | { | |
222 | tester::apply("l01c1" + label, | |
223 | "LINESTRING(12 -3, 4 8,-6 -13,-9 4,0 -15,-12 5)", | |
224 | 10, | |
225 | strategy, | |
226 | {{12,-3},{4,8},{-6,-13},{-12,5}} | |
227 | ); | |
228 | } | |
229 | else | |
230 | { | |
231 | tester::apply("l01c2" + label, | |
232 | "LINESTRING(12 -3, 4 8,-6 -13,-9 4,0 -15,-12 5)", | |
233 | 10, | |
234 | strategy, | |
235 | {{12,-3},{4,8},{-6,-13},{-9,4},{0,-15},{-12,5}} | |
236 | ); | |
237 | } | |
238 | } | |
239 | ||
240 | tester::apply("l02" + label, | |
241 | "LINESTRING(-6 -13,-9 4,0 -15,-12 5)", | |
242 | 10, | |
243 | strategy, | |
244 | {{-6,-13},{-12,5}} | |
245 | ); | |
246 | ||
247 | tester::apply("l03" + label, | |
248 | "LINESTRING(12 -3, 4 8,-6 -13,-9 4,0 -14,-12 5)", | |
249 | 10, | |
250 | strategy, | |
251 | {{12,-3},{4,8},{-6,-13},{-12,5}} | |
252 | ); | |
253 | ||
254 | tester::apply("l04" + label, | |
255 | "LINESTRING(12 -3, 4 8,-6 -13,-9 4,0 -14,-12 5)", | |
256 | 14, | |
257 | strategy, | |
258 | {{12,-3},{-6,-13},{-12,5}} | |
259 | ); | |
260 | ||
261 | { | |
262 | segment_type const s(point_type(0,-1), point_type(5,-4)); | |
263 | point_type const p1(5,-1), p2(0,-4); | |
264 | ||
265 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
266 | bool d_larger_first = (bg::distance(p1, s) > bg::distance(p2, s)); | |
267 | bool d_larger_second = (bg::distance(p1, s) < bg::distance(p2, s)); | |
268 | bool cd_larger_first | |
269 | = (bg::comparable_distance(p1, s) > bg::comparable_distance(p2, s)); | |
270 | bool cd_larger_second | |
271 | = (bg::comparable_distance(p1, s) < bg::comparable_distance(p2, s)); | |
272 | ||
273 | std::cout << "segment: " << bg::dsv(s) << std::endl; | |
274 | std::cout << "distance from " << bg::dsv(p1) << ": " | |
275 | << bg::distance(p1, s) << std::endl; | |
276 | std::cout << "comp. distance from " << bg::dsv(p1) << ": " | |
277 | << bg::comparable_distance(p1, s) << std::endl; | |
278 | std::cout << "distance from " << bg::dsv(p2) << ": " | |
279 | << bg::distance(p2, s) << std::endl; | |
280 | std::cout << "comp. distance from " << bg::dsv(p2) << ": " | |
281 | << bg::comparable_distance(p2, s) << std::endl; | |
282 | std::cout << "larger distance from " | |
283 | << (d_larger_first ? "first" : (d_larger_second ? "second" : "equal")) | |
284 | << std::endl; | |
285 | std::cout << "larger comp. distance from " | |
286 | << (cd_larger_first ? "first" : (cd_larger_second ? "second" : "equal")) | |
287 | << std::endl; | |
288 | std::cout << "difference of distances: " | |
289 | << (bg::distance(p1, s) - bg::distance(p2, s)) | |
290 | << std::endl; | |
291 | std::cout << "difference of comp. distances: " | |
292 | << (bg::comparable_distance(p1, s) - bg::comparable_distance(p2, s)) | |
293 | << std::endl; | |
294 | #endif | |
295 | ||
296 | std::string wkt = | |
297 | "LINESTRING(0 0,5 0,0 -1,5 -1,0 -2,5 -2,0 -3,5 -3,0 -4,5 -4,0 0)"; | |
298 | ||
299 | if (bg::comparable_distance(p1, s) >= bg::comparable_distance(p2, s)) | |
300 | { | |
301 | tester::apply("l05c1" + label, | |
302 | wkt, | |
303 | 1, | |
304 | strategy, | |
305 | {{0,0},{5,0},{0,-1},{5,-1},{0,-2},{5,-2},{0,-3},{5,-4},{0,0}} | |
306 | ); | |
307 | tester::apply("l05c1a" + label, | |
308 | wkt, | |
309 | 2, | |
310 | strategy, | |
311 | {{0,0},{5,0},{0,-1},{5,-1},{0,-2},{5,-4},{0,0}} | |
312 | ); | |
313 | } | |
314 | else | |
315 | { | |
316 | tester::apply("l05c2" + label, | |
317 | wkt, | |
318 | 1, | |
319 | strategy, | |
320 | {{0,0},{5,0},{0,-1},{5,-1},{0,-2},{5,-2},{0,-4},{5,-4},{0,0}} | |
321 | ); | |
322 | tester::apply("l05c2a" + label, | |
323 | wkt, | |
324 | 2, | |
325 | strategy, | |
326 | {{0,0},{5,0},{0,-1},{5,-1},{0,-4},{5,-4},{0,0}} | |
327 | ); | |
328 | } | |
329 | } | |
330 | ||
331 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
332 | std::cout << std::endl; | |
333 | std::cout << std::endl; | |
334 | std::cout << "*************************************************"; | |
335 | std::cout << std::endl; | |
336 | std::cout << std::endl; | |
337 | #endif | |
338 | } | |
339 | ||
340 | ||
341 | BOOST_AUTO_TEST_CASE( test_default_strategy ) | |
342 | { | |
343 | test_with_strategy<int, simplify_default_strategy<int>::type>("i"); | |
344 | test_with_strategy<float, simplify_default_strategy<float>::type>("f"); | |
345 | test_with_strategy<double, simplify_default_strategy<double>::type>("d"); | |
346 | test_with_strategy | |
347 | < | |
348 | long double, | |
349 | simplify_default_strategy<long double>::type | |
350 | >("ld"); | |
351 | } | |
352 | ||
353 | BOOST_AUTO_TEST_CASE( test_with_regular_distance_strategy ) | |
354 | { | |
355 | test_with_strategy | |
356 | < | |
357 | int, | |
358 | simplify_regular_strategy<int>::type | |
359 | >("i"); | |
360 | ||
361 | test_with_strategy | |
362 | < | |
363 | float, | |
364 | simplify_regular_strategy<float>::type | |
365 | >("f"); | |
366 | ||
367 | test_with_strategy | |
368 | < | |
369 | double, | |
370 | simplify_regular_strategy<double>::type | |
371 | >("d"); | |
372 | test_with_strategy | |
373 | < | |
374 | long double, | |
375 | simplify_regular_strategy<long double>::type | |
376 | >("ld"); | |
377 | } | |
378 | ||
379 | BOOST_AUTO_TEST_CASE( test_with_comparable_distance_strategy ) | |
380 | { | |
381 | test_with_strategy | |
382 | < | |
383 | int, | |
384 | simplify_comparable_strategy<int>::type | |
385 | >("i"); | |
386 | test_with_strategy | |
387 | < | |
388 | float, | |
389 | simplify_comparable_strategy<float>::type | |
390 | >("f"); | |
391 | test_with_strategy | |
392 | < | |
393 | double, | |
394 | simplify_comparable_strategy<double>::type | |
395 | >("d"); | |
396 | test_with_strategy | |
397 | < | |
398 | long double, | |
399 | simplify_comparable_strategy<long double>::type | |
400 | >("ld"); | |
401 | } |