123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364 |
- // Boost.Geometry
- // Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
- // Copyright (c) 2019-2021, Oracle and/or its affiliates.
- // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle
- // Licensed under the Boost Software License version 1.0.
- // http://www.boost.org/users/license.html
- #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP
- #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP
- #include <vector>
- #include <boost/range/size.hpp>
- #include <boost/geometry/algorithms/area.hpp>
- #include <boost/geometry/core/point_order.hpp>
- #include <boost/geometry/core/radian_access.hpp>
- #include <boost/geometry/core/static_assert.hpp>
- #include <boost/geometry/strategies/geographic/point_order.hpp>
- #include <boost/geometry/util/math.hpp>
- #include <boost/geometry/util/range.hpp>
- namespace boost { namespace geometry
- {
- namespace detail
- {
- template <typename Iter, typename CalcT>
- struct clean_point
- {
- explicit clean_point(Iter const& iter)
- : m_iter(iter), m_azi(0), m_razi(0), m_azi_diff(0)
- , m_is_azi_valid(false), m_is_azi_diff_valid(false)
- {}
- decltype(auto) ref() const
- {
- return *m_iter;
- }
- CalcT const& azimuth() const
- {
- return m_azi;
- }
- CalcT const& reverse_azimuth() const
- {
- return m_razi;
- }
- CalcT const& azimuth_difference() const
- {
- return m_azi_diff;
- }
- void set_azimuths(CalcT const& azi, CalcT const& razi)
- {
- m_azi = azi;
- m_razi = razi;
- m_is_azi_valid = true;
- }
- void set_azimuth_invalid()
- {
- m_is_azi_valid = false;
- }
- bool is_azimuth_valid() const
- {
- return m_is_azi_valid;
- }
- void set_azimuth_difference(CalcT const& diff)
- {
- m_azi_diff = diff;
- m_is_azi_diff_valid = true;
- }
- void set_azimuth_difference_invalid()
- {
- m_is_azi_diff_valid = false;
- }
- bool is_azimuth_difference_valid() const
- {
- return m_is_azi_diff_valid;
- }
- private:
- Iter m_iter;
- CalcT m_azi;
- CalcT m_razi;
- CalcT m_azi_diff;
- // NOTE: these flags could be removed and replaced with some magic number
- // assigned to the above variables, e.g. CalcT(1000).
- bool m_is_azi_valid;
- bool m_is_azi_diff_valid;
- };
- struct calculate_point_order_by_azimuth
- {
- template <typename Ring, typename Strategy>
- static geometry::order_selector apply(Ring const& ring, Strategy const& strategy)
- {
- typedef typename boost::range_iterator<Ring const>::type iter_t;
- typedef typename Strategy::template result_type<Ring>::type calc_t;
- typedef clean_point<iter_t, calc_t> clean_point_t;
- calc_t const zero = 0;
- calc_t const pi = math::pi<calc_t>();
- std::size_t const count = boost::size(ring);
- if (count < 3)
- {
- return geometry::order_undetermined;
- }
- // non-duplicated, non-spike points
- std::vector<clean_point_t> cleaned;
- cleaned.reserve(count);
- for (iter_t it = boost::begin(ring); it != boost::end(ring); ++it)
- {
- // Add point
- cleaned.push_back(clean_point_t(it));
- while (cleaned.size() >= 3)
- {
- auto it0 = cleaned.end() - 3;
- auto it1 = cleaned.end() - 2;
- auto it2 = cleaned.end() - 1;
- calc_t diff;
- if (get_or_calculate_azimuths_difference(*it0, *it1, *it2, diff, strategy)
- && ! math::equals(math::abs(diff), pi))
- {
- // neither duplicate nor a spike - difference already stored
- break;
- }
- else
- {
- // spike detected
- // TODO: angles have to be invalidated only if spike is detected
- // for duplicates it'd be ok to leave them
- it0->set_azimuth_invalid();
- it0->set_azimuth_difference_invalid();
- it2->set_azimuth_difference_invalid();
- cleaned.erase(it1);
- }
- }
- }
- // filter-out duplicates and spikes at the front and back of cleaned
- auto cleaned_b = cleaned.begin();
- auto cleaned_e = cleaned.end();
- std::size_t cleaned_count = cleaned.size();
- bool found = false;
- do
- {
- found = false;
- while(cleaned_count >= 3)
- {
- auto it0 = cleaned_e - 2;
- auto it1 = cleaned_e - 1;
- auto it2 = cleaned_b;
- auto it3 = cleaned_b + 1;
- calc_t diff = 0;
- if (! get_or_calculate_azimuths_difference(*it0, *it1, *it2, diff, strategy)
- || math::equals(math::abs(diff), pi))
- {
- // spike at the back
- // TODO: angles have to be invalidated only if spike is detected
- // for duplicates it'd be ok to leave them
- it0->set_azimuth_invalid();
- it0->set_azimuth_difference_invalid();
- it2->set_azimuth_difference_invalid();
- --cleaned_e;
- --cleaned_count;
- found = true;
- }
- else if (! get_or_calculate_azimuths_difference(*it1, *it2, *it3, diff, strategy)
- || math::equals(math::abs(diff), pi))
- {
- // spike at the front
- // TODO: angles have to be invalidated only if spike is detected
- // for duplicates it'd be ok to leave them
- it1->set_azimuth_invalid();
- it1->set_azimuth_difference_invalid();
- it3->set_azimuth_difference_invalid();
- ++cleaned_b;
- --cleaned_count;
- found = true;
- }
- else
- {
- break;
- }
- }
- }
- while (found);
- if (cleaned_count < 3)
- {
- return geometry::order_undetermined;
- }
- // calculate the sum of external angles
- calc_t angles_sum = zero;
- for (auto it = cleaned_b; it != cleaned_e; ++it)
- {
- auto it0 = (it == cleaned_b ? cleaned_e - 1 : it - 1);
- auto it2 = (it == cleaned_e - 1 ? cleaned_b : it + 1);
- calc_t diff = 0;
- get_or_calculate_azimuths_difference(*it0, *it, *it2, diff, strategy);
- angles_sum += diff;
- }
- #ifdef BOOST_GEOMETRY_DEBUG_POINT_ORDER
- std::cout << angles_sum << " for " << geometry::wkt(ring) << std::endl;
- #endif
- return angles_sum == zero ? geometry::order_undetermined
- : angles_sum > zero ? geometry::clockwise
- : geometry::counterclockwise;
- }
- private:
- template <typename Iter, typename T, typename Strategy>
- static bool get_or_calculate_azimuths_difference(clean_point<Iter, T> & p0,
- clean_point<Iter, T> & p1,
- clean_point<Iter, T> const& p2,
- T & diff,
- Strategy const& strategy)
- {
- if (p1.is_azimuth_difference_valid())
- {
- diff = p1.azimuth_difference();
- return true;
- }
- T azi1, razi1, azi2, razi2;
- if (get_or_calculate_azimuths(p0, p1, azi1, razi1, strategy)
- && get_or_calculate_azimuths(p1, p2, azi2, razi2, strategy))
- {
- diff = strategy.apply(p0.ref(), p1.ref(), p2.ref(), razi1, azi2);
- p1.set_azimuth_difference(diff);
- return true;
- }
- return false;
- }
- template <typename Iter, typename T, typename Strategy>
- static bool get_or_calculate_azimuths(clean_point<Iter, T> & p0,
- clean_point<Iter, T> const& p1,
- T & azi, T & razi,
- Strategy const& strategy)
- {
- if (p0.is_azimuth_valid())
- {
- azi = p0.azimuth();
- razi = p0.reverse_azimuth();
- return true;
- }
- if (strategy.apply(p0.ref(), p1.ref(), azi, razi))
- {
- p0.set_azimuths(azi, razi);
- return true;
- }
- return false;
- }
- };
- struct calculate_point_order_by_area
- {
- template <typename Ring, typename Strategy>
- static geometry::order_selector apply(Ring const& ring, Strategy const& strategy)
- {
- auto const result = detail::area::ring_area::apply(
- ring,
- // TEMP - in the future (umbrella) strategy will be passed
- geometry::strategies::area::services::strategy_converter
- <
- decltype(strategy.get_area_strategy())
- >::get(strategy.get_area_strategy()));
- decltype(result) const zero = 0;
- return result == zero ? geometry::order_undetermined
- : result > zero ? geometry::clockwise
- : geometry::counterclockwise;
- }
- };
- } // namespace detail
- namespace dispatch
- {
- template
- <
- typename Strategy,
- typename VersionTag = typename Strategy::version_tag
- >
- struct calculate_point_order
- {
- BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
- "Not implemented for this VersionTag.",
- VersionTag);
- };
- template <typename Strategy>
- struct calculate_point_order<Strategy, strategy::point_order::area_tag>
- : geometry::detail::calculate_point_order_by_area
- {};
- template <typename Strategy>
- struct calculate_point_order<Strategy, strategy::point_order::azimuth_tag>
- : geometry::detail::calculate_point_order_by_azimuth
- {};
- } // namespace dispatch
- namespace detail
- {
- template <typename Ring, typename Strategy>
- inline geometry::order_selector calculate_point_order(Ring const& ring, Strategy const& strategy)
- {
- concepts::check<Ring const>();
- return dispatch::calculate_point_order<Strategy>::apply(ring, strategy);
- }
- template <typename Ring>
- inline geometry::order_selector calculate_point_order(Ring const& ring)
- {
- typedef typename strategy::point_order::services::default_strategy
- <
- typename geometry::cs_tag<Ring>::type
- >::type strategy_type;
- concepts::check<Ring const>();
- return dispatch::calculate_point_order<strategy_type>::apply(ring, strategy_type());
- }
- } // namespace detail
- }} // namespace boost::geometry
- #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_CALCULATE_POINT_ORDER_HPP
|