Skip to content

Commit 7982423

Browse files
committed
Tessellator should fail if it cannot process a hole (#14974)
Tessellator throws an IAE exception if it cannot process a hole.
1 parent 586dc10 commit 7982423

File tree

5 files changed

+75
-10
lines changed

5 files changed

+75
-10
lines changed

lucene/CHANGES.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ Bug Fixes
209209
* GITHUB#15106: Fix infinite loop when RefCountedSharedArena's underlying
210210
Arena#close fails due to concurrent usage of segments. (Uwe Schindler)
211211

212+
* GITHUB#14974: Lucene tessellator now always fails if it cannot process a hole instead of just ignoring it.
213+
(Ignacio Vera)
214+
212215
Build
213216
---------------------
214217
* Upgrade forbiddenapis to version 3.9. (Uwe Schindler)

lucene/core/src/java/org/apache/lucene/geo/Tessellator.java

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
* <li>Requires valid polygons:
4444
* <ul>
4545
* <li>No self intersections
46+
* <li>Holes must be inside the polygon
4647
* <li>Holes may only touch at one vertex
4748
* <li>Polygon must have an area (e.g., no "line" boxes)
4849
* <li>sensitive to overflow (e.g, subatomic values such as E-200 can cause unexpected
@@ -339,16 +340,31 @@ private static Node eliminateHoles(
339340
holeMinY = holePoly.minY;
340341
holeMaxY = holePoly.maxY;
341342
}
342-
eliminateHole(holeNode, outerNode, holeMinX, holeMaxX, holeMinY, holeMaxY);
343-
// Filter the new polygon.
344-
outerNode = filterPoints(outerNode, outerNode.next);
343+
if (eliminateHole(holeNode, outerNode, holeMinX, holeMaxX, holeMinY, holeMaxY)) {
344+
// Filter the new polygon.
345+
outerNode = filterPoints(outerNode, outerNode.next);
346+
} else {
347+
// we couldn't find a point to the left of the hole's leftmost point, the point
348+
// is not inside the polygon.
349+
String polygon;
350+
if (h instanceof Polygon holePoly) {
351+
polygon = Polygon.verticesToGeoJSON(holePoly.getPolyLats(), holePoly.getPolyLons());
352+
} else {
353+
XYPolygon holePoly = (XYPolygon) h;
354+
polygon = XYPolygon.verticesToGeoJSON(holePoly.getPolyX(), holePoly.getPolyY());
355+
}
356+
if (polygon.length() > 100) {
357+
polygon = polygon.substring(0, 100) + "...";
358+
}
359+
throw new IllegalArgumentException("Illegal hole detected: " + polygon);
360+
}
345361
}
346362
// Return a pointer to the list.
347363
return outerNode;
348364
}
349365

350366
/** Finds a bridge between vertices that connects a hole with an outer ring, and links it */
351-
private static void eliminateHole(
367+
private static boolean eliminateHole(
352368
final Node holeNode,
353369
Node outerNode,
354370
double holeMinX,
@@ -359,7 +375,7 @@ private static void eliminateHole(
359375
// Attempt to merge the hole using a common point between if it exists.
360376
if (maybeMergeHoleWithSharedVertices(
361377
holeNode, outerNode, holeMinX, holeMaxX, holeMinY, holeMaxY)) {
362-
return;
378+
return true;
363379
}
364380
// Attempt to find a logical bridge between the HoleNode and OuterNode.
365381
outerNode = fetchHoleBridge(holeNode, outerNode);
@@ -374,6 +390,9 @@ private static void eliminateHole(
374390
Node node = splitPolygon(outerNode, holeNode, fromPolygon);
375391
// Filter the split nodes.
376392
filterPoints(node, node.next);
393+
return true;
394+
} else {
395+
return false;
377396
}
378397
}
379398

lucene/core/src/java/org/apache/lucene/geo/XYPolygon.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,19 @@ public boolean equals(Object obj) {
184184
return true;
185185
}
186186

187+
public static String verticesToGeoJSON(final float[] xs, final float[] ys) {
188+
StringBuilder sb = new StringBuilder();
189+
sb.append('[');
190+
for (int i = 0; i < xs.length; i++) {
191+
sb.append("[").append(xs[i]).append(", ").append(ys[i]).append("]");
192+
if (i != xs.length - 1) {
193+
sb.append(", ");
194+
}
195+
}
196+
sb.append(']');
197+
return sb.toString();
198+
}
199+
187200
@Override
188201
public String toString() {
189202
StringBuilder sb = new StringBuilder();

lucene/core/src/test/org/apache/lucene/document/TestShapeDocValues.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public void testSimpleDocValue() throws Exception {
4949
assertEquals(
5050
dv.relate(LatLonGeometry.create(new Rectangle(-0.25, -0.24, -3.8, -3.7))),
5151
PointValues.Relation.CELL_OUTSIDE_QUERY);
52-
assertNotEquals(
52+
assertEquals(
5353
dv.relate(LatLonGeometry.create(new Rectangle(-1.2, 1.2, -1.5, 1.7))),
5454
PointValues.Relation.CELL_CROSSES_QUERY);
5555
}
@@ -214,15 +214,14 @@ public void testVariableValueSizes() throws Exception {
214214
}
215215

216216
private Polygon getTestPolygonWithHole() {
217-
Polygon poly = GeoTestUtil.createRegularPolygon(0.0, 0.0, 100000, 7);
217+
Polygon poly = GeoTestUtil.createRegularPolygon(0.0, 0.0, 500000, 7);
218218
Polygon inner =
219219
new Polygon(
220220
new double[] {-1.0, -1.0, 0.5, 1.0, 1.0, 0.5, -1.0},
221221
new double[] {1.0, -1.0, -0.5, -1.0, 1.0, 0.5, 1.0});
222222
Polygon inner2 =
223223
new Polygon(
224-
new double[] {-1.0, -1.0, 0.5, 1.0, 1.0, 0.5, -1.0},
225-
new double[] {-2.0, -4.0, -3.5, -4.0, -2.0, -2.5, -2.0});
224+
new double[] {-1.0, -1.0, 1.0, 1.0, -1.0}, new double[] {-2.0, -4.0, -4.0, -2.0, -2.0});
226225

227226
return new Polygon(poly.getPolyLats(), poly.getPolyLons(), inner, inner2);
228227
}

lucene/core/src/test/org/apache/lucene/geo/TestTessellator.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public void testLinesIntersect() {
5757
}
5858

5959
public void testSimpleTessellation() throws Exception {
60-
Polygon poly = GeoTestUtil.createRegularPolygon(0.0, 0.0, 100000, 100000);
60+
Polygon poly = GeoTestUtil.createRegularPolygon(0.0, 0.0, 1000000, 100000);
6161
Polygon inner =
6262
new Polygon(
6363
new double[] {-1.0, -1.0, 0.5, 1.0, 1.0, 0.5, -1.0},
@@ -199,6 +199,37 @@ public void testInvalidPolygonCollinear() throws Exception {
199199
assertEquals("at least three non-collinear points required", ex.getMessage());
200200
}
201201

202+
public void testInvalidPolygonHoleDisjoint() throws Exception {
203+
String wkt =
204+
"POLYGON((172.42 51.3, 180.0 51.3, 180.0 71.4, 172.42 71.4, 172.42 51.3), "
205+
+ "(-180.0 51.3, -129.99 51.3, -129.99 71.4, -180.0 71.4, -180.0 51.3))";
206+
Polygon polygon = (Polygon) SimpleWKTShapeParser.parse(wkt);
207+
IllegalArgumentException ex =
208+
expectThrows(IllegalArgumentException.class, () -> Tessellator.tessellate(polygon, true));
209+
assertEquals(
210+
"Illegal hole detected: [[-180.0, 51.3], [-129.99, 51.3], [-129.99, 71.4], [-180.0, 71.4], [-180.0, 51.3]]",
211+
ex.getMessage());
212+
213+
float[] xs = new float[polygon.numPoints()];
214+
float[] ys = new float[polygon.numPoints()];
215+
for (int i = 0; i < polygon.numPoints(); i++) {
216+
xs[i] = (float) polygon.getPolyLon(i);
217+
ys[i] = (float) polygon.getPolyLat(i);
218+
}
219+
float[] xsHole = new float[polygon.getHole(0).numPoints()];
220+
float[] ysHole = new float[polygon.getHole(0).numPoints()];
221+
for (int i = 0; i < polygon.getHole(0).numPoints(); i++) {
222+
xsHole[i] = (float) polygon.getHole(0).getPolyLon(i);
223+
ysHole[i] = (float) polygon.getHole(0).getPolyLat(i);
224+
}
225+
XYPolygon xyPolygon = new XYPolygon(xs, ys, new XYPolygon(xsHole, ysHole));
226+
ex =
227+
expectThrows(IllegalArgumentException.class, () -> Tessellator.tessellate(xyPolygon, true));
228+
assertEquals(
229+
"Illegal hole detected: [[-180.0, 51.3], [-129.99, 51.3], [-129.99, 71.4], [-180.0, 71.4], [-180.0, 51.3]]",
230+
ex.getMessage());
231+
}
232+
202233
public void testComplexPolygon01() throws Exception {
203234
String wkt =
204235
"POLYGON((58.8792517 54.9160937, 58.8762477 54.9154524, 58.8735011 54.9140217, 58.8726428 54.9127389, 58.8731146 54.9122507, 58.8741877 54.9120482, 58.8771918 54.9117028, 58.88011 54.913331, 58.8801175 54.9137036, 58.8805885 54.9143186, 58.8807109 54.9148604, 58.88011 54.915551, 58.8792517 54.9160937), "

0 commit comments

Comments
 (0)