collect_vectors.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2007-2014 Barend Gehrels, Amsterdam, the Netherlands.
  3. // Copyright (c) 2008-2014 Bruno Lalande, Paris, France.
  4. // Copyright (c) 2009-2014 Mateusz Loskot, London, UK.
  5. // Copyright (c) 2014-2017 Adam Wulkiewicz, Lodz, Poland.
  6. // This file was modified by Oracle on 2017-2021.
  7. // Modifications copyright (c) 2017-2021 Oracle and/or its affiliates.
  8. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  9. // Parts of Boost.Geometry are redesigned from Geodan's Geographic Library
  10. // (geolib/GGL), copyright (c) 1995-2010 Geodan, Amsterdam, the Netherlands.
  11. // Use, modification and distribution is subject to the Boost Software License,
  12. // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  13. // http://www.boost.org/LICENSE_1_0.txt)
  14. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP
  15. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP
  16. #include <boost/range/size.hpp>
  17. #include <boost/geometry/algorithms/detail/normalize.hpp>
  18. #include <boost/geometry/algorithms/not_implemented.hpp>
  19. #include <boost/geometry/core/cs.hpp>
  20. #include <boost/geometry/core/interior_rings.hpp>
  21. #include <boost/geometry/core/tags.hpp>
  22. #include <boost/geometry/formulas/spherical.hpp>
  23. #include <boost/geometry/geometries/concepts/check.hpp>
  24. #include <boost/geometry/util/math.hpp>
  25. #include <boost/geometry/util/numeric_cast.hpp>
  26. #include <boost/geometry/util/range.hpp>
  27. #include <boost/geometry/views/detail/closed_clockwise_view.hpp>
  28. #include <boost/geometry/strategies/spherical/ssf.hpp>
  29. #include <boost/geometry/strategies/normalize.hpp>
  30. namespace boost { namespace geometry
  31. {
  32. template <typename T>
  33. struct collected_vector_cartesian
  34. {
  35. typedef T type;
  36. inline collected_vector_cartesian()
  37. {}
  38. inline collected_vector_cartesian(T const& px, T const& py,
  39. T const& pdx, T const& pdy)
  40. : x(px)
  41. , y(py)
  42. , dx(pdx)
  43. , dy(pdy)
  44. //, dx_0(dx)
  45. //, dy_0(dy)
  46. {}
  47. template <typename Point>
  48. inline collected_vector_cartesian(Point const& p1, Point const& p2)
  49. : x(get<0>(p1))
  50. , y(get<1>(p1))
  51. , dx(get<0>(p2) - x)
  52. , dy(get<1>(p2) - y)
  53. //, dx_0(dx)
  54. //, dy_0(dy)
  55. {}
  56. bool normalize()
  57. {
  58. T magnitude = math::sqrt(util::numeric_cast<T>(dx * dx + dy * dy));
  59. // NOTE: shouldn't here math::equals() be called?
  60. if (magnitude > 0)
  61. {
  62. dx /= magnitude;
  63. dy /= magnitude;
  64. return true;
  65. }
  66. return false;
  67. }
  68. // For sorting
  69. inline bool operator<(collected_vector_cartesian const& other) const
  70. {
  71. if (math::equals(x, other.x))
  72. {
  73. if (math::equals(y, other.y))
  74. {
  75. if (math::equals(dx, other.dx))
  76. {
  77. return dy < other.dy;
  78. }
  79. return dx < other.dx;
  80. }
  81. return y < other.y;
  82. }
  83. return x < other.x;
  84. }
  85. inline bool next_is_collinear(collected_vector_cartesian const& other) const
  86. {
  87. return same_direction(other);
  88. }
  89. // For std::equals
  90. inline bool operator==(collected_vector_cartesian const& other) const
  91. {
  92. return math::equals(x, other.x)
  93. && math::equals(y, other.y)
  94. && same_direction(other);
  95. }
  96. private:
  97. inline bool same_direction(collected_vector_cartesian const& other) const
  98. {
  99. // For high precision arithmetic, we have to be
  100. // more relaxed then using ==
  101. // Because 2/sqrt( (0,0)<->(2,2) ) == 1/sqrt( (0,0)<->(1,1) )
  102. // is not always true (at least, not for some user defined types)
  103. return math::equals_with_epsilon(dx, other.dx)
  104. && math::equals_with_epsilon(dy, other.dy);
  105. }
  106. T x, y;
  107. T dx, dy;
  108. //T dx_0, dy_0;
  109. };
  110. // Compatible with spherical_side_formula which currently
  111. // is the default spherical_equatorial and geographic strategy
  112. // so CSTag is spherical_equatorial_tag or geographic_tag
  113. template <typename T, typename Point>
  114. struct collected_vector_spherical
  115. {
  116. typedef T type;
  117. typedef model::point<T, 3, cs::cartesian> vector_type;
  118. collected_vector_spherical()
  119. {}
  120. collected_vector_spherical(Point const& p1, Point const& p2)
  121. : origin(get<0>(p1), get<1>(p1))
  122. {
  123. origin = detail::return_normalized<Point>(
  124. origin,
  125. strategy::normalize::spherical_point());
  126. using namespace geometry::formula;
  127. prev = sph_to_cart3d<vector_type>(p1);
  128. next = sph_to_cart3d<vector_type>(p2);
  129. cross = direction = cross_product(prev, next);
  130. }
  131. bool normalize()
  132. {
  133. T const magnitude_sqr = dot_product(direction, direction);
  134. // NOTE: shouldn't here math::equals() be called?
  135. if (magnitude_sqr > 0)
  136. {
  137. divide_value(direction, math::sqrt(magnitude_sqr));
  138. return true;
  139. }
  140. return false;
  141. }
  142. bool operator<(collected_vector_spherical const& other) const
  143. {
  144. if (math::equals(get<0>(origin), get<0>(other.origin)))
  145. {
  146. if (math::equals(get<1>(origin), get<1>(other.origin)))
  147. {
  148. if (math::equals(get<0>(direction), get<0>(other.direction)))
  149. {
  150. if (math::equals(get<1>(direction), get<1>(other.direction)))
  151. {
  152. return get<2>(direction) < get<2>(other.direction);
  153. }
  154. return get<1>(direction) < get<1>(other.direction);
  155. }
  156. return get<0>(direction) < get<0>(other.direction);
  157. }
  158. return get<1>(origin) < get<1>(other.origin);
  159. }
  160. return get<0>(origin) < get<0>(other.origin);
  161. }
  162. // For consistency with side and intersection strategies used by relops
  163. // IMPORTANT: this method should be called for previous vector
  164. // and next vector should be passed as parameter
  165. bool next_is_collinear(collected_vector_spherical const& other) const
  166. {
  167. return formula::sph_side_value(cross, other.next) == 0;
  168. }
  169. // For std::equals
  170. bool operator==(collected_vector_spherical const& other) const
  171. {
  172. return math::equals(get<0>(origin), get<0>(other.origin))
  173. && math::equals(get<1>(origin), get<1>(other.origin))
  174. && is_collinear(other);
  175. }
  176. private:
  177. // For consistency with side and intersection strategies used by relops
  178. // NOTE: alternative would be to equal-compare direction's coordinates
  179. // or to check if dot product of directions is equal to 1.
  180. bool is_collinear(collected_vector_spherical const& other) const
  181. {
  182. return formula::sph_side_value(cross, other.prev) == 0
  183. && formula::sph_side_value(cross, other.next) == 0;
  184. }
  185. Point origin; // used for sorting and equality check
  186. vector_type direction; // used for sorting, only in operator<
  187. vector_type cross; // used for sorting, used for collinearity check
  188. vector_type prev; // used for collinearity check, only in operator==
  189. vector_type next; // used for collinearity check
  190. };
  191. // Version for spherical polar
  192. template <typename T, typename Point>
  193. struct collected_vector_polar
  194. : public collected_vector_spherical<T, Point>
  195. {
  196. using type = T;
  197. using base_point_type
  198. = model::point<T, 2, cs::spherical_equatorial<geometry::degree>>;
  199. using base_type = collected_vector_spherical<T, base_point_type>;
  200. collected_vector_polar() {}
  201. collected_vector_polar(Point const& p1, Point const& p2)
  202. : base_type(to_equatorial(p1), to_equatorial(p2))
  203. {}
  204. private:
  205. static base_point_type to_equatorial(Point const& p)
  206. {
  207. using coord_type = typename coordinate_type<Point>::type;
  208. using constants = math::detail::constants_on_spheroid
  209. <
  210. coord_type,
  211. typename coordinate_system<Point>::type::units
  212. > ;
  213. constexpr coord_type pi_2 = constants::half_period() / 2;
  214. base_point_type res;
  215. set<0>(res, get<0>(p));
  216. set<1>(res, pi_2 - get<1>(p));
  217. return res;
  218. }
  219. };
  220. // TODO: implement collected_vector type for geographic
  221. #ifndef DOXYGEN_NO_DETAIL
  222. namespace detail { namespace collect_vectors
  223. {
  224. template <typename Range, typename Collection>
  225. struct range_collect_vectors
  226. {
  227. typedef typename boost::range_value<Collection>::type item_type;
  228. typedef typename item_type::type calculation_type;
  229. static inline void apply(Collection& collection, Range const& range)
  230. {
  231. apply_impl(collection,
  232. detail::closed_clockwise_view<Range const>(range));
  233. }
  234. private:
  235. template <typename ClosedClockwiseRange>
  236. static inline void apply_impl(Collection& collection, ClosedClockwiseRange const& range)
  237. {
  238. if (boost::size(range) < 2)
  239. {
  240. return;
  241. }
  242. auto c_old_size = boost::size(collection);
  243. bool is_first = true;
  244. auto it = boost::begin(range);
  245. for (auto prev = it++; it != boost::end(range); prev = it++)
  246. {
  247. typename boost::range_value<Collection>::type v(*prev, *it);
  248. // Normalize the vector -> this results in points+direction
  249. // and is comparible between geometries
  250. // Avoid non-duplicate points (AND division by zero)
  251. if (v.normalize())
  252. {
  253. // Avoid non-direction changing points
  254. if (is_first || ! collection.back().next_is_collinear(v))
  255. {
  256. collection.push_back(v);
  257. }
  258. is_first = false;
  259. }
  260. }
  261. // If first one has same direction as last one, remove first one
  262. if (boost::size(collection) > c_old_size + 1)
  263. {
  264. auto first = range::pos(collection, c_old_size);
  265. if (collection.back().next_is_collinear(*first))
  266. {
  267. //collection.erase(first);
  268. // O(1) instead of O(N)
  269. *first = collection.back();
  270. collection.pop_back();
  271. }
  272. }
  273. }
  274. };
  275. // Default version (cartesian)
  276. template <typename Box, typename Collection, typename CSTag = typename cs_tag<Box>::type>
  277. struct box_collect_vectors
  278. {
  279. // Calculate on coordinate type, but if it is integer,
  280. // then use double
  281. typedef typename boost::range_value<Collection>::type item_type;
  282. typedef typename item_type::type calculation_type;
  283. static inline void apply(Collection& collection, Box const& box)
  284. {
  285. typename point_type<Box>::type lower_left, lower_right,
  286. upper_left, upper_right;
  287. geometry::detail::assign_box_corners(box, lower_left, lower_right,
  288. upper_left, upper_right);
  289. typedef typename boost::range_value<Collection>::type item;
  290. collection.push_back(item(get<0>(lower_left), get<1>(lower_left), 0, 1));
  291. collection.push_back(item(get<0>(upper_left), get<1>(upper_left), 1, 0));
  292. collection.push_back(item(get<0>(upper_right), get<1>(upper_right), 0, -1));
  293. collection.push_back(item(get<0>(lower_right), get<1>(lower_right), -1, 0));
  294. }
  295. };
  296. // NOTE: This is not fully correct because Box in spherical and geographic
  297. // cordinate systems cannot be seen as Polygon
  298. template <typename Box, typename Collection>
  299. struct box_collect_vectors<Box, Collection, spherical_equatorial_tag>
  300. {
  301. static inline void apply(Collection& collection, Box const& box)
  302. {
  303. typename point_type<Box>::type lower_left, lower_right,
  304. upper_left, upper_right;
  305. geometry::detail::assign_box_corners(box, lower_left, lower_right,
  306. upper_left, upper_right);
  307. typedef typename boost::range_value<Collection>::type item;
  308. collection.push_back(item(lower_left, upper_left));
  309. collection.push_back(item(upper_left, upper_right));
  310. collection.push_back(item(upper_right, lower_right));
  311. collection.push_back(item(lower_right, lower_left));
  312. }
  313. };
  314. template <typename Box, typename Collection>
  315. struct box_collect_vectors<Box, Collection, spherical_polar_tag>
  316. : box_collect_vectors<Box, Collection, spherical_equatorial_tag>
  317. {};
  318. template <typename Box, typename Collection>
  319. struct box_collect_vectors<Box, Collection, geographic_tag>
  320. : box_collect_vectors<Box, Collection, spherical_equatorial_tag>
  321. {};
  322. template <typename Polygon, typename Collection>
  323. struct polygon_collect_vectors
  324. {
  325. static inline void apply(Collection& collection, Polygon const& polygon)
  326. {
  327. typedef typename geometry::ring_type<Polygon>::type ring_type;
  328. typedef range_collect_vectors<ring_type, Collection> per_range;
  329. per_range::apply(collection, exterior_ring(polygon));
  330. auto const& rings = interior_rings(polygon);
  331. for (auto it = boost::begin(rings); it != boost::end(rings); ++it)
  332. {
  333. per_range::apply(collection, *it);
  334. }
  335. }
  336. };
  337. template <typename MultiGeometry, typename Collection, typename SinglePolicy>
  338. struct multi_collect_vectors
  339. {
  340. static inline void apply(Collection& collection, MultiGeometry const& multi)
  341. {
  342. for (auto it = boost::begin(multi); it != boost::end(multi); ++it)
  343. {
  344. SinglePolicy::apply(collection, *it);
  345. }
  346. }
  347. };
  348. }} // namespace detail::collect_vectors
  349. #endif // DOXYGEN_NO_DETAIL
  350. #ifndef DOXYGEN_NO_DISPATCH
  351. namespace dispatch
  352. {
  353. template
  354. <
  355. typename Tag,
  356. typename Collection,
  357. typename Geometry
  358. >
  359. struct collect_vectors
  360. {
  361. static inline void apply(Collection&, Geometry const&)
  362. {}
  363. };
  364. template <typename Collection, typename Box>
  365. struct collect_vectors<box_tag, Collection, Box>
  366. : detail::collect_vectors::box_collect_vectors<Box, Collection>
  367. {};
  368. template <typename Collection, typename Ring>
  369. struct collect_vectors<ring_tag, Collection, Ring>
  370. : detail::collect_vectors::range_collect_vectors<Ring, Collection>
  371. {};
  372. template <typename Collection, typename LineString>
  373. struct collect_vectors<linestring_tag, Collection, LineString>
  374. : detail::collect_vectors::range_collect_vectors<LineString, Collection>
  375. {};
  376. template <typename Collection, typename Polygon>
  377. struct collect_vectors<polygon_tag, Collection, Polygon>
  378. : detail::collect_vectors::polygon_collect_vectors<Polygon, Collection>
  379. {};
  380. template <typename Collection, typename MultiPolygon>
  381. struct collect_vectors<multi_polygon_tag, Collection, MultiPolygon>
  382. : detail::collect_vectors::multi_collect_vectors
  383. <
  384. MultiPolygon,
  385. Collection,
  386. detail::collect_vectors::polygon_collect_vectors
  387. <
  388. typename boost::range_value<MultiPolygon>::type,
  389. Collection
  390. >
  391. >
  392. {};
  393. } // namespace dispatch
  394. #endif
  395. /*!
  396. \ingroup collect_vectors
  397. \tparam Collection Collection type, should be e.g. std::vector<>
  398. \tparam Geometry geometry type
  399. \param collection the collection of vectors
  400. \param geometry the geometry to make collect_vectors
  401. */
  402. template <typename Collection, typename Geometry>
  403. inline void collect_vectors(Collection& collection, Geometry const& geometry)
  404. {
  405. concepts::check<Geometry const>();
  406. dispatch::collect_vectors
  407. <
  408. typename tag<Geometry>::type,
  409. Collection,
  410. Geometry
  411. >::apply(collection, geometry);
  412. }
  413. }} // namespace boost::geometry
  414. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP