line_interpolate.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2023-2024 Adam Wulkiewicz, Lodz, Poland.
  3. // Copyright (c) 2018-2023 Oracle and/or its affiliates.
  4. // Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle
  5. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  6. // Use, modification and distribution is subject to the Boost Software License,
  7. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  8. // http://www.boost.org/LICENSE_1_0.txt)
  9. #ifndef BOOST_GEOMETRY_ALGORITHMS_LINE_INTERPOLATE_HPP
  10. #define BOOST_GEOMETRY_ALGORITHMS_LINE_INTERPOLATE_HPP
  11. #include <type_traits>
  12. #include <boost/range/begin.hpp>
  13. #include <boost/range/end.hpp>
  14. #include <boost/range/value_type.hpp>
  15. #include <boost/variant/apply_visitor.hpp>
  16. #include <boost/variant/static_visitor.hpp>
  17. #include <boost/variant/variant_fwd.hpp>
  18. #include <boost/geometry/algorithms/detail/convert_point_to_point.hpp>
  19. #include <boost/geometry/algorithms/detail/dummy_geometries.hpp>
  20. #include <boost/geometry/core/exception.hpp>
  21. #include <boost/geometry/core/static_assert.hpp>
  22. #include <boost/geometry/core/tags.hpp>
  23. #include <boost/geometry/geometries/concepts/check.hpp>
  24. #include <boost/geometry/strategies/default_strategy.hpp>
  25. #include <boost/geometry/strategies/detail.hpp>
  26. #include <boost/geometry/strategies/line_interpolate/cartesian.hpp>
  27. #include <boost/geometry/strategies/line_interpolate/geographic.hpp>
  28. #include <boost/geometry/strategies/line_interpolate/spherical.hpp>
  29. #include <boost/geometry/util/constexpr.hpp>
  30. #include <boost/geometry/util/range.hpp>
  31. #include <boost/geometry/util/type_traits.hpp>
  32. #include <boost/geometry/views/segment_view.hpp>
  33. namespace boost { namespace geometry
  34. {
  35. #ifndef DOXYGEN_NO_DETAIL
  36. namespace detail { namespace line_interpolate
  37. {
  38. struct convert_and_push_back
  39. {
  40. template <typename Range, typename Point>
  41. static inline void apply(Point const& p, Range& range)
  42. {
  43. typename boost::range_value<Range>::type p2;
  44. geometry::detail::conversion::convert_point_to_point(p, p2);
  45. range::push_back(range, p2);
  46. }
  47. };
  48. struct convert_and_assign
  49. {
  50. template <typename Point1, typename Point2>
  51. static inline void apply(Point1 const& p1, Point2& p2)
  52. {
  53. geometry::detail::conversion::convert_point_to_point(p1, p2);
  54. }
  55. };
  56. /*!
  57. \brief Internal, calculates interpolation point of a linestring using iterator pairs and
  58. specified strategy
  59. */
  60. template <typename Policy>
  61. struct interpolate_range
  62. {
  63. template
  64. <
  65. typename Range,
  66. typename Distance,
  67. typename PointLike,
  68. typename Strategies
  69. >
  70. static inline void apply(Range const& range,
  71. Distance const& max_distance,
  72. PointLike & pointlike,
  73. Strategies const& strategies)
  74. {
  75. typedef typename boost::range_value<Range const>::type point_t;
  76. auto it = boost::begin(range);
  77. auto const end = boost::end(range);
  78. if (it == end) // empty(range)
  79. {
  80. BOOST_THROW_EXCEPTION(empty_input_exception());
  81. return;
  82. }
  83. if (max_distance <= 0) //non positive distance
  84. {
  85. Policy::apply(*it, pointlike);
  86. return;
  87. }
  88. auto const pp_strategy = strategies.distance(dummy_point(), dummy_point());
  89. auto const strategy = strategies.line_interpolate(range);
  90. typedef decltype(pp_strategy.apply(
  91. std::declval<point_t>(), std::declval<point_t>())) distance_type;
  92. auto prev = it++;
  93. distance_type repeated_distance = max_distance;
  94. distance_type prev_distance = 0;
  95. distance_type current_distance = 0;
  96. point_t start_p = *prev;
  97. for ( ; it != end ; ++it)
  98. {
  99. distance_type dist = pp_strategy.apply(*prev, *it);
  100. current_distance = prev_distance + dist;
  101. while (current_distance >= repeated_distance)
  102. {
  103. point_t p;
  104. distance_type diff_distance = current_distance - prev_distance;
  105. BOOST_ASSERT(diff_distance != distance_type(0));
  106. strategy.apply(start_p, *it,
  107. (repeated_distance - prev_distance)/diff_distance,
  108. p,
  109. diff_distance);
  110. Policy::apply(p, pointlike);
  111. if BOOST_GEOMETRY_CONSTEXPR (util::is_point<PointLike>::value)
  112. {
  113. return;
  114. }
  115. else // else prevents unreachable code warning
  116. {
  117. start_p = p;
  118. prev_distance = repeated_distance;
  119. repeated_distance += max_distance;
  120. }
  121. }
  122. prev_distance = current_distance;
  123. prev = it;
  124. start_p = *prev;
  125. }
  126. // case when max_distance is larger than linestring's length
  127. // return the last point in range (range is not empty)
  128. if (repeated_distance == max_distance)
  129. {
  130. Policy::apply(*(end-1), pointlike);
  131. }
  132. }
  133. };
  134. template <typename Policy>
  135. struct interpolate_segment
  136. {
  137. template <typename Segment, typename Distance, typename Pointlike, typename Strategy>
  138. static inline void apply(Segment const& segment,
  139. Distance const& max_distance,
  140. Pointlike & point,
  141. Strategy const& strategy)
  142. {
  143. interpolate_range<Policy>().apply(segment_view<Segment>(segment),
  144. max_distance, point, strategy);
  145. }
  146. };
  147. }} // namespace detail::line_interpolate
  148. #endif // DOXYGEN_NO_DETAIL
  149. #ifndef DOXYGEN_NO_DISPATCH
  150. namespace dispatch
  151. {
  152. template
  153. <
  154. typename Geometry,
  155. typename Pointlike,
  156. typename Tag1 = typename tag<Geometry>::type,
  157. typename Tag2 = typename tag<Pointlike>::type
  158. >
  159. struct line_interpolate
  160. {
  161. BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
  162. "Not implemented for this Geometry type.",
  163. Geometry, Pointlike);
  164. };
  165. template <typename Geometry, typename Pointlike>
  166. struct line_interpolate<Geometry, Pointlike, linestring_tag, point_tag>
  167. : detail::line_interpolate::interpolate_range
  168. <
  169. detail::line_interpolate::convert_and_assign
  170. >
  171. {};
  172. template <typename Geometry, typename Pointlike>
  173. struct line_interpolate<Geometry, Pointlike, linestring_tag, multi_point_tag>
  174. : detail::line_interpolate::interpolate_range
  175. <
  176. detail::line_interpolate::convert_and_push_back
  177. >
  178. {};
  179. template <typename Geometry, typename Pointlike>
  180. struct line_interpolate<Geometry, Pointlike, segment_tag, point_tag>
  181. : detail::line_interpolate::interpolate_segment
  182. <
  183. detail::line_interpolate::convert_and_assign
  184. >
  185. {};
  186. template <typename Geometry, typename Pointlike>
  187. struct line_interpolate<Geometry, Pointlike, segment_tag, multi_point_tag>
  188. : detail::line_interpolate::interpolate_segment
  189. <
  190. detail::line_interpolate::convert_and_push_back
  191. >
  192. {};
  193. } // namespace dispatch
  194. #endif // DOXYGEN_NO_DISPATCH
  195. namespace resolve_strategy {
  196. template
  197. <
  198. typename Strategies,
  199. bool IsUmbrella = strategies::detail::is_umbrella_strategy<Strategies>::value
  200. >
  201. struct line_interpolate
  202. {
  203. template <typename Geometry, typename Distance, typename Pointlike>
  204. static inline void apply(Geometry const& geometry,
  205. Distance const& max_distance,
  206. Pointlike & pointlike,
  207. Strategies const& strategies)
  208. {
  209. dispatch::line_interpolate
  210. <
  211. Geometry, Pointlike
  212. >::apply(geometry, max_distance, pointlike, strategies);
  213. }
  214. };
  215. template <typename Strategy>
  216. struct line_interpolate<Strategy, false>
  217. {
  218. template <typename Geometry, typename Distance, typename Pointlike>
  219. static inline void apply(Geometry const& geometry,
  220. Distance const& max_distance,
  221. Pointlike & pointlike,
  222. Strategy const& strategy)
  223. {
  224. using strategies::line_interpolate::services::strategy_converter;
  225. dispatch::line_interpolate
  226. <
  227. Geometry, Pointlike
  228. >::apply(geometry, max_distance, pointlike,
  229. strategy_converter<Strategy>::get(strategy));
  230. }
  231. };
  232. template <>
  233. struct line_interpolate<default_strategy, false>
  234. {
  235. template <typename Geometry, typename Distance, typename Pointlike>
  236. static inline void apply(Geometry const& geometry,
  237. Distance const& max_distance,
  238. Pointlike & pointlike,
  239. default_strategy)
  240. {
  241. typedef typename strategies::line_interpolate::services::default_strategy
  242. <
  243. Geometry
  244. >::type strategy_type;
  245. dispatch::line_interpolate
  246. <
  247. Geometry, Pointlike
  248. >::apply(geometry, max_distance, pointlike, strategy_type());
  249. }
  250. };
  251. } // namespace resolve_strategy
  252. namespace resolve_variant {
  253. template <typename Geometry>
  254. struct line_interpolate
  255. {
  256. template <typename Distance, typename Pointlike, typename Strategy>
  257. static inline void apply(Geometry const& geometry,
  258. Distance const& max_distance,
  259. Pointlike & pointlike,
  260. Strategy const& strategy)
  261. {
  262. return resolve_strategy::line_interpolate
  263. <
  264. Strategy
  265. >::apply(geometry, max_distance, pointlike, strategy);
  266. }
  267. };
  268. template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
  269. struct line_interpolate<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
  270. {
  271. template <typename Pointlike, typename Strategy>
  272. struct visitor: boost::static_visitor<void>
  273. {
  274. Pointlike const& m_pointlike;
  275. Strategy const& m_strategy;
  276. visitor(Pointlike const& pointlike, Strategy const& strategy)
  277. : m_pointlike(pointlike)
  278. , m_strategy(strategy)
  279. {}
  280. template <typename Geometry, typename Distance>
  281. void operator()(Geometry const& geometry, Distance const& max_distance) const
  282. {
  283. line_interpolate<Geometry>::apply(geometry, max_distance,
  284. m_pointlike, m_strategy);
  285. }
  286. };
  287. template <typename Distance, typename Pointlike, typename Strategy>
  288. static inline void
  289. apply(boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> const& geometry,
  290. Distance const& max_distance,
  291. Pointlike & pointlike,
  292. Strategy const& strategy)
  293. {
  294. boost::apply_visitor(
  295. visitor<Pointlike, Strategy>(pointlike, strategy),
  296. geometry,
  297. max_distance
  298. );
  299. }
  300. };
  301. } // namespace resolve_variant
  302. /*!
  303. \brief Returns one or more points interpolated along a LineString \brief_strategy
  304. \ingroup line_interpolate
  305. \tparam Geometry Any type fulfilling a LineString concept
  306. \tparam Distance A numerical distance measure
  307. \tparam Pointlike Any type fulfilling Point or Multipoint concept
  308. \tparam Strategy A type fulfilling a LineInterpolatePointStrategy concept
  309. \param geometry Input geometry
  310. \param max_distance Distance threshold (in units depending on coordinate system)
  311. representing the spacing between the points
  312. \param pointlike Output: either a Point (exactly one point will be constructed) or
  313. a MultiPoint (depending on the max_distance one or more points will be constructed)
  314. \param strategy line_interpolate strategy to be used for interpolation of
  315. points
  316. \qbk{[include reference/algorithms/line_interpolate.qbk]}
  317. \qbk{distinguish,with strategy}
  318. \qbk{
  319. [heading Available Strategies]
  320. \* [link geometry.reference.strategies.strategy_line_interpolate_cartesian Cartesian]
  321. \* [link geometry.reference.strategies.strategy_line_interpolate_spherical Spherical]
  322. \* [link geometry.reference.strategies.strategy_line_interpolate_geographic Geographic]
  323. [heading Example]
  324. [line_interpolate_strategy]
  325. [line_interpolate_strategy_output]
  326. [heading See also]
  327. \* [link geometry.reference.algorithms.densify densify]
  328. }
  329. */
  330. template
  331. <
  332. typename Geometry,
  333. typename Distance,
  334. typename Pointlike,
  335. typename Strategy
  336. >
  337. inline void line_interpolate(Geometry const& geometry,
  338. Distance const& max_distance,
  339. Pointlike & pointlike,
  340. Strategy const& strategy)
  341. {
  342. concepts::check<Geometry const>();
  343. // detail::throw_on_empty_input(geometry);
  344. return resolve_variant::line_interpolate<Geometry>
  345. ::apply(geometry, max_distance, pointlike, strategy);
  346. }
  347. /*!
  348. \brief Returns one or more points interpolated along a LineString.
  349. \ingroup line_interpolate
  350. \tparam Geometry Any type fulfilling a LineString concept
  351. \tparam Distance A numerical distance measure
  352. \tparam Pointlike Any type fulfilling Point or Multipoint concept
  353. \param geometry Input geometry
  354. \param max_distance Distance threshold (in units depending on coordinate system)
  355. representing the spacing between the points
  356. \param pointlike Output: either a Point (exactly one point will be constructed) or
  357. a MultiPoint (depending on the max_distance one or more points will be constructed)
  358. \qbk{[include reference/algorithms/line_interpolate.qbk]
  359. [heading Example]
  360. [line_interpolate]
  361. [line_interpolate_output]
  362. [heading See also]
  363. \* [link geometry.reference.algorithms.densify densify]
  364. }
  365. */
  366. template<typename Geometry, typename Distance, typename Pointlike>
  367. inline void line_interpolate(Geometry const& geometry,
  368. Distance const& max_distance,
  369. Pointlike & pointlike)
  370. {
  371. concepts::check<Geometry const>();
  372. // detail::throw_on_empty_input(geometry);
  373. return resolve_variant::line_interpolate<Geometry>
  374. ::apply(geometry, max_distance, pointlike, default_strategy());
  375. }
  376. }} // namespace boost::geometry
  377. #endif // BOOST_GEOMETRY_ALGORITHMS_LINE_INTERPOLATE_HPP