| // Boost.Geometry (aka GGL, Generic Geometry Library) |
| |
| // Copyright (c) 2014-2015, Oracle and/or its affiliates. |
| |
| // Licensed under the Boost Software License version 1.0. |
| // http://www.boost.org/users/license.html |
| |
| // Contributed and/or modified by Menelaos Karavelas, on behalf of Oracle |
| |
| #ifndef BOOST_GEOMETRY_TEST_SET_OPS_LINEAR_LINEAR_HPP |
| #define BOOST_GEOMETRY_TEST_SET_OPS_LINEAR_LINEAR_HPP |
| |
| |
| #include <string> |
| #include <fstream> |
| #include <sstream> |
| #include <algorithm> |
| |
| #include <boost/core/ignore_unused.hpp> |
| #include <boost/range.hpp> |
| #include <boost/geometry/policies/compare.hpp> |
| #include <boost/geometry/algorithms/equals.hpp> |
| #include <boost/geometry/algorithms/reverse.hpp> |
| #include <boost/geometry/multi/algorithms/reverse.hpp> |
| |
| #include "test_get_turns_ll_invariance.hpp" |
| |
| namespace bg = ::boost::geometry; |
| |
| |
| |
| template <typename Linestring1, typename Linestring2> |
| struct ls_less |
| { |
| typedef typename boost::range_iterator<Linestring1 const>::type Iterator1; |
| typedef typename boost::range_iterator<Linestring2 const>::type Iterator2; |
| |
| typedef bg::less<typename bg::point_type<Linestring1>::type> point_less; |
| |
| bool operator()(Linestring1 const& linestring1, |
| Linestring2 const& linestring2) const |
| { |
| if ( boost::size(linestring1) != boost::size(linestring2) ) |
| return boost::size(linestring1) < boost::size(linestring2); |
| |
| Iterator1 it1 = boost::begin(linestring1); |
| Iterator2 it2 = boost::begin(linestring2); |
| point_less less; |
| for (; it1 != boost::end(linestring1); ++it1, ++it2) |
| { |
| if ( less(*it1, *it2) ) |
| return true; |
| if ( less(*it2, *it1) ) |
| return false; |
| } |
| return false; |
| } |
| }; |
| |
| |
| template <typename Linestring1, typename Linestring2> |
| struct ls_equal |
| { |
| bool operator()(Linestring1 const& linestring1, |
| Linestring2 const& linestring2) const |
| { |
| ls_less<Linestring1, Linestring2> less; |
| |
| return !less(linestring1, linestring2) |
| && !less(linestring2, linestring1); |
| } |
| }; |
| |
| |
| template <typename Point1, typename Point2> |
| class pt_equal |
| { |
| private: |
| double m_tolerence; |
| |
| template <typename T> |
| static inline T const& get_max(T const& a, T const& b, T const& c) |
| { |
| return (std::max)((std::max)(a, b), c); |
| } |
| |
| template <typename T> |
| static inline bool check_close(T const& a, T const& b, T const& tol) |
| { |
| return (a == b) |
| || (std::abs(a - b) <= tol * get_max(std::abs(a), std::abs(b), 1.0)); |
| } |
| |
| public: |
| pt_equal(double tolerence) : m_tolerence(tolerence) {} |
| |
| bool operator()(Point1 const& point1, Point2 const& point2) const |
| { |
| // allow for some tolerence in testing equality of points |
| return check_close(bg::get<0>(point1), bg::get<0>(point2), m_tolerence) |
| && check_close(bg::get<1>(point1), bg::get<1>(point2), m_tolerence); |
| } |
| }; |
| |
| |
| template <bool EnableUnique = false> |
| struct multilinestring_equals |
| { |
| template <typename MultiLinestring, bool Enable> |
| struct unique |
| { |
| typedef typename boost::range_value<MultiLinestring>::type Linestring; |
| typedef typename bg::point_type<MultiLinestring>::type point_type; |
| typedef ls_equal<Linestring, Linestring> linestring_equal; |
| typedef pt_equal<point_type, point_type> point_equal; |
| |
| template <typename Range, typename EqualTo> |
| void apply_to_range(Range& range, EqualTo const& equal_to) |
| { |
| range.erase(std::unique(boost::begin(range), boost::end(range), |
| equal_to), |
| boost::end(range)); |
| } |
| |
| void operator()(MultiLinestring& mls, double tolerance) |
| { |
| for (typename boost::range_iterator<MultiLinestring>::type it |
| = boost::begin(mls); it != boost::end(mls); ++it) |
| { |
| apply_to_range(*it, point_equal(tolerance)); |
| } |
| apply_to_range(mls, linestring_equal()); |
| } |
| }; |
| |
| template <typename MultiLinestring> |
| struct unique<MultiLinestring, false> |
| { |
| void operator()(MultiLinestring&, double) |
| { |
| } |
| }; |
| |
| template <typename MultiLinestring1, typename MultiLinestring2> |
| static inline |
| bool apply(MultiLinestring1 const& multilinestring1, |
| MultiLinestring2 const& multilinestring2, |
| double tolerance) |
| { |
| typedef typename boost::range_iterator |
| < |
| MultiLinestring1 const |
| >::type ls1_iterator; |
| |
| typedef typename boost::range_iterator |
| < |
| MultiLinestring2 const |
| >::type ls2_iterator; |
| |
| typedef typename boost::range_value<MultiLinestring1>::type Linestring1; |
| |
| typedef typename boost::range_value<MultiLinestring2>::type Linestring2; |
| |
| typedef typename boost::range_iterator |
| < |
| Linestring1 const |
| >::type point1_iterator; |
| |
| typedef typename boost::range_iterator |
| < |
| Linestring2 const |
| >::type point2_iterator; |
| |
| typedef ls_less<Linestring1, Linestring2> linestring_less; |
| |
| typedef pt_equal |
| < |
| typename boost::range_value |
| < |
| typename boost::range_value<MultiLinestring1>::type |
| >::type, |
| typename boost::range_value |
| < |
| typename boost::range_value<MultiLinestring2>::type |
| >::type |
| > point_equal; |
| |
| |
| MultiLinestring1 mls1 = multilinestring1; |
| MultiLinestring2 mls2 = multilinestring2; |
| |
| std::sort(boost::begin(mls1), boost::end(mls1), linestring_less()); |
| std::sort(boost::begin(mls2), boost::end(mls2), linestring_less()); |
| |
| unique<MultiLinestring1, EnableUnique>()(mls1, tolerance); |
| unique<MultiLinestring2, EnableUnique>()(mls2, tolerance); |
| |
| if ( boost::size(mls1) != boost::size(mls2) ) |
| { |
| return false; |
| } |
| |
| ls1_iterator it1 = boost::begin(mls1); |
| ls2_iterator it2 = boost::begin(mls2); |
| for (; it1 != boost::end(mls1); ++it1, ++it2) |
| { |
| if ( boost::size(*it1) != boost::size(*it2) ) |
| { |
| return false; |
| } |
| point1_iterator pit1 = boost::begin(*it1); |
| point2_iterator pit2 = boost::begin(*it2); |
| for (; pit1 != boost::end(*it1); ++pit1, ++pit2) |
| { |
| if (! point_equal(tolerance)(*pit1, *pit2)) |
| { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| }; |
| |
| |
| |
| |
| class equals |
| { |
| private: |
| template <typename Linestring, typename OutputIterator> |
| static inline OutputIterator |
| isolated_point_to_segment(Linestring const& linestring, OutputIterator oit) |
| { |
| BOOST_ASSERT( boost::size(linestring) == 1 ); |
| |
| *oit++ = *boost::begin(linestring); |
| *oit++ = *boost::begin(linestring); |
| return oit; |
| } |
| |
| |
| template <typename MultiLinestring, typename OutputIterator> |
| static inline OutputIterator |
| convert_isolated_points_to_segments(MultiLinestring const& multilinestring, |
| OutputIterator oit) |
| { |
| BOOST_AUTO_TPL(it, boost::begin(multilinestring)); |
| |
| for (; it != boost::end(multilinestring); ++it) |
| { |
| if ( boost::size(*it) == 1 ) |
| { |
| typename boost::range_value<MultiLinestring>::type linestring; |
| isolated_point_to_segment(*it, std::back_inserter(linestring)); |
| *oit++ = linestring; |
| } |
| else |
| { |
| *oit++ = *it; |
| } |
| } |
| return oit; |
| } |
| |
| |
| template <typename MultiLinestring1, typename MultiLinestring2> |
| static inline bool apply_base(MultiLinestring1 const& multilinestring1, |
| MultiLinestring2 const& multilinestring2, |
| double tolerance) |
| { |
| typedef multilinestring_equals<true> mls_equals; |
| |
| if ( mls_equals::apply(multilinestring1, multilinestring2, tolerance) ) |
| { |
| return true; |
| } |
| |
| MultiLinestring1 reverse_multilinestring1 = multilinestring1; |
| bg::reverse(reverse_multilinestring1); |
| if ( mls_equals::apply(reverse_multilinestring1, |
| multilinestring2, |
| tolerance) ) |
| { |
| return true; |
| } |
| |
| MultiLinestring2 reverse_multilinestring2 = multilinestring2; |
| bg::reverse(reverse_multilinestring2); |
| if ( mls_equals::apply(multilinestring1, |
| reverse_multilinestring2, |
| tolerance) ) |
| { |
| return true; |
| } |
| |
| return mls_equals::apply(reverse_multilinestring1, |
| reverse_multilinestring2, |
| tolerance); |
| } |
| |
| |
| |
| public: |
| template <typename MultiLinestring1, typename MultiLinestring2> |
| static inline bool apply(MultiLinestring1 const& multilinestring1, |
| MultiLinestring2 const& multilinestring2, |
| double tolerance) |
| { |
| #ifndef BOOST_GEOMETRY_ALLOW_ONE_POINT_LINESTRINGS |
| MultiLinestring1 converted_multilinestring1; |
| convert_isolated_points_to_segments |
| (multilinestring1, std::back_inserter(converted_multilinestring1)); |
| MultiLinestring2 converted_multilinestring2; |
| convert_isolated_points_to_segments |
| (multilinestring2, std::back_inserter(converted_multilinestring2)); |
| return apply_base(converted_multilinestring1, |
| converted_multilinestring2, tolerance); |
| #else |
| return apply_base(multilinestring1, multilinestring2, tolerance); |
| #endif |
| } |
| }; |
| |
| |
| |
| |
| template <typename Output, typename G1, typename G2> |
| void set_operation_output(std::string const& set_op_id, |
| std::string const& caseid, |
| G1 const& g1, G2 const& g2, |
| Output const& output) |
| { |
| boost::ignore_unused(set_op_id, caseid, g1, g2, output); |
| |
| #if defined(TEST_WITH_SVG) |
| typedef typename bg::coordinate_type<G1>::type coordinate_type; |
| typedef typename bg::point_type<G1>::type point_type; |
| |
| std::ostringstream filename; |
| filename << "svgs/" << set_op_id << "_" << caseid << ".svg"; |
| |
| std::ofstream svg(filename.str().c_str()); |
| |
| bg::svg_mapper<point_type> mapper(svg, 500, 500); |
| |
| mapper.add(g1); |
| mapper.add(g2); |
| |
| mapper.map(g2, "stroke-opacity:1;stroke:rgb(153,204,0);stroke-width:4"); |
| mapper.map(g1, "stroke-opacity:1;stroke:rgb(51,51,153);stroke-width:2"); |
| |
| BOOST_AUTO_TPL(it, output.begin()); |
| for (; it != output.end(); ++it) |
| { |
| if ( boost::size(*it) == 2 |
| && bg::equals(*boost::begin(*it), *++boost::begin(*it)) ) |
| { |
| // draw isolated points (generated by the intersection operation) |
| mapper.map(*boost::begin(*it), |
| "fill:rgb(255,0,255);stroke:rgb(0,0,0);stroke-width:1", |
| 4); |
| } |
| else |
| { |
| mapper.map(*it, |
| "stroke-opacity:0.4;stroke:rgb(255,0,255);stroke-width:8"); |
| } |
| } |
| #endif |
| } |
| |
| |
| #endif // BOOST_GEOMETRY_TEST_SET_OPS_LINEAR_LINEAR_HPP |