has_spikes.hpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2014-2021, Oracle and/or its affiliates.
  3. // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle
  4. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
  5. // Licensed under the Boost Software License version 1.0.
  6. // http://www.boost.org/users/license.html
  7. #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_SPIKES_HPP
  8. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_SPIKES_HPP
  9. #include <algorithm>
  10. #include <boost/core/ignore_unused.hpp>
  11. #include <boost/range/begin.hpp>
  12. #include <boost/range/end.hpp>
  13. #include <boost/range/rbegin.hpp>
  14. #include <boost/range/rend.hpp>
  15. #include <boost/geometry/algorithms/detail/equals/point_point.hpp>
  16. #include <boost/geometry/algorithms/validity_failure_type.hpp>
  17. #include <boost/geometry/algorithms/detail/point_is_spike_or_equal.hpp>
  18. #include <boost/geometry/core/assert.hpp>
  19. #include <boost/geometry/core/point_type.hpp>
  20. #include <boost/geometry/core/tag.hpp>
  21. #include <boost/geometry/core/tags.hpp>
  22. #include <boost/geometry/io/dsv/write.hpp>
  23. #include <boost/geometry/policies/is_valid/default_policy.hpp>
  24. #include <boost/geometry/util/range.hpp>
  25. #include <boost/geometry/util/type_traits.hpp>
  26. #include <boost/geometry/views/closeable_view.hpp>
  27. namespace boost { namespace geometry
  28. {
  29. #ifndef DOXYGEN_NO_DETAIL
  30. namespace detail { namespace is_valid
  31. {
  32. template <typename Range>
  33. struct has_spikes
  34. {
  35. template <typename Iterator, typename Strategy>
  36. static inline Iterator find_different_from_first(Iterator first,
  37. Iterator last,
  38. Strategy const& strategy)
  39. {
  40. if (first == last)
  41. {
  42. return last;
  43. }
  44. auto const& front = *first;
  45. ++first;
  46. return std::find_if(first, last, [&](auto const& pt) {
  47. return ! equals::equals_point_point(pt, front, strategy);
  48. });
  49. }
  50. template <typename View, typename VisitPolicy, typename Strategy>
  51. static inline bool apply_at_closure(View const& view, VisitPolicy& visitor,
  52. Strategy const& strategy,
  53. bool is_linear)
  54. {
  55. boost::ignore_unused(visitor);
  56. auto cur = boost::begin(view);
  57. auto prev = find_different_from_first(boost::rbegin(view),
  58. boost::rend(view),
  59. strategy);
  60. auto next = find_different_from_first(cur, boost::end(view), strategy);
  61. if (detail::is_spike_or_equal(*next, *cur, *prev, strategy.side()))
  62. {
  63. return ! visitor.template apply<failure_spikes>(is_linear, *cur);
  64. }
  65. else
  66. {
  67. return ! visitor.template apply<no_failure>();
  68. }
  69. }
  70. template <typename VisitPolicy, typename Strategy>
  71. static inline bool apply(Range const& range, VisitPolicy& visitor,
  72. Strategy const& strategy)
  73. {
  74. boost::ignore_unused(visitor);
  75. bool const is_linestring = util::is_linestring<Range>::value;
  76. detail::closed_view<Range const> const view(range);
  77. auto prev = boost::begin(view);
  78. auto const end = boost::end(view);
  79. auto cur = find_different_from_first(prev, boost::end(view), strategy);
  80. if (cur == end)
  81. {
  82. // the range has only one distinct point, so it
  83. // cannot have a spike
  84. return ! visitor.template apply<no_failure>();
  85. }
  86. auto next = find_different_from_first(cur, boost::end(view), strategy);
  87. if (next == end)
  88. {
  89. // the range has only two distinct points, so it
  90. // cannot have a spike
  91. return ! visitor.template apply<no_failure>();
  92. }
  93. while (next != end)
  94. {
  95. // Verify spike. TODO: this is a reverse order from expected
  96. // in is_spike_or_equal, but this order calls the side
  97. // strategy in the way to correctly detect the spikes,
  98. // also in geographic cases going over the pole
  99. if (detail::is_spike_or_equal(*next, *cur, *prev, strategy.side()))
  100. {
  101. return ! visitor.template apply<failure_spikes>(is_linestring, *cur);
  102. }
  103. prev = cur;
  104. cur = next;
  105. next = find_different_from_first(cur, boost::end(view), strategy);
  106. }
  107. if (equals::equals_point_point(range::front(view), range::back(view),
  108. strategy))
  109. {
  110. return apply_at_closure(view, visitor, strategy, is_linestring);
  111. }
  112. return ! visitor.template apply<no_failure>();
  113. }
  114. };
  115. }} // namespace detail::is_valid
  116. #endif // DOXYGEN_NO_DETAIL
  117. }} // namespace boost::geometry
  118. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_IS_VALID_HAS_SPIKES_HPP