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@16
|
7 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
|
Chris@16
|
8 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
|
Chris@16
|
9
|
Chris@16
|
10 // Use, modification and distribution is subject to the Boost Software License,
|
Chris@16
|
11 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@16
|
12 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
13
|
Chris@16
|
14 #ifndef BOOST_GEOMETRY_IO_WKT_READ_HPP
|
Chris@16
|
15 #define BOOST_GEOMETRY_IO_WKT_READ_HPP
|
Chris@16
|
16
|
Chris@16
|
17 #include <string>
|
Chris@16
|
18
|
Chris@16
|
19 #include <boost/lexical_cast.hpp>
|
Chris@16
|
20 #include <boost/tokenizer.hpp>
|
Chris@16
|
21
|
Chris@16
|
22 #include <boost/algorithm/string.hpp>
|
Chris@16
|
23 #include <boost/mpl/if.hpp>
|
Chris@16
|
24 #include <boost/range.hpp>
|
Chris@16
|
25
|
Chris@16
|
26 #include <boost/type_traits.hpp>
|
Chris@16
|
27
|
Chris@16
|
28 #include <boost/geometry/algorithms/assign.hpp>
|
Chris@16
|
29 #include <boost/geometry/algorithms/append.hpp>
|
Chris@16
|
30 #include <boost/geometry/algorithms/clear.hpp>
|
Chris@16
|
31
|
Chris@16
|
32 #include <boost/geometry/core/access.hpp>
|
Chris@16
|
33 #include <boost/geometry/core/coordinate_dimension.hpp>
|
Chris@16
|
34 #include <boost/geometry/core/exception.hpp>
|
Chris@16
|
35 #include <boost/geometry/core/exterior_ring.hpp>
|
Chris@16
|
36 #include <boost/geometry/core/geometry_id.hpp>
|
Chris@16
|
37 #include <boost/geometry/core/interior_rings.hpp>
|
Chris@16
|
38 #include <boost/geometry/core/mutable_range.hpp>
|
Chris@16
|
39
|
Chris@16
|
40 #include <boost/geometry/geometries/concepts/check.hpp>
|
Chris@16
|
41
|
Chris@16
|
42 #include <boost/geometry/util/coordinate_cast.hpp>
|
Chris@16
|
43
|
Chris@16
|
44 #include <boost/geometry/io/wkt/detail/prefix.hpp>
|
Chris@16
|
45
|
Chris@16
|
46 namespace boost { namespace geometry
|
Chris@16
|
47 {
|
Chris@16
|
48
|
Chris@16
|
49 /*!
|
Chris@16
|
50 \brief Exception showing things wrong with WKT parsing
|
Chris@16
|
51 \ingroup wkt
|
Chris@16
|
52 */
|
Chris@16
|
53 struct read_wkt_exception : public geometry::exception
|
Chris@16
|
54 {
|
Chris@16
|
55 template <typename Iterator>
|
Chris@16
|
56 read_wkt_exception(std::string const& msg,
|
Chris@16
|
57 Iterator const& it, Iterator const& end, std::string const& wkt)
|
Chris@16
|
58 : message(msg)
|
Chris@16
|
59 , wkt(wkt)
|
Chris@16
|
60 {
|
Chris@16
|
61 if (it != end)
|
Chris@16
|
62 {
|
Chris@16
|
63 source = " at '";
|
Chris@16
|
64 source += it->c_str();
|
Chris@16
|
65 source += "'";
|
Chris@16
|
66 }
|
Chris@16
|
67 complete = message + source + " in '" + wkt.substr(0, 100) + "'";
|
Chris@16
|
68 }
|
Chris@16
|
69
|
Chris@16
|
70 read_wkt_exception(std::string const& msg, std::string const& wkt)
|
Chris@16
|
71 : message(msg)
|
Chris@16
|
72 , wkt(wkt)
|
Chris@16
|
73 {
|
Chris@16
|
74 complete = message + "' in (" + wkt.substr(0, 100) + ")";
|
Chris@16
|
75 }
|
Chris@16
|
76
|
Chris@16
|
77 virtual ~read_wkt_exception() throw() {}
|
Chris@16
|
78
|
Chris@16
|
79 virtual const char* what() const throw()
|
Chris@16
|
80 {
|
Chris@16
|
81 return complete.c_str();
|
Chris@16
|
82 }
|
Chris@16
|
83 private :
|
Chris@16
|
84 std::string source;
|
Chris@16
|
85 std::string message;
|
Chris@16
|
86 std::string wkt;
|
Chris@16
|
87 std::string complete;
|
Chris@16
|
88 };
|
Chris@16
|
89
|
Chris@16
|
90
|
Chris@16
|
91 #ifndef DOXYGEN_NO_DETAIL
|
Chris@16
|
92 // (wkt: Well Known Text, defined by OGC for all geometries and implemented by e.g. databases (MySQL, PostGIS))
|
Chris@16
|
93 namespace detail { namespace wkt
|
Chris@16
|
94 {
|
Chris@16
|
95
|
Chris@16
|
96 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
|
Chris@16
|
97
|
Chris@16
|
98 template <typename Point, std::size_t Dimension, std::size_t DimensionCount>
|
Chris@16
|
99 struct parsing_assigner
|
Chris@16
|
100 {
|
Chris@16
|
101 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
102 Point& point, std::string const& wkt)
|
Chris@16
|
103 {
|
Chris@16
|
104 typedef typename coordinate_type<Point>::type coordinate_type;
|
Chris@16
|
105
|
Chris@16
|
106 // Stop at end of tokens, or at "," ot ")"
|
Chris@16
|
107 bool finished = (it == end || *it == "," || *it == ")");
|
Chris@16
|
108
|
Chris@16
|
109 try
|
Chris@16
|
110 {
|
Chris@16
|
111 // Initialize missing coordinates to default constructor (zero)
|
Chris@16
|
112 // OR
|
Chris@16
|
113 // Use lexical_cast for conversion to double/int
|
Chris@16
|
114 // Note that it is much slower than atof. However, it is more standard
|
Chris@16
|
115 // and in parsing the change in performance falls probably away against
|
Chris@16
|
116 // the tokenizing
|
Chris@16
|
117 set<Dimension>(point, finished
|
Chris@16
|
118 ? coordinate_type()
|
Chris@16
|
119 : coordinate_cast<coordinate_type>::apply(*it));
|
Chris@16
|
120 }
|
Chris@16
|
121 catch(boost::bad_lexical_cast const& blc)
|
Chris@16
|
122 {
|
Chris@16
|
123 throw read_wkt_exception(blc.what(), it, end, wkt);
|
Chris@16
|
124 }
|
Chris@16
|
125 catch(std::exception const& e)
|
Chris@16
|
126 {
|
Chris@16
|
127 throw read_wkt_exception(e.what(), it, end, wkt);
|
Chris@16
|
128 }
|
Chris@16
|
129 catch(...)
|
Chris@16
|
130 {
|
Chris@16
|
131 throw read_wkt_exception("", it, end, wkt);
|
Chris@16
|
132 }
|
Chris@16
|
133
|
Chris@16
|
134 parsing_assigner<Point, Dimension + 1, DimensionCount>::apply(
|
Chris@16
|
135 (finished ? it : ++it), end, point, wkt);
|
Chris@16
|
136 }
|
Chris@16
|
137 };
|
Chris@16
|
138
|
Chris@16
|
139 template <typename Point, std::size_t DimensionCount>
|
Chris@16
|
140 struct parsing_assigner<Point, DimensionCount, DimensionCount>
|
Chris@16
|
141 {
|
Chris@16
|
142 static inline void apply(tokenizer::iterator&, tokenizer::iterator, Point&,
|
Chris@16
|
143 std::string const&)
|
Chris@16
|
144 {
|
Chris@16
|
145 }
|
Chris@16
|
146 };
|
Chris@16
|
147
|
Chris@16
|
148
|
Chris@16
|
149
|
Chris@16
|
150 template <typename Iterator>
|
Chris@16
|
151 inline void handle_open_parenthesis(Iterator& it,
|
Chris@16
|
152 Iterator const& end, std::string const& wkt)
|
Chris@16
|
153 {
|
Chris@16
|
154 if (it == end || *it != "(")
|
Chris@16
|
155 {
|
Chris@16
|
156 throw read_wkt_exception("Expected '('", it, end, wkt);
|
Chris@16
|
157 }
|
Chris@16
|
158 ++it;
|
Chris@16
|
159 }
|
Chris@16
|
160
|
Chris@16
|
161
|
Chris@16
|
162 template <typename Iterator>
|
Chris@16
|
163 inline void handle_close_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 ++it;
|
Chris@16
|
169 }
|
Chris@16
|
170 else
|
Chris@16
|
171 {
|
Chris@16
|
172 throw read_wkt_exception("Expected ')'", it, end, wkt);
|
Chris@16
|
173 }
|
Chris@16
|
174 }
|
Chris@16
|
175
|
Chris@16
|
176 template <typename Iterator>
|
Chris@16
|
177 inline void check_end(Iterator& it,
|
Chris@16
|
178 Iterator const& end, std::string const& wkt)
|
Chris@16
|
179 {
|
Chris@16
|
180 if (it != end)
|
Chris@16
|
181 {
|
Chris@16
|
182 throw read_wkt_exception("Too much tokens", it, end, wkt);
|
Chris@16
|
183 }
|
Chris@16
|
184 }
|
Chris@16
|
185
|
Chris@16
|
186 /*!
|
Chris@16
|
187 \brief Internal, parses coordinate sequences, strings are formated like "(1 2,3 4,...)"
|
Chris@16
|
188 \param it token-iterator, should be pre-positioned at "(", is post-positions after last ")"
|
Chris@16
|
189 \param end end-token-iterator
|
Chris@16
|
190 \param out Output itererator receiving coordinates
|
Chris@16
|
191 */
|
Chris@16
|
192 template <typename Point>
|
Chris@16
|
193 struct container_inserter
|
Chris@16
|
194 {
|
Chris@16
|
195 // Version with output iterator
|
Chris@16
|
196 template <typename OutputIterator>
|
Chris@16
|
197 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
198 std::string const& wkt, OutputIterator out)
|
Chris@16
|
199 {
|
Chris@16
|
200 handle_open_parenthesis(it, end, wkt);
|
Chris@16
|
201
|
Chris@16
|
202 Point point;
|
Chris@16
|
203
|
Chris@16
|
204 // Parse points until closing parenthesis
|
Chris@16
|
205
|
Chris@16
|
206 while (it != end && *it != ")")
|
Chris@16
|
207 {
|
Chris@16
|
208 parsing_assigner
|
Chris@16
|
209 <
|
Chris@16
|
210 Point,
|
Chris@16
|
211 0,
|
Chris@16
|
212 dimension<Point>::value
|
Chris@16
|
213 >::apply(it, end, point, wkt);
|
Chris@16
|
214 out = point;
|
Chris@16
|
215 ++out;
|
Chris@16
|
216 if (it != end && *it == ",")
|
Chris@16
|
217 {
|
Chris@16
|
218 ++it;
|
Chris@16
|
219 }
|
Chris@16
|
220 }
|
Chris@16
|
221
|
Chris@16
|
222 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
223 }
|
Chris@16
|
224 };
|
Chris@16
|
225
|
Chris@16
|
226
|
Chris@16
|
227 // Geometry is a value-type or reference-type
|
Chris@16
|
228 template <typename Geometry>
|
Chris@16
|
229 struct container_appender
|
Chris@16
|
230 {
|
Chris@16
|
231 typedef typename geometry::point_type
|
Chris@16
|
232 <
|
Chris@16
|
233 typename boost::remove_reference<Geometry>::type
|
Chris@16
|
234 >::type point_type;
|
Chris@16
|
235
|
Chris@16
|
236 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
237 std::string const& wkt, Geometry out)
|
Chris@16
|
238 {
|
Chris@16
|
239 handle_open_parenthesis(it, end, wkt);
|
Chris@16
|
240
|
Chris@16
|
241 point_type point;
|
Chris@16
|
242
|
Chris@16
|
243 // Parse points until closing parenthesis
|
Chris@16
|
244
|
Chris@16
|
245 while (it != end && *it != ")")
|
Chris@16
|
246 {
|
Chris@16
|
247 parsing_assigner
|
Chris@16
|
248 <
|
Chris@16
|
249 point_type,
|
Chris@16
|
250 0,
|
Chris@16
|
251 dimension<point_type>::value
|
Chris@16
|
252 >::apply(it, end, point, wkt);
|
Chris@16
|
253
|
Chris@16
|
254 geometry::append(out, point);
|
Chris@16
|
255 if (it != end && *it == ",")
|
Chris@16
|
256 {
|
Chris@16
|
257 ++it;
|
Chris@16
|
258 }
|
Chris@16
|
259 }
|
Chris@16
|
260
|
Chris@16
|
261 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
262 }
|
Chris@16
|
263 };
|
Chris@16
|
264
|
Chris@16
|
265 /*!
|
Chris@16
|
266 \brief Internal, parses a point from a string like this "(x y)"
|
Chris@16
|
267 \note used for parsing points and multi-points
|
Chris@16
|
268 */
|
Chris@16
|
269 template <typename P>
|
Chris@16
|
270 struct point_parser
|
Chris@16
|
271 {
|
Chris@16
|
272 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
273 std::string const& wkt, P& point)
|
Chris@16
|
274 {
|
Chris@16
|
275 handle_open_parenthesis(it, end, wkt);
|
Chris@16
|
276 parsing_assigner<P, 0, dimension<P>::value>::apply(it, end, point, wkt);
|
Chris@16
|
277 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
278 }
|
Chris@16
|
279 };
|
Chris@16
|
280
|
Chris@16
|
281
|
Chris@16
|
282 template <typename Geometry>
|
Chris@16
|
283 struct linestring_parser
|
Chris@16
|
284 {
|
Chris@16
|
285 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
286 std::string const& wkt, Geometry& geometry)
|
Chris@16
|
287 {
|
Chris@16
|
288 container_appender<Geometry&>::apply(it, end, wkt, geometry);
|
Chris@16
|
289 }
|
Chris@16
|
290 };
|
Chris@16
|
291
|
Chris@16
|
292
|
Chris@16
|
293 template <typename Ring>
|
Chris@16
|
294 struct ring_parser
|
Chris@16
|
295 {
|
Chris@16
|
296 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
297 std::string const& wkt, Ring& ring)
|
Chris@16
|
298 {
|
Chris@16
|
299 // A ring should look like polygon((x y,x y,x y...))
|
Chris@16
|
300 // So handle the extra opening/closing parentheses
|
Chris@16
|
301 // and in between parse using the container-inserter
|
Chris@16
|
302 handle_open_parenthesis(it, end, wkt);
|
Chris@16
|
303 container_appender<Ring&>::apply(it, end, wkt, ring);
|
Chris@16
|
304 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
305 }
|
Chris@16
|
306 };
|
Chris@16
|
307
|
Chris@16
|
308
|
Chris@16
|
309
|
Chris@16
|
310
|
Chris@16
|
311 /*!
|
Chris@16
|
312 \brief Internal, parses a polygon from a string like this "((x y,x y),(x y,x y))"
|
Chris@16
|
313 \note used for parsing polygons and multi-polygons
|
Chris@16
|
314 */
|
Chris@16
|
315 template <typename Polygon>
|
Chris@16
|
316 struct polygon_parser
|
Chris@16
|
317 {
|
Chris@16
|
318 typedef typename ring_return_type<Polygon>::type ring_return_type;
|
Chris@16
|
319 typedef container_appender<ring_return_type> appender;
|
Chris@16
|
320
|
Chris@16
|
321 static inline void apply(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
322 std::string const& wkt, Polygon& poly)
|
Chris@16
|
323 {
|
Chris@16
|
324
|
Chris@16
|
325 handle_open_parenthesis(it, end, wkt);
|
Chris@16
|
326
|
Chris@16
|
327 int n = -1;
|
Chris@16
|
328
|
Chris@16
|
329 // Stop at ")"
|
Chris@16
|
330 while (it != end && *it != ")")
|
Chris@16
|
331 {
|
Chris@16
|
332 // Parse ring
|
Chris@16
|
333 if (++n == 0)
|
Chris@16
|
334 {
|
Chris@16
|
335 appender::apply(it, end, wkt, exterior_ring(poly));
|
Chris@16
|
336 }
|
Chris@16
|
337 else
|
Chris@16
|
338 {
|
Chris@16
|
339 typename ring_type<Polygon>::type ring;
|
Chris@16
|
340 appender::apply(it, end, wkt, ring);
|
Chris@16
|
341 traits::push_back
|
Chris@16
|
342 <
|
Chris@16
|
343 typename boost::remove_reference
|
Chris@16
|
344 <
|
Chris@16
|
345 typename traits::interior_mutable_type<Polygon>::type
|
Chris@16
|
346 >::type
|
Chris@16
|
347 >::apply(interior_rings(poly), ring);
|
Chris@16
|
348 }
|
Chris@16
|
349
|
Chris@16
|
350 if (it != end && *it == ",")
|
Chris@16
|
351 {
|
Chris@16
|
352 // Skip "," after ring is parsed
|
Chris@16
|
353 ++it;
|
Chris@16
|
354 }
|
Chris@16
|
355 }
|
Chris@16
|
356
|
Chris@16
|
357 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
358 }
|
Chris@16
|
359 };
|
Chris@16
|
360
|
Chris@16
|
361 inline bool one_of(tokenizer::iterator const& it, std::string const& value,
|
Chris@16
|
362 bool& is_present)
|
Chris@16
|
363 {
|
Chris@16
|
364 if (boost::iequals(*it, value))
|
Chris@16
|
365 {
|
Chris@16
|
366 is_present = true;
|
Chris@16
|
367 return true;
|
Chris@16
|
368 }
|
Chris@16
|
369 return false;
|
Chris@16
|
370 }
|
Chris@16
|
371
|
Chris@16
|
372 inline bool one_of(tokenizer::iterator const& it, std::string const& value,
|
Chris@16
|
373 bool& present1, bool& present2)
|
Chris@16
|
374 {
|
Chris@16
|
375 if (boost::iequals(*it, value))
|
Chris@16
|
376 {
|
Chris@16
|
377 present1 = true;
|
Chris@16
|
378 present2 = true;
|
Chris@16
|
379 return true;
|
Chris@16
|
380 }
|
Chris@16
|
381 return false;
|
Chris@16
|
382 }
|
Chris@16
|
383
|
Chris@16
|
384
|
Chris@16
|
385 inline void handle_empty_z_m(tokenizer::iterator& it, tokenizer::iterator end,
|
Chris@16
|
386 bool& has_empty, bool& has_z, bool& has_m)
|
Chris@16
|
387 {
|
Chris@16
|
388 has_empty = false;
|
Chris@16
|
389 has_z = false;
|
Chris@16
|
390 has_m = false;
|
Chris@16
|
391
|
Chris@16
|
392 // WKT can optionally have Z and M (measured) values as in
|
Chris@16
|
393 // POINT ZM (1 1 5 60), POINT M (1 1 80), POINT Z (1 1 5)
|
Chris@16
|
394 // GGL supports any of them as coordinate values, but is not aware
|
Chris@16
|
395 // of any Measured value.
|
Chris@16
|
396 while (it != end
|
Chris@16
|
397 && (one_of(it, "M", has_m)
|
Chris@16
|
398 || one_of(it, "Z", has_z)
|
Chris@16
|
399 || one_of(it, "EMPTY", has_empty)
|
Chris@16
|
400 || one_of(it, "MZ", has_m, has_z)
|
Chris@16
|
401 || one_of(it, "ZM", has_z, has_m)
|
Chris@16
|
402 )
|
Chris@16
|
403 )
|
Chris@16
|
404 {
|
Chris@16
|
405 ++it;
|
Chris@16
|
406 }
|
Chris@16
|
407 }
|
Chris@16
|
408
|
Chris@16
|
409 /*!
|
Chris@16
|
410 \brief Internal, starts parsing
|
Chris@16
|
411 \param tokens boost tokens, parsed with separator " " and keeping separator "()"
|
Chris@16
|
412 \param geometry string to compare with first token
|
Chris@16
|
413 */
|
Chris@16
|
414 template <typename Geometry>
|
Chris@16
|
415 inline bool initialize(tokenizer const& tokens,
|
Chris@16
|
416 std::string const& geometry_name, std::string const& wkt,
|
Chris@16
|
417 tokenizer::iterator& it)
|
Chris@16
|
418 {
|
Chris@16
|
419 it = tokens.begin();
|
Chris@16
|
420 if (it != tokens.end() && boost::iequals(*it++, geometry_name))
|
Chris@16
|
421 {
|
Chris@16
|
422 bool has_empty, has_z, has_m;
|
Chris@16
|
423
|
Chris@16
|
424 handle_empty_z_m(it, tokens.end(), has_empty, has_z, has_m);
|
Chris@16
|
425
|
Chris@16
|
426 // Silence warning C4127: conditional expression is constant
|
Chris@16
|
427 #if defined(_MSC_VER)
|
Chris@16
|
428 #pragma warning(push)
|
Chris@16
|
429 #pragma warning(disable : 4127)
|
Chris@16
|
430 #endif
|
Chris@16
|
431
|
Chris@16
|
432 if (has_z && dimension<Geometry>::type::value < 3)
|
Chris@16
|
433 {
|
Chris@16
|
434 throw read_wkt_exception("Z only allowed for 3 or more dimensions", wkt);
|
Chris@16
|
435 }
|
Chris@16
|
436
|
Chris@16
|
437 #if defined(_MSC_VER)
|
Chris@16
|
438 #pragma warning(pop)
|
Chris@16
|
439 #endif
|
Chris@16
|
440
|
Chris@16
|
441 if (has_empty)
|
Chris@16
|
442 {
|
Chris@16
|
443 check_end(it, tokens.end(), wkt);
|
Chris@16
|
444 return false;
|
Chris@16
|
445 }
|
Chris@16
|
446 // M is ignored at all.
|
Chris@16
|
447
|
Chris@16
|
448 return true;
|
Chris@16
|
449 }
|
Chris@16
|
450 throw read_wkt_exception(std::string("Should start with '") + geometry_name + "'", wkt);
|
Chris@16
|
451 }
|
Chris@16
|
452
|
Chris@16
|
453
|
Chris@16
|
454 template <typename Geometry, template<typename> class Parser, typename PrefixPolicy>
|
Chris@16
|
455 struct geometry_parser
|
Chris@16
|
456 {
|
Chris@16
|
457 static inline void apply(std::string const& wkt, Geometry& geometry)
|
Chris@16
|
458 {
|
Chris@16
|
459 geometry::clear(geometry);
|
Chris@16
|
460
|
Chris@16
|
461 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
|
Chris@16
|
462 tokenizer::iterator it;
|
Chris@16
|
463 if (initialize<Geometry>(tokens, PrefixPolicy::apply(), wkt, it))
|
Chris@16
|
464 {
|
Chris@16
|
465 Parser<Geometry>::apply(it, tokens.end(), wkt, geometry);
|
Chris@16
|
466 check_end(it, tokens.end(), wkt);
|
Chris@16
|
467 }
|
Chris@16
|
468 }
|
Chris@16
|
469 };
|
Chris@16
|
470
|
Chris@16
|
471
|
Chris@16
|
472
|
Chris@16
|
473
|
Chris@16
|
474
|
Chris@16
|
475 /*!
|
Chris@16
|
476 \brief Supports box parsing
|
Chris@16
|
477 \note OGC does not define the box geometry, and WKT does not support boxes.
|
Chris@16
|
478 However, to be generic GGL supports reading and writing from and to boxes.
|
Chris@16
|
479 Boxes are outputted as a standard POLYGON. GGL can read boxes from
|
Chris@16
|
480 a standard POLYGON, from a POLYGON with 2 points of from a BOX
|
Chris@16
|
481 \tparam Box the box
|
Chris@16
|
482 */
|
Chris@16
|
483 template <typename Box>
|
Chris@16
|
484 struct box_parser
|
Chris@16
|
485 {
|
Chris@16
|
486 static inline void apply(std::string const& wkt, Box& box)
|
Chris@16
|
487 {
|
Chris@16
|
488 bool should_close = false;
|
Chris@16
|
489 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
|
Chris@16
|
490 tokenizer::iterator it = tokens.begin();
|
Chris@16
|
491 tokenizer::iterator end = tokens.end();
|
Chris@16
|
492 if (it != end && boost::iequals(*it, "POLYGON"))
|
Chris@16
|
493 {
|
Chris@16
|
494 ++it;
|
Chris@16
|
495 bool has_empty, has_z, has_m;
|
Chris@16
|
496 handle_empty_z_m(it, end, has_empty, has_z, has_m);
|
Chris@16
|
497 if (has_empty)
|
Chris@16
|
498 {
|
Chris@16
|
499 assign_zero(box);
|
Chris@16
|
500 return;
|
Chris@16
|
501 }
|
Chris@16
|
502 handle_open_parenthesis(it, end, wkt);
|
Chris@16
|
503 should_close = true;
|
Chris@16
|
504 }
|
Chris@16
|
505 else if (it != end && boost::iequals(*it, "BOX"))
|
Chris@16
|
506 {
|
Chris@16
|
507 ++it;
|
Chris@16
|
508 }
|
Chris@16
|
509 else
|
Chris@16
|
510 {
|
Chris@16
|
511 throw read_wkt_exception("Should start with 'POLYGON' or 'BOX'", wkt);
|
Chris@16
|
512 }
|
Chris@16
|
513
|
Chris@16
|
514 typedef typename point_type<Box>::type point_type;
|
Chris@16
|
515 std::vector<point_type> points;
|
Chris@16
|
516 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
|
Chris@16
|
517
|
Chris@16
|
518 if (should_close)
|
Chris@16
|
519 {
|
Chris@16
|
520 handle_close_parenthesis(it, end, wkt);
|
Chris@16
|
521 }
|
Chris@16
|
522 check_end(it, end, wkt);
|
Chris@16
|
523
|
Chris@16
|
524 int index = 0;
|
Chris@16
|
525 int n = boost::size(points);
|
Chris@16
|
526 if (n == 2)
|
Chris@16
|
527 {
|
Chris@16
|
528 index = 1;
|
Chris@16
|
529 }
|
Chris@16
|
530 else if (n == 4 || n == 5)
|
Chris@16
|
531 {
|
Chris@16
|
532 // In case of 4 or 5 points, we do not check the other ones, just
|
Chris@16
|
533 // take the opposite corner which is always 2
|
Chris@16
|
534 index = 2;
|
Chris@16
|
535 }
|
Chris@16
|
536 else
|
Chris@16
|
537 {
|
Chris@16
|
538 throw read_wkt_exception("Box should have 2,4 or 5 points", wkt);
|
Chris@16
|
539 }
|
Chris@16
|
540
|
Chris@16
|
541 geometry::detail::assign_point_to_index<min_corner>(points.front(), box);
|
Chris@16
|
542 geometry::detail::assign_point_to_index<max_corner>(points[index], box);
|
Chris@16
|
543 }
|
Chris@16
|
544 };
|
Chris@16
|
545
|
Chris@16
|
546
|
Chris@16
|
547 /*!
|
Chris@16
|
548 \brief Supports segment parsing
|
Chris@16
|
549 \note OGC does not define the segment, and WKT does not support segmentes.
|
Chris@16
|
550 However, it is useful to implement it, also for testing purposes
|
Chris@16
|
551 \tparam Segment the segment
|
Chris@16
|
552 */
|
Chris@16
|
553 template <typename Segment>
|
Chris@16
|
554 struct segment_parser
|
Chris@16
|
555 {
|
Chris@16
|
556 static inline void apply(std::string const& wkt, Segment& segment)
|
Chris@16
|
557 {
|
Chris@16
|
558 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
|
Chris@16
|
559 tokenizer::iterator it = tokens.begin();
|
Chris@16
|
560 tokenizer::iterator end = tokens.end();
|
Chris@16
|
561 if (it != end &&
|
Chris@16
|
562 (boost::iequals(*it, "SEGMENT")
|
Chris@16
|
563 || boost::iequals(*it, "LINESTRING") ))
|
Chris@16
|
564 {
|
Chris@16
|
565 ++it;
|
Chris@16
|
566 }
|
Chris@16
|
567 else
|
Chris@16
|
568 {
|
Chris@16
|
569 throw read_wkt_exception("Should start with 'LINESTRING' or 'SEGMENT'", wkt);
|
Chris@16
|
570 }
|
Chris@16
|
571
|
Chris@16
|
572 typedef typename point_type<Segment>::type point_type;
|
Chris@16
|
573 std::vector<point_type> points;
|
Chris@16
|
574 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
|
Chris@16
|
575
|
Chris@16
|
576 check_end(it, end, wkt);
|
Chris@16
|
577
|
Chris@16
|
578 if (boost::size(points) == 2)
|
Chris@16
|
579 {
|
Chris@16
|
580 geometry::detail::assign_point_to_index<0>(points.front(), segment);
|
Chris@16
|
581 geometry::detail::assign_point_to_index<1>(points.back(), segment);
|
Chris@16
|
582 }
|
Chris@16
|
583 else
|
Chris@16
|
584 {
|
Chris@16
|
585 throw read_wkt_exception("Segment should have 2 points", wkt);
|
Chris@16
|
586 }
|
Chris@16
|
587
|
Chris@16
|
588 }
|
Chris@16
|
589 };
|
Chris@16
|
590
|
Chris@16
|
591
|
Chris@16
|
592
|
Chris@16
|
593 }} // namespace detail::wkt
|
Chris@16
|
594 #endif // DOXYGEN_NO_DETAIL
|
Chris@16
|
595
|
Chris@16
|
596 #ifndef DOXYGEN_NO_DISPATCH
|
Chris@16
|
597 namespace dispatch
|
Chris@16
|
598 {
|
Chris@16
|
599
|
Chris@16
|
600 template <typename Tag, typename Geometry>
|
Chris@16
|
601 struct read_wkt {};
|
Chris@16
|
602
|
Chris@16
|
603
|
Chris@16
|
604 template <typename Point>
|
Chris@16
|
605 struct read_wkt<point_tag, Point>
|
Chris@16
|
606 : detail::wkt::geometry_parser
|
Chris@16
|
607 <
|
Chris@16
|
608 Point,
|
Chris@16
|
609 detail::wkt::point_parser,
|
Chris@16
|
610 detail::wkt::prefix_point
|
Chris@16
|
611 >
|
Chris@16
|
612 {};
|
Chris@16
|
613
|
Chris@16
|
614
|
Chris@16
|
615 template <typename L>
|
Chris@16
|
616 struct read_wkt<linestring_tag, L>
|
Chris@16
|
617 : detail::wkt::geometry_parser
|
Chris@16
|
618 <
|
Chris@16
|
619 L,
|
Chris@16
|
620 detail::wkt::linestring_parser,
|
Chris@16
|
621 detail::wkt::prefix_linestring
|
Chris@16
|
622 >
|
Chris@16
|
623 {};
|
Chris@16
|
624
|
Chris@16
|
625 template <typename Ring>
|
Chris@16
|
626 struct read_wkt<ring_tag, Ring>
|
Chris@16
|
627 : detail::wkt::geometry_parser
|
Chris@16
|
628 <
|
Chris@16
|
629 Ring,
|
Chris@16
|
630 detail::wkt::ring_parser,
|
Chris@16
|
631 detail::wkt::prefix_polygon
|
Chris@16
|
632 >
|
Chris@16
|
633 {};
|
Chris@16
|
634
|
Chris@16
|
635 template <typename Geometry>
|
Chris@16
|
636 struct read_wkt<polygon_tag, Geometry>
|
Chris@16
|
637 : detail::wkt::geometry_parser
|
Chris@16
|
638 <
|
Chris@16
|
639 Geometry,
|
Chris@16
|
640 detail::wkt::polygon_parser,
|
Chris@16
|
641 detail::wkt::prefix_polygon
|
Chris@16
|
642 >
|
Chris@16
|
643 {};
|
Chris@16
|
644
|
Chris@16
|
645
|
Chris@16
|
646 // Box (Non-OGC)
|
Chris@16
|
647 template <typename Box>
|
Chris@16
|
648 struct read_wkt<box_tag, Box>
|
Chris@16
|
649 : detail::wkt::box_parser<Box>
|
Chris@16
|
650 {};
|
Chris@16
|
651
|
Chris@16
|
652 // Segment (Non-OGC)
|
Chris@16
|
653 template <typename Segment>
|
Chris@16
|
654 struct read_wkt<segment_tag, Segment>
|
Chris@16
|
655 : detail::wkt::segment_parser<Segment>
|
Chris@16
|
656 {};
|
Chris@16
|
657
|
Chris@16
|
658
|
Chris@16
|
659 } // namespace dispatch
|
Chris@16
|
660 #endif // DOXYGEN_NO_DISPATCH
|
Chris@16
|
661
|
Chris@16
|
662 /*!
|
Chris@16
|
663 \brief Parses OGC Well-Known Text (\ref WKT) into a geometry (any geometry)
|
Chris@16
|
664 \ingroup wkt
|
Chris@16
|
665 \param wkt string containing \ref WKT
|
Chris@16
|
666 \param geometry output geometry
|
Chris@16
|
667 \par Example:
|
Chris@16
|
668 \note It is case insensitive and can have the WKT forms "point", "point m", "point z", "point zm", "point mz"
|
Chris@16
|
669 \note Empty sequences can have forms as "LINESTRING ()" or "POLYGON(())"
|
Chris@16
|
670 Small example showing how to use read_wkt to build a point
|
Chris@16
|
671 \dontinclude doxygen_1.cpp
|
Chris@16
|
672 \skip example_from_wkt_point
|
Chris@16
|
673 \line {
|
Chris@16
|
674 \until }
|
Chris@16
|
675 \par Example:
|
Chris@16
|
676 Small example showing how to use read_wkt to build a linestring
|
Chris@16
|
677 \dontinclude doxygen_1.cpp
|
Chris@16
|
678 \skip example_from_wkt_linestring
|
Chris@16
|
679 \line {
|
Chris@16
|
680 \until }
|
Chris@16
|
681 \par Example:
|
Chris@16
|
682 Small example showing how to use read_wkt to build a polygon
|
Chris@16
|
683 \dontinclude doxygen_1.cpp
|
Chris@16
|
684 \skip example_from_wkt_polygon
|
Chris@16
|
685 \line {
|
Chris@16
|
686 \until }
|
Chris@16
|
687 */
|
Chris@16
|
688 template <typename Geometry>
|
Chris@16
|
689 inline void read_wkt(std::string const& wkt, Geometry& geometry)
|
Chris@16
|
690 {
|
Chris@16
|
691 geometry::concept::check<Geometry>();
|
Chris@16
|
692 dispatch::read_wkt<typename tag<Geometry>::type, Geometry>::apply(wkt, geometry);
|
Chris@16
|
693 }
|
Chris@16
|
694
|
Chris@16
|
695 }} // namespace boost::geometry
|
Chris@16
|
696
|
Chris@16
|
697 #endif // BOOST_GEOMETRY_IO_WKT_READ_HPP
|