Skip to content

Commit fc9ed7d

Browse files
authored
Fix BufferOp to handle geometries with all-invalid coordinate lists (#1165)
1 parent 1b5e7a3 commit fc9ed7d

File tree

2 files changed

+84
-16
lines changed

2 files changed

+84
-16
lines changed

modules/core/src/main/java/org/locationtech/jts/operation/buffer/BufferCurveSetBuilder.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ private void addLineString(LineString line)
175175

176176
Coordinate[] coord = clean(line.getCoordinates());
177177

178+
//-- skip if no valid coordinates
179+
if (coord.length == 0)
180+
return;
181+
178182
/**
179183
* Rings (closed lines) are generated with a continuous curve,
180184
* with no end arcs. This produces better quality linework,
@@ -215,17 +219,23 @@ private void addPolygon(Polygon p)
215219
}
216220

217221
LinearRing shell = p.getExteriorRing();
218-
Coordinate[] shellCoord = clean(shell.getCoordinates());
219222
// optimization - don't compute buffer
220223
// if the polygon would be completely eroded
221224
if (distance < 0.0 && isRingFullyEroded(shell, false, distance))
222225
return;
226+
227+
Coordinate[] shellCoords = clean(shell.getCoordinates());
228+
229+
//-- skip if no valid coordinates
230+
if (shellCoords.length == 0)
231+
return;
232+
223233
// don't attempt to buffer a polygon with too few distinct vertices
224-
if (distance <= 0.0 && shellCoord.length < 3)
234+
if (distance <= 0.0 && shellCoords.length < 3)
225235
return;
226236

227237
addPolygonRingSide(
228-
shellCoord,
238+
shellCoords,
229239
offsetDistance,
230240
offsetSide,
231241
Location.EXTERIOR,
@@ -234,18 +244,23 @@ private void addPolygon(Polygon p)
234244
for (int i = 0; i < p.getNumInteriorRing(); i++) {
235245

236246
LinearRing hole = p.getInteriorRingN(i);
237-
Coordinate[] holeCoord = clean(hole.getCoordinates());
238-
247+
239248
// optimization - don't compute buffer for this hole
240249
// if the hole would be completely covered
241250
if (distance > 0.0 && isRingFullyEroded(hole, true, distance))
242251
continue;
243252

253+
Coordinate[] holeCoords = clean(hole.getCoordinates());
254+
255+
//-- skip if no valid coordinates
256+
if (holeCoords.length == 0)
257+
continue;
258+
244259
// Holes are topologically labelled opposite to the shell, since
245260
// the interior of the polygon lies on their opposite side
246261
// (on the left, if the hole is oriented CCW)
247262
addPolygonRingSide(
248-
holeCoord,
263+
holeCoords,
249264
offsetDistance,
250265
Position.opposite(offsetSide),
251266
Location.INTERIOR,

modules/core/src/test/java/org/locationtech/jts/operation/buffer/BufferTest.java

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import org.locationtech.jts.geom.Geometry;
1616
import org.locationtech.jts.geom.GeometryCollection;
1717
import org.locationtech.jts.geom.GeometryFactory;
18+
import org.locationtech.jts.geom.LineString;
19+
import org.locationtech.jts.geom.LinearRing;
1820
import org.locationtech.jts.geom.Polygon;
1921
import org.locationtech.jts.geom.PrecisionModel;
2022

@@ -533,18 +535,18 @@ public void testBowtiePolygonHoleLargestAreaRetained() {
533535

534536
public void testPolygon4NegBufferEmpty() {
535537
String wkt = "POLYGON ((666360.09 429614.71, 666344.4 429597.12, 666358.47 429584.52, 666374.5 429602.33, 666360.09 429614.71))";
536-
checkBufferEmpty(wkt, -9, false);
537-
checkBufferEmpty(wkt, -10, true);
538-
checkBufferEmpty(wkt, -15, true);
539-
checkBufferEmpty(wkt, -18, true);
538+
checkBufferPolygonEmpty(wkt, -9, false);
539+
checkBufferPolygonEmpty(wkt, -10, true);
540+
checkBufferPolygonEmpty(wkt, -15, true);
541+
checkBufferPolygonEmpty(wkt, -18, true);
540542
}
541543

542544
public void testPolygon5NegBufferEmpty() {
543545
String wkt = "POLYGON ((6 20, 16 20, 21 9, 9 0, 0 10, 6 20))";
544-
checkBufferEmpty(wkt, -8, false);
545-
checkBufferEmpty(wkt, -8.6, true);
546-
checkBufferEmpty(wkt, -9.6, true);
547-
checkBufferEmpty(wkt, -11, true);
546+
checkBufferPolygonEmpty(wkt, -8, false);
547+
checkBufferPolygonEmpty(wkt, -8.6, true);
548+
checkBufferPolygonEmpty(wkt, -9.6, true);
549+
checkBufferPolygonEmpty(wkt, -11, true);
548550
}
549551

550552
public void testPolygonHole5BufferNoHole() {
@@ -682,8 +684,54 @@ public void testArtifactsRemovedFromLineBufferFlatEnd() {
682684
assertEquals(1, buf.getNumGeometries());
683685
}
684686

687+
//--------------------------------
688+
689+
public void testInvalidCoordPoint() {
690+
// works for Inf ordinates as well
691+
Geometry geom = read("POINT (NaN NaN)");
692+
checkBufferPolygonEmpty(geom, 1, true);
693+
}
694+
695+
public void testInvalidCoordsLine() {
696+
// works for Inf ordinates as well
697+
Geometry geom = read("LINESTRING (NaN NaN, NaN NaN)");
698+
checkBufferPolygonEmpty(geom, 1, true);
699+
}
700+
701+
public void testInvalidCoordShell() {
702+
// using Inf ordinates creates a valid ring with equal endpoints
703+
// this would be simpler if JTS WKT supported Inf
704+
Geometry geom = getGeometryFactory().createPolygon( infCoords(5) );
705+
checkBufferPolygonEmpty(geom, 1, true);
706+
}
707+
708+
public void testInvalidCoordHole() {
709+
// using Inf ordinates creates a valid ring with equal endpoints
710+
// this would be simpler if JTS WKT supported Inf
711+
Polygon poly = (Polygon) read("POLYGON ((1 9, 9 9, 9 1, 1 1, 1 9), (3 7, 7 7, 7 3, 3 3, 3 7))");
712+
713+
LinearRing shell = poly.getExteriorRing();
714+
LinearRing hole = poly.getInteriorRingN(0);
715+
LinearRing infHole = getGeometryFactory().createLinearRing( infCoords(5) );
716+
Geometry polyInfHole = getGeometryFactory().createPolygon(
717+
shell, new LinearRing[] { hole, infHole } );
718+
719+
Geometry bufferOrig = poly.buffer(1);
720+
Geometry bufferInf = polyInfHole.buffer(1);
721+
// buffers should be same since inf hole is skipped
722+
checkEqual(bufferOrig, bufferInf);
723+
}
724+
685725
//===================================================
686726

727+
private static Coordinate[] infCoords(int size) {
728+
Coordinate[] coords = new Coordinate[size];
729+
for (int i = 0; i < size; i++) {
730+
coords[i] = new Coordinate(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
731+
}
732+
return coords;
733+
}
734+
687735
private static BufferParameters bufParamRoundMitre(double mitreLimit) {
688736
BufferParameters param = new BufferParameters();
689737
param.setJoinStyle(BufferParameters.JOIN_MITRE);
@@ -711,9 +759,14 @@ private void checkBuffer(String wkt, double dist, String wktExpected) {
711759
checkEqual(expected, result, 0.01);
712760
}
713761

714-
private void checkBufferEmpty(String wkt, double dist, boolean isEmptyExpected) {
762+
private void checkBufferPolygonEmpty(String wkt, double dist, boolean isEmptyExpected) {
715763
Geometry a = read(wkt);
716-
Geometry result = a.buffer(dist);
764+
checkBufferPolygonEmpty(a, dist, isEmptyExpected);
765+
}
766+
767+
private void checkBufferPolygonEmpty(Geometry geom, double dist, boolean isEmptyExpected) {
768+
Geometry result = geom.buffer(dist);
769+
assertTrue(result instanceof Polygon);
717770
assertTrue(isEmptyExpected == result.isEmpty());
718771
}
719772

0 commit comments

Comments
 (0)