Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-09 08:46:02

0001 // Boost.Geometry (aka GGL, Generic Geometry Library)
0002 
0003 // Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands.
0004 // Copyright (c) 2008-2012 Bruno Lalande, Paris, France.
0005 // Copyright (c) 2009-2012 Mateusz Loskot, London, UK.
0006 // Copyright (c) 2017-2023 Adam Wulkiewicz, Lodz, Poland.
0007 // Copyright (c) 2020 Baidyanath Kundu, Haldia, India
0008 
0009 // This file was modified by Oracle on 2014-2021.
0010 // Modifications copyright (c) 2014-2021 Oracle and/or its affiliates.
0011 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
0012 
0013 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
0014 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
0015 
0016 // Use, modification and distribution is subject to the Boost Software License,
0017 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
0018 // http://www.boost.org/LICENSE_1_0.txt)
0019 
0020 #ifndef BOOST_GEOMETRY_IO_WKT_READ_HPP
0021 #define BOOST_GEOMETRY_IO_WKT_READ_HPP
0022 
0023 #include <cstddef>
0024 #include <string>
0025 
0026 #include <boost/algorithm/string/predicate.hpp>
0027 #include <boost/lexical_cast.hpp>
0028 #include <boost/range/begin.hpp>
0029 #include <boost/range/end.hpp>
0030 #include <boost/range/size.hpp>
0031 #include <boost/range/value_type.hpp>
0032 #include <boost/tokenizer.hpp>
0033 #include <boost/throw_exception.hpp>
0034 
0035 #include <boost/geometry/algorithms/assign.hpp>
0036 #include <boost/geometry/algorithms/append.hpp>
0037 #include <boost/geometry/algorithms/clear.hpp>
0038 #include <boost/geometry/algorithms/detail/disjoint/point_point.hpp>
0039 
0040 #include <boost/geometry/core/access.hpp>
0041 #include <boost/geometry/core/coordinate_dimension.hpp>
0042 #include <boost/geometry/core/exception.hpp>
0043 #include <boost/geometry/core/exterior_ring.hpp>
0044 #include <boost/geometry/core/geometry_id.hpp>
0045 #include <boost/geometry/core/geometry_types.hpp>
0046 #include <boost/geometry/core/interior_rings.hpp>
0047 #include <boost/geometry/core/mutable_range.hpp>
0048 #include <boost/geometry/core/point_type.hpp>
0049 #include <boost/geometry/core/tag.hpp>
0050 #include <boost/geometry/core/tags.hpp>
0051 
0052 #include <boost/geometry/geometries/adapted/boost_variant.hpp> // For consistency with other functions
0053 #include <boost/geometry/geometries/concepts/check.hpp>
0054 
0055 #include <boost/geometry/io/wkt/detail/prefix.hpp>
0056 
0057 #include <boost/geometry/strategies/io/cartesian.hpp>
0058 #include <boost/geometry/strategies/io/geographic.hpp>
0059 #include <boost/geometry/strategies/io/spherical.hpp>
0060 
0061 #include <boost/geometry/util/coordinate_cast.hpp>
0062 #include <boost/geometry/util/range.hpp>
0063 #include <boost/geometry/util/sequence.hpp>
0064 #include <boost/geometry/util/type_traits.hpp>
0065 
0066 namespace boost { namespace geometry
0067 {
0068 
0069 /*!
0070 \brief Exception showing things wrong with WKT parsing
0071 \ingroup wkt
0072 */
0073 struct read_wkt_exception : public geometry::exception
0074 {
0075     template <typename Iterator>
0076     read_wkt_exception(std::string const& msg,
0077                        Iterator const& it,
0078                        Iterator const& end,
0079                        std::string const& wkt)
0080         : message(msg)
0081         , wkt(wkt)
0082     {
0083         if (it != end)
0084         {
0085             source = " at '";
0086             source += it->c_str();
0087             source += "'";
0088         }
0089         complete = message + source + " in '" + wkt.substr(0, 100) + "'";
0090     }
0091 
0092     read_wkt_exception(std::string const& msg, std::string const& wkt)
0093         : message(msg)
0094         , wkt(wkt)
0095     {
0096         complete = message + "' in (" + wkt.substr(0, 100) + ")";
0097     }
0098 
0099     const char* what() const noexcept override
0100     {
0101         return complete.c_str();
0102     }
0103 private :
0104     std::string source;
0105     std::string message;
0106     std::string wkt;
0107     std::string complete;
0108 };
0109 
0110 
0111 #ifndef DOXYGEN_NO_DETAIL
0112 // (wkt: Well Known Text, defined by OGC for all geometries and implemented by e.g. databases (MySQL, PostGIS))
0113 namespace detail { namespace wkt
0114 {
0115 
0116 inline auto make_tokenizer(std::string const& wkt)
0117 {
0118     using separator = boost::char_separator<char>;
0119     using tokenizer = boost::tokenizer<separator>;
0120     const tokenizer tokens(wkt, separator(" \n\t\r", ",()"));
0121     return tokens;
0122 }
0123 
0124 template <typename Point,
0125           std::size_t Dimension = 0,
0126           std::size_t DimensionCount = geometry::dimension<Point>::value>
0127 struct parsing_assigner
0128 {
0129     template <typename TokenizerIterator>
0130     static inline void apply(TokenizerIterator& it,
0131                              TokenizerIterator const& end,
0132                              Point& point,
0133                              std::string const& wkt)
0134     {
0135         using coordinate_type = coordinate_type_t<Point>;
0136 
0137         // Stop at end of tokens, or at "," ot ")"
0138         bool finished = (it == end || *it == "," || *it == ")");
0139 
0140         try
0141         {
0142             // Initialize missing coordinates to default constructor (zero)
0143             // OR
0144             // Use lexical_cast for conversion to double/int
0145             // Note that it is much slower than atof. However, it is more standard
0146             // and in parsing the change in performance falls probably away against
0147             // the tokenizing
0148             set<Dimension>(point, finished
0149                     ? coordinate_type()
0150                     : coordinate_cast<coordinate_type>::apply(*it));
0151         }
0152         catch(boost::bad_lexical_cast const& blc)
0153         {
0154             BOOST_THROW_EXCEPTION(read_wkt_exception(blc.what(), it, end, wkt));
0155         }
0156         catch(std::exception const& e)
0157         {
0158             BOOST_THROW_EXCEPTION(read_wkt_exception(e.what(), it, end, wkt));
0159         }
0160         catch(...)
0161         {
0162             BOOST_THROW_EXCEPTION(read_wkt_exception("", it, end, wkt));
0163         }
0164 
0165         parsing_assigner<Point, Dimension + 1, DimensionCount>::apply(
0166                         (finished ? it : ++it), end, point, wkt);
0167     }
0168 };
0169 
0170 template <typename Point, std::size_t DimensionCount>
0171 struct parsing_assigner<Point, DimensionCount, DimensionCount>
0172 {
0173     template <typename TokenizerIterator>
0174     static inline void apply(TokenizerIterator&,
0175                              TokenizerIterator const&,
0176                              Point&,
0177                              std::string const&)
0178     {
0179     }
0180 };
0181 
0182 
0183 
0184 template <typename Iterator>
0185 inline void handle_open_parenthesis(Iterator& it,
0186                                     Iterator const& end,
0187                                     std::string const& wkt)
0188 {
0189     if (it == end || *it != "(")
0190     {
0191         BOOST_THROW_EXCEPTION(read_wkt_exception("Expected '('", it, end, wkt));
0192     }
0193     ++it;
0194 }
0195 
0196 
0197 template <typename Iterator>
0198 inline void handle_close_parenthesis(Iterator& it,
0199                                      Iterator const& end,
0200                                      std::string const& wkt)
0201 {
0202     if (it != end && *it == ")")
0203     {
0204         ++it;
0205     }
0206     else
0207     {
0208         BOOST_THROW_EXCEPTION(read_wkt_exception("Expected ')'", it, end, wkt));
0209     }
0210 }
0211 
0212 template <typename Iterator>
0213 inline void check_end(Iterator& it,
0214                       Iterator const& end,
0215                       std::string const& wkt)
0216 {
0217     if (it != end)
0218     {
0219         BOOST_THROW_EXCEPTION(read_wkt_exception("Too many tokens", it, end, wkt));
0220     }
0221 }
0222 
0223 /*!
0224 \brief Internal, parses coordinate sequences, strings are formated like "(1 2,3 4,...)"
0225 \param it token-iterator, should be pre-positioned at "(", is post-positions after last ")"
0226 \param end end-token-iterator
0227 \param out Output itererator receiving coordinates
0228 */
0229 template <typename Point>
0230 struct container_inserter
0231 {
0232     // Version with output iterator
0233     template <typename TokenizerIterator, typename OutputIterator>
0234     static inline void apply(TokenizerIterator& it,
0235                              TokenizerIterator const& end,
0236                              std::string const& wkt,
0237                              OutputIterator out)
0238     {
0239         handle_open_parenthesis(it, end, wkt);
0240 
0241         Point point;
0242 
0243         // Parse points until closing parenthesis
0244 
0245         while (it != end && *it != ")")
0246         {
0247             parsing_assigner<Point>::apply(it, end, point, wkt);
0248             out = point;
0249             ++out;
0250             if (it != end && *it == ",")
0251             {
0252                 ++it;
0253             }
0254         }
0255 
0256         handle_close_parenthesis(it, end, wkt);
0257     }
0258 };
0259 
0260 
0261 template <typename Geometry,
0262           closure_selector Closure = closure<Geometry>::value>
0263 struct stateful_range_appender
0264 {
0265     // NOTE: Geometry is a reference
0266     inline void append(Geometry geom, geometry::point_type_t<Geometry> const& point, bool)
0267     {
0268         geometry::append(geom, point);
0269     }
0270 };
0271 
0272 template <typename Geometry>
0273 struct stateful_range_appender<Geometry, open>
0274 {
0275     using point_type = geometry::point_type_t<Geometry>;
0276     using size_type = typename boost::range_size<util::remove_cptrref_t<Geometry>>::type;
0277 
0278     BOOST_STATIC_ASSERT((util::is_ring<Geometry>::value));
0279 
0280     inline stateful_range_appender()
0281         : pt_index(0)
0282     {}
0283 
0284     // NOTE: Geometry is a reference
0285     inline void append(Geometry geom, point_type const& point, bool is_next_expected)
0286     {
0287         bool should_append = true;
0288 
0289         if (pt_index == 0)
0290         {
0291             first_point = point;
0292         }
0293         else
0294         {
0295             // NOTE: if there are not enough Points, they're always appended
0296             should_append
0297                 = is_next_expected
0298                 || pt_index < core_detail::closure::minimum_ring_size<open>::value
0299                 || disjoint(point, first_point);
0300         }
0301         ++pt_index;
0302 
0303         if (should_append)
0304         {
0305             geometry::append(geom, point);
0306         }
0307     }
0308 
0309 private:
0310     static inline bool disjoint(point_type const& p1, point_type const& p2)
0311     {
0312         // TODO: pass strategy
0313         using strategy_type = typename strategies::io::services::default_strategy
0314             <
0315                 point_type
0316             >::type;
0317 
0318         return detail::disjoint::disjoint_point_point(p1, p2, strategy_type());
0319     }
0320 
0321     size_type pt_index;
0322     point_type first_point;
0323 };
0324 
0325 // Geometry is a value-type or reference-type
0326 template <typename Geometry>
0327 struct container_appender
0328 {
0329     using point_type = geometry::point_type_t<Geometry>;
0330 
0331     template <typename TokenizerIterator>
0332     static inline void apply(TokenizerIterator& it,
0333                              TokenizerIterator const& end,
0334                              std::string const& wkt,
0335                              Geometry out)
0336     {
0337         handle_open_parenthesis(it, end, wkt);
0338 
0339         stateful_range_appender<Geometry> appender;
0340 
0341         // Parse points until closing parenthesis
0342         while (it != end && *it != ")")
0343         {
0344             point_type point;
0345 
0346             parsing_assigner<point_type>::apply(it, end, point, wkt);
0347 
0348             bool const is_next_expected = it != end && *it == ",";
0349 
0350             appender.append(out, point, is_next_expected);
0351 
0352             if (is_next_expected)
0353             {
0354                 ++it;
0355             }
0356         }
0357 
0358         handle_close_parenthesis(it, end, wkt);
0359     }
0360 };
0361 
0362 /*!
0363 \brief Internal, parses a point from a string like this "(x y)"
0364 \note used for parsing points and multi-points
0365 */
0366 template <typename P>
0367 struct point_parser
0368 {
0369     template <typename TokenizerIterator>
0370     static inline void apply(TokenizerIterator& it,
0371                              TokenizerIterator const& end,
0372                              std::string const& wkt,
0373                              P& point)
0374     {
0375         handle_open_parenthesis(it, end, wkt);
0376         parsing_assigner<P>::apply(it, end, point, wkt);
0377         handle_close_parenthesis(it, end, wkt);
0378     }
0379 };
0380 
0381 
0382 template <typename Geometry>
0383 struct linestring_parser
0384 {
0385     template <typename TokenizerIterator>
0386     static inline void apply(TokenizerIterator& it,
0387                              TokenizerIterator const& end,
0388                              std::string const& wkt,
0389                              Geometry& geometry)
0390     {
0391         container_appender<Geometry&>::apply(it, end, wkt, geometry);
0392     }
0393 };
0394 
0395 
0396 template <typename Ring>
0397 struct ring_parser
0398 {
0399     template <typename TokenizerIterator>
0400     static inline void apply(TokenizerIterator& it,
0401                              TokenizerIterator const& end,
0402                              std::string const& wkt,
0403                              Ring& ring)
0404     {
0405         // A ring should look like polygon((x y,x y,x y...))
0406         // So handle the extra opening/closing parentheses
0407         // and in between parse using the container-inserter
0408         handle_open_parenthesis(it, end, wkt);
0409         container_appender<Ring&>::apply(it, end, wkt, ring);
0410         handle_close_parenthesis(it, end, wkt);
0411     }
0412 };
0413 
0414 
0415 /*!
0416 \brief Internal, parses a polygon from a string like this "((x y,x y),(x y,x y))"
0417 \note used for parsing polygons and multi-polygons
0418 */
0419 template <typename Polygon>
0420 struct polygon_parser
0421 {
0422     using appender = container_appender<ring_return_type_t<Polygon>>;
0423 
0424     template <typename TokenizerIterator>
0425     static inline void apply(TokenizerIterator& it,
0426                              TokenizerIterator const& end,
0427                              std::string const& wkt,
0428                              Polygon& poly)
0429     {
0430 
0431         handle_open_parenthesis(it, end, wkt);
0432 
0433         int n = -1;
0434 
0435         // Stop at ")"
0436         while (it != end && *it != ")")
0437         {
0438             // Parse ring
0439             if (++n == 0)
0440             {
0441                 appender::apply(it, end, wkt, exterior_ring(poly));
0442             }
0443             else
0444             {
0445                 ring_type_t<Polygon> ring;
0446                 appender::apply(it, end, wkt, ring);
0447                 range::push_back(geometry::interior_rings(poly), std::move(ring));
0448             }
0449 
0450             if (it != end && *it == ",")
0451             {
0452                 // Skip "," after ring is parsed
0453                 ++it;
0454             }
0455         }
0456 
0457         handle_close_parenthesis(it, end, wkt);
0458     }
0459 };
0460 
0461 template<typename PolyhedralSurface>
0462 struct polyhedral_surface_parser
0463 {
0464     using polygon_t = typename PolyhedralSurface::polygon_type;
0465 
0466     template <typename TokenizerIterator>
0467     static inline void apply(TokenizerIterator& it,
0468                              TokenizerIterator const& end,
0469                              std::string const& wkt,
0470                              PolyhedralSurface& polyhedral)
0471     {
0472         handle_open_parenthesis(it, end, wkt);
0473 
0474         // Parse polygons
0475         while (it != end && *it != ")")
0476         {
0477             traits::resize<PolyhedralSurface>::apply(polyhedral, boost::size(polyhedral) + 1);
0478             polygon_parser<polygon_t>::apply(it, end, wkt, *(boost::end(polyhedral) - 1));
0479             if (it != end && *it == ",")
0480             {
0481                 // Skip "," after multi-element is parsed
0482                 ++it;
0483             }
0484         }
0485 
0486         handle_close_parenthesis(it, end, wkt);
0487     }
0488 };
0489 
0490 
0491 template <typename TokenizerIterator>
0492 inline bool one_of(TokenizerIterator const& it,
0493                    std::string const& value,
0494                    bool& is_present)
0495 {
0496     if (boost::iequals(*it, value))
0497     {
0498         is_present = true;
0499         return true;
0500     }
0501     return false;
0502 }
0503 
0504 template <typename TokenizerIterator>
0505 inline bool one_of(TokenizerIterator const& it,
0506                    std::string const& value,
0507                    bool& present1,
0508                    bool& present2)
0509 {
0510     if (boost::iequals(*it, value))
0511     {
0512         present1 = true;
0513         present2 = true;
0514         return true;
0515     }
0516     return false;
0517 }
0518 
0519 
0520 template <typename TokenizerIterator>
0521 inline void handle_empty_z_m(TokenizerIterator& it,
0522                              TokenizerIterator const& end,
0523                              bool& has_empty,
0524                              bool& has_z,
0525                              bool& has_m)
0526 {
0527     has_empty = false;
0528     has_z = false;
0529     has_m = false;
0530 
0531     // WKT can optionally have Z and M (measured) values as in
0532     // POINT ZM (1 1 5 60), POINT M (1 1 80), POINT Z (1 1 5)
0533     // GGL supports any of them as coordinate values, but is not aware
0534     // of any Measured value.
0535     while (it != end
0536            && (one_of(it, "M", has_m)
0537                || one_of(it, "Z", has_z)
0538                || one_of(it, "EMPTY", has_empty)
0539                || one_of(it, "MZ", has_m, has_z)
0540                || one_of(it, "ZM", has_z, has_m)
0541                )
0542            )
0543     {
0544         ++it;
0545     }
0546 }
0547 
0548 
0549 template <typename Geometry, typename Tag = geometry::tag_t<Geometry>>
0550 struct dimension
0551     : geometry::dimension<Geometry>
0552 {};
0553 
0554 // TODO: For now assume the dimension of the first type defined for GC
0555 //       This should probably be unified for all algorithms
0556 template <typename Geometry>
0557 struct dimension<Geometry, geometry_collection_tag>
0558     : geometry::dimension
0559         <
0560             typename util::sequence_front
0561                 <
0562                     typename traits::geometry_types<Geometry>::type
0563                 >::type
0564         >
0565 {};
0566 
0567 
0568 /*!
0569 \brief Internal, starts parsing
0570 \param geometry_name string to compare with first token
0571 */
0572 template <typename Geometry, typename TokenizerIterator>
0573 inline bool initialize(TokenizerIterator& it,
0574                        TokenizerIterator const& end,
0575                        std::string const& wkt,
0576                        std::string const& geometry_name)
0577 {
0578     if (it == end || ! boost::iequals(*it++, geometry_name))
0579     {
0580         BOOST_THROW_EXCEPTION(read_wkt_exception(std::string("Should start with '") + geometry_name + "'", wkt));
0581     }
0582 
0583     bool has_empty, has_z, has_m;
0584 
0585     handle_empty_z_m(it, end, has_empty, has_z, has_m);
0586 
0587 // Silence warning C4127: conditional expression is constant
0588 #if defined(_MSC_VER)
0589 #pragma warning(push)
0590 #pragma warning(disable : 4127)
0591 #endif
0592 
0593     if (has_z && dimension<Geometry>::value < 3)
0594     {
0595         BOOST_THROW_EXCEPTION(read_wkt_exception("Z only allowed for 3 or more dimensions", wkt));
0596     }
0597 
0598 #if defined(_MSC_VER)
0599 #pragma warning(pop)
0600 #endif
0601 
0602     if (has_empty)
0603     {
0604         return false;
0605     }
0606     // M is ignored at all.
0607 
0608     return true;
0609 }
0610 
0611 
0612 template <typename Geometry, template<typename> class Parser, typename PrefixPolicy>
0613 struct geometry_parser
0614 {
0615     static inline void apply(std::string const& wkt, Geometry& geometry)
0616     {
0617         geometry::clear(geometry);
0618 
0619         auto const tokens{make_tokenizer(wkt)};
0620         auto it = tokens.begin();
0621         auto const end = tokens.end();
0622 
0623         apply(it, end, wkt, geometry);
0624 
0625         check_end(it, end, wkt);
0626     }
0627 
0628     template <typename TokenizerIterator>
0629     static inline void apply(TokenizerIterator& it,
0630                              TokenizerIterator const& end,
0631                              std::string const& wkt,
0632                              Geometry& geometry)
0633     {
0634         if (initialize<Geometry>(it, end, wkt, PrefixPolicy::apply()))
0635         {
0636             Parser<Geometry>::apply(it, end, wkt, geometry);
0637         }
0638     }
0639 };
0640 
0641 
0642 template <typename MultiGeometry, template<typename> class Parser, typename PrefixPolicy>
0643 struct multi_parser
0644 {
0645     static inline void apply(std::string const& wkt, MultiGeometry& geometry)
0646     {
0647         traits::clear<MultiGeometry>::apply(geometry);
0648 
0649         auto const tokens{make_tokenizer(wkt)};
0650         auto it = tokens.begin();
0651         auto const end = tokens.end();
0652 
0653         apply(it, end, wkt, geometry);
0654 
0655         check_end(it, end, wkt);
0656     }
0657 
0658     template <typename TokenizerIterator>
0659     static inline void apply(TokenizerIterator& it,
0660                              TokenizerIterator const& end,
0661                              std::string const& wkt,
0662                              MultiGeometry& geometry)
0663     {
0664         if (initialize<MultiGeometry>(it, end, wkt, PrefixPolicy::apply()))
0665         {
0666             handle_open_parenthesis(it, end, wkt);
0667 
0668             // Parse sub-geometries
0669             while(it != end && *it != ")")
0670             {
0671                 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
0672                 Parser
0673                     <
0674                         typename boost::range_value<MultiGeometry>::type
0675                     >::apply(it, end, wkt, *(boost::end(geometry) - 1));
0676                 if (it != end && *it == ",")
0677                 {
0678                     // Skip "," after multi-element is parsed
0679                     ++it;
0680                 }
0681             }
0682 
0683             handle_close_parenthesis(it, end, wkt);
0684         }
0685     }
0686 };
0687 
0688 template <typename P>
0689 struct noparenthesis_point_parser
0690 {
0691     template <typename TokenizerIterator>
0692     static inline void apply(TokenizerIterator& it,
0693                              TokenizerIterator const& end,
0694                              std::string const& wkt,
0695                              P& point)
0696     {
0697         parsing_assigner<P>::apply(it, end, point, wkt);
0698     }
0699 };
0700 
0701 template <typename MultiGeometry, typename PrefixPolicy>
0702 struct multi_point_parser
0703 {
0704     static inline void apply(std::string const& wkt, MultiGeometry& geometry)
0705     {
0706         traits::clear<MultiGeometry>::apply(geometry);
0707 
0708         auto const tokens{make_tokenizer(wkt)};
0709         auto it = tokens.begin();
0710         auto const end = tokens.end();
0711 
0712         apply(it, end, wkt, geometry);
0713 
0714         check_end(it, end, wkt);
0715     }
0716 
0717     template <typename TokenizerIterator>
0718     static inline void apply(TokenizerIterator& it,
0719                              TokenizerIterator const& end,
0720                              std::string const& wkt,
0721                              MultiGeometry& geometry)
0722     {
0723         if (initialize<MultiGeometry>(it, end, wkt, PrefixPolicy::apply()))
0724         {
0725             handle_open_parenthesis(it, end, wkt);
0726 
0727             // If first point definition starts with "(" then parse points as (x y)
0728             // otherwise as "x y"
0729             bool using_brackets = (it != end && *it == "(");
0730 
0731             while(it != end && *it != ")")
0732             {
0733                 traits::resize<MultiGeometry>::apply(geometry, boost::size(geometry) + 1);
0734 
0735                 if (using_brackets)
0736                 {
0737                     point_parser
0738                         <
0739                             typename boost::range_value<MultiGeometry>::type
0740                         >::apply(it, end, wkt, *(boost::end(geometry) - 1));
0741                 }
0742                 else
0743                 {
0744                     noparenthesis_point_parser
0745                         <
0746                             typename boost::range_value<MultiGeometry>::type
0747                         >::apply(it, end, wkt, *(boost::end(geometry) - 1));
0748                 }
0749 
0750                 if (it != end && *it == ",")
0751                 {
0752                     // Skip "," after point is parsed
0753                     ++it;
0754                 }
0755             }
0756 
0757             handle_close_parenthesis(it, end, wkt);
0758         }
0759     }
0760 };
0761 
0762 
0763 /*!
0764 \brief Supports box parsing
0765 \note OGC does not define the box geometry, and WKT does not support boxes.
0766     However, to be generic GGL supports reading and writing from and to boxes.
0767     Boxes are outputted as a standard POLYGON. GGL can read boxes from
0768     a standard POLYGON, from a POLYGON with 2 points of from a BOX
0769 \tparam Box the box
0770 */
0771 template <typename Box>
0772 struct box_parser
0773 {
0774     static inline void apply(std::string const& wkt, Box& box)
0775     {
0776         auto const tokens{make_tokenizer(wkt)};
0777         auto it = tokens.begin();
0778         auto const end = tokens.end();
0779 
0780         apply(it, end, wkt, box);
0781 
0782         check_end(it, end, wkt);
0783     }
0784 
0785     template <typename TokenizerIterator>
0786     static inline void apply(TokenizerIterator& it,
0787                              TokenizerIterator const& end,
0788                              std::string const& wkt,
0789                              Box& box)
0790     {
0791         bool should_close = false;
0792         if (it != end && boost::iequals(*it, "POLYGON"))
0793         {
0794             ++it;
0795             bool has_empty, has_z, has_m;
0796             handle_empty_z_m(it, end, has_empty, has_z, has_m);
0797             if (has_empty)
0798             {
0799                 assign_zero(box);
0800                 return;
0801             }
0802             handle_open_parenthesis(it, end, wkt);
0803             should_close = true;
0804         }
0805         else if (it != end && boost::iequals(*it, "BOX"))
0806         {
0807             ++it;
0808         }
0809         else
0810         {
0811             BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'POLYGON' or 'BOX'", wkt));
0812         }
0813 
0814         using point_type = point_type_t<Box>;
0815         std::vector<point_type> points;
0816         container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
0817 
0818         if (should_close)
0819         {
0820             handle_close_parenthesis(it, end, wkt);
0821         }
0822 
0823         unsigned int index = 0;
0824         std::size_t n = boost::size(points);
0825         if (n == 2)
0826         {
0827             index = 1;
0828         }
0829         else if (n == 4 || n == 5)
0830         {
0831             // In case of 4 or 5 points, we do not check the other ones, just
0832             // take the opposite corner which is always 2
0833             index = 2;
0834         }
0835         else
0836         {
0837             BOOST_THROW_EXCEPTION(read_wkt_exception("Box should have 2,4 or 5 points", wkt));
0838         }
0839 
0840         geometry::detail::assign_point_to_index<min_corner>(points.front(), box);
0841         geometry::detail::assign_point_to_index<max_corner>(points[index], box);
0842     }
0843 };
0844 
0845 
0846 /*!
0847 \brief Supports segment parsing
0848 \note OGC does not define the segment, and WKT does not support segmentes.
0849     However, it is useful to implement it, also for testing purposes
0850 \tparam Segment the segment
0851 */
0852 template <typename Segment>
0853 struct segment_parser
0854 {
0855     static inline void apply(std::string const& wkt, Segment& segment)
0856     {
0857         auto const tokens{make_tokenizer(wkt)};
0858         auto it = tokens.begin();
0859         auto const end = tokens.end();
0860 
0861         apply(it, end, wkt, segment);
0862 
0863         check_end(it, end, wkt);
0864     }
0865 
0866     template <typename TokenizerIterator>
0867     static inline void apply(TokenizerIterator& it,
0868                              TokenizerIterator const& end,
0869                              std::string const& wkt,
0870                              Segment& segment)
0871     {
0872         if (it != end
0873             && (boost::iequals(*it, prefix_segment::apply())
0874                 || boost::iequals(*it, prefix_linestring::apply())))
0875         {
0876             ++it;
0877         }
0878         else
0879         {
0880             BOOST_THROW_EXCEPTION(read_wkt_exception("Should start with 'LINESTRING' or 'SEGMENT'", wkt));
0881         }
0882 
0883         using point_type = point_type_t<Segment>;
0884         std::vector<point_type> points;
0885         container_inserter<point_type>::apply(it, end, wkt, std::back_inserter(points));
0886 
0887         if (boost::size(points) == 2)
0888         {
0889             geometry::detail::assign_point_to_index<0>(points.front(), segment);
0890             geometry::detail::assign_point_to_index<1>(points.back(), segment);
0891         }
0892         else
0893         {
0894             BOOST_THROW_EXCEPTION(read_wkt_exception("Segment should have 2 points", wkt));
0895         }
0896     }
0897 };
0898 
0899 
0900 struct dynamic_move_assign
0901 {
0902     template <typename DynamicGeometry, typename Geometry>
0903     static void apply(DynamicGeometry& dynamic_geometry, Geometry & geometry)
0904     {
0905         dynamic_geometry = std::move(geometry);
0906     }
0907 };
0908 
0909 struct dynamic_move_emplace_back
0910 {
0911     template <typename GeometryCollection, typename Geometry>
0912     static void apply(GeometryCollection& geometry_collection, Geometry & geometry)
0913     {
0914         traits::emplace_back<GeometryCollection>::apply(geometry_collection, std::move(geometry));
0915     }
0916 };
0917 
0918 template
0919 <
0920     typename Geometry,
0921     template <typename, typename> class ReadWkt,
0922     typename AppendPolicy
0923 >
0924 struct dynamic_readwkt_caller
0925 {
0926     template <typename TokenizerIterator>
0927     static inline void apply(TokenizerIterator& it,
0928                              TokenizerIterator const& end,
0929                              std::string const& wkt,
0930                              Geometry& geometry)
0931     {
0932         static const char* tag_point = prefix_point::apply();
0933         static const char* tag_linestring = prefix_linestring::apply();
0934         static const char* tag_polygon = prefix_polygon::apply();
0935 
0936         static const char* tag_multi_point = prefix_multipoint::apply();
0937         static const char* tag_multi_linestring = prefix_multilinestring::apply();
0938         static const char* tag_multi_polygon = prefix_multipolygon::apply();
0939 
0940         static const char* tag_segment = prefix_segment::apply();
0941         static const char* tag_box = prefix_box::apply();
0942         static const char* tag_gc = prefix_geometrycollection::apply();
0943 
0944         if (boost::iequals(*it, tag_point))
0945         {
0946             parse_geometry<util::is_point>(tag_point, it, end, wkt, geometry);
0947         }
0948         else if (boost::iequals(*it, tag_multi_point))
0949         {
0950             parse_geometry<util::is_multi_point>(tag_multi_point, it, end, wkt, geometry);
0951         }
0952         else if (boost::iequals(*it, tag_segment))
0953         {
0954             parse_geometry<util::is_segment>(tag_segment, it, end, wkt, geometry);
0955         }
0956         else if (boost::iequals(*it, tag_linestring))
0957         {
0958             parse_geometry<util::is_linestring>(tag_linestring, it, end, wkt, geometry, false)
0959             || parse_geometry<util::is_segment>(tag_linestring, it, end, wkt, geometry);
0960         }
0961         else if (boost::iequals(*it, tag_multi_linestring))
0962         {
0963             parse_geometry<util::is_multi_linestring>(tag_multi_linestring, it, end, wkt, geometry);
0964         }
0965         else if (boost::iequals(*it, tag_box))
0966         {
0967             parse_geometry<util::is_box>(tag_box, it, end, wkt, geometry);
0968         }
0969         else if (boost::iequals(*it, tag_polygon))
0970         {
0971             parse_geometry<util::is_polygon>(tag_polygon, it, end, wkt, geometry, false)
0972             || parse_geometry<util::is_ring>(tag_polygon, it, end, wkt, geometry, false)
0973             || parse_geometry<util::is_box>(tag_polygon, it, end, wkt, geometry);
0974         }
0975         else if (boost::iequals(*it, tag_multi_polygon))
0976         {
0977             parse_geometry<util::is_multi_polygon>(tag_multi_polygon, it, end, wkt, geometry);
0978         }
0979         else if (boost::iequals(*it, tag_gc))
0980         {
0981             parse_geometry<util::is_geometry_collection>(tag_gc, it, end, wkt, geometry);
0982         }
0983         else
0984         {
0985             BOOST_THROW_EXCEPTION(read_wkt_exception(
0986                 "Should start with geometry's type, for example 'POINT', 'LINESTRING', 'POLYGON'",
0987                 wkt));
0988         }
0989     }
0990 
0991 private:
0992     template
0993     <
0994         template <typename> class UnaryPred,
0995         typename TokenizerIterator,
0996         typename Geom = typename util::sequence_find_if
0997             <
0998                 typename traits::geometry_types<Geometry>::type, UnaryPred
0999             >::type,
1000         std::enable_if_t<! std::is_void<Geom>::value, int> = 0
1001     >
1002     static bool parse_geometry(const char * ,
1003                                TokenizerIterator& it,
1004                                TokenizerIterator const& end,
1005                                std::string const& wkt,
1006                                Geometry& geometry,
1007                                bool = true)
1008     {
1009         Geom g;
1010         ReadWkt<Geom, tag_t<Geom>>::apply(it, end, wkt, g);
1011         AppendPolicy::apply(geometry, g);
1012         return true;
1013     }
1014 
1015     template
1016     <
1017         template <typename> class UnaryPred,
1018         typename TokenizerIterator,
1019         typename Geom = typename util::sequence_find_if
1020             <
1021                 typename traits::geometry_types<Geometry>::type, UnaryPred
1022             >::type,
1023         std::enable_if_t<std::is_void<Geom>::value, int> = 0
1024     >
1025     static bool parse_geometry(const char * name,
1026                                TokenizerIterator& ,
1027                                TokenizerIterator const& ,
1028                                std::string const& wkt,
1029                                Geometry& ,
1030                                bool throw_on_misfit = true)
1031     {
1032         if (throw_on_misfit)
1033         {
1034             std::string msg = std::string("Unable to store '") + name + "' in this geometry";
1035             BOOST_THROW_EXCEPTION(read_wkt_exception(msg, wkt));
1036         }
1037 
1038         return false;
1039     }
1040 };
1041 
1042 
1043 }} // namespace detail::wkt
1044 #endif // DOXYGEN_NO_DETAIL
1045 
1046 #ifndef DOXYGEN_NO_DISPATCH
1047 namespace dispatch
1048 {
1049 
1050 template <typename Geometry, typename Tag = tag_t<Geometry>>
1051 struct read_wkt {};
1052 
1053 
1054 template <typename Point>
1055 struct read_wkt<Point, point_tag>
1056     : detail::wkt::geometry_parser
1057         <
1058             Point,
1059             detail::wkt::point_parser,
1060             detail::wkt::prefix_point
1061         >
1062 {};
1063 
1064 
1065 template <typename L>
1066 struct read_wkt<L, linestring_tag>
1067     : detail::wkt::geometry_parser
1068         <
1069             L,
1070             detail::wkt::linestring_parser,
1071             detail::wkt::prefix_linestring
1072         >
1073 {};
1074 
1075 template <typename Ring>
1076 struct read_wkt<Ring, ring_tag>
1077     : detail::wkt::geometry_parser
1078         <
1079             Ring,
1080             detail::wkt::ring_parser,
1081             detail::wkt::prefix_polygon
1082         >
1083 {};
1084 
1085 template <typename Geometry>
1086 struct read_wkt<Geometry, polygon_tag>
1087     : detail::wkt::geometry_parser
1088         <
1089             Geometry,
1090             detail::wkt::polygon_parser,
1091             detail::wkt::prefix_polygon
1092         >
1093 {};
1094 
1095 template <typename Geometry>
1096 struct read_wkt<Geometry, polyhedral_surface_tag>
1097     : detail::wkt::geometry_parser
1098         <
1099             Geometry,
1100             detail::wkt::polyhedral_surface_parser,
1101             detail::wkt::prefix_polyhedral_surface
1102         >
1103 {};
1104 
1105 template <typename MultiGeometry>
1106 struct read_wkt<MultiGeometry, multi_point_tag>
1107     : detail::wkt::multi_point_parser
1108             <
1109                 MultiGeometry,
1110                 detail::wkt::prefix_multipoint
1111             >
1112 {};
1113 
1114 template <typename MultiGeometry>
1115 struct read_wkt<MultiGeometry, multi_linestring_tag>
1116     : detail::wkt::multi_parser
1117             <
1118                 MultiGeometry,
1119                 detail::wkt::linestring_parser,
1120                 detail::wkt::prefix_multilinestring
1121             >
1122 {};
1123 
1124 template <typename MultiGeometry>
1125 struct read_wkt<MultiGeometry, multi_polygon_tag>
1126     : detail::wkt::multi_parser
1127             <
1128                 MultiGeometry,
1129                 detail::wkt::polygon_parser,
1130                 detail::wkt::prefix_multipolygon
1131             >
1132 {};
1133 
1134 
1135 // Box (Non-OGC)
1136 template <typename Box>
1137 struct read_wkt<Box, box_tag>
1138     : detail::wkt::box_parser<Box>
1139 {};
1140 
1141 // Segment (Non-OGC)
1142 template <typename Segment>
1143 struct read_wkt<Segment, segment_tag>
1144     : detail::wkt::segment_parser<Segment>
1145 {};
1146 
1147 
1148 template <typename DynamicGeometry>
1149 struct read_wkt<DynamicGeometry, dynamic_geometry_tag>
1150 {
1151     static inline void apply(std::string const& wkt, DynamicGeometry& dynamic_geometry)
1152     {
1153         auto tokens{detail::wkt::make_tokenizer(wkt)};
1154         auto it = tokens.begin();
1155         auto const end = tokens.end();
1156         if (it == end)
1157         {
1158             BOOST_THROW_EXCEPTION(read_wkt_exception(
1159                 "Should start with geometry's type, for example 'POINT', 'LINESTRING', 'POLYGON'",
1160                 wkt));
1161         }
1162 
1163         detail::wkt::dynamic_readwkt_caller
1164             <
1165                 DynamicGeometry, dispatch::read_wkt, detail::wkt::dynamic_move_assign
1166             >::apply(it, end, wkt, dynamic_geometry);
1167 
1168         detail::wkt::check_end(it, end, wkt);
1169     }
1170 };
1171 
1172 
1173 template <typename Geometry>
1174 struct read_wkt<Geometry, geometry_collection_tag>
1175 {
1176     static inline void apply(std::string const& wkt, Geometry& geometry)
1177     {
1178         range::clear(geometry);
1179 
1180         auto tokens{detail::wkt::make_tokenizer(wkt)};
1181         auto it = tokens.begin();
1182         auto const end = tokens.end();
1183 
1184         apply(it, end, wkt, geometry);
1185 
1186         detail::wkt::check_end(it, end, wkt);
1187     }
1188 
1189     template <typename TokenizerIterator>
1190     static inline void apply(TokenizerIterator& it,
1191                              TokenizerIterator const& end,
1192                              std::string const& wkt,
1193                              Geometry& geometry)
1194     {
1195         if (detail::wkt::initialize<Geometry>(it, end, wkt,
1196                 detail::wkt::prefix_geometrycollection::apply()))
1197         {
1198             detail::wkt::handle_open_parenthesis(it, end, wkt);
1199 
1200             // Stop at ")"
1201             while (it != end && *it != ")")
1202             {
1203                 detail::wkt::dynamic_readwkt_caller
1204                     <
1205                         Geometry, dispatch::read_wkt, detail::wkt::dynamic_move_emplace_back
1206                     >::apply(it, end, wkt, geometry);
1207 
1208                 if (it != end && *it == ",")
1209                 {
1210                     // Skip "," after geometry is parsed
1211                     ++it;
1212                 }
1213             }
1214 
1215             detail::wkt::handle_close_parenthesis(it, end, wkt);
1216         }
1217     }
1218 };
1219 
1220 
1221 } // namespace dispatch
1222 #endif // DOXYGEN_NO_DISPATCH
1223 
1224 /*!
1225 \brief Parses OGC \well_known_text (\wkt) into a geometry (any geometry)
1226 \ingroup wkt
1227 \tparam Geometry \tparam_geometry
1228 \param wkt string containing \wkt
1229 \param geometry \param_geometry output geometry
1230 \ingroup wkt
1231 \qbk{[include reference/io/read_wkt.qbk]}
1232 */
1233 template <typename Geometry>
1234 inline void read_wkt(std::string const& wkt, Geometry& geometry)
1235 {
1236     geometry::concepts::check<Geometry>();
1237     dispatch::read_wkt<Geometry>::apply(wkt, geometry);
1238 }
1239 
1240 /*!
1241 \brief Parses OGC \well_known_text (\wkt) into a geometry (any geometry) and returns it
1242 \ingroup wkt
1243 \tparam Geometry \tparam_geometry
1244 \param wkt string containing \wkt
1245 \ingroup wkt
1246 \qbk{[include reference/io/from_wkt.qbk]}
1247 */
1248 template <typename Geometry>
1249 inline Geometry from_wkt(std::string const& wkt)
1250 {
1251     Geometry geometry;
1252     geometry::concepts::check<Geometry>();
1253     dispatch::read_wkt<Geometry>::apply(wkt, geometry);
1254     return geometry;
1255 }
1256 
1257 }} // namespace boost::geometry
1258 
1259 #endif // BOOST_GEOMETRY_IO_WKT_READ_HPP