collect_vectors.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  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, 2019.
  7. // Modifications copyright (c) 2017, 2019 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/numeric/conversion/cast.hpp>
  17. #include <boost/geometry/algorithms/detail/interior_iterator.hpp>
  18. #include <boost/geometry/algorithms/detail/normalize.hpp>
  19. #include <boost/geometry/algorithms/not_implemented.hpp>
  20. #include <boost/geometry/core/cs.hpp>
  21. #include <boost/geometry/core/interior_rings.hpp>
  22. #include <boost/geometry/core/tags.hpp>
  23. #include <boost/geometry/formulas/spherical.hpp>
  24. #include <boost/geometry/geometries/concepts/check.hpp>
  25. #include <boost/geometry/util/math.hpp>
  26. #include <boost/geometry/util/range.hpp>
  27. #include <boost/geometry/views/detail/normalized_view.hpp>
  28. #include <boost/geometry/strategies/cartesian/side_by_triangle.hpp>
  29. #include <boost/geometry/strategies/spherical/ssf.hpp>
  30. #include <boost/geometry/strategies/normalize.hpp>
  31. namespace boost { namespace geometry
  32. {
  33. // Since these vectors (though ray would be a better name) are used in the
  34. // implementation of equals() for Areal geometries the internal representation
  35. // should be consistent with the side strategy.
  36. template
  37. <
  38. typename T,
  39. typename Geometry,
  40. typename SideStrategy,
  41. typename CSTag = typename cs_tag<Geometry>::type
  42. >
  43. struct collected_vector
  44. : nyi::not_implemented_tag
  45. {};
  46. // compatible with side_by_triangle cartesian strategy
  47. template <typename T, typename Geometry, typename CT, typename CSTag>
  48. struct collected_vector
  49. <
  50. T, Geometry, strategy::side::side_by_triangle<CT>, CSTag
  51. >
  52. {
  53. typedef T type;
  54. inline collected_vector()
  55. {}
  56. inline collected_vector(T const& px, T const& py,
  57. T const& pdx, T const& pdy)
  58. : x(px)
  59. , y(py)
  60. , dx(pdx)
  61. , dy(pdy)
  62. //, dx_0(dx)
  63. //, dy_0(dy)
  64. {}
  65. template <typename Point>
  66. inline collected_vector(Point const& p1, Point const& p2)
  67. : x(get<0>(p1))
  68. , y(get<1>(p1))
  69. , dx(get<0>(p2) - x)
  70. , dy(get<1>(p2) - y)
  71. //, dx_0(dx)
  72. //, dy_0(dy)
  73. {}
  74. bool normalize()
  75. {
  76. T magnitude = math::sqrt(
  77. boost::numeric_cast<T>(dx * dx + dy * dy));
  78. // NOTE: shouldn't here math::equals() be called?
  79. if (magnitude > 0)
  80. {
  81. dx /= magnitude;
  82. dy /= magnitude;
  83. return true;
  84. }
  85. return false;
  86. }
  87. // For sorting
  88. inline bool operator<(collected_vector const& other) const
  89. {
  90. if (math::equals(x, other.x))
  91. {
  92. if (math::equals(y, other.y))
  93. {
  94. if (math::equals(dx, other.dx))
  95. {
  96. return dy < other.dy;
  97. }
  98. return dx < other.dx;
  99. }
  100. return y < other.y;
  101. }
  102. return x < other.x;
  103. }
  104. inline bool next_is_collinear(collected_vector const& other) const
  105. {
  106. return same_direction(other);
  107. }
  108. // For std::equals
  109. inline bool operator==(collected_vector const& other) const
  110. {
  111. return math::equals(x, other.x)
  112. && math::equals(y, other.y)
  113. && same_direction(other);
  114. }
  115. private:
  116. inline bool same_direction(collected_vector const& other) const
  117. {
  118. // For high precision arithmetic, we have to be
  119. // more relaxed then using ==
  120. // Because 2/sqrt( (0,0)<->(2,2) ) == 1/sqrt( (0,0)<->(1,1) )
  121. // is not always true (at least, it is not for ttmath)
  122. return math::equals_with_epsilon(dx, other.dx)
  123. && math::equals_with_epsilon(dy, other.dy);
  124. }
  125. T x, y;
  126. T dx, dy;
  127. //T dx_0, dy_0;
  128. };
  129. // Compatible with spherical_side_formula which currently
  130. // is the default spherical_equatorial and geographic strategy
  131. // so CSTag is spherical_equatorial_tag or geographic_tag
  132. template <typename T, typename Geometry, typename CT, typename CSTag>
  133. struct collected_vector
  134. <
  135. T, Geometry, strategy::side::spherical_side_formula<CT>, CSTag
  136. >
  137. {
  138. typedef T type;
  139. typedef typename geometry::detail::cs_angular_units<Geometry>::type units_type;
  140. typedef model::point<T, 2, cs::spherical_equatorial<units_type> > point_type;
  141. typedef model::point<T, 3, cs::cartesian> vector_type;
  142. collected_vector()
  143. {}
  144. template <typename Point>
  145. collected_vector(Point const& p1, Point const& p2)
  146. : origin(get<0>(p1), get<1>(p1))
  147. {
  148. origin = detail::return_normalized<point_type>(
  149. origin,
  150. strategy::normalize::spherical_point());
  151. using namespace geometry::formula;
  152. prev = sph_to_cart3d<vector_type>(p1);
  153. next = sph_to_cart3d<vector_type>(p2);
  154. direction = cross_product(prev, next);
  155. }
  156. bool normalize()
  157. {
  158. T magnitude_sqr = dot_product(direction, direction);
  159. // NOTE: shouldn't here math::equals() be called?
  160. if (magnitude_sqr > 0)
  161. {
  162. divide_value(direction, math::sqrt(magnitude_sqr));
  163. return true;
  164. }
  165. return false;
  166. }
  167. bool operator<(collected_vector const& other) const
  168. {
  169. if (math::equals(get<0>(origin), get<0>(other.origin)))
  170. {
  171. if (math::equals(get<1>(origin), get<1>(other.origin)))
  172. {
  173. if (math::equals(get<0>(direction), get<0>(other.direction)))
  174. {
  175. if (math::equals(get<1>(direction), get<1>(other.direction)))
  176. {
  177. return get<2>(direction) < get<2>(other.direction);
  178. }
  179. return get<1>(direction) < get<1>(other.direction);
  180. }
  181. return get<0>(direction) < get<0>(other.direction);
  182. }
  183. return get<1>(origin) < get<1>(other.origin);
  184. }
  185. return get<0>(origin) < get<0>(other.origin);
  186. }
  187. // For consistency with side and intersection strategies used by relops
  188. // IMPORTANT: this method should be called for previous vector
  189. // and next vector should be passed as parameter
  190. bool next_is_collinear(collected_vector const& other) const
  191. {
  192. return formula::sph_side_value(direction, other.next) == 0;
  193. }
  194. // For std::equals
  195. bool operator==(collected_vector const& other) const
  196. {
  197. return math::equals(get<0>(origin), get<0>(other.origin))
  198. && math::equals(get<1>(origin), get<1>(other.origin))
  199. && is_collinear(other);
  200. }
  201. private:
  202. // For consistency with side and intersection strategies used by relops
  203. bool is_collinear(collected_vector const& other) const
  204. {
  205. return formula::sph_side_value(direction, other.prev) == 0
  206. && formula::sph_side_value(direction, other.next) == 0;
  207. }
  208. /*bool same_direction(collected_vector const& other) const
  209. {
  210. return math::equals_with_epsilon(get<0>(direction), get<0>(other.direction))
  211. && math::equals_with_epsilon(get<1>(direction), get<1>(other.direction))
  212. && math::equals_with_epsilon(get<2>(direction), get<2>(other.direction));
  213. }*/
  214. point_type origin; // used for sorting and equality check
  215. vector_type direction; // used for sorting, only in operator<
  216. vector_type prev; // used for collinearity check, only in operator==
  217. vector_type next; // used for collinearity check
  218. };
  219. // Specialization for spherical polar
  220. template <typename T, typename Geometry, typename CT>
  221. struct collected_vector
  222. <
  223. T, Geometry,
  224. strategy::side::spherical_side_formula<CT>,
  225. spherical_polar_tag
  226. >
  227. : public collected_vector
  228. <
  229. T, Geometry,
  230. strategy::side::spherical_side_formula<CT>,
  231. spherical_equatorial_tag
  232. >
  233. {
  234. typedef collected_vector
  235. <
  236. T, Geometry,
  237. strategy::side::spherical_side_formula<CT>,
  238. spherical_equatorial_tag
  239. > base_type;
  240. collected_vector() {}
  241. template <typename Point>
  242. collected_vector(Point const& p1, Point const& p2)
  243. : base_type(to_equatorial(p1), to_equatorial(p2))
  244. {}
  245. private:
  246. template <typename Point>
  247. Point to_equatorial(Point const& p)
  248. {
  249. typedef typename coordinate_type<Point>::type coord_type;
  250. typedef math::detail::constants_on_spheroid
  251. <
  252. coord_type,
  253. typename coordinate_system<Point>::type::units
  254. > constants;
  255. coord_type const pi_2 = constants::half_period() / 2;
  256. Point res = p;
  257. set<1>(res, pi_2 - get<1>(p));
  258. return res;
  259. }
  260. };
  261. // TODO: specialize collected_vector for geographic_tag
  262. #ifndef DOXYGEN_NO_DETAIL
  263. namespace detail { namespace collect_vectors
  264. {
  265. template <typename Range, typename Collection>
  266. struct range_collect_vectors
  267. {
  268. typedef typename boost::range_value<Collection>::type item_type;
  269. typedef typename item_type::type calculation_type;
  270. static inline void apply(Collection& collection, Range const& range)
  271. {
  272. typedef geometry::detail::normalized_view
  273. <
  274. Range const
  275. > normalized_range_type;
  276. apply_impl(collection, normalized_range_type(range));
  277. }
  278. private:
  279. template <typename NormalizedRange>
  280. static inline void apply_impl(Collection& collection, NormalizedRange const& range)
  281. {
  282. if (boost::size(range) < 2)
  283. {
  284. return;
  285. }
  286. typedef typename boost::range_size<Collection>::type collection_size_t;
  287. collection_size_t c_old_size = boost::size(collection);
  288. typedef typename boost::range_iterator<NormalizedRange const>::type iterator;
  289. bool is_first = true;
  290. iterator it = boost::begin(range);
  291. for (iterator prev = it++;
  292. it != boost::end(range);
  293. prev = it++)
  294. {
  295. typename boost::range_value<Collection>::type v(*prev, *it);
  296. // Normalize the vector -> this results in points+direction
  297. // and is comparible between geometries
  298. // Avoid non-duplicate points (AND division by zero)
  299. if (v.normalize())
  300. {
  301. // Avoid non-direction changing points
  302. if (is_first || ! collection.back().next_is_collinear(v))
  303. {
  304. collection.push_back(v);
  305. }
  306. is_first = false;
  307. }
  308. }
  309. // If first one has same direction as last one, remove first one
  310. collection_size_t collected_count = boost::size(collection) - c_old_size;
  311. if ( collected_count > 1 )
  312. {
  313. typedef typename boost::range_iterator<Collection>::type c_iterator;
  314. c_iterator first = range::pos(collection, c_old_size);
  315. if (collection.back().next_is_collinear(*first) )
  316. {
  317. //collection.erase(first);
  318. // O(1) instead of O(N)
  319. *first = collection.back();
  320. collection.pop_back();
  321. }
  322. }
  323. }
  324. };
  325. // Default version (cartesian)
  326. template <typename Box, typename Collection, typename CSTag = typename cs_tag<Box>::type>
  327. struct box_collect_vectors
  328. {
  329. // Calculate on coordinate type, but if it is integer,
  330. // then use double
  331. typedef typename boost::range_value<Collection>::type item_type;
  332. typedef typename item_type::type calculation_type;
  333. static inline void apply(Collection& collection, Box const& box)
  334. {
  335. typename point_type<Box>::type lower_left, lower_right,
  336. upper_left, upper_right;
  337. geometry::detail::assign_box_corners(box, lower_left, lower_right,
  338. upper_left, upper_right);
  339. typedef typename boost::range_value<Collection>::type item;
  340. collection.push_back(item(get<0>(lower_left), get<1>(lower_left), 0, 1));
  341. collection.push_back(item(get<0>(upper_left), get<1>(upper_left), 1, 0));
  342. collection.push_back(item(get<0>(upper_right), get<1>(upper_right), 0, -1));
  343. collection.push_back(item(get<0>(lower_right), get<1>(lower_right), -1, 0));
  344. }
  345. };
  346. // NOTE: This is not fully correct because Box in spherical and geographic
  347. // cordinate systems cannot be seen as Polygon
  348. template <typename Box, typename Collection>
  349. struct box_collect_vectors<Box, Collection, spherical_equatorial_tag>
  350. {
  351. static inline void apply(Collection& collection, Box const& box)
  352. {
  353. typename point_type<Box>::type lower_left, lower_right,
  354. upper_left, upper_right;
  355. geometry::detail::assign_box_corners(box, lower_left, lower_right,
  356. upper_left, upper_right);
  357. typedef typename boost::range_value<Collection>::type item;
  358. collection.push_back(item(lower_left, upper_left));
  359. collection.push_back(item(upper_left, upper_right));
  360. collection.push_back(item(upper_right, lower_right));
  361. collection.push_back(item(lower_right, lower_left));
  362. }
  363. };
  364. template <typename Box, typename Collection>
  365. struct box_collect_vectors<Box, Collection, spherical_polar_tag>
  366. : box_collect_vectors<Box, Collection, spherical_equatorial_tag>
  367. {};
  368. template <typename Box, typename Collection>
  369. struct box_collect_vectors<Box, Collection, geographic_tag>
  370. : box_collect_vectors<Box, Collection, spherical_equatorial_tag>
  371. {};
  372. template <typename Polygon, typename Collection>
  373. struct polygon_collect_vectors
  374. {
  375. static inline void apply(Collection& collection, Polygon const& polygon)
  376. {
  377. typedef typename geometry::ring_type<Polygon>::type ring_type;
  378. typedef range_collect_vectors<ring_type, Collection> per_range;
  379. per_range::apply(collection, exterior_ring(polygon));
  380. typename interior_return_type<Polygon const>::type
  381. rings = interior_rings(polygon);
  382. for (typename detail::interior_iterator<Polygon const>::type
  383. it = boost::begin(rings); it != boost::end(rings); ++it)
  384. {
  385. per_range::apply(collection, *it);
  386. }
  387. }
  388. };
  389. template <typename MultiGeometry, typename Collection, typename SinglePolicy>
  390. struct multi_collect_vectors
  391. {
  392. static inline void apply(Collection& collection, MultiGeometry const& multi)
  393. {
  394. for (typename boost::range_iterator<MultiGeometry const>::type
  395. it = boost::begin(multi);
  396. it != boost::end(multi);
  397. ++it)
  398. {
  399. SinglePolicy::apply(collection, *it);
  400. }
  401. }
  402. };
  403. }} // namespace detail::collect_vectors
  404. #endif // DOXYGEN_NO_DETAIL
  405. #ifndef DOXYGEN_NO_DISPATCH
  406. namespace dispatch
  407. {
  408. template
  409. <
  410. typename Tag,
  411. typename Collection,
  412. typename Geometry
  413. >
  414. struct collect_vectors
  415. {
  416. static inline void apply(Collection&, Geometry const&)
  417. {}
  418. };
  419. template <typename Collection, typename Box>
  420. struct collect_vectors<box_tag, Collection, Box>
  421. : detail::collect_vectors::box_collect_vectors<Box, Collection>
  422. {};
  423. template <typename Collection, typename Ring>
  424. struct collect_vectors<ring_tag, Collection, Ring>
  425. : detail::collect_vectors::range_collect_vectors<Ring, Collection>
  426. {};
  427. template <typename Collection, typename LineString>
  428. struct collect_vectors<linestring_tag, Collection, LineString>
  429. : detail::collect_vectors::range_collect_vectors<LineString, Collection>
  430. {};
  431. template <typename Collection, typename Polygon>
  432. struct collect_vectors<polygon_tag, Collection, Polygon>
  433. : detail::collect_vectors::polygon_collect_vectors<Polygon, Collection>
  434. {};
  435. template <typename Collection, typename MultiPolygon>
  436. struct collect_vectors<multi_polygon_tag, Collection, MultiPolygon>
  437. : detail::collect_vectors::multi_collect_vectors
  438. <
  439. MultiPolygon,
  440. Collection,
  441. detail::collect_vectors::polygon_collect_vectors
  442. <
  443. typename boost::range_value<MultiPolygon>::type,
  444. Collection
  445. >
  446. >
  447. {};
  448. } // namespace dispatch
  449. #endif
  450. /*!
  451. \ingroup collect_vectors
  452. \tparam Collection Collection type, should be e.g. std::vector<>
  453. \tparam Geometry geometry type
  454. \param collection the collection of vectors
  455. \param geometry the geometry to make collect_vectors
  456. */
  457. template <typename Collection, typename Geometry>
  458. inline void collect_vectors(Collection& collection, Geometry const& geometry)
  459. {
  460. concepts::check<Geometry const>();
  461. dispatch::collect_vectors
  462. <
  463. typename tag<Geometry>::type,
  464. Collection,
  465. Geometry
  466. >::apply(collection, geometry);
  467. }
  468. }} // namespace boost::geometry
  469. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_EQUALS_COLLECT_VECTORS_HPP