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