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