Chris@16
|
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
|
Chris@16
|
2
|
Chris@16
|
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
|
Chris@16
|
4 // Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
|
Chris@16
|
5 // Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
|
Chris@16
|
6
|
Chris@101
|
7 // This file was modified by Oracle on 2014.
|
Chris@101
|
8 // Modifications copyright (c) 2014 Oracle and/or its affiliates.
|
Chris@101
|
9
|
Chris@101
|
10 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
|
Chris@101
|
11
|
Chris@16
|
12 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
|
Chris@16
|
13 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
|
Chris@16
|
14
|
Chris@16
|
15 // Use, modification and distribution is subject to the Boost Software License,
|
Chris@16
|
16 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
17 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
18
|
Chris@16
|
19 #ifndef BOOST_GEOMETRY_IO_WKT_READ_HPP
|
Chris@16
|
20 #define BOOST_GEOMETRY_IO_WKT_READ_HPP
|
Chris@16
|
21
|
Chris@101
|
22 #include <cstddef>
|
Chris@16
|
23 #include <string>
|
Chris@16
|
24
|
Chris@16
|
25 #include <boost/lexical_cast.hpp>
|
Chris@16
|
26 #include <boost/tokenizer.hpp>
|
Chris@16
|
27
|
Chris@16
|
28 #include <boost/algorithm/string.hpp>
|
Chris@16
|
29 #include <boost/mpl/if.hpp>
|
Chris@16
|
30 #include <boost/range.hpp>
|
Chris@16
|
31
|
Chris@16
|
32 #include <boost/type_traits.hpp>
|
Chris@16
|
33
|
Chris@16
|
34 #include <boost/geometry/algorithms/assign.hpp>
|
Chris@16
|
35 #include <boost/geometry/algorithms/append.hpp>
|
Chris@16
|
36 #include <boost/geometry/algorithms/clear.hpp>
|
Chris@101
|
37 #include <boost/geometry/algorithms/detail/equals/point_point.hpp>
|
Chris@16
|
38
|
Chris@16
|
39 #include <boost/geometry/core/access.hpp>
|
Chris@16
|
40 #include <boost/geometry/core/coordinate_dimension.hpp>
|
Chris@16
|
41 #include <boost/geometry/core/exception.hpp>
|
Chris@16
|
42 #include <boost/geometry/core/exterior_ring.hpp>
|
Chris@16
|
43 #include <boost/geometry/core/geometry_id.hpp>
|
Chris@16
|
44 #include <boost/geometry/core/interior_rings.hpp>
|
Chris@16
|
45 #include <boost/geometry/core/mutable_range.hpp>
|
Chris@101
|
46 #include <boost/geometry/core/point_type.hpp>
|
Chris@101
|
47 #include <boost/geometry/core/tag_cast.hpp>
|
Chris@101
|
48 #include <boost/geometry/core/tags.hpp>
|
Chris@16
|
49
|
Chris@16
|
50 #include <boost/geometry/geometries/concepts/check.hpp>
|
Chris@16
|
51
|
Chris@16
|
52 #include <boost/geometry/util/coordinate_cast.hpp>
|
Chris@16
|
53
|
Chris@16
|
54 #include <boost/geometry/io/wkt/detail/prefix.hpp>
|
Chris@16
|
55
|
Chris@16
|
56 namespace boost { namespace geometry
|
Chris@16
|
57 {
|
Chris@16
|
58
|
Chris@16
|
59 /*!
|
Chris@16
|
60 \brief Exception showing things wrong with WKT parsing
|
Chris@16
|
61 \ingroup wkt
|
Chris@16
|
62 */
|
Chris@16
|
63 struct read_wkt_exception : public geometry::exception
|
Chris@16
|
64 {
|
Chris@16
|
65 template <typename Iterator>
|
Chris@16
|
66 read_wkt_exception(std::string const& msg,
|
Chris@16
|
67 Iterator const& it, Iterator const& end, std::string const& wkt)
|
Chris@16
|
68 : message(msg)
|
Chris@16
|
69 , wkt(wkt)
|
Chris@16
|
70 {
|
Chris@16
|
71 if (it != end)
|
Chris@16
|
72 {
|
Chris@16
|
73 source = " at '";
|
Chris@16
|
74 source += it->c_str();
|
Chris@16
|
75 source += "'";
|
Chris@16
|
76 }
|
Chris@16
|
77 complete = message + source + " in '" + wkt.substr(0, 100) + "'";
|
Chris@16
|
78 }
|
Chris@16
|
79
|
Chris@16
|
80 read_wkt_exception(std::string const& msg, std::string const& wkt)
|
Chris@16
|
81 : message(msg)
|
Chris@16
|
82 , wkt(wkt)
|
Chris@16
|
83 {
|
Chris@16
|
84 complete = message + "' in (" + wkt.substr(0, 100) + ")";
|
Chris@16
|
85 }
|
Chris@16
|
86
|
Chris@16
|
87 virtual ~read_wkt_exception() throw() {}
|
Chris@16
|
88
|
Chris@16
|
89 virtual const char* what() const throw()
|
Chris@16
|
90 {
|
Chris@16
|
91 return complete.c_str();
|
Chris@16
|
92 }
|
Chris@16
|
93 private :
|
Chris@16
|
94 std::string source;
|
Chris@16
|
95 std::string message;
|
Chris@16
|
96 std::string wkt;
|
Chris@16
|
97 std::string complete;
|
Chris@16
|
98 };
|
Chris@16
|
99
|
Chris@16
|
100
|
Chris@16
|
101 #ifndef DOXYGEN_NO_DETAIL
|
Chris@16
|
102 // (wkt: Well Known Text, defined by OGC for all geometries and implemented by e.g. databases (MySQL, PostGIS))
|
Chris@16
|
103 namespace detail { namespace wkt
|
Chris@16
|
104 {
|
Chris@16
|
105
|
Chris@16
|
106 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
|
Chris@16
|
107
|
Chris@101
|
108 template <typename Point,
|
Chris@101
|
109 std::size_t Dimension = 0,
|
Chris@101
|
110 std::size_t DimensionCount = geometry::dimension<Point>::value>
|
Chris@16
|
111 struct parsing_assigner
|
Chris@16
|
112 {
|
Chris@16
|
113 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
114 Point& point, std::string const& wkt)
|
Chris@16
|
115 {
|
Chris@16
|
116 typedef typename coordinate_type<Point>::type coordinate_type;
|
Chris@16
|
117
|
Chris@16
|
118 // Stop at end of tokens, or at "," ot ")"
|
Chris@16
|
119 bool finished = (it == end || *it == "," || *it == ")");
|
Chris@16
|
120
|
Chris@16
|
121 try
|
Chris@16
|
122 {
|
Chris@16
|
123 // Initialize missing coordinates to default constructor (zero)
|
Chris@16
|
124 // OR
|
Chris@16
|
125 // Use lexical_cast for conversion to double/int
|
Chris@16
|
126 // Note that it is much slower than atof. However, it is more standard
|
Chris@16
|
127 // and in parsing the change in performance falls probably away against
|
Chris@16
|
128 // the tokenizing
|
Chris@16
|
129 set<Dimension>(point, finished
|
Chris@16
|
130 ? coordinate_type()
|
Chris@16
|
131 : coordinate_cast<coordinate_type>::apply(*it));
|
Chris@16
|
132 }
|
Chris@16
|
133 catch(boost::bad_lexical_cast const& blc)
|
Chris@16
|
134 {
|
Chris@16
|
135 throw read_wkt_exception(blc.what(), it, end, wkt);
|
Chris@16
|
136 }
|
Chris@16
|
137 catch(std::exception const& e)
|
Chris@16
|
138 {
|
Chris@16
|
139 throw read_wkt_exception(e.what(), it, end, wkt);
|
Chris@16
|
140 }
|
Chris@16
|
141 catch(...)
|
Chris@16
|
142 {
|
Chris@16
|
143 throw read_wkt_exception("", it, end, wkt);
|
Chris@16
|
144 }
|
Chris@16
|
145
|
Chris@16
|
146 parsing_assigner<Point, Dimension + 1, DimensionCount>::apply(
|
Chris@16
|
147 (finished ? it : ++it), end, point, wkt);
|
Chris@16
|
148 }
|
Chris@16
|
149 };
|
Chris@16
|
150
|
Chris@16
|
151 template <typename Point, std::size_t DimensionCount>
|
Chris@16
|
152 struct parsing_assigner<Point, DimensionCount, DimensionCount>
|
Chris@16
|
153 {
|
Chris@16
|
154 static inline void apply(tokenizer::iterator&, tokenizer::iterator, Point&,
|
Chris@16
|
155 std::string const&)
|
Chris@16
|
156 {
|
Chris@16
|
157 }
|
Chris@16
|
158 };
|
Chris@16
|
159
|
Chris@16
|
160
|
Chris@16
|
161
|
Chris@16
|
162 template <typename Iterator>
|
Chris@16
|
163 inline void handle_open_parenthesis(Iterator& it,
|
Chris@16
|
164 Iterator const& end, std::string const& wkt)
|
Chris@16
|
165 {
|
Chris@16
|
166 if (it == end || *it != "(")
|
Chris@16
|
167 {
|
Chris@16
|
168 throw read_wkt_exception("Expected '('", it, end, wkt);
|
Chris@16
|
169 }
|
Chris@16
|
170 ++it;
|
Chris@16
|
171 }
|
Chris@16
|
172
|
Chris@16
|
173
|
Chris@16
|
174 template <typename Iterator>
|
Chris@16
|
175 inline void handle_close_parenthesis(Iterator& it,
|
Chris@16
|
176 Iterator const& end, std::string const& wkt)
|
Chris@16
|
177 {
|
Chris@16
|
178 if (it != end && *it == ")")
|
Chris@16
|
179 {
|
Chris@16
|
180 ++it;
|
Chris@16
|
181 }
|
Chris@16
|
182 else
|
Chris@16
|
183 {
|
Chris@16
|
184 throw read_wkt_exception("Expected ')'", it, end, wkt);
|
Chris@16
|
185 }
|
Chris@16
|
186 }
|
Chris@16
|
187
|
Chris@16
|
188 template <typename Iterator>
|
Chris@16
|
189 inline void check_end(Iterator& it,
|
Chris@16
|
190 Iterator const& end, std::string const& wkt)
|
Chris@16
|
191 {
|
Chris@16
|
192 if (it != end)
|
Chris@16
|
193 {
|
Chris@16
|
194 throw read_wkt_exception("Too much tokens", it, end, wkt);
|
Chris@16
|
195 }
|
Chris@16
|
196 }
|
Chris@16
|
197
|
Chris@16
|
198 /*!
|
Chris@16
|
199 \brief Internal, parses coordinate sequences, strings are formated like "(1 2,3 4,...)"
|
Chris@16
|
200 \param it token-iterator, should be pre-positioned at "(", is post-positions after last ")"
|
Chris@16
|
201 \param end end-token-iterator
|
Chris@16
|
202 \param out Output itererator receiving coordinates
|
Chris@16
|
203 */
|
Chris@16
|
204 template <typename Point>
|
Chris@16
|
205 struct container_inserter
|
Chris@16
|
206 {
|
Chris@16
|
207 // Version with output iterator
|
Chris@16
|
208 template <typename OutputIterator>
|
Chris@16
|
209 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
210 std::string const& wkt, OutputIterator out)
|
Chris@16
|
211 {
|
Chris@16
|
212 handle_open_parenthesis(it, end, wkt);
|
Chris@16
|
213
|
Chris@16
|
214 Point point;
|
Chris@16
|
215
|
Chris@16
|
216 // Parse points until closing parenthesis
|
Chris@16
|
217
|
Chris@16
|
218 while (it != end && *it != ")")
|
Chris@16
|
219 {
|
Chris@101
|
220 parsing_assigner<Point>::apply(it, end, point, wkt);
|
Chris@16
|
221 out = point;
|
Chris@16
|
222 ++out;
|
Chris@16
|
223 if (it != end && *it == ",")
|
Chris@16
|
224 {
|
Chris@16
|
225 ++it;
|
Chris@16
|
226 }
|
Chris@16
|
227 }
|
Chris@16
|
228
|
Chris@16
|
229 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
230 }
|
Chris@16
|
231 };
|
Chris@16
|
232
|
Chris@16
|
233
|
Chris@101
|
234 template <typename Geometry,
|
Chris@101
|
235 closure_selector Closure = closure<Geometry>::value>
|
Chris@101
|
236 struct stateful_range_appender
|
Chris@101
|
237 {
|
Chris@101
|
238 typedef typename geometry::point_type<Geometry>::type point_type;
|
Chris@101
|
239
|
Chris@101
|
240 // NOTE: Geometry is a reference
|
Chris@101
|
241 inline void append(Geometry geom, point_type const& point, bool)
|
Chris@101
|
242 {
|
Chris@101
|
243 geometry::append(geom, point);
|
Chris@101
|
244 }
|
Chris@101
|
245 };
|
Chris@101
|
246
|
Chris@101
|
247 template <typename Geometry>
|
Chris@101
|
248 struct stateful_range_appender<Geometry, open>
|
Chris@101
|
249 {
|
Chris@101
|
250 typedef typename geometry::point_type<Geometry>::type point_type;
|
Chris@101
|
251 typedef typename boost::range_size
|
Chris@101
|
252 <
|
Chris@101
|
253 typename util::bare_type<Geometry>::type
|
Chris@101
|
254 >::type size_type;
|
Chris@101
|
255
|
Chris@101
|
256 BOOST_STATIC_ASSERT(( boost::is_same
|
Chris@101
|
257 <
|
Chris@101
|
258 typename tag<Geometry>::type,
|
Chris@101
|
259 ring_tag
|
Chris@101
|
260 >::value ));
|
Chris@101
|
261
|
Chris@101
|
262 inline stateful_range_appender()
|
Chris@101
|
263 : pt_index(0)
|
Chris@101
|
264 {}
|
Chris@101
|
265
|
Chris@101
|
266 // NOTE: Geometry is a reference
|
Chris@101
|
267 inline void append(Geometry geom, point_type const& point, bool is_next_expected)
|
Chris@101
|
268 {
|
Chris@101
|
269 bool should_append = true;
|
Chris@101
|
270
|
Chris@101
|
271 if (pt_index == 0)
|
Chris@101
|
272 {
|
Chris@101
|
273 first_point = point;
|
Chris@101
|
274 //should_append = true;
|
Chris@101
|
275 }
|
Chris@101
|
276 else
|
Chris@101
|
277 {
|
Chris@101
|
278 // NOTE: if there is not enough Points, they're always appended
|
Chris@101
|
279 should_append
|
Chris@101
|
280 = is_next_expected
|
Chris@101
|
281 || pt_index < core_detail::closure::minimum_ring_size<open>::value
|
Chris@101
|
282 || !detail::equals::equals_point_point(point, first_point);
|
Chris@101
|
283 }
|
Chris@101
|
284 ++pt_index;
|
Chris@101
|
285
|
Chris@101
|
286 if (should_append)
|
Chris@101
|
287 {
|
Chris@101
|
288 geometry::append(geom, point);
|
Chris@101
|
289 }
|
Chris@101
|
290 }
|
Chris@101
|
291
|
Chris@101
|
292 private:
|
Chris@101
|
293 size_type pt_index;
|
Chris@101
|
294 point_type first_point;
|
Chris@101
|
295 };
|
Chris@101
|
296
|
Chris@16
|
297 // Geometry is a value-type or reference-type
|
Chris@16
|
298 template <typename Geometry>
|
Chris@16
|
299 struct container_appender
|
Chris@16
|
300 {
|
Chris@101
|
301 typedef typename geometry::point_type<Geometry>::type point_type;
|
Chris@16
|
302
|
Chris@16
|
303 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@101
|
304 std::string const& wkt, Geometry out)
|
Chris@16
|
305 {
|
Chris@16
|
306 handle_open_parenthesis(it, end, wkt);
|
Chris@16
|
307
|
Chris@101
|
308 stateful_range_appender<Geometry> appender;
|
Chris@16
|
309
|
Chris@16
|
310 // Parse points until closing parenthesis
|
Chris@16
|
311 while (it != end && *it != ")")
|
Chris@16
|
312 {
|
Chris@101
|
313 point_type point;
|
Chris@16
|
314
|
Chris@101
|
315 parsing_assigner<point_type>::apply(it, end, point, wkt);
|
Chris@101
|
316
|
Chris@101
|
317 bool const is_next_expected = it != end && *it == ",";
|
Chris@101
|
318
|
Chris@101
|
319 appender.append(out, point, is_next_expected);
|
Chris@101
|
320
|
Chris@101
|
321 if (is_next_expected)
|
Chris@16
|
322 {
|
Chris@16
|
323 ++it;
|
Chris@16
|
324 }
|
Chris@16
|
325 }
|
Chris@16
|
326
|
Chris@16
|
327 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
328 }
|
Chris@16
|
329 };
|
Chris@16
|
330
|
Chris@16
|
331 /*!
|
Chris@16
|
332 \brief Internal, parses a point from a string like this "(x y)"
|
Chris@16
|
333 \note used for parsing points and multi-points
|
Chris@16
|
334 */
|
Chris@16
|
335 template <typename P>
|
Chris@16
|
336 struct point_parser
|
Chris@16
|
337 {
|
Chris@16
|
338 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
339 std::string const& wkt, P& point)
|
Chris@16
|
340 {
|
Chris@16
|
341 handle_open_parenthesis(it, end, wkt);
|
Chris@101
|
342 parsing_assigner<P>::apply(it, end, point, wkt);
|
Chris@16
|
343 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
344 }
|
Chris@16
|
345 };
|
Chris@16
|
346
|
Chris@16
|
347
|
Chris@16
|
348 template <typename Geometry>
|
Chris@16
|
349 struct linestring_parser
|
Chris@16
|
350 {
|
Chris@16
|
351 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
352 std::string const& wkt, Geometry& geometry)
|
Chris@16
|
353 {
|
Chris@16
|
354 container_appender<Geometry&>::apply(it, end, wkt, geometry);
|
Chris@16
|
355 }
|
Chris@16
|
356 };
|
Chris@16
|
357
|
Chris@16
|
358
|
Chris@16
|
359 template <typename Ring>
|
Chris@16
|
360 struct ring_parser
|
Chris@16
|
361 {
|
Chris@16
|
362 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
363 std::string const& wkt, Ring& ring)
|
Chris@16
|
364 {
|
Chris@16
|
365 // A ring should look like polygon((x y,x y,x y...))
|
Chris@16
|
366 // So handle the extra opening/closing parentheses
|
Chris@16
|
367 // and in between parse using the container-inserter
|
Chris@16
|
368 handle_open_parenthesis(it, end, wkt);
|
Chris@16
|
369 container_appender<Ring&>::apply(it, end, wkt, ring);
|
Chris@16
|
370 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
371 }
|
Chris@16
|
372 };
|
Chris@16
|
373
|
Chris@16
|
374
|
Chris@16
|
375 /*!
|
Chris@16
|
376 \brief Internal, parses a polygon from a string like this "((x y,x y),(x y,x y))"
|
Chris@16
|
377 \note used for parsing polygons and multi-polygons
|
Chris@16
|
378 */
|
Chris@16
|
379 template <typename Polygon>
|
Chris@16
|
380 struct polygon_parser
|
Chris@16
|
381 {
|
Chris@16
|
382 typedef typename ring_return_type<Polygon>::type ring_return_type;
|
Chris@16
|
383 typedef container_appender<ring_return_type> appender;
|
Chris@16
|
384
|
Chris@16
|
385 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
386 std::string const& wkt, Polygon& poly)
|
Chris@16
|
387 {
|
Chris@16
|
388
|
Chris@16
|
389 handle_open_parenthesis(it, end, wkt);
|
Chris@16
|
390
|
Chris@16
|
391 int n = -1;
|
Chris@16
|
392
|
Chris@16
|
393 // Stop at ")"
|
Chris@16
|
394 while (it != end && *it != ")")
|
Chris@16
|
395 {
|
Chris@16
|
396 // Parse ring
|
Chris@16
|
397 if (++n == 0)
|
Chris@16
|
398 {
|
Chris@16
|
399 appender::apply(it, end, wkt, exterior_ring(poly));
|
Chris@16
|
400 }
|
Chris@16
|
401 else
|
Chris@16
|
402 {
|
Chris@16
|
403 typename ring_type<Polygon>::type ring;
|
Chris@16
|
404 appender::apply(it, end, wkt, ring);
|
Chris@16
|
405 traits::push_back
|
Chris@16
|
406 <
|
Chris@16
|
407 typename boost::remove_reference
|
Chris@16
|
408 <
|
Chris@16
|
409 typename traits::interior_mutable_type<Polygon>::type
|
Chris@16
|
410 >::type
|
Chris@16
|
411 >::apply(interior_rings(poly), ring);
|
Chris@16
|
412 }
|
Chris@16
|
413
|
Chris@16
|
414 if (it != end && *it == ",")
|
Chris@16
|
415 {
|
Chris@16
|
416 // Skip "," after ring is parsed
|
Chris@16
|
417 ++it;
|
Chris@16
|
418 }
|
Chris@16
|
419 }
|
Chris@16
|
420
|
Chris@16
|
421 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
422 }
|
Chris@16
|
423 };
|
Chris@16
|
424
|
Chris@101
|
425
|
Chris@16
|
426 inline bool one_of(tokenizer::iterator const& it, std::string const& value,
|
Chris@16
|
427 bool& is_present)
|
Chris@16
|
428 {
|
Chris@16
|
429 if (boost::iequals(*it, value))
|
Chris@16
|
430 {
|
Chris@16
|
431 is_present = true;
|
Chris@16
|
432 return true;
|
Chris@16
|
433 }
|
Chris@16
|
434 return false;
|
Chris@16
|
435 }
|
Chris@16
|
436
|
Chris@16
|
437 inline bool one_of(tokenizer::iterator const& it, std::string const& value,
|
Chris@16
|
438 bool& present1, bool& present2)
|
Chris@16
|
439 {
|
Chris@16
|
440 if (boost::iequals(*it, value))
|
Chris@16
|
441 {
|
Chris@16
|
442 present1 = true;
|
Chris@16
|
443 present2 = true;
|
Chris@16
|
444 return true;
|
Chris@16
|
445 }
|
Chris@16
|
446 return false;
|
Chris@16
|
447 }
|
Chris@16
|
448
|
Chris@16
|
449
|
Chris@16
|
450 inline void handle_empty_z_m(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
451 bool& has_empty, bool& has_z, bool& has_m)
|
Chris@16
|
452 {
|
Chris@16
|
453 has_empty = false;
|
Chris@16
|
454 has_z = false;
|
Chris@16
|
455 has_m = false;
|
Chris@16
|
456
|
Chris@16
|
457 // WKT can optionally have Z and M (measured) values as in
|
Chris@16
|
458 // POINT ZM (1 1 5 60), POINT M (1 1 80), POINT Z (1 1 5)
|
Chris@16
|
459 // GGL supports any of them as coordinate values, but is not aware
|
Chris@16
|
460 // of any Measured value.
|
Chris@16
|
461 while (it != end
|
Chris@16
|
462 && (one_of(it, "M", has_m)
|
Chris@16
|
463 || one_of(it, "Z", has_z)
|
Chris@16
|
464 || one_of(it, "EMPTY", has_empty)
|
Chris@16
|
465 || one_of(it, "MZ", has_m, has_z)
|
Chris@16
|
466 || one_of(it, "ZM", has_z, has_m)
|
Chris@16
|
467 )
|
Chris@16
|
468 )
|
Chris@16
|
469 {
|
Chris@16
|
470 ++it;
|
Chris@16
|
471 }
|
Chris@16
|
472 }
|
Chris@16
|
473
|
Chris@16
|
474 /*!
|
Chris@16
|
475 \brief Internal, starts parsing
|
Chris@16
|
476 \param tokens boost tokens, parsed with separator " " and keeping separator "()"
|
Chris@16
|
477 \param geometry string to compare with first token
|
Chris@16
|
478 */
|
Chris@16
|
479 template <typename Geometry>
|
Chris@16
|
480 inline bool initialize(tokenizer const& tokens,
|
Chris@16
|
481 std::string const& geometry_name, std::string const& wkt,
|
Chris@16
|
482 tokenizer::iterator& it)
|
Chris@16
|
483 {
|
Chris@16
|
484 it = tokens.begin();
|
Chris@16
|
485 if (it != tokens.end() && boost::iequals(*it++, geometry_name))
|
Chris@16
|
486 {
|
Chris@16
|
487 bool has_empty, has_z, has_m;
|
Chris@16
|
488
|
Chris@16
|
489 handle_empty_z_m(it, tokens.end(), has_empty, has_z, has_m);
|
Chris@16
|
490
|
Chris@16
|
491 // Silence warning C4127: conditional expression is constant
|
Chris@16
|
492 #if defined(_MSC_VER)
|
Chris@16
|
493 #pragma warning(push)
|
Chris@16
|
494 #pragma warning(disable : 4127)
|
Chris@16
|
495 #endif
|
Chris@16
|
496
|
Chris@16
|
497 if (has_z && dimension<Geometry>::type::value < 3)
|
Chris@16
|
498 {
|
Chris@16
|
499 throw read_wkt_exception("Z only allowed for 3 or more dimensions", wkt);
|
Chris@16
|
500 }
|
Chris@16
|
501
|
Chris@16
|
502 #if defined(_MSC_VER)
|
Chris@16
|
503 #pragma warning(pop)
|
Chris@16
|
504 #endif
|
Chris@16
|
505
|
Chris@16
|
506 if (has_empty)
|
Chris@16
|
507 {
|
Chris@16
|
508 check_end(it, tokens.end(), wkt);
|
Chris@16
|
509 return false;
|
Chris@16
|
510 }
|
Chris@16
|
511 // M is ignored at all.
|
Chris@16
|
512
|
Chris@16
|
513 return true;
|
Chris@16
|
514 }
|
Chris@16
|
515 throw read_wkt_exception(std::string("Should start with '") + geometry_name + "'", wkt);
|
Chris@16
|
516 }
|
Chris@16
|
517
|
Chris@16
|
518
|
Chris@16
|
519 template <typename Geometry, template<typename> class Parser, typename PrefixPolicy>
|
Chris@16
|
520 struct geometry_parser
|
Chris@16
|
521 {
|
Chris@16
|
522 static inline void apply(std::string const& wkt, Geometry& geometry)
|
Chris@16
|
523 {
|
Chris@16
|
524 geometry::clear(geometry);
|
Chris@16
|
525
|
Chris@16
|
526 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
|
Chris@16
|
527 tokenizer::iterator it;
|
Chris@16
|
528 if (initialize<Geometry>(tokens, PrefixPolicy::apply(), wkt, it))
|
Chris@16
|
529 {
|
Chris@16
|
530 Parser<Geometry>::apply(it, tokens.end(), wkt, geometry);
|
Chris@16
|
531 check_end(it, tokens.end(), wkt);
|
Chris@16
|
532 }
|
Chris@16
|
533 }
|
Chris@16
|
534 };
|
Chris@16
|
535
|
Chris@16
|
536
|
Chris@101
|
537 template <typename MultiGeometry, template<typename> class Parser, typename PrefixPolicy>
|
Chris@101
|
538 struct multi_parser
|
Chris@101
|
539 {
|
Chris@101
|
540 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
|
Chris@101
|
541 {
|
Chris@101
|
542 traits::clear<MultiGeometry>::apply(geometry);
|
Chris@16
|
543
|
Chris@101
|
544 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
|
Chris@101
|
545 tokenizer::iterator it;
|
Chris@101
|
546 if (initialize<MultiGeometry>(tokens, PrefixPolicy::apply(), wkt, it))
|
Chris@101
|
547 {
|
Chris@101
|
548 handle_open_parenthesis(it, tokens.end(), wkt);
|
Chris@101
|
549
|
Chris@101
|
550 // Parse sub-geometries
|
Chris@101
|
551 while(it != tokens.end() && *it != ")")
|
Chris@101
|
552 {
|
Chris@101
|
553 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
|
Chris@101
|
554 Parser
|
Chris@101
|
555 <
|
Chris@101
|
556 typename boost::range_value<MultiGeometry>::type
|
Chris@101
|
557 >::apply(it, tokens.end(), wkt, *(boost::end(geometry) - 1));
|
Chris@101
|
558 if (it != tokens.end() && *it == ",")
|
Chris@101
|
559 {
|
Chris@101
|
560 // Skip "," after multi-element is parsed
|
Chris@101
|
561 ++it;
|
Chris@101
|
562 }
|
Chris@101
|
563 }
|
Chris@101
|
564
|
Chris@101
|
565 handle_close_parenthesis(it, tokens.end(), wkt);
|
Chris@101
|
566 }
|
Chris@101
|
567
|
Chris@101
|
568 check_end(it, tokens.end(), wkt);
|
Chris@101
|
569 }
|
Chris@101
|
570 };
|
Chris@101
|
571
|
Chris@101
|
572 template <typename P>
|
Chris@101
|
573 struct noparenthesis_point_parser
|
Chris@101
|
574 {
|
Chris@101
|
575 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@101
|
576 std::string const& wkt, P& point)
|
Chris@101
|
577 {
|
Chris@101
|
578 parsing_assigner<P>::apply(it, end, point, wkt);
|
Chris@101
|
579 }
|
Chris@101
|
580 };
|
Chris@101
|
581
|
Chris@101
|
582 template <typename MultiGeometry, typename PrefixPolicy>
|
Chris@101
|
583 struct multi_point_parser
|
Chris@101
|
584 {
|
Chris@101
|
585 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
|
Chris@101
|
586 {
|
Chris@101
|
587 traits::clear<MultiGeometry>::apply(geometry);
|
Chris@101
|
588
|
Chris@101
|
589 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
|
Chris@101
|
590 tokenizer::iterator it;
|
Chris@101
|
591
|
Chris@101
|
592 if (initialize<MultiGeometry>(tokens, PrefixPolicy::apply(), wkt, it))
|
Chris@101
|
593 {
|
Chris@101
|
594 handle_open_parenthesis(it, tokens.end(), wkt);
|
Chris@101
|
595
|
Chris@101
|
596 // If first point definition starts with "(" then parse points as (x y)
|
Chris@101
|
597 // otherwise as "x y"
|
Chris@101
|
598 bool using_brackets = (it != tokens.end() && *it == "(");
|
Chris@101
|
599
|
Chris@101
|
600 while(it != tokens.end() && *it != ")")
|
Chris@101
|
601 {
|
Chris@101
|
602 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
|
Chris@101
|
603
|
Chris@101
|
604 if (using_brackets)
|
Chris@101
|
605 {
|
Chris@101
|
606 point_parser
|
Chris@101
|
607 <
|
Chris@101
|
608 typename boost::range_value<MultiGeometry>::type
|
Chris@101
|
609 >::apply(it, tokens.end(), wkt, *(boost::end(geometry) - 1));
|
Chris@101
|
610 }
|
Chris@101
|
611 else
|
Chris@101
|
612 {
|
Chris@101
|
613 noparenthesis_point_parser
|
Chris@101
|
614 <
|
Chris@101
|
615 typename boost::range_value<MultiGeometry>::type
|
Chris@101
|
616 >::apply(it, tokens.end(), wkt, *(boost::end(geometry) - 1));
|
Chris@101
|
617 }
|
Chris@101
|
618
|
Chris@101
|
619 if (it != tokens.end() && *it == ",")
|
Chris@101
|
620 {
|
Chris@101
|
621 // Skip "," after point is parsed
|
Chris@101
|
622 ++it;
|
Chris@101
|
623 }
|
Chris@101
|
624 }
|
Chris@101
|
625
|
Chris@101
|
626 handle_close_parenthesis(it, tokens.end(), wkt);
|
Chris@101
|
627 }
|
Chris@101
|
628
|
Chris@101
|
629 check_end(it, tokens.end(), wkt);
|
Chris@101
|
630 }
|
Chris@101
|
631 };
|
Chris@16
|
632
|
Chris@16
|
633
|
Chris@16
|
634 /*!
|
Chris@16
|
635 \brief Supports box parsing
|
Chris@16
|
636 \note OGC does not define the box geometry, and WKT does not support boxes.
|
Chris@16
|
637 However, to be generic GGL supports reading and writing from and to boxes.
|
Chris@16
|
638 Boxes are outputted as a standard POLYGON. GGL can read boxes from
|
Chris@16
|
639 a standard POLYGON, from a POLYGON with 2 points of from a BOX
|
Chris@16
|
640 \tparam Box the box
|
Chris@16
|
641 */
|
Chris@16
|
642 template <typename Box>
|
Chris@16
|
643 struct box_parser
|
Chris@16
|
644 {
|
Chris@16
|
645 static inline void apply(std::string const& wkt, Box& box)
|
Chris@16
|
646 {
|
Chris@16
|
647 bool should_close = false;
|
Chris@16
|
648 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
|
Chris@16
|
649 tokenizer::iterator it = tokens.begin();
|
Chris@16
|
650 tokenizer::iterator end = tokens.end();
|
Chris@16
|
651 if (it != end && boost::iequals(*it, "POLYGON"))
|
Chris@16
|
652 {
|
Chris@16
|
653 ++it;
|
Chris@16
|
654 bool has_empty, has_z, has_m;
|
Chris@16
|
655 handle_empty_z_m(it, end, has_empty, has_z, has_m);
|
Chris@16
|
656 if (has_empty)
|
Chris@16
|
657 {
|
Chris@16
|
658 assign_zero(box);
|
Chris@16
|
659 return;
|
Chris@16
|
660 }
|
Chris@16
|
661 handle_open_parenthesis(it, end, wkt);
|
Chris@16
|
662 should_close = true;
|
Chris@16
|
663 }
|
Chris@16
|
664 else if (it != end && boost::iequals(*it, "BOX"))
|
Chris@16
|
665 {
|
Chris@16
|
666 ++it;
|
Chris@16
|
667 }
|
Chris@16
|
668 else
|
Chris@16
|
669 {
|
Chris@16
|
670 throw read_wkt_exception("Should start with 'POLYGON' or 'BOX'", wkt);
|
Chris@16
|
671 }
|
Chris@16
|
672
|
Chris@16
|
673 typedef typename point_type<Box>::type point_type;
|
Chris@16
|
674 std::vector<point_type> points;
|
Chris@16
|
675 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
|
Chris@16
|
676
|
Chris@16
|
677 if (should_close)
|
Chris@16
|
678 {
|
Chris@16
|
679 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
680 }
|
Chris@16
|
681 check_end(it, end, wkt);
|
Chris@16
|
682
|
Chris@101
|
683 unsigned int index = 0;
|
Chris@101
|
684 std::size_t n = boost::size(points);
|
Chris@16
|
685 if (n == 2)
|
Chris@16
|
686 {
|
Chris@16
|
687 index = 1;
|
Chris@16
|
688 }
|
Chris@16
|
689 else if (n == 4 || n == 5)
|
Chris@16
|
690 {
|
Chris@16
|
691 // In case of 4 or 5 points, we do not check the other ones, just
|
Chris@16
|
692 // take the opposite corner which is always 2
|
Chris@16
|
693 index = 2;
|
Chris@16
|
694 }
|
Chris@16
|
695 else
|
Chris@16
|
696 {
|
Chris@16
|
697 throw read_wkt_exception("Box should have 2,4 or 5 points", wkt);
|
Chris@16
|
698 }
|
Chris@16
|
699
|
Chris@16
|
700 geometry::detail::assign_point_to_index<min_corner>(points.front(), box);
|
Chris@16
|
701 geometry::detail::assign_point_to_index<max_corner>(points[index], box);
|
Chris@16
|
702 }
|
Chris@16
|
703 };
|
Chris@16
|
704
|
Chris@16
|
705
|
Chris@16
|
706 /*!
|
Chris@16
|
707 \brief Supports segment parsing
|
Chris@16
|
708 \note OGC does not define the segment, and WKT does not support segmentes.
|
Chris@16
|
709 However, it is useful to implement it, also for testing purposes
|
Chris@16
|
710 \tparam Segment the segment
|
Chris@16
|
711 */
|
Chris@16
|
712 template <typename Segment>
|
Chris@16
|
713 struct segment_parser
|
Chris@16
|
714 {
|
Chris@16
|
715 static inline void apply(std::string const& wkt, Segment& segment)
|
Chris@16
|
716 {
|
Chris@16
|
717 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
|
Chris@16
|
718 tokenizer::iterator it = tokens.begin();
|
Chris@16
|
719 tokenizer::iterator end = tokens.end();
|
Chris@16
|
720 if (it != end &&
|
Chris@16
|
721 (boost::iequals(*it, "SEGMENT")
|
Chris@16
|
722 || boost::iequals(*it, "LINESTRING") ))
|
Chris@16
|
723 {
|
Chris@16
|
724 ++it;
|
Chris@16
|
725 }
|
Chris@16
|
726 else
|
Chris@16
|
727 {
|
Chris@16
|
728 throw read_wkt_exception("Should start with 'LINESTRING' or 'SEGMENT'", wkt);
|
Chris@16
|
729 }
|
Chris@16
|
730
|
Chris@16
|
731 typedef typename point_type<Segment>::type point_type;
|
Chris@16
|
732 std::vector<point_type> points;
|
Chris@16
|
733 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
|
Chris@16
|
734
|
Chris@16
|
735 check_end(it, end, wkt);
|
Chris@16
|
736
|
Chris@16
|
737 if (boost::size(points) == 2)
|
Chris@16
|
738 {
|
Chris@16
|
739 geometry::detail::assign_point_to_index<0>(points.front(), segment);
|
Chris@16
|
740 geometry::detail::assign_point_to_index<1>(points.back(), segment);
|
Chris@16
|
741 }
|
Chris@16
|
742 else
|
Chris@16
|
743 {
|
Chris@16
|
744 throw read_wkt_exception("Segment should have 2 points", wkt);
|
Chris@16
|
745 }
|
Chris@16
|
746
|
Chris@16
|
747 }
|
Chris@16
|
748 };
|
Chris@16
|
749
|
Chris@16
|
750
|
Chris@16
|
751 }} // namespace detail::wkt
|
Chris@16
|
752 #endif // DOXYGEN_NO_DETAIL
|
Chris@16
|
753
|
Chris@16
|
754 #ifndef DOXYGEN_NO_DISPATCH
|
Chris@16
|
755 namespace dispatch
|
Chris@16
|
756 {
|
Chris@16
|
757
|
Chris@16
|
758 template <typename Tag, typename Geometry>
|
Chris@16
|
759 struct read_wkt {};
|
Chris@16
|
760
|
Chris@16
|
761
|
Chris@16
|
762 template <typename Point>
|
Chris@16
|
763 struct read_wkt<point_tag, Point>
|
Chris@16
|
764 : detail::wkt::geometry_parser
|
Chris@16
|
765 <
|
Chris@16
|
766 Point,
|
Chris@16
|
767 detail::wkt::point_parser,
|
Chris@16
|
768 detail::wkt::prefix_point
|
Chris@16
|
769 >
|
Chris@16
|
770 {};
|
Chris@16
|
771
|
Chris@16
|
772
|
Chris@16
|
773 template <typename L>
|
Chris@16
|
774 struct read_wkt<linestring_tag, L>
|
Chris@16
|
775 : detail::wkt::geometry_parser
|
Chris@16
|
776 <
|
Chris@16
|
777 L,
|
Chris@16
|
778 detail::wkt::linestring_parser,
|
Chris@16
|
779 detail::wkt::prefix_linestring
|
Chris@16
|
780 >
|
Chris@16
|
781 {};
|
Chris@16
|
782
|
Chris@16
|
783 template <typename Ring>
|
Chris@16
|
784 struct read_wkt<ring_tag, Ring>
|
Chris@16
|
785 : detail::wkt::geometry_parser
|
Chris@16
|
786 <
|
Chris@16
|
787 Ring,
|
Chris@16
|
788 detail::wkt::ring_parser,
|
Chris@16
|
789 detail::wkt::prefix_polygon
|
Chris@16
|
790 >
|
Chris@16
|
791 {};
|
Chris@16
|
792
|
Chris@16
|
793 template <typename Geometry>
|
Chris@16
|
794 struct read_wkt<polygon_tag, Geometry>
|
Chris@16
|
795 : detail::wkt::geometry_parser
|
Chris@16
|
796 <
|
Chris@16
|
797 Geometry,
|
Chris@16
|
798 detail::wkt::polygon_parser,
|
Chris@16
|
799 detail::wkt::prefix_polygon
|
Chris@16
|
800 >
|
Chris@16
|
801 {};
|
Chris@16
|
802
|
Chris@16
|
803
|
Chris@101
|
804 template <typename MultiGeometry>
|
Chris@101
|
805 struct read_wkt<multi_point_tag, MultiGeometry>
|
Chris@101
|
806 : detail::wkt::multi_point_parser
|
Chris@101
|
807 <
|
Chris@101
|
808 MultiGeometry,
|
Chris@101
|
809 detail::wkt::prefix_multipoint
|
Chris@101
|
810 >
|
Chris@101
|
811 {};
|
Chris@101
|
812
|
Chris@101
|
813 template <typename MultiGeometry>
|
Chris@101
|
814 struct read_wkt<multi_linestring_tag, MultiGeometry>
|
Chris@101
|
815 : detail::wkt::multi_parser
|
Chris@101
|
816 <
|
Chris@101
|
817 MultiGeometry,
|
Chris@101
|
818 detail::wkt::linestring_parser,
|
Chris@101
|
819 detail::wkt::prefix_multilinestring
|
Chris@101
|
820 >
|
Chris@101
|
821 {};
|
Chris@101
|
822
|
Chris@101
|
823 template <typename MultiGeometry>
|
Chris@101
|
824 struct read_wkt<multi_polygon_tag, MultiGeometry>
|
Chris@101
|
825 : detail::wkt::multi_parser
|
Chris@101
|
826 <
|
Chris@101
|
827 MultiGeometry,
|
Chris@101
|
828 detail::wkt::polygon_parser,
|
Chris@101
|
829 detail::wkt::prefix_multipolygon
|
Chris@101
|
830 >
|
Chris@101
|
831 {};
|
Chris@101
|
832
|
Chris@101
|
833
|
Chris@16
|
834 // Box (Non-OGC)
|
Chris@16
|
835 template <typename Box>
|
Chris@16
|
836 struct read_wkt<box_tag, Box>
|
Chris@16
|
837 : detail::wkt::box_parser<Box>
|
Chris@16
|
838 {};
|
Chris@16
|
839
|
Chris@16
|
840 // Segment (Non-OGC)
|
Chris@16
|
841 template <typename Segment>
|
Chris@16
|
842 struct read_wkt<segment_tag, Segment>
|
Chris@16
|
843 : detail::wkt::segment_parser<Segment>
|
Chris@16
|
844 {};
|
Chris@16
|
845
|
Chris@16
|
846
|
Chris@16
|
847 } // namespace dispatch
|
Chris@16
|
848 #endif // DOXYGEN_NO_DISPATCH
|
Chris@16
|
849
|
Chris@16
|
850 /*!
|
Chris@16
|
851 \brief Parses OGC Well-Known Text (\ref WKT) into a geometry (any geometry)
|
Chris@16
|
852 \ingroup wkt
|
Chris@101
|
853 \tparam Geometry \tparam_geometry
|
Chris@16
|
854 \param wkt string containing \ref WKT
|
Chris@101
|
855 \param geometry \param_geometry output geometry
|
Chris@101
|
856 \ingroup wkt
|
Chris@101
|
857 \qbk{[include reference/io/read_wkt.qbk]}
|
Chris@16
|
858 */
|
Chris@16
|
859 template <typename Geometry>
|
Chris@16
|
860 inline void read_wkt(std::string const& wkt, Geometry& geometry)
|
Chris@16
|
861 {
|
Chris@16
|
862 geometry::concept::check<Geometry>();
|
Chris@16
|
863 dispatch::read_wkt<typename tag<Geometry>::type, Geometry>::apply(wkt, geometry);
|
Chris@16
|
864 }
|
Chris@16
|
865
|
Chris@16
|
866 }} // namespace boost::geometry
|
Chris@16
|
867
|
Chris@16
|
868 #endif // BOOST_GEOMETRY_IO_WKT_READ_HPP
|