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.
9 // Modifications copyright (c) 2014-2015 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/equals/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 || !detail::equals::equals_point_point(point, first_point);
304 geometry::append(geom, point);
310 point_type first_point;
313 // Geometry is a value-type or reference-type
314 template <typename Geometry>
315 struct container_appender
317 typedef typename geometry::point_type<Geometry>::type point_type;
319 static inline void apply(tokenizer::iterator& it,
320 tokenizer::iterator const& end,
321 std::string const& wkt,
324 handle_open_parenthesis(it, end, wkt);
326 stateful_range_appender<Geometry> appender;
328 // Parse points until closing parenthesis
329 while (it != end && *it != ")")
333 parsing_assigner<point_type>::apply(it, end, point, wkt);
335 bool const is_next_expected = it != end && *it == ",";
337 appender.append(out, point, is_next_expected);
339 if (is_next_expected)
345 handle_close_parenthesis(it, end, wkt);
350 \brief Internal, parses a point from a string like this "(x y)"
351 \note used for parsing points and multi-points
353 template <typename P>
356 static inline void apply(tokenizer::iterator& it,
357 tokenizer::iterator const& end,
358 std::string const& wkt,
361 handle_open_parenthesis(it, end, wkt);
362 parsing_assigner<P>::apply(it, end, point, wkt);
363 handle_close_parenthesis(it, end, wkt);
368 template <typename Geometry>
369 struct linestring_parser
371 static inline void apply(tokenizer::iterator& it,
372 tokenizer::iterator const& end,
373 std::string const& wkt,
376 container_appender<Geometry&>::apply(it, end, wkt, geometry);
381 template <typename Ring>
384 static inline void apply(tokenizer::iterator& it,
385 tokenizer::iterator const& end,
386 std::string const& wkt,
389 // A ring should look like polygon((x y,x y,x y...))
390 // So handle the extra opening/closing parentheses
391 // and in between parse using the container-inserter
392 handle_open_parenthesis(it, end, wkt);
393 container_appender<Ring&>::apply(it, end, wkt, ring);
394 handle_close_parenthesis(it, end, wkt);
400 \brief Internal, parses a polygon from a string like this "((x y,x y),(x y,x y))"
401 \note used for parsing polygons and multi-polygons
403 template <typename Polygon>
404 struct polygon_parser
406 typedef typename ring_return_type<Polygon>::type ring_return_type;
407 typedef container_appender<ring_return_type> appender;
409 static inline void apply(tokenizer::iterator& it,
410 tokenizer::iterator const& end,
411 std::string const& wkt,
415 handle_open_parenthesis(it, end, wkt);
420 while (it != end && *it != ")")
425 appender::apply(it, end, wkt, exterior_ring(poly));
429 typename ring_type<Polygon>::type ring;
430 appender::apply(it, end, wkt, ring);
433 typename boost::remove_reference
435 typename traits::interior_mutable_type<Polygon>::type
437 >::apply(interior_rings(poly), ring);
440 if (it != end && *it == ",")
442 // Skip "," after ring is parsed
447 handle_close_parenthesis(it, end, wkt);
452 inline bool one_of(tokenizer::iterator const& it,
453 std::string const& value,
456 if (boost::iequals(*it, value))
464 inline bool one_of(tokenizer::iterator const& it,
465 std::string const& value,
469 if (boost::iequals(*it, value))
479 inline void handle_empty_z_m(tokenizer::iterator& it,
480 tokenizer::iterator const& end,
489 // WKT can optionally have Z and M (measured) values as in
490 // POINT ZM (1 1 5 60), POINT M (1 1 80), POINT Z (1 1 5)
491 // GGL supports any of them as coordinate values, but is not aware
492 // of any Measured value.
494 && (one_of(it, "M", has_m)
495 || one_of(it, "Z", has_z)
496 || one_of(it, "EMPTY", has_empty)
497 || one_of(it, "MZ", has_m, has_z)
498 || one_of(it, "ZM", has_z, has_m)
507 \brief Internal, starts parsing
508 \param tokens boost tokens, parsed with separator " " and keeping separator "()"
509 \param geometry string to compare with first token
511 template <typename Geometry>
512 inline bool initialize(tokenizer const& tokens,
513 std::string const& geometry_name,
514 std::string const& wkt,
515 tokenizer::iterator& it,
516 tokenizer::iterator& end)
520 if (it != end && boost::iequals(*it++, geometry_name))
522 bool has_empty, has_z, has_m;
524 handle_empty_z_m(it, end, has_empty, has_z, has_m);
526 // Silence warning C4127: conditional expression is constant
527 #if defined(_MSC_VER)
528 #pragma warning(push)
529 #pragma warning(disable : 4127)
532 if (has_z && dimension<Geometry>::type::value < 3)
534 BOOST_THROW_EXCEPTION(read_wkt_exception("Z only allowed for 3 or more dimensions", wkt));
537 #if defined(_MSC_VER)
543 check_end(it, end, wkt);
546 // M is ignored at all.
550 BOOST_THROW_EXCEPTION(read_wkt_exception(std::string("Should start with '") + geometry_name + "'", wkt));
554 template <typename Geometry, template<typename> class Parser, typename PrefixPolicy>
555 struct geometry_parser
557 static inline void apply(std::string const& wkt, Geometry& geometry)
559 geometry::clear(geometry);
561 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
562 tokenizer::iterator it, end;
563 if (initialize<Geometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
565 Parser<Geometry>::apply(it, end, wkt, geometry);
566 check_end(it, end, wkt);
572 template <typename MultiGeometry, template<typename> class Parser, typename PrefixPolicy>
575 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
577 traits::clear<MultiGeometry>::apply(geometry);
579 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
580 tokenizer::iterator it, end;
581 if (initialize<MultiGeometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
583 handle_open_parenthesis(it, end, wkt);
585 // Parse sub-geometries
586 while(it != end && *it != ")")
588 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
591 typename boost::range_value<MultiGeometry>::type
592 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
593 if (it != end && *it == ",")
595 // Skip "," after multi-element is parsed
600 handle_close_parenthesis(it, end, wkt);
603 check_end(it, end, wkt);
607 template <typename P>
608 struct noparenthesis_point_parser
610 static inline void apply(tokenizer::iterator& it,
611 tokenizer::iterator const& end,
612 std::string const& wkt,
615 parsing_assigner<P>::apply(it, end, point, wkt);
619 template <typename MultiGeometry, typename PrefixPolicy>
620 struct multi_point_parser
622 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
624 traits::clear<MultiGeometry>::apply(geometry);
626 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
627 tokenizer::iterator it, end;
629 if (initialize<MultiGeometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
631 handle_open_parenthesis(it, end, wkt);
633 // If first point definition starts with "(" then parse points as (x y)
634 // otherwise as "x y"
635 bool using_brackets = (it != end && *it == "(");
637 while(it != end && *it != ")")
639 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
645 typename boost::range_value<MultiGeometry>::type
646 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
650 noparenthesis_point_parser
652 typename boost::range_value<MultiGeometry>::type
653 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
656 if (it != end && *it == ",")
658 // Skip "," after point is parsed
663 handle_close_parenthesis(it, end, wkt);
666 check_end(it, end, wkt);
672 \brief Supports box parsing
673 \note OGC does not define the box geometry, and WKT does not support boxes.
674 However, to be generic GGL supports reading and writing from and to boxes.
675 Boxes are outputted as a standard POLYGON. GGL can read boxes from
676 a standard POLYGON, from a POLYGON with 2 points of from a BOX
679 template <typename Box>
682 static inline void apply(std::string const& wkt, Box& box)
684 bool should_close = false;
685 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
686 tokenizer::iterator it = tokens.begin();
687 tokenizer::iterator end = tokens.end();
688 if (it != end && boost::iequals(*it, "POLYGON"))
691 bool has_empty, has_z, has_m;
692 handle_empty_z_m(it, end, has_empty, has_z, has_m);
698 handle_open_parenthesis(it, end, wkt);
701 else if (it != end && boost::iequals(*it, "BOX"))
707 BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'POLYGON' or 'BOX'", wkt));
710 typedef typename point_type<Box>::type point_type;
711 std::vector<point_type> points;
712 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
716 handle_close_parenthesis(it, end, wkt);
718 check_end(it, end, wkt);
720 unsigned int index = 0;
721 std::size_t n = boost::size(points);
726 else if (n == 4 || n == 5)
728 // In case of 4 or 5 points, we do not check the other ones, just
729 // take the opposite corner which is always 2
734 BOOST_THROW_EXCEPTION(read_wkt_exception("Box should have 2,4 or 5 points", wkt));
737 geometry::detail::assign_point_to_index<min_corner>(points.front(), box);
738 geometry::detail::assign_point_to_index<max_corner>(points[index], box);
744 \brief Supports segment parsing
745 \note OGC does not define the segment, and WKT does not support segmentes.
746 However, it is useful to implement it, also for testing purposes
747 \tparam Segment the segment
749 template <typename Segment>
750 struct segment_parser
752 static inline void apply(std::string const& wkt, Segment& segment)
754 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
755 tokenizer::iterator it = tokens.begin();
756 tokenizer::iterator end = tokens.end();
758 (boost::iequals(*it, "SEGMENT")
759 || boost::iequals(*it, "LINESTRING") ))
765 BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'LINESTRING' or 'SEGMENT'", wkt));
768 typedef typename point_type<Segment>::type point_type;
769 std::vector<point_type> points;
770 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
772 check_end(it, end, wkt);
774 if (boost::size(points) == 2)
776 geometry::detail::assign_point_to_index<0>(points.front(), segment);
777 geometry::detail::assign_point_to_index<1>(points.back(), segment);
781 BOOST_THROW_EXCEPTION(read_wkt_exception("Segment should have 2 points", wkt));
788 }} // namespace detail::wkt
789 #endif // DOXYGEN_NO_DETAIL
791 #ifndef DOXYGEN_NO_DISPATCH
795 template <typename Tag, typename Geometry>
799 template <typename Point>
800 struct read_wkt<point_tag, Point>
801 : detail::wkt::geometry_parser
804 detail::wkt::point_parser,
805 detail::wkt::prefix_point
810 template <typename L>
811 struct read_wkt<linestring_tag, L>
812 : detail::wkt::geometry_parser
815 detail::wkt::linestring_parser,
816 detail::wkt::prefix_linestring
820 template <typename Ring>
821 struct read_wkt<ring_tag, Ring>
822 : detail::wkt::geometry_parser
825 detail::wkt::ring_parser,
826 detail::wkt::prefix_polygon
830 template <typename Geometry>
831 struct read_wkt<polygon_tag, Geometry>
832 : detail::wkt::geometry_parser
835 detail::wkt::polygon_parser,
836 detail::wkt::prefix_polygon
841 template <typename MultiGeometry>
842 struct read_wkt<multi_point_tag, MultiGeometry>
843 : detail::wkt::multi_point_parser
846 detail::wkt::prefix_multipoint
850 template <typename MultiGeometry>
851 struct read_wkt<multi_linestring_tag, MultiGeometry>
852 : detail::wkt::multi_parser
855 detail::wkt::linestring_parser,
856 detail::wkt::prefix_multilinestring
860 template <typename MultiGeometry>
861 struct read_wkt<multi_polygon_tag, MultiGeometry>
862 : detail::wkt::multi_parser
865 detail::wkt::polygon_parser,
866 detail::wkt::prefix_multipolygon
872 template <typename Box>
873 struct read_wkt<box_tag, Box>
874 : detail::wkt::box_parser<Box>
878 template <typename Segment>
879 struct read_wkt<segment_tag, Segment>
880 : detail::wkt::segment_parser<Segment>
884 } // namespace dispatch
885 #endif // DOXYGEN_NO_DISPATCH
888 \brief Parses OGC Well-Known Text (\ref WKT) into a geometry (any geometry)
890 \tparam Geometry \tparam_geometry
891 \param wkt string containing \ref WKT
892 \param geometry \param_geometry output geometry
894 \qbk{[include reference/io/read_wkt.qbk]}
896 template <typename Geometry>
897 inline void read_wkt(std::string const& wkt, Geometry& geometry)
899 geometry::concepts::check<Geometry>();
900 dispatch::read_wkt<typename tag<Geometry>::type, Geometry>::apply(wkt, geometry);
903 }} // namespace boost::geometry
905 #endif // BOOST_GEOMETRY_IO_WKT_READ_HPP