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