Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.Location;
import org.locationtech.jts.geom.Polygon;
import org.locationtech.jts.geom.Position;
import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.geom.TopologyException;
Expand Down Expand Up @@ -176,10 +177,42 @@ public Geometry buffer(Geometry g, double distance)
return createEmptyResultGeometry();
}

/**
* Heuristic to remove artifacts caused by topology robustness problems
* or buffer curve generation anomalies.
* Uses fact that for distance > 0 single-element inputs must create single element buffers.
* This does not hold if distance <= 0;
* distance = 0 can create multipolygon results due to topology collapse,
* and distance < 0 may erode polygons so they are disconnected.
*/
if (distance > 0 && g.getNumGeometries() == 1 && resultPolyList.size() > 1) {
resultPolyList = keepLargestArea(resultPolyList);
}

Geometry resultGeom = geomFact.buildGeometry(resultPolyList);
return resultGeom;
}

private static List<Polygon> keepLargestArea(List<Polygon> polyList) {
Polygon largest = findLargestArea(polyList);
List<Polygon> largestPolyList = new ArrayList<Polygon>();
largestPolyList.add(largest);
return largestPolyList;
}

private static Polygon findLargestArea(List<Polygon> polyList) {
double largestArea = 0;
Polygon largest = null;
for (Polygon poly : polyList) {
double polyArea = poly.getArea();
if (largest == null || polyArea > largestArea) {
largest = poly;
largestArea = polyArea;
}
}
return largest;
}

private Noder getNoder(PrecisionModel precisionModel)
{
if (workingNoder != null) return workingNoder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,20 @@ public void testBufferByZeroKeepsAllElements() {
"MULTIPOLYGON (((24 95.239, 24 96, 24 99, 24.816 99, 24 95.239)), ((3 90, 3 93, 3 96, 3 99, 21 99, 21 96, 21 93, 21 90, 3 90)))");
}

//-- see https://github.com/libgeos/geos/issues/1321
public void testArtifactsRemovedFromLineBuffer() {
String wkt = "LINESTRING (640734.77510795 216861.236982439, 640733.832969266 216862.938074642, 640732.814325629 216865.039674844, 640731.225095251 216869.012141189, 640729.979984761 216871.879095724, 640729.445974092 216873.02148841, 640729.002794006 216873.679857725, 640728.952197105 216873.745389857, 640728.676962154 216874.089814544)";
checkBufferNumGeometries(wkt, 100, 1);
}

//-- see https://github.com/r-spatial/sf/issues/2552
public void testArtifactsRemovedFromLineBufferFlatEnd() {
String wkt = "LINESTRING (245184.6 6045650, 245193.3 6045649, 245201.7 6045651, 245204.3 6045653)";
Geometry geom = read(wkt);
Geometry buf = BufferOp.bufferOp(geom, 50, bufParamEndCapFlat());
assertEquals(1, buf.getNumGeometries());
}

//===================================================

private static BufferParameters bufParamRoundMitre(double mitreLimit) {
Expand All @@ -677,6 +691,12 @@ private static BufferParameters bufParamRoundMitre(double mitreLimit) {
return param;
}

private static BufferParameters bufParamEndCapFlat() {
BufferParameters param = new BufferParameters();
param.setEndCapStyle(BufferParameters.CAP_FLAT);
return param;
}

private void checkBuffer(String wkt, double dist, BufferParameters param, String wktExpected) {
Geometry geom = read(wkt);
Geometry result = BufferOp.bufferOp(geom, dist, param);
Expand Down Expand Up @@ -706,7 +726,7 @@ private void checkBufferHasHole(String wkt, double dist, boolean isHoleExpected)
private void checkBufferNumGeometries(String wkt, double dist, int numExpected) {
Geometry a = read(wkt);
Geometry result = a.buffer(dist);
assertTrue(numExpected == result.getNumGeometries());
assertEquals(numExpected, result.getNumGeometries());
}

private boolean hasHole(Geometry geom) {
Expand Down