Skip to content

Commit bdb181c

Browse files
committed
Add randomized shape GeometryTree tests
The tests is disabled at the moment since it fails regularly. Relates elastic#37206
1 parent 0ad1b36 commit bdb181c

File tree

2 files changed

+124
-7
lines changed

2 files changed

+124
-7
lines changed

server/src/test/java/org/elasticsearch/common/geo/GeometryTreeTests.java

+61-7
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,25 @@
1919
package org.elasticsearch.common.geo;
2020

2121
import org.elasticsearch.common.io.stream.BytesStreamOutput;
22+
import org.elasticsearch.geo.GeometryTestUtils;
2223
import org.elasticsearch.geometry.Geometry;
2324
import org.elasticsearch.geometry.Line;
2425
import org.elasticsearch.geometry.LinearRing;
2526
import org.elasticsearch.geometry.MultiPoint;
2627
import org.elasticsearch.geometry.Point;
2728
import org.elasticsearch.geometry.Polygon;
2829
import org.elasticsearch.geometry.Rectangle;
30+
import org.elasticsearch.index.mapper.GeoShapeIndexer;
2931
import org.elasticsearch.test.ESTestCase;
3032

3133
import java.io.IOException;
3234
import java.util.Arrays;
3335
import java.util.Collections;
3436
import java.util.List;
37+
import java.util.function.Function;
3538

39+
import static org.elasticsearch.geo.GeometryTestUtils.fold;
40+
import static org.elasticsearch.geo.GeometryTestUtils.randomPoint;
3641
import static org.hamcrest.Matchers.equalTo;
3742

3843
public class GeometryTreeTests extends ESTestCase {
@@ -56,8 +61,8 @@ public void testRectangleShape() throws IOException {
5661

5762
assertThat(Extent.fromPoints(minX, minY, maxX, maxY), equalTo(reader.getExtent()));
5863
// encoder loses precision when casting to integer, so centroid is calculated using integer division here
59-
assertThat(reader.getCentroidX(), equalTo((double) ((minX + maxX)/2)));
60-
assertThat(reader.getCentroidY(), equalTo((double) ((minY + maxY)/2)));
64+
assertThat(reader.getCentroidX(), equalTo((double) ((minX + maxX) / 2)));
65+
assertThat(reader.getCentroidY(), equalTo((double) ((minY + maxY) / 2)));
6166

6267
// box-query touches bottom-left corner
6368
assertTrue(reader.intersects(Extent.fromPoints(minX - randomIntBetween(1, 10), minY - randomIntBetween(1, 10), minX, minY)));
@@ -117,8 +122,8 @@ public void testPacManPolygon() throws Exception {
117122

118123
// adapted from org.apache.lucene.geo.TestPolygon2D#testMultiPolygon
119124
public void testPolygonWithHole() throws Exception {
120-
Polygon polyWithHole = new Polygon(new LinearRing(new double[] { -50, 50, 50, -50, -50 }, new double[] { -50, -50, 50, 50, -50 }),
121-
Collections.singletonList(new LinearRing(new double[] { -10, 10, 10, -10, -10 }, new double[] { -10, -10, 10, 10, -10 })));
125+
Polygon polyWithHole = new Polygon(new LinearRing(new double[]{-50, 50, 50, -50, -50}, new double[]{-50, -50, 50, 50, -50}),
126+
Collections.singletonList(new LinearRing(new double[]{-10, 10, 10, -10, -10}, new double[]{-10, -10, 10, 10, -10})));
122127

123128
GeometryTreeWriter writer = new GeometryTreeWriter(polyWithHole, TestCoordinateEncoder.INSTANCE);
124129
BytesStreamOutput output = new BytesStreamOutput();
@@ -135,13 +140,13 @@ public void testPolygonWithHole() throws Exception {
135140
}
136141

137142
public void testCombPolygon() throws Exception {
138-
double[] px = {0, 10, 10, 20, 20, 30, 30, 40, 40, 50, 50, 0, 0};
139-
double[] py = {0, 0, 20, 20, 0, 0, 20, 20, 0, 0, 30, 30, 0};
143+
double[] px = {0, 10, 10, 20, 20, 30, 30, 40, 40, 50, 50, 0, 0};
144+
double[] py = {0, 0, 20, 20, 0, 0, 20, 20, 0, 0, 30, 30, 0};
140145

141146
double[] hx = {21, 21, 29, 29, 21};
142147
double[] hy = {1, 20, 20, 1, 1};
143148

144-
Polygon polyWithHole = new Polygon(new LinearRing(px, py), Collections.singletonList(new LinearRing(hx, hy)));
149+
Polygon polyWithHole = new Polygon(new LinearRing(px, py), Collections.singletonList(new LinearRing(hx, hy)));
145150
// test cell crossing poly
146151
GeometryTreeWriter writer = new GeometryTreeWriter(polyWithHole, TestCoordinateEncoder.INSTANCE);
147152
BytesStreamOutput output = new BytesStreamOutput();
@@ -217,4 +222,53 @@ public void testPacManPoints() throws Exception {
217222
GeometryTreeReader reader = new GeometryTreeReader(output.bytes().toBytesRef(), TestCoordinateEncoder.INSTANCE);
218223
assertTrue(reader.intersects(Extent.fromPoints(xMin, yMin, xMax, yMax)));
219224
}
225+
226+
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/37206")
227+
public void testRandomGeometryIntersection() throws IOException {
228+
int testPointCount = randomIntBetween(100, 200);
229+
Point[] testPoints = new Point[testPointCount];
230+
double extentSize = randomDoubleBetween(1, 10, true);
231+
boolean[] intersects = new boolean[testPointCount];
232+
for (int i = 0; i < testPoints.length; i++) {
233+
testPoints[i] = randomPoint(false);
234+
}
235+
236+
Geometry geometry = randomGeometryTreeGeometry();
237+
GeoShapeIndexer indexer = new GeoShapeIndexer(true, "test");
238+
geometry = indexer.prepareForIndexing(geometry);
239+
240+
for (int i = 0; i < testPointCount; i++) {
241+
int cur = i;
242+
intersects[cur] = fold(geometry, false, (g, s) -> s || intersects(g, testPoints[cur], extentSize));
243+
}
244+
245+
for (int i = 0; i < testPointCount; i++) {
246+
assertEquals(intersects[i], intersects(geometry, testPoints[i], extentSize));
247+
}
248+
}
249+
250+
private boolean intersects(Geometry g, Point p, double extentSize) throws IOException {
251+
// TODO: Make this independent from GeometryTree
252+
GeometryTreeWriter writer = new GeometryTreeWriter(g, GeoShapeCoordinateEncoder.INSTANCE);
253+
BytesStreamOutput output = new BytesStreamOutput();
254+
writer.writeTo(output);
255+
output.close();
256+
int xMin = GeoShapeCoordinateEncoder.INSTANCE.encodeX(Math.max(p.getX() - extentSize, -180.0));
257+
int xMax = GeoShapeCoordinateEncoder.INSTANCE.encodeX(Math.min(p.getX() + extentSize, 180.0));
258+
int yMin = GeoShapeCoordinateEncoder.INSTANCE.encodeY(Math.max(p.getY() - extentSize, -90));
259+
int yMax = GeoShapeCoordinateEncoder.INSTANCE.encodeY(Math.min(p.getY() + extentSize, 90));
260+
GeometryTreeReader reader = new GeometryTreeReader(output.bytes().toBytesRef(), GeoShapeCoordinateEncoder.INSTANCE);
261+
return reader.intersects(Extent.fromPoints(xMin, yMin, xMax, yMax));
262+
}
263+
264+
private static Geometry randomGeometryTreeGeometry() {
265+
@SuppressWarnings("unchecked") Function<Boolean, Geometry> geometry = ESTestCase.randomFrom(
266+
GeometryTestUtils::randomLine,
267+
GeometryTestUtils::randomPoint,
268+
GeometryTestUtils::randomPolygon,
269+
GeometryTestUtils::randomMultiLine,
270+
GeometryTestUtils::randomMultiPoint
271+
);
272+
return geometry.apply(false);
273+
}
220274
}

test/framework/src/main/java/org/elasticsearch/geo/GeometryTestUtils.java

+63
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.geo;
2121

2222
import org.apache.lucene.geo.GeoTestUtil;
23+
import org.elasticsearch.common.CheckedBiFunction;
2324
import org.elasticsearch.geometry.Circle;
2425
import org.elasticsearch.geometry.Geometry;
2526
import org.elasticsearch.geometry.GeometryCollection;
@@ -258,4 +259,66 @@ public MultiPoint visit(Rectangle rectangle) throws RuntimeException {
258259
}
259260
});
260261
}
262+
263+
/**
264+
* Preforms left fold operation on all primitive geometries (points, lines polygons, circles and rectangles).
265+
* All collection geometries are iterated depth first.
266+
*/
267+
public static <R, E extends Exception> R fold(Geometry geometry, R state, CheckedBiFunction<Geometry, R, R, E> operation) throws E {
268+
return geometry.visit(new GeometryVisitor<R, E>() {
269+
@Override
270+
public R visit(Circle circle) throws E {
271+
return operation.apply(geometry, state);
272+
}
273+
274+
@Override
275+
public R visit(GeometryCollection<?> collection) throws E {
276+
R ret = state;
277+
for (Geometry g : collection) {
278+
ret = fold(g, ret, operation);
279+
}
280+
return ret;
281+
}
282+
283+
@Override
284+
public R visit(Line line) throws E {
285+
return operation.apply(line, state);
286+
}
287+
288+
@Override
289+
public R visit(LinearRing ring) throws E {
290+
return operation.apply(ring, state);
291+
}
292+
293+
@Override
294+
public R visit(MultiLine multiLine) throws E {
295+
return visit((GeometryCollection<?>) multiLine);
296+
}
297+
298+
@Override
299+
public R visit(MultiPoint multiPoint) throws E {
300+
return visit((GeometryCollection<?>) multiPoint); }
301+
302+
@Override
303+
public R visit(MultiPolygon multiPolygon) throws E {
304+
return visit((GeometryCollection<?>) multiPolygon);
305+
}
306+
307+
@Override
308+
public R visit(Point point) throws E {
309+
return operation.apply(point, state);
310+
}
311+
312+
@Override
313+
public R visit(Polygon polygon) throws E {
314+
return operation.apply(polygon, state);
315+
}
316+
317+
@Override
318+
public R visit(Rectangle rectangle) throws E {
319+
return operation.apply(rectangle, state);
320+
}
321+
});
322+
}
323+
261324
}

0 commit comments

Comments
 (0)