123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- // Boost.Geometry (aka GGL, Generic Geometry Library)
- // Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands.
- // Copyright (c) 2008-2017 Bruno Lalande, Paris, France.
- // Copyright (c) 2009-2017 Mateusz Loskot, London, UK.
- // Copyright (c) 2014-2023 Adam Wulkiewicz, Lodz, Poland.
- // Copyright (c) 2020 Baidyanath Kundu, Haldia, India.
- // This file was modified by Oracle on 2015-2021.
- // Modifications copyright (c) 2015-2021, Oracle and/or its affiliates.
- // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
- // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
- // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
- // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
- // Use, modification and distribution is subject to the Boost Software License,
- // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- #ifndef BOOST_GEOMETRY_IO_WKT_WRITE_HPP
- #define BOOST_GEOMETRY_IO_WKT_WRITE_HPP
- #include <array>
- #include <ostream>
- #include <string>
- #include <boost/range/begin.hpp>
- #include <boost/range/end.hpp>
- #include <boost/range/size.hpp>
- #include <boost/range/value_type.hpp>
- #include <boost/geometry/algorithms/assign.hpp>
- #include <boost/geometry/algorithms/convert.hpp>
- #include <boost/geometry/algorithms/detail/disjoint/point_point.hpp>
- #include <boost/geometry/algorithms/not_implemented.hpp>
- #include <boost/geometry/core/exterior_ring.hpp>
- #include <boost/geometry/core/interior_rings.hpp>
- #include <boost/geometry/core/ring_type.hpp>
- #include <boost/geometry/core/tags.hpp>
- #include <boost/geometry/core/visit.hpp>
- #include <boost/geometry/geometries/adapted/boost_variant.hpp> // For backward compatibility
- #include <boost/geometry/geometries/concepts/check.hpp>
- #include <boost/geometry/geometries/ring.hpp>
- #include <boost/geometry/io/wkt/detail/prefix.hpp>
- #include <boost/geometry/strategies/io/cartesian.hpp>
- #include <boost/geometry/strategies/io/geographic.hpp>
- #include <boost/geometry/strategies/io/spherical.hpp>
- #include <boost/geometry/util/constexpr.hpp>
- #include <boost/geometry/util/type_traits.hpp>
- namespace boost { namespace geometry
- {
- // Silence warning C4512: 'boost::geometry::wkt_manipulator<Geometry>' : assignment operator could not be generated
- #if defined(_MSC_VER)
- #pragma warning(push)
- #pragma warning(disable : 4512)
- #endif
- #ifndef DOXYGEN_NO_DETAIL
- namespace detail { namespace wkt
- {
- template <typename P, int I, int Count>
- struct stream_coordinate
- {
- template <typename Char, typename Traits>
- static inline void apply(std::basic_ostream<Char, Traits>& os, P const& p)
- {
- os << (I > 0 ? " " : "") << get<I>(p);
- stream_coordinate<P, I + 1, Count>::apply(os, p);
- }
- };
- template <typename P, int Count>
- struct stream_coordinate<P, Count, Count>
- {
- template <typename Char, typename Traits>
- static inline void apply(std::basic_ostream<Char, Traits>&, P const&)
- {}
- };
- /*!
- \brief Stream points as \ref WKT
- */
- template <typename Point, typename Policy>
- struct wkt_point
- {
- template <typename Char, typename Traits>
- static inline void apply(std::basic_ostream<Char, Traits>& os, Point const& p, bool)
- {
- os << Policy::apply() << "(";
- stream_coordinate<Point, 0, dimension<Point>::type::value>::apply(os, p);
- os << ")";
- }
- };
- /*!
- \brief Stream ranges as WKT
- */
- template
- <
- typename Range,
- typename PrefixPolicy,
- bool ForceClosurePossible = false,
- bool WriteDoubleBrackets = false
- >
- struct wkt_range
- {
- template <typename Char, typename Traits>
- static inline void apply(std::basic_ostream<Char, Traits>& os,
- Range const& range, bool force_closure = ForceClosurePossible)
- {
- using stream_type = stream_coordinate
- <
- point_type, 0, dimension<point_type>::type::value
- >;
- bool first = true;
- os << PrefixPolicy::apply();
- os << "(";
- if (boost::size(range) > 0)
- {
- if BOOST_GEOMETRY_CONSTEXPR (WriteDoubleBrackets)
- {
- os << "(";
- }
- auto begin = boost::begin(range);
- auto const end = boost::end(range);
- for (auto it = begin; it != end; ++it)
- {
- os << (first ? "" : ",");
- stream_type::apply(os, *it);
- first = false;
- }
- // optionally, close range to ring by repeating the first point
- if BOOST_GEOMETRY_CONSTEXPR (ForceClosurePossible)
- {
- if (force_closure
- && boost::size(range) > 1
- && wkt_range::disjoint(*begin, *(end - 1)))
- {
- os << ",";
- stream_type::apply(os, *begin);
- }
- }
- if BOOST_GEOMETRY_CONSTEXPR (WriteDoubleBrackets)
- {
- os << ")";
- }
- }
- os << ")";
- }
- private:
- using point_type = typename boost::range_value<Range>::type;
- static inline bool disjoint(point_type const& p1, point_type const& p2)
- {
- // TODO: pass strategy
- using strategy_type = typename strategies::io::services::default_strategy
- <
- point_type
- >::type;
- return detail::disjoint::disjoint_point_point(p1, p2, strategy_type());
- }
- };
- /*!
- \brief Stream sequence of points as WKT-part, e.g. (1 2),(3 4)
- \note Used in polygon, all multi-geometries
- */
- template <typename Range, bool ForceClosurePossible = true>
- struct wkt_sequence
- : wkt_range
- <
- Range,
- prefix_null,
- ForceClosurePossible
- >
- {};
- template <typename Polygon, typename PrefixPolicy>
- struct wkt_poly
- {
- template <typename Char, typename Traits>
- static inline void apply(std::basic_ostream<Char, Traits>& os,
- Polygon const& poly, bool force_closure)
- {
- using ring = typename ring_type<Polygon const>::type;
- auto const& exterior = exterior_ring(poly);
- auto const& rings = interior_rings(poly);
- std::size_t point_count = boost::size(exterior);
- for (auto it = boost::begin(rings); it != boost::end(rings); ++it)
- {
- point_count += boost::size(*it);
- }
- os << PrefixPolicy::apply();
- os << "(";
- if (point_count > 0)
- {
- wkt_sequence<ring>::apply(os, exterior, force_closure);
- for (auto it = boost::begin(rings); it != boost::end(rings); ++it)
- {
- os << ",";
- wkt_sequence<ring>::apply(os, *it, force_closure);
- }
- }
- os << ")";
- }
- };
- template <typename Multi, typename StreamPolicy, typename PrefixPolicy>
- struct wkt_multi
- {
- template <typename Char, typename Traits>
- static inline void apply(std::basic_ostream<Char, Traits>& os,
- Multi const& geometry, bool force_closure)
- {
- os << PrefixPolicy::apply();
- os << "(";
- for (auto it = boost::begin(geometry); it != boost::end(geometry); ++it)
- {
- if (it != boost::begin(geometry))
- {
- os << ",";
- }
- StreamPolicy::apply(os, *it, force_closure);
- }
- os << ")";
- }
- };
- template <typename Box>
- struct wkt_box
- {
- using point_type = typename point_type<Box>::type;
- template <typename Char, typename Traits>
- static inline void apply(std::basic_ostream<Char, Traits>& os,
- Box const& box, bool force_closure)
- {
- // Convert to a clockwire ring, then stream.
- // Never close it based on last point (box might be empty and
- // that should result in POLYGON((0 0,0 0,0 0,0 0, ...)) )
- if (force_closure)
- {
- do_apply<model::ring<point_type, true, true> >(os, box);
- }
- else
- {
- do_apply<model::ring<point_type, true, false> >(os, box);
- }
- }
- private:
- inline wkt_box()
- {
- // Only streaming of boxes with two dimensions is support, otherwise it is a polyhedron!
- //assert_dimension<B, 2>();
- }
- template <typename RingType, typename Char, typename Traits>
- static inline void do_apply(std::basic_ostream<Char, Traits>& os,
- Box const& box)
- {
- RingType ring;
- geometry::convert(box, ring);
- os << "POLYGON(";
- wkt_sequence<RingType, false>::apply(os, ring);
- os << ")";
- }
- };
- template <typename Segment>
- struct wkt_segment
- {
- using point_type = typename point_type<Segment>::type;
- template <typename Char, typename Traits>
- static inline void apply(std::basic_ostream<Char, Traits>& os,
- Segment const& segment, bool)
- {
- // Convert to two points, then stream
- using sequence = std::array<point_type, 2>;
- sequence points;
- geometry::detail::assign_point_from_index<0>(segment, points[0]);
- geometry::detail::assign_point_from_index<1>(segment, points[1]);
- // In Boost.Geometry a segment is represented
- // in WKT-format like (for 2D): LINESTRING(x y,x y)
- os << "LINESTRING";
- wkt_sequence<sequence, false>::apply(os, points);
- }
- private:
- inline wkt_segment()
- {}
- };
- }} // namespace detail::wkt
- #endif // DOXYGEN_NO_DETAIL
- #ifndef DOXYGEN_NO_DISPATCH
- namespace dispatch
- {
- template <typename Geometry, typename Tag = typename tag<Geometry>::type>
- struct wkt: not_implemented<Tag>
- {};
- template <typename Point>
- struct wkt<Point, point_tag>
- : detail::wkt::wkt_point
- <
- Point,
- detail::wkt::prefix_point
- >
- {};
- template <typename Linestring>
- struct wkt<Linestring, linestring_tag>
- : detail::wkt::wkt_range
- <
- Linestring,
- detail::wkt::prefix_linestring
- >
- {};
- /*!
- \brief Specialization to stream a box as WKT
- \details A "box" does not exist in WKT.
- It is therefore streamed as a polygon
- */
- template <typename Box>
- struct wkt<Box, box_tag>
- : detail::wkt::wkt_box<Box>
- {};
- template <typename Segment>
- struct wkt<Segment, segment_tag>
- : detail::wkt::wkt_segment<Segment>
- {};
- /*!
- \brief Specialization to stream a ring as WKT
- \details A ring or "linear_ring" does not exist in WKT.
- A ring is equivalent to a polygon without inner rings
- It is therefore streamed as a polygon
- */
- template <typename Ring>
- struct wkt<Ring, ring_tag>
- : detail::wkt::wkt_range
- <
- Ring,
- detail::wkt::prefix_polygon,
- true,
- true
- >
- {};
- /*!
- \brief Specialization to stream polygon as WKT
- */
- template <typename Polygon>
- struct wkt<Polygon, polygon_tag>
- : detail::wkt::wkt_poly
- <
- Polygon,
- detail::wkt::prefix_polygon
- >
- {};
- template <typename Multi>
- struct wkt<Multi, multi_point_tag>
- : detail::wkt::wkt_multi
- <
- Multi,
- detail::wkt::wkt_point
- <
- typename boost::range_value<Multi>::type,
- detail::wkt::prefix_null
- >,
- detail::wkt::prefix_multipoint
- >
- {};
- template <typename Multi>
- struct wkt<Multi, multi_linestring_tag>
- : detail::wkt::wkt_multi
- <
- Multi,
- detail::wkt::wkt_sequence
- <
- typename boost::range_value<Multi>::type,
- false
- >,
- detail::wkt::prefix_multilinestring
- >
- {};
- template <typename Multi>
- struct wkt<Multi, multi_polygon_tag>
- : detail::wkt::wkt_multi
- <
- Multi,
- detail::wkt::wkt_poly
- <
- typename boost::range_value<Multi>::type,
- detail::wkt::prefix_null
- >,
- detail::wkt::prefix_multipolygon
- >
- {};
- template <typename Geometry>
- struct wkt<Geometry, dynamic_geometry_tag>
- {
- template <typename OutputStream>
- static inline void apply(OutputStream& os, Geometry const& geometry,
- bool force_closure)
- {
- traits::visit<Geometry>::apply([&](auto const& g)
- {
- wkt<util::remove_cref_t<decltype(g)>>::apply(os, g, force_closure);
- }, geometry);
- }
- };
- // TODO: Implement non-recursive version
- template <typename Geometry>
- struct wkt<Geometry, geometry_collection_tag>
- {
- template <typename OutputStream>
- static inline void apply(OutputStream& os, Geometry const& geometry,
- bool force_closure)
- {
- wkt::output_or_recursive_call(os, geometry, force_closure);
- }
- private:
- template
- <
- typename OutputStream, typename Geom,
- std::enable_if_t<util::is_geometry_collection<Geom>::value, int> = 0
- >
- static void output_or_recursive_call(OutputStream& os, Geom const& geom, bool force_closure)
- {
- os << "GEOMETRYCOLLECTION(";
- bool first = true;
- auto const end = boost::end(geom);
- for (auto it = boost::begin(geom); it != end; ++it)
- {
- if (first)
- first = false;
- else
- os << ',';
- traits::iter_visit<Geom>::apply([&](auto const& g)
- {
- wkt::output_or_recursive_call(os, g, force_closure);
- }, it);
- }
- os << ')';
- }
- template
- <
- typename OutputStream, typename Geom,
- std::enable_if_t<! util::is_geometry_collection<Geom>::value, int> = 0
- >
- static void output_or_recursive_call(OutputStream& os, Geom const& geom, bool force_closure)
- {
- wkt<Geom>::apply(os, geom, force_closure);
- }
- };
- } // namespace dispatch
- #endif // DOXYGEN_NO_DISPATCH
- /*!
- \brief Generic geometry template manipulator class, takes corresponding output class from traits class
- \ingroup wkt
- \details Stream manipulator, streams geometry classes as \ref WKT streams
- \par Example:
- Small example showing how to use the wkt class
- \dontinclude doxygen_1.cpp
- \skip example_as_wkt_point
- \line {
- \until }
- */
- template <typename Geometry>
- class wkt_manipulator
- {
- static const bool is_ring = util::is_ring<Geometry>::value;
- public:
- // Boost.Geometry, by default, closes polygons explictly, but not rings
- // NOTE: this might change in the future!
- inline wkt_manipulator(Geometry const& g,
- bool force_closure = ! is_ring)
- : m_geometry(g)
- , m_force_closure(force_closure)
- {}
- template <typename Char, typename Traits>
- inline friend std::basic_ostream<Char, Traits>& operator<<(
- std::basic_ostream<Char, Traits>& os,
- wkt_manipulator const& m)
- {
- dispatch::wkt<Geometry>::apply(os, m.m_geometry, m.m_force_closure);
- os.flush();
- return os;
- }
- private:
- Geometry const& m_geometry;
- bool m_force_closure;
- };
- /*!
- \brief Main WKT-streaming function
- \tparam Geometry \tparam_geometry
- \param geometry \param_geometry
- \ingroup wkt
- \qbk{[include reference/io/wkt.qbk]}
- */
- template <typename Geometry>
- inline wkt_manipulator<Geometry> wkt(Geometry const& geometry)
- {
- concepts::check<Geometry const>();
- return wkt_manipulator<Geometry>(geometry);
- }
- /*!
- \brief WKT-string formulating function
- \tparam Geometry \tparam_geometry
- \param geometry \param_geometry
- \param significant_digits Specifies the no of significant digits to use in the output wkt
- \ingroup wkt
- \qbk{[include reference/io/to_wkt.qbk]}
- */
- template <typename Geometry>
- inline std::string to_wkt(Geometry const& geometry)
- {
- std::stringstream ss;
- ss << boost::geometry::wkt(geometry);
- return ss.str();
- }
- template <typename Geometry>
- inline std::string to_wkt(Geometry const& geometry, int significant_digits)
- {
- std::stringstream ss;
- ss.precision(significant_digits);
- ss << boost::geometry::wkt(geometry);
- return ss.str();
- }
- #if defined(_MSC_VER)
- #pragma warning(pop)
- #endif
- }} // namespace boost::geometry
- #endif // BOOST_GEOMETRY_IO_WKT_WRITE_HPP
|