write.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2022 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2008-2017 Bruno Lalande, Paris, France.
  4. // Copyright (c) 2009-2017 Mateusz Loskot, London, UK.
  5. // Copyright (c) 2014-2023 Adam Wulkiewicz, Lodz, Poland.
  6. // Copyright (c) 2020 Baidyanath Kundu, Haldia, India.
  7. // This file was modified by Oracle on 2015-2021.
  8. // Modifications copyright (c) 2015-2021, Oracle and/or its affiliates.
  9. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  10. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  11. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
  12. // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
  13. // Use, modification and distribution is subject to the Boost Software License,
  14. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  15. // http://www.boost.org/LICENSE_1_0.txt)
  16. #ifndef BOOST_GEOMETRY_IO_WKT_WRITE_HPP
  17. #define BOOST_GEOMETRY_IO_WKT_WRITE_HPP
  18. #include <array>
  19. #include <ostream>
  20. #include <string>
  21. #include <boost/range/begin.hpp>
  22. #include <boost/range/end.hpp>
  23. #include <boost/range/size.hpp>
  24. #include <boost/range/value_type.hpp>
  25. #include <boost/geometry/algorithms/assign.hpp>
  26. #include <boost/geometry/algorithms/convert.hpp>
  27. #include <boost/geometry/algorithms/detail/disjoint/point_point.hpp>
  28. #include <boost/geometry/algorithms/not_implemented.hpp>
  29. #include <boost/geometry/core/exterior_ring.hpp>
  30. #include <boost/geometry/core/interior_rings.hpp>
  31. #include <boost/geometry/core/ring_type.hpp>
  32. #include <boost/geometry/core/tags.hpp>
  33. #include <boost/geometry/core/visit.hpp>
  34. #include <boost/geometry/geometries/adapted/boost_variant.hpp> // For backward compatibility
  35. #include <boost/geometry/geometries/concepts/check.hpp>
  36. #include <boost/geometry/geometries/ring.hpp>
  37. #include <boost/geometry/io/wkt/detail/prefix.hpp>
  38. #include <boost/geometry/strategies/io/cartesian.hpp>
  39. #include <boost/geometry/strategies/io/geographic.hpp>
  40. #include <boost/geometry/strategies/io/spherical.hpp>
  41. #include <boost/geometry/util/constexpr.hpp>
  42. #include <boost/geometry/util/type_traits.hpp>
  43. namespace boost { namespace geometry
  44. {
  45. // Silence warning C4512: 'boost::geometry::wkt_manipulator<Geometry>' : assignment operator could not be generated
  46. #if defined(_MSC_VER)
  47. #pragma warning(push)
  48. #pragma warning(disable : 4512)
  49. #endif
  50. #ifndef DOXYGEN_NO_DETAIL
  51. namespace detail { namespace wkt
  52. {
  53. template <typename P, int I, int Count>
  54. struct stream_coordinate
  55. {
  56. template <typename Char, typename Traits>
  57. static inline void apply(std::basic_ostream<Char, Traits>& os, P const& p)
  58. {
  59. os << (I > 0 ? " " : "") << get<I>(p);
  60. stream_coordinate<P, I + 1, Count>::apply(os, p);
  61. }
  62. };
  63. template <typename P, int Count>
  64. struct stream_coordinate<P, Count, Count>
  65. {
  66. template <typename Char, typename Traits>
  67. static inline void apply(std::basic_ostream<Char, Traits>&, P const&)
  68. {}
  69. };
  70. /*!
  71. \brief Stream points as \ref WKT
  72. */
  73. template <typename Point, typename Policy>
  74. struct wkt_point
  75. {
  76. template <typename Char, typename Traits>
  77. static inline void apply(std::basic_ostream<Char, Traits>& os, Point const& p, bool)
  78. {
  79. os << Policy::apply() << "(";
  80. stream_coordinate<Point, 0, dimension<Point>::type::value>::apply(os, p);
  81. os << ")";
  82. }
  83. };
  84. /*!
  85. \brief Stream ranges as WKT
  86. */
  87. template
  88. <
  89. typename Range,
  90. typename PrefixPolicy,
  91. bool ForceClosurePossible = false,
  92. bool WriteDoubleBrackets = false
  93. >
  94. struct wkt_range
  95. {
  96. template <typename Char, typename Traits>
  97. static inline void apply(std::basic_ostream<Char, Traits>& os,
  98. Range const& range, bool force_closure = ForceClosurePossible)
  99. {
  100. using stream_type = stream_coordinate
  101. <
  102. point_type, 0, dimension<point_type>::type::value
  103. >;
  104. bool first = true;
  105. os << PrefixPolicy::apply();
  106. os << "(";
  107. if (boost::size(range) > 0)
  108. {
  109. if BOOST_GEOMETRY_CONSTEXPR (WriteDoubleBrackets)
  110. {
  111. os << "(";
  112. }
  113. auto begin = boost::begin(range);
  114. auto const end = boost::end(range);
  115. for (auto it = begin; it != end; ++it)
  116. {
  117. os << (first ? "" : ",");
  118. stream_type::apply(os, *it);
  119. first = false;
  120. }
  121. // optionally, close range to ring by repeating the first point
  122. if BOOST_GEOMETRY_CONSTEXPR (ForceClosurePossible)
  123. {
  124. if (force_closure
  125. && boost::size(range) > 1
  126. && wkt_range::disjoint(*begin, *(end - 1)))
  127. {
  128. os << ",";
  129. stream_type::apply(os, *begin);
  130. }
  131. }
  132. if BOOST_GEOMETRY_CONSTEXPR (WriteDoubleBrackets)
  133. {
  134. os << ")";
  135. }
  136. }
  137. os << ")";
  138. }
  139. private:
  140. using point_type = typename boost::range_value<Range>::type;
  141. static inline bool disjoint(point_type const& p1, point_type const& p2)
  142. {
  143. // TODO: pass strategy
  144. using strategy_type = typename strategies::io::services::default_strategy
  145. <
  146. point_type
  147. >::type;
  148. return detail::disjoint::disjoint_point_point(p1, p2, strategy_type());
  149. }
  150. };
  151. /*!
  152. \brief Stream sequence of points as WKT-part, e.g. (1 2),(3 4)
  153. \note Used in polygon, all multi-geometries
  154. */
  155. template <typename Range, bool ForceClosurePossible = true>
  156. struct wkt_sequence
  157. : wkt_range
  158. <
  159. Range,
  160. prefix_null,
  161. ForceClosurePossible
  162. >
  163. {};
  164. template <typename Polygon, typename PrefixPolicy>
  165. struct wkt_poly
  166. {
  167. template <typename Char, typename Traits>
  168. static inline void apply(std::basic_ostream<Char, Traits>& os,
  169. Polygon const& poly, bool force_closure)
  170. {
  171. using ring = typename ring_type<Polygon const>::type;
  172. auto const& exterior = exterior_ring(poly);
  173. auto const& rings = interior_rings(poly);
  174. std::size_t point_count = boost::size(exterior);
  175. for (auto it = boost::begin(rings); it != boost::end(rings); ++it)
  176. {
  177. point_count += boost::size(*it);
  178. }
  179. os << PrefixPolicy::apply();
  180. os << "(";
  181. if (point_count > 0)
  182. {
  183. wkt_sequence<ring>::apply(os, exterior, force_closure);
  184. for (auto it = boost::begin(rings); it != boost::end(rings); ++it)
  185. {
  186. os << ",";
  187. wkt_sequence<ring>::apply(os, *it, force_closure);
  188. }
  189. }
  190. os << ")";
  191. }
  192. };
  193. template <typename Multi, typename StreamPolicy, typename PrefixPolicy>
  194. struct wkt_multi
  195. {
  196. template <typename Char, typename Traits>
  197. static inline void apply(std::basic_ostream<Char, Traits>& os,
  198. Multi const& geometry, bool force_closure)
  199. {
  200. os << PrefixPolicy::apply();
  201. os << "(";
  202. for (auto it = boost::begin(geometry); it != boost::end(geometry); ++it)
  203. {
  204. if (it != boost::begin(geometry))
  205. {
  206. os << ",";
  207. }
  208. StreamPolicy::apply(os, *it, force_closure);
  209. }
  210. os << ")";
  211. }
  212. };
  213. template <typename Box>
  214. struct wkt_box
  215. {
  216. using point_type = typename point_type<Box>::type;
  217. template <typename Char, typename Traits>
  218. static inline void apply(std::basic_ostream<Char, Traits>& os,
  219. Box const& box, bool force_closure)
  220. {
  221. // Convert to a clockwire ring, then stream.
  222. // Never close it based on last point (box might be empty and
  223. // that should result in POLYGON((0 0,0 0,0 0,0 0, ...)) )
  224. if (force_closure)
  225. {
  226. do_apply<model::ring<point_type, true, true> >(os, box);
  227. }
  228. else
  229. {
  230. do_apply<model::ring<point_type, true, false> >(os, box);
  231. }
  232. }
  233. private:
  234. inline wkt_box()
  235. {
  236. // Only streaming of boxes with two dimensions is support, otherwise it is a polyhedron!
  237. //assert_dimension<B, 2>();
  238. }
  239. template <typename RingType, typename Char, typename Traits>
  240. static inline void do_apply(std::basic_ostream<Char, Traits>& os,
  241. Box const& box)
  242. {
  243. RingType ring;
  244. geometry::convert(box, ring);
  245. os << "POLYGON(";
  246. wkt_sequence<RingType, false>::apply(os, ring);
  247. os << ")";
  248. }
  249. };
  250. template <typename Segment>
  251. struct wkt_segment
  252. {
  253. using point_type = typename point_type<Segment>::type;
  254. template <typename Char, typename Traits>
  255. static inline void apply(std::basic_ostream<Char, Traits>& os,
  256. Segment const& segment, bool)
  257. {
  258. // Convert to two points, then stream
  259. using sequence = std::array<point_type, 2>;
  260. sequence points;
  261. geometry::detail::assign_point_from_index<0>(segment, points[0]);
  262. geometry::detail::assign_point_from_index<1>(segment, points[1]);
  263. // In Boost.Geometry a segment is represented
  264. // in WKT-format like (for 2D): LINESTRING(x y,x y)
  265. os << "LINESTRING";
  266. wkt_sequence<sequence, false>::apply(os, points);
  267. }
  268. private:
  269. inline wkt_segment()
  270. {}
  271. };
  272. }} // namespace detail::wkt
  273. #endif // DOXYGEN_NO_DETAIL
  274. #ifndef DOXYGEN_NO_DISPATCH
  275. namespace dispatch
  276. {
  277. template <typename Geometry, typename Tag = typename tag<Geometry>::type>
  278. struct wkt: not_implemented<Tag>
  279. {};
  280. template <typename Point>
  281. struct wkt<Point, point_tag>
  282. : detail::wkt::wkt_point
  283. <
  284. Point,
  285. detail::wkt::prefix_point
  286. >
  287. {};
  288. template <typename Linestring>
  289. struct wkt<Linestring, linestring_tag>
  290. : detail::wkt::wkt_range
  291. <
  292. Linestring,
  293. detail::wkt::prefix_linestring
  294. >
  295. {};
  296. /*!
  297. \brief Specialization to stream a box as WKT
  298. \details A "box" does not exist in WKT.
  299. It is therefore streamed as a polygon
  300. */
  301. template <typename Box>
  302. struct wkt<Box, box_tag>
  303. : detail::wkt::wkt_box<Box>
  304. {};
  305. template <typename Segment>
  306. struct wkt<Segment, segment_tag>
  307. : detail::wkt::wkt_segment<Segment>
  308. {};
  309. /*!
  310. \brief Specialization to stream a ring as WKT
  311. \details A ring or "linear_ring" does not exist in WKT.
  312. A ring is equivalent to a polygon without inner rings
  313. It is therefore streamed as a polygon
  314. */
  315. template <typename Ring>
  316. struct wkt<Ring, ring_tag>
  317. : detail::wkt::wkt_range
  318. <
  319. Ring,
  320. detail::wkt::prefix_polygon,
  321. true,
  322. true
  323. >
  324. {};
  325. /*!
  326. \brief Specialization to stream polygon as WKT
  327. */
  328. template <typename Polygon>
  329. struct wkt<Polygon, polygon_tag>
  330. : detail::wkt::wkt_poly
  331. <
  332. Polygon,
  333. detail::wkt::prefix_polygon
  334. >
  335. {};
  336. template <typename Multi>
  337. struct wkt<Multi, multi_point_tag>
  338. : detail::wkt::wkt_multi
  339. <
  340. Multi,
  341. detail::wkt::wkt_point
  342. <
  343. typename boost::range_value<Multi>::type,
  344. detail::wkt::prefix_null
  345. >,
  346. detail::wkt::prefix_multipoint
  347. >
  348. {};
  349. template <typename Multi>
  350. struct wkt<Multi, multi_linestring_tag>
  351. : detail::wkt::wkt_multi
  352. <
  353. Multi,
  354. detail::wkt::wkt_sequence
  355. <
  356. typename boost::range_value<Multi>::type,
  357. false
  358. >,
  359. detail::wkt::prefix_multilinestring
  360. >
  361. {};
  362. template <typename Multi>
  363. struct wkt<Multi, multi_polygon_tag>
  364. : detail::wkt::wkt_multi
  365. <
  366. Multi,
  367. detail::wkt::wkt_poly
  368. <
  369. typename boost::range_value<Multi>::type,
  370. detail::wkt::prefix_null
  371. >,
  372. detail::wkt::prefix_multipolygon
  373. >
  374. {};
  375. template <typename Geometry>
  376. struct wkt<Geometry, dynamic_geometry_tag>
  377. {
  378. template <typename OutputStream>
  379. static inline void apply(OutputStream& os, Geometry const& geometry,
  380. bool force_closure)
  381. {
  382. traits::visit<Geometry>::apply([&](auto const& g)
  383. {
  384. wkt<util::remove_cref_t<decltype(g)>>::apply(os, g, force_closure);
  385. }, geometry);
  386. }
  387. };
  388. // TODO: Implement non-recursive version
  389. template <typename Geometry>
  390. struct wkt<Geometry, geometry_collection_tag>
  391. {
  392. template <typename OutputStream>
  393. static inline void apply(OutputStream& os, Geometry const& geometry,
  394. bool force_closure)
  395. {
  396. wkt::output_or_recursive_call(os, geometry, force_closure);
  397. }
  398. private:
  399. template
  400. <
  401. typename OutputStream, typename Geom,
  402. std::enable_if_t<util::is_geometry_collection<Geom>::value, int> = 0
  403. >
  404. static void output_or_recursive_call(OutputStream& os, Geom const& geom, bool force_closure)
  405. {
  406. os << "GEOMETRYCOLLECTION(";
  407. bool first = true;
  408. auto const end = boost::end(geom);
  409. for (auto it = boost::begin(geom); it != end; ++it)
  410. {
  411. if (first)
  412. first = false;
  413. else
  414. os << ',';
  415. traits::iter_visit<Geom>::apply([&](auto const& g)
  416. {
  417. wkt::output_or_recursive_call(os, g, force_closure);
  418. }, it);
  419. }
  420. os << ')';
  421. }
  422. template
  423. <
  424. typename OutputStream, typename Geom,
  425. std::enable_if_t<! util::is_geometry_collection<Geom>::value, int> = 0
  426. >
  427. static void output_or_recursive_call(OutputStream& os, Geom const& geom, bool force_closure)
  428. {
  429. wkt<Geom>::apply(os, geom, force_closure);
  430. }
  431. };
  432. } // namespace dispatch
  433. #endif // DOXYGEN_NO_DISPATCH
  434. /*!
  435. \brief Generic geometry template manipulator class, takes corresponding output class from traits class
  436. \ingroup wkt
  437. \details Stream manipulator, streams geometry classes as \ref WKT streams
  438. \par Example:
  439. Small example showing how to use the wkt class
  440. \dontinclude doxygen_1.cpp
  441. \skip example_as_wkt_point
  442. \line {
  443. \until }
  444. */
  445. template <typename Geometry>
  446. class wkt_manipulator
  447. {
  448. static const bool is_ring = util::is_ring<Geometry>::value;
  449. public:
  450. // Boost.Geometry, by default, closes polygons explictly, but not rings
  451. // NOTE: this might change in the future!
  452. inline wkt_manipulator(Geometry const& g,
  453. bool force_closure = ! is_ring)
  454. : m_geometry(g)
  455. , m_force_closure(force_closure)
  456. {}
  457. template <typename Char, typename Traits>
  458. inline friend std::basic_ostream<Char, Traits>& operator<<(
  459. std::basic_ostream<Char, Traits>& os,
  460. wkt_manipulator const& m)
  461. {
  462. dispatch::wkt<Geometry>::apply(os, m.m_geometry, m.m_force_closure);
  463. os.flush();
  464. return os;
  465. }
  466. private:
  467. Geometry const& m_geometry;
  468. bool m_force_closure;
  469. };
  470. /*!
  471. \brief Main WKT-streaming function
  472. \tparam Geometry \tparam_geometry
  473. \param geometry \param_geometry
  474. \ingroup wkt
  475. \qbk{[include reference/io/wkt.qbk]}
  476. */
  477. template <typename Geometry>
  478. inline wkt_manipulator<Geometry> wkt(Geometry const& geometry)
  479. {
  480. concepts::check<Geometry const>();
  481. return wkt_manipulator<Geometry>(geometry);
  482. }
  483. /*!
  484. \brief WKT-string formulating function
  485. \tparam Geometry \tparam_geometry
  486. \param geometry \param_geometry
  487. \param significant_digits Specifies the no of significant digits to use in the output wkt
  488. \ingroup wkt
  489. \qbk{[include reference/io/to_wkt.qbk]}
  490. */
  491. template <typename Geometry>
  492. inline std::string to_wkt(Geometry const& geometry)
  493. {
  494. std::stringstream ss;
  495. ss << boost::geometry::wkt(geometry);
  496. return ss.str();
  497. }
  498. template <typename Geometry>
  499. inline std::string to_wkt(Geometry const& geometry, int significant_digits)
  500. {
  501. std::stringstream ss;
  502. ss.precision(significant_digits);
  503. ss << boost::geometry::wkt(geometry);
  504. return ss.str();
  505. }
  506. #if defined(_MSC_VER)
  507. #pragma warning(pop)
  508. #endif
  509. }} // namespace boost::geometry
  510. #endif // BOOST_GEOMETRY_IO_WKT_WRITE_HPP