]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // Boost.Geometry (aka GGL, Generic Geometry Library) |
2 | // Unit Test | |
3 | ||
4 | // Copyright (c) 2014-2015, Oracle and/or its affiliates. | |
5 | ||
6 | // Contributed and/or modified by Menelaos Karavelas, 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_IS_VALID_HPP | |
12 | #define BOOST_GEOMETRY_TEST_IS_VALID_HPP | |
13 | ||
14 | #include <iostream> | |
15 | #include <sstream> | |
16 | #include <string> | |
17 | ||
18 | #include <boost/core/ignore_unused.hpp> | |
19 | #include <boost/range.hpp> | |
20 | #include <boost/variant/variant.hpp> | |
21 | ||
22 | #include <boost/geometry/core/closure.hpp> | |
23 | #include <boost/geometry/core/exterior_ring.hpp> | |
24 | #include <boost/geometry/core/interior_rings.hpp> | |
25 | #include <boost/geometry/core/point_order.hpp> | |
26 | #include <boost/geometry/core/ring_type.hpp> | |
27 | #include <boost/geometry/core/tag.hpp> | |
28 | #include <boost/geometry/core/tags.hpp> | |
29 | ||
30 | #include <boost/geometry/geometries/point_xy.hpp> | |
31 | #include <boost/geometry/geometries/segment.hpp> | |
32 | #include <boost/geometry/geometries/box.hpp> | |
33 | #include <boost/geometry/geometries/linestring.hpp> | |
34 | #include <boost/geometry/geometries/ring.hpp> | |
35 | #include <boost/geometry/geometries/polygon.hpp> | |
36 | #include <boost/geometry/geometries/multi_point.hpp> | |
37 | #include <boost/geometry/geometries/multi_linestring.hpp> | |
38 | #include <boost/geometry/geometries/multi_polygon.hpp> | |
39 | ||
40 | #include <boost/geometry/strategies/strategies.hpp> | |
41 | ||
42 | #include <boost/geometry/io/wkt/wkt.hpp> | |
43 | ||
44 | #include <boost/geometry/algorithms/convert.hpp> | |
45 | #include <boost/geometry/algorithms/num_points.hpp> | |
46 | #include <boost/geometry/algorithms/is_valid.hpp> | |
47 | ||
48 | #include <boost/geometry/algorithms/detail/check_iterator_range.hpp> | |
49 | ||
50 | #include <from_wkt.hpp> | |
51 | ||
52 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
53 | #include "pretty_print_geometry.hpp" | |
54 | #endif | |
55 | ||
56 | ||
57 | namespace bg = ::boost::geometry; | |
58 | ||
59 | typedef bg::model::point<double, 2, bg::cs::cartesian> point_type; | |
60 | typedef bg::model::segment<point_type> segment_type; | |
61 | typedef bg::model::box<point_type> box_type; | |
62 | typedef bg::model::linestring<point_type> linestring_type; | |
63 | typedef bg::model::multi_linestring<linestring_type> multi_linestring_type; | |
64 | typedef bg::model::multi_point<point_type> multi_point_type; | |
65 | ||
66 | ||
67 | //---------------------------------------------------------------------------- | |
68 | ||
69 | ||
70 | // returns true if a geometry can be converted to closed | |
71 | template | |
72 | < | |
73 | typename Geometry, | |
74 | typename Tag = typename bg::tag<Geometry>::type, | |
75 | bg::closure_selector Closure = bg::closure<Geometry>::value | |
76 | > | |
77 | struct is_convertible_to_closed | |
78 | { | |
79 | static inline bool apply(Geometry const&) | |
80 | { | |
81 | return false; | |
82 | } | |
83 | }; | |
84 | ||
85 | template <typename Ring> | |
86 | struct is_convertible_to_closed<Ring, bg::ring_tag, bg::open> | |
87 | { | |
88 | static inline bool apply(Ring const& ring) | |
89 | { | |
90 | return boost::size(ring) > 0; | |
91 | } | |
92 | }; | |
93 | ||
94 | template <typename Polygon> | |
95 | struct is_convertible_to_closed<Polygon, bg::polygon_tag, bg::open> | |
96 | { | |
97 | typedef typename bg::ring_type<Polygon>::type ring_type; | |
98 | ||
99 | template <typename InteriorRings> | |
100 | static inline | |
101 | bool apply_to_interior_rings(InteriorRings const& interior_rings) | |
102 | { | |
103 | return bg::detail::check_iterator_range | |
104 | < | |
105 | is_convertible_to_closed<ring_type> | |
106 | >::apply(boost::begin(interior_rings), | |
107 | boost::end(interior_rings)); | |
108 | } | |
109 | ||
110 | static inline bool apply(Polygon const& polygon) | |
111 | { | |
112 | return boost::size(bg::exterior_ring(polygon)) > 0 | |
113 | && apply_to_interior_rings(bg::interior_rings(polygon)); | |
114 | } | |
115 | }; | |
116 | ||
117 | template <typename MultiPolygon> | |
118 | struct is_convertible_to_closed<MultiPolygon, bg::multi_polygon_tag, bg::open> | |
119 | { | |
120 | typedef typename boost::range_value<MultiPolygon>::type polygon; | |
121 | ||
122 | static inline bool apply(MultiPolygon const& multi_polygon) | |
123 | { | |
124 | return bg::detail::check_iterator_range | |
125 | < | |
126 | is_convertible_to_closed<polygon>, | |
127 | false // do not allow empty multi-polygon | |
128 | >::apply(boost::begin(multi_polygon), | |
129 | boost::end(multi_polygon)); | |
130 | } | |
131 | }; | |
132 | ||
133 | ||
134 | //---------------------------------------------------------------------------- | |
135 | ||
136 | ||
137 | // returns true if a geometry can be converted to cw | |
138 | template | |
139 | < | |
140 | typename Geometry, | |
141 | typename Tag = typename bg::tag<Geometry>::type, | |
142 | bg::order_selector Order = bg::point_order<Geometry>::value | |
143 | > | |
144 | struct is_convertible_to_cw | |
145 | { | |
146 | static inline bool apply(Geometry const&) | |
147 | { | |
148 | return bg::point_order<Geometry>::value == bg::counterclockwise; | |
149 | } | |
150 | }; | |
151 | ||
152 | ||
153 | //---------------------------------------------------------------------------- | |
154 | ||
155 | ||
156 | // returns true if geometry can be converted to polygon | |
157 | template | |
158 | < | |
159 | typename Geometry, | |
160 | typename Tag = typename bg::tag<Geometry>::type | |
161 | > | |
162 | struct is_convertible_to_polygon | |
163 | { | |
164 | typedef Geometry type; | |
165 | static bool const value = false; | |
166 | }; | |
167 | ||
168 | template <typename Ring> | |
169 | struct is_convertible_to_polygon<Ring, bg::ring_tag> | |
170 | { | |
171 | typedef bg::model::polygon | |
172 | < | |
173 | typename bg::point_type<Ring>::type, | |
174 | bg::point_order<Ring>::value == bg::clockwise, | |
175 | bg::closure<Ring>::value == bg::closed | |
176 | > type; | |
177 | ||
178 | static bool const value = true; | |
179 | }; | |
180 | ||
181 | ||
182 | //---------------------------------------------------------------------------- | |
183 | ||
184 | ||
185 | // returns true if geometry can be converted to multi-polygon | |
186 | template | |
187 | < | |
188 | typename Geometry, | |
189 | typename Tag = typename bg::tag<Geometry>::type | |
190 | > | |
191 | struct is_convertible_to_multipolygon | |
192 | { | |
193 | typedef Geometry type; | |
194 | static bool const value = false; | |
195 | }; | |
196 | ||
197 | template <typename Ring> | |
198 | struct is_convertible_to_multipolygon<Ring, bg::ring_tag> | |
199 | { | |
200 | typedef bg::model::multi_polygon | |
201 | < | |
202 | typename is_convertible_to_polygon<Ring>::type | |
203 | > type; | |
204 | ||
205 | static bool const value = true; | |
206 | }; | |
207 | ||
208 | template <typename Polygon> | |
209 | struct is_convertible_to_multipolygon<Polygon, bg::polygon_tag> | |
210 | { | |
211 | typedef bg::model::multi_polygon<Polygon> type; | |
212 | static bool const value = true; | |
213 | }; | |
214 | ||
215 | ||
216 | //---------------------------------------------------------------------------- | |
217 | ||
218 | ||
219 | template <typename ValidityTester> | |
220 | struct validity_checker | |
221 | { | |
222 | template <typename Geometry> | |
223 | static inline bool apply(std::string const& case_id, | |
224 | Geometry const& geometry, | |
225 | bool expected_result, | |
226 | std::string& reason) | |
227 | { | |
228 | bool valid = ValidityTester::apply(geometry); | |
229 | std::string const reason_valid | |
230 | = bg::validity_failure_type_message(bg::no_failure); | |
231 | reason = ValidityTester::reason(geometry); | |
232 | std::string reason_short = reason.substr(0, reason_valid.length()); | |
233 | ||
234 | BOOST_CHECK_MESSAGE(valid == expected_result, | |
235 | "case id: " << case_id | |
236 | << ", Expected: " << expected_result | |
237 | << ", detected: " << valid | |
238 | << "; wkt: " << bg::wkt(geometry)); | |
239 | ||
240 | BOOST_CHECK_MESSAGE(reason != "", | |
241 | "case id (empty reason): " << case_id | |
242 | << ", Expected: " << valid | |
243 | << ", detected reason: " << reason | |
244 | << "; wkt: " << bg::wkt(geometry)); | |
245 | ||
246 | BOOST_CHECK_MESSAGE((valid && reason == reason_valid) | |
247 | || | |
248 | (! valid && reason != reason_valid) | |
249 | || | |
250 | (! valid && reason_short != reason_valid), | |
251 | "case id (reason): " << case_id | |
252 | << ", Expected: " << valid | |
253 | << ", detected reason: " << reason | |
254 | << "; wkt: " << bg::wkt(geometry)); | |
255 | ||
256 | return valid; | |
257 | } | |
258 | }; | |
259 | ||
260 | ||
261 | //---------------------------------------------------------------------------- | |
262 | ||
263 | ||
264 | struct default_validity_tester | |
265 | { | |
266 | template <typename Geometry> | |
267 | static inline bool apply(Geometry const& geometry) | |
268 | { | |
269 | return bg::is_valid(geometry); | |
270 | } | |
271 | ||
272 | template <typename Geometry> | |
273 | static inline std::string reason(Geometry const& geometry) | |
274 | { | |
275 | std::string message; | |
276 | bg::is_valid(geometry, message); | |
277 | return message; | |
278 | } | |
279 | }; | |
280 | ||
281 | ||
282 | template <bool AllowSpikes> | |
283 | struct validity_tester_linear | |
284 | { | |
285 | template <typename Geometry> | |
286 | static inline bool apply(Geometry const& geometry) | |
287 | { | |
288 | bool const irrelevant = true; | |
289 | bg::is_valid_default_policy<irrelevant, AllowSpikes> visitor; | |
290 | return bg::is_valid(geometry, visitor); | |
291 | } | |
292 | ||
293 | template <typename Geometry> | |
294 | static inline std::string reason(Geometry const& geometry) | |
295 | { | |
296 | bool const irrelevant = true; | |
297 | std::ostringstream oss; | |
298 | bg::failing_reason_policy<irrelevant, AllowSpikes> visitor(oss); | |
299 | bg::is_valid(geometry, visitor); | |
300 | return oss.str(); | |
301 | } | |
302 | }; | |
303 | ||
304 | ||
305 | template <bool AllowDuplicates> | |
306 | struct validity_tester_areal | |
307 | { | |
308 | template <typename Geometry> | |
309 | static inline bool apply(Geometry const& geometry) | |
310 | { | |
311 | bg::is_valid_default_policy<AllowDuplicates> visitor; | |
312 | return bg::is_valid(geometry, visitor); | |
313 | } | |
314 | ||
315 | template <typename Geometry> | |
316 | static inline std::string reason(Geometry const& geometry) | |
317 | { | |
318 | std::ostringstream oss; | |
319 | bg::failing_reason_policy<AllowDuplicates> visitor(oss); | |
320 | bg::is_valid(geometry, visitor); | |
321 | return oss.str(); | |
322 | } | |
323 | ||
324 | }; | |
325 | ||
326 | ||
327 | //---------------------------------------------------------------------------- | |
328 | ||
329 | ||
330 | template | |
331 | < | |
332 | typename ValidityTester, | |
333 | typename Geometry, | |
334 | typename ClosedGeometry = Geometry, | |
335 | typename CWGeometry = Geometry, | |
336 | typename CWClosedGeometry = Geometry, | |
337 | typename Tag = typename bg::tag<Geometry>::type | |
338 | > | |
339 | class test_valid | |
340 | { | |
341 | protected: | |
342 | template <typename G> | |
343 | static inline void base_test(std::string const& case_id, | |
344 | G const& g, | |
345 | bool expected_result) | |
346 | { | |
347 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
348 | std::cout << "=======" << std::endl; | |
349 | #endif | |
350 | ||
351 | std::string reason; | |
352 | bool valid = validity_checker | |
353 | < | |
354 | ValidityTester | |
355 | >::apply(case_id, g, expected_result, reason); | |
356 | boost::ignore_unused(valid); | |
357 | ||
358 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
359 | std::cout << "case id: " << case_id << ", Geometry: "; | |
360 | pretty_print_geometry<G>::apply(std::cout, g); | |
361 | std::cout << std::endl; | |
362 | std::cout << "wkt: " << bg::wkt(g) << std::endl; | |
363 | std::cout << std::boolalpha; | |
364 | std::cout << "is valid? " << valid << std::endl; | |
365 | std::cout << "expected result: " << expected_result << std::endl; | |
366 | std::cout << "reason: " << reason << std::endl; | |
367 | std::cout << "=======" << std::endl; | |
368 | std::cout << std::noboolalpha; | |
369 | #endif | |
370 | } | |
371 | ||
372 | public: | |
373 | static inline void apply(std::string const& case_id, | |
374 | Geometry const& geometry, | |
375 | bool expected_result) | |
376 | { | |
377 | std::stringstream sstr; | |
378 | sstr << case_id << "-original"; | |
379 | base_test(sstr.str(), geometry, expected_result); | |
380 | ||
381 | if ( is_convertible_to_closed<Geometry>::apply(geometry) ) | |
382 | { | |
383 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
384 | std::cout << "...checking closed geometry..." | |
385 | << std::endl; | |
386 | #endif | |
387 | ClosedGeometry closed_geometry; | |
388 | bg::convert(geometry, closed_geometry); | |
389 | sstr.str(""); | |
390 | sstr << case_id << "-2closed"; | |
391 | base_test(sstr.str(), closed_geometry, expected_result); | |
392 | } | |
393 | if ( is_convertible_to_cw<Geometry>::apply(geometry) ) | |
394 | { | |
395 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
396 | std::cout << "...checking cw open geometry..." | |
397 | << std::endl; | |
398 | #endif | |
399 | CWGeometry cw_geometry; | |
400 | bg::convert(geometry, cw_geometry); | |
401 | sstr.str(""); | |
402 | sstr << case_id << "-2CW"; | |
403 | base_test(sstr.str(), cw_geometry, expected_result); | |
404 | if ( is_convertible_to_closed<CWGeometry>::apply(cw_geometry) ) | |
405 | { | |
406 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
407 | std::cout << "...checking cw closed geometry..." | |
408 | << std::endl; | |
409 | #endif | |
410 | CWClosedGeometry cw_closed_geometry; | |
411 | bg::convert(cw_geometry, cw_closed_geometry); | |
412 | sstr.str(""); | |
413 | sstr << case_id << "-2CWclosed"; | |
414 | base_test(sstr.str(), cw_closed_geometry, expected_result); | |
415 | } | |
416 | } | |
417 | ||
418 | if ( BOOST_GEOMETRY_CONDITION(is_convertible_to_polygon<Geometry>::value) ) | |
419 | { | |
420 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
421 | std::cout << "...checking geometry converted to polygon..." | |
422 | << std::endl; | |
423 | #endif | |
424 | typename is_convertible_to_polygon<Geometry>::type polygon; | |
425 | bg::convert(geometry, polygon); | |
426 | sstr.str(""); | |
427 | sstr << case_id << "-2Polygon"; | |
428 | base_test(sstr.str(), polygon, expected_result); | |
429 | } | |
430 | ||
431 | if ( BOOST_GEOMETRY_CONDITION(is_convertible_to_multipolygon<Geometry>::value) ) | |
432 | { | |
433 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
434 | std::cout << "...checking geometry converted to multi-polygon..." | |
435 | << std::endl; | |
436 | #endif | |
437 | typename is_convertible_to_multipolygon | |
438 | < | |
439 | Geometry | |
440 | >::type multipolygon; | |
441 | ||
442 | bg::convert(geometry, multipolygon); | |
443 | sstr.str(""); | |
444 | sstr << case_id << "-2MultiPolygon"; | |
445 | base_test(sstr.str(), multipolygon, expected_result); | |
446 | } | |
447 | ||
448 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
449 | std::cout << std::endl << std::endl << std::endl; | |
450 | #endif | |
451 | } | |
452 | ||
453 | static inline void apply(std::string const& case_id, | |
454 | std::string const& wkt, | |
455 | bool expected_result) | |
456 | { | |
457 | apply(case_id, from_wkt<Geometry>(wkt), expected_result); | |
458 | } | |
459 | }; | |
460 | ||
461 | ||
462 | //---------------------------------------------------------------------------- | |
463 | ||
464 | ||
465 | template <typename VariantGeometry> | |
466 | class test_valid_variant | |
467 | : test_valid<default_validity_tester, VariantGeometry> | |
468 | { | |
469 | private: | |
470 | typedef test_valid<default_validity_tester, VariantGeometry> base_type; | |
471 | ||
472 | public: | |
473 | static inline void apply(std::string const& case_id, | |
474 | VariantGeometry const& vg, | |
475 | bool expected_result) | |
476 | { | |
477 | std::ostringstream oss; | |
478 | base_type::base_test(case_id, vg, expected_result); | |
479 | } | |
480 | }; | |
481 | ||
482 | ||
483 | #endif // BOOST_GEOMETRY_TEST_IS_VALID_HPP |