]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
1e59de90 TL |
8 | // This file was modified by Oracle on 2014-2021. |
9 | // Modifications copyright (c) 2014-2021 Oracle and/or its affiliates. | |
7c673cae FG |
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 | #include <sstream> | |
21 | #include <string> | |
20effc67 | 22 | #include <type_traits> |
7c673cae FG |
23 | |
24 | #include <boost/algorithm/string.hpp> | |
25 | ||
26 | #include <geometry_test_common.hpp> | |
27 | ||
28 | #include <boost/geometry/geometries/geometries.hpp> | |
29 | ||
30 | #include <boost/geometry/algorithms/area.hpp> | |
1e59de90 | 31 | #include <boost/geometry/algorithms/make.hpp> |
7c673cae FG |
32 | #include <boost/geometry/algorithms/length.hpp> |
33 | #include <boost/geometry/algorithms/num_points.hpp> | |
34 | #include <boost/geometry/algorithms/perimeter.hpp> | |
35 | #include <boost/geometry/strategies/strategies.hpp> | |
36 | #include <boost/geometry/core/point_type.hpp> | |
37 | #include <boost/geometry/core/topological_dimension.hpp> | |
38 | #include <boost/geometry/io/wkt/read.hpp> | |
39 | #include <boost/geometry/io/wkt/write.hpp> | |
40 | #include <boost/variant/variant.hpp> | |
41 | ||
42 | template <typename G> | |
43 | void check_wkt(G const& geometry, std::string const& expected) | |
44 | { | |
45 | std::ostringstream out; | |
46 | out << bg::wkt(geometry); | |
47 | BOOST_CHECK_EQUAL(boost::to_upper_copy(out.str()), | |
48 | boost::to_upper_copy(expected)); | |
49 | } | |
50 | ||
51 | template <typename G> | |
1e59de90 TL |
52 | void check_to_wkt(G const& geometry, std::string const& expected) |
53 | { | |
54 | std::string out_string; | |
55 | out_string = bg::to_wkt(geometry); | |
56 | BOOST_CHECK_EQUAL(boost::to_upper_copy(out_string), | |
57 | boost::to_upper_copy(expected)); | |
58 | } | |
59 | ||
60 | template <typename G> | |
61 | void check_precise_to_wkt(G const& geometry, std::string const& expected, | |
62 | int significant_digits) | |
63 | { | |
64 | std::string out_string; | |
65 | out_string = bg::to_wkt(geometry, significant_digits); | |
66 | BOOST_CHECK_EQUAL(boost::to_upper_copy(out_string), | |
67 | boost::to_upper_copy(expected)); | |
68 | } | |
69 | ||
70 | template <typename G> | |
71 | void test_wkt_read_write(std::string const& wkt, std::string const& expected, | |
7c673cae FG |
72 | std::size_t n, double len = 0, double ar = 0, double peri = 0) |
73 | { | |
74 | G geometry; | |
75 | ||
76 | bg::read_wkt(wkt, geometry); | |
77 | ||
1e59de90 | 78 | #ifdef BOOST_GEOMETRY_TEST_DEBUG |
7c673cae FG |
79 | std::cout << "n=" << bg::num_points(geometry) |
80 | << " dim=" << bg::topological_dimension<G>::value | |
81 | << " length=" << bg::length(geometry) | |
82 | << " area=" << bg::area(geometry) | |
83 | << " perimeter=" << bg::perimeter(geometry) | |
84 | << std::endl << "\t\tgeometry=" << dsv(geometry) | |
85 | << std::endl; | |
1e59de90 | 86 | #endif |
7c673cae FG |
87 | |
88 | BOOST_CHECK_EQUAL(bg::num_points(geometry), n); | |
89 | if (n > 0) | |
90 | { | |
91 | BOOST_CHECK_CLOSE(double(bg::length(geometry)), len, 0.0001); | |
92 | BOOST_CHECK_CLOSE(double(bg::area(geometry)), ar, 0.0001); | |
93 | BOOST_CHECK_CLOSE(double(bg::perimeter(geometry)), peri, 0.0001); | |
94 | } | |
95 | ||
96 | check_wkt(geometry, expected); | |
1e59de90 TL |
97 | |
98 | boost::variant<G> v; | |
99 | bg::read_wkt(wkt, v); | |
100 | check_wkt(v, expected); | |
101 | ||
102 | bg::model::geometry_collection<boost::variant<G>> gc1{v}; | |
103 | bg::read_wkt(std::string("GEOMETRYCOLLECTION(") + wkt + ')', gc1); | |
104 | check_wkt(gc1, std::string("GEOMETRYCOLLECTION(") + expected + ')'); | |
105 | ||
106 | bg::model::geometry_collection<boost::variant<G>> gc2{v, v}; | |
107 | bg::read_wkt(std::string("GEOMETRYCOLLECTION(") + wkt + ',' + wkt + ')', gc2); | |
108 | check_wkt(gc2, std::string("GEOMETRYCOLLECTION(") + expected + ',' + expected + ')'); | |
109 | } | |
110 | ||
111 | template <typename G> | |
112 | void test_wkt_to_from(std::string const& wkt, std::string const& expected, | |
113 | std::size_t n, double len = 0, double ar = 0, double peri = 0) | |
114 | { | |
115 | G geometry; | |
116 | ||
117 | geometry = bg::from_wkt<G>(wkt); | |
118 | ||
119 | #ifdef BOOST_GEOMETRY_TEST_DEBUG | |
120 | std::cout << "n=" << bg::num_points(geometry) | |
121 | << " dim=" << bg::topological_dimension<G>::value | |
122 | << " length=" << bg::length(geometry) | |
123 | << " area=" << bg::area(geometry) | |
124 | << " perimeter=" << bg::perimeter(geometry) | |
125 | << std::endl << "\t\tgeometry=" << dsv(geometry) | |
126 | << std::endl; | |
127 | #endif | |
128 | ||
129 | BOOST_CHECK_EQUAL(bg::num_points(geometry), n); | |
130 | if (n > 0) | |
131 | { | |
132 | BOOST_CHECK_CLOSE(double(bg::length(geometry)), len, 0.0001); | |
133 | BOOST_CHECK_CLOSE(double(bg::area(geometry)), ar, 0.0001); | |
134 | BOOST_CHECK_CLOSE(double(bg::perimeter(geometry)), peri, 0.0001); | |
135 | } | |
136 | ||
137 | check_to_wkt(geometry, expected); | |
138 | check_to_wkt(boost::variant<G>(geometry), expected); | |
139 | } | |
140 | ||
141 | template <typename G> | |
142 | void test_wkt(std::string const& wkt, std::string const& expected, | |
143 | std::size_t n, double len = 0, double ar = 0, double peri = 0) | |
144 | { | |
145 | test_wkt_read_write<G>(wkt, expected, n, len, ar, peri); | |
146 | test_wkt_to_from<G>(wkt, expected, n, len, ar, peri); | |
7c673cae FG |
147 | } |
148 | ||
149 | template <typename G> | |
150 | void test_wkt(std::string const& wkt, | |
151 | std::size_t n, double len = 0, double ar = 0, double peri = 0) | |
152 | { | |
153 | test_wkt<G>(wkt, wkt, n, len, ar, peri); | |
154 | } | |
155 | ||
156 | template <typename G> | |
1e59de90 | 157 | void test_relaxed_wkt_read_write(std::string const& wkt, std::string const& expected) |
7c673cae FG |
158 | { |
159 | std::string e; | |
160 | G geometry; | |
161 | bg::read_wkt(wkt, geometry); | |
162 | std::ostringstream out; | |
163 | out << bg::wkt(geometry); | |
164 | ||
165 | BOOST_CHECK_EQUAL(boost::to_upper_copy(out.str()), boost::to_upper_copy(expected)); | |
166 | } | |
167 | ||
1e59de90 TL |
168 | template <typename G> |
169 | void test_relaxed_wkt_to_from(std::string const& wkt, std::string const& expected) | |
170 | { | |
171 | std::string e; | |
172 | G geometry; | |
173 | geometry = bg::from_wkt<G>(wkt); | |
174 | std::string out; | |
175 | out = bg::to_wkt(geometry); | |
7c673cae | 176 | |
1e59de90 TL |
177 | BOOST_CHECK_EQUAL(boost::to_upper_copy(out), boost::to_upper_copy(expected)); |
178 | } | |
7c673cae | 179 | |
1e59de90 TL |
180 | template <typename G> |
181 | void test_relaxed_wkt(std::string const& wkt, std::string const& expected) | |
182 | { | |
183 | test_relaxed_wkt_read_write<G>(wkt, expected); | |
184 | test_relaxed_wkt_to_from<G>(wkt, expected); | |
185 | } | |
7c673cae FG |
186 | |
187 | template <typename G> | |
1e59de90 | 188 | void test_wrong_wkt_read_write(std::string const& wkt, std::string const& start) |
7c673cae FG |
189 | { |
190 | std::string e("no exception"); | |
191 | G geometry; | |
192 | try | |
193 | { | |
1e59de90 TL |
194 | bg::read_wkt<G>(wkt, geometry); |
195 | } | |
196 | catch(bg::read_wkt_exception const& ex) | |
197 | { | |
198 | e = ex.what(); | |
199 | boost::to_lower(e); | |
200 | } | |
201 | catch(...) | |
202 | { | |
203 | e = "other exception"; | |
204 | } | |
205 | ||
206 | bool check = true; | |
207 | ||
208 | #if defined(HAVE_TTMATH) | |
209 | // For ttmath we skip bad lexical casts | |
210 | typedef typename bg::coordinate_type<G>::type ct; | |
211 | ||
212 | if (boost::is_same<ct, ttmath_big>::type::value | |
213 | && boost::starts_with(start, "bad lexical cast")) | |
214 | { | |
215 | check = false; | |
216 | } | |
217 | #endif | |
218 | ||
219 | if (check) | |
220 | { | |
221 | BOOST_CHECK_MESSAGE(boost::starts_with(e, start), " Expected:" | |
222 | << start << " Got:" << e << " with WKT: " << wkt); | |
223 | } | |
224 | } | |
225 | ||
226 | template <typename G> | |
227 | void test_wrong_wkt_to_from(std::string const& wkt, std::string const& start) | |
228 | { | |
229 | std::string e("no exception"); | |
230 | G geometry; | |
231 | try | |
232 | { | |
233 | geometry = bg::from_wkt<G>(wkt); | |
7c673cae FG |
234 | } |
235 | catch(bg::read_wkt_exception const& ex) | |
236 | { | |
237 | e = ex.what(); | |
238 | boost::to_lower(e); | |
239 | } | |
240 | catch(...) | |
241 | { | |
242 | e = "other exception"; | |
243 | } | |
244 | ||
20effc67 | 245 | BOOST_CHECK_MESSAGE(boost::starts_with(e, start), " Expected:" |
7c673cae | 246 | << start << " Got:" << e << " with WKT: " << wkt); |
7c673cae FG |
247 | } |
248 | ||
1e59de90 TL |
249 | template <typename G> |
250 | void test_wrong_wkt(std::string const& wkt, std::string const& start) | |
251 | { | |
252 | test_wrong_wkt_read_write<G>(wkt, start); | |
253 | test_wrong_wkt_to_from<G>(wkt, start); | |
254 | } | |
255 | ||
7c673cae FG |
256 | template <typename G> |
257 | void test_wkt_output_iterator(std::string const& wkt) | |
258 | { | |
259 | G geometry; | |
260 | bg::read_wkt<G>(wkt, std::back_inserter(geometry)); | |
261 | } | |
262 | ||
1e59de90 TL |
263 | void test_precise_to_wkt() |
264 | { | |
265 | typedef boost::geometry::model::d2::point_xy<double> point_type; | |
266 | point_type point = boost::geometry::make<point_type>(1.2345, 6.7890); | |
267 | boost::geometry::model::polygon<point_type> polygon; | |
268 | boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make<point_type>(0.00000, 0.00000)); | |
269 | boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make<point_type>(0.00000, 4.00001)); | |
270 | boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make<point_type>(4.00001, 4.00001)); | |
271 | boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make<point_type>(4.00001, 0.00000)); | |
272 | boost::geometry::append(boost::geometry::exterior_ring(polygon), boost::geometry::make<point_type>(0.00000, 0.00000)); | |
273 | check_precise_to_wkt(point,"POINT(1.23 6.79)",3); | |
274 | check_precise_to_wkt(polygon,"POLYGON((0 0,0 4,4 4,4 0,0 0))",3); | |
275 | } | |
7c673cae FG |
276 | |
277 | #ifndef GEOMETRY_TEST_MULTI | |
278 | template <typename T> | |
279 | void test_order_closure() | |
280 | { | |
281 | using namespace boost::geometry; | |
282 | typedef bg::model::point<T, 2, bg::cs::cartesian> Pt; | |
283 | typedef bg::model::polygon<Pt, true, true> PCWC; | |
284 | typedef bg::model::polygon<Pt, true, false> PCWO; | |
285 | typedef bg::model::polygon<Pt, false, true> PCCWC; | |
286 | typedef bg::model::polygon<Pt, false, false> PCCWO; | |
287 | ||
288 | { | |
289 | std::string wkt_cwc = "POLYGON((0 0,0 2,2 2,2 0,0 0))"; | |
290 | std::string wkt_cwo = "POLYGON((0 0,0 2,2 2,2 0))"; | |
291 | std::string wkt_ccwc = "POLYGON((0 0,2 0,2 2,0 2,0 0))"; | |
292 | std::string wkt_ccwo = "POLYGON((0 0,2 0,2 2,0 2))"; | |
293 | ||
294 | test_wkt<PCWC>(wkt_cwc, 5, 0, 4, 8); | |
295 | test_wkt<PCWO>(wkt_cwc, 4, 0, 4, 8); | |
296 | test_wkt<PCWO>(wkt_cwo, wkt_cwc, 4, 0, 4, 8); | |
297 | test_wkt<PCCWC>(wkt_ccwc, 5, 0, 4, 8); | |
298 | test_wkt<PCCWO>(wkt_ccwc, 4, 0, 4, 8); | |
299 | test_wkt<PCCWO>(wkt_ccwo, wkt_ccwc, 4, 0, 4, 8); | |
300 | } | |
301 | { | |
302 | std::string wkt_cwc = "POLYGON((0 0,0 3,3 3,3 0,0 0),(1 1,2 1,2 2,1 2,1 1))"; | |
303 | std::string wkt_cwo = "POLYGON((0 0,0 3,3 3,3 0),(1 1,2 1,2 2,1 2))"; | |
304 | std::string wkt_ccwc = "POLYGON((0 0,3 0,3 3,0 3,0 0),(1 1,1 2,2 2,2 1,1 1))"; | |
305 | std::string wkt_ccwo = "POLYGON((0 0,3 0,3 3,0 3),(1 1,1 2,2 2,2 1,1 1))"; | |
306 | ||
307 | test_wkt<PCWC>(wkt_cwc, 10, 0, 8, 16); | |
308 | test_wkt<PCWO>(wkt_cwc, 8, 0, 8, 16); | |
309 | test_wkt<PCWO>(wkt_cwo, wkt_cwc, 8, 0, 8, 16); | |
310 | test_wkt<PCCWC>(wkt_ccwc, 10, 0, 8, 16); | |
311 | test_wkt<PCCWO>(wkt_ccwc, 8, 0, 8, 16); | |
312 | test_wkt<PCCWO>(wkt_ccwo, wkt_ccwc, 8, 0, 8, 16); | |
313 | } | |
314 | } | |
315 | ||
316 | template <typename T> | |
317 | void test_all() | |
318 | { | |
319 | using namespace boost::geometry; | |
320 | typedef bg::model::point<T, 2, bg::cs::cartesian> P; | |
321 | ||
322 | test_wkt<P>("POINT(1 2)", 1); | |
323 | test_wkt<bg::model::linestring<P> >("LINESTRING(1 1,2 2,3 3)", 3, 2 * sqrt(2.0)); | |
324 | test_wkt<bg::model::polygon<P> >("POLYGON((0 0,0 4,4 4,4 0,0 0)" | |
325 | ",(1 1,1 2,2 2,2 1,1 1),(1 1,1 2,2 2,2 1,1 1))", 15, 0, 18, 24); | |
326 | ||
327 | // Non OGC: a box defined by a polygon | |
328 | //test_wkt<box<P> >("POLYGON((0 0,0 1,1 1,1 0,0 0))", 4, 0, 1, 4); | |
329 | test_wkt<bg::model::ring<P> >("POLYGON((0 0,0 1,1 1,1 0,0 0))", 5, 0, 1, 4); | |
330 | ||
331 | // We accept empty sequences as well (much better than EMPTY)... | |
332 | // ...or even POINT() (see below) | |
333 | test_wkt<bg::model::linestring<P> >("LINESTRING()", 0, 0); | |
334 | test_wkt<bg::model::polygon<P> >("POLYGON(())", 0); | |
335 | // ... or even with empty holes | |
336 | test_wkt<bg::model::polygon<P> >("POLYGON((),(),())", 0); | |
337 | // which all make no valid geometries, but they can exist. | |
338 | ||
1e59de90 TL |
339 | |
340 | ||
7c673cae FG |
341 | // These WKT's are incomplete or abnormal but they are considered OK |
342 | test_relaxed_wkt<P>("POINT(1)", "POINT(1 0)"); | |
343 | test_relaxed_wkt<P>("POINT()", "POINT(0 0)"); | |
344 | test_relaxed_wkt<bg::model::linestring<P> >("LINESTRING(1,2,3)", | |
345 | "LINESTRING(1 0,2 0,3 0)"); | |
346 | test_relaxed_wkt<P>("POINT ( 1 2) ", "POINT(1 2)"); | |
347 | test_relaxed_wkt<P>("POINT M ( 1 2)", "POINT(1 2)"); | |
348 | test_relaxed_wkt<bg::model::box<P> >("BOX(1 1,2 2)", "POLYGON((1 1,1 2,2 2,2 1,1 1))"); | |
349 | ||
350 | test_relaxed_wkt<bg::model::linestring<P> >("LINESTRING EMPTY", "LINESTRING()"); | |
351 | ||
352 | test_relaxed_wkt<bg::model::polygon<P> >("POLYGON( ( ) , ( ) , ( ) )", | |
353 | "POLYGON((),(),())"); | |
354 | ||
355 | // Wrong WKT's | |
356 | test_wrong_wkt<P>("POINT(1 2", "expected ')'"); | |
357 | test_wrong_wkt<P>("POINT 1 2)", "expected '('"); | |
358 | test_wrong_wkt<P>("POINT(1 2,)", "expected ')'"); | |
b32b8144 | 359 | test_wrong_wkt<P>("POINT(1 2)foo", "too many tokens at 'foo'"); |
7c673cae FG |
360 | test_wrong_wkt<P>("POINT(1 2 3)", "expected ')'"); |
361 | test_wrong_wkt<P>("POINT(a 2 3)", "bad lexical cast"); | |
362 | test_wrong_wkt<P>("POINT 2 3", "expected '('"); | |
363 | test_wrong_wkt<P>("POINT Z (1 2 3)", "z only allowed"); | |
364 | ||
365 | test_wrong_wkt<P>("PIONT (1 2)", "should start with 'point'"); | |
366 | ||
b32b8144 | 367 | test_wrong_wkt<bg::model::linestring<P> >("LINESTRING())", "too many tokens"); |
7c673cae FG |
368 | |
369 | test_wrong_wkt<bg::model::polygon<P> >("POLYGON((1 1,1 4,4 4,4 1,1 1)" | |
370 | ",((2 2,2 3,3 3,3 2,2 2))", "bad lexical cast"); | |
371 | ||
372 | test_wrong_wkt<bg::model::box<P> >("BOX(1 1,2 2,3 3)", "box should have 2"); | |
b32b8144 | 373 | test_wrong_wkt<bg::model::box<P> >("BOX(1 1,2 2) )", "too many tokens"); |
7c673cae | 374 | |
20effc67 TL |
375 | if ( BOOST_GEOMETRY_CONDITION(std::is_floating_point<T>::value |
376 | || ! std::is_fundamental<T>::value ) ) | |
7c673cae FG |
377 | { |
378 | test_wkt<P>("POINT(1.1 2.1)", 1); | |
379 | } | |
380 | ||
381 | // Deprecated: | |
382 | // test_wkt_output_iterator<bg::model::linestring<P> >("LINESTRING(1 1,2 2,3 3)"); | |
383 | // test_wkt_output_iterator<bg::model::ring<P> >("POLYGON((1 1,2 2,3 3))"); | |
384 | ||
385 | test_order_closure<T>(); | |
386 | } | |
387 | #endif | |
388 | ||
389 | int test_main(int, char* []) | |
390 | { | |
391 | test_all<double>(); | |
392 | test_all<int>(); | |
1e59de90 TL |
393 | test_precise_to_wkt(); |
394 | ||
395 | #if defined(HAVE_TTMATH) | |
396 | test_all<ttmath_big>(); | |
397 | #endif | |
7c673cae | 398 | |
7c673cae FG |
399 | return 0; |
400 | } | |
401 | ||
402 | /* | |
403 | ||
404 | Results can be checked in PostGIS by query below, | |
405 | or by MySQL (but replace length by glength and remove the perimeter) | |
406 | ||
407 | Note: | |
408 | - PostGIS gives "3" for a numpoints of a multi-linestring of 6 points in total (!) | |
409 | --> "npoints" should be taken for all geometries | |
410 | - SQL Server 2008 gives "6" | |
411 | select geometry::STGeomFromText('MULTILINESTRING((1 1,2 2,3 3),(4 4,5 5,6 6))',0).STNumPoints() | |
412 | - MySQL gives "NULL" | |
413 | ||
414 | select 1 as code,'np p' as header,npoints(geomfromtext('POINT(1 2)')) as contents | |
415 | union select 2,'length point', length(geomfromtext('POINT(1 2)')) | |
416 | union select 3,'peri point', perimeter(geomfromtext('POINT(1 2)')) | |
417 | union select 4,'area point',area(geomfromtext('POINT(1 2)')) | |
418 | ||
419 | ||
420 | union select 5,'# ls',npoints(geomfromtext('LINESTRING(1 1,2 2,3 3)')) | |
421 | union select 6,'length ls',length(geomfromtext('LINESTRING(1 1,2 2,3 3)')) | |
422 | union select 7,'peri ls',perimeter(geomfromtext('LINESTRING(1 1,2 2,3 3)')) | |
423 | union select 8,'aera ls',area(geomfromtext('LINESTRING(1 1,2 2,3 3)')) | |
424 | ||
425 | union select 9,'# poly',npoints(geomfromtext('POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 2,2 2,2 1,1 1),(1 1,1 2,2 2,2 1,1 1))')) | |
426 | union select 10,'length poly',length(geomfromtext('POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 2,2 2,2 1,1 1),(1 1,1 2,2 2,2 1,1 1))')) | |
427 | union select 11,'peri poly',perimeter(geomfromtext('POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 2,2 2,2 1,1 1),(1 1,1 2,2 2,2 1,1 1))')) | |
428 | union select 12,'area poly',area(geomfromtext('POLYGON((0 0,0 4,4 4,4 0,0 0),(1 1,1 2,2 2,2 1,1 1),(1 1,1 2,2 2,2 1,1 1))')) | |
429 | ||
430 | */ |