GEO: Switch to using GeoTestUtil to generate random geo shapes (#44635)

Switches to more robust way of generating random test geometries by
reusing lucene's GeoTestUtil. Removes duplicate random geometry
generators by moving them to the test framework.

Closes #37278
This commit is contained in:
Igor Motov 2019-07-22 08:51:03 -04:00
parent 9524d71159
commit 9338fc8536
11 changed files with 206 additions and 283 deletions

View File

@ -28,10 +28,7 @@ import org.elasticsearch.test.AbstractWireTestCase;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
abstract class BaseGeometryTestCase<T extends Geometry> extends AbstractWireTestCase<T> {
@ -131,123 +128,4 @@ abstract class BaseGeometryTestCase<T extends Geometry> extends AbstractWireTest
assertEquals("result", result);
}
public static double randomLat() {
return randomDoubleBetween(-90, 90, true);
}
public static double randomLon() {
return randomDoubleBetween(-180, 180, true);
}
public static Circle randomCircle(boolean hasAlt) {
if (hasAlt) {
return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDouble(),
randomDoubleBetween(0, 100, false));
} else {
return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDoubleBetween(0, 100, false));
}
}
public static Line randomLine() {
return randomLine(randomBoolean());
}
public static Line randomLine(boolean hasAlts) {
int size = randomIntBetween(2, 10);
double[] lats = new double[size];
double[] lons = new double[size];
double[] alts = hasAlts ? new double[size] : null;
for (int i = 0; i < size; i++) {
lats[i] = randomLat();
lons[i] = randomLon();
if (hasAlts) {
alts[i] = randomDouble();
}
}
if (hasAlts) {
return new Line(lats, lons, alts);
}
return new Line(lats, lons);
}
public static Point randomPoint() {
return randomPoint(randomBoolean());
}
public static Point randomPoint(boolean hasAlt) {
if (hasAlt) {
return new Point(randomLat(), randomLon(), randomDouble());
} else {
return new Point(randomLat(), randomLon());
}
}
public static LinearRing randomLinearRing(boolean hasAlt) {
int size = randomIntBetween(3, 10);
double[] lats = new double[size + 1];
double[] lons = new double[size + 1];
double[] alts;
if (hasAlt) {
alts = new double[size + 1];
} else {
alts = null;
}
for (int i = 0; i < size; i++) {
lats[i] = randomLat();
lons[i] = randomLon();
if (hasAlt) {
alts[i] = randomDouble();
}
}
lats[size] = lats[0];
lons[size] = lons[0];
if (hasAlt) {
alts[size] = alts[0];
return new LinearRing(lats, lons, alts);
} else {
return new LinearRing(lats, lons);
}
}
public static Polygon randomPolygon(boolean hasAlt) {
int size = randomIntBetween(0, 10);
List<LinearRing> holes = new ArrayList<>();
for (int i = 0; i < size; i++) {
holes.add(randomLinearRing(hasAlt));
}
if (holes.size() > 0) {
return new Polygon(randomLinearRing(hasAlt), holes);
} else {
return new Polygon(randomLinearRing(hasAlt));
}
}
public static Rectangle randomRectangle() {
double lat1 = randomLat();
double lat2 = randomLat();
double minLon = randomLon();
double maxLon = randomLon();
return new Rectangle(Math.min(lat1, lat2), Math.max(lat1, lat2), minLon, maxLon);
}
public static GeometryCollection<Geometry> randomGeometryCollection(boolean hasAlt) {
return randomGeometryCollection(0, hasAlt);
}
private static GeometryCollection<Geometry> randomGeometryCollection(int level, boolean hasAlt) {
int size = randomIntBetween(1, 10);
List<Geometry> shapes = new ArrayList<>();
for (int i = 0; i < size; i++) {
@SuppressWarnings("unchecked") Function<Boolean, Geometry> geometry = randomFrom(
BaseGeometryTestCase::randomCircle,
BaseGeometryTestCase::randomLine,
BaseGeometryTestCase::randomPoint,
BaseGeometryTestCase::randomPolygon,
hasAlt ? BaseGeometryTestCase::randomPoint : (b) -> randomRectangle(),
level < 3 ? (b) -> randomGeometryCollection(level + 1, b) : BaseGeometryTestCase::randomPoint // don't build too deep
);
shapes.add(geometry.apply(hasAlt));
}
return new GeometryCollection<>(shapes);
}
}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.geo.geometry;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geo.utils.GeographyValidator;
import org.elasticsearch.geo.utils.StandardValidator;
import org.elasticsearch.geo.utils.WellKnownText;
@ -31,7 +32,7 @@ import java.util.Collections;
public class GeometryCollectionTests extends BaseGeometryTestCase<GeometryCollection<Geometry>> {
@Override
protected GeometryCollection<Geometry> createTestInstance(boolean hasAlt) {
return randomGeometryCollection(hasAlt);
return GeometryTestUtils.randomGeometryCollection(hasAlt);
}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.geo.geometry;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geo.utils.GeographyValidator;
import org.elasticsearch.geo.utils.GeometryValidator;
import org.elasticsearch.geo.utils.StandardValidator;
@ -30,7 +31,7 @@ import java.text.ParseException;
public class LineTests extends BaseGeometryTestCase<Line> {
@Override
protected Line createTestInstance(boolean hasAlt) {
return randomLine(hasAlt);
return GeometryTestUtils.randomLine(hasAlt);
}
public void testBasicSerialization() throws IOException, ParseException {

View File

@ -19,6 +19,7 @@
package org.elasticsearch.geo.geometry;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geo.utils.GeographyValidator;
import org.elasticsearch.geo.utils.StandardValidator;
import org.elasticsearch.geo.utils.WellKnownText;
@ -36,7 +37,7 @@ public class MultiLineTests extends BaseGeometryTestCase<MultiLine> {
int size = randomIntBetween(1, 10);
List<Line> arr = new ArrayList<Line>();
for (int i = 0; i < size; i++) {
arr.add(randomLine(hasAlt));
arr.add(GeometryTestUtils.randomLine(hasAlt));
}
return new MultiLine(arr);
}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.geo.geometry;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geo.utils.GeographyValidator;
import org.elasticsearch.geo.utils.StandardValidator;
import org.elasticsearch.geo.utils.WellKnownText;
@ -37,7 +38,7 @@ public class MultiPointTests extends BaseGeometryTestCase<MultiPoint> {
int size = randomIntBetween(1, 10);
List<Point> arr = new ArrayList<>();
for (int i = 0; i < size; i++) {
arr.add(randomPoint(hasAlt));
arr.add(GeometryTestUtils.randomPoint(hasAlt));
}
return new MultiPoint(arr);
}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.geo.geometry;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geo.utils.GeographyValidator;
import org.elasticsearch.geo.utils.StandardValidator;
import org.elasticsearch.geo.utils.WellKnownText;
@ -36,7 +37,7 @@ public class MultiPolygonTests extends BaseGeometryTestCase<MultiPolygon> {
int size = randomIntBetween(1, 10);
List<Polygon> arr = new ArrayList<>();
for (int i = 0; i < size; i++) {
arr.add(randomPolygon(hasAlt));
arr.add(GeometryTestUtils.randomPolygon(hasAlt));
}
return new MultiPolygon(arr);
}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.geo.geometry;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geo.utils.GeographyValidator;
import org.elasticsearch.geo.utils.GeometryValidator;
import org.elasticsearch.geo.utils.StandardValidator;
@ -30,7 +31,7 @@ import java.text.ParseException;
public class PointTests extends BaseGeometryTestCase<Point> {
@Override
protected Point createTestInstance(boolean hasAlt) {
return randomPoint(hasAlt);
return GeometryTestUtils.randomPoint(hasAlt);
}
public void testBasicSerialization() throws IOException, ParseException {

View File

@ -19,6 +19,7 @@
package org.elasticsearch.geo.geometry;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geo.utils.GeographyValidator;
import org.elasticsearch.geo.utils.StandardValidator;
import org.elasticsearch.geo.utils.WellKnownText;
@ -30,7 +31,7 @@ import java.util.Collections;
public class PolygonTests extends BaseGeometryTestCase<Polygon> {
@Override
protected Polygon createTestInstance(boolean hasAlt) {
return randomPolygon(hasAlt);
return GeometryTestUtils.randomPolygon(hasAlt);
}
public void testBasicSerialization() throws IOException, ParseException {

View File

@ -19,6 +19,7 @@
package org.elasticsearch.geo.geometry;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geo.utils.GeographyValidator;
import org.elasticsearch.geo.utils.GeometryValidator;
import org.elasticsearch.geo.utils.StandardValidator;
@ -31,7 +32,7 @@ public class RectangleTests extends BaseGeometryTestCase<Rectangle> {
@Override
protected Rectangle createTestInstance(boolean hasAlt) {
assumeFalse("3rd dimension is not supported yet", hasAlt);
return randomRectangle();
return GeometryTestUtils.randomRectangle();
}
public void testBasicSerialization() throws IOException, ParseException {

View File

@ -23,28 +23,25 @@ import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geo.geometry.Circle;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geo.geometry.Geometry;
import org.elasticsearch.geo.geometry.GeometryCollection;
import org.elasticsearch.geo.geometry.Line;
import org.elasticsearch.geo.geometry.LinearRing;
import org.elasticsearch.geo.geometry.MultiLine;
import org.elasticsearch.geo.geometry.MultiPoint;
import org.elasticsearch.geo.geometry.MultiPolygon;
import org.elasticsearch.geo.geometry.Point;
import org.elasticsearch.geo.geometry.Polygon;
import org.elasticsearch.geo.geometry.Rectangle;
import org.elasticsearch.geo.utils.GeographyValidator;
import org.elasticsearch.test.AbstractXContentTestCase;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
import static org.elasticsearch.geo.GeometryTestUtils.randomCircle;
import static org.elasticsearch.geo.GeometryTestUtils.randomGeometryCollection;
import static org.elasticsearch.geo.GeometryTestUtils.randomLine;
import static org.elasticsearch.geo.GeometryTestUtils.randomMultiLine;
import static org.elasticsearch.geo.GeometryTestUtils.randomMultiPoint;
import static org.elasticsearch.geo.GeometryTestUtils.randomMultiPolygon;
import static org.elasticsearch.geo.GeometryTestUtils.randomPoint;
import static org.elasticsearch.geo.GeometryTestUtils.randomPolygon;
public class GeoJsonSerializationTests extends ESTestCase {
private static class GeometryWrapper implements ToXContentObject {
@ -119,7 +116,7 @@ public class GeoJsonSerializationTests extends ESTestCase {
}
public void testEnvelope() throws IOException {
xContentTest(GeoJsonSerializationTests::randomRectangle);
xContentTest(GeometryTestUtils::randomRectangle);
}
public void testGeometryCollection() throws IOException {
@ -129,143 +126,4 @@ public class GeoJsonSerializationTests extends ESTestCase {
public void testCircle() throws IOException {
xContentTest(() -> randomCircle(randomBoolean()));
}
public static double randomLat() {
return randomDoubleBetween(-90, 90, true);
}
public static double randomLon() {
return randomDoubleBetween(-180, 180, true);
}
public static Circle randomCircle(boolean hasAlt) {
if (hasAlt) {
return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDouble(),
randomDoubleBetween(0, 100, false));
} else {
return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDoubleBetween(0, 100, false));
}
}
public static Line randomLine(boolean hasAlts) {
int size = randomIntBetween(2, 10);
double[] lats = new double[size];
double[] lons = new double[size];
double[] alts = hasAlts ? new double[size] : null;
for (int i = 0; i < size; i++) {
lats[i] = randomLat();
lons[i] = randomLon();
if (hasAlts) {
alts[i] = randomDouble();
}
}
if (hasAlts) {
return new Line(lats, lons, alts);
}
return new Line(lats, lons);
}
public static Point randomPoint(boolean hasAlt) {
if (hasAlt) {
return new Point(randomLat(), randomLon(), randomDouble());
} else {
return new Point(randomLat(), randomLon());
}
}
public static MultiPoint randomMultiPoint(boolean hasAlt) {
int size = randomIntBetween(3, 10);
List<Point> points = new ArrayList<>();
for (int i = 0; i < size; i++) {
points.add(randomPoint(hasAlt));
}
return new MultiPoint(points);
}
public static MultiLine randomMultiLine(boolean hasAlt) {
int size = randomIntBetween(3, 10);
List<Line> lines = new ArrayList<>();
for (int i = 0; i < size; i++) {
lines.add(randomLine(hasAlt));
}
return new MultiLine(lines);
}
public static MultiPolygon randomMultiPolygon(boolean hasAlt) {
int size = randomIntBetween(3, 10);
List<Polygon> polygons = new ArrayList<>();
for (int i = 0; i < size; i++) {
polygons.add(randomPolygon(hasAlt));
}
return new MultiPolygon(polygons);
}
public static LinearRing randomLinearRing(boolean hasAlt) {
int size = randomIntBetween(3, 10);
double[] lats = new double[size + 1];
double[] lons = new double[size + 1];
double[] alts;
if (hasAlt) {
alts = new double[size + 1];
} else {
alts = null;
}
for (int i = 0; i < size; i++) {
lats[i] = randomLat();
lons[i] = randomLon();
if (hasAlt) {
alts[i] = randomDouble();
}
}
lats[size] = lats[0];
lons[size] = lons[0];
if (hasAlt) {
alts[size] = alts[0];
return new LinearRing(lats, lons, alts);
} else {
return new LinearRing(lats, lons);
}
}
public static Polygon randomPolygon(boolean hasAlt) {
int size = randomIntBetween(0, 10);
List<LinearRing> holes = new ArrayList<>();
for (int i = 0; i < size; i++) {
holes.add(randomLinearRing(hasAlt));
}
if (holes.size() > 0) {
return new Polygon(randomLinearRing(hasAlt), holes);
} else {
return new Polygon(randomLinearRing(hasAlt));
}
}
public static Rectangle randomRectangle() {
double lat1 = randomLat();
double lat2 = randomLat();
double minLon = randomLon();
double maxLon = randomLon();
return new Rectangle(Math.min(lat1, lat2), Math.max(lat1, lat2), minLon, maxLon);
}
public static GeometryCollection<Geometry> randomGeometryCollection(boolean hasAlt) {
return randomGeometryCollection(0, hasAlt);
}
private static GeometryCollection<Geometry> randomGeometryCollection(int level, boolean hasAlt) {
int size = randomIntBetween(1, 10);
List<Geometry> shapes = new ArrayList<>();
for (int i = 0; i < size; i++) {
@SuppressWarnings("unchecked") Function<Boolean, Geometry> geometry = randomFrom(
GeoJsonSerializationTests::randomCircle,
GeoJsonSerializationTests::randomLine,
GeoJsonSerializationTests::randomPoint,
GeoJsonSerializationTests::randomPolygon,
hasAlt ? GeoJsonSerializationTests::randomPoint : (b) -> randomRectangle(),
level < 3 ? (b) -> randomGeometryCollection(level + 1, b) : GeoJsonSerializationTests::randomPoint // don't build too deep
);
shapes.add(geometry.apply(hasAlt));
}
return new GeometryCollection<>(shapes);
}
}

View File

@ -0,0 +1,179 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.geo;
import org.apache.lucene.geo.GeoTestUtil;
import org.elasticsearch.geo.geometry.Circle;
import org.elasticsearch.geo.geometry.Geometry;
import org.elasticsearch.geo.geometry.GeometryCollection;
import org.elasticsearch.geo.geometry.Line;
import org.elasticsearch.geo.geometry.LinearRing;
import org.elasticsearch.geo.geometry.MultiLine;
import org.elasticsearch.geo.geometry.MultiPoint;
import org.elasticsearch.geo.geometry.MultiPolygon;
import org.elasticsearch.geo.geometry.Point;
import org.elasticsearch.geo.geometry.Polygon;
import org.elasticsearch.geo.geometry.Rectangle;
import org.elasticsearch.test.ESTestCase;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class GeometryTestUtils {
public static double randomLat() {
return GeoTestUtil.nextLatitude();
}
public static double randomLon() {
return GeoTestUtil.nextLongitude();
}
public static double randomAlt() {
return ESTestCase.randomDouble();
}
public static Circle randomCircle(boolean hasAlt) {
if (hasAlt) {
return new Circle(randomLat(), randomLon(), ESTestCase.randomDouble(),
ESTestCase.randomDoubleBetween(0, 100, false));
} else {
return new Circle(randomLat(), randomLon(), ESTestCase.randomDoubleBetween(0, 100, false));
}
}
public static Line randomLine(boolean hasAlts) {
int size = ESTestCase.randomIntBetween(2, 10);
double[] lats = new double[size];
double[] lons = new double[size];
double[] alts = hasAlts ? new double[size] : null;
for (int i = 0; i < size; i++) {
lats[i] = randomLat();
lons[i] = randomLon();
if (hasAlts) {
alts[i] = randomAlt();
}
}
if (hasAlts) {
return new Line(lats, lons, alts);
}
return new Line(lats, lons);
}
public static Point randomPoint() {
return randomPoint(ESTestCase.randomBoolean());
}
public static Point randomPoint(boolean hasAlt) {
if (hasAlt) {
return new Point(randomLat(), randomLon(), randomAlt());
} else {
return new Point(randomLat(), randomLon());
}
}
public static Polygon randomPolygon(boolean hasAlt) {
org.apache.lucene.geo.Polygon lucenePolygon = GeoTestUtil.nextPolygon();
if (lucenePolygon.numHoles() > 0) {
org.apache.lucene.geo.Polygon[] luceneHoles = lucenePolygon.getHoles();
List<LinearRing> holes = new ArrayList<>();
for (int i = 0; i < lucenePolygon.numHoles(); i++) {
holes.add(linearRing(luceneHoles[i], hasAlt));
}
return new Polygon(linearRing(lucenePolygon, hasAlt), holes);
}
return new Polygon(linearRing(lucenePolygon, hasAlt));
}
private static double[] randomAltRing(int size) {
double[] alts = new double[size];
for (int i = 0; i < size - 1; i++) {
alts[i] = randomAlt();
}
alts[size - 1] = alts[0];
return alts;
}
private static LinearRing linearRing(org.apache.lucene.geo.Polygon polygon, boolean generateAlts) {
if (generateAlts) {
return new LinearRing(polygon.getPolyLats(), polygon.getPolyLons(), randomAltRing(polygon.numPoints()));
} else {
return new LinearRing(polygon.getPolyLats(), polygon.getPolyLons());
}
}
public static Rectangle randomRectangle() {
org.apache.lucene.geo.Rectangle rectangle = GeoTestUtil.nextBox();
return new Rectangle(rectangle.minLat, rectangle.maxLat, rectangle.minLon, rectangle.maxLon);
}
public static MultiPoint randomMultiPoint(boolean hasAlt) {
int size = ESTestCase.randomIntBetween(3, 10);
List<Point> points = new ArrayList<>();
for (int i = 0; i < size; i++) {
points.add(randomPoint(hasAlt));
}
return new MultiPoint(points);
}
public static MultiLine randomMultiLine(boolean hasAlt) {
int size = ESTestCase.randomIntBetween(3, 10);
List<Line> lines = new ArrayList<>();
for (int i = 0; i < size; i++) {
lines.add(randomLine(hasAlt));
}
return new MultiLine(lines);
}
public static MultiPolygon randomMultiPolygon(boolean hasAlt) {
int size = ESTestCase.randomIntBetween(3, 10);
List<Polygon> polygons = new ArrayList<>();
for (int i = 0; i < size; i++) {
polygons.add(randomPolygon(hasAlt));
}
return new MultiPolygon(polygons);
}
public static GeometryCollection<Geometry> randomGeometryCollection(boolean hasAlt) {
return randomGeometryCollection(0, hasAlt);
}
private static GeometryCollection<Geometry> randomGeometryCollection(int level, boolean hasAlt) {
int size = ESTestCase.randomIntBetween(1, 10);
List<Geometry> shapes = new ArrayList<>();
for (int i = 0; i < size; i++) {
@SuppressWarnings("unchecked") Function<Boolean, Geometry> geometry = ESTestCase.randomFrom(
GeometryTestUtils::randomCircle,
GeometryTestUtils::randomLine,
GeometryTestUtils::randomPoint,
GeometryTestUtils::randomPolygon,
GeometryTestUtils::randomMultiLine,
GeometryTestUtils::randomMultiPoint,
GeometryTestUtils::randomMultiPolygon,
hasAlt ? GeometryTestUtils::randomPoint : (b) -> randomRectangle(),
level < 3 ? (b) -> randomGeometryCollection(level + 1, b) : GeometryTestUtils::randomPoint // don't build too deep
);
shapes.add(geometry.apply(hasAlt));
}
return new GeometryCollection<>(shapes);
}
}