1 // Boost.Geometry (aka GGL, Generic Geometry Library)
3 // Copyright (c) 2007-2017 Barend Gehrels, Amsterdam, the Netherlands.
4 // Copyright (c) 2008-2017 Bruno Lalande, Paris, France.
5 // Copyright (c) 2009-2017 Mateusz Loskot, London, UK.
6 // Copyright (c) 2014-2017 Adam Wulkiewicz, Lodz, Poland.
7 // Copyright (c) 2020 Baidyanath Kundu, Haldia, India.
9 // This file was modified by Oracle on 2015-2021.
10 // Modifications copyright (c) 2015-2021, Oracle and/or its affiliates.
12 // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
13 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
15 // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
16 // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
18 // Use, modification and distribution is subject to the Boost Software License,
19 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
20 // http://www.boost.org/LICENSE_1_0.txt)
22 #ifndef BOOST_GEOMETRY_IO_WKT_WRITE_HPP
23 #define BOOST_GEOMETRY_IO_WKT_WRITE_HPP
28 #include <boost/array.hpp>
29 #include <boost/range/begin.hpp>
30 #include <boost/range/end.hpp>
31 #include <boost/range/size.hpp>
32 #include <boost/range/value_type.hpp>
34 #include <boost/geometry/algorithms/detail/interior_iterator.hpp>
35 #include <boost/geometry/algorithms/assign.hpp>
36 #include <boost/geometry/algorithms/convert.hpp>
37 #include <boost/geometry/algorithms/detail/disjoint/point_point.hpp>
38 #include <boost/geometry/algorithms/not_implemented.hpp>
39 #include <boost/geometry/core/exterior_ring.hpp>
40 #include <boost/geometry/core/interior_rings.hpp>
41 #include <boost/geometry/core/ring_type.hpp>
42 #include <boost/geometry/core/tags.hpp>
43 #include <boost/geometry/core/visit.hpp>
45 #include <boost/geometry/geometries/adapted/boost_variant.hpp> // For backward compatibility
46 #include <boost/geometry/geometries/concepts/check.hpp>
47 #include <boost/geometry/geometries/ring.hpp>
49 #include <boost/geometry/io/wkt/detail/prefix.hpp>
51 #include <boost/geometry/strategies/io/cartesian.hpp>
52 #include <boost/geometry/strategies/io/geographic.hpp>
53 #include <boost/geometry/strategies/io/spherical.hpp>
55 #include <boost/geometry/util/condition.hpp>
56 #include <boost/geometry/util/type_traits.hpp>
59 namespace boost { namespace geometry
62 // Silence warning C4512: 'boost::geometry::wkt_manipulator<Geometry>' : assignment operator could not be generated
65 #pragma warning(disable : 4512)
68 #ifndef DOXYGEN_NO_DETAIL
69 namespace detail { namespace wkt
72 template <typename P, int I, int Count>
73 struct stream_coordinate
75 template <typename Char, typename Traits>
76 static inline void apply(std::basic_ostream<Char, Traits>& os, P const& p)
78 os << (I > 0 ? " " : "") << get<I>(p);
79 stream_coordinate<P, I + 1, Count>::apply(os, p);
83 template <typename P, int Count>
84 struct stream_coordinate<P, Count, Count>
86 template <typename Char, typename Traits>
87 static inline void apply(std::basic_ostream<Char, Traits>&, P const&)
91 struct prefix_linestring_par
93 static inline const char* apply() { return "LINESTRING("; }
96 struct prefix_ring_par_par
98 // Note, double parentheses are intentional, indicating WKT ring begin/end
99 static inline const char* apply() { return "POLYGON(("; }
102 struct opening_parenthesis
104 static inline const char* apply() { return "("; }
107 struct closing_parenthesis
109 static inline const char* apply() { return ")"; }
112 struct double_closing_parenthesis
114 static inline const char* apply() { return "))"; }
118 \brief Stream points as \ref WKT
120 template <typename Point, typename Policy>
123 template <typename Char, typename Traits>
124 static inline void apply(std::basic_ostream<Char, Traits>& os, Point const& p, bool)
126 os << Policy::apply() << "(";
127 stream_coordinate<Point, 0, dimension<Point>::type::value>::apply(os, p);
133 \brief Stream ranges as WKT
134 \note policy is used to stream prefix/postfix, enabling derived classes to override this
139 bool ForceClosurePossible,
140 typename PrefixPolicy,
141 typename SuffixPolicy
145 template <typename Char, typename Traits>
146 static inline void apply(std::basic_ostream<Char, Traits>& os,
147 Range const& range, bool force_closure = ForceClosurePossible)
149 typedef typename boost::range_iterator<Range const>::type iterator_type;
151 typedef stream_coordinate
153 point_type, 0, dimension<point_type>::type::value
158 os << PrefixPolicy::apply();
160 // TODO: check EMPTY here
162 iterator_type begin = boost::begin(range);
163 iterator_type end = boost::end(range);
164 for (iterator_type it = begin; it != end; ++it)
166 os << (first ? "" : ",");
167 stream_type::apply(os, *it);
171 // optionally, close range to ring by repeating the first point
172 if (BOOST_GEOMETRY_CONDITION(ForceClosurePossible)
174 && boost::size(range) > 1
175 && wkt_range::disjoint(*begin, *(end - 1)))
178 stream_type::apply(os, *begin);
181 os << SuffixPolicy::apply();
186 typedef typename boost::range_value<Range>::type point_type;
188 static inline bool disjoint(point_type const& p1, point_type const& p2)
190 // TODO: pass strategy
191 typedef typename strategies::io::services::default_strategy
194 >::type strategy_type;
196 return detail::disjoint::disjoint_point_point(p1, p2, strategy_type());
201 \brief Stream sequence of points as WKT-part, e.g. (1 2),(3 4)
202 \note Used in polygon, all multi-geometries
204 template <typename Range, bool ForceClosurePossible = true>
209 ForceClosurePossible,
215 template <typename Polygon, typename PrefixPolicy>
218 template <typename Char, typename Traits>
219 static inline void apply(std::basic_ostream<Char, Traits>& os,
220 Polygon const& poly, bool force_closure)
222 typedef typename ring_type<Polygon const>::type ring;
224 os << PrefixPolicy::apply();
225 // TODO: check EMPTY here
227 wkt_sequence<ring>::apply(os, exterior_ring(poly), force_closure);
229 typename interior_return_type<Polygon const>::type
230 rings = interior_rings(poly);
231 for (typename detail::interior_iterator<Polygon const>::type
232 it = boost::begin(rings); it != boost::end(rings); ++it)
235 wkt_sequence<ring>::apply(os, *it, force_closure);
242 template <typename Multi, typename StreamPolicy, typename PrefixPolicy>
245 template <typename Char, typename Traits>
246 static inline void apply(std::basic_ostream<Char, Traits>& os,
247 Multi const& geometry, bool force_closure)
249 os << PrefixPolicy::apply();
250 // TODO: check EMPTY here
253 for (typename boost::range_iterator<Multi const>::type
254 it = boost::begin(geometry);
255 it != boost::end(geometry);
258 if (it != boost::begin(geometry))
262 StreamPolicy::apply(os, *it, force_closure);
269 template <typename Box>
272 typedef typename point_type<Box>::type point_type;
274 template <typename Char, typename Traits>
275 static inline void apply(std::basic_ostream<Char, Traits>& os,
276 Box const& box, bool force_closure)
278 // Convert to a clockwire ring, then stream.
279 // Never close it based on last point (box might be empty and
280 // that should result in POLYGON((0 0,0 0,0 0,0 0, ...)) )
283 do_apply<model::ring<point_type, true, true> >(os, box);
287 do_apply<model::ring<point_type, true, false> >(os, box);
295 // Only streaming of boxes with two dimensions is support, otherwise it is a polyhedron!
296 //assert_dimension<B, 2>();
299 template <typename RingType, typename Char, typename Traits>
300 static inline void do_apply(std::basic_ostream<Char, Traits>& os,
304 geometry::convert(box, ring);
306 wkt_sequence<RingType, false>::apply(os, ring);
313 template <typename Segment>
316 typedef typename point_type<Segment>::type point_type;
318 template <typename Char, typename Traits>
319 static inline void apply(std::basic_ostream<Char, Traits>& os,
320 Segment const& segment, bool)
322 // Convert to two points, then stream
323 typedef boost::array<point_type, 2> sequence;
326 geometry::detail::assign_point_from_index<0>(segment, points[0]);
327 geometry::detail::assign_point_from_index<1>(segment, points[1]);
329 // In Boost.Geometry a segment is represented
330 // in WKT-format like (for 2D): LINESTRING(x y,x y)
332 wkt_sequence<sequence, false>::apply(os, points);
341 }} // namespace detail::wkt
342 #endif // DOXYGEN_NO_DETAIL
344 #ifndef DOXYGEN_NO_DISPATCH
348 template <typename Geometry, typename Tag = typename tag<Geometry>::type>
349 struct wkt: not_implemented<Tag>
352 template <typename Point>
353 struct wkt<Point, point_tag>
354 : detail::wkt::wkt_point
357 detail::wkt::prefix_point
361 template <typename Linestring>
362 struct wkt<Linestring, linestring_tag>
363 : detail::wkt::wkt_range
367 detail::wkt::prefix_linestring_par,
368 detail::wkt::closing_parenthesis
373 \brief Specialization to stream a box as WKT
374 \details A "box" does not exist in WKT.
375 It is therefore streamed as a polygon
377 template <typename Box>
378 struct wkt<Box, box_tag>
379 : detail::wkt::wkt_box<Box>
382 template <typename Segment>
383 struct wkt<Segment, segment_tag>
384 : detail::wkt::wkt_segment<Segment>
388 \brief Specialization to stream a ring as WKT
389 \details A ring or "linear_ring" does not exist in WKT.
390 A ring is equivalent to a polygon without inner rings
391 It is therefore streamed as a polygon
393 template <typename Ring>
394 struct wkt<Ring, ring_tag>
395 : detail::wkt::wkt_range
399 detail::wkt::prefix_ring_par_par,
400 detail::wkt::double_closing_parenthesis
405 \brief Specialization to stream polygon as WKT
407 template <typename Polygon>
408 struct wkt<Polygon, polygon_tag>
409 : detail::wkt::wkt_poly
412 detail::wkt::prefix_polygon
416 template <typename Multi>
417 struct wkt<Multi, multi_point_tag>
418 : detail::wkt::wkt_multi
421 detail::wkt::wkt_point
423 typename boost::range_value<Multi>::type,
424 detail::wkt::prefix_null
426 detail::wkt::prefix_multipoint
430 template <typename Multi>
431 struct wkt<Multi, multi_linestring_tag>
432 : detail::wkt::wkt_multi
435 detail::wkt::wkt_sequence
437 typename boost::range_value<Multi>::type,
440 detail::wkt::prefix_multilinestring
444 template <typename Multi>
445 struct wkt<Multi, multi_polygon_tag>
446 : detail::wkt::wkt_multi
449 detail::wkt::wkt_poly
451 typename boost::range_value<Multi>::type,
452 detail::wkt::prefix_null
454 detail::wkt::prefix_multipolygon
459 template <typename Geometry>
460 struct wkt<Geometry, dynamic_geometry_tag>
462 template <typename OutputStream>
463 static inline void apply(OutputStream& os, Geometry const& geometry,
466 traits::visit<Geometry>::apply([&](auto const& g)
468 wkt<util::remove_cref_t<decltype(g)>>::apply(os, g, force_closure);
473 // TODO: Implement non-recursive version
474 template <typename Geometry>
475 struct wkt<Geometry, geometry_collection_tag>
477 template <typename OutputStream>
478 static inline void apply(OutputStream& os, Geometry const& geometry,
481 wkt::output_or_recursive_call(os, geometry, force_closure);
487 typename OutputStream, typename Geom,
488 std::enable_if_t<util::is_geometry_collection<Geom>::value, int> = 0
490 static void output_or_recursive_call(OutputStream& os, Geom const& geom, bool force_closure)
492 os << "GEOMETRYCOLLECTION(";
495 auto const end = boost::end(geom);
496 for (auto it = boost::begin(geom); it != end; ++it)
503 traits::iter_visit<Geom>::apply([&](auto const& g)
505 wkt::output_or_recursive_call(os, g, force_closure);
514 typename OutputStream, typename Geom,
515 std::enable_if_t<! util::is_geometry_collection<Geom>::value, int> = 0
517 static void output_or_recursive_call(OutputStream& os, Geom const& geom, bool force_closure)
519 wkt<Geom>::apply(os, geom, force_closure);
524 } // namespace dispatch
525 #endif // DOXYGEN_NO_DISPATCH
528 \brief Generic geometry template manipulator class, takes corresponding output class from traits class
530 \details Stream manipulator, streams geometry classes as \ref WKT streams
532 Small example showing how to use the wkt class
533 \dontinclude doxygen_1.cpp
534 \skip example_as_wkt_point
538 template <typename Geometry>
539 class wkt_manipulator
541 static const bool is_ring = util::is_ring<Geometry>::value;
545 // Boost.Geometry, by default, closes polygons explictly, but not rings
546 // NOTE: this might change in the future!
547 inline wkt_manipulator(Geometry const& g,
548 bool force_closure = ! is_ring)
550 , m_force_closure(force_closure)
553 template <typename Char, typename Traits>
554 inline friend std::basic_ostream<Char, Traits>& operator<<(
555 std::basic_ostream<Char, Traits>& os,
556 wkt_manipulator const& m)
558 dispatch::wkt<Geometry>::apply(os, m.m_geometry, m.m_force_closure);
564 Geometry const& m_geometry;
565 bool m_force_closure;
569 \brief Main WKT-streaming function
570 \tparam Geometry \tparam_geometry
571 \param geometry \param_geometry
573 \qbk{[include reference/io/wkt.qbk]}
575 template <typename Geometry>
576 inline wkt_manipulator<Geometry> wkt(Geometry const& geometry)
578 concepts::check<Geometry const>();
580 return wkt_manipulator<Geometry>(geometry);
584 \brief WKT-string formulating function
585 \tparam Geometry \tparam_geometry
586 \param geometry \param_geometry
587 \param significant_digits Specifies the no of significant digits to use in the output wkt
589 \qbk{[include reference/io/to_wkt.qbk]}
591 template <typename Geometry>
592 inline std::string to_wkt(Geometry const& geometry)
594 std::stringstream ss;
595 ss << boost::geometry::wkt(geometry);
599 template <typename Geometry>
600 inline std::string to_wkt(Geometry const& geometry, int significant_digits)
602 std::stringstream ss;
603 ss.precision(significant_digits);
604 ss << boost::geometry::wkt(geometry);
608 #if defined(_MSC_VER)
612 }} // namespace boost::geometry
614 #endif // BOOST_GEOMETRY_IO_WKT_WRITE_HPP