[GEO] Add support to ShapeBuilders for building Lucene geometry (#35707)
* [GEO] Add support to ShapeBuilders for building Lucene geometry This commit adds support for building lucene geometry from the ShapeBuilders. This is needed for integrating LatLonShape as the primary indexing approach for geo_shape field types. All unit and integration tests are updated to add randomization for testing both jts/s4j shapes and lucene shapes.
This commit is contained in:
parent
d9c6986b75
commit
3bee25cb70
|
@ -233,7 +233,11 @@ public class GeoUtils {
|
|||
* @return The normalized longitude.
|
||||
*/
|
||||
public static double normalizeLon(double lon) {
|
||||
return centeredModulus(lon, 360);
|
||||
if (lon > 180d || lon <= -180d) {
|
||||
lon = centeredModulus(lon, 360);
|
||||
}
|
||||
// avoid -0.0
|
||||
return lon + 0d;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -250,13 +254,16 @@ public class GeoUtils {
|
|||
* @see #normalizePoint(GeoPoint)
|
||||
*/
|
||||
public static double normalizeLat(double lat) {
|
||||
lat = centeredModulus(lat, 360);
|
||||
if (lat < -90) {
|
||||
lat = -180 - lat;
|
||||
} else if (lat > 90) {
|
||||
lat = 180 - lat;
|
||||
if (lat > 90d || lat < -90d) {
|
||||
lat = centeredModulus(lat, 360);
|
||||
if (lat < -90) {
|
||||
lat = -180 - lat;
|
||||
} else if (lat > 90) {
|
||||
lat = 180 - lat;
|
||||
}
|
||||
}
|
||||
return lat;
|
||||
// avoid -0.0
|
||||
return lat + 0d;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -159,10 +159,15 @@ public class CircleBuilder extends ShapeBuilder<Circle, CircleBuilder> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Circle build() {
|
||||
public Circle buildS4J() {
|
||||
return SPATIAL_CONTEXT.makeCircle(center.x, center.y, 360 * radius / unit.getEarthCircumference());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object buildLucene() {
|
||||
throw new UnsupportedOperationException("CIRCLE geometry is not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoShapeType type() {
|
||||
return TYPE;
|
||||
|
|
|
@ -108,10 +108,15 @@ public class EnvelopeBuilder extends ShapeBuilder<Rectangle, EnvelopeBuilder> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Rectangle build() {
|
||||
public Rectangle buildS4J() {
|
||||
return SPATIAL_CONTEXT.makeRectangle(topLeft.x, bottomRight.x, bottomRight.y, topLeft.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.apache.lucene.geo.Rectangle buildLucene() {
|
||||
return new org.apache.lucene.geo.Rectangle(bottomRight.y, topLeft.y, topLeft.x, bottomRight.x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoShapeType type() {
|
||||
return TYPE;
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.locationtech.spatial4j.shape.Shape;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
|
@ -168,12 +169,12 @@ public class GeometryCollectionBuilder extends ShapeBuilder<Shape, GeometryColle
|
|||
}
|
||||
|
||||
@Override
|
||||
public Shape build() {
|
||||
public Shape buildS4J() {
|
||||
|
||||
List<Shape> shapes = new ArrayList<>(this.shapes.size());
|
||||
|
||||
for (ShapeBuilder shape : this.shapes) {
|
||||
shapes.add(shape.build());
|
||||
shapes.add(shape.buildS4J());
|
||||
}
|
||||
|
||||
if (shapes.size() == 1)
|
||||
|
@ -183,6 +184,25 @@ public class GeometryCollectionBuilder extends ShapeBuilder<Shape, GeometryColle
|
|||
//note: ShapeCollection is probably faster than a Multi* geom.
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object buildLucene() {
|
||||
List<Object> shapes = new ArrayList<>(this.shapes.size());
|
||||
|
||||
for (ShapeBuilder shape : this.shapes) {
|
||||
Object o = shape.buildLucene();
|
||||
if (o.getClass().isArray()) {
|
||||
shapes.addAll(Arrays.asList((Object[])o));
|
||||
} else {
|
||||
shapes.add(o);
|
||||
}
|
||||
}
|
||||
|
||||
if (shapes.size() == 1) {
|
||||
return shapes.get(0);
|
||||
}
|
||||
return shapes.toArray(new Object[shapes.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(shapes);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.common.geo.builders;
|
||||
|
||||
import org.apache.lucene.geo.Line;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
import org.locationtech.jts.geom.GeometryFactory;
|
||||
|
@ -35,6 +36,9 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.common.geo.GeoUtils.normalizeLat;
|
||||
import static org.elasticsearch.common.geo.GeoUtils.normalizeLon;
|
||||
|
||||
public class LineStringBuilder extends ShapeBuilder<JtsGeometry, LineStringBuilder> {
|
||||
public static final GeoShapeType TYPE = GeoShapeType.LINESTRING;
|
||||
|
||||
|
@ -101,11 +105,11 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, LineStringBuild
|
|||
}
|
||||
|
||||
@Override
|
||||
public JtsGeometry build() {
|
||||
public JtsGeometry buildS4J() {
|
||||
Coordinate[] coordinates = this.coordinates.toArray(new Coordinate[this.coordinates.size()]);
|
||||
Geometry geometry;
|
||||
if(wrapdateline) {
|
||||
ArrayList<LineString> strings = decompose(FACTORY, coordinates, new ArrayList<LineString>());
|
||||
ArrayList<LineString> strings = decomposeS4J(FACTORY, coordinates, new ArrayList<LineString>());
|
||||
|
||||
if(strings.size() == 1) {
|
||||
geometry = strings.get(0);
|
||||
|
@ -120,7 +124,23 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, LineStringBuild
|
|||
return jtsGeometry(geometry);
|
||||
}
|
||||
|
||||
static ArrayList<LineString> decompose(GeometryFactory factory, Coordinate[] coordinates, ArrayList<LineString> strings) {
|
||||
@Override
|
||||
public Object buildLucene() {
|
||||
// decompose linestrings crossing dateline into array of Lines
|
||||
Coordinate[] coordinates = this.coordinates.toArray(new Coordinate[this.coordinates.size()]);
|
||||
if (wrapdateline) {
|
||||
ArrayList<Line> linestrings = decomposeLucene(coordinates, new ArrayList<>());
|
||||
if (linestrings.size() == 1) {
|
||||
return linestrings.get(0);
|
||||
} else {
|
||||
return linestrings.toArray(new Line[linestrings.size()]);
|
||||
}
|
||||
}
|
||||
return new Line(Arrays.stream(coordinates).mapToDouble(i->normalizeLat(i.y)).toArray(),
|
||||
Arrays.stream(coordinates).mapToDouble(i->normalizeLon(i.x)).toArray());
|
||||
}
|
||||
|
||||
static ArrayList<LineString> decomposeS4J(GeometryFactory factory, Coordinate[] coordinates, ArrayList<LineString> strings) {
|
||||
for(Coordinate[] part : decompose(+DATELINE, coordinates)) {
|
||||
for(Coordinate[] line : decompose(-DATELINE, part)) {
|
||||
strings.add(factory.createLineString(line));
|
||||
|
@ -129,6 +149,16 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, LineStringBuild
|
|||
return strings;
|
||||
}
|
||||
|
||||
static ArrayList<Line> decomposeLucene(Coordinate[] coordinates, ArrayList<Line> lines) {
|
||||
for (Coordinate[] part : decompose(+DATELINE, coordinates)) {
|
||||
for (Coordinate[] line : decompose(-DATELINE, part)) {
|
||||
lines.add(new Line(Arrays.stream(line).mapToDouble(i->normalizeLat(i.y)).toArray(),
|
||||
Arrays.stream(line).mapToDouble(i->normalizeLon(i.x)).toArray()));
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompose a linestring given as array of coordinates at a vertical line.
|
||||
*
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.common.geo.builders;
|
||||
|
||||
import org.apache.lucene.geo.Line;
|
||||
import org.elasticsearch.common.geo.GeoShapeType;
|
||||
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
|
||||
import org.elasticsearch.common.geo.parsers.ShapeParser;
|
||||
|
@ -124,12 +125,12 @@ public class MultiLineStringBuilder extends ShapeBuilder<JtsGeometry, MultiLineS
|
|||
}
|
||||
|
||||
@Override
|
||||
public JtsGeometry build() {
|
||||
public JtsGeometry buildS4J() {
|
||||
final Geometry geometry;
|
||||
if(wrapdateline) {
|
||||
ArrayList<LineString> parts = new ArrayList<>();
|
||||
for (LineStringBuilder line : lines) {
|
||||
LineStringBuilder.decompose(FACTORY, line.coordinates(false), parts);
|
||||
LineStringBuilder.decomposeS4J(FACTORY, line.coordinates(false), parts);
|
||||
}
|
||||
if(parts.size() == 1) {
|
||||
geometry = parts.get(0);
|
||||
|
@ -148,6 +149,27 @@ public class MultiLineStringBuilder extends ShapeBuilder<JtsGeometry, MultiLineS
|
|||
return jtsGeometry(geometry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object buildLucene() {
|
||||
if (wrapdateline) {
|
||||
ArrayList<Line> parts = new ArrayList<>();
|
||||
for (LineStringBuilder line : lines) {
|
||||
LineStringBuilder.decomposeLucene(line.coordinates(false), parts);
|
||||
}
|
||||
if (parts.size() == 1) {
|
||||
return parts.get(0);
|
||||
}
|
||||
return parts.toArray(new Line[parts.size()]);
|
||||
}
|
||||
Line[] linestrings = new Line[lines.size()];
|
||||
for (int i = 0; i < lines.size(); ++i) {
|
||||
LineStringBuilder lsb = lines.get(i);
|
||||
linestrings[i] = new Line(lsb.coordinates.stream().mapToDouble(c->c.y).toArray(),
|
||||
lsb.coordinates.stream().mapToDouble(c->c.x).toArray());
|
||||
}
|
||||
return linestrings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(lines);
|
||||
|
|
|
@ -61,7 +61,7 @@ public class MultiPointBuilder extends ShapeBuilder<XShapeCollection<Point>, Mul
|
|||
}
|
||||
|
||||
@Override
|
||||
public XShapeCollection<Point> build() {
|
||||
public XShapeCollection<Point> buildS4J() {
|
||||
//Could wrap JtsGeometry but probably slower due to conversions to/from JTS in relate()
|
||||
//MultiPoint geometry = FACTORY.createMultiPoint(points.toArray(new Coordinate[points.size()]));
|
||||
List<Point> shapes = new ArrayList<>(coordinates.size());
|
||||
|
@ -73,6 +73,17 @@ public class MultiPointBuilder extends ShapeBuilder<XShapeCollection<Point>, Mul
|
|||
return multiPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double[][] buildLucene() {
|
||||
double[][] points = new double[coordinates.size()][];
|
||||
Coordinate coord;
|
||||
for (int i = 0; i < coordinates.size(); ++i) {
|
||||
coord = coordinates.get(i);
|
||||
points[i] = new double[] {coord.x, coord.y};
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoShapeType type() {
|
||||
return TYPE;
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.locationtech.spatial4j.shape.Shape;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
@ -162,19 +163,19 @@ public class MultiPolygonBuilder extends ShapeBuilder<Shape, MultiPolygonBuilder
|
|||
}
|
||||
|
||||
@Override
|
||||
public Shape build() {
|
||||
public Shape buildS4J() {
|
||||
|
||||
List<Shape> shapes = new ArrayList<>(this.polygons.size());
|
||||
|
||||
if(wrapdateline) {
|
||||
for (PolygonBuilder polygon : this.polygons) {
|
||||
for(Coordinate[][] part : polygon.coordinates()) {
|
||||
shapes.add(jtsGeometry(PolygonBuilder.polygon(FACTORY, part)));
|
||||
shapes.add(jtsGeometry(PolygonBuilder.polygonS4J(FACTORY, part)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (PolygonBuilder polygon : this.polygons) {
|
||||
shapes.add(jtsGeometry(polygon.toPolygon(FACTORY)));
|
||||
shapes.add(jtsGeometry(polygon.toPolygonS4J(FACTORY)));
|
||||
}
|
||||
}
|
||||
if (shapes.size() == 1)
|
||||
|
@ -184,6 +185,33 @@ public class MultiPolygonBuilder extends ShapeBuilder<Shape, MultiPolygonBuilder
|
|||
//note: ShapeCollection is probably faster than a Multi* geom.
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object buildLucene() {
|
||||
List<org.apache.lucene.geo.Polygon> shapes = new ArrayList<>(this.polygons.size());
|
||||
Object poly;
|
||||
if (wrapdateline) {
|
||||
for (PolygonBuilder polygon : this.polygons) {
|
||||
poly = polygon.buildLucene();
|
||||
if (poly instanceof org.apache.lucene.geo.Polygon[]) {
|
||||
shapes.addAll(Arrays.asList((org.apache.lucene.geo.Polygon[])poly));
|
||||
} else {
|
||||
shapes.add((org.apache.lucene.geo.Polygon)poly);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < this.polygons.size(); ++i) {
|
||||
PolygonBuilder pb = this.polygons.get(i);
|
||||
poly = pb.buildLucene();
|
||||
if (poly instanceof org.apache.lucene.geo.Polygon[]) {
|
||||
shapes.addAll(Arrays.asList((org.apache.lucene.geo.Polygon[])poly));
|
||||
} else {
|
||||
shapes.add((org.apache.lucene.geo.Polygon)poly);
|
||||
}
|
||||
}
|
||||
}
|
||||
return shapes.stream().toArray(org.apache.lucene.geo.Polygon[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(polygons, orientation);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.common.geo.builders;
|
||||
|
||||
import org.elasticsearch.common.geo.GeoPoint;
|
||||
import org.elasticsearch.common.geo.GeoShapeType;
|
||||
import org.elasticsearch.common.geo.parsers.ShapeParser;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
|
@ -84,10 +85,15 @@ public class PointBuilder extends ShapeBuilder<Point, PointBuilder> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Point build() {
|
||||
public Point buildS4J() {
|
||||
return SPATIAL_CONTEXT.makePoint(coordinates.get(0).x, coordinates.get(0).y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint buildLucene() {
|
||||
return new GeoPoint(coordinates.get(0).y, coordinates.get(0).x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoShapeType type() {
|
||||
return TYPE;
|
||||
|
|
|
@ -46,6 +46,8 @@ import java.util.Locale;
|
|||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.elasticsearch.common.geo.GeoUtils.normalizeLat;
|
||||
import static org.elasticsearch.common.geo.GeoUtils.normalizeLon;
|
||||
import static org.apache.lucene.geo.GeoUtils.orient;
|
||||
|
||||
/**
|
||||
|
@ -226,8 +228,19 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, PolygonBuilder> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public JtsGeometry build() {
|
||||
return jtsGeometry(buildGeometry(FACTORY, wrapdateline));
|
||||
public JtsGeometry buildS4J() {
|
||||
return jtsGeometry(buildS4JGeometry(FACTORY, wrapdateline));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object buildLucene() {
|
||||
if (wrapdateline) {
|
||||
Coordinate[][][] polygons = coordinates();
|
||||
return polygons.length == 1
|
||||
? polygonLucene(polygons[0])
|
||||
: multipolygonLucene(polygons);
|
||||
}
|
||||
return toPolygonLucene();
|
||||
}
|
||||
|
||||
protected XContentBuilder coordinatesArray(XContentBuilder builder, Params params) throws IOException {
|
||||
|
@ -250,32 +263,46 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, PolygonBuilder> {
|
|||
return builder;
|
||||
}
|
||||
|
||||
public Geometry buildGeometry(GeometryFactory factory, boolean fixDateline) {
|
||||
public Geometry buildS4JGeometry(GeometryFactory factory, boolean fixDateline) {
|
||||
if(fixDateline) {
|
||||
Coordinate[][][] polygons = coordinates();
|
||||
return polygons.length == 1
|
||||
? polygon(factory, polygons[0])
|
||||
: multipolygon(factory, polygons);
|
||||
? polygonS4J(factory, polygons[0])
|
||||
: multipolygonS4J(factory, polygons);
|
||||
} else {
|
||||
return toPolygon(factory);
|
||||
return toPolygonS4J(factory);
|
||||
}
|
||||
}
|
||||
|
||||
public Polygon toPolygon() {
|
||||
return toPolygon(FACTORY);
|
||||
public Polygon toPolygonS4J() {
|
||||
return toPolygonS4J(FACTORY);
|
||||
}
|
||||
|
||||
protected Polygon toPolygon(GeometryFactory factory) {
|
||||
final LinearRing shell = linearRing(factory, this.shell.coordinates);
|
||||
protected Polygon toPolygonS4J(GeometryFactory factory) {
|
||||
final LinearRing shell = linearRingS4J(factory, this.shell.coordinates);
|
||||
final LinearRing[] holes = new LinearRing[this.holes.size()];
|
||||
Iterator<LineStringBuilder> iterator = this.holes.iterator();
|
||||
for (int i = 0; iterator.hasNext(); i++) {
|
||||
holes[i] = linearRing(factory, iterator.next().coordinates);
|
||||
holes[i] = linearRingS4J(factory, iterator.next().coordinates);
|
||||
}
|
||||
return factory.createPolygon(shell, holes);
|
||||
}
|
||||
|
||||
protected static LinearRing linearRing(GeometryFactory factory, List<Coordinate> coordinates) {
|
||||
public Object toPolygonLucene() {
|
||||
final org.apache.lucene.geo.Polygon[] holes = new org.apache.lucene.geo.Polygon[this.holes.size()];
|
||||
for (int i = 0; i < holes.length; ++i) {
|
||||
holes[i] = linearRing(this.holes.get(i).coordinates);
|
||||
}
|
||||
return new org.apache.lucene.geo.Polygon(this.shell.coordinates.stream().mapToDouble(i -> normalizeLat(i.y)).toArray(),
|
||||
this.shell.coordinates.stream().mapToDouble(i -> normalizeLon(i.x)).toArray(), holes);
|
||||
}
|
||||
|
||||
protected static org.apache.lucene.geo.Polygon linearRing(List<Coordinate> coordinates) {
|
||||
return new org.apache.lucene.geo.Polygon(coordinates.stream().mapToDouble(i -> normalizeLat(i.y)).toArray(),
|
||||
coordinates.stream().mapToDouble(i -> normalizeLon(i.x)).toArray());
|
||||
}
|
||||
|
||||
protected static LinearRing linearRingS4J(GeometryFactory factory, List<Coordinate> coordinates) {
|
||||
return factory.createLinearRing(coordinates.toArray(new Coordinate[coordinates.size()]));
|
||||
}
|
||||
|
||||
|
@ -293,7 +320,7 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, PolygonBuilder> {
|
|||
return shell.numDimensions();
|
||||
}
|
||||
|
||||
protected static Polygon polygon(GeometryFactory factory, Coordinate[][] polygon) {
|
||||
protected static Polygon polygonS4J(GeometryFactory factory, Coordinate[][] polygon) {
|
||||
LinearRing shell = factory.createLinearRing(polygon[0]);
|
||||
LinearRing[] holes;
|
||||
|
||||
|
@ -308,6 +335,35 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, PolygonBuilder> {
|
|||
return factory.createPolygon(shell, holes);
|
||||
}
|
||||
|
||||
protected static org.apache.lucene.geo.Polygon polygonLucene(Coordinate[][] polygon) {
|
||||
org.apache.lucene.geo.Polygon[] holes;
|
||||
Coordinate[] shell = polygon[0];
|
||||
if (polygon.length > 1) {
|
||||
holes = new org.apache.lucene.geo.Polygon[polygon.length - 1];
|
||||
for (int i = 0; i < holes.length; ++i) {
|
||||
Coordinate[] coords = polygon[i+1];
|
||||
double[] x = new double[coords.length];
|
||||
double[] y = new double[coords.length];
|
||||
for (int c = 0; c < coords.length; ++c) {
|
||||
x[c] = normalizeLon(coords[c].x);
|
||||
y[c] = normalizeLat(coords[c].y);
|
||||
}
|
||||
holes[i] = new org.apache.lucene.geo.Polygon(y, x);
|
||||
}
|
||||
} else {
|
||||
holes = new org.apache.lucene.geo.Polygon[0];
|
||||
}
|
||||
|
||||
double[] x = new double[shell.length];
|
||||
double[] y = new double[shell.length];
|
||||
for (int i = 0; i < shell.length; ++i) {
|
||||
x[i] = normalizeLon(shell[i].x);
|
||||
y[i] = normalizeLat(shell[i].y);
|
||||
}
|
||||
|
||||
return new org.apache.lucene.geo.Polygon(y, x, holes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Multipolygon from a set of coordinates. Each primary array contains a polygon which
|
||||
* in turn contains an array of linestrings. These line Strings are represented as an array of
|
||||
|
@ -318,14 +374,22 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, PolygonBuilder> {
|
|||
* @param polygons definition of polygons
|
||||
* @return a new Multipolygon
|
||||
*/
|
||||
protected static MultiPolygon multipolygon(GeometryFactory factory, Coordinate[][][] polygons) {
|
||||
protected static MultiPolygon multipolygonS4J(GeometryFactory factory, Coordinate[][][] polygons) {
|
||||
Polygon[] polygonSet = new Polygon[polygons.length];
|
||||
for (int i = 0; i < polygonSet.length; i++) {
|
||||
polygonSet[i] = polygon(factory, polygons[i]);
|
||||
polygonSet[i] = polygonS4J(factory, polygons[i]);
|
||||
}
|
||||
return factory.createMultiPolygon(polygonSet);
|
||||
}
|
||||
|
||||
protected static org.apache.lucene.geo.Polygon[] multipolygonLucene(Coordinate[][][] polygons) {
|
||||
org.apache.lucene.geo.Polygon[] polygonSet = new org.apache.lucene.geo.Polygon[polygons.length];
|
||||
for (int i = 0; i < polygonSet.length; ++i) {
|
||||
polygonSet[i] = polygonLucene(polygons[i]);
|
||||
}
|
||||
return polygonSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the component id of all edges in a ring to a given id and shifts the
|
||||
* coordinates of this component according to the dateline
|
||||
|
|
|
@ -211,7 +211,14 @@ public abstract class ShapeBuilder<T extends Shape, E extends ShapeBuilder<T,E>>
|
|||
* the builder looses its validity. So this method should only be called once on a builder
|
||||
* @return new {@link Shape} defined by the builder
|
||||
*/
|
||||
public abstract T build();
|
||||
public abstract T buildS4J();
|
||||
|
||||
/**
|
||||
* build lucene geometry.
|
||||
*
|
||||
* @return GeoPoint, double[][], Line, Line[], Polygon, Polygon[], Rectangle, Object[]
|
||||
*/
|
||||
public abstract Object buildLucene();
|
||||
|
||||
protected static Coordinate shift(Coordinate coordinate, double dateline) {
|
||||
if (dateline == 0) {
|
||||
|
|
|
@ -496,7 +496,7 @@ public class GeoShapeFieldMapper extends FieldMapper {
|
|||
if (shapeBuilder == null) {
|
||||
return;
|
||||
}
|
||||
shape = shapeBuilder.build();
|
||||
shape = shapeBuilder.buildS4J();
|
||||
}
|
||||
if (fieldType().pointsOnly() == true) {
|
||||
// index configured for pointsOnly
|
||||
|
|
|
@ -524,7 +524,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|||
throw new ElasticsearchParseException("failed to parse bounding box. Conflicting definition found "
|
||||
+ "using well-known text and explicit corners.");
|
||||
}
|
||||
org.locationtech.spatial4j.shape.Rectangle r = envelope.build();
|
||||
org.locationtech.spatial4j.shape.Rectangle r = envelope.buildS4J();
|
||||
return new double[]{r.getMinY(), r.getMaxY(), r.getMinX(), r.getMaxX()};
|
||||
}
|
||||
return new double[]{bottom, top, left, right};
|
||||
|
|
|
@ -472,13 +472,13 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
|
|||
public static SpatialArgs getArgs(ShapeBuilder shape, ShapeRelation relation) {
|
||||
switch (relation) {
|
||||
case DISJOINT:
|
||||
return new SpatialArgs(SpatialOperation.IsDisjointTo, shape.build());
|
||||
return new SpatialArgs(SpatialOperation.IsDisjointTo, shape.buildS4J());
|
||||
case INTERSECTS:
|
||||
return new SpatialArgs(SpatialOperation.Intersects, shape.build());
|
||||
return new SpatialArgs(SpatialOperation.Intersects, shape.buildS4J());
|
||||
case WITHIN:
|
||||
return new SpatialArgs(SpatialOperation.IsWithin, shape.build());
|
||||
return new SpatialArgs(SpatialOperation.IsWithin, shape.buildS4J());
|
||||
case CONTAINS:
|
||||
return new SpatialArgs(SpatialOperation.Contains, shape.build());
|
||||
return new SpatialArgs(SpatialOperation.Contains, shape.buildS4J());
|
||||
default:
|
||||
throw new IllegalArgumentException("invalid relation [" + relation + "]");
|
||||
}
|
||||
|
|
|
@ -56,10 +56,14 @@ abstract class BaseGeoParsingTestCase extends ESTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
protected void assertGeometryEquals(Shape expected, XContentBuilder geoJson) throws IOException {
|
||||
protected void assertGeometryEquals(Object expected, XContentBuilder geoJson, boolean useJTS) throws IOException {
|
||||
try (XContentParser parser = createParser(geoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertEquals(expected, ShapeParser.parse(parser).build());
|
||||
if (useJTS) {
|
||||
ElasticsearchGeoAssertions.assertEquals(expected, ShapeParser.parse(parser).buildS4J());
|
||||
} else {
|
||||
ElasticsearchGeoAssertions.assertEquals(expected, ShapeParser.parse(parser).buildLucene());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.common.geo;
|
||||
|
||||
import org.apache.lucene.geo.Line;
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
|
@ -33,11 +34,13 @@ import org.elasticsearch.common.xcontent.json.JsonXContent;
|
|||
import org.elasticsearch.index.mapper.ContentPath;
|
||||
import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
|
||||
import org.elasticsearch.index.mapper.Mapper;
|
||||
import org.elasticsearch.test.VersionUtils;
|
||||
import org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions;
|
||||
import org.locationtech.jts.geom.Coordinate;
|
||||
import org.locationtech.jts.geom.LineString;
|
||||
import org.locationtech.jts.geom.LinearRing;
|
||||
import org.locationtech.jts.geom.MultiLineString;
|
||||
import org.locationtech.jts.geom.MultiPolygon;
|
||||
import org.locationtech.jts.geom.Point;
|
||||
import org.locationtech.jts.geom.Polygon;
|
||||
import org.locationtech.spatial4j.exception.InvalidShapeException;
|
||||
|
@ -49,6 +52,7 @@ import org.locationtech.spatial4j.shape.jts.JtsPoint;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.common.geo.builders.ShapeBuilder.SPATIAL_CONTEXT;
|
||||
|
@ -66,9 +70,9 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.field("type", "Point")
|
||||
.startArray("coordinates").value(100.0).value(0.0).endArray()
|
||||
.endObject();
|
||||
|
||||
Point expected = GEOMETRY_FACTORY.createPoint(new Coordinate(100.0, 0.0));
|
||||
assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson);
|
||||
assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson, true);
|
||||
assertGeometryEquals(new GeoPoint(0d, 100d), pointGeoJson, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -86,9 +90,16 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
lineCoordinates.add(new Coordinate(100, 0));
|
||||
lineCoordinates.add(new Coordinate(101, 1));
|
||||
|
||||
LineString expected = GEOMETRY_FACTORY.createLineString(
|
||||
lineCoordinates.toArray(new Coordinate[lineCoordinates.size()]));
|
||||
assertGeometryEquals(jtsGeom(expected), lineGeoJson);
|
||||
try (XContentParser parser = createParser(lineGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertLineString(shape, true);
|
||||
}
|
||||
|
||||
try (XContentParser parser = createParser(lineGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertLineString(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -118,7 +129,11 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
new Coordinate(103, 3),
|
||||
}),
|
||||
});
|
||||
assertGeometryEquals(jtsGeom(expected), multilinesGeoJson);
|
||||
assertGeometryEquals(jtsGeom(expected), multilinesGeoJson, true);
|
||||
assertGeometryEquals(new Line[] {
|
||||
new Line(new double[] {0d, 1d}, new double[] {100d, 101d}),
|
||||
new Line(new double[] {2d, 3d}, new double[] {102d, 103d})},
|
||||
multilinesGeoJson, false);
|
||||
}
|
||||
|
||||
public void testParseCircle() throws IOException {
|
||||
|
@ -130,7 +145,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.endObject();
|
||||
|
||||
Circle expected = SPATIAL_CONTEXT.makeCircle(100.0, 0.0, 360 * 100 / GeoUtils.EARTH_EQUATOR);
|
||||
assertGeometryEquals(expected, multilinesGeoJson);
|
||||
assertGeometryEquals(expected, multilinesGeoJson, true);
|
||||
}
|
||||
|
||||
public void testParseMultiDimensionShapes() throws IOException {
|
||||
|
@ -171,9 +186,10 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.startArray().value(50).value(-30).endArray()
|
||||
.endArray()
|
||||
.endObject();
|
||||
|
||||
Rectangle expected = SPATIAL_CONTEXT.makeRectangle(-50, 50, -30, 30);
|
||||
assertGeometryEquals(expected, multilinesGeoJson);
|
||||
assertGeometryEquals(expected, multilinesGeoJson, true);
|
||||
assertGeometryEquals(new org.apache.lucene.geo.Rectangle(-30, 30, -50, 50),
|
||||
multilinesGeoJson, false);
|
||||
|
||||
// test #2: envelope that spans dateline
|
||||
multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "envelope")
|
||||
|
@ -184,7 +200,9 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.endObject();
|
||||
|
||||
expected = SPATIAL_CONTEXT.makeRectangle(50, -50, -30, 30);
|
||||
assertGeometryEquals(expected, multilinesGeoJson);
|
||||
assertGeometryEquals(expected, multilinesGeoJson, true);
|
||||
assertGeometryEquals(new org.apache.lucene.geo.Rectangle(-30, 30, 50, -50),
|
||||
multilinesGeoJson, false);
|
||||
|
||||
// test #3: "envelope" (actually a triangle) with invalid number of coordinates (TopRight, BottomLeft, BottomRight)
|
||||
multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "envelope")
|
||||
|
@ -234,10 +252,15 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
shellCoordinates.add(new Coordinate(101, 1));
|
||||
shellCoordinates.add(new Coordinate(100, 1));
|
||||
shellCoordinates.add(new Coordinate(100, 0));
|
||||
|
||||
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]));
|
||||
Coordinate[] coordinates = shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]);
|
||||
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(coordinates);
|
||||
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null);
|
||||
assertGeometryEquals(jtsGeom(expected), polygonGeoJson);
|
||||
assertGeometryEquals(jtsGeom(expected), polygonGeoJson, true);
|
||||
|
||||
org.apache.lucene.geo.Polygon p = new org.apache.lucene.geo.Polygon(
|
||||
new double[] {0d, 0d, 1d, 1d, 0d},
|
||||
new double[] {100d, 101d, 101d, 100d, 100d});
|
||||
assertGeometryEquals(p, polygonGeoJson, false);
|
||||
}
|
||||
|
||||
public void testParse3DPolygon() throws IOException {
|
||||
|
@ -261,20 +284,28 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
shellCoordinates.add(new Coordinate(101, 1, 10));
|
||||
shellCoordinates.add(new Coordinate(100, 1, 10));
|
||||
shellCoordinates.add(new Coordinate(100, 0, 10));
|
||||
Coordinate[] coordinates = shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]);
|
||||
|
||||
Version randomVersion = VersionUtils.randomVersionBetween(random(), Version.V_6_0_0, Version.CURRENT);
|
||||
Settings indexSettings = Settings.builder()
|
||||
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_6_3_0)
|
||||
.put(IndexMetaData.SETTING_VERSION_CREATED, randomVersion)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 0)
|
||||
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
|
||||
.put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()).build();
|
||||
|
||||
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]));
|
||||
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null);
|
||||
Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
|
||||
final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext);
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertEquals(jtsGeom(expected), ShapeParser.parse(parser, mapperBuilder).build());
|
||||
ElasticsearchGeoAssertions.assertEquals(jtsGeom(expected), ShapeParser.parse(parser, mapperBuilder).buildS4J());
|
||||
}
|
||||
|
||||
org.apache.lucene.geo.Polygon p = new org.apache.lucene.geo.Polygon(
|
||||
Arrays.stream(coordinates).mapToDouble(i->i.y).toArray(),
|
||||
Arrays.stream(coordinates).mapToDouble(i->i.x).toArray());
|
||||
assertGeometryEquals(p, polygonGeoJson, false);
|
||||
}
|
||||
|
||||
public void testInvalidDimensionalPolygon() throws IOException {
|
||||
|
@ -464,9 +495,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape);
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
|
||||
// test 2: ccw poly crossing dateline
|
||||
|
@ -485,9 +520,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape);
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
|
||||
// test 3: cw poly not crossing dateline
|
||||
|
@ -506,9 +545,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape);
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
|
||||
// test 4: cw poly crossing dateline
|
||||
|
@ -527,9 +570,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape);
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -556,9 +603,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape);
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
|
||||
// test 2: ccw poly crossing dateline
|
||||
|
@ -583,9 +634,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape);
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
|
||||
// test 3: cw poly not crossing dateline
|
||||
|
@ -610,11 +665,14 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape);
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape, true);
|
||||
}
|
||||
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
|
||||
// test 4: cw poly crossing dateline
|
||||
polygonGeoJson = Strings.toString(XContentFactory.jsonBuilder().startObject().field("type", "Polygon")
|
||||
|
@ -638,9 +696,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape);
|
||||
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -785,12 +847,20 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
holeCoordinates.add(new Coordinate(100.2, 0.2));
|
||||
|
||||
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(
|
||||
shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]));
|
||||
shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]));
|
||||
LinearRing[] holes = new LinearRing[1];
|
||||
holes[0] = GEOMETRY_FACTORY.createLinearRing(
|
||||
holeCoordinates.toArray(new Coordinate[holeCoordinates.size()]));
|
||||
holeCoordinates.toArray(new Coordinate[holeCoordinates.size()]));
|
||||
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, holes);
|
||||
assertGeometryEquals(jtsGeom(expected), polygonGeoJson);
|
||||
assertGeometryEquals(jtsGeom(expected), polygonGeoJson, true);
|
||||
|
||||
org.apache.lucene.geo.Polygon hole =
|
||||
new org.apache.lucene.geo.Polygon(
|
||||
new double[] {0.8d, 0.2d, 0.2d, 0.8d, 0.8d}, new double[] {100.8d, 100.8d, 100.2d, 100.2d, 100.8d});
|
||||
org.apache.lucene.geo.Polygon p =
|
||||
new org.apache.lucene.geo.Polygon(
|
||||
new double[] {0d, 0d, 1d, 1d, 0d}, new double[] {100d, 101d, 101d, 100d, 100d}, hole);
|
||||
assertGeometryEquals(p, polygonGeoJson, false);
|
||||
}
|
||||
|
||||
public void testParseSelfCrossingPolygon() throws IOException {
|
||||
|
@ -828,9 +898,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.endObject();
|
||||
|
||||
ShapeCollection<?> expected = shapeCollection(
|
||||
SPATIAL_CONTEXT.makePoint(100, 0),
|
||||
SPATIAL_CONTEXT.makePoint(101, 1.0));
|
||||
assertGeometryEquals(expected, multiPointGeoJson);
|
||||
SPATIAL_CONTEXT.makePoint(100, 0),
|
||||
SPATIAL_CONTEXT.makePoint(101, 1.0));
|
||||
assertGeometryEquals(expected, multiPointGeoJson, true);
|
||||
|
||||
assertGeometryEquals(new double[][]{
|
||||
new double[] {100d, 0d},
|
||||
new double[] {101d, 1d}}, multiPointGeoJson, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -900,7 +974,19 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
Shape expected = shapeCollection(withoutHoles, withHoles);
|
||||
|
||||
assertGeometryEquals(expected, multiPolygonGeoJson);
|
||||
assertGeometryEquals(expected, multiPolygonGeoJson, true);
|
||||
|
||||
org.apache.lucene.geo.Polygon hole =
|
||||
new org.apache.lucene.geo.Polygon(
|
||||
new double[] {0.8d, 0.2d, 0.2d, 0.8d, 0.8d}, new double[] {100.8d, 100.8d, 100.2d, 100.2d, 100.8d});
|
||||
|
||||
org.apache.lucene.geo.Polygon[] polygons = new org.apache.lucene.geo.Polygon[] {
|
||||
new org.apache.lucene.geo.Polygon(
|
||||
new double[] {2d, 3d, 3d, 2d, 2d}, new double[] {103d, 103d, 102d, 102d, 103d}),
|
||||
new org.apache.lucene.geo.Polygon(
|
||||
new double[] {0d, 1d, 1d, 0d, 0d}, new double[] {101d, 101d, 100d, 100d, 101d}, hole)
|
||||
};
|
||||
assertGeometryEquals(polygons, multiPolygonGeoJson, false);
|
||||
|
||||
// test #2: multipolygon; one polygon with one hole
|
||||
// this test converting the multipolygon from a ShapeCollection type
|
||||
|
@ -947,7 +1033,17 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
holes[0] = GEOMETRY_FACTORY.createLinearRing(holeCoordinates.toArray(new Coordinate[holeCoordinates.size()]));
|
||||
withHoles = GEOMETRY_FACTORY.createPolygon(shell, holes);
|
||||
|
||||
assertGeometryEquals(jtsGeom(withHoles), multiPolygonGeoJson);
|
||||
assertGeometryEquals(jtsGeom(withHoles), multiPolygonGeoJson, true);
|
||||
|
||||
org.apache.lucene.geo.Polygon luceneHole =
|
||||
new org.apache.lucene.geo.Polygon(
|
||||
new double[] {0.8d, 0.2d, 0.2d, 0.8d, 0.8d}, new double[] {100.8d, 100.8d, 100.2d, 100.2d, 100.8d});
|
||||
|
||||
org.apache.lucene.geo.Polygon[] lucenePolygons = new org.apache.lucene.geo.Polygon[] {
|
||||
new org.apache.lucene.geo.Polygon(
|
||||
new double[] {0d, 0d, 1d, 1d, 0d}, new double[] {100d, 101d, 101d, 100d, 100d}, luceneHole)
|
||||
};
|
||||
assertGeometryEquals(lucenePolygons, multiPolygonGeoJson, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -967,20 +1063,74 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.field("type", "Point")
|
||||
.startArray("coordinates").value(102.0).value(2.0).endArray()
|
||||
.endObject()
|
||||
.startObject()
|
||||
.field("type", "Polygon")
|
||||
.startArray("coordinates")
|
||||
.startArray()
|
||||
.startArray().value(-177.0).value(10.0).endArray()
|
||||
.startArray().value(176.0).value(15.0).endArray()
|
||||
.startArray().value(172.0).value(0.0).endArray()
|
||||
.startArray().value(176.0).value(-15.0).endArray()
|
||||
.startArray().value(-177.0).value(-10.0).endArray()
|
||||
.startArray().value(-177.0).value(10.0).endArray()
|
||||
.endArray()
|
||||
.endArray()
|
||||
.endObject()
|
||||
.endArray()
|
||||
.endObject();
|
||||
|
||||
Shape[] expected = new Shape[2];
|
||||
ArrayList<Coordinate> shellCoordinates1 = new ArrayList<>();
|
||||
shellCoordinates1.add(new Coordinate(180.0, -12.142857142857142));
|
||||
shellCoordinates1.add(new Coordinate(180.0, 12.142857142857142));
|
||||
shellCoordinates1.add(new Coordinate(176.0, 15.0));
|
||||
shellCoordinates1.add(new Coordinate(172.0, 0.0));
|
||||
shellCoordinates1.add(new Coordinate(176.0, -15));
|
||||
shellCoordinates1.add(new Coordinate(180.0, -12.142857142857142));
|
||||
|
||||
ArrayList<Coordinate> shellCoordinates2 = new ArrayList<>();
|
||||
shellCoordinates2.add(new Coordinate(-180.0, 12.142857142857142));
|
||||
shellCoordinates2.add(new Coordinate(-180.0, -12.142857142857142));
|
||||
shellCoordinates2.add(new Coordinate(-177.0, -10.0));
|
||||
shellCoordinates2.add(new Coordinate(-177.0, 10.0));
|
||||
shellCoordinates2.add(new Coordinate(-180.0, 12.142857142857142));
|
||||
|
||||
Shape[] expected = new Shape[3];
|
||||
LineString expectedLineString = GEOMETRY_FACTORY.createLineString(new Coordinate[]{
|
||||
new Coordinate(100, 0),
|
||||
new Coordinate(101, 1),
|
||||
new Coordinate(100, 0),
|
||||
new Coordinate(101, 1),
|
||||
});
|
||||
expected[0] = jtsGeom(expectedLineString);
|
||||
Point expectedPoint = GEOMETRY_FACTORY.createPoint(new Coordinate(102.0, 2.0));
|
||||
expected[1] = new JtsPoint(expectedPoint, SPATIAL_CONTEXT);
|
||||
LinearRing shell1 = GEOMETRY_FACTORY.createLinearRing(
|
||||
shellCoordinates1.toArray(new Coordinate[shellCoordinates1.size()]));
|
||||
LinearRing shell2 = GEOMETRY_FACTORY.createLinearRing(
|
||||
shellCoordinates2.toArray(new Coordinate[shellCoordinates2.size()]));
|
||||
MultiPolygon expectedMultiPoly = GEOMETRY_FACTORY.createMultiPolygon(
|
||||
new Polygon[] {
|
||||
GEOMETRY_FACTORY.createPolygon(shell1),
|
||||
GEOMETRY_FACTORY.createPolygon(shell2)
|
||||
}
|
||||
);
|
||||
expected[2] = jtsGeom(expectedMultiPoly);
|
||||
|
||||
|
||||
//equals returns true only if geometries are in the same order
|
||||
assertGeometryEquals(shapeCollection(expected), geometryCollectionGeoJson);
|
||||
assertGeometryEquals(shapeCollection(expected), geometryCollectionGeoJson, true);
|
||||
|
||||
Object[] luceneExpected = new Object[] {
|
||||
new Line(new double[] {0d, 1d}, new double[] {100d, 101d}),
|
||||
new GeoPoint(2d, 102d),
|
||||
new org.apache.lucene.geo.Polygon(
|
||||
new double[] {-12.142857142857142d, 12.142857142857142d, 15d, 0d, -15d, -12.142857142857142d},
|
||||
new double[] {180d, 180d, 176d, 172d, 176d, 180d}
|
||||
),
|
||||
new org.apache.lucene.geo.Polygon(
|
||||
new double[] {12.142857142857142d, -12.142857142857142d, -10d, 10d, 12.142857142857142d},
|
||||
new double[] {180d, 180d, -177d, -177d, 180d}
|
||||
)
|
||||
};
|
||||
assertGeometryEquals(luceneExpected, geometryCollectionGeoJson, false);
|
||||
}
|
||||
|
||||
public void testThatParserExtractsCorrectTypeAndCoordinatesFromArbitraryJson() throws IOException {
|
||||
|
@ -1000,8 +1150,11 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
.startObject("lala").field("type", "NotAPoint").endObject()
|
||||
.endObject();
|
||||
|
||||
Point expected = GEOMETRY_FACTORY.createPoint(new Coordinate(100.0, 0.0));
|
||||
assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson);
|
||||
Point expected = GEOMETRY_FACTORY.createPoint(new Coordinate(100.0, 0.0));
|
||||
assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson, true);
|
||||
|
||||
GeoPoint expectedPt = new GeoPoint(0, 100);
|
||||
assertGeometryEquals(expectedPt, pointGeoJson, false);
|
||||
}
|
||||
|
||||
public void testParseOrientationOption() throws IOException {
|
||||
|
@ -1030,9 +1183,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape);
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
|
||||
// test 2: valid ccw (right handed system) poly not crossing dateline (with 'ccw' field)
|
||||
|
@ -1060,9 +1217,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape);
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
|
||||
// test 3: valid ccw (right handed system) poly not crossing dateline (with 'counterclockwise' field)
|
||||
|
@ -1090,9 +1251,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertPolygon(shape);
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
|
||||
// test 4: valid cw (left handed system) poly crossing dateline (with 'left' field)
|
||||
|
@ -1120,9 +1285,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape);
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
|
||||
// test 5: valid cw multipoly (left handed system) poly crossing dateline (with 'cw' field)
|
||||
|
@ -1150,9 +1319,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape);
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
|
||||
// test 6: valid cw multipoly (left handed system) poly crossing dateline (with 'clockwise' field)
|
||||
|
@ -1180,9 +1353,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
|
|||
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
Shape shape = ShapeParser.parse(parser).build();
|
||||
Shape shape = ShapeParser.parse(parser).buildS4J();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape, true);
|
||||
}
|
||||
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(shape);
|
||||
try (XContentParser parser = createParser(polygonGeoJson)) {
|
||||
parser.nextToken();
|
||||
ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.elasticsearch.common.geo;
|
||||
|
||||
import org.apache.lucene.geo.GeoTestUtil;
|
||||
import org.apache.lucene.geo.Line;
|
||||
import org.elasticsearch.ElasticsearchException;
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.Version;
|
||||
|
@ -58,6 +59,7 @@ import org.locationtech.spatial4j.shape.jts.JtsPoint;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.elasticsearch.common.geo.builders.ShapeBuilder.SPATIAL_CONTEXT;
|
||||
|
@ -84,9 +86,9 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
return XContentFactory.jsonBuilder().value(wkt);
|
||||
}
|
||||
|
||||
private void assertExpected(Shape expected, ShapeBuilder<?, ?> builder) throws IOException {
|
||||
private void assertExpected(Object expected, ShapeBuilder<?, ?> builder, boolean useJTS) throws IOException {
|
||||
XContentBuilder xContentBuilder = toWKTContent(builder, false);
|
||||
assertGeometryEquals(expected, xContentBuilder);
|
||||
assertGeometryEquals(expected, xContentBuilder, useJTS);
|
||||
}
|
||||
|
||||
private void assertMalformed(ShapeBuilder<?, ?> builder) throws IOException {
|
||||
|
@ -99,7 +101,8 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
GeoPoint p = RandomShapeGenerator.randomPoint(random());
|
||||
Coordinate c = new Coordinate(p.lon(), p.lat());
|
||||
Point expected = GEOMETRY_FACTORY.createPoint(c);
|
||||
assertExpected(new JtsPoint(expected, SPATIAL_CONTEXT), new PointBuilder().coordinate(c));
|
||||
assertExpected(new JtsPoint(expected, SPATIAL_CONTEXT), new PointBuilder().coordinate(c), true);
|
||||
assertExpected(new GeoPoint(p.lat(), p.lon()), new PointBuilder().coordinate(c), false);
|
||||
assertMalformed(new PointBuilder().coordinate(c));
|
||||
}
|
||||
|
||||
|
@ -107,15 +110,25 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
public void testParseMultiPoint() throws IOException {
|
||||
int numPoints = randomIntBetween(2, 100);
|
||||
List<Coordinate> coordinates = new ArrayList<>(numPoints);
|
||||
Shape[] shapes = new Shape[numPoints];
|
||||
GeoPoint p = new GeoPoint();
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
p.reset(GeoTestUtil.nextLatitude(), GeoTestUtil.nextLongitude());
|
||||
coordinates.add(new Coordinate(p.lon(), p.lat()));
|
||||
shapes[i] = SPATIAL_CONTEXT.makePoint(p.lon(), p.lat());
|
||||
coordinates.add(new Coordinate(GeoTestUtil.nextLongitude(), GeoTestUtil.nextLatitude()));
|
||||
}
|
||||
|
||||
Shape[] shapes = new Shape[numPoints];
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
Coordinate c = coordinates.get(i);
|
||||
shapes[i] = SPATIAL_CONTEXT.makePoint(c.x, c.y);
|
||||
}
|
||||
ShapeCollection<?> expected = shapeCollection(shapes);
|
||||
assertExpected(expected, new MultiPointBuilder(coordinates));
|
||||
assertExpected(expected, new MultiPointBuilder(coordinates), true);
|
||||
|
||||
double[][] luceneShapes = new double[numPoints][2];
|
||||
for (int i = 0; i < numPoints; ++i) {
|
||||
Coordinate c = coordinates.get(i);
|
||||
luceneShapes[i][0] = c.x;
|
||||
luceneShapes[i][1] = c.y;
|
||||
}
|
||||
assertExpected(luceneShapes, new MultiPointBuilder(coordinates), false);
|
||||
assertMalformed(new MultiPointBuilder(coordinates));
|
||||
}
|
||||
|
||||
|
@ -133,8 +146,17 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
@Override
|
||||
public void testParseLineString() throws IOException {
|
||||
List<Coordinate> coordinates = randomLineStringCoords();
|
||||
|
||||
LineString expected = GEOMETRY_FACTORY.createLineString(coordinates.toArray(new Coordinate[coordinates.size()]));
|
||||
assertExpected(jtsGeom(expected), new LineStringBuilder(coordinates));
|
||||
assertExpected(jtsGeom(expected), new LineStringBuilder(coordinates), true);
|
||||
|
||||
double[] lats = new double[coordinates.size()];
|
||||
double[] lons = new double[lats.length];
|
||||
for (int i = 0; i < lats.length; ++i) {
|
||||
lats[i] = coordinates.get(i).y;
|
||||
lons[i] = coordinates.get(i).x;
|
||||
}
|
||||
assertExpected(new Line(lats, lons), new LineStringBuilder(coordinates), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -148,9 +170,18 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
lineStrings.add(GEOMETRY_FACTORY.createLineString(coords));
|
||||
builder.linestring(new LineStringBuilder(lsc));
|
||||
}
|
||||
|
||||
MultiLineString expected = GEOMETRY_FACTORY.createMultiLineString(
|
||||
lineStrings.toArray(new LineString[lineStrings.size()]));
|
||||
assertExpected(jtsGeom(expected), builder);
|
||||
assertExpected(jtsGeom(expected), builder, true);
|
||||
|
||||
Line[] lines = new Line[lineStrings.size()];
|
||||
for (int j = 0; j < lineStrings.size(); ++j) {
|
||||
Coordinate[] c = lineStrings.get(j).getCoordinates();
|
||||
lines[j] = new Line(Arrays.stream(c).mapToDouble(i->i.y).toArray(),
|
||||
Arrays.stream(c).mapToDouble(i->i.x).toArray());
|
||||
}
|
||||
assertExpected(lines, builder, false);
|
||||
assertMalformed(builder);
|
||||
}
|
||||
|
||||
|
@ -159,9 +190,10 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
PolygonBuilder builder = PolygonBuilder.class.cast(
|
||||
RandomShapeGenerator.createShape(random(), RandomShapeGenerator.ShapeType.POLYGON));
|
||||
Coordinate[] coords = builder.coordinates()[0][0];
|
||||
|
||||
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(coords);
|
||||
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null);
|
||||
assertExpected(jtsGeom(expected), builder);
|
||||
assertExpected(jtsGeom(expected), builder, true);
|
||||
assertMalformed(builder);
|
||||
}
|
||||
|
||||
|
@ -180,8 +212,9 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
shell = GEOMETRY_FACTORY.createLinearRing(coordinates);
|
||||
shapes[i] = GEOMETRY_FACTORY.createPolygon(shell, null);
|
||||
}
|
||||
|
||||
Shape expected = shapeCollection(shapes);
|
||||
assertExpected(expected, builder);
|
||||
assertExpected(expected, builder, true);
|
||||
assertMalformed(builder);
|
||||
}
|
||||
|
||||
|
@ -210,8 +243,15 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
holes[0] = GEOMETRY_FACTORY.createLinearRing(
|
||||
holeCoordinates.toArray(new Coordinate[holeCoordinates.size()]));
|
||||
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, holes);
|
||||
assertExpected(jtsGeom(expected), polygonWithHole, true);
|
||||
|
||||
assertExpected(jtsGeom(expected), polygonWithHole);
|
||||
org.apache.lucene.geo.Polygon hole =
|
||||
new org.apache.lucene.geo.Polygon(
|
||||
new double[] {0.8d, 0.8d, 0.2d, 0.2d, 0.8d}, new double[] {100.2d, 100.8d, 100.8d, 100.2d, 100.2d});
|
||||
org.apache.lucene.geo.Polygon p =
|
||||
new org.apache.lucene.geo.Polygon(
|
||||
new double[] {0d, 1d, 1d, 0d, 0d}, new double[] {101d, 101d, 100d, 100d, 101d}, hole);
|
||||
assertExpected(p, polygonWithHole, false);
|
||||
assertMalformed(polygonWithHole);
|
||||
}
|
||||
|
||||
|
@ -370,8 +410,10 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
public void testParseEnvelope() throws IOException {
|
||||
org.apache.lucene.geo.Rectangle r = GeoTestUtil.nextBox();
|
||||
EnvelopeBuilder builder = new EnvelopeBuilder(new Coordinate(r.minLon, r.maxLat), new Coordinate(r.maxLon, r.minLat));
|
||||
|
||||
Rectangle expected = SPATIAL_CONTEXT.makeRectangle(r.minLon, r.maxLon, r.minLat, r.maxLat);
|
||||
assertExpected(expected, builder);
|
||||
assertExpected(expected, builder, true);
|
||||
assertExpected(r, builder, false);
|
||||
assertMalformed(builder);
|
||||
}
|
||||
|
||||
|
@ -386,10 +428,15 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
|
|||
// assert empty shape collection
|
||||
GeometryCollectionBuilder builder = new GeometryCollectionBuilder();
|
||||
Shape[] expected = new Shape[0];
|
||||
assertEquals(shapeCollection(expected).isEmpty(), builder.build().isEmpty());
|
||||
if (randomBoolean()) {
|
||||
assertEquals(shapeCollection(expected).isEmpty(), builder.buildS4J().isEmpty());
|
||||
} else {
|
||||
assertEquals(shapeCollection(expected).isEmpty(), ((Object[])builder.buildLucene()).length == 0);
|
||||
}
|
||||
} else {
|
||||
GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
|
||||
assertExpected(gcb.build(), gcb);
|
||||
assertExpected(gcb.buildS4J(), gcb, true);
|
||||
assertExpected(gcb.buildLucene(), gcb, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ import org.locationtech.spatial4j.exception.InvalidShapeException;
|
|||
import org.locationtech.spatial4j.shape.Circle;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Rectangle;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.impl.PointImpl;
|
||||
|
||||
import static org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions.assertMultiLineString;
|
||||
|
@ -49,65 +48,109 @@ import static org.hamcrest.Matchers.containsString;
|
|||
public class ShapeBuilderTests extends ESTestCase {
|
||||
|
||||
public void testNewPoint() {
|
||||
Point point = new PointBuilder().coordinate(-100, 45).build();
|
||||
PointBuilder pb = new PointBuilder().coordinate(-100, 45);
|
||||
Point point = pb.buildS4J();
|
||||
assertEquals(-100D, point.getX(), 0.0d);
|
||||
assertEquals(45D, point.getY(), 0.0d);
|
||||
GeoPoint geoPoint = pb.buildLucene();
|
||||
assertEquals(-100D, geoPoint.getLon(), 0.0d);
|
||||
assertEquals(45D, geoPoint.getLat(), 0.0d);
|
||||
}
|
||||
|
||||
public void testNewRectangle() {
|
||||
Rectangle rectangle = new EnvelopeBuilder(new Coordinate(-45, 30), new Coordinate(45, -30)).build();
|
||||
EnvelopeBuilder eb = new EnvelopeBuilder(new Coordinate(-45, 30), new Coordinate(45, -30));
|
||||
Rectangle rectangle = eb.buildS4J();
|
||||
assertEquals(-45D, rectangle.getMinX(), 0.0d);
|
||||
assertEquals(-30D, rectangle.getMinY(), 0.0d);
|
||||
assertEquals(45D, rectangle.getMaxX(), 0.0d);
|
||||
assertEquals(30D, rectangle.getMaxY(), 0.0d);
|
||||
|
||||
org.apache.lucene.geo.Rectangle luceneRectangle = eb.buildLucene();
|
||||
assertEquals(-45D, luceneRectangle.minLon, 0.0d);
|
||||
assertEquals(-30D, luceneRectangle.minLat, 0.0d);
|
||||
assertEquals(45D, luceneRectangle.maxLon, 0.0d);
|
||||
assertEquals(30D, luceneRectangle.maxLat, 0.0d);
|
||||
}
|
||||
|
||||
public void testNewPolygon() {
|
||||
Polygon polygon = new PolygonBuilder(new CoordinatesBuilder()
|
||||
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
|
||||
.coordinate(-45, 30)
|
||||
.coordinate(45, 30)
|
||||
.coordinate(45, -30)
|
||||
.coordinate(-45, -30)
|
||||
.coordinate(-45, 30)).toPolygon();
|
||||
.coordinate(-45, 30));
|
||||
|
||||
LineString exterior = polygon.getExteriorRing();
|
||||
Polygon poly = pb.toPolygonS4J();
|
||||
LineString exterior = poly.getExteriorRing();
|
||||
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30));
|
||||
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
|
||||
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
|
||||
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30));
|
||||
|
||||
org.apache.lucene.geo.Polygon lucenePoly = (org.apache.lucene.geo.Polygon)(pb.toPolygonLucene());
|
||||
assertEquals(lucenePoly.getPolyLat(0), 30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(0), -45, 0d);
|
||||
assertEquals(lucenePoly.getPolyLat(1), 30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(1), 45, 0d);
|
||||
assertEquals(lucenePoly.getPolyLat(2), -30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(2), 45, 0d);
|
||||
assertEquals(lucenePoly.getPolyLat(3), -30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(3), -45, 0d);
|
||||
}
|
||||
|
||||
public void testNewPolygon_coordinate() {
|
||||
Polygon polygon = new PolygonBuilder(new CoordinatesBuilder()
|
||||
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
|
||||
.coordinate(new Coordinate(-45, 30))
|
||||
.coordinate(new Coordinate(45, 30))
|
||||
.coordinate(new Coordinate(45, -30))
|
||||
.coordinate(new Coordinate(-45, -30))
|
||||
.coordinate(new Coordinate(-45, 30))).toPolygon();
|
||||
.coordinate(new Coordinate(-45, 30)));
|
||||
|
||||
LineString exterior = polygon.getExteriorRing();
|
||||
Polygon poly = pb.toPolygonS4J();
|
||||
LineString exterior = poly.getExteriorRing();
|
||||
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30));
|
||||
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
|
||||
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
|
||||
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30));
|
||||
|
||||
org.apache.lucene.geo.Polygon lucenePoly = (org.apache.lucene.geo.Polygon)(pb.toPolygonLucene());
|
||||
assertEquals(lucenePoly.getPolyLat(0), 30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(0), -45, 0d);
|
||||
assertEquals(lucenePoly.getPolyLat(1), 30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(1), 45, 0d);
|
||||
assertEquals(lucenePoly.getPolyLat(2), -30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(2), 45, 0d);
|
||||
assertEquals(lucenePoly.getPolyLat(3), -30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(3), -45, 0d);
|
||||
}
|
||||
|
||||
public void testNewPolygon_coordinates() {
|
||||
Polygon polygon = new PolygonBuilder(new CoordinatesBuilder()
|
||||
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
|
||||
.coordinates(new Coordinate(-45, 30), new Coordinate(45, 30),
|
||||
new Coordinate(45, -30), new Coordinate(-45, -30), new Coordinate(-45, 30))
|
||||
).toPolygon();
|
||||
);
|
||||
|
||||
LineString exterior = polygon.getExteriorRing();
|
||||
Polygon poly = pb.toPolygonS4J();
|
||||
LineString exterior = poly.getExteriorRing();
|
||||
assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30));
|
||||
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
|
||||
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
|
||||
assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30));
|
||||
|
||||
org.apache.lucene.geo.Polygon lucenePoly = (org.apache.lucene.geo.Polygon)(pb.toPolygonLucene());
|
||||
assertEquals(lucenePoly.getPolyLat(0), 30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(0), -45, 0d);
|
||||
assertEquals(lucenePoly.getPolyLat(1), 30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(1), 45, 0d);
|
||||
assertEquals(lucenePoly.getPolyLat(2), -30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(2), 45, 0d);
|
||||
assertEquals(lucenePoly.getPolyLat(3), -30, 0d);
|
||||
assertEquals(lucenePoly.getPolyLon(3), -45, 0d);
|
||||
}
|
||||
|
||||
public void testLineStringBuilder() {
|
||||
// Building a simple LineString
|
||||
new LineStringBuilder(new CoordinatesBuilder()
|
||||
LineStringBuilder lsb = new LineStringBuilder(new CoordinatesBuilder()
|
||||
.coordinate(-130.0, 55.0)
|
||||
.coordinate(-130.0, -40.0)
|
||||
.coordinate(-15.0, -40.0)
|
||||
|
@ -115,10 +158,13 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(-45.0, 50.0)
|
||||
.coordinate(-45.0, -15.0)
|
||||
.coordinate(-110.0, -15.0)
|
||||
.coordinate(-110.0, 55.0)).build();
|
||||
.coordinate(-110.0, 55.0));
|
||||
|
||||
lsb.buildS4J();
|
||||
lsb.buildLucene();
|
||||
|
||||
// Building a linestring that needs to be wrapped
|
||||
new LineStringBuilder(new CoordinatesBuilder()
|
||||
lsb = new LineStringBuilder(new CoordinatesBuilder()
|
||||
.coordinate(100.0, 50.0)
|
||||
.coordinate(110.0, -40.0)
|
||||
.coordinate(240.0, -40.0)
|
||||
|
@ -126,31 +172,34 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(200.0, 60.0)
|
||||
.coordinate(200.0, -30.0)
|
||||
.coordinate(130.0, -30.0)
|
||||
.coordinate(130.0, 60.0)
|
||||
)
|
||||
.build();
|
||||
.coordinate(130.0, 60.0));
|
||||
|
||||
lsb.buildS4J();
|
||||
lsb.buildLucene();
|
||||
|
||||
// Building a lineString on the dateline
|
||||
new LineStringBuilder(new CoordinatesBuilder()
|
||||
lsb = new LineStringBuilder(new CoordinatesBuilder()
|
||||
.coordinate(-180.0, 80.0)
|
||||
.coordinate(-180.0, 40.0)
|
||||
.coordinate(-180.0, -40.0)
|
||||
.coordinate(-180.0, -80.0)
|
||||
)
|
||||
.build();
|
||||
.coordinate(-180.0, -80.0));
|
||||
|
||||
lsb.buildS4J();
|
||||
lsb.buildLucene();
|
||||
|
||||
// Building a lineString on the dateline
|
||||
new LineStringBuilder(new CoordinatesBuilder()
|
||||
lsb = new LineStringBuilder(new CoordinatesBuilder()
|
||||
.coordinate(180.0, 80.0)
|
||||
.coordinate(180.0, 40.0)
|
||||
.coordinate(180.0, -40.0)
|
||||
.coordinate(180.0, -80.0)
|
||||
)
|
||||
.build();
|
||||
.coordinate(180.0, -80.0));
|
||||
|
||||
lsb.buildS4J();
|
||||
lsb.buildLucene();
|
||||
}
|
||||
|
||||
public void testMultiLineString() {
|
||||
new MultiLineStringBuilder()
|
||||
MultiLineStringBuilder mlsb = new MultiLineStringBuilder()
|
||||
.linestring(new LineStringBuilder(new CoordinatesBuilder()
|
||||
.coordinate(-100.0, 50.0)
|
||||
.coordinate(50.0, 50.0)
|
||||
|
@ -164,8 +213,9 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(50.0, 0.0)
|
||||
.coordinate(-100.0, 0.0)
|
||||
)
|
||||
)
|
||||
.build();
|
||||
);
|
||||
mlsb.buildS4J();
|
||||
mlsb.buildLucene();
|
||||
|
||||
// LineString that needs to be wrapped
|
||||
new MultiLineStringBuilder()
|
||||
|
@ -182,8 +232,10 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(200.0, 0.0)
|
||||
.coordinate(150.0, 0.0)
|
||||
)
|
||||
)
|
||||
.build();
|
||||
);
|
||||
|
||||
mlsb.buildS4J();
|
||||
mlsb.buildLucene();
|
||||
}
|
||||
|
||||
public void testPolygonSelfIntersection() {
|
||||
|
@ -192,58 +244,58 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(40.0, 50.0)
|
||||
.coordinate(-40.0, -50.0)
|
||||
.coordinate(40.0, -50.0).close());
|
||||
Exception e = expectThrows(InvalidShapeException.class, () -> newPolygon.build());
|
||||
Exception e = expectThrows(InvalidShapeException.class, () -> newPolygon.buildS4J());
|
||||
assertThat(e.getMessage(), containsString("Self-intersection at or near point (0.0"));
|
||||
}
|
||||
|
||||
/** note: only supported by S4J at the moment */
|
||||
public void testGeoCircle() {
|
||||
double earthCircumference = 40075016.69;
|
||||
Circle circle = new CircleBuilder().center(0, 0).radius("100m").build();
|
||||
Circle circle = new CircleBuilder().center(0, 0).radius("100m").buildS4J();
|
||||
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
|
||||
assertEquals(new PointImpl(0, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
|
||||
circle = new CircleBuilder().center(+180, 0).radius("100m").build();
|
||||
circle = new CircleBuilder().center(+180, 0).radius("100m").buildS4J();
|
||||
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
|
||||
assertEquals(new PointImpl(180, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
|
||||
circle = new CircleBuilder().center(-180, 0).radius("100m").build();
|
||||
circle = new CircleBuilder().center(-180, 0).radius("100m").buildS4J();
|
||||
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
|
||||
assertEquals(new PointImpl(-180, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
|
||||
circle = new CircleBuilder().center(0, 90).radius("100m").build();
|
||||
circle = new CircleBuilder().center(0, 90).radius("100m").buildS4J();
|
||||
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
|
||||
assertEquals(new PointImpl(0, 90, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
|
||||
circle = new CircleBuilder().center(0, -90).radius("100m").build();
|
||||
circle = new CircleBuilder().center(0, -90).radius("100m").buildS4J();
|
||||
assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
|
||||
assertEquals(new PointImpl(0, -90, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
|
||||
double randomLat = (randomDouble() * 180) - 90;
|
||||
double randomLon = (randomDouble() * 360) - 180;
|
||||
double randomRadius = randomIntBetween(1, (int) earthCircumference / 4);
|
||||
circle = new CircleBuilder().center(randomLon, randomLat).radius(randomRadius + "m").build();
|
||||
circle = new CircleBuilder().center(randomLon, randomLat).radius(randomRadius + "m").buildS4J();
|
||||
assertEquals((360 * randomRadius) / earthCircumference, circle.getRadius(), 0.00000001);
|
||||
assertEquals(new PointImpl(randomLon, randomLat, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
|
||||
}
|
||||
|
||||
public void testPolygonWrapping() {
|
||||
Shape shape = new PolygonBuilder(new CoordinatesBuilder()
|
||||
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
|
||||
.coordinate(-150.0, 65.0)
|
||||
.coordinate(-250.0, 65.0)
|
||||
.coordinate(-250.0, -65.0)
|
||||
.coordinate(-150.0, -65.0)
|
||||
.close()
|
||||
)
|
||||
.build();
|
||||
.close());
|
||||
|
||||
assertMultiPolygon(shape);
|
||||
assertMultiPolygon(pb.buildS4J(), true);
|
||||
assertMultiPolygon(pb.buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testLineStringWrapping() {
|
||||
Shape shape = new LineStringBuilder(new CoordinatesBuilder()
|
||||
LineStringBuilder lsb = new LineStringBuilder(new CoordinatesBuilder()
|
||||
.coordinate(-150.0, 65.0)
|
||||
.coordinate(-250.0, 65.0)
|
||||
.coordinate(-250.0, -65.0)
|
||||
.coordinate(-150.0, -65.0)
|
||||
.close()
|
||||
)
|
||||
.build();
|
||||
assertMultiLineString(shape);
|
||||
.close());
|
||||
|
||||
assertMultiLineString(lsb.buildS4J(), true);
|
||||
assertMultiLineString(lsb.buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testDatelineOGC() {
|
||||
|
@ -286,8 +338,8 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(-179,1)
|
||||
));
|
||||
|
||||
Shape shape = builder.close().build();
|
||||
assertMultiPolygon(shape);
|
||||
assertMultiPolygon(builder.close().buildS4J(), true);
|
||||
assertMultiPolygon(builder.close().buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testDateline() {
|
||||
|
@ -330,8 +382,8 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(-179,1)
|
||||
));
|
||||
|
||||
Shape shape = builder.close().build();
|
||||
assertMultiPolygon(shape);
|
||||
assertMultiPolygon(builder.close().buildS4J(), true);
|
||||
assertMultiPolygon(builder.close().buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testComplexShapeWithHole() {
|
||||
|
@ -405,9 +457,8 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(-85.0000002,37.1317672)
|
||||
)
|
||||
);
|
||||
|
||||
Shape shape = builder.close().build();
|
||||
assertPolygon(shape);
|
||||
assertPolygon(builder.close().buildS4J(), true);
|
||||
assertPolygon(builder.close().buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testShapeWithHoleAtEdgeEndPoints() {
|
||||
|
@ -428,9 +479,8 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(-4, 1)
|
||||
.coordinate(4, 1)
|
||||
));
|
||||
|
||||
Shape shape = builder.close().build();
|
||||
assertPolygon(shape);
|
||||
assertPolygon(builder.close().buildS4J(), true);
|
||||
assertPolygon(builder.close().buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testShapeWithPointOnDateline() {
|
||||
|
@ -440,9 +490,8 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(176, -4)
|
||||
.coordinate(180, 0)
|
||||
);
|
||||
|
||||
Shape shape = builder.close().build();
|
||||
assertPolygon(shape);
|
||||
assertPolygon(builder.close().buildS4J(), true);
|
||||
assertPolygon(builder.close().buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testShapeWithEdgeAlongDateline() {
|
||||
|
@ -454,8 +503,8 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(180, 0)
|
||||
);
|
||||
|
||||
Shape shape = builder.close().build();
|
||||
assertPolygon(shape);
|
||||
assertPolygon(builder.close().buildS4J(), true);
|
||||
assertPolygon(builder.close().buildLucene(), false);
|
||||
|
||||
// test case 2: test the negative side of the dateline
|
||||
builder = new PolygonBuilder(new CoordinatesBuilder()
|
||||
|
@ -465,8 +514,8 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(-176, 4)
|
||||
);
|
||||
|
||||
shape = builder.close().build();
|
||||
assertPolygon(shape);
|
||||
assertPolygon(builder.close().buildS4J(), true);
|
||||
assertPolygon(builder.close().buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testShapeWithBoundaryHoles() {
|
||||
|
@ -486,8 +535,9 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(176, -10)
|
||||
.coordinate(176, 10)
|
||||
));
|
||||
Shape shape = builder.close().build();
|
||||
assertMultiPolygon(shape);
|
||||
|
||||
assertMultiPolygon(builder.close().buildS4J(), true);
|
||||
assertMultiPolygon(builder.close().buildLucene(), false);
|
||||
|
||||
// test case 2: test the negative side of the dateline
|
||||
builder = new PolygonBuilder(
|
||||
|
@ -508,8 +558,9 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(-176, 10)
|
||||
.close()
|
||||
));
|
||||
shape = builder.close().build();
|
||||
assertMultiPolygon(shape);
|
||||
|
||||
assertMultiPolygon(builder.close().buildS4J(), true);
|
||||
assertMultiPolygon(builder.close().buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testShapeWithTangentialHole() {
|
||||
|
@ -529,8 +580,9 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(-180, 5)
|
||||
.coordinate(-177, 10)
|
||||
));
|
||||
Shape shape = builder.close().build();
|
||||
assertMultiPolygon(shape);
|
||||
|
||||
assertMultiPolygon(builder.close().buildS4J(), true);
|
||||
assertMultiPolygon(builder.close().buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testShapeWithInvalidTangentialHole() {
|
||||
|
@ -550,7 +602,11 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(179, -10)
|
||||
.coordinate(164, 0)
|
||||
));
|
||||
Exception e = expectThrows(InvalidShapeException.class, () -> builder.close().build());
|
||||
Exception e;
|
||||
|
||||
e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J());
|
||||
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
|
||||
e = expectThrows(InvalidShapeException.class, () -> builder.close().buildLucene());
|
||||
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
|
||||
}
|
||||
|
||||
|
@ -577,8 +633,8 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(176, -5)
|
||||
.coordinate(172, 0)
|
||||
));
|
||||
Shape shape = builder.close().build();
|
||||
assertMultiPolygon(shape);
|
||||
assertMultiPolygon(builder.close().buildS4J(), true);
|
||||
assertMultiPolygon(builder.close().buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testBoundaryShapeWithInvalidTangentialHole() {
|
||||
|
@ -598,7 +654,10 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(176, -10)
|
||||
.coordinate(-177, 10)
|
||||
));
|
||||
Exception e = expectThrows(InvalidShapeException.class, () -> builder.close().build());
|
||||
Exception e;
|
||||
e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J());
|
||||
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
|
||||
e = expectThrows(InvalidShapeException.class, () -> builder.close().buildLucene());
|
||||
assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior"));
|
||||
}
|
||||
|
||||
|
@ -613,9 +672,8 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(-180, 90)
|
||||
);
|
||||
|
||||
Shape shape = builder.close().build();
|
||||
|
||||
assertPolygon(shape);
|
||||
assertPolygon(builder.close().buildS4J(), true);
|
||||
assertPolygon(builder.close().buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testShapeWithAlternateOrientation() {
|
||||
|
@ -627,8 +685,8 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(180, 0)
|
||||
);
|
||||
|
||||
Shape shape = builder.close().build();
|
||||
assertPolygon(shape);
|
||||
assertPolygon(builder.close().buildS4J(), true);
|
||||
assertPolygon(builder.close().buildLucene(), false);
|
||||
|
||||
// cw: geo core will convert to ccw across the dateline
|
||||
builder = new PolygonBuilder(new CoordinatesBuilder()
|
||||
|
@ -638,9 +696,8 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(180, 0)
|
||||
);
|
||||
|
||||
shape = builder.close().build();
|
||||
|
||||
assertMultiPolygon(shape);
|
||||
assertMultiPolygon(builder.close().buildS4J(), true);
|
||||
assertMultiPolygon(builder.close().buildLucene(), false);
|
||||
}
|
||||
|
||||
public void testInvalidShapeWithConsecutiveDuplicatePoints() {
|
||||
|
@ -651,7 +708,10 @@ public class ShapeBuilderTests extends ESTestCase {
|
|||
.coordinate(-176, 4)
|
||||
.coordinate(180, 0)
|
||||
);
|
||||
Exception e = expectThrows(InvalidShapeException.class, () -> builder.close().build());
|
||||
|
||||
Exception e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J());
|
||||
assertThat(e.getMessage(), containsString("duplicate consecutive coordinates at: ("));
|
||||
e = expectThrows(InvalidShapeException.class, () -> builder.close().buildLucene());
|
||||
assertThat(e.getMessage(), containsString("duplicate consecutive coordinates at: ("));
|
||||
}
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ public class PolygonBuilderTests extends AbstractShapeBuilderTestCase<PolygonBui
|
|||
InvalidShapeException e = expectThrows(InvalidShapeException.class, () -> {
|
||||
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder().coordinate(4, 3).coordinate(3, 2).coordinate(3, 3).close());
|
||||
pb.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(4, 2).coordinate(3, 1).coordinate(4, 1).close()));
|
||||
pb.build();
|
||||
pb.buildS4J();
|
||||
});
|
||||
|
||||
assertEquals("Hole lies outside shell at or near point (4.0, 1.0, NaN)", e.getMessage());
|
||||
|
@ -138,7 +138,7 @@ public class PolygonBuilderTests extends AbstractShapeBuilderTestCase<PolygonBui
|
|||
InvalidShapeException e = expectThrows(InvalidShapeException.class, () -> {
|
||||
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder().coordinate(3, 2).coordinate(4, 1).coordinate(3, 1).close());
|
||||
pb.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(3, 3).coordinate(4, 2).coordinate(4, 3).close()));
|
||||
pb.build();
|
||||
pb.buildS4J();
|
||||
});
|
||||
|
||||
assertEquals("Hole lies outside shell at or near point (4.0, 3.0, NaN)", e.getMessage());
|
||||
|
@ -152,13 +152,13 @@ public class PolygonBuilderTests extends AbstractShapeBuilderTestCase<PolygonBui
|
|||
|
||||
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
|
||||
.coordinate(10, -20).coordinate(100, 0).coordinate(-100, 0).coordinate(20, -45).coordinate(40, -60).close());
|
||||
pb.build(); // Should not throw an exception
|
||||
pb.buildS4J(); // Should not throw an exception
|
||||
}
|
||||
|
||||
public void testPolygonWithUndefinedOrientationDueToCollinearPoints() {
|
||||
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
|
||||
.coordinate(0.0, 0.0).coordinate(1.0, 1.0).coordinate(-1.0, -1.0).close());
|
||||
InvalidShapeException e = expectThrows(InvalidShapeException.class, pb::build);
|
||||
InvalidShapeException e = expectThrows(InvalidShapeException.class, pb::buildS4J);
|
||||
assertEquals("Cannot determine orientation: edges adjacent to (-1.0,-1.0) coincide", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -182,7 +182,7 @@ public class ExternalMapper extends FieldMapper {
|
|||
pointMapper.parse(context.createExternalValueContext(point));
|
||||
|
||||
// Let's add a Dummy Shape
|
||||
Point shape = new PointBuilder(-100, 45).build();
|
||||
Point shape = new PointBuilder(-100, 45).buildS4J();
|
||||
shapeMapper.parse(context.createExternalValueContext(shape));
|
||||
|
||||
context = context.createExternalValueContext(generatedValue);
|
||||
|
|
|
@ -70,7 +70,7 @@ public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase<GeoPolygo
|
|||
while (shapeBuilder == null) {
|
||||
shapeBuilder = RandomShapeGenerator.createShapeWithin(random(), null, ShapeType.POLYGON);
|
||||
}
|
||||
JtsGeometry shape = (JtsGeometry) shapeBuilder.build();
|
||||
JtsGeometry shape = (JtsGeometry) shapeBuilder.buildS4J();
|
||||
Coordinate[] coordinates = shape.getGeom().getCoordinates();
|
||||
ArrayList<GeoPoint> polygonPoints = new ArrayList<>();
|
||||
for (Coordinate coord : coordinates) {
|
||||
|
|
|
@ -123,7 +123,7 @@ public class GeoFilterIT extends ESIntegTestCase {
|
|||
.coordinate(-10, 10)
|
||||
.coordinate(10, -10)
|
||||
.close())
|
||||
.build();
|
||||
.buildS4J();
|
||||
fail("Self intersection not detected");
|
||||
} catch (InvalidShapeException e) {
|
||||
}
|
||||
|
@ -132,14 +132,14 @@ public class GeoFilterIT extends ESIntegTestCase {
|
|||
new PolygonBuilder(new CoordinatesBuilder()
|
||||
.coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close())
|
||||
.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(-5, -5).coordinate(-5, 5).coordinate(5, 5).coordinate(5, -5).close()))
|
||||
.build();
|
||||
.buildS4J();
|
||||
try {
|
||||
// polygon with overlapping hole
|
||||
new PolygonBuilder(new CoordinatesBuilder()
|
||||
.coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close())
|
||||
.hole(new LineStringBuilder(new CoordinatesBuilder()
|
||||
.coordinate(-5, -5).coordinate(-5, 11).coordinate(5, 11).coordinate(5, -5).close()))
|
||||
.build();
|
||||
.buildS4J();
|
||||
|
||||
fail("Self intersection not detected");
|
||||
} catch (InvalidShapeException e) {
|
||||
|
@ -151,7 +151,7 @@ public class GeoFilterIT extends ESIntegTestCase {
|
|||
.coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close())
|
||||
.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(-5, -5).coordinate(-5, 5).coordinate(5, 5).coordinate(5, -5).close()))
|
||||
.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(-5, -6).coordinate(5, -6).coordinate(5, -4).coordinate(-5, -4).close()))
|
||||
.build();
|
||||
.buildS4J();
|
||||
fail("Intersection of holes not detected");
|
||||
} catch (InvalidShapeException e) {
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ public class GeoFilterIT extends ESIntegTestCase {
|
|||
.coordinate(10, 20)
|
||||
.coordinate(10, -10)
|
||||
.close())
|
||||
.build();
|
||||
.buildS4J();
|
||||
fail("Self intersection not detected");
|
||||
} catch (InvalidShapeException e) {
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ public class GeoFilterIT extends ESIntegTestCase {
|
|||
.coordinate(-4, 4)
|
||||
.coordinate(4, 4)
|
||||
.coordinate(4, -4).close()))
|
||||
.build();
|
||||
.buildS4J();
|
||||
}
|
||||
|
||||
public void testShapeRelations() throws Exception {
|
||||
|
|
|
@ -240,7 +240,7 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
|
|||
|
||||
private void assertUnmodified(ShapeBuilder builder) throws IOException {
|
||||
String before = Strings.toString(jsonBuilder().startObject().field("area", builder).endObject());
|
||||
builder.build();
|
||||
builder.buildS4J();
|
||||
String after = Strings.toString(jsonBuilder().startObject().field("area", builder).endObject());
|
||||
assertThat(before, equalTo(after));
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ import org.elasticsearch.common.geo.builders.MultiPointBuilder;
|
|||
import org.elasticsearch.common.geo.builders.PointBuilder;
|
||||
import org.elasticsearch.common.geo.builders.PolygonBuilder;
|
||||
import org.elasticsearch.common.geo.builders.ShapeBuilder;
|
||||
import org.elasticsearch.search.geo.GeoShapeQueryTests;
|
||||
import org.junit.Assert;
|
||||
import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
|
@ -154,7 +153,6 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
|
|||
/**
|
||||
* Creates a random shape useful for randomized testing, NOTE: exercise caution when using this to build random GeometryCollections
|
||||
* as creating a large random number of random shapes can result in massive resource consumption
|
||||
* see: {@link GeoShapeQueryTests#testShapeFilterWithRandomGeoCollection}
|
||||
*
|
||||
* The following options are included
|
||||
* @param nearPoint Create a shape near a provided point
|
||||
|
@ -206,7 +204,7 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
|
|||
numPoints = RandomNumbers.randomIntBetween(r, 5, 25);
|
||||
Coordinate[] coordinates = new Coordinate[numPoints];
|
||||
for (int i=0; i<numPoints; ++i) {
|
||||
p = (Point) createShape(r, nearPoint, within, ShapeType.POINT, false).build();
|
||||
p = (Point) createShape(r, nearPoint, within, ShapeType.POINT, false).buildS4J();
|
||||
coordinates[i] = new Coordinate(p.getX(), p.getY());
|
||||
}
|
||||
// random point order or random linestrings can lead to invalid self-crossing polygons,
|
||||
|
@ -229,7 +227,7 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
|
|||
// intent for ambiguous polygons. Therefore, an invalid oriented dateline crossing polygon could be built.
|
||||
// The validate flag will check for these possibilities and bail if an incorrect geometry is created
|
||||
try {
|
||||
pgb.build();
|
||||
pgb.buildS4J();
|
||||
} catch (AssertionError | InvalidShapeException e) {
|
||||
// jts bug may occasionally misinterpret coordinate order causing an unhelpful ('geom' assertion)
|
||||
// or InvalidShapeException
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.locationtech.jts.geom.MultiLineString;
|
|||
import org.locationtech.jts.geom.MultiPoint;
|
||||
import org.locationtech.jts.geom.MultiPolygon;
|
||||
import org.locationtech.jts.geom.Polygon;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.ShapeCollection;
|
||||
import org.locationtech.spatial4j.shape.impl.GeoCircle;
|
||||
import org.locationtech.spatial4j.shape.impl.RectangleImpl;
|
||||
|
@ -199,7 +198,7 @@ public class ElasticsearchGeoAssertions {
|
|||
}
|
||||
}
|
||||
|
||||
public static void assertEquals(Shape s1, Shape s2) {
|
||||
public static void assertEquals(Object s1, Object s2) {
|
||||
if(s1 instanceof JtsGeometry && s2 instanceof JtsGeometry) {
|
||||
assertEquals((JtsGeometry) s1, (JtsGeometry) s2);
|
||||
} else if(s1 instanceof JtsPoint && s2 instanceof JtsPoint) {
|
||||
|
@ -212,6 +211,17 @@ public class ElasticsearchGeoAssertions {
|
|||
Assert.assertEquals(s1, s2);
|
||||
} else if (s1 instanceof RectangleImpl && s2 instanceof RectangleImpl) {
|
||||
Assert.assertEquals(s1, s2);
|
||||
} else if (s1 instanceof org.apache.lucene.geo.Line[] && s2 instanceof org.apache.lucene.geo.Line[]) {
|
||||
Assert.assertArrayEquals((org.apache.lucene.geo.Line[])s1, (org.apache.lucene.geo.Line[])s2);
|
||||
} else if (s1 instanceof org.apache.lucene.geo.Polygon[] && s2 instanceof org.apache.lucene.geo.Polygon[]) {
|
||||
Assert.assertArrayEquals((org.apache.lucene.geo.Polygon[]) s1, (org.apache.lucene.geo.Polygon[]) s2);
|
||||
} else if ((s1 instanceof org.apache.lucene.geo.Line && s2 instanceof org.apache.lucene.geo.Line)
|
||||
|| (s1 instanceof org.apache.lucene.geo.Polygon && s2 instanceof org.apache.lucene.geo.Polygon)
|
||||
|| (s1 instanceof org.apache.lucene.geo.Rectangle && s2 instanceof org.apache.lucene.geo.Rectangle)
|
||||
|| (s1 instanceof GeoPoint && s2 instanceof GeoPoint)) {
|
||||
Assert.assertEquals(s1, s2);
|
||||
} else if (s1 instanceof Object[] && s2 instanceof Object[]) {
|
||||
Assert.assertArrayEquals((Object[])s1, (Object[])s2);
|
||||
} else {
|
||||
//We want to know the type of the shape because we test shape equality in a special way...
|
||||
//... in particular we test that one ring is equivalent to another ring even if the points are rotated or reversed.
|
||||
|
@ -220,25 +230,50 @@ public class ElasticsearchGeoAssertions {
|
|||
}
|
||||
}
|
||||
|
||||
private static Geometry unwrap(Shape shape) {
|
||||
@Deprecated
|
||||
private static Geometry unwrapJTS(Object shape) {
|
||||
assertThat(shape, instanceOf(JtsGeometry.class));
|
||||
return ((JtsGeometry)shape).getGeom();
|
||||
}
|
||||
|
||||
public static void assertMultiPolygon(Shape shape) {
|
||||
assert(unwrap(shape) instanceof MultiPolygon): "expected MultiPolygon but found " + unwrap(shape).getClass().getName();
|
||||
public static void assertMultiPolygon(Object shape, boolean useJTS) {
|
||||
if (useJTS) {
|
||||
assertTrue("expected MultiPolygon but found " + unwrapJTS(shape).getClass().getName(),
|
||||
unwrapJTS(shape) instanceof MultiPolygon);
|
||||
} else {
|
||||
assertTrue("expected Polygon[] but found " + shape.getClass().getName(),
|
||||
shape instanceof org.apache.lucene.geo.Polygon[]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertPolygon(Shape shape) {
|
||||
assert(unwrap(shape) instanceof Polygon): "expected Polygon but found " + unwrap(shape).getClass().getName();
|
||||
public static void assertPolygon(Object shape, boolean useJTS) {
|
||||
if (useJTS) {
|
||||
assertTrue("expected Polygon but found "
|
||||
+ unwrapJTS(shape).getClass().getName(), unwrapJTS(shape) instanceof Polygon);
|
||||
} else {
|
||||
assertTrue("expected Polygon but found " + shape.getClass().getName(),
|
||||
shape instanceof org.apache.lucene.geo.Polygon);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertLineString(Shape shape) {
|
||||
assert(unwrap(shape) instanceof LineString): "expected LineString but found " + unwrap(shape).getClass().getName();
|
||||
public static void assertLineString(Object shape, boolean useJTS) {
|
||||
if (useJTS) {
|
||||
assertTrue("expected LineString but found "
|
||||
+ unwrapJTS(shape).getClass().getName(), unwrapJTS(shape) instanceof LineString);
|
||||
} else {
|
||||
assertTrue("expected Line but found " + shape.getClass().getName(),
|
||||
shape instanceof org.apache.lucene.geo.Line);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertMultiLineString(Shape shape) {
|
||||
assert(unwrap(shape) instanceof MultiLineString): "expected MultiLineString but found " + unwrap(shape).getClass().getName();
|
||||
public static void assertMultiLineString(Object shape, boolean useJTS) {
|
||||
if (useJTS) {
|
||||
assertTrue("expected MultiLineString but found "
|
||||
+ unwrapJTS(shape).getClass().getName(), unwrapJTS(shape) instanceof MultiLineString);
|
||||
} else {
|
||||
assertTrue("expected Line[] but found " + shape.getClass().getName(),
|
||||
shape instanceof org.apache.lucene.geo.Line[]);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertDistance(String geohash1, String geohash2, Matcher<Double> match) {
|
||||
|
@ -257,11 +292,11 @@ public class ElasticsearchGeoAssertions {
|
|||
|
||||
public static void assertValidException(XContentParser parser, Class<?> expectedException) {
|
||||
try {
|
||||
ShapeParser.parse(parser).build();
|
||||
ShapeParser.parse(parser).buildS4J();
|
||||
Assert.fail("process completed successfully when " + expectedException.getName() + " expected");
|
||||
} catch (Exception e) {
|
||||
assert(e.getClass().equals(expectedException)):
|
||||
"expected " + expectedException.getName() + " but found " + e.getClass().getName();
|
||||
assertTrue("expected " + expectedException.getName() + " but found " + e.getClass().getName(),
|
||||
e.getClass().equals(expectedException));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue