Skip to content

Commit

Permalink
Use buffer hole erosion heuristic for rings (#1117)
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-jts authored Feb 5, 2025
1 parent 027df7d commit 040802a
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -216,9 +216,9 @@ private void addPolygon(Polygon p)

LinearRing shell = p.getExteriorRing();
Coordinate[] shellCoord = clean(shell.getCoordinates());
// optimization - don't bother computing buffer
// optimization - don't compute buffer
// if the polygon would be completely eroded
if (distance < 0.0 && isErodedCompletely(shell, distance))
if (distance < 0.0 && isRingFullyEroded(shell, false, distance))
return;
// don't attempt to buffer a polygon with too few distinct vertices
if (distance <= 0.0 && shellCoord.length < 3)
Expand All @@ -236,9 +236,9 @@ private void addPolygon(Polygon p)
LinearRing hole = p.getInteriorRingN(i);
Coordinate[] holeCoord = clean(hole.getCoordinates());

// optimization - don't bother computing buffer for this hole
// optimization - don't compute buffer for this hole
// if the hole would be completely covered
if (distance > 0.0 && isErodedCompletely(hole, -distance))
if (distance > 0.0 && isRingFullyEroded(hole, true, distance))
continue;

// Holes are topologically labelled opposite to the shell, since
Expand All @@ -255,14 +255,27 @@ private void addPolygon(Polygon p)

private void addRingBothSides(Coordinate[] coord, double distance)
{
addRingSide(coord, distance,
Position.LEFT,
Location.EXTERIOR, Location.INTERIOR);
/* Add the opposite side of the ring
*/
addRingSide(coord, distance,
Position.RIGHT,
Location.INTERIOR, Location.EXTERIOR);
/*
* (f "hole" side will be eroded completely, avoid generating it.
* This prevents hole artifacts (e.g. https://github.com/libgeos/geos/issues/1223)
*/
//-- distance is assumed positive, due to previous checks
boolean isHoleComputed = ! isRingFullyEroded(coord, CoordinateArrays.envelope(coord), true, distance);

boolean isCCW = isRingCCW(coord);

boolean isShellLeft = ! isCCW;
if (isShellLeft || isHoleComputed) {
addRingSide(coord, distance,
Position.LEFT,
Location.EXTERIOR, Location.INTERIOR);
}
boolean isShellRight = isCCW;
if (isShellRight || isHoleComputed) {
addRingSide(coord, distance,
Position.RIGHT,
Location.INTERIOR, Location.EXTERIOR);
}
}

/**
Expand Down Expand Up @@ -411,25 +424,32 @@ private static boolean hasPointOnBuffer(Coordinate[] inputRing, double distance,
* @param offsetDistance
* @return
*/
private static boolean isErodedCompletely(LinearRing ring, double bufferDistance)
private static boolean isRingFullyEroded(LinearRing ring, boolean isHole, double bufferDistance)
{
return isRingFullyEroded(ring.getCoordinates(), ring.getEnvelopeInternal(), isHole, bufferDistance);
}

private static boolean isRingFullyEroded(Coordinate[] ringCoord, Envelope ringEnv, boolean isHole, double bufferDistance)
{
Coordinate[] ringCoord = ring.getCoordinates();
// degenerate ring has no area
if (ringCoord.length < 4)
return bufferDistance < 0;
return true;

// important test to eliminate inverted triangle bug
// also optimizes erosion test for triangles
if (ringCoord.length == 4)
return isTriangleErodedCompletely(ringCoord, bufferDistance);

// if envelope is narrower than twice the buffer distance, ring is eroded
Envelope env = ring.getEnvelopeInternal();
double envMinDimension = Math.min(env.getHeight(), env.getWidth());
if (bufferDistance < 0.0
&& 2 * Math.abs(bufferDistance) > envMinDimension)
return true;

boolean isErodable =
( isHole && bufferDistance > 0) ||
(! isHole && bufferDistance < 0);

if (isErodable) {
//-- if envelope is narrower than twice the buffer distance, ring is eroded
double envMinDimension = Math.min(ringEnv.getHeight(), ringEnv.getWidth());
if (2 * Math.abs(bufferDistance) > envMinDimension)
return true;
}
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,17 @@ public void testPolygonQS_1KeepHoles() {
assertEquals(geom.getNumInteriorRing(), buf.getNumInteriorRing());
}

//See https://github.com/libgeos/geos/issues/1223
public void testRingHoleEroded() {
String wkt = "LINESTRING (25 44, 31 44, 32 38, 29 37, 25 37, 25 38, 24 40, 24 44, 25 44)";
checkBuffer(wkt, 100,
"POLYGON ((50.95 141.99, 70.09 136.04, 87.66 126.4, 102.96 113.44, 115.36 97.69, 124.38 79.78, 129.64 60.44, 130.64 54.44, 131.93 34.31, 129.16 14.34, 122.44 -4.68, 112.03 -21.96, 98.37 -36.8, 82.02 -48.59, 63.62 -56.87, 60.62 -57.87, 45.02 -61.71, 29 -63, 25 -63, 4.33 -60.84, -15.44 -54.46, -33.47 -44.12, -48.97 -30.29, -61.28 -13.55, -69.87 5.38, -70.87 8.38, -74.71 23.98, -76 40, -76 44, -74.08 63.51, -68.39 82.27, -59.15 99.56, -46.71 114.71, -31.56 127.15, -14.27 136.39, 4.49 142.08, 24 144, 31 144, 50.95 141.99))");
checkBuffer(wkt, 10,
"POLYGON ((15.06 35.53, 14.27 37.7, 14 40, 14 44, 14.19 45.95, 14.76 47.83, 15.69 49.56, 16.93 51.07, 18.44 52.31, 20.17 53.24, 22.05 53.81, 24 54, 31 54, 32.99 53.8, 34.91 53.2, 36.67 52.24, 38.2 50.94, 39.44 49.37, 40.34 47.58, 40.86 45.64, 41.86 39.64, 41.99 37.63, 41.72 35.63, 41.04 33.73, 40 32, 38.64 30.52, 37 29.34, 35.16 28.51, 32.16 27.51, 30.6 27.13, 29 27, 25 27, 23.05 27.19, 21.17 27.76, 19.44 28.69, 17.93 29.93, 16.69 31.44, 15.76 33.17, 15.19 35.05, 15.17 35.31, 15.06 35.53))");
checkBuffer(wkt, 2,
"POLYGON ((31.4 45.96, 31.78 45.84, 32.13 45.65, 32.44 45.39, 32.69 45.07, 32.87 44.72, 32.97 44.33, 33.97 38.33, 34 37.93, 33.94 37.53, 33.81 37.15, 33.6 36.8, 33.33 36.5, 33 36.27, 32.63 36.1, 29.63 35.1, 29.32 35.03, 29 35, 25 35, 24.61 35.04, 24.23 35.15, 23.89 35.34, 23.59 35.59, 23.34 35.89, 23.15 36.23, 23.04 36.61, 23 37, 23 37.53, 22.21 39.11, 22.05 39.54, 22 40, 22 44, 22.04 44.39, 22.15 44.77, 22.34 45.11, 22.59 45.41, 22.89 45.66, 23.23 45.85, 23.61 45.96, 24 46, 31 46, 31.4 45.96), (26 40.47, 26.74 39, 28.68 39, 29.75 39.36, 29.31 42, 26 42, 26 40.47))");
}

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

private static BufferParameters bufParamRoundMitre(double mitreLimit) {
Expand Down

0 comments on commit 040802a

Please sign in to comment.