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
56namespace boost { namespace geometry
57{
58
59/*!
60\brief Exception showing things wrong with WKT parsing
61\ingroup wkt
62*/
63struct 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(0, 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(0, 100) + ")";
87 }
88
89 virtual ~read_wkt_exception() throw() {}
90
91 virtual const char* what() const throw()
92 {
93 return complete.c_str();
94 }
95private :
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))
105namespace detail { namespace wkt
106{
107
108typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
109
110template <typename Point,
111 std::size_t Dimension = 0,
112 std::size_t DimensionCount = geometry::dimension<Point>::value>
113struct 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
155template <typename Point, std::size_t DimensionCount>
156struct 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
168template <typename Iterator>
169inline 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
181template <typename Iterator>
182inline 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
196template <typename Iterator>
197inline 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*/
213template <typename Point>
214struct 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
245template <typename Geometry,
246 closure_selector Closure = closure<Geometry>::value>
247struct 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
258template <typename Geometry>
259struct 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
303private:
304 size_type pt_index;
305 point_type first_point;
306};
307
308// Geometry is a value-type or reference-type
309template <typename Geometry>
310struct 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*/
348template <typename P>
349struct 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
363template <typename Geometry>
364struct 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
376template <typename Ring>
377struct 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*/
398template <typename Polygon>
399struct 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
447inline bool one_of(tokenizer::iterator const& it,
448 std::string const& value,
449 bool& is_present)
450{
451 if (boost::iequals(*it, value))
452 {
453 is_present = true;
454 return true;
455 }
456 return false;
457}
458
459inline bool one_of(tokenizer::iterator const& it,
460 std::string const& value,
461 bool& present1,
462 bool& present2)
463{
464 if (boost::iequals(*it, value))
465 {
466 present1 = true;
467 present2 = true;
468 return true;
469 }
470 return false;
471}
472
473
474inline 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, "M", has_m)
490 || one_of(it, "Z", has_z)
491 || one_of(it, "EMPTY", has_empty)
492 || one_of(it, "MZ", has_m, has_z)
493 || one_of(it, "ZM", has_z, 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*/
506template <typename Geometry>
507inline 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(*it++, 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
549template <typename Geometry, template<typename> class Parser, typename PrefixPolicy>
550struct 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
567template <typename MultiGeometry, template<typename> class Parser, typename PrefixPolicy>
568struct 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
602template <typename P>
603struct 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
614template <typename MultiGeometry, typename PrefixPolicy>
615struct 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*/
674template <typename Box>
675struct 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(*it, "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(*it, "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*/
744template <typename Segment>
745struct 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(*it, "SEGMENT")
754 || boost::iequals(*it, "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
787namespace dispatch
788{
789
790template <typename Tag, typename Geometry>
791struct read_wkt {};
792
793
794template <typename Point>
795struct 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
805template <typename L>
806struct 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
815template <typename Ring>
816struct 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
825template <typename Geometry>
826struct 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
836template <typename MultiGeometry>
837struct read_wkt<multi_point_tag, MultiGeometry>
838 : detail::wkt::multi_point_parser
839 <
840 MultiGeometry,
841 detail::wkt::prefix_multipoint
842 >
843{};
844
845template <typename MultiGeometry>
846struct 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
855template <typename MultiGeometry>
856struct 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)
867template <typename Box>
868struct read_wkt<box_tag, Box>
869 : detail::wkt::box_parser<Box>
870{};
871
872// Segment (Non-OGC)
873template <typename Segment>
874struct 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*/
891template <typename Geometry>
892inline 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