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