write.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  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 \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_t = ring_type_t<Polygon const>;
  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_t>::apply(os, exterior, force_closure);
  184. for (auto it = boost::begin(rings); it != boost::end(rings); ++it)
  185. {
  186. os << ",";
  187. wkt_sequence<ring_t>::apply(os, *it, force_closure);
  188. }
  189. }
  190. os << ")";
  191. }
  192. };
  193. template <typename PolyhedralSurface, typename PrefixPolicy>
  194. struct wkt_polyhedral_surface
  195. {
  196. template <typename Char, typename Traits>
  197. static inline void apply(std::basic_ostream<Char, Traits>& os,
  198. PolyhedralSurface const& polyhedral, bool force_closure)
  199. {
  200. using polygon = typename PolyhedralSurface::polygon_type;
  201. os << PrefixPolicy::apply();
  202. os << "(";
  203. for (auto it = boost::begin(polyhedral); it != boost::end(polyhedral); ++it)
  204. {
  205. if (it != boost::begin(polyhedral))
  206. {
  207. os << ",";
  208. }
  209. wkt_poly<polygon, detail::wkt::prefix_null>::apply(os, *it, force_closure);
  210. }
  211. os << ")";
  212. }
  213. };
  214. template <typename Multi, typename StreamPolicy, typename PrefixPolicy>
  215. struct wkt_multi
  216. {
  217. template <typename Char, typename Traits>
  218. static inline void apply(std::basic_ostream<Char, Traits>& os,
  219. Multi const& geometry, bool force_closure)
  220. {
  221. os << PrefixPolicy::apply();
  222. os << "(";
  223. for (auto it = boost::begin(geometry); it != boost::end(geometry); ++it)
  224. {
  225. if (it != boost::begin(geometry))
  226. {
  227. os << ",";
  228. }
  229. StreamPolicy::apply(os, *it, force_closure);
  230. }
  231. os << ")";
  232. }
  233. };
  234. template <typename Box>
  235. struct wkt_box
  236. {
  237. using point_type = point_type_t<Box>;
  238. template <typename Char, typename Traits>
  239. static inline void apply(std::basic_ostream<Char, Traits>& os,
  240. Box const& box, bool force_closure)
  241. {
  242. // Convert to a clockwire ring, then stream.
  243. // Never close it based on last point (box might be empty and
  244. // that should result in POLYGON((0 0,0 0,0 0,0 0, ...)) )
  245. if (force_closure)
  246. {
  247. do_apply<model::ring<point_type, true, true> >(os, box);
  248. }
  249. else
  250. {
  251. do_apply<model::ring<point_type, true, false> >(os, box);
  252. }
  253. }
  254. private:
  255. inline wkt_box()
  256. {
  257. // Only streaming of boxes with two dimensions is support, otherwise it is a polyhedron!
  258. //assert_dimension<B, 2>();
  259. }
  260. template <typename RingType, typename Char, typename Traits>
  261. static inline void do_apply(std::basic_ostream<Char, Traits>& os,
  262. Box const& box)
  263. {
  264. RingType ring;
  265. geometry::convert(box, ring);
  266. os << "POLYGON(";
  267. wkt_sequence<RingType, false>::apply(os, ring);
  268. os << ")";
  269. }
  270. };
  271. template <typename Segment>
  272. struct wkt_segment
  273. {
  274. using point_type = point_type_t<Segment>;
  275. template <typename Char, typename Traits>
  276. static inline void apply(std::basic_ostream<Char, Traits>& os,
  277. Segment const& segment, bool)
  278. {
  279. // Convert to two points, then stream
  280. using sequence = std::array<point_type, 2>;
  281. sequence points;
  282. geometry::detail::assign_point_from_index<0>(segment, points[0]);
  283. geometry::detail::assign_point_from_index<1>(segment, points[1]);
  284. // In Boost.Geometry a segment is represented
  285. // in WKT-format like (for 2D): LINESTRING(x y,x y)
  286. os << "LINESTRING";
  287. wkt_sequence<sequence, false>::apply(os, points);
  288. }
  289. private:
  290. inline wkt_segment()
  291. {}
  292. };
  293. }} // namespace detail::wkt
  294. #endif // DOXYGEN_NO_DETAIL
  295. #ifndef DOXYGEN_NO_DISPATCH
  296. namespace dispatch
  297. {
  298. template <typename Geometry, typename Tag = tag_t<Geometry>>
  299. struct wkt: not_implemented<Tag>
  300. {};
  301. template <typename Point>
  302. struct wkt<Point, point_tag>
  303. : detail::wkt::wkt_point
  304. <
  305. Point,
  306. detail::wkt::prefix_point
  307. >
  308. {};
  309. template <typename Linestring>
  310. struct wkt<Linestring, linestring_tag>
  311. : detail::wkt::wkt_range
  312. <
  313. Linestring,
  314. detail::wkt::prefix_linestring
  315. >
  316. {};
  317. /*!
  318. \brief Specialization to stream a box as WKT
  319. \details A "box" does not exist in WKT.
  320. It is therefore streamed as a polygon
  321. */
  322. template <typename Box>
  323. struct wkt<Box, box_tag>
  324. : detail::wkt::wkt_box<Box>
  325. {};
  326. template <typename Segment>
  327. struct wkt<Segment, segment_tag>
  328. : detail::wkt::wkt_segment<Segment>
  329. {};
  330. /*!
  331. \brief Specialization to stream a ring as WKT
  332. \details A ring or "linear_ring" does not exist in WKT.
  333. A ring is equivalent to a polygon without inner rings
  334. It is therefore streamed as a polygon
  335. */
  336. template <typename Ring>
  337. struct wkt<Ring, ring_tag>
  338. : detail::wkt::wkt_range
  339. <
  340. Ring,
  341. detail::wkt::prefix_polygon,
  342. true,
  343. true
  344. >
  345. {};
  346. /*!
  347. \brief Specialization to stream polygon as WKT
  348. */
  349. template <typename Polygon>
  350. struct wkt<Polygon, polygon_tag>
  351. : detail::wkt::wkt_poly
  352. <
  353. Polygon,
  354. detail::wkt::prefix_polygon
  355. >
  356. {};
  357. template <typename PolyhedralSurface>
  358. struct wkt<PolyhedralSurface, polyhedral_surface_tag>
  359. : detail::wkt::wkt_polyhedral_surface
  360. <
  361. PolyhedralSurface,
  362. detail::wkt::prefix_polyhedral_surface
  363. >
  364. {};
  365. template <typename Multi>
  366. struct wkt<Multi, multi_point_tag>
  367. : detail::wkt::wkt_multi
  368. <
  369. Multi,
  370. detail::wkt::wkt_point
  371. <
  372. typename boost::range_value<Multi>::type,
  373. detail::wkt::prefix_null
  374. >,
  375. detail::wkt::prefix_multipoint
  376. >
  377. {};
  378. template <typename Multi>
  379. struct wkt<Multi, multi_linestring_tag>
  380. : detail::wkt::wkt_multi
  381. <
  382. Multi,
  383. detail::wkt::wkt_sequence
  384. <
  385. typename boost::range_value<Multi>::type,
  386. false
  387. >,
  388. detail::wkt::prefix_multilinestring
  389. >
  390. {};
  391. template <typename Multi>
  392. struct wkt<Multi, multi_polygon_tag>
  393. : detail::wkt::wkt_multi
  394. <
  395. Multi,
  396. detail::wkt::wkt_poly
  397. <
  398. typename boost::range_value<Multi>::type,
  399. detail::wkt::prefix_null
  400. >,
  401. detail::wkt::prefix_multipolygon
  402. >
  403. {};
  404. template <typename Geometry>
  405. struct wkt<Geometry, dynamic_geometry_tag>
  406. {
  407. template <typename OutputStream>
  408. static inline void apply(OutputStream& os, Geometry const& geometry,
  409. bool force_closure)
  410. {
  411. traits::visit<Geometry>::apply([&](auto const& g)
  412. {
  413. wkt<util::remove_cref_t<decltype(g)>>::apply(os, g, force_closure);
  414. }, geometry);
  415. }
  416. };
  417. // TODO: Implement non-recursive version
  418. template <typename Geometry>
  419. struct wkt<Geometry, geometry_collection_tag>
  420. {
  421. template <typename OutputStream>
  422. static inline void apply(OutputStream& os, Geometry const& geometry,
  423. bool force_closure)
  424. {
  425. wkt::output_or_recursive_call(os, geometry, force_closure);
  426. }
  427. private:
  428. template
  429. <
  430. typename OutputStream, typename Geom,
  431. std::enable_if_t<util::is_geometry_collection<Geom>::value, int> = 0
  432. >
  433. static void output_or_recursive_call(OutputStream& os, Geom const& geom, bool force_closure)
  434. {
  435. os << "GEOMETRYCOLLECTION(";
  436. bool first = true;
  437. auto const end = boost::end(geom);
  438. for (auto it = boost::begin(geom); it != end; ++it)
  439. {
  440. if (first)
  441. first = false;
  442. else
  443. os << ',';
  444. traits::iter_visit<Geom>::apply([&](auto const& g)
  445. {
  446. wkt::output_or_recursive_call(os, g, force_closure);
  447. }, it);
  448. }
  449. os << ')';
  450. }
  451. template
  452. <
  453. typename OutputStream, typename Geom,
  454. std::enable_if_t<! util::is_geometry_collection<Geom>::value, int> = 0
  455. >
  456. static void output_or_recursive_call(OutputStream& os, Geom const& geom, bool force_closure)
  457. {
  458. wkt<Geom>::apply(os, geom, force_closure);
  459. }
  460. };
  461. } // namespace dispatch
  462. #endif // DOXYGEN_NO_DISPATCH
  463. /*!
  464. \brief Generic geometry template manipulator class, takes corresponding output class from traits class
  465. \ingroup wkt
  466. \details Stream manipulator, streams geometry classes as \wkt streams
  467. \par Example:
  468. Small example showing how to use the wkt class
  469. \dontinclude doxygen_1.cpp
  470. \skip example_as_wkt_point
  471. \line {
  472. \until }
  473. */
  474. template <typename Geometry>
  475. class wkt_manipulator
  476. {
  477. static const bool is_ring = util::is_ring<Geometry>::value;
  478. public:
  479. // Boost.Geometry, by default, closes polygons explictly, but not rings
  480. // NOTE: this might change in the future!
  481. inline wkt_manipulator(Geometry const& g,
  482. bool force_closure = ! is_ring)
  483. : m_geometry(g)
  484. , m_force_closure(force_closure)
  485. {}
  486. template <typename Char, typename Traits>
  487. inline friend std::basic_ostream<Char, Traits>& operator<<(
  488. std::basic_ostream<Char, Traits>& os,
  489. wkt_manipulator const& m)
  490. {
  491. dispatch::wkt<Geometry>::apply(os, m.m_geometry, m.m_force_closure);
  492. os.flush();
  493. return os;
  494. }
  495. private:
  496. Geometry const& m_geometry;
  497. bool m_force_closure;
  498. };
  499. /*!
  500. \brief Main WKT-streaming function
  501. \tparam Geometry \tparam_geometry
  502. \param geometry \param_geometry
  503. \ingroup wkt
  504. \qbk{[include reference/io/wkt.qbk]}
  505. */
  506. template <typename Geometry>
  507. inline wkt_manipulator<Geometry> wkt(Geometry const& geometry)
  508. {
  509. concepts::check<Geometry const>();
  510. return wkt_manipulator<Geometry>(geometry);
  511. }
  512. /*!
  513. \brief WKT-string formulating function
  514. \tparam Geometry \tparam_geometry
  515. \param geometry \param_geometry
  516. \ingroup wkt
  517. \qbk{[include reference/io/to_wkt.qbk]}
  518. */
  519. template <typename Geometry>
  520. inline std::string to_wkt(Geometry const& geometry)
  521. {
  522. std::stringstream ss;
  523. ss << boost::geometry::wkt(geometry);
  524. return ss.str();
  525. }
  526. /*!
  527. \brief WKT-string formulating function (with significant digits)
  528. \tparam Geometry \tparam_geometry
  529. \param geometry \param_geometry
  530. \param significant_digits Specifies the number of significant digits to use in the output wkt
  531. \ingroup wkt
  532. \qbk{distinguish, with significant digits}
  533. */
  534. template <typename Geometry>
  535. inline std::string to_wkt(Geometry const& geometry, int significant_digits)
  536. {
  537. std::stringstream ss;
  538. ss.precision(significant_digits);
  539. ss << boost::geometry::wkt(geometry);
  540. return ss.str();
  541. }
  542. #if defined(_MSC_VER)
  543. #pragma warning(pop)
  544. #endif
  545. }} // namespace boost::geometry
  546. #endif // BOOST_GEOMETRY_IO_WKT_WRITE_HPP