diff --git a/NEWS.md b/NEWS.md index 3f8bf9b43b..b59293b734 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,8 +10,8 @@ - BufferOp returns POLYGON EMPTY when fed Inf/Nan coords (GH-1332) - Return Inf when calculating distance to an empty geometry (GH-1345, Even Rouault) - - Fixes/Improvements: + - GEOSSnap: Recursively snap segments till nothing moves (#760, Sandro Santilli) - Buffer of Linestring includes spurious hole (GH-1217, Moritz Kirmse) - Preserve M values in GEOSInterpolate (GH-1390, Dan Baston) - Preserve M values in GEOSLineMerge (GH-1364, Dan Baston) diff --git a/src/operation/overlay/snap/LineStringSnapper.cpp b/src/operation/overlay/snap/LineStringSnapper.cpp index d196414a71..6679462040 100644 --- a/src/operation/overlay/snap/LineStringSnapper.cpp +++ b/src/operation/overlay/snap/LineStringSnapper.cpp @@ -239,6 +239,12 @@ LineStringSnapper::snapSegments(geom::CoordinateList& srcCoords, std::cerr << "Snapping segments of: " << srcCoords << std::endl; #endif + // Recursively snap segments till nothing moves + // See https://trac.osgeo.org/geos/ticket/760 + bool moved = false; + do + { + moved = false; for(Coordinate::ConstVect::const_iterator it = snapPts.begin(), end = snapPts.end(); it != end; @@ -261,6 +267,8 @@ LineStringSnapper::snapSegments(geom::CoordinateList& srcCoords, continue; } + moved = true; + /* Check if the snap point falls outside of the segment */ // If the snap point is outside, this means that an endpoint // was not snap where it should have been @@ -376,6 +384,8 @@ LineStringSnapper::snapSegments(geom::CoordinateList& srcCoords, srcCoords.insert(segpos, snapPt); } } + // while things keep moving + } while (moved); #if GEOS_DEBUG std::cerr << " After segment snapping, srcCoors are: " << srcCoords << std::endl; diff --git a/tests/unit/operation/overlay/snap/LineStringSnapperTest.cpp b/tests/unit/operation/overlay/snap/LineStringSnapperTest.cpp index 6342b4243e..af44e7b62d 100644 --- a/tests/unit/operation/overlay/snap/LineStringSnapperTest.cpp +++ b/tests/unit/operation/overlay/snap/LineStringSnapperTest.cpp @@ -355,4 +355,46 @@ void object::test<9> ensure_equals(ret->operator[](4), snp_a); // 0 0 } + // Recursive snap of segments + // See https://trac.osgeo.org/geos/ticket/760 + template<> + template<> + void object::test<10>() + { + using geos::geom::Coordinate; + using geos::operation::overlay::snap::LineStringSnapper; + + typedef std::auto_ptr CoordsVectAptr; + + + // Source: (0 0,0 20) + Coordinate src_a(0, 0); + Coordinate src_b(0, 20); + Coordinate::Vect srcCoords; + srcCoords.push_back(src_a); + srcCoords.push_back(src_b); + + // Snap: (1.5 17,1 18,0.5 19) + Coordinate snp_a(1.5, 17); + Coordinate snp_b(1, 18); + Coordinate snp_c(0.5, 19); + Coordinate::ConstVect snpCoords; + snpCoords.push_back( &snp_a ); + snpCoords.push_back( &snp_b ); + snpCoords.push_back( &snp_c ); + + // Snap with tolerance of 0.8 + // (both first and second point could be snapped) + LineStringSnapper snapper(srcCoords, 0.8); + + // Expect: (0 0,1.5 17,1 18,0.5 19,0 20) + CoordsVectAptr ret(snapper.snapTo(snpCoords)); + ensure_equals(ret->size(), 5u); + ensure_equals(ret->operator[](0), src_a); // 0 0 + ensure_equals(ret->operator[](1), snp_a); // 1.5 17 + ensure_equals(ret->operator[](2), snp_b); // 1 18 + ensure_equals(ret->operator[](3), snp_c); // 0.5 19 + ensure_equals(ret->operator[](4), src_b); // 0 20 + } + } // namespace tut