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 | |
66 | namespace boost { namespace geometry |
67 | { |
68 | |
69 | /*! |
70 | \brief Exception showing things wrong with WKT parsing |
71 | \ingroup wkt |
72 | */ |
73 | struct 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 | } |
103 | private : |
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)) |
113 | namespace detail { namespace wkt |
114 | { |
115 | |
116 | inline 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 | |
124 | template <typename Point, |
125 | std::size_t Dimension = 0, |
126 | std::size_t DimensionCount = geometry::dimension<Point>::value> |
127 | struct 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 | |
170 | template <typename Point, std::size_t DimensionCount> |
171 | struct 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 | |
184 | template <typename Iterator> |
185 | inline 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 | |
197 | template <typename Iterator> |
198 | inline 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 | |
212 | template <typename Iterator> |
213 | inline 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 | */ |
229 | template <typename Point> |
230 | struct 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 | |
261 | template <typename Geometry, |
262 | closure_selector Closure = closure<Geometry>::value> |
263 | struct 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 | |
272 | template <typename Geometry> |
273 | struct 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 | |
312 | private: |
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 |
329 | template <typename Geometry> |
330 | struct 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 | */ |
369 | template <typename P> |
370 | struct 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 | |
385 | template <typename Geometry> |
386 | struct 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 | |
399 | template <typename Ring> |
400 | struct 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 | */ |
422 | template <typename Polygon> |
423 | struct 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 | |
466 | template <typename TokenizerIterator> |
467 | inline 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 | |
479 | template <typename TokenizerIterator> |
480 | inline 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 | |
495 | template <typename TokenizerIterator> |
496 | inline 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 | |
524 | template <typename Geometry, typename Tag = typename geometry::tag<Geometry>::type> |
525 | struct 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 |
531 | template <typename Geometry> |
532 | struct 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 | */ |
547 | template <typename Geometry, typename TokenizerIterator> |
548 | inline 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 | |
587 | template <typename Geometry, template<typename> class Parser, typename PrefixPolicy> |
588 | struct 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 | |
617 | template <typename MultiGeometry, template<typename> class Parser, typename PrefixPolicy> |
618 | struct 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 | |
663 | template <typename P> |
664 | struct 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 | |
676 | template <typename MultiGeometry, typename PrefixPolicy> |
677 | struct 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 | */ |
746 | template <typename Box> |
747 | struct 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 | */ |
827 | template <typename Segment> |
828 | struct 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 | |
875 | struct 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 | |
884 | struct 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 | |
893 | template |
894 | < |
895 | typename Geometry, |
896 | template <typename, typename> class ReadWkt, |
897 | typename AppendPolicy |
898 | > |
899 | struct 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 | |
966 | private: |
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 |
1022 | namespace dispatch |
1023 | { |
1024 | |
1025 | template <typename Geometry, typename Tag = typename tag<Geometry>::type> |
1026 | struct read_wkt {}; |
1027 | |
1028 | |
1029 | template <typename Point> |
1030 | struct 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 | |
1040 | template <typename L> |
1041 | struct 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 | |
1050 | template <typename Ring> |
1051 | struct 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 | |
1060 | template <typename Geometry> |
1061 | struct 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 | |
1071 | template <typename MultiGeometry> |
1072 | struct read_wkt<MultiGeometry, multi_point_tag> |
1073 | : detail::wkt::multi_point_parser |
1074 | < |
1075 | MultiGeometry, |
1076 | detail::wkt::prefix_multipoint |
1077 | > |
1078 | {}; |
1079 | |
1080 | template <typename MultiGeometry> |
1081 | struct 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 | |
1090 | template <typename MultiGeometry> |
1091 | struct 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) |
1102 | template <typename Box> |
1103 | struct read_wkt<Box, box_tag> |
1104 | : detail::wkt::box_parser<Box> |
1105 | {}; |
1106 | |
1107 | // Segment (Non-OGC) |
1108 | template <typename Segment> |
1109 | struct read_wkt<Segment, segment_tag> |
1110 | : detail::wkt::segment_parser<Segment> |
1111 | {}; |
1112 | |
1113 | |
1114 | template <typename DynamicGeometry> |
1115 | struct 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 | |
1139 | template <typename Geometry> |
1140 | struct 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 | */ |
1199 | template <typename Geometry> |
1200 | inline 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 | */ |
1214 | template <typename Geometry> |
1215 | inline 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 | |