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.
9 // Modifications copyright (c) 2014 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 #include <sstream>
21 #include <string>
22 
23 #include <boost/algorithm/string.hpp>
24 
25 #include <geometry_test_common.hpp>
26 
27 #include <boost/geometry/geometries/geometries.hpp>
28 
29 #include <boost/geometry/algorithms/area.hpp>
30 #include <boost/geometry/algorithms/length.hpp>
31 #include <boost/geometry/algorithms/num_points.hpp>
32 #include <boost/geometry/algorithms/perimeter.hpp>
33 #include <boost/geometry/strategies/strategies.hpp>
34 #include <boost/geometry/core/point_type.hpp>
35 #include <boost/geometry/core/topological_dimension.hpp>
36 #include <boost/geometry/io/wkt/read.hpp>
37 #include <boost/geometry/io/wkt/write.hpp>
38 #include <boost/variant/variant.hpp>
39 
40 template <typename G>
check_wkt(G const & geometry,std::string const & expected)41 void check_wkt(G const& geometry, std::string const& expected)
42 {
43     std::ostringstream out;
44     out << bg::wkt(geometry);
45     BOOST_CHECK_EQUAL(boost::to_upper_copy(out.str()),
46                       boost::to_upper_copy(expected));
47 }
48 
49 template <typename G>
test_wkt(std::string const & wkt,std::string const & expected,std::size_t n,double len=0,double ar=0,double peri=0)50 void test_wkt(std::string const& wkt, std::string const& expected,
51               std::size_t n, double len = 0, double ar = 0, double peri = 0)
52 {
53     G geometry;
54 
55     bg::read_wkt(wkt, geometry);
56 
57     /*
58     std::cout << "n=" << bg::num_points(geometry)
59         << " dim=" << bg::topological_dimension<G>::value
60         << " length=" << bg::length(geometry)
61         << " area=" << bg::area(geometry)
62         << " perimeter=" << bg::perimeter(geometry)
63         << std::endl << "\t\tgeometry=" << dsv(geometry)
64         << std::endl;
65     */
66 
67     BOOST_CHECK_EQUAL(bg::num_points(geometry), n);
68     if (n > 0)
69     {
70         BOOST_CHECK_CLOSE(double(bg::length(geometry)), len, 0.0001);
71         BOOST_CHECK_CLOSE(double(bg::area(geometry)), ar, 0.0001);
72         BOOST_CHECK_CLOSE(double(bg::perimeter(geometry)), peri, 0.0001);
73     }
74 
75     check_wkt(geometry, expected);
76     check_wkt(boost::variant<G>(geometry), expected);
77 }
78 
79 template <typename G>
test_wkt(std::string const & wkt,std::size_t n,double len=0,double ar=0,double peri=0)80 void test_wkt(std::string const& wkt,
81               std::size_t n, double len = 0, double ar = 0, double peri = 0)
82 {
83     test_wkt<G>(wkt, wkt, n, len, ar, peri);
84 }
85 
86 template <typename G>
test_relaxed_wkt(std::string const & wkt,std::string const & expected)87 void test_relaxed_wkt(std::string const& wkt, std::string const& expected)
88 {
89     std::string e;
90     G geometry;
91     bg::read_wkt(wkt, geometry);
92     std::ostringstream out;
93     out << bg::wkt(geometry);
94 
95     BOOST_CHECK_EQUAL(boost::to_upper_copy(out.str()), boost::to_upper_copy(expected));
96 }
97 
98 
99 
100 
101 template <typename G>
test_wrong_wkt(std::string const & wkt,std::string const & start)102 void test_wrong_wkt(std::string const& wkt, std::string const& start)
103 {
104     std::string e("no exception");
105     G geometry;
106     try
107     {
108         bg::read_wkt(wkt, geometry);
109     }
110     catch(bg::read_wkt_exception const& ex)
111     {
112         e = ex.what();
113         boost::to_lower(e);
114     }
115     catch(...)
116     {
117         e = "other exception";
118     }
119 
120     bool check = true;
121 
122 #if defined(HAVE_TTMATH)
123     // For ttmath we skip bad lexical casts
124     typedef typename bg::coordinate_type<G>::type ct;
125 
126     if (boost::is_same<ct, ttmath_big>::type::value
127         && boost::starts_with(start, "bad lexical cast"))
128     {
129         check = false;
130     }
131 #endif
132 
133     if (check)
134     {
135         BOOST_CHECK_MESSAGE(boost::starts_with(e, start), "  Expected:"
136                     << start << " Got:" << e << " with WKT: " << wkt);
137     }
138 }
139 
140 template <typename G>
test_wkt_output_iterator(std::string const & wkt)141 void test_wkt_output_iterator(std::string const& wkt)
142 {
143     G geometry;
144     bg::read_wkt<G>(wkt, std::back_inserter(geometry));
145 }
146 
147 
148 
149 #ifndef GEOMETRY_TEST_MULTI
150 template <typename T>
test_order_closure()151 void test_order_closure()
152 {
153     using namespace boost::geometry;
154     typedef bg::model::point<T, 2, bg::cs::cartesian> Pt;
155     typedef bg::model::polygon<Pt, true, true> PCWC;
156     typedef bg::model::polygon<Pt, true, false> PCWO;
157     typedef bg::model::polygon<Pt, false, true> PCCWC;
158     typedef bg::model::polygon<Pt, false, false> PCCWO;
159 
160     {
161         std::string wkt_cwc = "POLYGON((0 0,0 2,2 2,2 0,0 0))";
162         std::string wkt_cwo = "POLYGON((0 0,0 2,2 2,2 0))";
163         std::string wkt_ccwc = "POLYGON((0 0,2 0,2 2,0 2,0 0))";
164         std::string wkt_ccwo = "POLYGON((0 0,2 0,2 2,0 2))";
165 
166         test_wkt<PCWC>(wkt_cwc, 5, 0, 4, 8);
167         test_wkt<PCWO>(wkt_cwc, 4, 0, 4, 8);
168         test_wkt<PCWO>(wkt_cwo, wkt_cwc, 4, 0, 4, 8);
169         test_wkt<PCCWC>(wkt_ccwc, 5, 0, 4, 8);
170         test_wkt<PCCWO>(wkt_ccwc, 4, 0, 4, 8);
171         test_wkt<PCCWO>(wkt_ccwo, wkt_ccwc, 4, 0, 4, 8);
172     }
173     {
174         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))";
175         std::string wkt_cwo = "POLYGON((0 0,0 3,3 3,3 0),(1 1,2 1,2 2,1 2))";
176         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))";
177         std::string wkt_ccwo = "POLYGON((0 0,3 0,3 3,0 3),(1 1,1 2,2 2,2 1,1 1))";
178 
179         test_wkt<PCWC>(wkt_cwc, 10, 0, 8, 16);
180         test_wkt<PCWO>(wkt_cwc, 8, 0, 8, 16);
181         test_wkt<PCWO>(wkt_cwo, wkt_cwc, 8, 0, 8, 16);
182         test_wkt<PCCWC>(wkt_ccwc, 10, 0, 8, 16);
183         test_wkt<PCCWO>(wkt_ccwc, 8, 0, 8, 16);
184         test_wkt<PCCWO>(wkt_ccwo, wkt_ccwc, 8, 0, 8, 16);
185     }
186 }
187 
188 template <typename T>
test_all()189 void test_all()
190 {
191     using namespace boost::geometry;
192     typedef bg::model::point<T, 2, bg::cs::cartesian> P;
193 
194     test_wkt<P>("POINT(1 2)", 1);
195     test_wkt<bg::model::linestring<P> >("LINESTRING(1 1,2 2,3 3)", 3, 2 * sqrt(2.0));
196     test_wkt<bg::model::polygon<P> >("POLYGON((0 0,0 4,4 4,4 0,0 0)"
197             ",(1 1,1 2,2 2,2 1,1 1),(1 1,1 2,2 2,2 1,1 1))", 15, 0, 18, 24);
198 
199     // Non OGC: a box defined by a polygon
200     //test_wkt<box<P> >("POLYGON((0 0,0 1,1 1,1 0,0 0))", 4, 0, 1, 4);
201     test_wkt<bg::model::ring<P> >("POLYGON((0 0,0 1,1 1,1 0,0 0))", 5, 0, 1, 4);
202 
203     // We accept empty sequences as well (much better than EMPTY)...
204     // ...or even POINT() (see below)
205     test_wkt<bg::model::linestring<P> >("LINESTRING()", 0, 0);
206     test_wkt<bg::model::polygon<P> >("POLYGON(())", 0);
207     // ... or even with empty holes
208     test_wkt<bg::model::polygon<P> >("POLYGON((),(),())", 0);
209     // which all make no valid geometries, but they can exist.
210 
211     // These WKT's are incomplete or abnormal but they are considered OK
212     test_relaxed_wkt<P>("POINT(1)", "POINT(1 0)");
213     test_relaxed_wkt<P>("POINT()", "POINT(0 0)");
214     test_relaxed_wkt<bg::model::linestring<P> >("LINESTRING(1,2,3)",
215                 "LINESTRING(1 0,2 0,3 0)");
216     test_relaxed_wkt<P>("POINT  ( 1 2)   ", "POINT(1 2)");
217     test_relaxed_wkt<P>("POINT  M ( 1 2)", "POINT(1 2)");
218     test_relaxed_wkt<bg::model::box<P> >("BOX(1 1,2 2)", "POLYGON((1 1,1 2,2 2,2 1,1 1))");
219 
220     test_relaxed_wkt<bg::model::linestring<P> >("LINESTRING EMPTY", "LINESTRING()");
221 
222     test_relaxed_wkt<bg::model::polygon<P> >("POLYGON( ( ) , ( ) , ( ) )",
223                 "POLYGON((),(),())");
224 
225     // Wrong WKT's
226     test_wrong_wkt<P>("POINT(1 2", "expected ')'");
227     test_wrong_wkt<P>("POINT 1 2)", "expected '('");
228     test_wrong_wkt<P>("POINT(1 2,)", "expected ')'");
229     test_wrong_wkt<P>("POINT(1 2)foo", "too many tokens at 'foo'");
230     test_wrong_wkt<P>("POINT(1 2 3)", "expected ')'");
231     test_wrong_wkt<P>("POINT(a 2 3)", "bad lexical cast");
232     test_wrong_wkt<P>("POINT 2 3", "expected '('");
233     test_wrong_wkt<P>("POINT Z (1 2 3)", "z only allowed");
234 
235     test_wrong_wkt<P>("PIONT (1 2)", "should start with 'point'");
236 
237     test_wrong_wkt<bg::model::linestring<P> >("LINESTRING())", "too many tokens");
238 
239     test_wrong_wkt<bg::model::polygon<P> >("POLYGON((1 1,1 4,4 4,4 1,1 1)"
240                 ",((2 2,2 3,3 3,3 2,2 2))", "bad lexical cast");
241 
242     test_wrong_wkt<bg::model::box<P> >("BOX(1 1,2 2,3 3)", "box should have 2");
243     test_wrong_wkt<bg::model::box<P> >("BOX(1 1,2 2) )", "too many tokens");
244 
245     if ( BOOST_GEOMETRY_CONDITION(boost::is_floating_point<T>::type::value
246                                || ! boost::is_fundamental<T>::type::value ) )
247     {
248         test_wkt<P>("POINT(1.1 2.1)", 1);
249     }
250 
251     // Deprecated:
252     // test_wkt_output_iterator<bg::model::linestring<P> >("LINESTRING(1 1,2 2,3 3)");
253     // test_wkt_output_iterator<bg::model::ring<P> >("POLYGON((1 1,2 2,3 3))");
254 
255     test_order_closure<T>();
256 }
257 #endif
258 
test_main(int,char * [])259 int test_main(int, char* [])
260 {
261     test_all<double>();
262     test_all<int>();
263 
264 #if defined(HAVE_TTMATH)
265     test_all<ttmath_big>();
266 #endif
267 
268     return 0;
269 }
270 
271 /*
272 
273 Results can be checked in PostGIS by query below,
274 or by MySQL (but replace length by glength and remove the perimeter)
275 
276 Note:
277 - PostGIS gives "3" for a numpoints of a multi-linestring of 6 points in total (!)
278     --> "npoints" should be taken for all geometries
279 - SQL Server 2008 gives "6"
280     select geometry::STGeomFromText('MULTILINESTRING((1 1,2 2,3 3),(4 4,5 5,6 6))',0).STNumPoints()
281 - MySQL gives "NULL"
282 
283 select 1 as code,'np p' as header,npoints(geomfromtext('POINT(1 2)')) as contents
284 union select 2,'length point', length(geomfromtext('POINT(1 2)'))
285 union select 3,'peri point', perimeter(geomfromtext('POINT(1 2)'))
286 union select 4,'area point',area(geomfromtext('POINT(1 2)'))
287 
288 
289 union select 5,'# ls',npoints(geomfromtext('LINESTRING(1 1,2 2,3 3)'))
290 union select 6,'length ls',length(geomfromtext('LINESTRING(1 1,2 2,3 3)'))
291 union select 7,'peri ls',perimeter(geomfromtext('LINESTRING(1 1,2 2,3 3)'))
292 union select 8,'aera ls',area(geomfromtext('LINESTRING(1 1,2 2,3 3)'))
293 
294 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))'))
295 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))'))
296 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))'))
297 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))'))
298 
299 */
300