1 //
2 // Test Suite for geos::operation::distance::DistanceOp class.
3
4 // tut
5 #include <tut/tut.hpp>
6 // geos
7 #include <geos/operation/distance/DistanceOp.h>
8 #include <geos/constants.h>
9 #include <geos/geom/Coordinate.h>
10 #include <geos/geom/GeometryFactory.h>
11 #include <geos/geom/Geometry.h>
12 #include <geos/geom/LineSegment.h>
13 #include <geos/geom/LineString.h>
14 #include <geos/geom/PrecisionModel.h>
15 #include <geos/algorithm/PointLocator.h>
16 #include <geos/io/WKTReader.h>
17 #include <geos/io/WKBReader.h>
18 #include <geos/geom/CoordinateSequence.h>
19 #include <geos/geom/CoordinateArraySequence.h>
20 // std
21 #include <memory>
22 #include <string>
23 #include <vector>
24
25 namespace tut {
26 //
27 // Test Group
28 //
29
30 // Common data used by tests
31 struct test_distanceop_data {
32 geos::io::WKTReader wktreader;
33
34 typedef geos::geom::Geometry::Ptr GeomPtr;
35 typedef std::unique_ptr<geos::geom::CoordinateSequence> CSPtr;
36
test_distanceop_datatut::test_distanceop_data37 test_distanceop_data()
38 : wktreader()
39 {}
40 };
41
42 typedef test_group<test_distanceop_data> group;
43 typedef group::object object;
44
45 group test_distanceop_group("geos::operation::distance::DistanceOp");
46
47 //
48 // Test Cases
49 //
50 template<>
51 template<>
test()52 void object::test<1>
53 ()
54 {
55 using geos::operation::distance::DistanceOp;
56 using geos::geom::Coordinate;
57
58 std::string wkt0("POINT(0 0)");
59 std::string wkt1("POINT(10 0)");
60 GeomPtr g0(wktreader.read(wkt0));
61 GeomPtr g1(wktreader.read(wkt1));
62
63 DistanceOp dist(g0.get(), g1.get());
64
65 ensure_equals(dist.distance(), 10);
66
67 CSPtr cs(dist.nearestPoints());
68 ensure_equals(cs->getAt(0), Coordinate(0, 0));
69 ensure_equals(cs->getAt(1), Coordinate(10, 0));
70 }
71
72 template<>
73 template<>
test()74 void object::test<2>
75 ()
76 {
77 using geos::operation::distance::DistanceOp;
78 using geos::geom::Coordinate;
79
80 std::string wkt0("POINT(0 0)");
81 std::string wkt1("MULTIPOINT(10 0, 50 30)");
82 GeomPtr g0(wktreader.read(wkt0));
83 GeomPtr g1(wktreader.read(wkt1));
84
85 DistanceOp dist(g0.get(), g1.get());
86
87 ensure_equals(dist.distance(), 10);
88
89 CSPtr cs(dist.nearestPoints());
90 ensure_equals(cs->getAt(0), Coordinate(0, 0));
91 ensure_equals(cs->getAt(1), Coordinate(10, 0));
92
93 }
94
95 template<>
96 template<>
test()97 void object::test<3>
98 ()
99 {
100 using geos::operation::distance::DistanceOp;
101 using geos::geom::Coordinate;
102
103 std::string wkt0("POINT(3 0)");
104 std::string wkt1("LINESTRING(0 10, 50 10, 100 50)");
105 GeomPtr g0(wktreader.read(wkt0));
106 GeomPtr g1(wktreader.read(wkt1));
107
108 DistanceOp dist(g0.get(), g1.get());
109
110 ensure_equals(dist.distance(), 10);
111
112 CSPtr cs(dist.nearestPoints());
113 ensure_equals(cs->getAt(0), Coordinate(3, 0));
114 ensure_equals(cs->getAt(1), Coordinate(3, 10));
115
116 }
117
118 template<>
119 template<>
test()120 void object::test<4>
121 ()
122 {
123 using geos::operation::distance::DistanceOp;
124 using geos::geom::Coordinate;
125
126 std::string wkt0("POINT(3 0)");
127 std::string wkt1("MULTILINESTRING((34 54, 60 34),(0 10, 50 10, 100 50))");
128 GeomPtr g0(wktreader.read(wkt0));
129 GeomPtr g1(wktreader.read(wkt1));
130
131 DistanceOp dist(g0.get(), g1.get());
132
133 ensure_equals(dist.distance(), 10);
134
135 CSPtr cs(dist.nearestPoints());
136 ensure_equals(cs->getAt(0), Coordinate(3, 0));
137 ensure_equals(cs->getAt(1), Coordinate(3, 10));
138
139 }
140
141 template<>
142 template<>
test()143 void object::test<5>
144 ()
145 {
146 using geos::operation::distance::DistanceOp;
147 using geos::geom::Coordinate;
148
149 std::string wkt0("POINT(35 60)");
150 std::string wkt1("POLYGON((34 54, 60 34, 60 54, 34 54),(50 50, 52 50, 52 48, 50 48, 50 50))");
151 GeomPtr g0(wktreader.read(wkt0));
152 GeomPtr g1(wktreader.read(wkt1));
153
154 DistanceOp dist(g0.get(), g1.get());
155
156 ensure_equals(dist.distance(), 6);
157
158 CSPtr cs(dist.nearestPoints());
159 ensure_equals(cs->getAt(0), Coordinate(35, 60));
160 ensure_equals(cs->getAt(1), Coordinate(35, 54));
161
162 }
163
164 template<>
165 template<>
test()166 void object::test<6>
167 ()
168 {
169 using geos::operation::distance::DistanceOp;
170 using geos::geom::Coordinate;
171
172 std::string wkt0("POINT(35 60)");
173 std::string
174 wkt1("MULTIPOLYGON(((34 54, 60 34, 60 54, 34 54),(50 50, 52 50, 52 48, 50 48, 50 50)),( (100 100, 150 100, 150 150, 100 150, 100 100),(120 120, 120 130, 130 130, 130 120, 120 120)) ))");
175 GeomPtr g0(wktreader.read(wkt0));
176 GeomPtr g1(wktreader.read(wkt1));
177
178 DistanceOp dist(g0.get(), g1.get());
179
180 ensure_equals(dist.distance(), 6);
181
182 CSPtr cs(dist.nearestPoints());
183 ensure_equals(cs->getAt(0), Coordinate(35, 60));
184 ensure_equals(cs->getAt(1), Coordinate(35, 54));
185
186 }
187
188 template<>
189 template<>
test()190 void object::test<7>
191 ()
192 {
193 using geos::operation::distance::DistanceOp;
194 using geos::geom::Coordinate;
195
196 std::string wkt0("POINT(35 60)");
197
198 // This is an invalid geom... anyway
199 std::string
200 wkt1("GEOMETRYCOLLECTION(MULTIPOLYGON(((34 54, 60 34, 60 54, 34 54),(50 50, 52 50, 52 48, 50 48, 50 50)),( (100 100, 150 100, 150 150, 100 150, 100 100),(120 120, 120 130, 130 130, 130 120, 120 120)) )), POLYGON((34 54, 60 34, 60 54, 34 54),(50 50, 52 50, 52 48, 50 48, 50 50)), MULTILINESTRING((34 54, 60 34),(0 10, 50 10, 100 50)), LINESTRING(0 10, 50 10, 100 50), MULTIPOINT(10 0, 50 30), POINT(10 0))");
201
202 GeomPtr g0(wktreader.read(wkt0));
203 GeomPtr g1(wktreader.read(wkt1));
204
205 DistanceOp dist(g0.get(), g1.get());
206
207 ensure_equals(dist.distance(), 6);
208
209 CSPtr cs(dist.nearestPoints());
210 ensure_equals(cs->getAt(0), Coordinate(35, 60));
211 ensure_equals(cs->getAt(1), Coordinate(35, 54));
212 }
213
214 template<>
215 template<>
test()216 void object::test<8>
217 ()
218 {
219 using geos::operation::distance::DistanceOp;
220 using geos::geom::Coordinate;
221
222 std::string wkt0("POINT(35 60)");
223
224 // This is an invalid geom... anyway
225 std::string wkt1("GEOMETRYCOLLECTION EMPTY");
226
227 GeomPtr g0(wktreader.read(wkt0));
228 GeomPtr g1(wktreader.read(wkt1));
229
230 DistanceOp dist(g0.get(), g1.get());
231
232 ensure_equals(dist.distance(), 0);
233
234 ensure(dist.nearestPoints() == nullptr);
235 }
236
237 template<>
238 template<>
test()239 void object::test<9>
240 ()
241 {
242 using geos::operation::distance::DistanceOp;
243 using geos::geom::Coordinate;
244
245 std::string wkt0("MULTIPOINT(10 0, 50 30)");
246 std::string wkt1("POINT(10 0)");
247 GeomPtr g0(wktreader.read(wkt0));
248 GeomPtr g1(wktreader.read(wkt1));
249
250 DistanceOp dist(g0.get(), g1.get());
251
252 ensure_equals(dist.distance(), 0);
253
254 CSPtr cs(dist.nearestPoints());
255 ensure_equals(cs->getAt(0), Coordinate(10, 0));
256 ensure_equals(cs->getAt(1), Coordinate(10, 0));
257
258 }
259
260 template<>
261 template<>
test()262 void object::test<10>
263 ()
264 {
265 using geos::operation::distance::DistanceOp;
266 using geos::geom::Coordinate;
267
268 std::string wkt0("MULTIPOINT(10 0, 50 30)");
269 std::string wkt1("MULTIPOINT(0 0, 150 30)");
270 GeomPtr g0(wktreader.read(wkt0));
271 GeomPtr g1(wktreader.read(wkt1));
272
273 DistanceOp dist(g0.get(), g1.get());
274
275 ensure_equals(dist.distance(), 10);
276
277 CSPtr cs(dist.nearestPoints());
278 ensure_equals(cs->getAt(0), Coordinate(10, 0));
279 ensure_equals(cs->getAt(1), Coordinate(0, 0));
280
281 }
282
283 template<>
284 template<>
test()285 void object::test<11>
286 ()
287 {
288 using geos::operation::distance::DistanceOp;
289 using geos::geom::Coordinate;
290
291 std::string wkt0("MULTIPOINT(3 0, 200 30)");
292 std::string wkt1("LINESTRING(0 10, 50 10, 100 50)");
293 GeomPtr g0(wktreader.read(wkt0));
294 GeomPtr g1(wktreader.read(wkt1));
295
296 DistanceOp dist(g0.get(), g1.get());
297
298 ensure_equals(dist.distance(), 10);
299
300 CSPtr cs(dist.nearestPoints());
301 ensure_equals(cs->getAt(0), Coordinate(3, 0));
302 ensure_equals(cs->getAt(1), Coordinate(3, 10));
303
304 }
305
306 template<>
307 template<>
test()308 void object::test<12>
309 ()
310 {
311 using geos::operation::distance::DistanceOp;
312 using geos::geom::Coordinate;
313
314 std::string wkt0("MULTIPOINT(3 0, -50 30)");
315 std::string wkt1("MULTILINESTRING((34 54, 60 34),(0 10, 50 10, 100 50))");
316 GeomPtr g0(wktreader.read(wkt0));
317 GeomPtr g1(wktreader.read(wkt1));
318
319 DistanceOp dist(g0.get(), g1.get());
320
321 ensure_equals(dist.distance(), 10);
322
323 CSPtr cs(dist.nearestPoints());
324 ensure_equals(cs->getAt(0), Coordinate(3, 0));
325 ensure_equals(cs->getAt(1), Coordinate(3, 10));
326
327 }
328
329 template<>
330 template<>
test()331 void object::test<13>
332 ()
333 {
334 using geos::operation::distance::DistanceOp;
335 using geos::geom::Coordinate;
336
337 std::string wkt0("MULTIPOINT(-100 0, 35 60)");
338 std::string wkt1("POLYGON((34 54, 60 34, 60 54, 34 54),(50 50, 52 50, 52 48, 50 48, 50 50))");
339 GeomPtr g0(wktreader.read(wkt0));
340 GeomPtr g1(wktreader.read(wkt1));
341
342 DistanceOp dist(g0.get(), g1.get());
343
344 ensure_equals(dist.distance(), 6);
345
346 CSPtr cs(dist.nearestPoints());
347 ensure_equals(cs->getAt(0), Coordinate(35, 60));
348 ensure_equals(cs->getAt(1), Coordinate(35, 54));
349
350 }
351
352 template<>
353 template<>
test()354 void object::test<14>
355 ()
356 {
357 using geos::operation::distance::DistanceOp;
358 using geos::geom::Coordinate;
359
360 std::string wkt0("MULTIPOINT(-100 0, 35 60)");
361 std::string
362 wkt1("MULTIPOLYGON(((34 54, 60 34, 60 54, 34 54),(50 50, 52 50, 52 48, 50 48, 50 50)),( (100 100, 150 100, 150 150, 100 150, 100 100),(120 120, 120 130, 130 130, 130 120, 120 120)) ))");
363 GeomPtr g0(wktreader.read(wkt0));
364 GeomPtr g1(wktreader.read(wkt1));
365
366 DistanceOp dist(g0.get(), g1.get());
367
368 ensure_equals(dist.distance(), 6);
369
370 CSPtr cs(dist.nearestPoints());
371 ensure_equals(cs->getAt(0), Coordinate(35, 60));
372 ensure_equals(cs->getAt(1), Coordinate(35, 54));
373 }
374
375 template<>
376 template<>
test()377 void object::test<15>
378 ()
379 {
380 using geos::operation::distance::DistanceOp;
381 using geos::geom::Coordinate;
382
383 std::string wkt0("MULTIPOINT(-100 0, 35 60)");
384
385 // This is an invalid geom... anyway
386 std::string
387 wkt1("GEOMETRYCOLLECTION(MULTIPOLYGON(((34 54, 60 34, 60 54, 34 54),(50 50, 52 50, 52 48, 50 48, 50 50)),( (100 100, 150 100, 150 150, 100 150, 100 100),(120 120, 120 130, 130 130, 130 120, 120 120)) )), POLYGON((34 54, 60 34, 60 54, 34 54),(50 50, 52 50, 52 48, 50 48, 50 50)), MULTILINESTRING((34 54, 60 34),(0 10, 50 10, 100 50)), LINESTRING(0 10, 50 10, 100 50), MULTIPOINT(10 0, 50 30), POINT(10 0))");
388
389 GeomPtr g0(wktreader.read(wkt0));
390 GeomPtr g1(wktreader.read(wkt1));
391
392 DistanceOp dist(g0.get(), g1.get());
393
394 ensure_equals(dist.distance(), 6);
395
396 CSPtr cs(dist.nearestPoints());
397 ensure_equals(cs->getAt(0), Coordinate(35, 60));
398 ensure_equals(cs->getAt(1), Coordinate(35, 54));
399
400 }
401
402 template<>
403 template<>
test()404 void object::test<16>
405 ()
406 {
407 using geos::operation::distance::DistanceOp;
408 using geos::geom::Coordinate;
409
410 std::string wkt0("MULTIPOINT(-100 0, 35 60)");
411
412 // This is an invalid geom... anyway
413 std::string wkt1("GEOMETRYCOLLECTION EMPTY");
414
415 GeomPtr g0(wktreader.read(wkt0));
416 GeomPtr g1(wktreader.read(wkt1));
417
418 DistanceOp dist(g0.get(), g1.get());
419
420 ensure_equals(dist.distance(), 0);
421
422 ensure(dist.nearestPoints() == nullptr);
423 }
424
425 // Test for crash reported in Ticket #236:
426 // http://trac.osgeo.org/geos/ticket/236
427 template<>
428 template<>
test()429 void object::test<17>
430 ()
431 {
432 using geos::operation::distance::DistanceOp;
433 using geos::geom::Coordinate;
434
435 std::string wkt0("POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))");
436 std::string wkt1("POLYGON((1.25 0.25, 1.25 0.75, 1.75 0.75, 1.75 0.25, 1.25 0.25))");
437
438 GeomPtr g0(wktreader.read(wkt0));
439 GeomPtr g1(wktreader.read(wkt1));
440
441 DistanceOp dist(g0.get(), g1.get());
442 ensure_equals(dist.distance(), 0.25);
443
444 CSPtr cs(dist.nearestPoints());
445 ensure_equals(cs->getAt(0), Coordinate(1, 0.25));
446 ensure_equals(cs->getAt(1), Coordinate(1.25, 0.25));
447 }
448
449 // Test for isWithinDistance
450 template<>
451 template<>
test()452 void object::test<18>
453 ()
454 {
455 using geos::operation::distance::DistanceOp;
456 using geos::geom::Coordinate;
457
458 std::string wkt0("POLYGON((0 0, 0 1, 1 1, 1 0, 0 0))");
459 std::string wkt1("POLYGON((1.25 0.25, 1.25 0.75, 1.75 0.75, 1.75 0.25, 1.25 0.25))");
460
461 GeomPtr g0(wktreader.read(wkt0));
462 GeomPtr g1(wktreader.read(wkt1));
463
464 ensure_equals(DistanceOp::distance(*g0, *g1), 0.25);
465 ensure(DistanceOp::isWithinDistance(*g0, *g1, 0.26));
466 ensure(!DistanceOp::isWithinDistance(*g0, *g1, 0.24));
467
468 wkt0 = "LINESTRING(0 0, 0 1, 1 1, 1 0, 0 0)";
469 wkt1 = "LINESTRING(2 0, 10 1, 10 10)";
470 g0 = wktreader.read(wkt0);
471 g1 = wktreader.read(wkt1);
472
473 ensure_equals(DistanceOp::distance(*g0, *g1), 1);
474 ensure(DistanceOp::isWithinDistance(*g0, *g1, 2));
475 ensure(!DistanceOp::isWithinDistance(*g0, *g1, 0.8));
476
477 // TODO: test closestPoints
478 }
479
480 // Test case submitted to Ticket #367
481 // http://trac.osgeo.org/geos/ticket/367/
482 template<>
483 template<>
test()484 void object::test<19>
485 ()
486 {
487 using geos::geom::GeometryFactory;
488 const char* wkb_geom1 = "01060000000100000001030000000100000000000000";
489 const char* wkb_geom2 = "010100000000000000000000000000000000000000";
490
491 geos::geom::PrecisionModel precision(geos::geom::PrecisionModel::FLOATING);
492 GeometryFactory::Ptr f(GeometryFactory::create(&precision));
493 std::istringstream istr1(wkb_geom1);
494 std::istringstream istr2(wkb_geom2);
495 geos::io::WKBReader wkb(*f);
496 GeomPtr g1(wkb.readHEX(istr1));
497 GeomPtr g2(wkb.readHEX(istr2));
498 ensure(g1->isValid());
499 ensure(g2->isValid());
500
501 ensure_equals(g1->distance(g2.get()), 0);
502 }
503
504 // Test case reported in Shapely
505 // https://github.com/Toblerity/Shapely/issues/560
506 template<>
507 template<>
test()508 void object::test<20>()
509 {
510 using geos::operation::distance::DistanceOp;
511 using geos::geom::Coordinate;
512 using geos::geom::LineSegment;
513 using geos::geom::Geometry;
514 using geos::geom::LineString;
515 using geos::geom::GeometryFactory;
516 using geos::geom::CoordinateArraySequence;
517
518 auto gfact = GeometryFactory::create();
519
520 CSPtr seq0(new CoordinateArraySequence(2));
521 CSPtr seq1(new CoordinateArraySequence(2));
522
523 Coordinate a0{1, 5.0/3.0};
524 Coordinate a1{2, 10.0/3.0};
525
526 Coordinate b0{3, 5};
527 Coordinate b1{0, 0};
528
529 seq0->setAt(a0, 0);
530 seq0->setAt(a1, 1);
531
532 seq1->setAt(b0, 0);
533 seq1->setAt(b1, 1);
534
535 GeomPtr g0(gfact->createLineString(seq0.release()));
536 GeomPtr g1(gfact->createLineString(seq1.release()));
537
538 DistanceOp dist(g0.get(), g1.get());
539 CSPtr seq(dist.nearestPoints());
540
541 // input lines overlap, so generated point should intersect both geometries
542 ensure(geos::geom::LineSegment(a0, a1).distance(seq->getAt(0)) < 1e-8);
543 ensure(geos::geom::LineSegment(a0, a1).distance(seq->getAt(1)) < 1e-8);
544 ensure(geos::geom::LineSegment(b0, b1).distance(seq->getAt(0)) < 1e-8);
545 ensure(geos::geom::LineSegment(b0, b1).distance(seq->getAt(1)) < 1e-8);
546
547 // reverse argument order and check again
548 DistanceOp dist2(g1.get(), g0.get());
549 seq = dist2.nearestPoints();
550
551 ensure(geos::geom::LineSegment(a0, a1).distance(seq->getAt(0)) < 1e-8);
552 ensure(geos::geom::LineSegment(a0, a1).distance(seq->getAt(1)) < 1e-8);
553 ensure(geos::geom::LineSegment(b0, b1).distance(seq->getAt(0)) < 1e-8);
554 ensure(geos::geom::LineSegment(b0, b1).distance(seq->getAt(1)) < 1e-8);
555 }
556
557 // JTS testDisjointCollinearSegments
558 template<>
559 template<>
test()560 void object::test<21>()
561 {
562 auto g1 = wktreader.read("LINESTRING (0.0 0.0, 9.9 1.4)");
563 auto g2 = wktreader.read("LINESTRING (11.88 1.68, 21.78 3.08)");
564
565 ensure_equals(g1->distance(g2.get()), 1.9996999774966246);
566 }
567
568 // TODO: finish the tests by adding:
569 // LINESTRING - *all*
570 // MULTILINESTRING - *all*
571 // POLYGON - *all*
572 // MULTIPOLYGON - *all*
573 // COLLECTION - *all*
574
575 } // namespace tut
576
577