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