[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:
Nick Knize 2018-11-21 11:15:01 -06:00 committed by GitHub
parent d9c6986b75
commit 3bee25cb70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 761 additions and 235 deletions

View File

@ -233,7 +233,11 @@ public class GeoUtils {
* @return The normalized longitude. * @return The normalized longitude.
*/ */
public static double normalizeLon(double lon) { 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) * @see #normalizePoint(GeoPoint)
*/ */
public static double normalizeLat(double lat) { public static double normalizeLat(double lat) {
if (lat > 90d || lat < -90d) {
lat = centeredModulus(lat, 360); lat = centeredModulus(lat, 360);
if (lat < -90) { if (lat < -90) {
lat = -180 - lat; lat = -180 - lat;
} else if (lat > 90) { } else if (lat > 90) {
lat = 180 - lat; lat = 180 - lat;
} }
return lat; }
// avoid -0.0
return lat + 0d;
} }
/** /**

View File

@ -159,10 +159,15 @@ public class CircleBuilder extends ShapeBuilder<Circle, CircleBuilder> {
} }
@Override @Override
public Circle build() { public Circle buildS4J() {
return SPATIAL_CONTEXT.makeCircle(center.x, center.y, 360 * radius / unit.getEarthCircumference()); 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 @Override
public GeoShapeType type() { public GeoShapeType type() {
return TYPE; return TYPE;

View File

@ -108,10 +108,15 @@ public class EnvelopeBuilder extends ShapeBuilder<Rectangle, EnvelopeBuilder> {
} }
@Override @Override
public Rectangle build() { public Rectangle buildS4J() {
return SPATIAL_CONTEXT.makeRectangle(topLeft.x, bottomRight.x, bottomRight.y, topLeft.y); 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 @Override
public GeoShapeType type() { public GeoShapeType type() {
return TYPE; return TYPE;

View File

@ -31,6 +31,7 @@ import org.locationtech.spatial4j.shape.Shape;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -168,12 +169,12 @@ public class GeometryCollectionBuilder extends ShapeBuilder<Shape, GeometryColle
} }
@Override @Override
public Shape build() { public Shape buildS4J() {
List<Shape> shapes = new ArrayList<>(this.shapes.size()); List<Shape> shapes = new ArrayList<>(this.shapes.size());
for (ShapeBuilder shape : this.shapes) { for (ShapeBuilder shape : this.shapes) {
shapes.add(shape.build()); shapes.add(shape.buildS4J());
} }
if (shapes.size() == 1) if (shapes.size() == 1)
@ -183,6 +184,25 @@ public class GeometryCollectionBuilder extends ShapeBuilder<Shape, GeometryColle
//note: ShapeCollection is probably faster than a Multi* geom. //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 @Override
public int hashCode() { public int hashCode() {
return Objects.hash(shapes); return Objects.hash(shapes);

View File

@ -19,6 +19,7 @@
package org.elasticsearch.common.geo.builders; package org.elasticsearch.common.geo.builders;
import org.apache.lucene.geo.Line;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.GeometryFactory;
@ -35,6 +36,9 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; 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 class LineStringBuilder extends ShapeBuilder<JtsGeometry, LineStringBuilder> {
public static final GeoShapeType TYPE = GeoShapeType.LINESTRING; public static final GeoShapeType TYPE = GeoShapeType.LINESTRING;
@ -101,11 +105,11 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, LineStringBuild
} }
@Override @Override
public JtsGeometry build() { public JtsGeometry buildS4J() {
Coordinate[] coordinates = this.coordinates.toArray(new Coordinate[this.coordinates.size()]); Coordinate[] coordinates = this.coordinates.toArray(new Coordinate[this.coordinates.size()]);
Geometry geometry; Geometry geometry;
if(wrapdateline) { if(wrapdateline) {
ArrayList<LineString> strings = decompose(FACTORY, coordinates, new ArrayList<LineString>()); ArrayList<LineString> strings = decomposeS4J(FACTORY, coordinates, new ArrayList<LineString>());
if(strings.size() == 1) { if(strings.size() == 1) {
geometry = strings.get(0); geometry = strings.get(0);
@ -120,7 +124,23 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, LineStringBuild
return jtsGeometry(geometry); 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[] part : decompose(+DATELINE, coordinates)) {
for(Coordinate[] line : decompose(-DATELINE, part)) { for(Coordinate[] line : decompose(-DATELINE, part)) {
strings.add(factory.createLineString(line)); strings.add(factory.createLineString(line));
@ -129,6 +149,16 @@ public class LineStringBuilder extends ShapeBuilder<JtsGeometry, LineStringBuild
return strings; 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. * Decompose a linestring given as array of coordinates at a vertical line.
* *

View File

@ -19,6 +19,7 @@
package org.elasticsearch.common.geo.builders; package org.elasticsearch.common.geo.builders;
import org.apache.lucene.geo.Line;
import org.elasticsearch.common.geo.GeoShapeType; import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.parsers.GeoWKTParser; import org.elasticsearch.common.geo.parsers.GeoWKTParser;
import org.elasticsearch.common.geo.parsers.ShapeParser; import org.elasticsearch.common.geo.parsers.ShapeParser;
@ -124,12 +125,12 @@ public class MultiLineStringBuilder extends ShapeBuilder<JtsGeometry, MultiLineS
} }
@Override @Override
public JtsGeometry build() { public JtsGeometry buildS4J() {
final Geometry geometry; final Geometry geometry;
if(wrapdateline) { if(wrapdateline) {
ArrayList<LineString> parts = new ArrayList<>(); ArrayList<LineString> parts = new ArrayList<>();
for (LineStringBuilder line : lines) { for (LineStringBuilder line : lines) {
LineStringBuilder.decompose(FACTORY, line.coordinates(false), parts); LineStringBuilder.decomposeS4J(FACTORY, line.coordinates(false), parts);
} }
if(parts.size() == 1) { if(parts.size() == 1) {
geometry = parts.get(0); geometry = parts.get(0);
@ -148,6 +149,27 @@ public class MultiLineStringBuilder extends ShapeBuilder<JtsGeometry, MultiLineS
return jtsGeometry(geometry); 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 @Override
public int hashCode() { public int hashCode() {
return Objects.hash(lines); return Objects.hash(lines);

View File

@ -61,7 +61,7 @@ public class MultiPointBuilder extends ShapeBuilder<XShapeCollection<Point>, Mul
} }
@Override @Override
public XShapeCollection<Point> build() { public XShapeCollection<Point> buildS4J() {
//Could wrap JtsGeometry but probably slower due to conversions to/from JTS in relate() //Could wrap JtsGeometry but probably slower due to conversions to/from JTS in relate()
//MultiPoint geometry = FACTORY.createMultiPoint(points.toArray(new Coordinate[points.size()])); //MultiPoint geometry = FACTORY.createMultiPoint(points.toArray(new Coordinate[points.size()]));
List<Point> shapes = new ArrayList<>(coordinates.size()); List<Point> shapes = new ArrayList<>(coordinates.size());
@ -73,6 +73,17 @@ public class MultiPointBuilder extends ShapeBuilder<XShapeCollection<Point>, Mul
return multiPoints; 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 @Override
public GeoShapeType type() { public GeoShapeType type() {
return TYPE; return TYPE;

View File

@ -31,6 +31,7 @@ import org.locationtech.spatial4j.shape.Shape;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects; import java.util.Objects;
@ -162,19 +163,19 @@ public class MultiPolygonBuilder extends ShapeBuilder<Shape, MultiPolygonBuilder
} }
@Override @Override
public Shape build() { public Shape buildS4J() {
List<Shape> shapes = new ArrayList<>(this.polygons.size()); List<Shape> shapes = new ArrayList<>(this.polygons.size());
if(wrapdateline) { if(wrapdateline) {
for (PolygonBuilder polygon : this.polygons) { for (PolygonBuilder polygon : this.polygons) {
for(Coordinate[][] part : polygon.coordinates()) { for(Coordinate[][] part : polygon.coordinates()) {
shapes.add(jtsGeometry(PolygonBuilder.polygon(FACTORY, part))); shapes.add(jtsGeometry(PolygonBuilder.polygonS4J(FACTORY, part)));
} }
} }
} else { } else {
for (PolygonBuilder polygon : this.polygons) { for (PolygonBuilder polygon : this.polygons) {
shapes.add(jtsGeometry(polygon.toPolygon(FACTORY))); shapes.add(jtsGeometry(polygon.toPolygonS4J(FACTORY)));
} }
} }
if (shapes.size() == 1) if (shapes.size() == 1)
@ -184,6 +185,33 @@ public class MultiPolygonBuilder extends ShapeBuilder<Shape, MultiPolygonBuilder
//note: ShapeCollection is probably faster than a Multi* geom. //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 @Override
public int hashCode() { public int hashCode() {
return Objects.hash(polygons, orientation); return Objects.hash(polygons, orientation);

View File

@ -19,6 +19,7 @@
package org.elasticsearch.common.geo.builders; package org.elasticsearch.common.geo.builders;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoShapeType; import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.parsers.ShapeParser; import org.elasticsearch.common.geo.parsers.ShapeParser;
import org.locationtech.spatial4j.shape.Point; import org.locationtech.spatial4j.shape.Point;
@ -84,10 +85,15 @@ public class PointBuilder extends ShapeBuilder<Point, PointBuilder> {
} }
@Override @Override
public Point build() { public Point buildS4J() {
return SPATIAL_CONTEXT.makePoint(coordinates.get(0).x, coordinates.get(0).y); 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 @Override
public GeoShapeType type() { public GeoShapeType type() {
return TYPE; return TYPE;

View File

@ -46,6 +46,8 @@ import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean; 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; import static org.apache.lucene.geo.GeoUtils.orient;
/** /**
@ -226,8 +228,19 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, PolygonBuilder> {
} }
@Override @Override
public JtsGeometry build() { public JtsGeometry buildS4J() {
return jtsGeometry(buildGeometry(FACTORY, wrapdateline)); 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 { protected XContentBuilder coordinatesArray(XContentBuilder builder, Params params) throws IOException {
@ -250,32 +263,46 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, PolygonBuilder> {
return builder; return builder;
} }
public Geometry buildGeometry(GeometryFactory factory, boolean fixDateline) { public Geometry buildS4JGeometry(GeometryFactory factory, boolean fixDateline) {
if(fixDateline) { if(fixDateline) {
Coordinate[][][] polygons = coordinates(); Coordinate[][][] polygons = coordinates();
return polygons.length == 1 return polygons.length == 1
? polygon(factory, polygons[0]) ? polygonS4J(factory, polygons[0])
: multipolygon(factory, polygons); : multipolygonS4J(factory, polygons);
} else { } else {
return toPolygon(factory); return toPolygonS4J(factory);
} }
} }
public Polygon toPolygon() { public Polygon toPolygonS4J() {
return toPolygon(FACTORY); return toPolygonS4J(FACTORY);
} }
protected Polygon toPolygon(GeometryFactory factory) { protected Polygon toPolygonS4J(GeometryFactory factory) {
final LinearRing shell = linearRing(factory, this.shell.coordinates); final LinearRing shell = linearRingS4J(factory, this.shell.coordinates);
final LinearRing[] holes = new LinearRing[this.holes.size()]; final LinearRing[] holes = new LinearRing[this.holes.size()];
Iterator<LineStringBuilder> iterator = this.holes.iterator(); Iterator<LineStringBuilder> iterator = this.holes.iterator();
for (int i = 0; iterator.hasNext(); i++) { 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); 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()])); return factory.createLinearRing(coordinates.toArray(new Coordinate[coordinates.size()]));
} }
@ -293,7 +320,7 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, PolygonBuilder> {
return shell.numDimensions(); 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 shell = factory.createLinearRing(polygon[0]);
LinearRing[] holes; LinearRing[] holes;
@ -308,6 +335,35 @@ public class PolygonBuilder extends ShapeBuilder<JtsGeometry, PolygonBuilder> {
return factory.createPolygon(shell, holes); 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 * 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 * 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 * @param polygons definition of polygons
* @return a new Multipolygon * @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]; Polygon[] polygonSet = new Polygon[polygons.length];
for (int i = 0; i < polygonSet.length; i++) { for (int i = 0; i < polygonSet.length; i++) {
polygonSet[i] = polygon(factory, polygons[i]); polygonSet[i] = polygonS4J(factory, polygons[i]);
} }
return factory.createMultiPolygon(polygonSet); 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 * 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 * coordinates of this component according to the dateline

View File

@ -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 * the builder looses its validity. So this method should only be called once on a builder
* @return new {@link Shape} defined by the 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) { protected static Coordinate shift(Coordinate coordinate, double dateline) {
if (dateline == 0) { if (dateline == 0) {

View File

@ -496,7 +496,7 @@ public class GeoShapeFieldMapper extends FieldMapper {
if (shapeBuilder == null) { if (shapeBuilder == null) {
return; return;
} }
shape = shapeBuilder.build(); shape = shapeBuilder.buildS4J();
} }
if (fieldType().pointsOnly() == true) { if (fieldType().pointsOnly() == true) {
// index configured for pointsOnly // index configured for pointsOnly

View File

@ -524,7 +524,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
throw new ElasticsearchParseException("failed to parse bounding box. Conflicting definition found " throw new ElasticsearchParseException("failed to parse bounding box. Conflicting definition found "
+ "using well-known text and explicit corners."); + "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[]{r.getMinY(), r.getMaxY(), r.getMinX(), r.getMaxX()};
} }
return new double[]{bottom, top, left, right}; return new double[]{bottom, top, left, right};

View File

@ -472,13 +472,13 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
public static SpatialArgs getArgs(ShapeBuilder shape, ShapeRelation relation) { public static SpatialArgs getArgs(ShapeBuilder shape, ShapeRelation relation) {
switch (relation) { switch (relation) {
case DISJOINT: case DISJOINT:
return new SpatialArgs(SpatialOperation.IsDisjointTo, shape.build()); return new SpatialArgs(SpatialOperation.IsDisjointTo, shape.buildS4J());
case INTERSECTS: case INTERSECTS:
return new SpatialArgs(SpatialOperation.Intersects, shape.build()); return new SpatialArgs(SpatialOperation.Intersects, shape.buildS4J());
case WITHIN: case WITHIN:
return new SpatialArgs(SpatialOperation.IsWithin, shape.build()); return new SpatialArgs(SpatialOperation.IsWithin, shape.buildS4J());
case CONTAINS: case CONTAINS:
return new SpatialArgs(SpatialOperation.Contains, shape.build()); return new SpatialArgs(SpatialOperation.Contains, shape.buildS4J());
default: default:
throw new IllegalArgumentException("invalid relation [" + relation + "]"); throw new IllegalArgumentException("invalid relation [" + relation + "]");
} }

View File

@ -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)) { try (XContentParser parser = createParser(geoJson)) {
parser.nextToken(); 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());
}
} }
} }

View File

@ -19,6 +19,7 @@
package org.elasticsearch.common.geo; package org.elasticsearch.common.geo;
import org.apache.lucene.geo.Line;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData; 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.ContentPath;
import org.elasticsearch.index.mapper.GeoShapeFieldMapper; import org.elasticsearch.index.mapper.GeoShapeFieldMapper;
import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.test.VersionUtils;
import org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions; import org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions;
import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.LineString; import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.LinearRing; import org.locationtech.jts.geom.LinearRing;
import org.locationtech.jts.geom.MultiLineString; import org.locationtech.jts.geom.MultiLineString;
import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.geom.Point; import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.Polygon;
import org.locationtech.spatial4j.exception.InvalidShapeException; import org.locationtech.spatial4j.exception.InvalidShapeException;
@ -49,6 +52,7 @@ import org.locationtech.spatial4j.shape.jts.JtsPoint;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import static org.elasticsearch.common.geo.builders.ShapeBuilder.SPATIAL_CONTEXT; import static org.elasticsearch.common.geo.builders.ShapeBuilder.SPATIAL_CONTEXT;
@ -66,9 +70,9 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
.field("type", "Point") .field("type", "Point")
.startArray("coordinates").value(100.0).value(0.0).endArray() .startArray("coordinates").value(100.0).value(0.0).endArray()
.endObject(); .endObject();
Point expected = GEOMETRY_FACTORY.createPoint(new Coordinate(100.0, 0.0)); 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 @Override
@ -86,9 +90,16 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
lineCoordinates.add(new Coordinate(100, 0)); lineCoordinates.add(new Coordinate(100, 0));
lineCoordinates.add(new Coordinate(101, 1)); lineCoordinates.add(new Coordinate(101, 1));
LineString expected = GEOMETRY_FACTORY.createLineString( try (XContentParser parser = createParser(lineGeoJson)) {
lineCoordinates.toArray(new Coordinate[lineCoordinates.size()])); parser.nextToken();
assertGeometryEquals(jtsGeom(expected), lineGeoJson); 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 @Override
@ -118,7 +129,11 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
new Coordinate(103, 3), 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 { public void testParseCircle() throws IOException {
@ -130,7 +145,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
.endObject(); .endObject();
Circle expected = SPATIAL_CONTEXT.makeCircle(100.0, 0.0, 360 * 100 / GeoUtils.EARTH_EQUATOR); 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 { public void testParseMultiDimensionShapes() throws IOException {
@ -171,9 +186,10 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
.startArray().value(50).value(-30).endArray() .startArray().value(50).value(-30).endArray()
.endArray() .endArray()
.endObject(); .endObject();
Rectangle expected = SPATIAL_CONTEXT.makeRectangle(-50, 50, -30, 30); 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 // test #2: envelope that spans dateline
multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "envelope") multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "envelope")
@ -184,7 +200,9 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
.endObject(); .endObject();
expected = SPATIAL_CONTEXT.makeRectangle(50, -50, -30, 30); 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) // test #3: "envelope" (actually a triangle) with invalid number of coordinates (TopRight, BottomLeft, BottomRight)
multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "envelope") 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(101, 1));
shellCoordinates.add(new Coordinate(100, 1)); shellCoordinates.add(new Coordinate(100, 1));
shellCoordinates.add(new Coordinate(100, 0)); shellCoordinates.add(new Coordinate(100, 0));
Coordinate[] coordinates = shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]);
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()])); LinearRing shell = GEOMETRY_FACTORY.createLinearRing(coordinates);
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null); 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 { 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(101, 1, 10));
shellCoordinates.add(new Coordinate(100, 1, 10)); shellCoordinates.add(new Coordinate(100, 1, 10));
shellCoordinates.add(new Coordinate(100, 0, 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() 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_REPLICAS, 0)
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1) .put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1)
.put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()).build(); .put(IndexMetaData.SETTING_INDEX_UUID, UUIDs.randomBase64UUID()).build();
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()])); LinearRing shell = GEOMETRY_FACTORY.createLinearRing(shellCoordinates.toArray(new Coordinate[shellCoordinates.size()]));
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null); Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null);
Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath()); Mapper.BuilderContext mockBuilderContext = new Mapper.BuilderContext(indexSettings, new ContentPath());
final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext); final GeoShapeFieldMapper mapperBuilder = new GeoShapeFieldMapper.Builder("test").ignoreZValue(true).build(mockBuilderContext);
try (XContentParser parser = createParser(polygonGeoJson)) { try (XContentParser parser = createParser(polygonGeoJson)) {
parser.nextToken(); 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 { public void testInvalidDimensionalPolygon() throws IOException {
@ -464,9 +495,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
parser.nextToken(); 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 // test 2: ccw poly crossing dateline
@ -485,9 +520,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
parser.nextToken(); 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 // test 3: cw poly not crossing dateline
@ -506,9 +545,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
parser.nextToken(); 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 // test 4: cw poly crossing dateline
@ -527,9 +570,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
parser.nextToken(); 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)) { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
parser.nextToken(); 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 // test 2: ccw poly crossing dateline
@ -583,9 +634,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
parser.nextToken(); 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 // test 3: cw poly not crossing dateline
@ -610,11 +665,14 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
parser.nextToken(); 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 // test 4: cw poly crossing dateline
polygonGeoJson = Strings.toString(XContentFactory.jsonBuilder().startObject().field("type", "Polygon") 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)) { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) {
parser.nextToken(); 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);
} }
} }
@ -790,7 +852,15 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
holes[0] = GEOMETRY_FACTORY.createLinearRing( holes[0] = GEOMETRY_FACTORY.createLinearRing(
holeCoordinates.toArray(new Coordinate[holeCoordinates.size()])); holeCoordinates.toArray(new Coordinate[holeCoordinates.size()]));
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, holes); 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 { public void testParseSelfCrossingPolygon() throws IOException {
@ -830,7 +900,11 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
ShapeCollection<?> expected = shapeCollection( ShapeCollection<?> expected = shapeCollection(
SPATIAL_CONTEXT.makePoint(100, 0), SPATIAL_CONTEXT.makePoint(100, 0),
SPATIAL_CONTEXT.makePoint(101, 1.0)); SPATIAL_CONTEXT.makePoint(101, 1.0));
assertGeometryEquals(expected, multiPointGeoJson); assertGeometryEquals(expected, multiPointGeoJson, true);
assertGeometryEquals(new double[][]{
new double[] {100d, 0d},
new double[] {101d, 1d}}, multiPointGeoJson, false);
} }
@Override @Override
@ -900,7 +974,19 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
Shape expected = shapeCollection(withoutHoles, withHoles); 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 // test #2: multipolygon; one polygon with one hole
// this test converting the multipolygon from a ShapeCollection type // 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()])); holes[0] = GEOMETRY_FACTORY.createLinearRing(holeCoordinates.toArray(new Coordinate[holeCoordinates.size()]));
withHoles = GEOMETRY_FACTORY.createPolygon(shell, holes); 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 @Override
@ -967,10 +1063,38 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
.field("type", "Point") .field("type", "Point")
.startArray("coordinates").value(102.0).value(2.0).endArray() .startArray("coordinates").value(102.0).value(2.0).endArray()
.endObject() .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() .endArray()
.endObject(); .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[]{ LineString expectedLineString = GEOMETRY_FACTORY.createLineString(new Coordinate[]{
new Coordinate(100, 0), new Coordinate(100, 0),
new Coordinate(101, 1), new Coordinate(101, 1),
@ -978,9 +1102,35 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
expected[0] = jtsGeom(expectedLineString); expected[0] = jtsGeom(expectedLineString);
Point expectedPoint = GEOMETRY_FACTORY.createPoint(new Coordinate(102.0, 2.0)); Point expectedPoint = GEOMETRY_FACTORY.createPoint(new Coordinate(102.0, 2.0));
expected[1] = new JtsPoint(expectedPoint, SPATIAL_CONTEXT); 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 //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 { public void testThatParserExtractsCorrectTypeAndCoordinatesFromArbitraryJson() throws IOException {
@ -1001,7 +1151,10 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
.endObject(); .endObject();
Point expected = GEOMETRY_FACTORY.createPoint(new Coordinate(100.0, 0.0)); 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);
GeoPoint expectedPt = new GeoPoint(0, 100);
assertGeometryEquals(expectedPt, pointGeoJson, false);
} }
public void testParseOrientationOption() throws IOException { public void testParseOrientationOption() throws IOException {
@ -1030,9 +1183,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
try (XContentParser parser = createParser(polygonGeoJson)) { try (XContentParser parser = createParser(polygonGeoJson)) {
parser.nextToken(); 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) // 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)) { try (XContentParser parser = createParser(polygonGeoJson)) {
parser.nextToken(); 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) // 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)) { try (XContentParser parser = createParser(polygonGeoJson)) {
parser.nextToken(); 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) // 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)) { try (XContentParser parser = createParser(polygonGeoJson)) {
parser.nextToken(); 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) // 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)) { try (XContentParser parser = createParser(polygonGeoJson)) {
parser.nextToken(); 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) // 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)) { try (XContentParser parser = createParser(polygonGeoJson)) {
parser.nextToken(); 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);
} }
} }

View File

@ -19,6 +19,7 @@
package org.elasticsearch.common.geo; package org.elasticsearch.common.geo;
import org.apache.lucene.geo.GeoTestUtil; import org.apache.lucene.geo.GeoTestUtil;
import org.apache.lucene.geo.Line;
import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.Version; import org.elasticsearch.Version;
@ -58,6 +59,7 @@ import org.locationtech.spatial4j.shape.jts.JtsPoint;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import static org.elasticsearch.common.geo.builders.ShapeBuilder.SPATIAL_CONTEXT; import static org.elasticsearch.common.geo.builders.ShapeBuilder.SPATIAL_CONTEXT;
@ -84,9 +86,9 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
return XContentFactory.jsonBuilder().value(wkt); 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); XContentBuilder xContentBuilder = toWKTContent(builder, false);
assertGeometryEquals(expected, xContentBuilder); assertGeometryEquals(expected, xContentBuilder, useJTS);
} }
private void assertMalformed(ShapeBuilder<?, ?> builder) throws IOException { private void assertMalformed(ShapeBuilder<?, ?> builder) throws IOException {
@ -99,7 +101,8 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
GeoPoint p = RandomShapeGenerator.randomPoint(random()); GeoPoint p = RandomShapeGenerator.randomPoint(random());
Coordinate c = new Coordinate(p.lon(), p.lat()); Coordinate c = new Coordinate(p.lon(), p.lat());
Point expected = GEOMETRY_FACTORY.createPoint(c); 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)); assertMalformed(new PointBuilder().coordinate(c));
} }
@ -107,15 +110,25 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
public void testParseMultiPoint() throws IOException { public void testParseMultiPoint() throws IOException {
int numPoints = randomIntBetween(2, 100); int numPoints = randomIntBetween(2, 100);
List<Coordinate> coordinates = new ArrayList<>(numPoints); List<Coordinate> coordinates = new ArrayList<>(numPoints);
Shape[] shapes = new Shape[numPoints];
GeoPoint p = new GeoPoint();
for (int i = 0; i < numPoints; ++i) { for (int i = 0; i < numPoints; ++i) {
p.reset(GeoTestUtil.nextLatitude(), GeoTestUtil.nextLongitude()); coordinates.add(new Coordinate(GeoTestUtil.nextLongitude(), GeoTestUtil.nextLatitude()));
coordinates.add(new Coordinate(p.lon(), p.lat())); }
shapes[i] = SPATIAL_CONTEXT.makePoint(p.lon(), p.lat());
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); 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)); assertMalformed(new MultiPointBuilder(coordinates));
} }
@ -133,8 +146,17 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
@Override @Override
public void testParseLineString() throws IOException { public void testParseLineString() throws IOException {
List<Coordinate> coordinates = randomLineStringCoords(); List<Coordinate> coordinates = randomLineStringCoords();
LineString expected = GEOMETRY_FACTORY.createLineString(coordinates.toArray(new Coordinate[coordinates.size()])); 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 @Override
@ -148,9 +170,18 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
lineStrings.add(GEOMETRY_FACTORY.createLineString(coords)); lineStrings.add(GEOMETRY_FACTORY.createLineString(coords));
builder.linestring(new LineStringBuilder(lsc)); builder.linestring(new LineStringBuilder(lsc));
} }
MultiLineString expected = GEOMETRY_FACTORY.createMultiLineString( MultiLineString expected = GEOMETRY_FACTORY.createMultiLineString(
lineStrings.toArray(new LineString[lineStrings.size()])); 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); assertMalformed(builder);
} }
@ -159,9 +190,10 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
PolygonBuilder builder = PolygonBuilder.class.cast( PolygonBuilder builder = PolygonBuilder.class.cast(
RandomShapeGenerator.createShape(random(), RandomShapeGenerator.ShapeType.POLYGON)); RandomShapeGenerator.createShape(random(), RandomShapeGenerator.ShapeType.POLYGON));
Coordinate[] coords = builder.coordinates()[0][0]; Coordinate[] coords = builder.coordinates()[0][0];
LinearRing shell = GEOMETRY_FACTORY.createLinearRing(coords); LinearRing shell = GEOMETRY_FACTORY.createLinearRing(coords);
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null); Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null);
assertExpected(jtsGeom(expected), builder); assertExpected(jtsGeom(expected), builder, true);
assertMalformed(builder); assertMalformed(builder);
} }
@ -180,8 +212,9 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
shell = GEOMETRY_FACTORY.createLinearRing(coordinates); shell = GEOMETRY_FACTORY.createLinearRing(coordinates);
shapes[i] = GEOMETRY_FACTORY.createPolygon(shell, null); shapes[i] = GEOMETRY_FACTORY.createPolygon(shell, null);
} }
Shape expected = shapeCollection(shapes); Shape expected = shapeCollection(shapes);
assertExpected(expected, builder); assertExpected(expected, builder, true);
assertMalformed(builder); assertMalformed(builder);
} }
@ -210,8 +243,15 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
holes[0] = GEOMETRY_FACTORY.createLinearRing( holes[0] = GEOMETRY_FACTORY.createLinearRing(
holeCoordinates.toArray(new Coordinate[holeCoordinates.size()])); holeCoordinates.toArray(new Coordinate[holeCoordinates.size()]));
Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, holes); 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); assertMalformed(polygonWithHole);
} }
@ -370,8 +410,10 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
public void testParseEnvelope() throws IOException { public void testParseEnvelope() throws IOException {
org.apache.lucene.geo.Rectangle r = GeoTestUtil.nextBox(); org.apache.lucene.geo.Rectangle r = GeoTestUtil.nextBox();
EnvelopeBuilder builder = new EnvelopeBuilder(new Coordinate(r.minLon, r.maxLat), new Coordinate(r.maxLon, r.minLat)); 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); 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); assertMalformed(builder);
} }
@ -386,10 +428,15 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase {
// assert empty shape collection // assert empty shape collection
GeometryCollectionBuilder builder = new GeometryCollectionBuilder(); GeometryCollectionBuilder builder = new GeometryCollectionBuilder();
Shape[] expected = new Shape[0]; 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 { } else {
GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random()); GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random());
assertExpected(gcb.build(), gcb); assertExpected(gcb.buildS4J(), gcb, true);
assertExpected(gcb.buildLucene(), gcb, false);
} }
} }

View File

@ -36,7 +36,6 @@ import org.locationtech.spatial4j.exception.InvalidShapeException;
import org.locationtech.spatial4j.shape.Circle; import org.locationtech.spatial4j.shape.Circle;
import org.locationtech.spatial4j.shape.Point; import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Rectangle; import org.locationtech.spatial4j.shape.Rectangle;
import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.impl.PointImpl; import org.locationtech.spatial4j.shape.impl.PointImpl;
import static org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions.assertMultiLineString; import static org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions.assertMultiLineString;
@ -49,65 +48,109 @@ import static org.hamcrest.Matchers.containsString;
public class ShapeBuilderTests extends ESTestCase { public class ShapeBuilderTests extends ESTestCase {
public void testNewPoint() { 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(-100D, point.getX(), 0.0d);
assertEquals(45D, point.getY(), 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() { 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(-45D, rectangle.getMinX(), 0.0d);
assertEquals(-30D, rectangle.getMinY(), 0.0d); assertEquals(-30D, rectangle.getMinY(), 0.0d);
assertEquals(45D, rectangle.getMaxX(), 0.0d); assertEquals(45D, rectangle.getMaxX(), 0.0d);
assertEquals(30D, rectangle.getMaxY(), 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() { 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) .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(0), new Coordinate(-45, 30));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30)); assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30)); assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
assertEquals(exterior.getCoordinateN(3), 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() { 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)) .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(0), new Coordinate(-45, 30));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30)); assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30)); assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
assertEquals(exterior.getCoordinateN(3), 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() { 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), .coordinates(new Coordinate(-45, 30), new Coordinate(45, 30),
new Coordinate(45, -30), 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(0), new Coordinate(-45, 30));
assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30)); assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30));
assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30)); assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30));
assertEquals(exterior.getCoordinateN(3), 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() { public void testLineStringBuilder() {
// Building a simple LineString // Building a simple LineString
new LineStringBuilder(new CoordinatesBuilder() LineStringBuilder lsb = new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-130.0, 55.0) .coordinate(-130.0, 55.0)
.coordinate(-130.0, -40.0) .coordinate(-130.0, -40.0)
.coordinate(-15.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, 50.0)
.coordinate(-45.0, -15.0) .coordinate(-45.0, -15.0)
.coordinate(-110.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 // Building a linestring that needs to be wrapped
new LineStringBuilder(new CoordinatesBuilder() lsb = new LineStringBuilder(new CoordinatesBuilder()
.coordinate(100.0, 50.0) .coordinate(100.0, 50.0)
.coordinate(110.0, -40.0) .coordinate(110.0, -40.0)
.coordinate(240.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, 60.0)
.coordinate(200.0, -30.0) .coordinate(200.0, -30.0)
.coordinate(130.0, -30.0) .coordinate(130.0, -30.0)
.coordinate(130.0, 60.0) .coordinate(130.0, 60.0));
)
.build(); lsb.buildS4J();
lsb.buildLucene();
// Building a lineString on the dateline // Building a lineString on the dateline
new LineStringBuilder(new CoordinatesBuilder() lsb = new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-180.0, 80.0) .coordinate(-180.0, 80.0)
.coordinate(-180.0, 40.0) .coordinate(-180.0, 40.0)
.coordinate(-180.0, -40.0) .coordinate(-180.0, -40.0)
.coordinate(-180.0, -80.0) .coordinate(-180.0, -80.0));
)
.build(); lsb.buildS4J();
lsb.buildLucene();
// Building a lineString on the dateline // Building a lineString on the dateline
new LineStringBuilder(new CoordinatesBuilder() lsb = new LineStringBuilder(new CoordinatesBuilder()
.coordinate(180.0, 80.0) .coordinate(180.0, 80.0)
.coordinate(180.0, 40.0) .coordinate(180.0, 40.0)
.coordinate(180.0, -40.0) .coordinate(180.0, -40.0)
.coordinate(180.0, -80.0) .coordinate(180.0, -80.0));
)
.build(); lsb.buildS4J();
lsb.buildLucene();
} }
public void testMultiLineString() { public void testMultiLineString() {
new MultiLineStringBuilder() MultiLineStringBuilder mlsb = new MultiLineStringBuilder()
.linestring(new LineStringBuilder(new CoordinatesBuilder() .linestring(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-100.0, 50.0) .coordinate(-100.0, 50.0)
.coordinate(50.0, 50.0) .coordinate(50.0, 50.0)
@ -164,8 +213,9 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(50.0, 0.0) .coordinate(50.0, 0.0)
.coordinate(-100.0, 0.0) .coordinate(-100.0, 0.0)
) )
) );
.build(); mlsb.buildS4J();
mlsb.buildLucene();
// LineString that needs to be wrapped // LineString that needs to be wrapped
new MultiLineStringBuilder() new MultiLineStringBuilder()
@ -182,8 +232,10 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(200.0, 0.0) .coordinate(200.0, 0.0)
.coordinate(150.0, 0.0) .coordinate(150.0, 0.0)
) )
) );
.build();
mlsb.buildS4J();
mlsb.buildLucene();
} }
public void testPolygonSelfIntersection() { 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) .coordinate(-40.0, -50.0)
.coordinate(40.0, -50.0).close()); .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")); assertThat(e.getMessage(), containsString("Self-intersection at or near point (0.0"));
} }
/** note: only supported by S4J at the moment */
public void testGeoCircle() { public void testGeoCircle() {
double earthCircumference = 40075016.69; 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((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(0, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); 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((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(180, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); 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((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(-180, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); 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((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(0, 90, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); 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((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(0, -90, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); assertEquals(new PointImpl(0, -90, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
double randomLat = (randomDouble() * 180) - 90; double randomLat = (randomDouble() * 180) - 90;
double randomLon = (randomDouble() * 360) - 180; double randomLon = (randomDouble() * 360) - 180;
double randomRadius = randomIntBetween(1, (int) earthCircumference / 4); 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((360 * randomRadius) / earthCircumference, circle.getRadius(), 0.00000001);
assertEquals(new PointImpl(randomLon, randomLat, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); assertEquals(new PointImpl(randomLon, randomLat, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter());
} }
public void testPolygonWrapping() { public void testPolygonWrapping() {
Shape shape = new PolygonBuilder(new CoordinatesBuilder() PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-150.0, 65.0) .coordinate(-150.0, 65.0)
.coordinate(-250.0, 65.0) .coordinate(-250.0, 65.0)
.coordinate(-250.0, -65.0) .coordinate(-250.0, -65.0)
.coordinate(-150.0, -65.0) .coordinate(-150.0, -65.0)
.close() .close());
)
.build();
assertMultiPolygon(shape); assertMultiPolygon(pb.buildS4J(), true);
assertMultiPolygon(pb.buildLucene(), false);
} }
public void testLineStringWrapping() { public void testLineStringWrapping() {
Shape shape = new LineStringBuilder(new CoordinatesBuilder() LineStringBuilder lsb = new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-150.0, 65.0) .coordinate(-150.0, 65.0)
.coordinate(-250.0, 65.0) .coordinate(-250.0, 65.0)
.coordinate(-250.0, -65.0) .coordinate(-250.0, -65.0)
.coordinate(-150.0, -65.0) .coordinate(-150.0, -65.0)
.close() .close());
)
.build(); assertMultiLineString(lsb.buildS4J(), true);
assertMultiLineString(shape); assertMultiLineString(lsb.buildLucene(), false);
} }
public void testDatelineOGC() { public void testDatelineOGC() {
@ -286,8 +338,8 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(-179,1) .coordinate(-179,1)
)); ));
Shape shape = builder.close().build(); assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(shape); assertMultiPolygon(builder.close().buildLucene(), false);
} }
public void testDateline() { public void testDateline() {
@ -330,8 +382,8 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(-179,1) .coordinate(-179,1)
)); ));
Shape shape = builder.close().build(); assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(shape); assertMultiPolygon(builder.close().buildLucene(), false);
} }
public void testComplexShapeWithHole() { public void testComplexShapeWithHole() {
@ -405,9 +457,8 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(-85.0000002,37.1317672) .coordinate(-85.0000002,37.1317672)
) )
); );
assertPolygon(builder.close().buildS4J(), true);
Shape shape = builder.close().build(); assertPolygon(builder.close().buildLucene(), false);
assertPolygon(shape);
} }
public void testShapeWithHoleAtEdgeEndPoints() { public void testShapeWithHoleAtEdgeEndPoints() {
@ -428,9 +479,8 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(-4, 1) .coordinate(-4, 1)
.coordinate(4, 1) .coordinate(4, 1)
)); ));
assertPolygon(builder.close().buildS4J(), true);
Shape shape = builder.close().build(); assertPolygon(builder.close().buildLucene(), false);
assertPolygon(shape);
} }
public void testShapeWithPointOnDateline() { public void testShapeWithPointOnDateline() {
@ -440,9 +490,8 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(176, -4) .coordinate(176, -4)
.coordinate(180, 0) .coordinate(180, 0)
); );
assertPolygon(builder.close().buildS4J(), true);
Shape shape = builder.close().build(); assertPolygon(builder.close().buildLucene(), false);
assertPolygon(shape);
} }
public void testShapeWithEdgeAlongDateline() { public void testShapeWithEdgeAlongDateline() {
@ -454,8 +503,8 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(180, 0) .coordinate(180, 0)
); );
Shape shape = builder.close().build(); assertPolygon(builder.close().buildS4J(), true);
assertPolygon(shape); assertPolygon(builder.close().buildLucene(), false);
// test case 2: test the negative side of the dateline // test case 2: test the negative side of the dateline
builder = new PolygonBuilder(new CoordinatesBuilder() builder = new PolygonBuilder(new CoordinatesBuilder()
@ -465,8 +514,8 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(-176, 4) .coordinate(-176, 4)
); );
shape = builder.close().build(); assertPolygon(builder.close().buildS4J(), true);
assertPolygon(shape); assertPolygon(builder.close().buildLucene(), false);
} }
public void testShapeWithBoundaryHoles() { public void testShapeWithBoundaryHoles() {
@ -486,8 +535,9 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(176, -10) .coordinate(176, -10)
.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 // test case 2: test the negative side of the dateline
builder = new PolygonBuilder( builder = new PolygonBuilder(
@ -508,8 +558,9 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(-176, 10) .coordinate(-176, 10)
.close() .close()
)); ));
shape = builder.close().build();
assertMultiPolygon(shape); assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(builder.close().buildLucene(), false);
} }
public void testShapeWithTangentialHole() { public void testShapeWithTangentialHole() {
@ -529,8 +580,9 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(-180, 5) .coordinate(-180, 5)
.coordinate(-177, 10) .coordinate(-177, 10)
)); ));
Shape shape = builder.close().build();
assertMultiPolygon(shape); assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(builder.close().buildLucene(), false);
} }
public void testShapeWithInvalidTangentialHole() { public void testShapeWithInvalidTangentialHole() {
@ -550,7 +602,11 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(179, -10) .coordinate(179, -10)
.coordinate(164, 0) .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")); 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(176, -5)
.coordinate(172, 0) .coordinate(172, 0)
)); ));
Shape shape = builder.close().build(); assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(shape); assertMultiPolygon(builder.close().buildLucene(), false);
} }
public void testBoundaryShapeWithInvalidTangentialHole() { public void testBoundaryShapeWithInvalidTangentialHole() {
@ -598,7 +654,10 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(176, -10) .coordinate(176, -10)
.coordinate(-177, 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")); 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) .coordinate(-180, 90)
); );
Shape shape = builder.close().build(); assertPolygon(builder.close().buildS4J(), true);
assertPolygon(builder.close().buildLucene(), false);
assertPolygon(shape);
} }
public void testShapeWithAlternateOrientation() { public void testShapeWithAlternateOrientation() {
@ -627,8 +685,8 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(180, 0) .coordinate(180, 0)
); );
Shape shape = builder.close().build(); assertPolygon(builder.close().buildS4J(), true);
assertPolygon(shape); assertPolygon(builder.close().buildLucene(), false);
// cw: geo core will convert to ccw across the dateline // cw: geo core will convert to ccw across the dateline
builder = new PolygonBuilder(new CoordinatesBuilder() builder = new PolygonBuilder(new CoordinatesBuilder()
@ -638,9 +696,8 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(180, 0) .coordinate(180, 0)
); );
shape = builder.close().build(); assertMultiPolygon(builder.close().buildS4J(), true);
assertMultiPolygon(builder.close().buildLucene(), false);
assertMultiPolygon(shape);
} }
public void testInvalidShapeWithConsecutiveDuplicatePoints() { public void testInvalidShapeWithConsecutiveDuplicatePoints() {
@ -651,7 +708,10 @@ public class ShapeBuilderTests extends ESTestCase {
.coordinate(-176, 4) .coordinate(-176, 4)
.coordinate(180, 0) .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: (")); assertThat(e.getMessage(), containsString("duplicate consecutive coordinates at: ("));
} }

View File

@ -128,7 +128,7 @@ public class PolygonBuilderTests extends AbstractShapeBuilderTestCase<PolygonBui
InvalidShapeException e = expectThrows(InvalidShapeException.class, () -> { InvalidShapeException e = expectThrows(InvalidShapeException.class, () -> {
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder().coordinate(4, 3).coordinate(3, 2).coordinate(3, 3).close()); 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.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()); 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, () -> { InvalidShapeException e = expectThrows(InvalidShapeException.class, () -> {
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder().coordinate(3, 2).coordinate(4, 1).coordinate(3, 1).close()); 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.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()); 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() PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(10, -20).coordinate(100, 0).coordinate(-100, 0).coordinate(20, -45).coordinate(40, -60).close()); .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() { public void testPolygonWithUndefinedOrientationDueToCollinearPoints() {
PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder() PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(0.0, 0.0).coordinate(1.0, 1.0).coordinate(-1.0, -1.0).close()); .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()); assertEquals("Cannot determine orientation: edges adjacent to (-1.0,-1.0) coincide", e.getMessage());
} }
} }

View File

@ -182,7 +182,7 @@ public class ExternalMapper extends FieldMapper {
pointMapper.parse(context.createExternalValueContext(point)); pointMapper.parse(context.createExternalValueContext(point));
// Let's add a Dummy Shape // 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)); shapeMapper.parse(context.createExternalValueContext(shape));
context = context.createExternalValueContext(generatedValue); context = context.createExternalValueContext(generatedValue);

View File

@ -70,7 +70,7 @@ public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase<GeoPolygo
while (shapeBuilder == null) { while (shapeBuilder == null) {
shapeBuilder = RandomShapeGenerator.createShapeWithin(random(), null, ShapeType.POLYGON); shapeBuilder = RandomShapeGenerator.createShapeWithin(random(), null, ShapeType.POLYGON);
} }
JtsGeometry shape = (JtsGeometry) shapeBuilder.build(); JtsGeometry shape = (JtsGeometry) shapeBuilder.buildS4J();
Coordinate[] coordinates = shape.getGeom().getCoordinates(); Coordinate[] coordinates = shape.getGeom().getCoordinates();
ArrayList<GeoPoint> polygonPoints = new ArrayList<>(); ArrayList<GeoPoint> polygonPoints = new ArrayList<>();
for (Coordinate coord : coordinates) { for (Coordinate coord : coordinates) {

View File

@ -123,7 +123,7 @@ public class GeoFilterIT extends ESIntegTestCase {
.coordinate(-10, 10) .coordinate(-10, 10)
.coordinate(10, -10) .coordinate(10, -10)
.close()) .close())
.build(); .buildS4J();
fail("Self intersection not detected"); fail("Self intersection not detected");
} catch (InvalidShapeException e) { } catch (InvalidShapeException e) {
} }
@ -132,14 +132,14 @@ public class GeoFilterIT extends ESIntegTestCase {
new PolygonBuilder(new CoordinatesBuilder() new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close()) .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, -5).coordinate(-5, 5).coordinate(5, 5).coordinate(5, -5).close()))
.build(); .buildS4J();
try { try {
// polygon with overlapping hole // polygon with overlapping hole
new PolygonBuilder(new CoordinatesBuilder() new PolygonBuilder(new CoordinatesBuilder()
.coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close()) .coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close())
.hole(new LineStringBuilder(new CoordinatesBuilder() .hole(new LineStringBuilder(new CoordinatesBuilder()
.coordinate(-5, -5).coordinate(-5, 11).coordinate(5, 11).coordinate(5, -5).close())) .coordinate(-5, -5).coordinate(-5, 11).coordinate(5, 11).coordinate(5, -5).close()))
.build(); .buildS4J();
fail("Self intersection not detected"); fail("Self intersection not detected");
} catch (InvalidShapeException e) { } 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()) .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, -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())) .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"); fail("Intersection of holes not detected");
} catch (InvalidShapeException e) { } catch (InvalidShapeException e) {
} }
@ -167,7 +167,7 @@ public class GeoFilterIT extends ESIntegTestCase {
.coordinate(10, 20) .coordinate(10, 20)
.coordinate(10, -10) .coordinate(10, -10)
.close()) .close())
.build(); .buildS4J();
fail("Self intersection not detected"); fail("Self intersection not detected");
} catch (InvalidShapeException e) { } catch (InvalidShapeException e) {
} }
@ -190,7 +190,7 @@ public class GeoFilterIT extends ESIntegTestCase {
.coordinate(-4, 4) .coordinate(-4, 4)
.coordinate(4, 4) .coordinate(4, 4)
.coordinate(4, -4).close())) .coordinate(4, -4).close()))
.build(); .buildS4J();
} }
public void testShapeRelations() throws Exception { public void testShapeRelations() throws Exception {

View File

@ -240,7 +240,7 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
private void assertUnmodified(ShapeBuilder builder) throws IOException { private void assertUnmodified(ShapeBuilder builder) throws IOException {
String before = Strings.toString(jsonBuilder().startObject().field("area", builder).endObject()); String before = Strings.toString(jsonBuilder().startObject().field("area", builder).endObject());
builder.build(); builder.buildS4J();
String after = Strings.toString(jsonBuilder().startObject().field("area", builder).endObject()); String after = Strings.toString(jsonBuilder().startObject().field("area", builder).endObject());
assertThat(before, equalTo(after)); assertThat(before, equalTo(after));
} }

View File

@ -32,7 +32,6 @@ import org.elasticsearch.common.geo.builders.MultiPointBuilder;
import org.elasticsearch.common.geo.builders.PointBuilder; import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.builders.PolygonBuilder; import org.elasticsearch.common.geo.builders.PolygonBuilder;
import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.search.geo.GeoShapeQueryTests;
import org.junit.Assert; import org.junit.Assert;
import org.locationtech.spatial4j.context.jts.JtsSpatialContext; import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
import org.locationtech.spatial4j.distance.DistanceUtils; 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 * 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 * as creating a large random number of random shapes can result in massive resource consumption
* see: {@link GeoShapeQueryTests#testShapeFilterWithRandomGeoCollection}
* *
* The following options are included * The following options are included
* @param nearPoint Create a shape near a provided point * @param nearPoint Create a shape near a provided point
@ -206,7 +204,7 @@ public class RandomShapeGenerator extends RandomGeoGenerator {
numPoints = RandomNumbers.randomIntBetween(r, 5, 25); numPoints = RandomNumbers.randomIntBetween(r, 5, 25);
Coordinate[] coordinates = new Coordinate[numPoints]; Coordinate[] coordinates = new Coordinate[numPoints];
for (int i=0; i<numPoints; ++i) { 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()); coordinates[i] = new Coordinate(p.getX(), p.getY());
} }
// random point order or random linestrings can lead to invalid self-crossing polygons, // 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. // 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 // The validate flag will check for these possibilities and bail if an incorrect geometry is created
try { try {
pgb.build(); pgb.buildS4J();
} catch (AssertionError | InvalidShapeException e) { } catch (AssertionError | InvalidShapeException e) {
// jts bug may occasionally misinterpret coordinate order causing an unhelpful ('geom' assertion) // jts bug may occasionally misinterpret coordinate order causing an unhelpful ('geom' assertion)
// or InvalidShapeException // or InvalidShapeException

View File

@ -33,7 +33,6 @@ import org.locationtech.jts.geom.MultiLineString;
import org.locationtech.jts.geom.MultiPoint; import org.locationtech.jts.geom.MultiPoint;
import org.locationtech.jts.geom.MultiPolygon; import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.geom.Polygon; import org.locationtech.jts.geom.Polygon;
import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.ShapeCollection; import org.locationtech.spatial4j.shape.ShapeCollection;
import org.locationtech.spatial4j.shape.impl.GeoCircle; import org.locationtech.spatial4j.shape.impl.GeoCircle;
import org.locationtech.spatial4j.shape.impl.RectangleImpl; 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) { if(s1 instanceof JtsGeometry && s2 instanceof JtsGeometry) {
assertEquals((JtsGeometry) s1, (JtsGeometry) s2); assertEquals((JtsGeometry) s1, (JtsGeometry) s2);
} else if(s1 instanceof JtsPoint && s2 instanceof JtsPoint) { } else if(s1 instanceof JtsPoint && s2 instanceof JtsPoint) {
@ -212,6 +211,17 @@ public class ElasticsearchGeoAssertions {
Assert.assertEquals(s1, s2); Assert.assertEquals(s1, s2);
} else if (s1 instanceof RectangleImpl && s2 instanceof RectangleImpl) { } else if (s1 instanceof RectangleImpl && s2 instanceof RectangleImpl) {
Assert.assertEquals(s1, s2); 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 { } else {
//We want to know the type of the shape because we test shape equality in a special way... //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. //... 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)); assertThat(shape, instanceOf(JtsGeometry.class));
return ((JtsGeometry)shape).getGeom(); return ((JtsGeometry)shape).getGeom();
} }
public static void assertMultiPolygon(Shape shape) { public static void assertMultiPolygon(Object shape, boolean useJTS) {
assert(unwrap(shape) instanceof MultiPolygon): "expected MultiPolygon but found " + unwrap(shape).getClass().getName(); 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) { public static void assertPolygon(Object shape, boolean useJTS) {
assert(unwrap(shape) instanceof Polygon): "expected Polygon but found " + unwrap(shape).getClass().getName(); 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) { public static void assertLineString(Object shape, boolean useJTS) {
assert(unwrap(shape) instanceof LineString): "expected LineString but found " + unwrap(shape).getClass().getName(); 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) { public static void assertMultiLineString(Object shape, boolean useJTS) {
assert(unwrap(shape) instanceof MultiLineString): "expected MultiLineString but found " + unwrap(shape).getClass().getName(); 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) { 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) { public static void assertValidException(XContentParser parser, Class<?> expectedException) {
try { try {
ShapeParser.parse(parser).build(); ShapeParser.parse(parser).buildS4J();
Assert.fail("process completed successfully when " + expectedException.getName() + " expected"); Assert.fail("process completed successfully when " + expectedException.getName() + " expected");
} catch (Exception e) { } catch (Exception e) {
assert(e.getClass().equals(expectedException)): assertTrue("expected " + expectedException.getName() + " but found " + e.getClass().getName(),
"expected " + expectedException.getName() + " but found " + e.getClass().getName(); e.getClass().equals(expectedException));
} }
} }
} }