@@ -216,9 +216,9 @@ private void addPolygon(Polygon p)
216216
217217 LinearRing shell = p .getExteriorRing ();
218218 Coordinate [] shellCoord = clean (shell .getCoordinates ());
219- // optimization - don't bother computing buffer
219+ // optimization - don't compute buffer
220220 // if the polygon would be completely eroded
221- if (distance < 0.0 && isErodedCompletely (shell , distance ))
221+ if (distance < 0.0 && isRingFullyEroded (shell , false , distance ))
222222 return ;
223223 // don't attempt to buffer a polygon with too few distinct vertices
224224 if (distance <= 0.0 && shellCoord .length < 3 )
@@ -236,9 +236,9 @@ private void addPolygon(Polygon p)
236236 LinearRing hole = p .getInteriorRingN (i );
237237 Coordinate [] holeCoord = clean (hole .getCoordinates ());
238238
239- // optimization - don't bother computing buffer for this hole
239+ // optimization - don't compute buffer for this hole
240240 // if the hole would be completely covered
241- if (distance > 0.0 && isErodedCompletely (hole , - distance ))
241+ if (distance > 0.0 && isRingFullyEroded (hole , true , distance ))
242242 continue ;
243243
244244 // Holes are topologically labelled opposite to the shell, since
@@ -255,14 +255,27 @@ private void addPolygon(Polygon p)
255255
256256 private void addRingBothSides (Coordinate [] coord , double distance )
257257 {
258- addRingSide (coord , distance ,
259- Position .LEFT ,
260- Location .EXTERIOR , Location .INTERIOR );
261- /* Add the opposite side of the ring
262- */
263- addRingSide (coord , distance ,
264- Position .RIGHT ,
265- Location .INTERIOR , Location .EXTERIOR );
258+ /*
259+ * (f "hole" side will be eroded completely, avoid generating it.
260+ * This prevents hole artifacts (e.g. https://github.com/libgeos/geos/issues/1223)
261+ */
262+ //-- distance is assumed positive, due to previous checks
263+ boolean isHoleComputed = ! isRingFullyEroded (coord , CoordinateArrays .envelope (coord ), true , distance );
264+
265+ boolean isCCW = isRingCCW (coord );
266+
267+ boolean isShellLeft = ! isCCW ;
268+ if (isShellLeft || isHoleComputed ) {
269+ addRingSide (coord , distance ,
270+ Position .LEFT ,
271+ Location .EXTERIOR , Location .INTERIOR );
272+ }
273+ boolean isShellRight = isCCW ;
274+ if (isShellRight || isHoleComputed ) {
275+ addRingSide (coord , distance ,
276+ Position .RIGHT ,
277+ Location .INTERIOR , Location .EXTERIOR );
278+ }
266279 }
267280
268281 /**
@@ -411,25 +424,32 @@ private static boolean hasPointOnBuffer(Coordinate[] inputRing, double distance,
411424 * @param offsetDistance
412425 * @return
413426 */
414- private static boolean isErodedCompletely (LinearRing ring , double bufferDistance )
427+ private static boolean isRingFullyEroded (LinearRing ring , boolean isHole , double bufferDistance )
428+ {
429+ return isRingFullyEroded (ring .getCoordinates (), ring .getEnvelopeInternal (), isHole , bufferDistance );
430+ }
431+
432+ private static boolean isRingFullyEroded (Coordinate [] ringCoord , Envelope ringEnv , boolean isHole , double bufferDistance )
415433 {
416- Coordinate [] ringCoord = ring .getCoordinates ();
417434 // degenerate ring has no area
418435 if (ringCoord .length < 4 )
419- return bufferDistance < 0 ;
436+ return true ;
420437
421438 // important test to eliminate inverted triangle bug
422439 // also optimizes erosion test for triangles
423440 if (ringCoord .length == 4 )
424441 return isTriangleErodedCompletely (ringCoord , bufferDistance );
425442
426- // if envelope is narrower than twice the buffer distance, ring is eroded
427- Envelope env = ring .getEnvelopeInternal ();
428- double envMinDimension = Math .min (env .getHeight (), env .getWidth ());
429- if (bufferDistance < 0.0
430- && 2 * Math .abs (bufferDistance ) > envMinDimension )
431- return true ;
432-
443+ boolean isErodable =
444+ ( isHole && bufferDistance > 0 ) ||
445+ (! isHole && bufferDistance < 0 );
446+
447+ if (isErodable ) {
448+ //-- if envelope is narrower than twice the buffer distance, ring is eroded
449+ double envMinDimension = Math .min (ringEnv .getHeight (), ringEnv .getWidth ());
450+ if (2 * Math .abs (bufferDistance ) > envMinDimension )
451+ return true ;
452+ }
433453 return false ;
434454 }
435455
0 commit comments