calculate_point_order.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // Boost.Geometry
  2. // Copyright (c) 2019, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  4. // Licensed under the Boost Software License version 1.0.
  5. // http://www.boost.org/users/license.html
  6. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP
  7. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP
  8. #include <vector>
  9. #include <boost/mpl/assert.hpp>
  10. #include <boost/geometry/algorithms/area.hpp>
  11. #include <boost/geometry/core/point_order.hpp>
  12. #include <boost/geometry/core/radian_access.hpp>
  13. #include <boost/geometry/strategies/geographic/point_order.hpp>
  14. #include <boost/geometry/util/math.hpp>
  15. #include <boost/geometry/util/range.hpp>
  16. namespace boost { namespace geometry
  17. {
  18. namespace detail
  19. {
  20. template <typename Iter, typename CalcT>
  21. struct clean_point
  22. {
  23. explicit clean_point(Iter const& iter)
  24. : m_iter(iter), m_azi(0), m_razi(0), m_azi_diff(0)
  25. , m_is_azi_valid(false), m_is_azi_diff_valid(false)
  26. {}
  27. typename boost::iterators::iterator_reference<Iter>::type ref() const
  28. {
  29. return *m_iter;
  30. }
  31. CalcT const& azimuth() const
  32. {
  33. return m_azi;
  34. }
  35. CalcT const& reverse_azimuth() const
  36. {
  37. return m_razi;
  38. }
  39. CalcT const& azimuth_difference() const
  40. {
  41. return m_azi_diff;
  42. }
  43. void set_azimuths(CalcT const& azi, CalcT const& razi)
  44. {
  45. m_azi = azi;
  46. m_razi = razi;
  47. m_is_azi_valid = true;
  48. }
  49. void set_azimuth_invalid()
  50. {
  51. m_is_azi_valid = false;
  52. }
  53. bool is_azimuth_valid() const
  54. {
  55. return m_is_azi_valid;
  56. }
  57. void set_azimuth_difference(CalcT const& diff)
  58. {
  59. m_azi_diff = diff;
  60. m_is_azi_diff_valid = true;
  61. }
  62. void set_azimuth_difference_invalid()
  63. {
  64. m_is_azi_diff_valid = false;
  65. }
  66. bool is_azimuth_difference_valid() const
  67. {
  68. return m_is_azi_diff_valid;
  69. }
  70. private:
  71. Iter m_iter;
  72. CalcT m_azi;
  73. CalcT m_razi;
  74. CalcT m_azi_diff;
  75. // NOTE: these flags could be removed and replaced with some magic number
  76. // assigned to the above variables, e.g. CalcT(1000).
  77. bool m_is_azi_valid;
  78. bool m_is_azi_diff_valid;
  79. };
  80. struct calculate_point_order_by_azimuth
  81. {
  82. template <typename Ring, typename Strategy>
  83. static geometry::order_selector apply(Ring const& ring, Strategy const& strategy)
  84. {
  85. typedef typename boost::range_iterator<Ring const>::type iter_t;
  86. typedef typename Strategy::template result_type<Ring>::type calc_t;
  87. typedef clean_point<iter_t, calc_t> clean_point_t;
  88. typedef std::vector<clean_point_t> cleaned_container_t;
  89. typedef typename cleaned_container_t::iterator cleaned_iter_t;
  90. calc_t const zero = 0;
  91. calc_t const pi = math::pi<calc_t>();
  92. std::size_t const count = boost::size(ring);
  93. if (count < 3)
  94. {
  95. return geometry::order_undetermined;
  96. }
  97. // non-duplicated, non-spike points
  98. cleaned_container_t cleaned;
  99. cleaned.reserve(count);
  100. for (iter_t it = boost::begin(ring); it != boost::end(ring); ++it)
  101. {
  102. // Add point
  103. cleaned.push_back(clean_point_t(it));
  104. while (cleaned.size() >= 3)
  105. {
  106. cleaned_iter_t it0 = cleaned.end() - 3;
  107. cleaned_iter_t it1 = cleaned.end() - 2;
  108. cleaned_iter_t it2 = cleaned.end() - 1;
  109. calc_t diff;
  110. if (get_or_calculate_azimuths_difference(*it0, *it1, *it2, diff, strategy)
  111. && ! math::equals(math::abs(diff), pi))
  112. {
  113. // neither duplicate nor a spike - difference already stored
  114. break;
  115. }
  116. else
  117. {
  118. // spike detected
  119. // TODO: angles have to be invalidated only if spike is detected
  120. // for duplicates it'd be ok to leave them
  121. it0->set_azimuth_invalid();
  122. it0->set_azimuth_difference_invalid();
  123. it2->set_azimuth_difference_invalid();
  124. cleaned.erase(it1);
  125. }
  126. }
  127. }
  128. // filter-out duplicates and spikes at the front and back of cleaned
  129. cleaned_iter_t cleaned_b = cleaned.begin();
  130. cleaned_iter_t cleaned_e = cleaned.end();
  131. std::size_t cleaned_count = cleaned.size();
  132. bool found = false;
  133. do
  134. {
  135. found = false;
  136. while(cleaned_count >= 3)
  137. {
  138. cleaned_iter_t it0 = cleaned_e - 2;
  139. cleaned_iter_t it1 = cleaned_e - 1;
  140. cleaned_iter_t it2 = cleaned_b;
  141. cleaned_iter_t it3 = cleaned_b + 1;
  142. calc_t diff = 0;
  143. if (! get_or_calculate_azimuths_difference(*it0, *it1, *it2, diff, strategy)
  144. || math::equals(math::abs(diff), pi))
  145. {
  146. // spike at the back
  147. // TODO: angles have to be invalidated only if spike is detected
  148. // for duplicates it'd be ok to leave them
  149. it0->set_azimuth_invalid();
  150. it0->set_azimuth_difference_invalid();
  151. it2->set_azimuth_difference_invalid();
  152. --cleaned_e;
  153. --cleaned_count;
  154. found = true;
  155. }
  156. else if (! get_or_calculate_azimuths_difference(*it1, *it2, *it3, diff, strategy)
  157. || math::equals(math::abs(diff), pi))
  158. {
  159. // spike at the front
  160. // TODO: angles have to be invalidated only if spike is detected
  161. // for duplicates it'd be ok to leave them
  162. it1->set_azimuth_invalid();
  163. it1->set_azimuth_difference_invalid();
  164. it3->set_azimuth_difference_invalid();
  165. ++cleaned_b;
  166. --cleaned_count;
  167. found = true;
  168. }
  169. else
  170. {
  171. break;
  172. }
  173. }
  174. }
  175. while (found);
  176. if (cleaned_count < 3)
  177. {
  178. return geometry::order_undetermined;
  179. }
  180. // calculate the sum of external angles
  181. calc_t angles_sum = zero;
  182. for (cleaned_iter_t it = cleaned_b; it != cleaned_e; ++it)
  183. {
  184. cleaned_iter_t it0 = (it == cleaned_b ? cleaned_e - 1 : it - 1);
  185. cleaned_iter_t it2 = (it == cleaned_e - 1 ? cleaned_b : it + 1);
  186. calc_t diff = 0;
  187. get_or_calculate_azimuths_difference(*it0, *it, *it2, diff, strategy);
  188. angles_sum += diff;
  189. }
  190. #ifdef BOOST_GEOMETRY_DEBUG_POINT_ORDER
  191. std::cout << angles_sum << " for " << geometry::wkt(ring) << std::endl;
  192. #endif
  193. return angles_sum == zero ? geometry::order_undetermined
  194. : angles_sum > zero ? geometry::clockwise
  195. : geometry::counterclockwise;
  196. }
  197. private:
  198. template <typename Iter, typename T, typename Strategy>
  199. static bool get_or_calculate_azimuths_difference(clean_point<Iter, T> & p0,
  200. clean_point<Iter, T> & p1,
  201. clean_point<Iter, T> const& p2,
  202. T & diff,
  203. Strategy const& strategy)
  204. {
  205. if (p1.is_azimuth_difference_valid())
  206. {
  207. diff = p1.azimuth_difference();
  208. return true;
  209. }
  210. T azi1, razi1, azi2, razi2;
  211. if (get_or_calculate_azimuths(p0, p1, azi1, razi1, strategy)
  212. && get_or_calculate_azimuths(p1, p2, azi2, razi2, strategy))
  213. {
  214. diff = strategy.apply(p0.ref(), p1.ref(), p2.ref(), razi1, azi2);
  215. p1.set_azimuth_difference(diff);
  216. return true;
  217. }
  218. return false;
  219. }
  220. template <typename Iter, typename T, typename Strategy>
  221. static bool get_or_calculate_azimuths(clean_point<Iter, T> & p0,
  222. clean_point<Iter, T> const& p1,
  223. T & azi, T & razi,
  224. Strategy const& strategy)
  225. {
  226. if (p0.is_azimuth_valid())
  227. {
  228. azi = p0.azimuth();
  229. razi = p0.reverse_azimuth();
  230. return true;
  231. }
  232. if (strategy.apply(p0.ref(), p1.ref(), azi, razi))
  233. {
  234. p0.set_azimuths(azi, razi);
  235. return true;
  236. }
  237. return false;
  238. }
  239. };
  240. struct calculate_point_order_by_area
  241. {
  242. template <typename Ring, typename Strategy>
  243. static geometry::order_selector apply(Ring const& ring, Strategy const& strategy)
  244. {
  245. typedef detail::area::ring_area
  246. <
  247. geometry::order_as_direction<geometry::point_order<Ring>::value>::value,
  248. geometry::closure<Ring>::value
  249. > ring_area_type;
  250. typedef typename area_result
  251. <
  252. Ring, Strategy
  253. >::type result_type;
  254. result_type const result = ring_area_type::apply(ring, strategy);
  255. result_type const zero = 0;
  256. return result == zero ? geometry::order_undetermined
  257. : result > zero ? geometry::clockwise
  258. : geometry::counterclockwise;
  259. }
  260. };
  261. } // namespace detail
  262. namespace dispatch
  263. {
  264. template
  265. <
  266. typename Strategy,
  267. typename VersionTag = typename Strategy::version_tag
  268. >
  269. struct calculate_point_order
  270. {
  271. BOOST_MPL_ASSERT_MSG
  272. (
  273. false, NOT_IMPLEMENTED_FOR_THIS_TAG, (types<VersionTag>)
  274. );
  275. };
  276. template <typename Strategy>
  277. struct calculate_point_order<Strategy, strategy::point_order::area_tag>
  278. : geometry::detail::calculate_point_order_by_area
  279. {};
  280. template <typename Strategy>
  281. struct calculate_point_order<Strategy, strategy::point_order::azimuth_tag>
  282. : geometry::detail::calculate_point_order_by_azimuth
  283. {};
  284. } // namespace dispatch
  285. namespace detail
  286. {
  287. template <typename Ring, typename Strategy>
  288. inline geometry::order_selector calculate_point_order(Ring const& ring, Strategy const& strategy)
  289. {
  290. concepts::check<Ring>();
  291. return dispatch::calculate_point_order<Strategy>::apply(ring, strategy);
  292. }
  293. template <typename Ring>
  294. inline geometry::order_selector calculate_point_order(Ring const& ring)
  295. {
  296. typedef typename strategy::point_order::services::default_strategy
  297. <
  298. typename geometry::cs_tag<Ring>::type
  299. >::type strategy_type;
  300. concepts::check<Ring>();
  301. return dispatch::calculate_point_order<strategy_type>::apply(ring, strategy_type());
  302. }
  303. } // namespace detail
  304. }} // namespace boost::geometry
  305. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP