1// Boost.Geometry (aka GGL, Generic Geometry Library)
2
3// Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands.
4// Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
5// Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
6// Copyright (c) 2017-2023 Adam Wulkiewicz, Lodz, Poland.
7// Copyright (c) 2020 Baidyanath Kundu, Haldia, India
8
9// This file was modified by Oracle on 2014-2021.
10// Modifications copyright (c) 2014-2021 Oracle and/or its affiliates.
11// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
12
13// Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
14// (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
15
16// Use, modification and distribution is subject to the Boost Software License,
17// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
18// http://www.boost.org/LICENSE_1_0.txt)
19
20#ifndef BOOST_GEOMETRY_IO_WKT_READ_HPP
21#define BOOST_GEOMETRY_IO_WKT_READ_HPP
22
23#include <cstddef>
24#include <string>
25
26#include <boost/algorithm/string/predicate.hpp>
27#include <boost/lexical_cast.hpp>
28#include <boost/range/begin.hpp>
29#include <boost/range/end.hpp>
30#include <boost/range/size.hpp>
31#include <boost/range/value_type.hpp>
32#include <boost/tokenizer.hpp>
33#include <boost/throw_exception.hpp>
34
35#include <boost/geometry/algorithms/assign.hpp>
36#include <boost/geometry/algorithms/append.hpp>
37#include <boost/geometry/algorithms/clear.hpp>
38#include <boost/geometry/algorithms/detail/disjoint/point_point.hpp>
39
40#include <boost/geometry/core/access.hpp>
41#include <boost/geometry/core/coordinate_dimension.hpp>
42#include <boost/geometry/core/exception.hpp>
43#include <boost/geometry/core/exterior_ring.hpp>
44#include <boost/geometry/core/geometry_id.hpp>
45#include <boost/geometry/core/geometry_types.hpp>
46#include <boost/geometry/core/interior_rings.hpp>
47#include <boost/geometry/core/mutable_range.hpp>
48#include <boost/geometry/core/point_type.hpp>
49#include <boost/geometry/core/tag.hpp>
50#include <boost/geometry/core/tags.hpp>
51
52#include <boost/geometry/geometries/adapted/boost_variant.hpp> // For consistency with other functions
53#include <boost/geometry/geometries/concepts/check.hpp>
54
55#include <boost/geometry/io/wkt/detail/prefix.hpp>
56
57#include <boost/geometry/strategies/io/cartesian.hpp>
58#include <boost/geometry/strategies/io/geographic.hpp>
59#include <boost/geometry/strategies/io/spherical.hpp>
60
61#include <boost/geometry/util/coordinate_cast.hpp>
62#include <boost/geometry/util/range.hpp>
63#include <boost/geometry/util/sequence.hpp>
64#include <boost/geometry/util/type_traits.hpp>
65
66namespace boost { namespace geometry
67{
68
69/*!
70\brief Exception showing things wrong with WKT parsing
71\ingroup wkt
72*/
73struct read_wkt_exception : public geometry::exception
74{
75 template <typename Iterator>
76 read_wkt_exception(std::string const& msg,
77 Iterator const& it,
78 Iterator const& end,
79 std::string const& wkt)
80 : message(msg)
81 , wkt(wkt)
82 {
83 if (it != end)
84 {
85 source = " at '";
86 source += it->c_str();
87 source += "'";
88 }
89 complete = message + source + " in '" + wkt.substr(pos: 0, n: 100) + "'";
90 }
91
92 read_wkt_exception(std::string const& msg, std::string const& wkt)
93 : message(msg)
94 , wkt(wkt)
95 {
96 complete = message + "' in (" + wkt.substr(pos: 0, n: 100) + ")";
97 }
98
99 const char* what() const noexcept override
100 {
101 return complete.c_str();
102 }
103private :
104 std::string source;
105 std::string message;
106 std::string wkt;
107 std::string complete;
108};
109
110
111#ifndef DOXYGEN_NO_DETAIL
112// (wkt: Well Known Text, defined by OGC for all geometries and implemented by e.g. databases (MySQL, PostGIS))
113namespace detail { namespace wkt
114{
115
116inline auto make_tokenizer(std::string const& wkt)
117{
118 using separator = boost::char_separator<char>;
119 using tokenizer = boost::tokenizer<separator>;
120 const tokenizer tokens(wkt, separator(" \n\t\r", ",()"));
121 return tokens;
122}
123
124template <typename Point,
125 std::size_t Dimension = 0,
126 std::size_t DimensionCount = geometry::dimension<Point>::value>
127struct parsing_assigner
128{
129 template <typename TokenizerIterator>
130 static inline void apply(TokenizerIterator& it,
131 TokenizerIterator const& end,
132 Point& point,
133 std::string const& wkt)
134 {
135 using coordinate_type = typename coordinate_type<Point>::type;
136
137 // Stop at end of tokens, or at "," ot ")"
138 bool finished = (it == end || *it == "," || *it == ")");
139
140 try
141 {
142 // Initialize missing coordinates to default constructor (zero)
143 // OR
144 // Use lexical_cast for conversion to double/int
145 // Note that it is much slower than atof. However, it is more standard
146 // and in parsing the change in performance falls probably away against
147 // the tokenizing
148 set<Dimension>(point, finished
149 ? coordinate_type()
150 : coordinate_cast<coordinate_type>::apply(*it));
151 }
152 catch(boost::bad_lexical_cast const& blc)
153 {
154 BOOST_THROW_EXCEPTION(read_wkt_exception(blc.what(), it, end, wkt));
155 }
156 catch(std::exception const& e)
157 {
158 BOOST_THROW_EXCEPTION(read_wkt_exception(e.what(), it, end, wkt));
159 }
160 catch(...)
161 {
162 BOOST_THROW_EXCEPTION(read_wkt_exception("", it, end, wkt));
163 }
164
165 parsing_assigner<Point, Dimension + 1, DimensionCount>::apply(
166 (finished ? it : ++it), end, point, wkt);
167 }
168};
169
170template <typename Point, std::size_t DimensionCount>
171struct parsing_assigner<Point, DimensionCount, DimensionCount>
172{
173 template <typename TokenizerIterator>
174 static inline void apply(TokenizerIterator&,
175 TokenizerIterator const&,
176 Point&,
177 std::string const&)
178 {
179 }
180};
181
182
183
184template <typename Iterator>
185inline void handle_open_parenthesis(Iterator& it,
186 Iterator const& end,
187 std::string const& wkt)
188{
189 if (it == end || *it != "(")
190 {
191 BOOST_THROW_EXCEPTION(read_wkt_exception("Expected '('", it, end, wkt));
192 }
193 ++it;
194}
195
196
197template <typename Iterator>
198inline void handle_close_parenthesis(Iterator& it,
199 Iterator const& end,
200 std::string const& wkt)
201{
202 if (it != end && *it == ")")
203 {
204 ++it;
205 }
206 else
207 {
208 BOOST_THROW_EXCEPTION(read_wkt_exception("Expected ')'", it, end, wkt));
209 }
210}
211
212template <typename Iterator>
213inline void check_end(Iterator& it,
214 Iterator const& end,
215 std::string const& wkt)
216{
217 if (it != end)
218 {
219 BOOST_THROW_EXCEPTION(read_wkt_exception("Too many tokens", it, end, wkt));
220 }
221}
222
223/*!
224\brief Internal, parses coordinate sequences, strings are formated like "(1 2,3 4,...)"
225\param it token-iterator, should be pre-positioned at "(", is post-positions after last ")"
226\param end end-token-iterator
227\param out Output itererator receiving coordinates
228*/
229template <typename Point>
230struct container_inserter
231{
232 // Version with output iterator
233 template <typename TokenizerIterator, typename OutputIterator>
234 static inline void apply(TokenizerIterator& it,
235 TokenizerIterator const& end,
236 std::string const& wkt,
237 OutputIterator out)
238 {
239 handle_open_parenthesis(it, end, wkt);
240
241 Point point;
242
243 // Parse points until closing parenthesis
244
245 while (it != end && *it != ")")
246 {
247 parsing_assigner<Point>::apply(it, end, point, wkt);
248 out = point;
249 ++out;
250 if (it != end && *it == ",")
251 {
252 ++it;
253 }
254 }
255
256 handle_close_parenthesis(it, end, wkt);
257 }
258};
259
260
261template <typename Geometry,
262 closure_selector Closure = closure<Geometry>::value>
263struct stateful_range_appender
264{
265 // NOTE: Geometry is a reference
266 inline void append(Geometry geom, typename geometry::point_type<Geometry>::type const& point, bool)
267 {
268 geometry::append(geom, point);
269 }
270};
271
272template <typename Geometry>
273struct stateful_range_appender<Geometry, open>
274{
275 using point_type = typename geometry::point_type<Geometry>::type;
276 using size_type = typename boost::range_size
277 <
278 typename util::remove_cptrref<Geometry>::type
279 >::type;
280
281 BOOST_STATIC_ASSERT((util::is_ring<Geometry>::value));
282
283 inline stateful_range_appender()
284 : pt_index(0)
285 {}
286
287 // NOTE: Geometry is a reference
288 inline void append(Geometry geom, point_type const& point, bool is_next_expected)
289 {
290 bool should_append = true;
291
292 if (pt_index == 0)
293 {
294 first_point = point;
295 }
296 else
297 {
298 // NOTE: if there are not enough Points, they're always appended
299 should_append
300 = is_next_expected
301 || pt_index < core_detail::closure::minimum_ring_size<open>::value
302 || disjoint(p1: point, p2: first_point);
303 }
304 ++pt_index;
305
306 if (should_append)
307 {
308 geometry::append(geom, point);
309 }
310 }
311
312private:
313 static inline bool disjoint(point_type const& p1, point_type const& p2)
314 {
315 // TODO: pass strategy
316 using strategy_type = typename strategies::io::services::default_strategy
317 <
318 point_type
319 >::type;
320
321 return detail::disjoint::disjoint_point_point(p1, p2, strategy_type());
322 }
323
324 size_type pt_index;
325 point_type first_point;
326};
327
328// Geometry is a value-type or reference-type
329template <typename Geometry>
330struct container_appender
331{
332 using point_type = typename geometry::point_type<Geometry>::type;
333
334 template <typename TokenizerIterator>
335 static inline void apply(TokenizerIterator& it,
336 TokenizerIterator const& end,
337 std::string const& wkt,
338 Geometry out)
339 {
340 handle_open_parenthesis(it, end, wkt);
341
342 stateful_range_appender<Geometry> appender;
343
344 // Parse points until closing parenthesis
345 while (it != end && *it != ")")
346 {
347 point_type point;
348
349 parsing_assigner<point_type>::apply(it, end, point, wkt);
350
351 bool const is_next_expected = it != end && *it == ",";
352
353 appender.append(out, point, is_next_expected);
354
355 if (is_next_expected)
356 {
357 ++it;
358 }
359 }
360
361 handle_close_parenthesis(it, end, wkt);
362 }
363};
364
365/*!
366\brief Internal, parses a point from a string like this "(x y)"
367\note used for parsing points and multi-points
368*/
369template <typename P>
370struct point_parser
371{
372 template <typename TokenizerIterator>
373 static inline void apply(TokenizerIterator& it,
374 TokenizerIterator const& end,
375 std::string const& wkt,
376 P& point)
377 {
378 handle_open_parenthesis(it, end, wkt);
379 parsing_assigner<P>::apply(it, end, point, wkt);
380 handle_close_parenthesis(it, end, wkt);
381 }
382};
383
384
385template <typename Geometry>
386struct linestring_parser
387{
388 template <typename TokenizerIterator>
389 static inline void apply(TokenizerIterator& it,
390 TokenizerIterator const& end,
391 std::string const& wkt,
392 Geometry& geometry)
393 {
394 container_appender<Geometry&>::apply(it, end, wkt, geometry);
395 }
396};
397
398
399template <typename Ring>
400struct ring_parser
401{
402 template <typename TokenizerIterator>
403 static inline void apply(TokenizerIterator& it,
404 TokenizerIterator const& end,
405 std::string const& wkt,
406 Ring& ring)
407 {
408 // A ring should look like polygon((x y,x y,x y...))
409 // So handle the extra opening/closing parentheses
410 // and in between parse using the container-inserter
411 handle_open_parenthesis(it, end, wkt);
412 container_appender<Ring&>::apply(it, end, wkt, ring);
413 handle_close_parenthesis(it, end, wkt);
414 }
415};
416
417
418/*!
419\brief Internal, parses a polygon from a string like this "((x y,x y),(x y,x y))"
420\note used for parsing polygons and multi-polygons
421*/
422template <typename Polygon>
423struct polygon_parser
424{
425 using ring_return_type = typename ring_return_type<Polygon>::type;
426 using appender = container_appender<ring_return_type>;
427
428 template <typename TokenizerIterator>
429 static inline void apply(TokenizerIterator& it,
430 TokenizerIterator const& end,
431 std::string const& wkt,
432 Polygon& poly)
433 {
434
435 handle_open_parenthesis(it, end, wkt);
436
437 int n = -1;
438
439 // Stop at ")"
440 while (it != end && *it != ")")
441 {
442 // Parse ring
443 if (++n == 0)
444 {
445 appender::apply(it, end, wkt, exterior_ring(poly));
446 }
447 else
448 {
449 typename ring_type<Polygon>::type ring;
450 appender::apply(it, end, wkt, ring);
451 range::push_back(geometry::interior_rings(poly), std::move(ring));
452 }
453
454 if (it != end && *it == ",")
455 {
456 // Skip "," after ring is parsed
457 ++it;
458 }
459 }
460
461 handle_close_parenthesis(it, end, wkt);
462 }
463};
464
465
466template <typename TokenizerIterator>
467inline bool one_of(TokenizerIterator const& it,
468 std::string const& value,
469 bool& is_present)
470{
471 if (boost::iequals(*it, value))
472 {
473 is_present = true;
474 return true;
475 }
476 return false;
477}
478
479template <typename TokenizerIterator>
480inline bool one_of(TokenizerIterator const& it,
481 std::string const& value,
482 bool& present1,
483 bool& present2)
484{
485 if (boost::iequals(*it, value))
486 {
487 present1 = true;
488 present2 = true;
489 return true;
490 }
491 return false;
492}
493
494
495template <typename TokenizerIterator>
496inline void handle_empty_z_m(TokenizerIterator& it,
497 TokenizerIterator const& end,
498 bool& has_empty,
499 bool& has_z,
500 bool& has_m)
501{
502 has_empty = false;
503 has_z = false;
504 has_m = false;
505
506 // WKT can optionally have Z and M (measured) values as in
507 // POINT ZM (1 1 5 60), POINT M (1 1 80), POINT Z (1 1 5)
508 // GGL supports any of them as coordinate values, but is not aware
509 // of any Measured value.
510 while (it != end
511 && (one_of(it, "M", has_m)
512 || one_of(it, "Z", has_z)
513 || one_of(it, "EMPTY", has_empty)
514 || one_of(it, "MZ", has_m, has_z)
515 || one_of(it, "ZM", has_z, has_m)
516 )
517 )
518 {
519 ++it;
520 }
521}
522
523
524template <typename Geometry, typename Tag = typename geometry::tag<Geometry>::type>
525struct dimension
526 : geometry::dimension<Geometry>
527{};
528
529// TODO: For now assume the dimension of the first type defined for GC
530// This should probably be unified for all algorithms
531template <typename Geometry>
532struct dimension<Geometry, geometry_collection_tag>
533 : geometry::dimension
534 <
535 typename util::sequence_front
536 <
537 typename traits::geometry_types<Geometry>::type
538 >::type
539 >
540{};
541
542
543/*!
544\brief Internal, starts parsing
545\param geometry_name string to compare with first token
546*/
547template <typename Geometry, typename TokenizerIterator>
548inline bool initialize(TokenizerIterator& it,
549 TokenizerIterator const& end,
550 std::string const& wkt,
551 std::string const& geometry_name)
552{
553 if (it == end || ! boost::iequals(*it++, geometry_name))
554 {
555 BOOST_THROW_EXCEPTION(read_wkt_exception(std::string("Should start with '") + geometry_name + "'", wkt));
556 }
557
558 bool has_empty, has_z, has_m;
559
560 handle_empty_z_m(it, end, has_empty, has_z, has_m);
561
562// Silence warning C4127: conditional expression is constant
563#if defined(_MSC_VER)
564#pragma warning(push)
565#pragma warning(disable : 4127)
566#endif
567
568 if (has_z && dimension<Geometry>::value < 3)
569 {
570 BOOST_THROW_EXCEPTION(read_wkt_exception("Z only allowed for 3 or more dimensions", wkt));
571 }
572
573#if defined(_MSC_VER)
574#pragma warning(pop)
575#endif
576
577 if (has_empty)
578 {
579 return false;
580 }
581 // M is ignored at all.
582
583 return true;
584}
585
586
587template <typename Geometry, template<typename> class Parser, typename PrefixPolicy>
588struct geometry_parser
589{
590 static inline void apply(std::string const& wkt, Geometry& geometry)
591 {
592 geometry::clear(geometry);
593
594 auto const tokens{make_tokenizer(wkt)};
595 auto it = tokens.begin();
596 auto const end = tokens.end();
597
598 apply(it, end, wkt, geometry);
599
600 check_end(it, end, wkt);
601 }
602
603 template <typename TokenizerIterator>
604 static inline void apply(TokenizerIterator& it,
605 TokenizerIterator const& end,
606 std::string const& wkt,
607 Geometry& geometry)
608 {
609 if (initialize<Geometry>(it, end, wkt, PrefixPolicy::apply()))
610 {
611 Parser<Geometry>::apply(it, end, wkt, geometry);
612 }
613 }
614};
615
616
617template <typename MultiGeometry, template<typename> class Parser, typename PrefixPolicy>
618struct multi_parser
619{
620 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
621 {
622 traits::clear<MultiGeometry>::apply(geometry);
623
624 auto const tokens{make_tokenizer(wkt)};
625 auto it = tokens.begin();
626 auto const end = tokens.end();
627
628 apply(it, end, wkt, geometry);
629
630 check_end(it, end, wkt);
631 }
632
633 template <typename TokenizerIterator>
634 static inline void apply(TokenizerIterator& it,
635 TokenizerIterator const& end,
636 std::string const& wkt,
637 MultiGeometry& geometry)
638 {
639 if (initialize<MultiGeometry>(it, end, wkt, PrefixPolicy::apply()))
640 {
641 handle_open_parenthesis(it, end, wkt);
642
643 // Parse sub-geometries
644 while(it != end && *it != ")")
645 {
646 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
647 Parser
648 <
649 typename boost::range_value<MultiGeometry>::type
650 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
651 if (it != end && *it == ",")
652 {
653 // Skip "," after multi-element is parsed
654 ++it;
655 }
656 }
657
658 handle_close_parenthesis(it, end, wkt);
659 }
660 }
661};
662
663template <typename P>
664struct noparenthesis_point_parser
665{
666 template <typename TokenizerIterator>
667 static inline void apply(TokenizerIterator& it,
668 TokenizerIterator const& end,
669 std::string const& wkt,
670 P& point)
671 {
672 parsing_assigner<P>::apply(it, end, point, wkt);
673 }
674};
675
676template <typename MultiGeometry, typename PrefixPolicy>
677struct multi_point_parser
678{
679 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
680 {
681 traits::clear<MultiGeometry>::apply(geometry);
682
683 auto const tokens{make_tokenizer(wkt)};
684 auto it = tokens.begin();
685 auto const end = tokens.end();
686
687 apply(it, end, wkt, geometry);
688
689 check_end(it, end, wkt);
690 }
691
692 template <typename TokenizerIterator>
693 static inline void apply(TokenizerIterator& it,
694 TokenizerIterator const& end,
695 std::string const& wkt,
696 MultiGeometry& geometry)
697 {
698 if (initialize<MultiGeometry>(it, end, wkt, PrefixPolicy::apply()))
699 {
700 handle_open_parenthesis(it, end, wkt);
701
702 // If first point definition starts with "(" then parse points as (x y)
703 // otherwise as "x y"
704 bool using_brackets = (it != end && *it == "(");
705
706 while(it != end && *it != ")")
707 {
708 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
709
710 if (using_brackets)
711 {
712 point_parser
713 <
714 typename boost::range_value<MultiGeometry>::type
715 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
716 }
717 else
718 {
719 noparenthesis_point_parser
720 <
721 typename boost::range_value<MultiGeometry>::type
722 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
723 }
724
725 if (it != end && *it == ",")
726 {
727 // Skip "," after point is parsed
728 ++it;
729 }
730 }
731
732 handle_close_parenthesis(it, end, wkt);
733 }
734 }
735};
736
737
738/*!
739\brief Supports box parsing
740\note OGC does not define the box geometry, and WKT does not support boxes.
741 However, to be generic GGL supports reading and writing from and to boxes.
742 Boxes are outputted as a standard POLYGON. GGL can read boxes from
743 a standard POLYGON, from a POLYGON with 2 points of from a BOX
744\tparam Box the box
745*/
746template <typename Box>
747struct box_parser
748{
749 static inline void apply(std::string const& wkt, Box& box)
750 {
751 auto const tokens{make_tokenizer(wkt)};
752 auto it = tokens.begin();
753 auto const end = tokens.end();
754
755 apply(it, end, wkt, box);
756
757 check_end(it, end, wkt);
758 }
759
760 template <typename TokenizerIterator>
761 static inline void apply(TokenizerIterator& it,
762 TokenizerIterator const& end,
763 std::string const& wkt,
764 Box& box)
765 {
766 bool should_close = false;
767 if (it != end && boost::iequals(*it, "POLYGON"))
768 {
769 ++it;
770 bool has_empty, has_z, has_m;
771 handle_empty_z_m(it, end, has_empty, has_z, has_m);
772 if (has_empty)
773 {
774 assign_zero(box);
775 return;
776 }
777 handle_open_parenthesis(it, end, wkt);
778 should_close = true;
779 }
780 else if (it != end && boost::iequals(*it, "BOX"))
781 {
782 ++it;
783 }
784 else
785 {
786 BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'POLYGON' or 'BOX'", wkt));
787 }
788
789 using point_type = typename point_type<Box>::type;
790 std::vector<point_type> points;
791 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
792
793 if (should_close)
794 {
795 handle_close_parenthesis(it, end, wkt);
796 }
797
798 unsigned int index = 0;
799 std::size_t n = boost::size(points);
800 if (n == 2)
801 {
802 index = 1;
803 }
804 else if (n == 4 || n == 5)
805 {
806 // In case of 4 or 5 points, we do not check the other ones, just
807 // take the opposite corner which is always 2
808 index = 2;
809 }
810 else
811 {
812 BOOST_THROW_EXCEPTION(read_wkt_exception("Box should have 2,4 or 5 points", wkt));
813 }
814
815 geometry::detail::assign_point_to_index<min_corner>(points.front(), box);
816 geometry::detail::assign_point_to_index<max_corner>(points[index], box);
817 }
818};
819
820
821/*!
822\brief Supports segment parsing
823\note OGC does not define the segment, and WKT does not support segmentes.
824 However, it is useful to implement it, also for testing purposes
825\tparam Segment the segment
826*/
827template <typename Segment>
828struct segment_parser
829{
830 static inline void apply(std::string const& wkt, Segment& segment)
831 {
832 auto const tokens{make_tokenizer(wkt)};
833 auto it = tokens.begin();
834 auto const end = tokens.end();
835
836 apply(it, end, wkt, segment);
837
838 check_end(it, end, wkt);
839 }
840
841 template <typename TokenizerIterator>
842 static inline void apply(TokenizerIterator& it,
843 TokenizerIterator const& end,
844 std::string const& wkt,
845 Segment& segment)
846 {
847 if (it != end
848 && (boost::iequals(*it, prefix_segment::apply())
849 || boost::iequals(*it, prefix_linestring::apply())))
850 {
851 ++it;
852 }
853 else
854 {
855 BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'LINESTRING' or 'SEGMENT'", wkt));
856 }
857
858 using point_type = typename point_type<Segment>::type;
859 std::vector<point_type> points;
860 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
861
862 if (boost::size(points) == 2)
863 {
864 geometry::detail::assign_point_to_index<0>(points.front(), segment);
865 geometry::detail::assign_point_to_index<1>(points.back(), segment);
866 }
867 else
868 {
869 BOOST_THROW_EXCEPTION(read_wkt_exception("Segment should have 2 points", wkt));
870 }
871 }
872};
873
874
875struct dynamic_move_assign
876{
877 template <typename DynamicGeometry, typename Geometry>
878 static void apply(DynamicGeometry& dynamic_geometry, Geometry & geometry)
879 {
880 dynamic_geometry = std::move(geometry);
881 }
882};
883
884struct dynamic_move_emplace_back
885{
886 template <typename GeometryCollection, typename Geometry>
887 static void apply(GeometryCollection& geometry_collection, Geometry & geometry)
888 {
889 traits::emplace_back<GeometryCollection>::apply(geometry_collection, std::move(geometry));
890 }
891};
892
893template
894<
895 typename Geometry,
896 template <typename, typename> class ReadWkt,
897 typename AppendPolicy
898>
899struct dynamic_readwkt_caller
900{
901 template <typename TokenizerIterator>
902 static inline void apply(TokenizerIterator& it,
903 TokenizerIterator const& end,
904 std::string const& wkt,
905 Geometry& geometry)
906 {
907 static const char* tag_point = prefix_point::apply();
908 static const char* tag_linestring = prefix_linestring::apply();
909 static const char* tag_polygon = prefix_polygon::apply();
910
911 static const char* tag_multi_point = prefix_multipoint::apply();
912 static const char* tag_multi_linestring = prefix_multilinestring::apply();
913 static const char* tag_multi_polygon = prefix_multipolygon::apply();
914
915 static const char* tag_segment = prefix_segment::apply();
916 static const char* tag_box = prefix_box::apply();
917 static const char* tag_gc = prefix_geometrycollection::apply();
918
919 if (boost::iequals(*it, tag_point))
920 {
921 parse_geometry<util::is_point>(tag_point, it, end, wkt, geometry);
922 }
923 else if (boost::iequals(*it, tag_multi_point))
924 {
925 parse_geometry<util::is_multi_point>(tag_multi_point, it, end, wkt, geometry);
926 }
927 else if (boost::iequals(*it, tag_segment))
928 {
929 parse_geometry<util::is_segment>(tag_segment, it, end, wkt, geometry);
930 }
931 else if (boost::iequals(*it, tag_linestring))
932 {
933 parse_geometry<util::is_linestring>(tag_linestring, it, end, wkt, geometry, false)
934 || parse_geometry<util::is_segment>(tag_linestring, it, end, wkt, geometry);
935 }
936 else if (boost::iequals(*it, tag_multi_linestring))
937 {
938 parse_geometry<util::is_multi_linestring>(tag_multi_linestring, it, end, wkt, geometry);
939 }
940 else if (boost::iequals(*it, tag_box))
941 {
942 parse_geometry<util::is_box>(tag_box, it, end, wkt, geometry);
943 }
944 else if (boost::iequals(*it, tag_polygon))
945 {
946 parse_geometry<util::is_polygon>(tag_polygon, it, end, wkt, geometry, false)
947 || parse_geometry<util::is_ring>(tag_polygon, it, end, wkt, geometry, false)
948 || parse_geometry<util::is_box>(tag_polygon, it, end, wkt, geometry);
949 }
950 else if (boost::iequals(*it, tag_multi_polygon))
951 {
952 parse_geometry<util::is_multi_polygon>(tag_multi_polygon, it, end, wkt, geometry);
953 }
954 else if (boost::iequals(*it, tag_gc))
955 {
956 parse_geometry<util::is_geometry_collection>(tag_gc, it, end, wkt, geometry);
957 }
958 else
959 {
960 BOOST_THROW_EXCEPTION(read_wkt_exception(
961 "Should start with geometry's type, for example 'POINT', 'LINESTRING', 'POLYGON'",
962 wkt));
963 }
964 }
965
966private:
967 template
968 <
969 template <typename> class UnaryPred,
970 typename TokenizerIterator,
971 typename Geom = typename util::sequence_find_if
972 <
973 typename traits::geometry_types<Geometry>::type, UnaryPred
974 >::type,
975 std::enable_if_t<! std::is_void<Geom>::value, int> = 0
976 >
977 static bool parse_geometry(const char * ,
978 TokenizerIterator& it,
979 TokenizerIterator const& end,
980 std::string const& wkt,
981 Geometry& geometry,
982 bool = true)
983 {
984 Geom g;
985 ReadWkt<Geom, typename tag<Geom>::type>::apply(it, end, wkt, g);
986 AppendPolicy::apply(geometry, g);
987 return true;
988 }
989
990 template
991 <
992 template <typename> class UnaryPred,
993 typename TokenizerIterator,
994 typename Geom = typename util::sequence_find_if
995 <
996 typename traits::geometry_types<Geometry>::type, UnaryPred
997 >::type,
998 std::enable_if_t<std::is_void<Geom>::value, int> = 0
999 >
1000 static bool parse_geometry(const char * name,
1001 TokenizerIterator& ,
1002 TokenizerIterator const& ,
1003 std::string const& wkt,
1004 Geometry& ,
1005 bool throw_on_misfit = true)
1006 {
1007 if (throw_on_misfit)
1008 {
1009 std::string msg = std::string("Unable to store '") + name + "' in this geometry";
1010 BOOST_THROW_EXCEPTION(read_wkt_exception(msg, wkt));
1011 }
1012
1013 return false;
1014 }
1015};
1016
1017
1018}} // namespace detail::wkt
1019#endif // DOXYGEN_NO_DETAIL
1020
1021#ifndef DOXYGEN_NO_DISPATCH
1022namespace dispatch
1023{
1024
1025template <typename Geometry, typename Tag = typename tag<Geometry>::type>
1026struct read_wkt {};
1027
1028
1029template <typename Point>
1030struct read_wkt<Point, point_tag>
1031 : detail::wkt::geometry_parser
1032 <
1033 Point,
1034 detail::wkt::point_parser,
1035 detail::wkt::prefix_point
1036 >
1037{};
1038
1039
1040template <typename L>
1041struct read_wkt<L, linestring_tag>
1042 : detail::wkt::geometry_parser
1043 <
1044 L,
1045 detail::wkt::linestring_parser,
1046 detail::wkt::prefix_linestring
1047 >
1048{};
1049
1050template <typename Ring>
1051struct read_wkt<Ring, ring_tag>
1052 : detail::wkt::geometry_parser
1053 <
1054 Ring,
1055 detail::wkt::ring_parser,
1056 detail::wkt::prefix_polygon
1057 >
1058{};
1059
1060template <typename Geometry>
1061struct read_wkt<Geometry, polygon_tag>
1062 : detail::wkt::geometry_parser
1063 <
1064 Geometry,
1065 detail::wkt::polygon_parser,
1066 detail::wkt::prefix_polygon
1067 >
1068{};
1069
1070
1071template <typename MultiGeometry>
1072struct read_wkt<MultiGeometry, multi_point_tag>
1073 : detail::wkt::multi_point_parser
1074 <
1075 MultiGeometry,
1076 detail::wkt::prefix_multipoint
1077 >
1078{};
1079
1080template <typename MultiGeometry>
1081struct read_wkt<MultiGeometry, multi_linestring_tag>
1082 : detail::wkt::multi_parser
1083 <
1084 MultiGeometry,
1085 detail::wkt::linestring_parser,
1086 detail::wkt::prefix_multilinestring
1087 >
1088{};
1089
1090template <typename MultiGeometry>
1091struct read_wkt<MultiGeometry, multi_polygon_tag>
1092 : detail::wkt::multi_parser
1093 <
1094 MultiGeometry,
1095 detail::wkt::polygon_parser,
1096 detail::wkt::prefix_multipolygon
1097 >
1098{};
1099
1100
1101// Box (Non-OGC)
1102template <typename Box>
1103struct read_wkt<Box, box_tag>
1104 : detail::wkt::box_parser<Box>
1105{};
1106
1107// Segment (Non-OGC)
1108template <typename Segment>
1109struct read_wkt<Segment, segment_tag>
1110 : detail::wkt::segment_parser<Segment>
1111{};
1112
1113
1114template <typename DynamicGeometry>
1115struct read_wkt<DynamicGeometry, dynamic_geometry_tag>
1116{
1117 static inline void apply(std::string const& wkt, DynamicGeometry& dynamic_geometry)
1118 {
1119 auto tokens{detail::wkt::make_tokenizer(wkt)};
1120 auto it = tokens.begin();
1121 auto const end = tokens.end();
1122 if (it == end)
1123 {
1124 BOOST_THROW_EXCEPTION(read_wkt_exception(
1125 "Should start with geometry's type, for example 'POINT', 'LINESTRING', 'POLYGON'",
1126 wkt));
1127 }
1128
1129 detail::wkt::dynamic_readwkt_caller
1130 <
1131 DynamicGeometry, dispatch::read_wkt, detail::wkt::dynamic_move_assign
1132 >::apply(it, end, wkt, dynamic_geometry);
1133
1134 detail::wkt::check_end(it, end, wkt);
1135 }
1136};
1137
1138
1139template <typename Geometry>
1140struct read_wkt<Geometry, geometry_collection_tag>
1141{
1142 static inline void apply(std::string const& wkt, Geometry& geometry)
1143 {
1144 range::clear(geometry);
1145
1146 auto tokens{detail::wkt::make_tokenizer(wkt)};
1147 auto it = tokens.begin();
1148 auto const end = tokens.end();
1149
1150 apply(it, end, wkt, geometry);
1151
1152 detail::wkt::check_end(it, end, wkt);
1153 }
1154
1155 template <typename TokenizerIterator>
1156 static inline void apply(TokenizerIterator& it,
1157 TokenizerIterator const& end,
1158 std::string const& wkt,
1159 Geometry& geometry)
1160 {
1161 if (detail::wkt::initialize<Geometry>(it, end, wkt,
1162 detail::wkt::prefix_geometrycollection::apply()))
1163 {
1164 detail::wkt::handle_open_parenthesis(it, end, wkt);
1165
1166 // Stop at ")"
1167 while (it != end && *it != ")")
1168 {
1169 detail::wkt::dynamic_readwkt_caller
1170 <
1171 Geometry, dispatch::read_wkt, detail::wkt::dynamic_move_emplace_back
1172 >::apply(it, end, wkt, geometry);
1173
1174 if (it != end && *it == ",")
1175 {
1176 // Skip "," after geometry is parsed
1177 ++it;
1178 }
1179 }
1180
1181 detail::wkt::handle_close_parenthesis(it, end, wkt);
1182 }
1183 }
1184};
1185
1186
1187} // namespace dispatch
1188#endif // DOXYGEN_NO_DISPATCH
1189
1190/*!
1191\brief Parses OGC Well-Known Text (\ref WKT) into a geometry (any geometry)
1192\ingroup wkt
1193\tparam Geometry \tparam_geometry
1194\param wkt string containing \ref WKT
1195\param geometry \param_geometry output geometry
1196\ingroup wkt
1197\qbk{[include reference/io/read_wkt.qbk]}
1198*/
1199template <typename Geometry>
1200inline void read_wkt(std::string const& wkt, Geometry& geometry)
1201{
1202 geometry::concepts::check<Geometry>();
1203 dispatch::read_wkt<Geometry>::apply(wkt, geometry);
1204}
1205
1206/*!
1207\brief Parses OGC Well-Known Text (\ref WKT) into a geometry (any geometry) and returns it
1208\ingroup wkt
1209\tparam Geometry \tparam_geometry
1210\param wkt string containing \ref WKT
1211\ingroup wkt
1212\qbk{[include reference/io/from_wkt.qbk]}
1213*/
1214template <typename Geometry>
1215inline Geometry from_wkt(std::string const& wkt)
1216{
1217 Geometry geometry;
1218 geometry::concepts::check<Geometry>();
1219 dispatch::read_wkt<Geometry>::apply(wkt, geometry);
1220 return geometry;
1221}
1222
1223}} // namespace boost::geometry
1224
1225#endif // BOOST_GEOMETRY_IO_WKT_READ_HPP
1226

source code of boost/libs/geometry/include/boost/geometry/io/wkt/read.hpp