Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use buffer hole erosion heuristic for rings #1117

Merged
merged 1 commit into from
Feb 5, 2025
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 @@ -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
Loading