discard_duplicate_turns.hpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // Boost.Geometry (aka GGL, Generic Geometry Library)
  2. // Copyright (c) 2021 Barend Gehrels, Amsterdam, the Netherlands.
  3. // This file was modified by Oracle on 2023.
  4. // Modifications copyright (c) 2023 Oracle and/or its affiliates.
  5. // Contributed and/or modified by Vissarion Fysikopoulos, 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_DETAIL_OVERLAY_DISCARD_DUPLICATE_TURNS_HPP
  10. #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DISCARD_DUPLICATE_TURNS_HPP
  11. #include <map>
  12. #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
  13. #include <boost/geometry/algorithms/detail/overlay/get_ring.hpp>
  14. namespace boost { namespace geometry
  15. {
  16. #ifndef DOXYGEN_NO_DETAIL
  17. namespace detail { namespace overlay
  18. {
  19. inline bool same_multi_and_ring_id(segment_identifier const& first,
  20. segment_identifier const& second)
  21. {
  22. return first.ring_index == second.ring_index
  23. && first.multi_index == second.multi_index;
  24. }
  25. template <typename Geometry0, typename Geometry1>
  26. inline bool is_consecutive(segment_identifier const& first,
  27. segment_identifier const& second,
  28. Geometry0 const& geometry0,
  29. Geometry1 const& geometry1)
  30. {
  31. if (first.source_index == second.source_index
  32. && first.ring_index == second.ring_index
  33. && first.multi_index == second.multi_index)
  34. {
  35. // If the segment distance is 1, there are no segments in between
  36. // and the segments are consecutive.
  37. signed_size_type const sd = first.source_index == 0
  38. ? segment_distance(geometry0, first, second)
  39. : segment_distance(geometry1, first, second);
  40. return sd <= 1;
  41. }
  42. return false;
  43. }
  44. // Returns true if two turns correspond to each other in the sense that one has
  45. // "method_start" and the other has "method_touch" or "method_interior" at that
  46. // same point (but with next segment)
  47. template <typename Turn, typename Geometry0, typename Geometry1>
  48. inline bool corresponding_turn(Turn const& turn, Turn const& start_turn,
  49. Geometry0 const& geometry0,
  50. Geometry1 const& geometry1)
  51. {
  52. std::size_t count = 0;
  53. for (std::size_t i = 0; i < 2; i++)
  54. {
  55. for (std::size_t j = 0; j < 2; j++)
  56. {
  57. if (same_multi_and_ring_id(turn.operations[i].seg_id,
  58. start_turn.operations[j].seg_id))
  59. {
  60. // Verify if all operations are consecutive
  61. if (is_consecutive(turn.operations[i].seg_id,
  62. start_turn.operations[j].seg_id,
  63. geometry0, geometry1)
  64. && is_consecutive(turn.operations[1 - i].seg_id,
  65. start_turn.operations[1 - j].seg_id,
  66. geometry0, geometry1))
  67. {
  68. count++;
  69. }
  70. }
  71. }
  72. }
  73. // An intersection is located on exactly two rings
  74. // The corresponding turn, if any, should be located on the same two rings.
  75. return count == 2;
  76. }
  77. template <typename Turns, typename Geometry0, typename Geometry1>
  78. inline void discard_duplicate_start_turns(Turns& turns,
  79. Geometry0 const& geometry0,
  80. Geometry1 const& geometry1)
  81. {
  82. // Start turns are generated, in case the previous turn is missed.
  83. // But often it is not missed, and then it should be deleted.
  84. // This is how it can be
  85. // (in float, collinear, points far apart due to floating point precision)
  86. // [m, i s:0, v:6 1/1 (1) // u s:1, v:5 pnt (2.54044, 3.12623)]
  87. // [s, i s:0, v:7 0/1 (0) // u s:1, v:5 pnt (2.70711, 3.29289)]
  88. using multi_and_ring_id_type = std::pair<signed_size_type, signed_size_type>;
  89. auto adapt_id = [](segment_identifier const& seg_id)
  90. {
  91. return multi_and_ring_id_type{seg_id.multi_index, seg_id.ring_index};
  92. };
  93. // 1 Build map of start turns (multi/ring-id -> turn indices)
  94. std::map<multi_and_ring_id_type, std::vector<std::size_t>> start_turns_per_segment;
  95. std::size_t index = 0;
  96. for (auto const& turn : turns)
  97. {
  98. if (turn.method == method_start)
  99. {
  100. for (auto const& op : turn.operations)
  101. {
  102. start_turns_per_segment[adapt_id(op.seg_id)].push_back(index);
  103. }
  104. }
  105. index++;
  106. }
  107. // 2: Verify all other turns if they are present in the map, and if so,
  108. // if they have the other turns as corresponding
  109. for (auto const& turn : turns)
  110. {
  111. // Any turn which "crosses" does not have a corresponding turn.
  112. // Also avoid comparing "start" with itself.
  113. if (turn.method != method_crosses && turn.method != method_start)
  114. {
  115. for (auto const& op : turn.operations)
  116. {
  117. auto it = start_turns_per_segment.find(adapt_id(op.seg_id));
  118. if (it != start_turns_per_segment.end())
  119. {
  120. for (std::size_t const& i : it->second)
  121. {
  122. if (turns[i].cluster_id != turn.cluster_id)
  123. {
  124. // The turns are not part of the same cluster,
  125. // or one is clustered and the other is not.
  126. // This is not corresponding.
  127. continue;
  128. }
  129. if (corresponding_turn(turn, turns[i],
  130. geometry0, geometry1))
  131. {
  132. turns[i].discarded = true;
  133. }
  134. }
  135. }
  136. }
  137. }
  138. index++;
  139. }
  140. }
  141. }} // namespace detail::overlay
  142. #endif //DOXYGEN_NO_DETAIL
  143. }} // namespace boost::geometry
  144. #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_OVERLAY_DISCARD_DUPLICATE_TURNS_HPP