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.
7 // This file was modified by Oracle on 2014, 2015.
8 // Modifications copyright (c) 2014-2015 Oracle and/or its affiliates.
10 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
12 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
13 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
15 // Use, modification and distribution is subject to the Boost Software License,
16 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
17 // http://www.boost.org/LICENSE_1_0.txt)
19 #ifndef BOOST_GEOMETRY_IO_WKT_READ_HPP
20 #define BOOST_GEOMETRY_IO_WKT_READ_HPP
25 #include <boost/lexical_cast.hpp>
26 #include <boost/tokenizer.hpp>
28 #include <boost/algorithm/string.hpp>
29 #include <boost/mpl/if.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/type_traits/is_same.hpp>
35 #include <boost/type_traits/remove_reference.hpp>
37 #include <boost/geometry/algorithms/assign.hpp>
38 #include <boost/geometry/algorithms/append.hpp>
39 #include <boost/geometry/algorithms/clear.hpp>
40 #include <boost/geometry/algorithms/detail/equals/point_point.hpp>
42 #include <boost/geometry/core/access.hpp>
43 #include <boost/geometry/core/coordinate_dimension.hpp>
44 #include <boost/geometry/core/exception.hpp>
45 #include <boost/geometry/core/exterior_ring.hpp>
46 #include <boost/geometry/core/geometry_id.hpp>
47 #include <boost/geometry/core/interior_rings.hpp>
48 #include <boost/geometry/core/mutable_range.hpp>
49 #include <boost/geometry/core/point_type.hpp>
50 #include <boost/geometry/core/tag_cast.hpp>
51 #include <boost/geometry/core/tags.hpp>
53 #include <boost/geometry/geometries/concepts/check.hpp>
55 #include <boost/geometry/util/coordinate_cast.hpp>
57 #include <boost/geometry/io/wkt/detail/prefix.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 throw read_wkt_exception(blc.what(), it, end, wkt);
144 catch(std::exception const& e)
146 throw read_wkt_exception(e.what(), it, end, wkt);
150 throw 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 throw 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 throw read_wkt_exception("Expected ')'", it, end, wkt);
199 template <typename Iterator>
200 inline void check_end(Iterator& it,
202 std::string const& wkt)
206 throw read_wkt_exception("Too much 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::bare_type<Geometry>::type
270 BOOST_STATIC_ASSERT(( boost::is_same
272 typename tag<Geometry>::type,
276 inline stateful_range_appender()
280 // NOTE: Geometry is a reference
281 inline void append(Geometry geom, point_type const& point, bool is_next_expected)
283 bool should_append = true;
288 //should_append = true;
292 // NOTE: if there is not enough Points, they're always appended
295 || pt_index < core_detail::closure::minimum_ring_size<open>::value
296 || !detail::equals::equals_point_point(point, first_point);
302 geometry::append(geom, point);
308 point_type first_point;
311 // Geometry is a value-type or reference-type
312 template <typename Geometry>
313 struct container_appender
315 typedef typename geometry::point_type<Geometry>::type point_type;
317 static inline void apply(tokenizer::iterator& it,
318 tokenizer::iterator const& end,
319 std::string const& wkt,
322 handle_open_parenthesis(it, end, wkt);
324 stateful_range_appender<Geometry> appender;
326 // Parse points until closing parenthesis
327 while (it != end && *it != ")")
331 parsing_assigner<point_type>::apply(it, end, point, wkt);
333 bool const is_next_expected = it != end && *it == ",";
335 appender.append(out, point, is_next_expected);
337 if (is_next_expected)
343 handle_close_parenthesis(it, end, wkt);
348 \brief Internal, parses a point from a string like this "(x y)"
349 \note used for parsing points and multi-points
351 template <typename P>
354 static inline void apply(tokenizer::iterator& it,
355 tokenizer::iterator const& end,
356 std::string const& wkt,
359 handle_open_parenthesis(it, end, wkt);
360 parsing_assigner<P>::apply(it, end, point, wkt);
361 handle_close_parenthesis(it, end, wkt);
366 template <typename Geometry>
367 struct linestring_parser
369 static inline void apply(tokenizer::iterator& it,
370 tokenizer::iterator const& end,
371 std::string const& wkt,
374 container_appender<Geometry&>::apply(it, end, wkt, geometry);
379 template <typename Ring>
382 static inline void apply(tokenizer::iterator& it,
383 tokenizer::iterator const& end,
384 std::string const& wkt,
387 // A ring should look like polygon((x y,x y,x y...))
388 // So handle the extra opening/closing parentheses
389 // and in between parse using the container-inserter
390 handle_open_parenthesis(it, end, wkt);
391 container_appender<Ring&>::apply(it, end, wkt, ring);
392 handle_close_parenthesis(it, end, wkt);
398 \brief Internal, parses a polygon from a string like this "((x y,x y),(x y,x y))"
399 \note used for parsing polygons and multi-polygons
401 template <typename Polygon>
402 struct polygon_parser
404 typedef typename ring_return_type<Polygon>::type ring_return_type;
405 typedef container_appender<ring_return_type> appender;
407 static inline void apply(tokenizer::iterator& it,
408 tokenizer::iterator const& end,
409 std::string const& wkt,
413 handle_open_parenthesis(it, end, wkt);
418 while (it != end && *it != ")")
423 appender::apply(it, end, wkt, exterior_ring(poly));
427 typename ring_type<Polygon>::type ring;
428 appender::apply(it, end, wkt, ring);
431 typename boost::remove_reference
433 typename traits::interior_mutable_type<Polygon>::type
435 >::apply(interior_rings(poly), ring);
438 if (it != end && *it == ",")
440 // Skip "," after ring is parsed
445 handle_close_parenthesis(it, end, wkt);
450 inline bool one_of(tokenizer::iterator const& it,
451 std::string const& value,
454 if (boost::iequals(*it, value))
462 inline bool one_of(tokenizer::iterator const& it,
463 std::string const& value,
467 if (boost::iequals(*it, value))
477 inline void handle_empty_z_m(tokenizer::iterator& it,
478 tokenizer::iterator const& end,
487 // WKT can optionally have Z and M (measured) values as in
488 // POINT ZM (1 1 5 60), POINT M (1 1 80), POINT Z (1 1 5)
489 // GGL supports any of them as coordinate values, but is not aware
490 // of any Measured value.
492 && (one_of(it, "M", has_m)
493 || one_of(it, "Z", has_z)
494 || one_of(it, "EMPTY", has_empty)
495 || one_of(it, "MZ", has_m, has_z)
496 || one_of(it, "ZM", has_z, has_m)
505 \brief Internal, starts parsing
506 \param tokens boost tokens, parsed with separator " " and keeping separator "()"
507 \param geometry string to compare with first token
509 template <typename Geometry>
510 inline bool initialize(tokenizer const& tokens,
511 std::string const& geometry_name,
512 std::string const& wkt,
513 tokenizer::iterator& it,
514 tokenizer::iterator& end)
518 if (it != end && boost::iequals(*it++, geometry_name))
520 bool has_empty, has_z, has_m;
522 handle_empty_z_m(it, end, has_empty, has_z, has_m);
524 // Silence warning C4127: conditional expression is constant
525 #if defined(_MSC_VER)
526 #pragma warning(push)
527 #pragma warning(disable : 4127)
530 if (has_z && dimension<Geometry>::type::value < 3)
532 throw read_wkt_exception("Z only allowed for 3 or more dimensions", wkt);
535 #if defined(_MSC_VER)
541 check_end(it, end, wkt);
544 // M is ignored at all.
548 throw read_wkt_exception(std::string("Should start with '") + geometry_name + "'", wkt);
552 template <typename Geometry, template<typename> class Parser, typename PrefixPolicy>
553 struct geometry_parser
555 static inline void apply(std::string const& wkt, Geometry& geometry)
557 geometry::clear(geometry);
559 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
560 tokenizer::iterator it, end;
561 if (initialize<Geometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
563 Parser<Geometry>::apply(it, end, wkt, geometry);
564 check_end(it, end, wkt);
570 template <typename MultiGeometry, template<typename> class Parser, typename PrefixPolicy>
573 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
575 traits::clear<MultiGeometry>::apply(geometry);
577 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
578 tokenizer::iterator it, end;
579 if (initialize<MultiGeometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
581 handle_open_parenthesis(it, end, wkt);
583 // Parse sub-geometries
584 while(it != end && *it != ")")
586 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
589 typename boost::range_value<MultiGeometry>::type
590 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
591 if (it != end && *it == ",")
593 // Skip "," after multi-element is parsed
598 handle_close_parenthesis(it, end, wkt);
601 check_end(it, end, wkt);
605 template <typename P>
606 struct noparenthesis_point_parser
608 static inline void apply(tokenizer::iterator& it,
609 tokenizer::iterator const& end,
610 std::string const& wkt,
613 parsing_assigner<P>::apply(it, end, point, wkt);
617 template <typename MultiGeometry, typename PrefixPolicy>
618 struct multi_point_parser
620 static inline void apply(std::string const& wkt, MultiGeometry& geometry)
622 traits::clear<MultiGeometry>::apply(geometry);
624 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
625 tokenizer::iterator it, end;
627 if (initialize<MultiGeometry>(tokens, PrefixPolicy::apply(), wkt, it, end))
629 handle_open_parenthesis(it, end, wkt);
631 // If first point definition starts with "(" then parse points as (x y)
632 // otherwise as "x y"
633 bool using_brackets = (it != end && *it == "(");
635 while(it != end && *it != ")")
637 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
643 typename boost::range_value<MultiGeometry>::type
644 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
648 noparenthesis_point_parser
650 typename boost::range_value<MultiGeometry>::type
651 >::apply(it, end, wkt, *(boost::end(geometry) - 1));
654 if (it != end && *it == ",")
656 // Skip "," after point is parsed
661 handle_close_parenthesis(it, end, wkt);
664 check_end(it, end, wkt);
670 \brief Supports box parsing
671 \note OGC does not define the box geometry, and WKT does not support boxes.
672 However, to be generic GGL supports reading and writing from and to boxes.
673 Boxes are outputted as a standard POLYGON. GGL can read boxes from
674 a standard POLYGON, from a POLYGON with 2 points of from a BOX
677 template <typename Box>
680 static inline void apply(std::string const& wkt, Box& box)
682 bool should_close = false;
683 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
684 tokenizer::iterator it = tokens.begin();
685 tokenizer::iterator end = tokens.end();
686 if (it != end && boost::iequals(*it, "POLYGON"))
689 bool has_empty, has_z, has_m;
690 handle_empty_z_m(it, end, has_empty, has_z, has_m);
696 handle_open_parenthesis(it, end, wkt);
699 else if (it != end && boost::iequals(*it, "BOX"))
705 throw read_wkt_exception("Should start with 'POLYGON' or 'BOX'", wkt);
708 typedef typename point_type<Box>::type point_type;
709 std::vector<point_type> points;
710 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
714 handle_close_parenthesis(it, end, wkt);
716 check_end(it, end, wkt);
718 unsigned int index = 0;
719 std::size_t n = boost::size(points);
724 else if (n == 4 || n == 5)
726 // In case of 4 or 5 points, we do not check the other ones, just
727 // take the opposite corner which is always 2
732 throw read_wkt_exception("Box should have 2,4 or 5 points", wkt);
735 geometry::detail::assign_point_to_index<min_corner>(points.front(), box);
736 geometry::detail::assign_point_to_index<max_corner>(points[index], box);
742 \brief Supports segment parsing
743 \note OGC does not define the segment, and WKT does not support segmentes.
744 However, it is useful to implement it, also for testing purposes
745 \tparam Segment the segment
747 template <typename Segment>
748 struct segment_parser
750 static inline void apply(std::string const& wkt, Segment& segment)
752 tokenizer tokens(wkt, boost::char_separator<char>(" ", ",()"));
753 tokenizer::iterator it = tokens.begin();
754 tokenizer::iterator end = tokens.end();
756 (boost::iequals(*it, "SEGMENT")
757 || boost::iequals(*it, "LINESTRING") ))
763 throw read_wkt_exception("Should start with 'LINESTRING' or 'SEGMENT'", wkt);
766 typedef typename point_type<Segment>::type point_type;
767 std::vector<point_type> points;
768 container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
770 check_end(it, end, wkt);
772 if (boost::size(points) == 2)
774 geometry::detail::assign_point_to_index<0>(points.front(), segment);
775 geometry::detail::assign_point_to_index<1>(points.back(), segment);
779 throw read_wkt_exception("Segment should have 2 points", wkt);
786 }} // namespace detail::wkt
787 #endif // DOXYGEN_NO_DETAIL
789 #ifndef DOXYGEN_NO_DISPATCH
793 template <typename Tag, typename Geometry>
797 template <typename Point>
798 struct read_wkt<point_tag, Point>
799 : detail::wkt::geometry_parser
802 detail::wkt::point_parser,
803 detail::wkt::prefix_point
808 template <typename L>
809 struct read_wkt<linestring_tag, L>
810 : detail::wkt::geometry_parser
813 detail::wkt::linestring_parser,
814 detail::wkt::prefix_linestring
818 template <typename Ring>
819 struct read_wkt<ring_tag, Ring>
820 : detail::wkt::geometry_parser
823 detail::wkt::ring_parser,
824 detail::wkt::prefix_polygon
828 template <typename Geometry>
829 struct read_wkt<polygon_tag, Geometry>
830 : detail::wkt::geometry_parser
833 detail::wkt::polygon_parser,
834 detail::wkt::prefix_polygon
839 template <typename MultiGeometry>
840 struct read_wkt<multi_point_tag, MultiGeometry>
841 : detail::wkt::multi_point_parser
844 detail::wkt::prefix_multipoint
848 template <typename MultiGeometry>
849 struct read_wkt<multi_linestring_tag, MultiGeometry>
850 : detail::wkt::multi_parser
853 detail::wkt::linestring_parser,
854 detail::wkt::prefix_multilinestring
858 template <typename MultiGeometry>
859 struct read_wkt<multi_polygon_tag, MultiGeometry>
860 : detail::wkt::multi_parser
863 detail::wkt::polygon_parser,
864 detail::wkt::prefix_multipolygon
870 template <typename Box>
871 struct read_wkt<box_tag, Box>
872 : detail::wkt::box_parser<Box>
876 template <typename Segment>
877 struct read_wkt<segment_tag, Segment>
878 : detail::wkt::segment_parser<Segment>
882 } // namespace dispatch
883 #endif // DOXYGEN_NO_DISPATCH
886 \brief Parses OGC Well-Known Text (\ref WKT) into a geometry (any geometry)
888 \tparam Geometry \tparam_geometry
889 \param wkt string containing \ref WKT
890 \param geometry \param_geometry output geometry
892 \qbk{[include reference/io/read_wkt.qbk]}
894 template <typename Geometry>
895 inline void read_wkt(std::string const& wkt, Geometry& geometry)
897 geometry::concepts::check<Geometry>();
898 dispatch::read_wkt<typename tag<Geometry>::type, Geometry>::apply(wkt, geometry);
901 }} // namespace boost::geometry
903 #endif // BOOST_GEOMETRY_IO_WKT_READ_HPP