1 // Boost.Geometry (aka GGL, Generic Geometry Library)
4 // Copyright (c) 2009-2015 Barend Gehrels, Amsterdam, the Netherlands.
5 // Use, modification and distribution is subject to the Boost Software License,
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
9 #ifndef BOOST_GEOMETRY_TEST_OVERLAY_P_Q_HPP
10 #define BOOST_GEOMETRY_TEST_OVERLAY_P_Q_HPP
17 #include <boost/typeof/typeof.hpp>
19 //#define BOOST_GEOMETRY_ROBUSTNESS_USE_DIFFERENCE
21 #include <geometry_test_common.hpp>
23 // For mixing int/float
25 #pragma warning( disable : 4244 )
26 #pragma warning( disable : 4267 )
30 #include <boost/geometry.hpp>
31 #include <boost/geometry/geometries/geometries.hpp>
32 #include <boost/geometry/geometries/point_xy.hpp>
33 #include <boost/geometry/io/svg/svg_mapper.hpp>
35 #include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
36 #include <boost/geometry/algorithms/intersects.hpp>
37 #include <boost/geometry/algorithms/is_valid.hpp>
38 #include <boost/geometry/algorithms/touches.hpp>
51 , also_difference(false)
55 , tolerance(1.0e-3) // since rescaling to integer the tolerance should be less. Was originally 1.0e-6
59 template <typename Geometry>
60 inline typename bg::default_area_result<Geometry>::type p_q_area(Geometry const& g)
66 catch(bg::empty_input_exception const&)
74 template <typename Iterator>
75 static inline bool check_ring(Iterator begin, Iterator end)
77 for (Iterator it = begin; it != end; ++it)
79 double const area = bg::area(*it);
80 if (fabs(area) < 0.01)
88 template <typename Interiors>
89 static inline bool check_rings(Interiors const& rings)
91 return check_ring(boost::begin(rings), boost::end(rings));
94 template <typename Iterator>
95 static inline bool check_polys(Iterator begin, Iterator end)
97 for (Iterator it = begin; it != end; ++it)
99 // If necessary, exterior_ring can be checked too
100 if (! check_rings(bg::interior_rings(*it)))
108 template <typename Geometry>
109 static inline bool apply(Geometry const& g)
111 return check_polys(boost::begin(g), boost::end(g));
115 template <typename OutputType, typename CalculationType, typename G1, typename G2>
116 static bool test_overlay_p_q(std::string const& caseid,
117 G1 const& p, G2 const& q,
118 p_q_settings const& settings)
122 typedef typename bg::coordinate_type<G1>::type coordinate_type;
123 typedef typename bg::point_type<G1>::type point_type;
125 bg::model::multi_polygon<OutputType> out_i, out_u, out_d1, out_d2;
127 CalculationType area_p = p_q_area(p);
128 CalculationType area_q = p_q_area(q);
129 CalculationType area_d1 = 0, area_d2 = 0;
131 bg::intersection(p, q, out_i);
132 CalculationType area_i = p_q_area(out_i);
134 bg::union_(p, q, out_u);
135 CalculationType area_u = p_q_area(out_u);
137 double sum = (area_p + area_q) - area_u - area_i;
139 bool wrong = std::abs(sum) > settings.tolerance;
141 if (settings.also_difference)
143 bg::difference(p, q, out_d1);
144 bg::difference(q, p, out_d2);
145 area_d1 = p_q_area(out_d1);
146 area_d2 = p_q_area(out_d2);
147 double sum_d1 = (area_u - area_q) - area_d1;
148 double sum_d2 = (area_u - area_p) - area_d2;
149 bool wrong_d1 = std::abs(sum_d1) > settings.tolerance;
150 bool wrong_d2 = std::abs(sum_d2) > settings.tolerance;
152 if (wrong_d1 || wrong_d2)
158 if (settings.validity)
161 if (! bg::is_valid(out_u, message))
163 std::cout << "Union is not valid: " << message << std::endl;
166 if (! bg::is_valid(out_i, message))
168 std::cout << "Intersection is not valid: " << message << std::endl;
171 if (settings.also_difference)
173 if (! bg::is_valid(out_d1, message))
175 std::cout << "Difference (p-q) is not valid: " << message << std::endl;
178 if (! bg::is_valid(out_d2, message))
180 std::cout << "Difference (q-p) is not valid: " << message << std::endl;
185 if (settings.verify_area && ! verify_area::apply(out_u))
187 std::cout << "Union/interior area incorrect" << std::endl;
190 if (settings.verify_area && ! verify_area::apply(out_i))
192 std::cout << "Intersection/interior area incorrect" << std::endl;
199 if ((area_i > 0 && bg::touches(p, q))
200 || (area_i <= 0 && bg::intersects(p, q) && ! bg::touches(p, q)))
202 std::cout << "Wrong 'touch'! "
203 << " Intersection area: " << area_i
204 << " Touch gives: " << std::boolalpha << bg::touches(p, q)
210 bool svg = settings.svg;
212 if (wrong || settings.wkt)
223 << "type: " << string_from_type<CalculationType>::name()
225 << " area i: " << area_i
226 << " area u: " << area_u
227 << " area p: " << area_p
228 << " area q: " << area_q
231 if (settings.also_difference)
234 << " area d1: " << area_d1
235 << " area d2: " << area_d2;
239 << std::setprecision(9)
240 << " p: " << bg::wkt(p) << std::endl
241 << " q: " << bg::wkt(q) << std::endl
242 << " i: " << bg::wkt(out_i) << std::endl
243 << " u: " << bg::wkt(out_u) << std::endl
250 std::ostringstream filename;
251 filename << "overlay_" << caseid << "_"
252 << string_from_type<coordinate_type>::name()
253 << string_from_type<CalculationType>::name()
256 std::ofstream svg(filename.str().c_str());
258 bg::svg_mapper<point_type> mapper(svg, 500, 500);
263 // Input shapes in green/blue
264 mapper.map(p, "fill-opacity:0.5;fill:rgb(153,204,0);"
265 "stroke:rgb(153,204,0);stroke-width:3");
266 mapper.map(q, "fill-opacity:0.3;fill:rgb(51,51,153);"
267 "stroke:rgb(51,51,153);stroke-width:3");
269 if (settings.also_difference)
271 for (BOOST_AUTO(it, out_d1.begin()); it != out_d1.end(); ++it)
274 "opacity:0.8;fill:none;stroke:rgb(255,128,0);stroke-width:4;stroke-dasharray:1,7;stroke-linecap:round");
276 for (BOOST_AUTO(it, out_d2.begin()); it != out_d2.end(); ++it)
279 "opacity:0.8;fill:none;stroke:rgb(255,0,255);stroke-width:4;stroke-dasharray:1,7;stroke-linecap:round");
284 mapper.map(out_i, "fill-opacity:0.1;stroke-opacity:0.4;fill:rgb(255,0,128);"
285 "stroke:rgb(255,0,0);stroke-width:4");
286 mapper.map(out_u, "fill-opacity:0.1;stroke-opacity:0.4;fill:rgb(255,0,0);"
287 "stroke:rgb(255,0,255);stroke-width:4");
293 #endif // BOOST_GEOMETRY_TEST_OVERLAY_P_Q_HPP