1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 // Unit Test
3 
4 // Copyright (c) 2007-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)
8 
9 #ifndef BOOST_GEOMETRY_TEST_DIFFERENCE_HPP
10 #define BOOST_GEOMETRY_TEST_DIFFERENCE_HPP
11 
12 #include <fstream>
13 #include <iomanip>
14 
15 #include <geometry_test_common.hpp>
16 
17 #include <boost/core/ignore_unused.hpp>
18 #include <boost/foreach.hpp>
19 
20 #include <boost/range/algorithm/copy.hpp>
21 
22 #include <boost/geometry/algorithms/correct.hpp>
23 #include <boost/geometry/algorithms/difference.hpp>
24 #include <boost/geometry/algorithms/sym_difference.hpp>
25 
26 #include <boost/geometry/algorithms/area.hpp>
27 #include <boost/geometry/algorithms/length.hpp>
28 #include <boost/geometry/algorithms/num_points.hpp>
29 
30 #include <boost/geometry/geometries/geometries.hpp>
31 
32 
33 #include <boost/geometry/geometries/multi_point.hpp>
34 #include <boost/geometry/geometries/multi_linestring.hpp>
35 #include <boost/geometry/geometries/multi_polygon.hpp>
36 
37 #include <boost/geometry/strategies/strategies.hpp>
38 
39 #include <boost/geometry/io/wkt/wkt.hpp>
40 
41 
42 #if defined(TEST_WITH_SVG)
43 #  define BOOST_GEOMETRY_DEBUG_SEGMENT_IDENTIFIER
44 #  define BOOST_GEOMETRY_DEBUG_IDENTIFIER
45 #  include <boost/geometry/io/svg/svg_mapper.hpp>
46 #  include <boost/geometry/algorithms/detail/overlay/debug_turn_info.hpp>
47 #endif
48 
49 
50 template <typename Output, typename G1, typename G2>
difference_output(std::string const & caseid,G1 const & g1,G2 const & g2,Output const & output)51 void difference_output(std::string const& caseid, G1 const& g1, G2 const& g2, Output const& output)
52 {
53     boost::ignore_unused(caseid, g1, g2, output);
54 
55 #if defined(TEST_WITH_SVG)
56     {
57         typedef typename bg::coordinate_type<G1>::type coordinate_type;
58         typedef typename bg::point_type<G1>::type point_type;
59 
60         std::ostringstream filename;
61         filename << "difference_"
62             << caseid << "_"
63             << string_from_type<coordinate_type>::name()
64 #if defined(BOOST_GEOMETRY_NO_ROBUSTNESS)
65             << "_no_rob"
66 #endif
67             << ".svg";
68 
69         std::ofstream svg(filename.str().c_str());
70 
71         bg::svg_mapper<point_type> mapper(svg, 500, 500);
72 
73         mapper.add(g1);
74         mapper.add(g2);
75 
76         mapper.map(g1, "fill-opacity:0.3;fill:rgb(51,51,153);stroke:rgb(51,51,153);stroke-width:3");
77         mapper.map(g2, "fill-opacity:0.5;fill:rgb(153,204,0);stroke:rgb(153,204,0);stroke-width:3");
78 
79 
80         for (typename Output::const_iterator it = output.begin(); it != output.end(); ++it)
81         {
82             mapper.map(*it,
83                 //sym ? "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,255,0);stroke:rgb(255,0,255);stroke-width:8" :
84                 "fill-opacity:0.2;stroke-opacity:0.4;fill:rgb(255,0,0);stroke:rgb(255,0,255);stroke-width:8");
85         }
86     }
87 #endif
88 }
89 
90 template <typename OutputType, typename G1, typename G2>
test_difference(std::string const & caseid,G1 const & g1,G2 const & g2,int expected_count,int expected_point_count,double expected_area,double percentage=0.0001,bool sym=false)91 void test_difference(std::string const& caseid, G1 const& g1, G2 const& g2,
92         int expected_count, int expected_point_count,
93         double expected_area,
94         double percentage = 0.0001,
95         bool sym = false)
96 {
97     typedef typename bg::coordinate_type<G1>::type coordinate_type;
98     boost::ignore_unused<coordinate_type>();
99 
100     std::vector<OutputType> clip;
101 
102     if (sym)
103     {
104         bg::sym_difference(g1, g2, clip);
105     }
106     else
107     {
108         bg::difference(g1, g2, clip);
109     }
110 
111     typename bg::default_area_result<G1>::type area = 0;
112     std::size_t n = 0;
113     for (typename std::vector<OutputType>::iterator it = clip.begin();
114             it != clip.end();
115             ++it)
116     {
117         if (expected_point_count >= 0)
118         {
119             n += bg::num_points(*it);
120         }
121 
122         area += bg::area(*it);
123     }
124 
125     difference_output(caseid, g1, g2, clip);
126 
127 #ifndef BOOST_GEOMETRY_DEBUG_ASSEMBLE
128     {
129         // Test inserter functionality
130         // Test if inserter returns output-iterator (using Boost.Range copy)
131         typedef typename bg::point_type<G1>::type point_type;
132         typedef typename bg::rescale_policy_type<point_type>::type
133             rescale_policy_type;
134 
135         rescale_policy_type rescale_policy
136                 = bg::get_rescale_policy<rescale_policy_type>(g1, g2);
137 
138         std::vector<OutputType> inserted, array_with_one_empty_geometry;
139         array_with_one_empty_geometry.push_back(OutputType());
140         if (sym)
141         {
142             boost::copy(array_with_one_empty_geometry,
143                 bg::detail::sym_difference::sym_difference_insert<OutputType>
144                     (g1, g2, rescale_policy, std::back_inserter(inserted)));
145         }
146         else
147         {
148             boost::copy(array_with_one_empty_geometry,
149                 bg::detail::difference::difference_insert<OutputType>(
150                     g1, g2, rescale_policy, std::back_inserter(inserted)));
151         }
152 
153         BOOST_CHECK_EQUAL(boost::size(clip), boost::size(inserted) - 1);
154     }
155 #endif
156 
157 
158 
159 #if ! defined(BOOST_GEOMETRY_NO_BOOST_TEST)
160     if (expected_point_count >= 0)
161     {
162         BOOST_CHECK_MESSAGE(bg::math::abs(int(n) - expected_point_count) < 3,
163                 "difference: " << caseid
164                 << " #points expected: " << expected_point_count
165                 << " detected: " << n
166                 << " type: " << (type_for_assert_message<G1, G2>())
167                 );
168     }
169 
170     if (expected_count >= 0)
171     {
172         BOOST_CHECK_MESSAGE(int(clip.size()) == expected_count,
173                 "difference: " << caseid
174                 << " #outputs expected: " << expected_count
175                 << " detected: " << clip.size()
176                 << " type: " << (type_for_assert_message<G1, G2>())
177                 );
178     }
179 
180     BOOST_CHECK_CLOSE(area, expected_area, percentage);
181 #endif
182 
183 
184 }
185 
186 
187 #ifdef BOOST_GEOMETRY_CHECK_WITH_POSTGIS
188 static int counter = 0;
189 #endif
190 
191 
192 template <typename OutputType, typename G1, typename G2>
test_one(std::string const & caseid,std::string const & wkt1,std::string const & wkt2,int expected_count1,int expected_point_count1,double expected_area1,int expected_count2,int expected_point_count2,double expected_area2,int expected_count_s,int expected_point_count_s,double expected_area_s,double percentage=0.0001)193 void test_one(std::string const& caseid,
194         std::string const& wkt1, std::string const& wkt2,
195         int expected_count1,
196         int expected_point_count1,
197         double expected_area1,
198         int expected_count2,
199         int expected_point_count2,
200         double expected_area2,
201         int expected_count_s,
202         int expected_point_count_s,
203         double expected_area_s,
204         double percentage = 0.0001)
205 {
206 #ifdef BOOST_GEOMETRY_CHECK_WITH_SQLSERVER
207     std::cout
208         << "-- " << caseid << std::endl
209         << "with qu as (" << std::endl
210         << "select geometry::STGeomFromText('" << wkt1 << "',0) as p," << std::endl
211         << "geometry::STGeomFromText('" << wkt2 << "',0) as q)" << std::endl
212         << "select " << std::endl
213         << " p.STDifference(q).STNumGeometries() as cnt1,p.STDifference(q).STNumPoints() as pcnt1,p.STDifference(q).STArea() as area1," << std::endl
214         << " q.STDifference(p).STNumGeometries() as cnt2,q.STDifference(p).STNumPoints() as pcnt2,q.STDifference(p).STArea() as area2," << std::endl
215         << " p.STDifference(q) as d1,q.STDifference(p) as d2 from qu" << std::endl << std::endl;
216 #endif
217 
218 
219     G1 g1;
220     bg::read_wkt(wkt1, g1);
221 
222     G2 g2;
223     bg::read_wkt(wkt2, g2);
224 
225     bg::correct(g1);
226     bg::correct(g2);
227 
228     test_difference<OutputType>(caseid + "_a", g1, g2,
229         expected_count1, expected_point_count1,
230         expected_area1, percentage);
231 #ifdef BOOST_GEOMETRY_DEBUG_ASSEMBLE
232     return;
233 #endif
234     test_difference<OutputType>(caseid + "_b", g2, g1,
235         expected_count2, expected_point_count2,
236         expected_area2, percentage);
237     test_difference<OutputType>(caseid + "_s", g1, g2,
238         expected_count_s,
239         expected_point_count_s,
240         expected_area_s,
241         percentage, true);
242 
243 
244 #ifdef BOOST_GEOMETRY_CHECK_WITH_POSTGIS
245     std::cout
246         << (counter > 0 ? "union " : "")
247         << "select " << counter++
248         << ", '" << caseid << "' as caseid"
249         << ", ST_NumPoints(ST_Difference(ST_GeomFromText('" << wkt1 << "'), "
250         << "      ST_GeomFromText('" << wkt2 << "'))) "
251         << ", ST_NumGeometries(ST_Difference(ST_GeomFromText('" << wkt1 << "'), "
252         << "      ST_GeomFromText('" << wkt2 << "'))) "
253         << ", ST_Area(ST_Difference(ST_GeomFromText('" << wkt1 << "'), "
254         << "      ST_GeomFromText('" << wkt2 << "'))) "
255         //<< ", " << expected_area1 << " as expected_area_a"
256         //<< ", " << expected_count1 << " as expected_count_a"
257         << ", ST_NumPoints(ST_Difference(ST_GeomFromText('" << wkt2 << "'), "
258         << "      ST_GeomFromText('" << wkt1 << "'))) "
259         << ", ST_NumGeometries(ST_Difference(ST_GeomFromText('" << wkt2 << "'), "
260         << "      ST_GeomFromText('" << wkt1 << "'))) "
261         << ", ST_Area(ST_Difference(ST_GeomFromText('" << wkt2 << "'), "
262         << "      ST_GeomFromText('" << wkt1 << "'))) "
263         //<< ", " << expected_area2 << " as expected_area_b"
264         //<< ", " << expected_count2 << " as expected_count_b"
265         << ", ST_NumPoints(ST_SymDifference(ST_GeomFromText('" << wkt1 << "'), "
266         << "      ST_GeomFromText('" << wkt2 << "'))) "
267         << ", ST_NumGeometries(ST_SymDifference(ST_GeomFromText('" << wkt1 << "'), "
268         << "      ST_GeomFromText('" << wkt2 << "'))) "
269         << ", ST_Area(ST_SymDifference(ST_GeomFromText('" << wkt1 << "'), "
270         << "      ST_GeomFromText('" << wkt2 << "'))) "
271         //<< ", " << expected_area1 + expected_area2 << " as expected_area_s"
272         //<< ", " << expected_count1 + expected_count2 << " as expected_count_s"
273         << std::endl;
274 #endif
275 
276 }
277 
278 template <typename OutputType, typename G1, typename G2>
test_one(std::string const & caseid,std::string const & wkt1,std::string const & wkt2,int expected_count1,int expected_point_count1,double expected_area1,int expected_count2,int expected_point_count2,double expected_area2,double percentage=0.0001)279 void test_one(std::string const& caseid,
280         std::string const& wkt1, std::string const& wkt2,
281         int expected_count1,
282         int expected_point_count1,
283         double expected_area1,
284         int expected_count2,
285         int expected_point_count2,
286         double expected_area2,
287         double percentage = 0.0001)
288 {
289     test_one<OutputType, G1, G2>(caseid, wkt1, wkt2,
290         expected_count1, expected_point_count1, expected_area1,
291         expected_count2, expected_point_count2, expected_area2,
292         expected_count1 + expected_count2,
293         expected_point_count1 >= 0 && expected_point_count2 >= 0
294             ? (expected_point_count1 + expected_point_count2) : -1,
295         expected_area1 + expected_area2,
296         percentage);
297 }
298 
299 template <typename OutputType, typename G1, typename G2>
test_one_lp(std::string const & caseid,std::string const & wkt1,std::string const & wkt2,std::size_t expected_count,int expected_point_count,double expected_length)300 void test_one_lp(std::string const& caseid,
301         std::string const& wkt1, std::string const& wkt2,
302         std::size_t expected_count,
303         int expected_point_count,
304         double expected_length)
305 {
306     G1 g1;
307     bg::read_wkt(wkt1, g1);
308 
309     G2 g2;
310     bg::read_wkt(wkt2, g2);
311 
312     bg::correct(g1);
313 
314     std::vector<OutputType> pieces;
315     bg::difference(g1, g2, pieces);
316 
317     typename bg::default_length_result<G1>::type length = 0;
318     std::size_t n = 0;
319     std::size_t piece_count = 0;
320     for (typename std::vector<OutputType>::iterator it = pieces.begin();
321             it != pieces.end();
322             ++it)
323     {
324         if (expected_point_count >= 0)
325         {
326             n += bg::num_points(*it);
327         }
328         piece_count++;
329         length += bg::length(*it);
330     }
331 
332     BOOST_CHECK_MESSAGE(piece_count == expected_count,
333             "difference: " << caseid
334             << " #outputs expected: " << expected_count
335             << " detected: " << pieces.size()
336             );
337 
338     if (expected_point_count >= 0)
339     {
340         BOOST_CHECK_EQUAL(n, std::size_t(expected_point_count));
341     }
342 
343     BOOST_CHECK_CLOSE(length, expected_length, 0.001);
344 
345     std::string lp = "lp_";
346     difference_output(lp + caseid, g1, g2, pieces);
347 }
348 
349 
350 
351 #endif
352