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