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, 2015, 2018.
9 // Modifications copyright (c) 2014-2018 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/mpl/if.hpp>
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>
35 #include <boost/throw_exception.hpp>
36 #include <boost/type_traits/is_same.hpp>
37 #include <boost/type_traits/remove_reference.hpp>
39 #include <boost/geometry/algorithms/assign.hpp>
40 #include <boost/geometry/algorithms/append.hpp>
41 #include <boost/geometry/algorithms/clear.hpp>
42 #include <boost/geometry/algorithms/detail/disjoint/point_point.hpp>
44 #include <boost/geometry/core/access.hpp>
45 #include <boost/geometry/core/coordinate_dimension.hpp>
46 #include <boost/geometry/core/exception.hpp>
47 #include <boost/geometry/core/exterior_ring.hpp>
48 #include <boost/geometry/core/geometry_id.hpp>
49 #include <boost/geometry/core/interior_rings.hpp>
50 #include <boost/geometry/core/mutable_range.hpp>
51 #include <boost/geometry/core/point_type.hpp>
52 #include <boost/geometry/core/tag_cast.hpp>
53 #include <boost/geometry/core/tags.hpp>
55 #include <boost/geometry/geometries/concepts/check.hpp>
57 #include <boost/geometry/util/coordinate_cast.hpp>
59 #include <boost/geometry/io/wkt/detail/prefix.hpp>
61 namespace boost { namespace geometry
65 \brief Exception showing things wrong with WKT parsing
68 struct read_wkt_exception : public geometry::exception
70 template <typename Iterator>
71 read_wkt_exception(std::string const& msg,
74 std::string const& wkt)
81 source += it->c_str();
84 complete = message + source + " in '" + wkt.substr(0, 100) + "'";
87 read_wkt_exception(std::string const& msg, std::string const& wkt)
91 complete = message + "' in (" + wkt.substr(0, 100) + ")";
94 virtual ~read_wkt_exception() throw() {}
96 virtual const char* what() const throw()
98 return complete.c_str();
104 std::string complete;
108 #ifndef DOXYGEN_NO_DETAIL
109 // (wkt: Well Known Text, defined by OGC for all geometries and implemented by e.g. databases (MySQL, PostGIS))
110 namespace detail { namespace wkt
113 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
115 template <typename Point,
116 std::size_t Dimension = 0,
117 std::size_t DimensionCount = geometry::dimension<Point>::value>
118 struct parsing_assigner
120 static inline void apply(tokenizer::iterator& it,
121 tokenizer::iterator const& end,
123 std::string const& wkt)
125 typedef typename coordinate_type<Point>::type coordinate_type;
127 // Stop at end of tokens, or at "," ot ")"
128 bool finished = (it == end || *it == "," || *it == ")");
132 // Initialize missing coordinates to default constructor (zero)
134 // Use lexical_cast for conversion to double/int
135 // Note that it is much slower than atof. However, it is more standard
136 // and in parsing the change in performance falls probably away against
138 set<Dimension>(point, finished
140 : coordinate_cast<coordinate_type>::apply(*it));
142 catch(boost::bad_lexical_cast const& blc)
144 BOOST_THROW_EXCEPTION(read_wkt_exception(blc.what(), it, end, wkt));
146 catch(std::exception const& e)
148 BOOST_THROW_EXCEPTION(read_wkt_exception(e.what(), it, end, wkt));
152 BOOST_THROW_EXCEPTION(read_wkt_exception("", it, end, wkt));
155 parsing_assigner<Point, Dimension + 1, DimensionCount>::apply(
156 (finished ? it : ++it), end, point, wkt);
160 template <typename Point, std::size_t DimensionCount>
161 struct parsing_assigner<Point, DimensionCount, DimensionCount>
163 static inline void apply(tokenizer::iterator&,
164 tokenizer::iterator const&,
173 template <typename Iterator>
174 inline void handle_open_parenthesis(Iterator& it,
176 std::string const& wkt)
178 if (it == end || *it != "(")
180 BOOST_THROW_EXCEPTION(read_wkt_exception("Expected '('", it, end, wkt));
186 template <typename Iterator>
187 inline void handle_close_parenthesis(Iterator& it,
189 std::string const& wkt)
191 if (it != end && *it == ")")
197 BOOST_THROW_EXCEPTION(read_wkt_exception("Expected ')'", it, end, wkt));
201 template <typename Iterator>
202 inline void check_end(Iterator& it,
204 std::string const& wkt)
208 BOOST_THROW_EXCEPTION(read_wkt_exception("Too many tokens", it, end, wkt));
213 \brief Internal, parses coordinate sequences, strings are formated like "(1 2,3 4,...)"
214 \param it token-iterator, should be pre-positioned at "(", is post-positions after last ")"
215 \param end end-token-iterator
216 \param out Output itererator receiving coordinates
218 template <typename Point>
219 struct container_inserter
221 // Version with output iterator
222 template <typename OutputIterator>
223 static inline void apply(tokenizer::iterator& it,
224 tokenizer::iterator const& end,
225 std::string const& wkt,
228 handle_open_parenthesis(it, end, wkt);
232 // Parse points until closing parenthesis
234 while (it != end && *it != ")")
236 parsing_assigner<Point>::apply(it, end, point, wkt);
239 if (it != end && *it == ",")
245 handle_close_parenthesis(it, end, wkt);
250 template <typename Geometry,
251 closure_selector Closure = closure<Geometry>::value>
252 struct stateful_range_appender
254 typedef typename geometry::point_type<Geometry>::type point_type;
256 // NOTE: Geometry is a reference
257 inline void append(Geometry geom, point_type const& point, bool)
259 geometry::append(geom, point);
263 template <typename Geometry>
264 struct stateful_range_appender<Geometry, open>
266 typedef typename geometry::point_type<Geometry>::type point_type;
267 typedef typename boost::range_size
269 typename util::bare_type<Geometry>::type
272 BOOST_STATIC_ASSERT(( boost::is_same
274 typename tag<Geometry>::type,
278 inline stateful_range_appender()
282 // NOTE: Geometry is a reference
283 inline void append(Geometry geom, point_type const& point, bool is_next_expected)
285 bool should_append = true;
290 //should_append = true;
294 // NOTE: if there is not enough Points, they're always appended
297 || pt_index < core_detail::closure::minimum_ring_size<open>::value
298 || disjoint(point, first_point);
304 geometry::append(geom, point);
309 static inline bool disjoint(point_type const& p1, point_type const& p2)
311 // TODO: pass strategy
312 typedef typename strategy::disjoint::services::default_strategy
314 point_type, point_type
315 >::type strategy_type;
317 return detail::disjoint::disjoint_point_point(p1, p2, strategy_type());
321 point_type first_point;
324 // Geometry is a value-type or reference-type
325 template <typename Geometry>
326 struct container_appender
328 typedef typename geometry::point_type<Geometry>::type point_type;
330 static inline void apply(tokenizer::iterator& it,
331 tokenizer::iterator const& end,
332 std::string const& wkt,
335 handle_open_parenthesis(it, end, wkt);
337 stateful_range_appender<Geometry> appender;
339 // Parse points until closing parenthesis
340 while (it != end && *it != ")")
344 parsing_assigner<point_type>::apply(it, end, point, wkt);
346 bool const is_next_expected = it != end && *it == ",";
348 appender.append(out, point, is_next_expected);
350 if (is_next_expected)
356 handle_close_parenthesis(it, end, wkt);
361 \brief Internal, parses a point from a string like this "(x y)"
362 \note used for parsing points and multi-points
364 template <typename P>
367 static inline void apply(tokenizer::iterator& it,
368 tokenizer::iterator const& end,
369 std::string const& wkt,
372 handle_open_parenthesis(it, end, wkt);
373 parsing_assigner<P>::apply(it, end, point, wkt);
374 handle_close_parenthesis(it, end, wkt);
379 template <typename Geometry>
380 struct linestring_parser
382 static inline void apply(tokenizer::iterator& it,
383 tokenizer::iterator const& end,
384 std::string const& wkt,
387 container_appender<Geometry&>::apply(it, end, wkt, geometry);
392 template <typename Ring>
395 static inline void apply(tokenizer::iterator& it,
396 tokenizer::iterator const& end,
397 std::string const& wkt,
400 // A ring should look like polygon((x y,x y,x y...))
401 // So handle the extra opening/closing parentheses
402 // and in between parse using the container-inserter
403 handle_open_parenthesis(it, end, wkt);
404 container_appender<Ring&>::apply(it, end, wkt, ring);
405 handle_close_parenthesis(it, end, wkt);
411 \brief Internal, parses a polygon from a string like this "((x y,x y),(x y,x y))"
412 \note used for parsing polygons and multi-polygons
414 template <typename Polygon>
415 struct polygon_parser
417 typedef typename ring_return_type<Polygon>::type ring_return_type;
418 typedef container_appender<ring_return_type> appender;
420 static inline void apply(tokenizer::iterator& it,
421 tokenizer::iterator const& end,
422 std::string const& wkt,
426 handle_open_parenthesis(it, end, wkt);
431 while (it != end && *it != ")")
436 appender::apply(it, end, wkt, exterior_ring(poly));
440 typename ring_type<Polygon>::type ring;
441 appender::apply(it, end, wkt, ring);
444 typename boost::remove_reference
446 typename traits::interior_mutable_type<Polygon>::type
448 >::apply(interior_rings(poly), ring);
451 if (it != end && *it == ",")
453 // Skip "," after ring is parsed
458 handle_close_parenthesis(it, end, wkt);
463 inline bool one_of(tokenizer::iterator const& it,
464 std::string const& value,
467 if (boost::iequals(*it, value))
475 inline bool one_of(tokenizer::iterator const& it,
476 std::string const& value,
480 if (boost::iequals(*it, value))
490 inline void handle_empty_z_m(tokenizer::iterator& it,
491 tokenizer::iterator const& end,
500 // WKT can optionally have Z and M (measured) values as in
501 // POINT ZM (1 1 5 60), POINT M (1 1 80), POINT Z (1 1 5)
502 // GGL supports any of them as coordinate values, but is not aware
503 // of any Measured value.
505 && (one_of(it, "M", has_m)
506 || one_of(it, "Z", has_z)
507 || one_of(it, "EMPTY", has_empty)
508 || one_of(it, "MZ", has_m, has_z)
509 || one_of(it, "ZM", has_z, has_m)
518 \brief Internal, starts parsing
519 \param tokens boost tokens, parsed with separator " " and keeping separator "()"
520 \param geometry string to compare with first token
522 template <typename Geometry>
523 inline bool initialize(tokenizer const& tokens,
524 std::string const& geometry_name,
525 std::string const& wkt,
526 tokenizer::iterator& it,
527 tokenizer::iterator& end)
532 if (it == end || ! boost::iequals(*it++, geometry_name))
534 BOOST_THROW_EXCEPTION(read_wkt_exception(std::string("Should start with '") + geometry_name + "'", wkt));
537 bool has_empty, has_z, has_m;
539 handle_empty_z_m(it, end, has_empty, has_z, has_m);
541 // Silence warning C4127: conditional expression is constant
542 #if defined(_MSC_VER)
543 #pragma warning(push)
544 #pragma warning(disable : 4127)
547 if (has_z && dimension<Geometry>::type::value < 3)
549 BOOST_THROW_EXCEPTION(read_wkt_exception("Z only allowed for 3 or more dimensions", wkt));
552 #if defined(_MSC_VER)
558 check_end(it, end, wkt);
561 // M is ignored at all.
567 template <typename Geometry, template<typename> class Parser, typename PrefixPolicy>
568 struct geometry_parser
570 static inline void apply(std::string const& wkt, Geometry& geometry)
572 geometry::clear(geometry);
574 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
575 tokenizer::iterator it, end;
576 if (initialize<Geometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
578 Parser<Geometry>::apply(it, end, wkt, geometry);
579 check_end(it, end, wkt);
585 template <typename MultiGeometry, template<typename> class Parser, typename PrefixPolicy>
588 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
590 traits::clear<MultiGeometry>::apply(geometry);
592 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
593 tokenizer::iterator it, end;
594 if (initialize<MultiGeometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
596 handle_open_parenthesis(it, end, wkt);
598 // Parse sub-geometries
599 while(it != end && *it != ")")
601 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
604 typename boost::range_value<MultiGeometry>::type
605 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
606 if (it != end && *it == ",")
608 // Skip "," after multi-element is parsed
613 handle_close_parenthesis(it, end, wkt);
616 check_end(it, end, wkt);
620 template <typename P>
621 struct noparenthesis_point_parser
623 static inline void apply(tokenizer::iterator& it,
624 tokenizer::iterator const& end,
625 std::string const& wkt,
628 parsing_assigner<P>::apply(it, end, point, wkt);
632 template <typename MultiGeometry, typename PrefixPolicy>
633 struct multi_point_parser
635 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
637 traits::clear<MultiGeometry>::apply(geometry);
639 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
640 tokenizer::iterator it, end;
642 if (initialize<MultiGeometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
644 handle_open_parenthesis(it, end, wkt);
646 // If first point definition starts with "(" then parse points as (x y)
647 // otherwise as "x y"
648 bool using_brackets = (it != end && *it == "(");
650 while(it != end && *it != ")")
652 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
658 typename boost::range_value<MultiGeometry>::type
659 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
663 noparenthesis_point_parser
665 typename boost::range_value<MultiGeometry>::type
666 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
669 if (it != end && *it == ",")
671 // Skip "," after point is parsed
676 handle_close_parenthesis(it, end, wkt);
679 check_end(it, end, wkt);
685 \brief Supports box parsing
686 \note OGC does not define the box geometry, and WKT does not support boxes.
687 However, to be generic GGL supports reading and writing from and to boxes.
688 Boxes are outputted as a standard POLYGON. GGL can read boxes from
689 a standard POLYGON, from a POLYGON with 2 points of from a BOX
692 template <typename Box>
695 static inline void apply(std::string const& wkt, Box& box)
697 bool should_close = false;
698 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
699 tokenizer::iterator it = tokens.begin();
700 tokenizer::iterator end = tokens.end();
701 if (it != end && boost::iequals(*it, "POLYGON"))
704 bool has_empty, has_z, has_m;
705 handle_empty_z_m(it, end, has_empty, has_z, has_m);
711 handle_open_parenthesis(it, end, wkt);
714 else if (it != end && boost::iequals(*it, "BOX"))
720 BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'POLYGON' or 'BOX'", wkt));
723 typedef typename point_type<Box>::type point_type;
724 std::vector<point_type> points;
725 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
729 handle_close_parenthesis(it, end, wkt);
731 check_end(it, end, wkt);
733 unsigned int index = 0;
734 std::size_t n = boost::size(points);
739 else if (n == 4 || n == 5)
741 // In case of 4 or 5 points, we do not check the other ones, just
742 // take the opposite corner which is always 2
747 BOOST_THROW_EXCEPTION(read_wkt_exception("Box should have 2,4 or 5 points", wkt));
750 geometry::detail::assign_point_to_index<min_corner>(points.front(), box);
751 geometry::detail::assign_point_to_index<max_corner>(points[index], box);
757 \brief Supports segment parsing
758 \note OGC does not define the segment, and WKT does not support segmentes.
759 However, it is useful to implement it, also for testing purposes
760 \tparam Segment the segment
762 template <typename Segment>
763 struct segment_parser
765 static inline void apply(std::string const& wkt, Segment& segment)
767 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
768 tokenizer::iterator it = tokens.begin();
769 tokenizer::iterator end = tokens.end();
771 (boost::iequals(*it, "SEGMENT")
772 || boost::iequals(*it, "LINESTRING") ))
778 BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'LINESTRING' or 'SEGMENT'", wkt));
781 typedef typename point_type<Segment>::type point_type;
782 std::vector<point_type> points;
783 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
785 check_end(it, end, wkt);
787 if (boost::size(points) == 2)
789 geometry::detail::assign_point_to_index<0>(points.front(), segment);
790 geometry::detail::assign_point_to_index<1>(points.back(), segment);
794 BOOST_THROW_EXCEPTION(read_wkt_exception("Segment should have 2 points", wkt));
801 }} // namespace detail::wkt
802 #endif // DOXYGEN_NO_DETAIL
804 #ifndef DOXYGEN_NO_DISPATCH
808 template <typename Tag, typename Geometry>
812 template <typename Point>
813 struct read_wkt<point_tag, Point>
814 : detail::wkt::geometry_parser
817 detail::wkt::point_parser,
818 detail::wkt::prefix_point
823 template <typename L>
824 struct read_wkt<linestring_tag, L>
825 : detail::wkt::geometry_parser
828 detail::wkt::linestring_parser,
829 detail::wkt::prefix_linestring
833 template <typename Ring>
834 struct read_wkt<ring_tag, Ring>
835 : detail::wkt::geometry_parser
838 detail::wkt::ring_parser,
839 detail::wkt::prefix_polygon
843 template <typename Geometry>
844 struct read_wkt<polygon_tag, Geometry>
845 : detail::wkt::geometry_parser
848 detail::wkt::polygon_parser,
849 detail::wkt::prefix_polygon
854 template <typename MultiGeometry>
855 struct read_wkt<multi_point_tag, MultiGeometry>
856 : detail::wkt::multi_point_parser
859 detail::wkt::prefix_multipoint
863 template <typename MultiGeometry>
864 struct read_wkt<multi_linestring_tag, MultiGeometry>
865 : detail::wkt::multi_parser
868 detail::wkt::linestring_parser,
869 detail::wkt::prefix_multilinestring
873 template <typename MultiGeometry>
874 struct read_wkt<multi_polygon_tag, MultiGeometry>
875 : detail::wkt::multi_parser
878 detail::wkt::polygon_parser,
879 detail::wkt::prefix_multipolygon
885 template <typename Box>
886 struct read_wkt<box_tag, Box>
887 : detail::wkt::box_parser<Box>
891 template <typename Segment>
892 struct read_wkt<segment_tag, Segment>
893 : detail::wkt::segment_parser<Segment>
897 } // namespace dispatch
898 #endif // DOXYGEN_NO_DISPATCH
901 \brief Parses OGC Well-Known Text (\ref WKT) into a geometry (any geometry)
903 \tparam Geometry \tparam_geometry
904 \param wkt string containing \ref WKT
905 \param geometry \param_geometry output geometry
907 \qbk{[include reference/io/read_wkt.qbk]}
909 template <typename Geometry>
910 inline void read_wkt(std::string const& wkt, Geometry& geometry)
912 geometry::concepts::check<Geometry>();
913 dispatch::read_wkt<typename tag<Geometry>::type, Geometry>::apply(wkt, geometry);
916 }} // namespace boost::geometry
918 #endif // BOOST_GEOMETRY_IO_WKT_READ_HPP