3 // Copyright (c) 2019, Oracle and/or its affiliates.
5 // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
7 // Licensed under the Boost Software License version 1.0.
8 // http://www.boost.org/users/license.html
10 #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP
11 #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP
16 #include <boost/mpl/assert.hpp>
18 #include <boost/geometry/algorithms/area.hpp>
19 #include <boost/geometry/core/point_order.hpp>
20 #include <boost/geometry/core/radian_access.hpp>
21 #include <boost/geometry/strategies/geographic/point_order.hpp>
22 #include <boost/geometry/util/math.hpp>
23 #include <boost/geometry/util/range.hpp>
26 namespace boost { namespace geometry
32 template <typename Iter, typename CalcT>
35 explicit clean_point(Iter const& iter)
36 : m_iter(iter), m_azi(0), m_razi(0), m_azi_diff(0)
37 , m_is_azi_valid(false), m_is_azi_diff_valid(false)
40 typename boost::iterators::iterator_reference<Iter>::type ref() const
45 CalcT const& azimuth() const
50 CalcT const& reverse_azimuth() const
55 CalcT const& azimuth_difference() const
60 void set_azimuths(CalcT const& azi, CalcT const& razi)
64 m_is_azi_valid = true;
67 void set_azimuth_invalid()
69 m_is_azi_valid = false;
72 bool is_azimuth_valid() const
74 return m_is_azi_valid;
77 void set_azimuth_difference(CalcT const& diff)
80 m_is_azi_diff_valid = true;
83 void set_azimuth_difference_invalid()
85 m_is_azi_diff_valid = false;
88 bool is_azimuth_difference_valid() const
90 return m_is_azi_diff_valid;
98 // NOTE: these flags could be removed and replaced with some magic number
99 // assigned to the above variables, e.g. CalcT(1000).
101 bool m_is_azi_diff_valid;
104 struct calculate_point_order_by_azimuth
106 template <typename Ring, typename Strategy>
107 static geometry::order_selector apply(Ring const& ring, Strategy const& strategy)
109 typedef typename boost::range_iterator<Ring const>::type iter_t;
110 typedef typename Strategy::template result_type<Ring>::type calc_t;
111 typedef clean_point<iter_t, calc_t> clean_point_t;
112 typedef std::vector<clean_point_t> cleaned_container_t;
113 typedef typename cleaned_container_t::iterator cleaned_iter_t;
115 calc_t const zero = 0;
116 calc_t const pi = math::pi<calc_t>();
118 std::size_t const count = boost::size(ring);
121 return geometry::order_undetermined;
124 // non-duplicated, non-spike points
125 cleaned_container_t cleaned;
126 cleaned.reserve(count);
128 for (iter_t it = boost::begin(ring); it != boost::end(ring); ++it)
131 cleaned.push_back(clean_point_t(it));
133 while (cleaned.size() >= 3)
135 cleaned_iter_t it0 = cleaned.end() - 3;
136 cleaned_iter_t it1 = cleaned.end() - 2;
137 cleaned_iter_t it2 = cleaned.end() - 1;
140 if (get_or_calculate_azimuths_difference(*it0, *it1, *it2, diff, strategy)
141 && ! math::equals(math::abs(diff), pi))
143 // neither duplicate nor a spike - difference already stored
149 // TODO: angles have to be invalidated only if spike is detected
150 // for duplicates it'd be ok to leave them
151 it0->set_azimuth_invalid();
152 it0->set_azimuth_difference_invalid();
153 it2->set_azimuth_difference_invalid();
159 // filter-out duplicates and spikes at the front and back of cleaned
160 cleaned_iter_t cleaned_b = cleaned.begin();
161 cleaned_iter_t cleaned_e = cleaned.end();
162 std::size_t cleaned_count = cleaned.size();
167 while(cleaned_count >= 3)
169 cleaned_iter_t it0 = cleaned_e - 2;
170 cleaned_iter_t it1 = cleaned_e - 1;
171 cleaned_iter_t it2 = cleaned_b;
172 cleaned_iter_t it3 = cleaned_b + 1;
175 if (! get_or_calculate_azimuths_difference(*it0, *it1, *it2, diff, strategy)
176 || math::equals(math::abs(diff), pi))
179 // TODO: angles have to be invalidated only if spike is detected
180 // for duplicates it'd be ok to leave them
181 it0->set_azimuth_invalid();
182 it0->set_azimuth_difference_invalid();
183 it2->set_azimuth_difference_invalid();
188 else if (! get_or_calculate_azimuths_difference(*it1, *it2, *it3, diff, strategy)
189 || math::equals(math::abs(diff), pi))
191 // spike at the front
192 // TODO: angles have to be invalidated only if spike is detected
193 // for duplicates it'd be ok to leave them
194 it1->set_azimuth_invalid();
195 it1->set_azimuth_difference_invalid();
196 it3->set_azimuth_difference_invalid();
209 if (cleaned_count < 3)
211 return geometry::order_undetermined;
214 // calculate the sum of external angles
215 calc_t angles_sum = zero;
216 for (cleaned_iter_t it = cleaned_b; it != cleaned_e; ++it)
218 cleaned_iter_t it0 = (it == cleaned_b ? cleaned_e - 1 : it - 1);
219 cleaned_iter_t it2 = (it == cleaned_e - 1 ? cleaned_b : it + 1);
222 get_or_calculate_azimuths_difference(*it0, *it, *it2, diff, strategy);
227 #ifdef BOOST_GEOMETRY_DEBUG_POINT_ORDER
228 std::cout << angles_sum << " for " << geometry::wkt(ring) << std::endl;
231 return angles_sum == zero ? geometry::order_undetermined
232 : angles_sum > zero ? geometry::clockwise
233 : geometry::counterclockwise;
237 template <typename Iter, typename T, typename Strategy>
238 static bool get_or_calculate_azimuths_difference(clean_point<Iter, T> & p0,
239 clean_point<Iter, T> & p1,
240 clean_point<Iter, T> const& p2,
242 Strategy const& strategy)
244 if (p1.is_azimuth_difference_valid())
246 diff = p1.azimuth_difference();
250 T azi1, razi1, azi2, razi2;
251 if (get_or_calculate_azimuths(p0, p1, azi1, razi1, strategy)
252 && get_or_calculate_azimuths(p1, p2, azi2, razi2, strategy))
254 diff = strategy.apply(p0.ref(), p1.ref(), p2.ref(), razi1, azi2);
255 p1.set_azimuth_difference(diff);
261 template <typename Iter, typename T, typename Strategy>
262 static bool get_or_calculate_azimuths(clean_point<Iter, T> & p0,
263 clean_point<Iter, T> const& p1,
265 Strategy const& strategy)
267 if (p0.is_azimuth_valid())
270 razi = p0.reverse_azimuth();
274 if (strategy.apply(p0.ref(), p1.ref(), azi, razi))
276 p0.set_azimuths(azi, razi);
284 struct calculate_point_order_by_area
286 template <typename Ring, typename Strategy>
287 static geometry::order_selector apply(Ring const& ring, Strategy const& strategy)
289 typedef detail::area::ring_area
291 geometry::order_as_direction<geometry::point_order<Ring>::value>::value,
292 geometry::closure<Ring>::value
295 typedef typename area_result
300 result_type const result = ring_area_type::apply(ring, strategy);
302 result_type const zero = 0;
303 return result == zero ? geometry::order_undetermined
304 : result > zero ? geometry::clockwise
305 : geometry::counterclockwise;
309 } // namespace detail
317 typename VersionTag = typename Strategy::version_tag
319 struct calculate_point_order
323 false, NOT_IMPLEMENTED_FOR_THIS_TAG, (types<VersionTag>)
327 template <typename Strategy>
328 struct calculate_point_order<Strategy, strategy::point_order::area_tag>
329 : geometry::detail::calculate_point_order_by_area
332 template <typename Strategy>
333 struct calculate_point_order<Strategy, strategy::point_order::azimuth_tag>
334 : geometry::detail::calculate_point_order_by_azimuth
338 } // namespace dispatch
343 template <typename Ring, typename Strategy>
344 inline geometry::order_selector calculate_point_order(Ring const& ring, Strategy const& strategy)
346 concepts::check<Ring>();
348 return dispatch::calculate_point_order<Strategy>::apply(ring, strategy);
351 template <typename Ring>
352 inline geometry::order_selector calculate_point_order(Ring const& ring)
354 typedef typename strategy::point_order::services::default_strategy
356 typename geometry::cs_tag<Ring>::type
357 >::type strategy_type;
359 concepts::check<Ring>();
361 return dispatch::calculate_point_order<strategy_type>::apply(ring, strategy_type());
365 } // namespace detail
367 }} // namespace boost::geometry
370 #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP