1 //
2 // Ported from JTS junit/linearref/AbstractIndexedLineTestCase.java r466
3 // and  junit/linearref/LengthIndexedLineTestCase.java r466
4 
5 #include <tut/tut.hpp>
6 #include <utility.h>
7 // geos
8 #include <geos/io/WKTReader.h>
9 #include <geos/io/WKTWriter.h>
10 #include <geos/geom/PrecisionModel.h>
11 #include <geos/geom/GeometryFactory.h>
12 #include <geos/geom/Geometry.h> // required for use in unique_ptr
13 #include <geos/geom/LineString.h>
14 #include <geos/geom/Coordinate.h>
15 #include <geos/linearref/LengthIndexedLine.h>
16 
17 // std
18 #include <cmath>
19 #include <sstream>
20 #include <string>
21 #include <memory>
22 
23 using namespace geos::geom;
24 using namespace geos::linearref;
25 using namespace std;
26 
27 /**
28  * Tests the {@link LocationIndexedLine} class
29  */
30 namespace tut {
31 
32 typedef unique_ptr<Geometry> GeomPtr;
33 static const double TOLERANCE_DIST = 0.001;
34 
35 struct test_lengthindexedline_data {
test_lengthindexedline_datatut::test_lengthindexedline_data36     test_lengthindexedline_data()
37         : reader(), writer()
38     {
39         writer.setTrim(true);
40     }
41 
42     geos::io::WKTReader reader;
43     geos::io::WKTWriter writer;
44 
45     void
checkExpectedtut::test_lengthindexedline_data46     checkExpected(Geometry* result, string const& expected)
47     {
48         GeomPtr subLine(reader.read(expected));
49         checkExpected(result, subLine.get());
50     }
51 
52     void
checkExpectedtut::test_lengthindexedline_data53     checkExpected(Geometry* result, const Geometry* expected)
54     {
55         bool isEqual = result->equalsExact(expected, 1.0e-5);
56         ensure_equals("Expect: " + writer.write(expected) + " Obtained: " + writer.write(result), isEqual, true);
57     }
58 
59     void
runIndicesOfThenExtracttut::test_lengthindexedline_data60     runIndicesOfThenExtract(string const& inputStr, string const& subLineStr)
61     {
62         GeomPtr input(reader.read(inputStr));
63         GeomPtr subLine(reader.read(subLineStr));
64         GeomPtr result(indicesOfThenExtract(input.get(), subLine.get()));
65 
66         checkExpected(result.get(), subLine.get());
67     }
68 
69     bool
indexOfAfterChecktut::test_lengthindexedline_data70     indexOfAfterCheck(Geometry* linearGeom, Coordinate testPt)
71     {
72         LengthIndexedLine indexedLine(linearGeom);
73 
74         // check locations are consecutive
75         double loc1 = indexedLine.indexOf(testPt);
76         double loc2 = indexedLine.indexOfAfter(testPt, loc1);
77         if(loc2 <= loc1) {
78             return false;
79         }
80 
81         // check extracted points are the same as the input
82         Coordinate pt1 = indexedLine.extractPoint(loc1);
83         Coordinate pt2 = indexedLine.extractPoint(loc2);
84         if(! pt1.equals2D(testPt)) {
85             return false;
86         }
87         if(! pt2.equals2D(testPt)) {
88             return false;
89         }
90 
91         return true;
92     }
93 
94     void
runIndexOfAfterTesttut::test_lengthindexedline_data95     runIndexOfAfterTest(string const& inputStr, string const& testPtWKT)
96     {
97         GeomPtr input(reader.read(inputStr));
98         GeomPtr testPoint(reader.read(testPtWKT));
99         const Coordinate* testPt = testPoint->getCoordinate();
100         bool resultOK = indexOfAfterCheck(input.get(), *testPt);
101         ensure(resultOK);
102     }
103 
104     void
runOffsetTesttut::test_lengthindexedline_data105     runOffsetTest(string const& inputWKT, string const& testPtWKT,
106                   double offsetDistance, string const& expectedPtWKT)
107     {
108         GeomPtr input(reader.read(inputWKT));
109         GeomPtr testPoint(reader.read(testPtWKT));
110         GeomPtr expectedPoint(reader.read(expectedPtWKT));
111         const Coordinate* testPt = testPoint->getCoordinate();
112         const Coordinate* expectedPt = expectedPoint->getCoordinate();
113         Coordinate offsetPt = extractOffsetAt(input.get(), *testPt, offsetDistance);
114 
115         bool isOk = offsetPt.distance(*expectedPt) < TOLERANCE_DIST;
116         if(! isOk) {
117             cout << "Expected = " << *expectedPoint << "  Actual = " << offsetPt << endl;
118         }
119         ensure(isOk);
120     }
121 
122     Coordinate
extractOffsetAttut::test_lengthindexedline_data123     extractOffsetAt(Geometry* linearGeom, Coordinate testPt, double offsetDistance)
124     {
125         LengthIndexedLine indexedLine(linearGeom);
126         double index = indexedLine.indexOf(testPt);
127         return indexedLine.extractPoint(index, offsetDistance);
128     }
129 
130     void
checkExtractLinetut::test_lengthindexedline_data131     checkExtractLine(const char* wkt, double start, double end, const char* expected)
132     {
133         string wktstr(wkt);
134         GeomPtr linearGeom(reader.read(wktstr));
135         LengthIndexedLine indexedLine(linearGeom.get());
136         GeomPtr result(indexedLine.extractLine(start, end));
137         checkExpected(result.get(), expected);
138     }
139 
140 
141     std::unique_ptr<Geometry>
indicesOfThenExtracttut::test_lengthindexedline_data142     indicesOfThenExtract(Geometry* linearGeom, Geometry* subLine)
143     {
144         LengthIndexedLine indexedLine(linearGeom);
145         double* loc = indexedLine.indicesOf(subLine);
146         auto result = indexedLine.extractLine(loc[0], loc[1]);
147         delete [] loc;
148         return result;
149     }
150 
151 }; // struct test_lengthindexedline_data
152 
153 typedef test_group<test_lengthindexedline_data> group;
154 typedef group::object object;
155 
156 group test_lengthindexedline_group("geos::linearref::LengthIndexedLine");
157 
158 // testML
159 template<>
160 template<>
test()161 void object::test<1>
162 ()
163 {
164     runIndicesOfThenExtract("MULTILINESTRING ((0 0, 10 10), (20 20, 30 30))",
165                             "MULTILINESTRING ((1 1, 10 10), (20 20, 25 25))");
166 }
167 
168 // testPartOfSegmentNoVertex
169 template<>
170 template<>
test()171 void object::test<2>
172 ()
173 {
174     runIndicesOfThenExtract("LINESTRING (0 0, 10 10, 20 20)",
175                             "LINESTRING (1 1, 9 9)");
176 }
177 
178 // testPartOfSegmentContainingVertex()
179 template<>
180 template<>
test()181 void object::test<3>
182 ()
183 {
184     runIndicesOfThenExtract("LINESTRING (0 0, 10 10, 20 20)",
185                             "LINESTRING (5 5, 10 10, 15 15)");
186 }
187 
188 /**
189  * Tests that duplicate coordinates are handled correctly.
190  *
191  * @throws Exception
192  */
193 // testPartOfSegmentContainingDuplicateCoords
194 template<>
195 template<>
test()196 void object::test<4>
197 ()
198 {
199     runIndicesOfThenExtract("LINESTRING (0 0, 10 10, 10 10, 20 20)",
200                             "LINESTRING (5 5, 10 10, 10 10, 15 15)");
201 }
202 
203 /**
204  * Following tests check that correct portion of loop is identified.
205  * This requires that the correct vertex for (0,0) is selected.
206  */
207 
208 // testLoopWithStartSubLine
209 template<>
210 template<>
test()211 void object::test<5>
212 ()
213 {
214     runIndicesOfThenExtract("LINESTRING (0 0, 0 10, 10 10, 10 0, 0 0)",
215                             "LINESTRING (0 0, 0 10, 10 10)");
216 }
217 
218 // testLoopWithEndingSubLine()
219 template<>
220 template<>
test()221 void object::test<6>
222 ()
223 {
224     runIndicesOfThenExtract("LINESTRING (0 0, 0 10, 10 10, 10 0, 0 0)",
225                             "LINESTRING (10 10, 10 0, 0 0)");
226 }
227 
228 // test a subline equal to the parent loop
229 // testLoopWithIdenticalSubLine()
230 template<>
231 template<>
test()232 void object::test<7>
233 ()
234 {
235     runIndicesOfThenExtract("LINESTRING (0 0, 0 10, 10 10, 10 0, 0 0)",
236                             "LINESTRING (0 0, 0 10, 10 10, 10 0, 0 0)");
237 }
238 
239 // test a zero-length subline equal to the start point
240 // testZeroLenSubLineAtStart()
241 template<>
242 template<>
test()243 void object::test<8>
244 ()
245 {
246     runIndicesOfThenExtract("LINESTRING (0 0, 0 10, 10 10, 10 0, 0 0)",
247                             "LINESTRING (0 0, 0 0)");
248 }
249 
250 // test a zero-length subline equal to a mid point
251 // testZeroLenSubLineAtMidVertex()
252 template<>
253 template<>
test()254 void object::test<9>
255 ()
256 {
257     runIndicesOfThenExtract("LINESTRING (0 0, 0 10, 10 10, 10 0, 0 0)",
258                             "LINESTRING (10 10, 10 10)");
259 }
260 
261 // testIndexOfAfterSquare()
262 template<>
263 template<>
test()264 void object::test<10>
265 ()
266 {
267     runIndexOfAfterTest("LINESTRING (0 0, 0 10, 10 10, 10 0, 0 0)",
268                         "POINT (0 0)");
269 }
270 
271 // testIndexOfAfterRibbon()
272 template<>
273 template<>
test()274 void object::test<11>
275 ()
276 {
277     runIndexOfAfterTest("LINESTRING (0 0, 0 60, 50 60, 50 20, -20 20)",
278                         "POINT (0 20)");
279 }
280 
281 // testOffsetStartPoint()
282 template<>
283 template<>
test()284 void object::test<12>
285 ()
286 {
287     runOffsetTest("LINESTRING (0 0, 10 10, 10 10, 20 20)", "POINT(0 0)", 1.0,
288                   "POINT (-0.7071067811865475 0.7071067811865475)");
289     runOffsetTest("LINESTRING (0 0, 10 10, 10 10, 20 20)", "POINT(0 0)", -1.0,
290                   "POINT (0.7071067811865475 -0.7071067811865475)");
291     runOffsetTest("LINESTRING (0 0, 10 10, 10 10, 20 20)", "POINT(10 10)", 5.0,
292                   "POINT (6.464466094067262 13.535533905932738)");
293     runOffsetTest("LINESTRING (0 0, 10 10, 10 10, 20 20)", "POINT(10 10)", -5.0,
294                   "POINT (13.535533905932738 6.464466094067262)");
295 }
296 
297 // testExtractLineBothIndicesAtEndpointXXX()
298 template<>
299 template<>
test()300 void object::test<13>
301 ()
302 {
303     checkExtractLine(
304         "MULTILINESTRING ((0 0, 10 0), (20 0, 25 0, 30 0))",
305         -10, 10, "LINESTRING (10 0, 10 0)"
306     );
307 }
308 
309 
310 // testExtractLineBeyondRange()
311 template<> template<>
test()312 void object::test<14>
313 ()
314 {
315     checkExtractLine("LINESTRING (0 0, 10 10)", -100, 100, "LINESTRING (0 0, 10 10)");
316 }
317 
318 // testExtractLineReverse()
319 template<>
320 template<>
test()321 void object::test<15>
322 ()
323 {
324     checkExtractLine("LINESTRING (0 0, 10 0)", 9, 1, "LINESTRING (9 0, 1 0)");
325 }
326 
327 // testExtractLineReverseMulti()
328 template<>
329 template<>
test()330 void object::test<16>
331 ()
332 {
333     checkExtractLine("MULTILINESTRING ((0 0, 10 0), (20 0, 25 0, 30 0))",
334                      19, 1, "MULTILINESTRING ((10 0, 1 0), (29 0, 25 0, 20 0))");
335 }
336 
337 // testExtractLineNegative()
338 template<>
339 template<>
test()340 void object::test<17>
341 ()
342 {
343     checkExtractLine("LINESTRING (0 0, 10 0)", -9, -1, "LINESTRING (1 0, 9 0)");
344 }
345 
346 // testExtractLineNegativeReverse()
347 template<>
348 template<>
test()349 void object::test<18>
350 ()
351 {
352     checkExtractLine("LINESTRING (0 0, 10 0)", -1, -9, "LINESTRING (9 0, 1 0)");
353 }
354 
355 // testExtractLineIndexAtEndpoint()
356 template<>
357 template<>
test()358 void object::test<19>
359 ()
360 {
361     checkExtractLine("MULTILINESTRING ((0 0, 10 0), (20 0, 25 0, 30 0))",
362                      10, -1, "LINESTRING (20 0, 25 0, 29 0)");
363 }
364 
365 // testExtractLineBothIndicesAtEndpoint()
366 template<>
367 template<>
test()368 void object::test<20>
369 ()
370 {
371     checkExtractLine("MULTILINESTRING ((0 0, 10 0), (20 0, 25 0, 30 0))",
372                      10, 10, "LINESTRING (10 0, 10 0)");
373 }
374 
375 // testExtractLineBothIndicesAtEndpointNegative()
376 template<>
377 template<>
test()378 void object::test<21>
379 ()
380 {
381     checkExtractLine("MULTILINESTRING ((0 0, 10 0), (20 0, 25 0, 30 0))",
382                      -10, 10, "LINESTRING (10 0, 10 0)");
383 }
384 
385 // testExtractPointBeyondRange()
386 template<>
387 template<>
test()388 void object::test<22>
389 ()
390 {
391     GeomPtr linearGeom(reader.read("LINESTRING (0 0, 10 10)"));
392     LengthIndexedLine indexedLine(linearGeom.get());
393     Coordinate pt = indexedLine.extractPoint(100);
394     ensure(pt == Coordinate(10, 10));
395 
396     Coordinate pt2 = indexedLine.extractPoint(0);
397     ensure(pt2 == Coordinate(0, 0));
398 }
399 
400 // testProjectPointWithDuplicateCoords()
401 template<>
402 template<>
test()403 void object::test<23>
404 ()
405 {
406     GeomPtr linearGeom(reader.read("LINESTRING (0 0, 10 0, 10 0, 20 0)"));
407     LengthIndexedLine indexedLine(linearGeom.get());
408     double projIndex = indexedLine.project(Coordinate(10, 1));
409     ensure(projIndex == 10.0);
410 }
411 
412 /**
413  * Tests that z values are interpolated
414  *
415  */
416 // testComputeZ()
417 template<>
418 template<>
test()419 void object::test<24>
420 ()
421 {
422     GeomPtr linearGeom(reader.read("LINESTRING (0 0 0, 10 10 10)"));
423     LengthIndexedLine indexedLine(linearGeom.get());
424     double projIndex = indexedLine.project(Coordinate(5, 5));
425     Coordinate projPt = indexedLine.extractPoint(projIndex);
426     //    System.out.println(projPt);
427     ensure(projPt.equals3D(Coordinate(5, 5, 5)));
428 }
429 
430 /**
431  * Tests that if the input does not have Z ordinates, neither does the output.
432  *
433  */
434 // testComputeZNaN()
435 template<>
436 template<>
test()437 void object::test<25>
438 ()
439 {
440 
441     GeomPtr linearGeom(reader.read("LINESTRING (0 0, 10 10 10)"));
442     LengthIndexedLine indexedLine(linearGeom.get());
443     double projIndex = indexedLine.project(Coordinate(5, 5));
444     Coordinate projPt = indexedLine.extractPoint(projIndex);
445     ensure(0 != std::isnan(projPt.z));
446 }
447 
448 /**
449  * From GEOS Ticket #323
450  */
451 // testProjectExtractPoint()
452 template<>
453 template<>
test()454 void object::test<26>
455 ()
456 {
457     GeomPtr linearGeom(reader.read("MULTILINESTRING ((0 2, 0 0), (-1 1, 1 1))"));
458     LengthIndexedLine indexedLine(linearGeom.get());
459     double index = indexedLine.project(Coordinate(1, 0));
460     Coordinate pt = indexedLine.extractPoint(index);
461     ensure_equals(pt, Coordinate(0, 0));
462 }
463 
464 /**
465  * Tests that leading and trailing zero-length sublines are trimmed in
466  * the computed result, and that zero-length extracts return the lowest
467  * extracted zero-length line
468  */
469 // testExtractLineIndexAtEndpointWithZeroLenComponents()
470 template<> template<>
test()471 void object::test<27>
472 ()
473 {
474     checkExtractLine(
475         "MULTILINESTRING ((0 0, 10 0), (10 0, 10 0), (20 0, 25 0, 30 0))",
476         10, -1, "LINESTRING (20 0, 25 0, 29 0)");
477 
478     checkExtractLine(
479         "MULTILINESTRING ((0 0, 10 0), (10 0, 10 0), (20 0, 25 0, 30 0))",
480         5, 10, "LINESTRING (5 0, 10 0)");
481 
482     checkExtractLine(
483         "MULTILINESTRING ((0 0,10 0),(10 0,10 0),(10 0,10 0),(20 0,25 0,30 0))",
484         10, 10, "LINESTRING (10 0, 10 0)");
485 
486     checkExtractLine(
487         "MULTILINESTRING((0 0,10 0),(10 0,10 0),(10 0,10 0),(10 0,10 0),(20 0,25 0,30 0))",
488         10, -10, "LINESTRING (10 0, 10 0)");
489 }
490 
491 
492 
493 
494 
495 #if 0
496 template<>
497 template<>
498 void object::test<28>
499 ()
500 {
501 
502     GeomPtr linearGeom(reader.read(
503                            "MULTILINESTRING ((0 -2, 0 2),(-2 0, 2 0))"
504                        ));
505     LengthIndexedLine indexedLine(linearGeom.get());
506 
507     double projIndex = indexedLine.project(Coordinate(2, 1.9));
508     ensure_equals(projIndex, 8);
509     Coordinate projPt = indexedLine.extractPoint(projIndex);
510     ensure_equals(projPt, Coordinate(2, 0));
511 
512     projIndex = indexedLine.project(Coordinate(2, 2.1));
513     ensure_equals(projIndex, 4);
514     projPt = indexedLine.extractPoint(projIndex);
515     ensure_equals(projPt, Coordinate(0, 2));
516 }
517 #endif
518 
519 template<>
520 template<>
test()521 void object::test<29>
522 ()
523 {
524     GeomPtr linearGeom(reader.read("LINESTRING EMPTY"));
525     LengthIndexedLine indexedLine(linearGeom.get());
526     Coordinate pt = indexedLine.extractPoint(100);
527     ensure(pt.isNull());
528 }
529 
530 } // namespace tut
531 
532