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