1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
5 // Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
6 // Copyright (c) 2017 Adam Wulkiewicz, Lodz, Poland.
8 // This file was modified by Oracle on 2014-2020.
9 // Modifications copyright (c) 2014-2020 Oracle and/or its affiliates.
11 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
13 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
14 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
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)
20 #ifndef BOOST_GEOMETRY_IO_WKT_READ_HPP
21 #define BOOST_GEOMETRY_IO_WKT_READ_HPP
26 #include <boost/lexical_cast.hpp>
27 #include <boost/tokenizer.hpp>
29 #include <boost/algorithm/string.hpp>
30 #include <boost/range/begin.hpp>
31 #include <boost/range/end.hpp>
32 #include <boost/range/size.hpp>
33 #include <boost/range/value_type.hpp>
34 #include <boost/throw_exception.hpp>
36 #include <boost/geometry/algorithms/assign.hpp>
37 #include <boost/geometry/algorithms/append.hpp>
38 #include <boost/geometry/algorithms/clear.hpp>
39 #include <boost/geometry/algorithms/detail/disjoint/point_point.hpp>
41 #include <boost/geometry/core/access.hpp>
42 #include <boost/geometry/core/coordinate_dimension.hpp>
43 #include <boost/geometry/core/exception.hpp>
44 #include <boost/geometry/core/exterior_ring.hpp>
45 #include <boost/geometry/core/geometry_id.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>
52 #include <boost/geometry/geometries/concepts/check.hpp>
54 #include <boost/geometry/io/wkt/detail/prefix.hpp>
56 #include <boost/geometry/util/coordinate_cast.hpp>
57 #include <boost/geometry/util/type_traits.hpp>
59 namespace boost { namespace geometry
63 \brief Exception showing things wrong with WKT parsing
66 struct read_wkt_exception : public geometry::exception
68 template <typename Iterator>
69 read_wkt_exception(std::string const& msg,
72 std::string const& wkt)
79 source += it->c_str();
82 complete = message + source + " in '" + wkt.substr(0, 100) + "'";
85 read_wkt_exception(std::string const& msg, std::string const& wkt)
89 complete = message + "' in (" + wkt.substr(0, 100) + ")";
92 virtual ~read_wkt_exception() throw() {}
94 virtual const char* what() const throw()
96 return complete.c_str();
102 std::string complete;
106 #ifndef DOXYGEN_NO_DETAIL
107 // (wkt: Well Known Text, defined by OGC for all geometries and implemented by e.g. databases (MySQL, PostGIS))
108 namespace detail { namespace wkt
111 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
113 template <typename Point,
114 std::size_t Dimension = 0,
115 std::size_t DimensionCount = geometry::dimension<Point>::value>
116 struct parsing_assigner
118 static inline void apply(tokenizer::iterator& it,
119 tokenizer::iterator const& end,
121 std::string const& wkt)
123 typedef typename coordinate_type<Point>::type coordinate_type;
125 // Stop at end of tokens, or at "," ot ")"
126 bool finished = (it == end || *it == "," || *it == ")");
130 // Initialize missing coordinates to default constructor (zero)
132 // Use lexical_cast for conversion to double/int
133 // Note that it is much slower than atof. However, it is more standard
134 // and in parsing the change in performance falls probably away against
136 set<Dimension>(point, finished
138 : coordinate_cast<coordinate_type>::apply(*it));
140 catch(boost::bad_lexical_cast const& blc)
142 BOOST_THROW_EXCEPTION(read_wkt_exception(blc.what(), it, end, wkt));
144 catch(std::exception const& e)
146 BOOST_THROW_EXCEPTION(read_wkt_exception(e.what(), it, end, wkt));
150 BOOST_THROW_EXCEPTION(read_wkt_exception("", it, end, wkt));
153 parsing_assigner<Point, Dimension + 1, DimensionCount>::apply(
154 (finished ? it : ++it), end, point, wkt);
158 template <typename Point, std::size_t DimensionCount>
159 struct parsing_assigner<Point, DimensionCount, DimensionCount>
161 static inline void apply(tokenizer::iterator&,
162 tokenizer::iterator const&,
171 template <typename Iterator>
172 inline void handle_open_parenthesis(Iterator& it,
174 std::string const& wkt)
176 if (it == end || *it != "(")
178 BOOST_THROW_EXCEPTION(read_wkt_exception("Expected '('", it, end, wkt));
184 template <typename Iterator>
185 inline void handle_close_parenthesis(Iterator& it,
187 std::string const& wkt)
189 if (it != end && *it == ")")
195 BOOST_THROW_EXCEPTION(read_wkt_exception("Expected ')'", it, end, wkt));
199 template <typename Iterator>
200 inline void check_end(Iterator& it,
202 std::string const& wkt)
206 BOOST_THROW_EXCEPTION(read_wkt_exception("Too many tokens", it, end, wkt));
211 \brief Internal, parses coordinate sequences, strings are formated like "(1 2,3 4,...)"
212 \param it token-iterator, should be pre-positioned at "(", is post-positions after last ")"
213 \param end end-token-iterator
214 \param out Output itererator receiving coordinates
216 template <typename Point>
217 struct container_inserter
219 // Version with output iterator
220 template <typename OutputIterator>
221 static inline void apply(tokenizer::iterator& it,
222 tokenizer::iterator const& end,
223 std::string const& wkt,
226 handle_open_parenthesis(it, end, wkt);
230 // Parse points until closing parenthesis
232 while (it != end && *it != ")")
234 parsing_assigner<Point>::apply(it, end, point, wkt);
237 if (it != end && *it == ",")
243 handle_close_parenthesis(it, end, wkt);
248 template <typename Geometry,
249 closure_selector Closure = closure<Geometry>::value>
250 struct stateful_range_appender
252 typedef typename geometry::point_type<Geometry>::type point_type;
254 // NOTE: Geometry is a reference
255 inline void append(Geometry geom, point_type const& point, bool)
257 geometry::append(geom, point);
261 template <typename Geometry>
262 struct stateful_range_appender<Geometry, open>
264 typedef typename geometry::point_type<Geometry>::type point_type;
265 typedef typename boost::range_size
267 typename util::remove_cptrref<Geometry>::type
270 BOOST_STATIC_ASSERT(( util::is_ring<Geometry>::value ));
272 inline stateful_range_appender()
276 // NOTE: Geometry is a reference
277 inline void append(Geometry geom, point_type const& point, bool is_next_expected)
279 bool should_append = true;
284 //should_append = true;
288 // NOTE: if there is not enough Points, they're always appended
291 || pt_index < core_detail::closure::minimum_ring_size<open>::value
292 || disjoint(point, first_point);
298 geometry::append(geom, point);
303 static inline bool disjoint(point_type const& p1, point_type const& p2)
305 // TODO: pass strategy
306 typedef typename strategy::disjoint::services::default_strategy
308 point_type, point_type
309 >::type strategy_type;
311 return detail::disjoint::disjoint_point_point(p1, p2, strategy_type());
315 point_type first_point;
318 // Geometry is a value-type or reference-type
319 template <typename Geometry>
320 struct container_appender
322 typedef typename geometry::point_type<Geometry>::type point_type;
324 static inline void apply(tokenizer::iterator& it,
325 tokenizer::iterator const& end,
326 std::string const& wkt,
329 handle_open_parenthesis(it, end, wkt);
331 stateful_range_appender<Geometry> appender;
333 // Parse points until closing parenthesis
334 while (it != end && *it != ")")
338 parsing_assigner<point_type>::apply(it, end, point, wkt);
340 bool const is_next_expected = it != end && *it == ",";
342 appender.append(out, point, is_next_expected);
344 if (is_next_expected)
350 handle_close_parenthesis(it, end, wkt);
355 \brief Internal, parses a point from a string like this "(x y)"
356 \note used for parsing points and multi-points
358 template <typename P>
361 static inline void apply(tokenizer::iterator& it,
362 tokenizer::iterator const& end,
363 std::string const& wkt,
366 handle_open_parenthesis(it, end, wkt);
367 parsing_assigner<P>::apply(it, end, point, wkt);
368 handle_close_parenthesis(it, end, wkt);
373 template <typename Geometry>
374 struct linestring_parser
376 static inline void apply(tokenizer::iterator& it,
377 tokenizer::iterator const& end,
378 std::string const& wkt,
381 container_appender<Geometry&>::apply(it, end, wkt, geometry);
386 template <typename Ring>
389 static inline void apply(tokenizer::iterator& it,
390 tokenizer::iterator const& end,
391 std::string const& wkt,
394 // A ring should look like polygon((x y,x y,x y...))
395 // So handle the extra opening/closing parentheses
396 // and in between parse using the container-inserter
397 handle_open_parenthesis(it, end, wkt);
398 container_appender<Ring&>::apply(it, end, wkt, ring);
399 handle_close_parenthesis(it, end, wkt);
405 \brief Internal, parses a polygon from a string like this "((x y,x y),(x y,x y))"
406 \note used for parsing polygons and multi-polygons
408 template <typename Polygon>
409 struct polygon_parser
411 typedef typename ring_return_type<Polygon>::type ring_return_type;
412 typedef container_appender<ring_return_type> appender;
414 static inline void apply(tokenizer::iterator& it,
415 tokenizer::iterator const& end,
416 std::string const& wkt,
420 handle_open_parenthesis(it, end, wkt);
425 while (it != end && *it != ")")
430 appender::apply(it, end, wkt, exterior_ring(poly));
434 typename ring_type<Polygon>::type ring;
435 appender::apply(it, end, wkt, ring);
438 typename std::remove_reference
440 typename traits::interior_mutable_type<Polygon>::type
442 >::apply(interior_rings(poly), ring);
445 if (it != end && *it == ",")
447 // Skip "," after ring is parsed
452 handle_close_parenthesis(it, end, wkt);
457 inline bool one_of(tokenizer::iterator const& it,
458 std::string const& value,
461 if (boost::iequals(*it, value))
469 inline bool one_of(tokenizer::iterator const& it,
470 std::string const& value,
474 if (boost::iequals(*it, value))
484 inline void handle_empty_z_m(tokenizer::iterator& it,
485 tokenizer::iterator const& end,
494 // WKT can optionally have Z and M (measured) values as in
495 // POINT ZM (1 1 5 60), POINT M (1 1 80), POINT Z (1 1 5)
496 // GGL supports any of them as coordinate values, but is not aware
497 // of any Measured value.
499 && (one_of(it, "M", has_m)
500 || one_of(it, "Z", has_z)
501 || one_of(it, "EMPTY", has_empty)
502 || one_of(it, "MZ", has_m, has_z)
503 || one_of(it, "ZM", has_z, has_m)
512 \brief Internal, starts parsing
513 \param tokens boost tokens, parsed with separator " " and keeping separator "()"
514 \param geometry string to compare with first token
516 template <typename Geometry>
517 inline bool initialize(tokenizer const& tokens,
518 std::string const& geometry_name,
519 std::string const& wkt,
520 tokenizer::iterator& it,
521 tokenizer::iterator& end)
526 if (it == end || ! boost::iequals(*it++, geometry_name))
528 BOOST_THROW_EXCEPTION(read_wkt_exception(std::string("Should start with '") + geometry_name + "'", wkt));
531 bool has_empty, has_z, has_m;
533 handle_empty_z_m(it, end, has_empty, has_z, has_m);
535 // Silence warning C4127: conditional expression is constant
536 #if defined(_MSC_VER)
537 #pragma warning(push)
538 #pragma warning(disable : 4127)
541 if (has_z && dimension<Geometry>::type::value < 3)
543 BOOST_THROW_EXCEPTION(read_wkt_exception("Z only allowed for 3 or more dimensions", wkt));
546 #if defined(_MSC_VER)
552 check_end(it, end, wkt);
555 // M is ignored at all.
561 template <typename Geometry, template<typename> class Parser, typename PrefixPolicy>
562 struct geometry_parser
564 static inline void apply(std::string const& wkt, Geometry& geometry)
566 geometry::clear(geometry);
568 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
569 tokenizer::iterator it, end;
570 if (initialize<Geometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
572 Parser<Geometry>::apply(it, end, wkt, geometry);
573 check_end(it, end, wkt);
579 template <typename MultiGeometry, template<typename> class Parser, typename PrefixPolicy>
582 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
584 traits::clear<MultiGeometry>::apply(geometry);
586 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
587 tokenizer::iterator it, end;
588 if (initialize<MultiGeometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
590 handle_open_parenthesis(it, end, wkt);
592 // Parse sub-geometries
593 while(it != end && *it != ")")
595 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
598 typename boost::range_value<MultiGeometry>::type
599 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
600 if (it != end && *it == ",")
602 // Skip "," after multi-element is parsed
607 handle_close_parenthesis(it, end, wkt);
610 check_end(it, end, wkt);
614 template <typename P>
615 struct noparenthesis_point_parser
617 static inline void apply(tokenizer::iterator& it,
618 tokenizer::iterator const& end,
619 std::string const& wkt,
622 parsing_assigner<P>::apply(it, end, point, wkt);
626 template <typename MultiGeometry, typename PrefixPolicy>
627 struct multi_point_parser
629 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
631 traits::clear<MultiGeometry>::apply(geometry);
633 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
634 tokenizer::iterator it, end;
636 if (initialize<MultiGeometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
638 handle_open_parenthesis(it, end, wkt);
640 // If first point definition starts with "(" then parse points as (x y)
641 // otherwise as "x y"
642 bool using_brackets = (it != end && *it == "(");
644 while(it != end && *it != ")")
646 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
652 typename boost::range_value<MultiGeometry>::type
653 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
657 noparenthesis_point_parser
659 typename boost::range_value<MultiGeometry>::type
660 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
663 if (it != end && *it == ",")
665 // Skip "," after point is parsed
670 handle_close_parenthesis(it, end, wkt);
673 check_end(it, end, wkt);
679 \brief Supports box parsing
680 \note OGC does not define the box geometry, and WKT does not support boxes.
681 However, to be generic GGL supports reading and writing from and to boxes.
682 Boxes are outputted as a standard POLYGON. GGL can read boxes from
683 a standard POLYGON, from a POLYGON with 2 points of from a BOX
686 template <typename Box>
689 static inline void apply(std::string const& wkt, Box& box)
691 bool should_close = false;
692 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
693 tokenizer::iterator it = tokens.begin();
694 tokenizer::iterator end = tokens.end();
695 if (it != end && boost::iequals(*it, "POLYGON"))
698 bool has_empty, has_z, has_m;
699 handle_empty_z_m(it, end, has_empty, has_z, has_m);
705 handle_open_parenthesis(it, end, wkt);
708 else if (it != end && boost::iequals(*it, "BOX"))
714 BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'POLYGON' or 'BOX'", wkt));
717 typedef typename point_type<Box>::type point_type;
718 std::vector<point_type> points;
719 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
723 handle_close_parenthesis(it, end, wkt);
725 check_end(it, end, wkt);
727 unsigned int index = 0;
728 std::size_t n = boost::size(points);
733 else if (n == 4 || n == 5)
735 // In case of 4 or 5 points, we do not check the other ones, just
736 // take the opposite corner which is always 2
741 BOOST_THROW_EXCEPTION(read_wkt_exception("Box should have 2,4 or 5 points", wkt));
744 geometry::detail::assign_point_to_index<min_corner>(points.front(), box);
745 geometry::detail::assign_point_to_index<max_corner>(points[index], box);
751 \brief Supports segment parsing
752 \note OGC does not define the segment, and WKT does not support segmentes.
753 However, it is useful to implement it, also for testing purposes
754 \tparam Segment the segment
756 template <typename Segment>
757 struct segment_parser
759 static inline void apply(std::string const& wkt, Segment& segment)
761 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
762 tokenizer::iterator it = tokens.begin();
763 tokenizer::iterator end = tokens.end();
765 (boost::iequals(*it, "SEGMENT")
766 || boost::iequals(*it, "LINESTRING") ))
772 BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'LINESTRING' or 'SEGMENT'", wkt));
775 typedef typename point_type<Segment>::type point_type;
776 std::vector<point_type> points;
777 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
779 check_end(it, end, wkt);
781 if (boost::size(points) == 2)
783 geometry::detail::assign_point_to_index<0>(points.front(), segment);
784 geometry::detail::assign_point_to_index<1>(points.back(), segment);
788 BOOST_THROW_EXCEPTION(read_wkt_exception("Segment should have 2 points", wkt));
795 }} // namespace detail::wkt
796 #endif // DOXYGEN_NO_DETAIL
798 #ifndef DOXYGEN_NO_DISPATCH
802 template <typename Tag, typename Geometry>
806 template <typename Point>
807 struct read_wkt<point_tag, Point>
808 : detail::wkt::geometry_parser
811 detail::wkt::point_parser,
812 detail::wkt::prefix_point
817 template <typename L>
818 struct read_wkt<linestring_tag, L>
819 : detail::wkt::geometry_parser
822 detail::wkt::linestring_parser,
823 detail::wkt::prefix_linestring
827 template <typename Ring>
828 struct read_wkt<ring_tag, Ring>
829 : detail::wkt::geometry_parser
832 detail::wkt::ring_parser,
833 detail::wkt::prefix_polygon
837 template <typename Geometry>
838 struct read_wkt<polygon_tag, Geometry>
839 : detail::wkt::geometry_parser
842 detail::wkt::polygon_parser,
843 detail::wkt::prefix_polygon
848 template <typename MultiGeometry>
849 struct read_wkt<multi_point_tag, MultiGeometry>
850 : detail::wkt::multi_point_parser
853 detail::wkt::prefix_multipoint
857 template <typename MultiGeometry>
858 struct read_wkt<multi_linestring_tag, MultiGeometry>
859 : detail::wkt::multi_parser
862 detail::wkt::linestring_parser,
863 detail::wkt::prefix_multilinestring
867 template <typename MultiGeometry>
868 struct read_wkt<multi_polygon_tag, MultiGeometry>
869 : detail::wkt::multi_parser
872 detail::wkt::polygon_parser,
873 detail::wkt::prefix_multipolygon
879 template <typename Box>
880 struct read_wkt<box_tag, Box>
881 : detail::wkt::box_parser<Box>
885 template <typename Segment>
886 struct read_wkt<segment_tag, Segment>
887 : detail::wkt::segment_parser<Segment>
891 } // namespace dispatch
892 #endif // DOXYGEN_NO_DISPATCH
895 \brief Parses OGC Well-Known Text (\ref WKT) into a geometry (any geometry)
897 \tparam Geometry \tparam_geometry
898 \param wkt string containing \ref WKT
899 \param geometry \param_geometry output geometry
901 \qbk{[include reference/io/read_wkt.qbk]}
903 template <typename Geometry>
904 inline void read_wkt(std::string const& wkt, Geometry& geometry)
906 geometry::concepts::check<Geometry>();
907 dispatch::read_wkt<typename tag<Geometry>::type, Geometry>::apply(wkt, geometry);
910 }} // namespace boost::geometry
912 #endif // BOOST_GEOMETRY_IO_WKT_READ_HPP