• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
2 // Unit Test
3 
4 // Copyright (c) 2020 Barend Gehrels, Amsterdam, the Netherlands.
5 
6 // Use, modification and distribution is subject to the Boost Software License,
7 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
8 // htt//www.boost.org/LICENSE_1_0.txt)
9 
10 #include "geometry_test_common.hpp"
11 
12 #include <boost/geometry/algorithms/area.hpp>
13 #include <boost/geometry/algorithms/detail/overlay/turn_info.hpp>
14 #include <boost/geometry/algorithms/detail/buffer/piece_border.hpp>
15 #include <boost/geometry/util/range.hpp>
16 
17 #include <boost/geometry/io/wkt/wkt.hpp>
18 #include <boost/geometry/geometries/geometries.hpp>
19 
20 #include <boost/geometry/strategies/comparable_distance_result.hpp>
21 #include <boost/geometry/strategies/cartesian/distance_pythagoras.hpp>
22 #include <boost/geometry/strategies/cartesian/distance_projected_point.hpp>
23 
24 #if defined(TEST_WITH_SVG)
25 #include <boost/geometry/io/svg/svg_mapper.hpp>
26 #include <boost/geometry/algorithms/detail/buffer/buffer_box.hpp>
27 #endif
28 
29 static const std::string rectangle_offsetted = "POLYGON((0 0,0 1,0 2,0 3,1 3,2 3,3 3,3 2,3 1,3 0,2 0,1 0,0 0))";
30 static const std::string rectangle_original = "POLYGON((1 1,1 2,2 2,2 1,1 1))";
31 
32 static const std::string diamond_offsetted = "POLYGON((3 0,2 1,1 2,0 3,1 4,2 5,3 6,4 5,5 4,6 3,5 2,4 1,3 0))";
33 static const std::string diamond_original = "POLYGON((3 2,2 3,3 4,4 3,3 2))";
34 
35 template <typename Geometry>
as_wkt(Geometry const & geometry)36 inline std::string as_wkt(Geometry const& geometry)
37 {
38     std::ostringstream out;
39     out << bg::wkt(geometry);
40     return out.str();
41 }
42 
43 // Return the border. It needs to pass the ring because its address
44 // is preserved in the border. It also gets the original.
45 template <typename Border, typename Ring>
setup_piece_border(Ring & ring,Ring & original,std::string const & offsetted_wkt,std::string const & original_wkt,char piece)46 Border setup_piece_border(Ring& ring, Ring& original,
47                           std::string const& offsetted_wkt,
48                           std::string const& original_wkt,
49                           char piece)
50 {
51     // The test setup (here fore case "rectangle"; "diamond" has same layout).
52     // Points on offsetted ring are numbered, points on original like this [0].
53     // a, b, c: test pieces.
54     //
55     //           4       5
56     //   3 +-----+-------+-----+ 6
57     //     |     |       |     |
58     //     |     |   a   |  b  |
59     //     |     |       |     |
60     //   2 +----[1]-----[2]----+ 7
61     //     |     |       |     |
62     //     |     |  ori  |  c  |
63     //     |     |       |     |
64     //   1 +----[0,4]---[3]----+ 8
65     //     |     |       |     |
66     //     |     |       |     |
67     //     |     |       |     |
68     //   0 +-----+-------+-----+ 9
69     //    12    11       10
70 
71 
72     bg::read_wkt(original_wkt, original);
73     bg::read_wkt(offsetted_wkt, ring);
74 
75     Border border;
76 
77     switch(piece)
78     {
79         case 'a' :
80             border.set_offsetted(ring, 4, 6);
81             border.add_original_point(original[2]);
82             border.add_original_point(original[1]);
83             break;
84         case 'b' :
85             border.set_offsetted(ring, 5, 8);
86             border.add_original_point(original[2]);
87             break;
88         case 'c' :
89             border.set_offsetted(ring, 7, 9);
90             border.add_original_point(original[3]);
91             border.add_original_point(original[2]);
92             break;
93         default :
94             return border;
95     }
96 
97     typedef typename bg::point_type<Ring>::type point_type;
98 
99     border.get_properties_of_border(false, point_type());
100 
101     bg::strategy::side::side_by_triangle<> side_strategy;
102     border.get_properties_of_offsetted_ring_part(side_strategy);
103 
104     return border;
105 }
106 
107 template <typename Point>
test_rectangle_properties()108 void test_rectangle_properties()
109 {
110     typedef bg::model::ring<Point> ring_type;
111     typedef bg::detail::buffer::piece_border<ring_type, Point> border_type;
112 
113     border_type empty;
114     BOOST_CHECK_MESSAGE(empty.ring_or_original_empty(), "piece should be empty");
115 
116     ring_type offsetted, original;
117     border_type border = setup_piece_border<border_type>(offsetted, original,
118             rectangle_offsetted, rectangle_original, 'a');
119 
120     // Check get_full_ring functionality (used for testing, writing SVG)
121     ring_type const outline = border.get_full_ring();
122     std::string const outline_wkt = as_wkt(outline);
123 
124     std::string const expected = "POLYGON((1 3,2 3,2 2,1 2,1 3))";
125     BOOST_CHECK_MESSAGE(outline_wkt == expected,
126                         "expected: " << expected
127                         << " detected: " << outline_wkt);
128 
129     BOOST_CHECK_MESSAGE(! border.ring_or_original_empty(),
130                         "piece should not be empty");
131 
132     // Check border-properties functionality (envelope, radius)
133     double const area = bg::area(border.m_envelope);
134     BOOST_CHECK_MESSAGE(area > 1.0 && area < 1.01,
135                         "detected: " << area);
136 
137     // Check offsetted-ring-properties functionality (convexity, monotonicity)
138     BOOST_CHECK_MESSAGE(border.m_is_convex == true,
139                         "piece should be convex");
140     BOOST_CHECK_MESSAGE(border.m_is_monotonic_increasing == true,
141                         "piece should be monotonically increasing");
142     BOOST_CHECK_MESSAGE(border.m_is_monotonic_decreasing == false,
143                         "piece NOT should be monotonically decreasing");
144 }
145 
146 template <typename Point, typename Border, typename Mapper>
test_point(std::string const & wkt,bool expected,Border const & border,Mapper & mapper,std::string const & color)147 void test_point(std::string const& wkt, bool expected, Border const& border,
148                 Mapper& mapper, std::string const& color)
149 {
150     typename Border::state_type state;
151     Point point;
152     bg::read_wkt(wkt, point);
153     border.point_on_piece(point, false, false, state);
154     bool const on_piece = state.code() == 1;
155     BOOST_CHECK_MESSAGE(on_piece == expected,
156                         " expected: " << expected
157                         << " detected: " << on_piece
158                         << " wkt: " << wkt);
159 
160 #ifdef TEST_WITH_SVG
161     std::string style = "fill:" + color + ";stroke:rgb(0,0,0);stroke-width:1";
162     mapper.map(point, style);
163 
164     // Mark on-piece as T or F
165     std::ostringstream out;
166     out << (on_piece ? "T" : "F");
167     mapper.text(point, out.str(), "fill:rgb(0,0,0);font-family='Arial';font-size:9px;", 10, -10);
168 #else
169     boost::ignore_unused(mapper, color);
170 #endif
171 }
172 
173 #ifdef TEST_WITH_SVG
174 template <typename Mapper, typename Ring, typename Border>
start_svg(Mapper & mapper,Ring const & original,Ring const & offsetted,Border const & border)175 void start_svg(Mapper& mapper, Ring const& original, Ring const& offsetted,
176                Border const& border)
177 {
178     typedef typename bg::point_type<Ring>::type point_type;
179     typedef bg::model::box<point_type> box_type;
180     box_type box = border.m_envelope;
181     bg::detail::buffer::buffer_box(box, 1.0, box);
182     mapper.add(box);
183     mapper.add(offsetted);
184     mapper.map(offsetted,
185                "fill-opacity:0.5;fill:rgb(255,255,128);stroke:none");
186     mapper.map(original,
187                "fill-opacity:0.5;fill:rgb(51,51,153);stroke:none");
188     mapper.map(border.get_full_ring(),
189                "fill-opacity:0.5;fill:rgb(153,204,0);"
190                "stroke:rgb(153,204,0);stroke-width:1");
191 }
192 #endif
193 
194 template <typename Point>
test_rectangle_point_on_piece_a()195 void test_rectangle_point_on_piece_a()
196 {
197     typedef bg::model::ring<Point> ring_type;
198     typedef bg::detail::buffer::piece_border<ring_type, Point> border_type;
199 
200     ring_type offsetted, original;
201     border_type border = setup_piece_border<border_type>(offsetted, original,
202             rectangle_offsetted, rectangle_original, 'a');
203 
204 #ifdef TEST_WITH_SVG
205     std::ofstream svg("/tmp/border_rectangle_a.svg");
206     bg::svg_mapper<Point> mapper(svg, 500, 500);
207     start_svg(mapper, original, offsetted, border);
208 #else
209     int mapper = 0;
210 #endif
211 
212     // Points inside
213     test_point<Point>("POINT(1.5 2.5)", true, border, mapper, "red");
214 
215     // Points outside
216     test_point<Point>("POINT(0.5 2.5)", false, border, mapper, "green");
217     test_point<Point>("POINT(2.5 2.5)", false, border, mapper, "green");
218     test_point<Point>("POINT(1.5 1.5)", false, border, mapper, "green");
219     test_point<Point>("POINT(1.5 3.5)", false, border, mapper, "green");
220 
221     // Points on the original (should be INSIDE)
222     test_point<Point>("POINT(1.0 2.0)", true, border, mapper, "orange");
223     test_point<Point>("POINT(1.5 2.0)", true, border, mapper, "orange");
224     test_point<Point>("POINT(2.0 2.0)", true, border, mapper, "orange");
225 
226     // Points on the offsetted ring (should be OUTSIDE)
227     test_point<Point>("POINT(1.0 3.0)", false, border, mapper, "blue");
228     test_point<Point>("POINT(1.5 3.0)", false, border, mapper, "blue");
229     test_point<Point>("POINT(2.0 3.0)", false, border, mapper, "blue");
230 
231     // Points on between original and offsetted ring (should be INSIDE)
232     test_point<Point>("POINT(1.0 2.5)", true, border, mapper, "cyan");
233     test_point<Point>("POINT(2.0 2.5)", true, border, mapper, "cyan");
234 }
235 
236 template <typename Point>
test_rectangle_point_on_piece_c()237 void test_rectangle_point_on_piece_c()
238 {
239     typedef bg::model::ring<Point> ring_type;
240     typedef bg::detail::buffer::piece_border<ring_type, Point> border_type;
241 
242     ring_type offsetted, original;
243     border_type border = setup_piece_border<border_type>(offsetted, original,
244             rectangle_offsetted, rectangle_original, 'c');
245 
246 #ifdef TEST_WITH_SVG
247     std::ofstream svg("/tmp/border_rectangle_c.svg");
248     bg::svg_mapper<Point> mapper(svg, 500, 500);
249     start_svg(mapper, original, offsetted, border);
250 #else
251     int mapper = 0;
252 #endif
253 
254     // Piece labeled as 'c' : from (2,1) to (3,2)
255 
256     // Points inside
257     test_point<Point>("POINT(2.5 1.5)", true, border, mapper, "red");
258 
259     // Points outside
260     test_point<Point>("POINT(1.5 1.5)", false, border, mapper, "green");
261     test_point<Point>("POINT(3.5 1.5)", false, border, mapper, "green");
262     test_point<Point>("POINT(2.5 0.5)", false, border, mapper, "green");
263     test_point<Point>("POINT(2.5 2.5)", false, border, mapper, "green");
264 
265     // Points on the original (should be INSIDE)
266     test_point<Point>("POINT(2.0 1.0)", true, border, mapper, "orange");
267     test_point<Point>("POINT(2.0 1.5)", true, border, mapper, "orange");
268     test_point<Point>("POINT(2.0 2.0)", true, border, mapper, "orange");
269 
270     // Points on the offsetted ring (should be OUTSIDE)
271     test_point<Point>("POINT(3.0 1.0)", false, border, mapper, "blue");
272     test_point<Point>("POINT(3.0 1.5)", false, border, mapper, "blue");
273     test_point<Point>("POINT(3.0 2.0)", false, border, mapper, "blue");
274 
275     // Points on between original and offsetted ring (should be INSIDE)
276     test_point<Point>("POINT(2.5 2.0)", true, border, mapper, "cyan");
277     test_point<Point>("POINT(2.5 1.0)", true, border, mapper, "cyan");
278 }
279 
280 template <typename Point>
test_diamond_point_on_piece_a()281 void test_diamond_point_on_piece_a()
282 {
283     typedef bg::model::ring<Point> ring_type;
284     typedef bg::detail::buffer::piece_border<ring_type, Point> border_type;
285 
286     ring_type offsetted, original;
287     border_type border = setup_piece_border<border_type>(offsetted, original,
288             diamond_offsetted, diamond_original, 'a');
289 
290 #ifdef TEST_WITH_SVG
291     std::ofstream svg("/tmp/border_diamond_a.svg");
292     bg::svg_mapper<Point> mapper(svg, 500, 500);
293     start_svg(mapper, original, offsetted, border);
294 #else
295     int mapper = 0;
296 #endif
297 
298     // Points inside
299     test_point<Point>("POINT(2 4)", true, border, mapper, "red");
300 
301     // Points outside
302     test_point<Point>("POINT(1 3)", false, border, mapper, "green");
303     test_point<Point>("POINT(1 5)", false, border, mapper, "green");
304     test_point<Point>("POINT(3 3)", false, border, mapper, "green");
305     test_point<Point>("POINT(3 5)", false, border, mapper, "green");
306 
307     // Points on the original (should be INSIDE)
308     test_point<Point>("POINT(2 3)", true, border, mapper, "orange");
309     test_point<Point>("POINT(2.5 3.5)", true, border, mapper, "orange");
310     test_point<Point>("POINT(3 4)", true, border, mapper, "orange");
311 
312     // Points on the offsetted ring (should be OUTSIDE)
313     test_point<Point>("POINT(1 4)", false, border, mapper, "blue");
314     test_point<Point>("POINT(1.5 4.5)", false, border, mapper, "blue");
315     test_point<Point>("POINT(2 5)", false, border, mapper, "blue");
316 
317     // Points on between original and offsetted ring (should be INSIDE)
318     test_point<Point>("POINT(1.5 3.5)", true, border, mapper, "cyan");
319     test_point<Point>("POINT(2.5 4.5)", true, border, mapper, "cyan");
320 }
321 
322 template <typename Point>
test_diamond_point_on_piece_c()323 void test_diamond_point_on_piece_c()
324 {
325     typedef bg::model::ring<Point> ring_type;
326     typedef bg::detail::buffer::piece_border<ring_type, Point> border_type;
327 
328     ring_type offsetted, original;
329     border_type border = setup_piece_border<border_type>(offsetted, original,
330             diamond_offsetted, diamond_original, 'c');
331 
332 #ifdef TEST_WITH_SVG
333     std::ofstream svg("/tmp/border_diamond_c.svg");
334     bg::svg_mapper<Point> mapper(svg, 500, 500);
335     start_svg(mapper, original, offsetted, border);
336 #else
337     int mapper = 0;
338 #endif
339 
340     // Points inside
341     test_point<Point>("POINT(4 4)", true, border, mapper, "red");
342 
343     // Points outside
344     test_point<Point>("POINT(3 3)", false, border, mapper, "green");
345     test_point<Point>("POINT(3 5)", false, border, mapper, "green");
346     test_point<Point>("POINT(5 3)", false, border, mapper, "green");
347     test_point<Point>("POINT(5 5)", false, border, mapper, "green");
348 
349     // Points on the original (should be INSIDE)
350     test_point<Point>("POINT(3 4)", true, border, mapper, "orange");
351     test_point<Point>("POINT(3.5 3.5)", true, border, mapper, "orange");
352     test_point<Point>("POINT(4 3)", true, border, mapper, "orange");
353 
354     // Points on the offsetted ring (should be OUTSIDE)
355     test_point<Point>("POINT(4 5)", false, border, mapper, "blue");
356     test_point<Point>("POINT(4.5 4.5)", false, border, mapper, "blue");
357     test_point<Point>("POINT(5 4)", false, border, mapper, "blue");
358 
359     // Points on between original and offsetted ring (should be INSIDE)
360     test_point<Point>("POINT(3.5 4.5)", true, border, mapper, "cyan");
361     test_point<Point>("POINT(4.5 3.5)", true, border, mapper, "cyan");
362 }
363 
test_main(int,char * [])364 int test_main(int, char* [])
365 {
366     BoostGeometryWriteTestConfiguration();
367 
368     typedef bg::model::point<default_test_type, 2, bg::cs::cartesian> point_type;
369 
370     test_rectangle_properties<point_type>();
371 
372     test_rectangle_point_on_piece_a<point_type>();
373     test_rectangle_point_on_piece_c<point_type>();
374 
375     test_diamond_point_on_piece_a<point_type>();
376     test_diamond_point_on_piece_c<point_type>();
377 
378     return 0;
379 }
380