From 3bee25cb700208311ef751b1f460342bdba4ec70 Mon Sep 17 00:00:00 2001 From: Nick Knize Date: Wed, 21 Nov 2018 11:15:01 -0600 Subject: [PATCH] [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. --- .../elasticsearch/common/geo/GeoUtils.java | 21 +- .../common/geo/builders/CircleBuilder.java | 7 +- .../common/geo/builders/EnvelopeBuilder.java | 7 +- .../builders/GeometryCollectionBuilder.java | 24 +- .../geo/builders/LineStringBuilder.java | 36 ++- .../geo/builders/MultiLineStringBuilder.java | 26 +- .../geo/builders/MultiPointBuilder.java | 13 +- .../geo/builders/MultiPolygonBuilder.java | 34 +- .../common/geo/builders/PointBuilder.java | 8 +- .../common/geo/builders/PolygonBuilder.java | 94 +++++- .../common/geo/builders/ShapeBuilder.java | 9 +- .../index/mapper/GeoShapeFieldMapper.java | 2 +- .../query/GeoBoundingBoxQueryBuilder.java | 2 +- .../index/query/GeoShapeQueryBuilder.java | 8 +- .../common/geo/BaseGeoParsingTestCase.java | 8 +- .../common/geo/GeoJsonShapeParserTests.java | 293 ++++++++++++++---- .../common/geo/GeoWKTShapeParserTests.java | 81 ++++- .../common/geo/ShapeBuilderTests.java | 228 +++++++++----- .../geo/builders/PolygonBuilderTests.java | 8 +- .../index/mapper/ExternalMapper.java | 2 +- .../query/GeoPolygonQueryBuilderTests.java | 2 +- .../elasticsearch/search/geo/GeoFilterIT.java | 12 +- .../search/geo/GeoShapeQueryTests.java | 2 +- .../test/geo/RandomShapeGenerator.java | 6 +- .../hamcrest/ElasticsearchGeoAssertions.java | 63 +++- 25 files changed, 761 insertions(+), 235 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java b/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java index 2f3443639cd..795cc235ce7 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java +++ b/server/src/main/java/org/elasticsearch/common/geo/GeoUtils.java @@ -233,7 +233,11 @@ public class GeoUtils { * @return The normalized longitude. */ public static double normalizeLon(double lon) { - return centeredModulus(lon, 360); + if (lon > 180d || lon <= -180d) { + lon = centeredModulus(lon, 360); + } + // avoid -0.0 + return lon + 0d; } /** @@ -250,13 +254,16 @@ public class GeoUtils { * @see #normalizePoint(GeoPoint) */ public static double normalizeLat(double lat) { - lat = centeredModulus(lat, 360); - if (lat < -90) { - lat = -180 - lat; - } else if (lat > 90) { - lat = 180 - lat; + if (lat > 90d || lat < -90d) { + lat = centeredModulus(lat, 360); + if (lat < -90) { + lat = -180 - lat; + } else if (lat > 90) { + lat = 180 - lat; + } } - return lat; + // avoid -0.0 + return lat + 0d; } /** diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/CircleBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/CircleBuilder.java index c6a0743980f..5e2b6cc1c44 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/CircleBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/CircleBuilder.java @@ -159,10 +159,15 @@ public class CircleBuilder extends ShapeBuilder { } @Override - public Circle build() { + public Circle buildS4J() { return SPATIAL_CONTEXT.makeCircle(center.x, center.y, 360 * radius / unit.getEarthCircumference()); } + @Override + public Object buildLucene() { + throw new UnsupportedOperationException("CIRCLE geometry is not supported"); + } + @Override public GeoShapeType type() { return TYPE; diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/EnvelopeBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/EnvelopeBuilder.java index a878a7c6d86..5f69f4ad44d 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/EnvelopeBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/EnvelopeBuilder.java @@ -108,10 +108,15 @@ public class EnvelopeBuilder extends ShapeBuilder { } @Override - public Rectangle build() { + public Rectangle buildS4J() { return SPATIAL_CONTEXT.makeRectangle(topLeft.x, bottomRight.x, bottomRight.y, topLeft.y); } + @Override + public org.apache.lucene.geo.Rectangle buildLucene() { + return new org.apache.lucene.geo.Rectangle(bottomRight.y, topLeft.y, topLeft.x, bottomRight.x); + } + @Override public GeoShapeType type() { return TYPE; diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/GeometryCollectionBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/GeometryCollectionBuilder.java index d2ff86ea63c..b6e94c012c6 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/GeometryCollectionBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/GeometryCollectionBuilder.java @@ -31,6 +31,7 @@ import org.locationtech.spatial4j.shape.Shape; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -168,12 +169,12 @@ public class GeometryCollectionBuilder extends ShapeBuilder shapes = new ArrayList<>(this.shapes.size()); for (ShapeBuilder shape : this.shapes) { - shapes.add(shape.build()); + shapes.add(shape.buildS4J()); } if (shapes.size() == 1) @@ -183,6 +184,25 @@ public class GeometryCollectionBuilder extends ShapeBuilder shapes = new ArrayList<>(this.shapes.size()); + + for (ShapeBuilder shape : this.shapes) { + Object o = shape.buildLucene(); + if (o.getClass().isArray()) { + shapes.addAll(Arrays.asList((Object[])o)); + } else { + shapes.add(o); + } + } + + if (shapes.size() == 1) { + return shapes.get(0); + } + return shapes.toArray(new Object[shapes.size()]); + } + @Override public int hashCode() { return Objects.hash(shapes); diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/LineStringBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/LineStringBuilder.java index 035c4566a57..07edb241cd7 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/LineStringBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/LineStringBuilder.java @@ -19,6 +19,7 @@ package org.elasticsearch.common.geo.builders; +import org.apache.lucene.geo.Line; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; @@ -35,6 +36,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static org.elasticsearch.common.geo.GeoUtils.normalizeLat; +import static org.elasticsearch.common.geo.GeoUtils.normalizeLon; + public class LineStringBuilder extends ShapeBuilder { public static final GeoShapeType TYPE = GeoShapeType.LINESTRING; @@ -101,11 +105,11 @@ public class LineStringBuilder extends ShapeBuilder strings = decompose(FACTORY, coordinates, new ArrayList()); + ArrayList strings = decomposeS4J(FACTORY, coordinates, new ArrayList()); if(strings.size() == 1) { geometry = strings.get(0); @@ -120,7 +124,23 @@ public class LineStringBuilder extends ShapeBuilder decompose(GeometryFactory factory, Coordinate[] coordinates, ArrayList 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 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 decomposeS4J(GeometryFactory factory, Coordinate[] coordinates, ArrayList strings) { for(Coordinate[] part : decompose(+DATELINE, coordinates)) { for(Coordinate[] line : decompose(-DATELINE, part)) { strings.add(factory.createLineString(line)); @@ -129,6 +149,16 @@ public class LineStringBuilder extends ShapeBuilder decomposeLucene(Coordinate[] coordinates, ArrayList 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. * diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/MultiLineStringBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/MultiLineStringBuilder.java index 68da45bbf0c..9902744fc3b 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/MultiLineStringBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/MultiLineStringBuilder.java @@ -19,6 +19,7 @@ package org.elasticsearch.common.geo.builders; +import org.apache.lucene.geo.Line; import org.elasticsearch.common.geo.GeoShapeType; import org.elasticsearch.common.geo.parsers.GeoWKTParser; import org.elasticsearch.common.geo.parsers.ShapeParser; @@ -124,12 +125,12 @@ public class MultiLineStringBuilder extends ShapeBuilder parts = new ArrayList<>(); for (LineStringBuilder line : lines) { - LineStringBuilder.decompose(FACTORY, line.coordinates(false), parts); + LineStringBuilder.decomposeS4J(FACTORY, line.coordinates(false), parts); } if(parts.size() == 1) { geometry = parts.get(0); @@ -148,6 +149,27 @@ public class MultiLineStringBuilder extends ShapeBuilder parts = new ArrayList<>(); + for (LineStringBuilder line : lines) { + LineStringBuilder.decomposeLucene(line.coordinates(false), parts); + } + if (parts.size() == 1) { + return parts.get(0); + } + return parts.toArray(new Line[parts.size()]); + } + Line[] linestrings = new Line[lines.size()]; + for (int i = 0; i < lines.size(); ++i) { + LineStringBuilder lsb = lines.get(i); + linestrings[i] = new Line(lsb.coordinates.stream().mapToDouble(c->c.y).toArray(), + lsb.coordinates.stream().mapToDouble(c->c.x).toArray()); + } + return linestrings; + } + @Override public int hashCode() { return Objects.hash(lines); diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPointBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPointBuilder.java index c39cc397a34..b559bb58117 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPointBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPointBuilder.java @@ -61,7 +61,7 @@ public class MultiPointBuilder extends ShapeBuilder, Mul } @Override - public XShapeCollection build() { + public XShapeCollection buildS4J() { //Could wrap JtsGeometry but probably slower due to conversions to/from JTS in relate() //MultiPoint geometry = FACTORY.createMultiPoint(points.toArray(new Coordinate[points.size()])); List shapes = new ArrayList<>(coordinates.size()); @@ -73,6 +73,17 @@ public class MultiPointBuilder extends ShapeBuilder, Mul return multiPoints; } + @Override + public double[][] buildLucene() { + double[][] points = new double[coordinates.size()][]; + Coordinate coord; + for (int i = 0; i < coordinates.size(); ++i) { + coord = coordinates.get(i); + points[i] = new double[] {coord.x, coord.y}; + } + return points; + } + @Override public GeoShapeType type() { return TYPE; diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPolygonBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPolygonBuilder.java index bac74c29dd8..95c2bbc6275 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPolygonBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/MultiPolygonBuilder.java @@ -31,6 +31,7 @@ import org.locationtech.spatial4j.shape.Shape; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -162,19 +163,19 @@ public class MultiPolygonBuilder extends ShapeBuilder shapes = new ArrayList<>(this.polygons.size()); if(wrapdateline) { for (PolygonBuilder polygon : this.polygons) { for(Coordinate[][] part : polygon.coordinates()) { - shapes.add(jtsGeometry(PolygonBuilder.polygon(FACTORY, part))); + shapes.add(jtsGeometry(PolygonBuilder.polygonS4J(FACTORY, part))); } } } else { for (PolygonBuilder polygon : this.polygons) { - shapes.add(jtsGeometry(polygon.toPolygon(FACTORY))); + shapes.add(jtsGeometry(polygon.toPolygonS4J(FACTORY))); } } if (shapes.size() == 1) @@ -184,6 +185,33 @@ public class MultiPolygonBuilder extends ShapeBuilder shapes = new ArrayList<>(this.polygons.size()); + Object poly; + if (wrapdateline) { + for (PolygonBuilder polygon : this.polygons) { + poly = polygon.buildLucene(); + if (poly instanceof org.apache.lucene.geo.Polygon[]) { + shapes.addAll(Arrays.asList((org.apache.lucene.geo.Polygon[])poly)); + } else { + shapes.add((org.apache.lucene.geo.Polygon)poly); + } + } + } else { + for (int i = 0; i < this.polygons.size(); ++i) { + PolygonBuilder pb = this.polygons.get(i); + poly = pb.buildLucene(); + if (poly instanceof org.apache.lucene.geo.Polygon[]) { + shapes.addAll(Arrays.asList((org.apache.lucene.geo.Polygon[])poly)); + } else { + shapes.add((org.apache.lucene.geo.Polygon)poly); + } + } + } + return shapes.stream().toArray(org.apache.lucene.geo.Polygon[]::new); + } + @Override public int hashCode() { return Objects.hash(polygons, orientation); diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/PointBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/PointBuilder.java index f8c18f317b6..c13eca936e4 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/PointBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/PointBuilder.java @@ -19,6 +19,7 @@ package org.elasticsearch.common.geo.builders; +import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoShapeType; import org.elasticsearch.common.geo.parsers.ShapeParser; import org.locationtech.spatial4j.shape.Point; @@ -84,10 +85,15 @@ public class PointBuilder extends ShapeBuilder { } @Override - public Point build() { + public Point buildS4J() { return SPATIAL_CONTEXT.makePoint(coordinates.get(0).x, coordinates.get(0).y); } + @Override + public GeoPoint buildLucene() { + return new GeoPoint(coordinates.get(0).y, coordinates.get(0).x); + } + @Override public GeoShapeType type() { return TYPE; diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java index e4d48b8df45..8f7876d2ba9 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java @@ -46,6 +46,8 @@ import java.util.Locale; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import static org.elasticsearch.common.geo.GeoUtils.normalizeLat; +import static org.elasticsearch.common.geo.GeoUtils.normalizeLon; import static org.apache.lucene.geo.GeoUtils.orient; /** @@ -226,8 +228,19 @@ public class PolygonBuilder extends ShapeBuilder { } @Override - public JtsGeometry build() { - return jtsGeometry(buildGeometry(FACTORY, wrapdateline)); + public JtsGeometry buildS4J() { + return jtsGeometry(buildS4JGeometry(FACTORY, wrapdateline)); + } + + @Override + public Object buildLucene() { + if (wrapdateline) { + Coordinate[][][] polygons = coordinates(); + return polygons.length == 1 + ? polygonLucene(polygons[0]) + : multipolygonLucene(polygons); + } + return toPolygonLucene(); } protected XContentBuilder coordinatesArray(XContentBuilder builder, Params params) throws IOException { @@ -250,32 +263,46 @@ public class PolygonBuilder extends ShapeBuilder { return builder; } - public Geometry buildGeometry(GeometryFactory factory, boolean fixDateline) { + public Geometry buildS4JGeometry(GeometryFactory factory, boolean fixDateline) { if(fixDateline) { Coordinate[][][] polygons = coordinates(); return polygons.length == 1 - ? polygon(factory, polygons[0]) - : multipolygon(factory, polygons); + ? polygonS4J(factory, polygons[0]) + : multipolygonS4J(factory, polygons); } else { - return toPolygon(factory); + return toPolygonS4J(factory); } } - public Polygon toPolygon() { - return toPolygon(FACTORY); + public Polygon toPolygonS4J() { + return toPolygonS4J(FACTORY); } - protected Polygon toPolygon(GeometryFactory factory) { - final LinearRing shell = linearRing(factory, this.shell.coordinates); + protected Polygon toPolygonS4J(GeometryFactory factory) { + final LinearRing shell = linearRingS4J(factory, this.shell.coordinates); final LinearRing[] holes = new LinearRing[this.holes.size()]; Iterator iterator = this.holes.iterator(); for (int i = 0; iterator.hasNext(); i++) { - holes[i] = linearRing(factory, iterator.next().coordinates); + holes[i] = linearRingS4J(factory, iterator.next().coordinates); } return factory.createPolygon(shell, holes); } - protected static LinearRing linearRing(GeometryFactory factory, List 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 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 coordinates) { return factory.createLinearRing(coordinates.toArray(new Coordinate[coordinates.size()])); } @@ -293,7 +320,7 @@ public class PolygonBuilder extends ShapeBuilder { return shell.numDimensions(); } - protected static Polygon polygon(GeometryFactory factory, Coordinate[][] polygon) { + protected static Polygon polygonS4J(GeometryFactory factory, Coordinate[][] polygon) { LinearRing shell = factory.createLinearRing(polygon[0]); LinearRing[] holes; @@ -308,6 +335,35 @@ public class PolygonBuilder extends ShapeBuilder { return factory.createPolygon(shell, holes); } + protected static org.apache.lucene.geo.Polygon polygonLucene(Coordinate[][] polygon) { + org.apache.lucene.geo.Polygon[] holes; + Coordinate[] shell = polygon[0]; + if (polygon.length > 1) { + holes = new org.apache.lucene.geo.Polygon[polygon.length - 1]; + for (int i = 0; i < holes.length; ++i) { + Coordinate[] coords = polygon[i+1]; + double[] x = new double[coords.length]; + double[] y = new double[coords.length]; + for (int c = 0; c < coords.length; ++c) { + x[c] = normalizeLon(coords[c].x); + y[c] = normalizeLat(coords[c].y); + } + holes[i] = new org.apache.lucene.geo.Polygon(y, x); + } + } else { + holes = new org.apache.lucene.geo.Polygon[0]; + } + + double[] x = new double[shell.length]; + double[] y = new double[shell.length]; + for (int i = 0; i < shell.length; ++i) { + x[i] = normalizeLon(shell[i].x); + y[i] = normalizeLat(shell[i].y); + } + + return new org.apache.lucene.geo.Polygon(y, x, holes); + } + /** * Create a Multipolygon from a set of coordinates. Each primary array contains a polygon which * in turn contains an array of linestrings. These line Strings are represented as an array of @@ -318,14 +374,22 @@ public class PolygonBuilder extends ShapeBuilder { * @param polygons definition of polygons * @return a new Multipolygon */ - protected static MultiPolygon multipolygon(GeometryFactory factory, Coordinate[][][] polygons) { + protected static MultiPolygon multipolygonS4J(GeometryFactory factory, Coordinate[][][] polygons) { Polygon[] polygonSet = new Polygon[polygons.length]; for (int i = 0; i < polygonSet.length; i++) { - polygonSet[i] = polygon(factory, polygons[i]); + polygonSet[i] = polygonS4J(factory, polygons[i]); } return factory.createMultiPolygon(polygonSet); } + protected static org.apache.lucene.geo.Polygon[] multipolygonLucene(Coordinate[][][] polygons) { + org.apache.lucene.geo.Polygon[] polygonSet = new org.apache.lucene.geo.Polygon[polygons.length]; + for (int i = 0; i < polygonSet.length; ++i) { + polygonSet[i] = polygonLucene(polygons[i]); + } + return polygonSet; + } + /** * This method sets the component id of all edges in a ring to a given id and shifts the * coordinates of this component according to the dateline diff --git a/server/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilder.java b/server/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilder.java index e90d7261c99..365dddb70ea 100644 --- a/server/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilder.java +++ b/server/src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilder.java @@ -211,7 +211,14 @@ public abstract class ShapeBuilder> * the builder looses its validity. So this method should only be called once on a builder * @return new {@link Shape} defined by the builder */ - public abstract T build(); + public abstract T buildS4J(); + + /** + * build lucene geometry. + * + * @return GeoPoint, double[][], Line, Line[], Polygon, Polygon[], Rectangle, Object[] + */ + public abstract Object buildLucene(); protected static Coordinate shift(Coordinate coordinate, double dateline) { if (dateline == 0) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java index d2d081a63d0..7de40fe337d 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/GeoShapeFieldMapper.java @@ -496,7 +496,7 @@ public class GeoShapeFieldMapper extends FieldMapper { if (shapeBuilder == null) { return; } - shape = shapeBuilder.build(); + shape = shapeBuilder.buildS4J(); } if (fieldType().pointsOnly() == true) { // index configured for pointsOnly diff --git a/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java index d26973e390a..537127bc635 100644 --- a/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/GeoBoundingBoxQueryBuilder.java @@ -524,7 +524,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilderi.y).toArray(), + Arrays.stream(coordinates).mapToDouble(i->i.x).toArray()); + assertGeometryEquals(p, polygonGeoJson, false); } public void testInvalidDimensionalPolygon() throws IOException { @@ -464,9 +495,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertPolygon(shape); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false); } // test 2: ccw poly crossing dateline @@ -485,9 +520,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertMultiPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertMultiPolygon(shape); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false); } // test 3: cw poly not crossing dateline @@ -506,9 +545,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertPolygon(shape); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false); } // test 4: cw poly crossing dateline @@ -527,9 +570,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertMultiPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertMultiPolygon(shape); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false); } } @@ -556,9 +603,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertPolygon(shape); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false); } // test 2: ccw poly crossing dateline @@ -583,9 +634,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertMultiPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertMultiPolygon(shape); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false); } // test 3: cw poly not crossing dateline @@ -610,11 +665,14 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); - - ElasticsearchGeoAssertions.assertPolygon(shape); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertPolygon(shape, true); } + try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false); + } // test 4: cw poly crossing dateline polygonGeoJson = Strings.toString(XContentFactory.jsonBuilder().startObject().field("type", "Polygon") @@ -638,9 +696,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertMultiPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertMultiPolygon(shape); + try (XContentParser parser = createParser(JsonXContent.jsonXContent, polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false); } } @@ -785,12 +847,20 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { holeCoordinates.add(new Coordinate(100.2, 0.2)); LinearRing shell = GEOMETRY_FACTORY.createLinearRing( - shellCoordinates.toArray(new Coordinate[shellCoordinates.size()])); + shellCoordinates.toArray(new Coordinate[shellCoordinates.size()])); LinearRing[] holes = new LinearRing[1]; holes[0] = GEOMETRY_FACTORY.createLinearRing( - holeCoordinates.toArray(new Coordinate[holeCoordinates.size()])); + holeCoordinates.toArray(new Coordinate[holeCoordinates.size()])); Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, holes); - assertGeometryEquals(jtsGeom(expected), polygonGeoJson); + assertGeometryEquals(jtsGeom(expected), polygonGeoJson, true); + + org.apache.lucene.geo.Polygon hole = + new org.apache.lucene.geo.Polygon( + new double[] {0.8d, 0.2d, 0.2d, 0.8d, 0.8d}, new double[] {100.8d, 100.8d, 100.2d, 100.2d, 100.8d}); + org.apache.lucene.geo.Polygon p = + new org.apache.lucene.geo.Polygon( + new double[] {0d, 0d, 1d, 1d, 0d}, new double[] {100d, 101d, 101d, 100d, 100d}, hole); + assertGeometryEquals(p, polygonGeoJson, false); } public void testParseSelfCrossingPolygon() throws IOException { @@ -828,9 +898,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { .endObject(); ShapeCollection expected = shapeCollection( - SPATIAL_CONTEXT.makePoint(100, 0), - SPATIAL_CONTEXT.makePoint(101, 1.0)); - assertGeometryEquals(expected, multiPointGeoJson); + SPATIAL_CONTEXT.makePoint(100, 0), + SPATIAL_CONTEXT.makePoint(101, 1.0)); + assertGeometryEquals(expected, multiPointGeoJson, true); + + assertGeometryEquals(new double[][]{ + new double[] {100d, 0d}, + new double[] {101d, 1d}}, multiPointGeoJson, false); } @Override @@ -900,7 +974,19 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { Shape expected = shapeCollection(withoutHoles, withHoles); - assertGeometryEquals(expected, multiPolygonGeoJson); + assertGeometryEquals(expected, multiPolygonGeoJson, true); + + org.apache.lucene.geo.Polygon hole = + new org.apache.lucene.geo.Polygon( + new double[] {0.8d, 0.2d, 0.2d, 0.8d, 0.8d}, new double[] {100.8d, 100.8d, 100.2d, 100.2d, 100.8d}); + + org.apache.lucene.geo.Polygon[] polygons = new org.apache.lucene.geo.Polygon[] { + new org.apache.lucene.geo.Polygon( + new double[] {2d, 3d, 3d, 2d, 2d}, new double[] {103d, 103d, 102d, 102d, 103d}), + new org.apache.lucene.geo.Polygon( + new double[] {0d, 1d, 1d, 0d, 0d}, new double[] {101d, 101d, 100d, 100d, 101d}, hole) + }; + assertGeometryEquals(polygons, multiPolygonGeoJson, false); // test #2: multipolygon; one polygon with one hole // this test converting the multipolygon from a ShapeCollection type @@ -947,7 +1033,17 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { holes[0] = GEOMETRY_FACTORY.createLinearRing(holeCoordinates.toArray(new Coordinate[holeCoordinates.size()])); withHoles = GEOMETRY_FACTORY.createPolygon(shell, holes); - assertGeometryEquals(jtsGeom(withHoles), multiPolygonGeoJson); + assertGeometryEquals(jtsGeom(withHoles), multiPolygonGeoJson, true); + + org.apache.lucene.geo.Polygon luceneHole = + new org.apache.lucene.geo.Polygon( + new double[] {0.8d, 0.2d, 0.2d, 0.8d, 0.8d}, new double[] {100.8d, 100.8d, 100.2d, 100.2d, 100.8d}); + + org.apache.lucene.geo.Polygon[] lucenePolygons = new org.apache.lucene.geo.Polygon[] { + new org.apache.lucene.geo.Polygon( + new double[] {0d, 0d, 1d, 1d, 0d}, new double[] {100d, 101d, 101d, 100d, 100d}, luceneHole) + }; + assertGeometryEquals(lucenePolygons, multiPolygonGeoJson, false); } @Override @@ -967,20 +1063,74 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { .field("type", "Point") .startArray("coordinates").value(102.0).value(2.0).endArray() .endObject() + .startObject() + .field("type", "Polygon") + .startArray("coordinates") + .startArray() + .startArray().value(-177.0).value(10.0).endArray() + .startArray().value(176.0).value(15.0).endArray() + .startArray().value(172.0).value(0.0).endArray() + .startArray().value(176.0).value(-15.0).endArray() + .startArray().value(-177.0).value(-10.0).endArray() + .startArray().value(-177.0).value(10.0).endArray() + .endArray() + .endArray() + .endObject() .endArray() .endObject(); - Shape[] expected = new Shape[2]; + ArrayList 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 shellCoordinates2 = new ArrayList<>(); + shellCoordinates2.add(new Coordinate(-180.0, 12.142857142857142)); + shellCoordinates2.add(new Coordinate(-180.0, -12.142857142857142)); + shellCoordinates2.add(new Coordinate(-177.0, -10.0)); + shellCoordinates2.add(new Coordinate(-177.0, 10.0)); + shellCoordinates2.add(new Coordinate(-180.0, 12.142857142857142)); + + Shape[] expected = new Shape[3]; LineString expectedLineString = GEOMETRY_FACTORY.createLineString(new Coordinate[]{ - new Coordinate(100, 0), - new Coordinate(101, 1), + new Coordinate(100, 0), + new Coordinate(101, 1), }); expected[0] = jtsGeom(expectedLineString); Point expectedPoint = GEOMETRY_FACTORY.createPoint(new Coordinate(102.0, 2.0)); expected[1] = new JtsPoint(expectedPoint, SPATIAL_CONTEXT); + LinearRing shell1 = GEOMETRY_FACTORY.createLinearRing( + shellCoordinates1.toArray(new Coordinate[shellCoordinates1.size()])); + LinearRing shell2 = GEOMETRY_FACTORY.createLinearRing( + shellCoordinates2.toArray(new Coordinate[shellCoordinates2.size()])); + MultiPolygon expectedMultiPoly = GEOMETRY_FACTORY.createMultiPolygon( + new Polygon[] { + GEOMETRY_FACTORY.createPolygon(shell1), + GEOMETRY_FACTORY.createPolygon(shell2) + } + ); + expected[2] = jtsGeom(expectedMultiPoly); + //equals returns true only if geometries are in the same order - assertGeometryEquals(shapeCollection(expected), geometryCollectionGeoJson); + assertGeometryEquals(shapeCollection(expected), geometryCollectionGeoJson, true); + + Object[] luceneExpected = new Object[] { + new Line(new double[] {0d, 1d}, new double[] {100d, 101d}), + new GeoPoint(2d, 102d), + new org.apache.lucene.geo.Polygon( + new double[] {-12.142857142857142d, 12.142857142857142d, 15d, 0d, -15d, -12.142857142857142d}, + new double[] {180d, 180d, 176d, 172d, 176d, 180d} + ), + new org.apache.lucene.geo.Polygon( + new double[] {12.142857142857142d, -12.142857142857142d, -10d, 10d, 12.142857142857142d}, + new double[] {180d, 180d, -177d, -177d, 180d} + ) + }; + assertGeometryEquals(luceneExpected, geometryCollectionGeoJson, false); } public void testThatParserExtractsCorrectTypeAndCoordinatesFromArbitraryJson() throws IOException { @@ -1000,8 +1150,11 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { .startObject("lala").field("type", "NotAPoint").endObject() .endObject(); - Point expected = GEOMETRY_FACTORY.createPoint(new Coordinate(100.0, 0.0)); - assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson); + Point expected = GEOMETRY_FACTORY.createPoint(new Coordinate(100.0, 0.0)); + assertGeometryEquals(new JtsPoint(expected, SPATIAL_CONTEXT), pointGeoJson, true); + + GeoPoint expectedPt = new GeoPoint(0, 100); + assertGeometryEquals(expectedPt, pointGeoJson, false); } public void testParseOrientationOption() throws IOException { @@ -1030,9 +1183,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertPolygon(shape); + try (XContentParser parser = createParser(polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false); } // test 2: valid ccw (right handed system) poly not crossing dateline (with 'ccw' field) @@ -1060,9 +1217,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertPolygon(shape); + try (XContentParser parser = createParser(polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false); } // test 3: valid ccw (right handed system) poly not crossing dateline (with 'counterclockwise' field) @@ -1090,9 +1251,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertPolygon(shape); + try (XContentParser parser = createParser(polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertPolygon(ShapeParser.parse(parser).buildLucene(), false); } // test 4: valid cw (left handed system) poly crossing dateline (with 'left' field) @@ -1120,9 +1285,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertMultiPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertMultiPolygon(shape); + try (XContentParser parser = createParser(polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false); } // test 5: valid cw multipoly (left handed system) poly crossing dateline (with 'cw' field) @@ -1150,9 +1319,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertMultiPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertMultiPolygon(shape); + try (XContentParser parser = createParser(polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false); } // test 6: valid cw multipoly (left handed system) poly crossing dateline (with 'clockwise' field) @@ -1180,9 +1353,13 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase { try (XContentParser parser = createParser(polygonGeoJson)) { parser.nextToken(); - Shape shape = ShapeParser.parse(parser).build(); + Shape shape = ShapeParser.parse(parser).buildS4J(); + ElasticsearchGeoAssertions.assertMultiPolygon(shape, true); + } - ElasticsearchGeoAssertions.assertMultiPolygon(shape); + try (XContentParser parser = createParser(polygonGeoJson)) { + parser.nextToken(); + ElasticsearchGeoAssertions.assertMultiPolygon(ShapeParser.parse(parser).buildLucene(), false); } } diff --git a/server/src/test/java/org/elasticsearch/common/geo/GeoWKTShapeParserTests.java b/server/src/test/java/org/elasticsearch/common/geo/GeoWKTShapeParserTests.java index 965ca234ddd..1b4c0b9dce0 100644 --- a/server/src/test/java/org/elasticsearch/common/geo/GeoWKTShapeParserTests.java +++ b/server/src/test/java/org/elasticsearch/common/geo/GeoWKTShapeParserTests.java @@ -19,6 +19,7 @@ package org.elasticsearch.common.geo; import org.apache.lucene.geo.GeoTestUtil; +import org.apache.lucene.geo.Line; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchParseException; import org.elasticsearch.Version; @@ -58,6 +59,7 @@ import org.locationtech.spatial4j.shape.jts.JtsPoint; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static org.elasticsearch.common.geo.builders.ShapeBuilder.SPATIAL_CONTEXT; @@ -84,9 +86,9 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase { return XContentFactory.jsonBuilder().value(wkt); } - private void assertExpected(Shape expected, ShapeBuilder builder) throws IOException { + private void assertExpected(Object expected, ShapeBuilder builder, boolean useJTS) throws IOException { XContentBuilder xContentBuilder = toWKTContent(builder, false); - assertGeometryEquals(expected, xContentBuilder); + assertGeometryEquals(expected, xContentBuilder, useJTS); } private void assertMalformed(ShapeBuilder builder) throws IOException { @@ -99,7 +101,8 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase { GeoPoint p = RandomShapeGenerator.randomPoint(random()); Coordinate c = new Coordinate(p.lon(), p.lat()); Point expected = GEOMETRY_FACTORY.createPoint(c); - assertExpected(new JtsPoint(expected, SPATIAL_CONTEXT), new PointBuilder().coordinate(c)); + assertExpected(new JtsPoint(expected, SPATIAL_CONTEXT), new PointBuilder().coordinate(c), true); + assertExpected(new GeoPoint(p.lat(), p.lon()), new PointBuilder().coordinate(c), false); assertMalformed(new PointBuilder().coordinate(c)); } @@ -107,15 +110,25 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase { public void testParseMultiPoint() throws IOException { int numPoints = randomIntBetween(2, 100); List coordinates = new ArrayList<>(numPoints); - Shape[] shapes = new Shape[numPoints]; - GeoPoint p = new GeoPoint(); for (int i = 0; i < numPoints; ++i) { - p.reset(GeoTestUtil.nextLatitude(), GeoTestUtil.nextLongitude()); - coordinates.add(new Coordinate(p.lon(), p.lat())); - shapes[i] = SPATIAL_CONTEXT.makePoint(p.lon(), p.lat()); + coordinates.add(new Coordinate(GeoTestUtil.nextLongitude(), GeoTestUtil.nextLatitude())); + } + + Shape[] shapes = new Shape[numPoints]; + for (int i = 0; i < numPoints; ++i) { + Coordinate c = coordinates.get(i); + shapes[i] = SPATIAL_CONTEXT.makePoint(c.x, c.y); } ShapeCollection expected = shapeCollection(shapes); - assertExpected(expected, new MultiPointBuilder(coordinates)); + assertExpected(expected, new MultiPointBuilder(coordinates), true); + + double[][] luceneShapes = new double[numPoints][2]; + for (int i = 0; i < numPoints; ++i) { + Coordinate c = coordinates.get(i); + luceneShapes[i][0] = c.x; + luceneShapes[i][1] = c.y; + } + assertExpected(luceneShapes, new MultiPointBuilder(coordinates), false); assertMalformed(new MultiPointBuilder(coordinates)); } @@ -133,8 +146,17 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase { @Override public void testParseLineString() throws IOException { List coordinates = randomLineStringCoords(); + LineString expected = GEOMETRY_FACTORY.createLineString(coordinates.toArray(new Coordinate[coordinates.size()])); - assertExpected(jtsGeom(expected), new LineStringBuilder(coordinates)); + assertExpected(jtsGeom(expected), new LineStringBuilder(coordinates), true); + + double[] lats = new double[coordinates.size()]; + double[] lons = new double[lats.length]; + for (int i = 0; i < lats.length; ++i) { + lats[i] = coordinates.get(i).y; + lons[i] = coordinates.get(i).x; + } + assertExpected(new Line(lats, lons), new LineStringBuilder(coordinates), false); } @Override @@ -148,9 +170,18 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase { lineStrings.add(GEOMETRY_FACTORY.createLineString(coords)); builder.linestring(new LineStringBuilder(lsc)); } + MultiLineString expected = GEOMETRY_FACTORY.createMultiLineString( lineStrings.toArray(new LineString[lineStrings.size()])); - assertExpected(jtsGeom(expected), builder); + assertExpected(jtsGeom(expected), builder, true); + + Line[] lines = new Line[lineStrings.size()]; + for (int j = 0; j < lineStrings.size(); ++j) { + Coordinate[] c = lineStrings.get(j).getCoordinates(); + lines[j] = new Line(Arrays.stream(c).mapToDouble(i->i.y).toArray(), + Arrays.stream(c).mapToDouble(i->i.x).toArray()); + } + assertExpected(lines, builder, false); assertMalformed(builder); } @@ -159,9 +190,10 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase { PolygonBuilder builder = PolygonBuilder.class.cast( RandomShapeGenerator.createShape(random(), RandomShapeGenerator.ShapeType.POLYGON)); Coordinate[] coords = builder.coordinates()[0][0]; + LinearRing shell = GEOMETRY_FACTORY.createLinearRing(coords); Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, null); - assertExpected(jtsGeom(expected), builder); + assertExpected(jtsGeom(expected), builder, true); assertMalformed(builder); } @@ -180,8 +212,9 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase { shell = GEOMETRY_FACTORY.createLinearRing(coordinates); shapes[i] = GEOMETRY_FACTORY.createPolygon(shell, null); } + Shape expected = shapeCollection(shapes); - assertExpected(expected, builder); + assertExpected(expected, builder, true); assertMalformed(builder); } @@ -210,8 +243,15 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase { holes[0] = GEOMETRY_FACTORY.createLinearRing( holeCoordinates.toArray(new Coordinate[holeCoordinates.size()])); Polygon expected = GEOMETRY_FACTORY.createPolygon(shell, holes); + assertExpected(jtsGeom(expected), polygonWithHole, true); - assertExpected(jtsGeom(expected), polygonWithHole); + org.apache.lucene.geo.Polygon hole = + new org.apache.lucene.geo.Polygon( + new double[] {0.8d, 0.8d, 0.2d, 0.2d, 0.8d}, new double[] {100.2d, 100.8d, 100.8d, 100.2d, 100.2d}); + org.apache.lucene.geo.Polygon p = + new org.apache.lucene.geo.Polygon( + new double[] {0d, 1d, 1d, 0d, 0d}, new double[] {101d, 101d, 100d, 100d, 101d}, hole); + assertExpected(p, polygonWithHole, false); assertMalformed(polygonWithHole); } @@ -370,8 +410,10 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase { public void testParseEnvelope() throws IOException { org.apache.lucene.geo.Rectangle r = GeoTestUtil.nextBox(); EnvelopeBuilder builder = new EnvelopeBuilder(new Coordinate(r.minLon, r.maxLat), new Coordinate(r.maxLon, r.minLat)); + Rectangle expected = SPATIAL_CONTEXT.makeRectangle(r.minLon, r.maxLon, r.minLat, r.maxLat); - assertExpected(expected, builder); + assertExpected(expected, builder, true); + assertExpected(r, builder, false); assertMalformed(builder); } @@ -386,10 +428,15 @@ public class GeoWKTShapeParserTests extends BaseGeoParsingTestCase { // assert empty shape collection GeometryCollectionBuilder builder = new GeometryCollectionBuilder(); Shape[] expected = new Shape[0]; - assertEquals(shapeCollection(expected).isEmpty(), builder.build().isEmpty()); + if (randomBoolean()) { + assertEquals(shapeCollection(expected).isEmpty(), builder.buildS4J().isEmpty()); + } else { + assertEquals(shapeCollection(expected).isEmpty(), ((Object[])builder.buildLucene()).length == 0); + } } else { GeometryCollectionBuilder gcb = RandomShapeGenerator.createGeometryCollection(random()); - assertExpected(gcb.build(), gcb); + assertExpected(gcb.buildS4J(), gcb, true); + assertExpected(gcb.buildLucene(), gcb, false); } } diff --git a/server/src/test/java/org/elasticsearch/common/geo/ShapeBuilderTests.java b/server/src/test/java/org/elasticsearch/common/geo/ShapeBuilderTests.java index 5dcc811b0c6..8b8bd2285ca 100644 --- a/server/src/test/java/org/elasticsearch/common/geo/ShapeBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/common/geo/ShapeBuilderTests.java @@ -36,7 +36,6 @@ import org.locationtech.spatial4j.exception.InvalidShapeException; import org.locationtech.spatial4j.shape.Circle; import org.locationtech.spatial4j.shape.Point; import org.locationtech.spatial4j.shape.Rectangle; -import org.locationtech.spatial4j.shape.Shape; import org.locationtech.spatial4j.shape.impl.PointImpl; import static org.elasticsearch.test.hamcrest.ElasticsearchGeoAssertions.assertMultiLineString; @@ -49,65 +48,109 @@ import static org.hamcrest.Matchers.containsString; public class ShapeBuilderTests extends ESTestCase { public void testNewPoint() { - Point point = new PointBuilder().coordinate(-100, 45).build(); + PointBuilder pb = new PointBuilder().coordinate(-100, 45); + Point point = pb.buildS4J(); assertEquals(-100D, point.getX(), 0.0d); assertEquals(45D, point.getY(), 0.0d); + GeoPoint geoPoint = pb.buildLucene(); + assertEquals(-100D, geoPoint.getLon(), 0.0d); + assertEquals(45D, geoPoint.getLat(), 0.0d); } public void testNewRectangle() { - Rectangle rectangle = new EnvelopeBuilder(new Coordinate(-45, 30), new Coordinate(45, -30)).build(); + EnvelopeBuilder eb = new EnvelopeBuilder(new Coordinate(-45, 30), new Coordinate(45, -30)); + Rectangle rectangle = eb.buildS4J(); assertEquals(-45D, rectangle.getMinX(), 0.0d); assertEquals(-30D, rectangle.getMinY(), 0.0d); assertEquals(45D, rectangle.getMaxX(), 0.0d); assertEquals(30D, rectangle.getMaxY(), 0.0d); + + org.apache.lucene.geo.Rectangle luceneRectangle = eb.buildLucene(); + assertEquals(-45D, luceneRectangle.minLon, 0.0d); + assertEquals(-30D, luceneRectangle.minLat, 0.0d); + assertEquals(45D, luceneRectangle.maxLon, 0.0d); + assertEquals(30D, luceneRectangle.maxLat, 0.0d); } public void testNewPolygon() { - Polygon polygon = new PolygonBuilder(new CoordinatesBuilder() + PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder() .coordinate(-45, 30) .coordinate(45, 30) .coordinate(45, -30) .coordinate(-45, -30) - .coordinate(-45, 30)).toPolygon(); + .coordinate(-45, 30)); - LineString exterior = polygon.getExteriorRing(); + Polygon poly = pb.toPolygonS4J(); + LineString exterior = poly.getExteriorRing(); assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30)); assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30)); assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30)); assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30)); + + org.apache.lucene.geo.Polygon lucenePoly = (org.apache.lucene.geo.Polygon)(pb.toPolygonLucene()); + assertEquals(lucenePoly.getPolyLat(0), 30, 0d); + assertEquals(lucenePoly.getPolyLon(0), -45, 0d); + assertEquals(lucenePoly.getPolyLat(1), 30, 0d); + assertEquals(lucenePoly.getPolyLon(1), 45, 0d); + assertEquals(lucenePoly.getPolyLat(2), -30, 0d); + assertEquals(lucenePoly.getPolyLon(2), 45, 0d); + assertEquals(lucenePoly.getPolyLat(3), -30, 0d); + assertEquals(lucenePoly.getPolyLon(3), -45, 0d); } public void testNewPolygon_coordinate() { - Polygon polygon = new PolygonBuilder(new CoordinatesBuilder() + PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder() .coordinate(new Coordinate(-45, 30)) .coordinate(new Coordinate(45, 30)) .coordinate(new Coordinate(45, -30)) .coordinate(new Coordinate(-45, -30)) - .coordinate(new Coordinate(-45, 30))).toPolygon(); + .coordinate(new Coordinate(-45, 30))); - LineString exterior = polygon.getExteriorRing(); + Polygon poly = pb.toPolygonS4J(); + LineString exterior = poly.getExteriorRing(); assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30)); assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30)); assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30)); assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30)); + + org.apache.lucene.geo.Polygon lucenePoly = (org.apache.lucene.geo.Polygon)(pb.toPolygonLucene()); + assertEquals(lucenePoly.getPolyLat(0), 30, 0d); + assertEquals(lucenePoly.getPolyLon(0), -45, 0d); + assertEquals(lucenePoly.getPolyLat(1), 30, 0d); + assertEquals(lucenePoly.getPolyLon(1), 45, 0d); + assertEquals(lucenePoly.getPolyLat(2), -30, 0d); + assertEquals(lucenePoly.getPolyLon(2), 45, 0d); + assertEquals(lucenePoly.getPolyLat(3), -30, 0d); + assertEquals(lucenePoly.getPolyLon(3), -45, 0d); } public void testNewPolygon_coordinates() { - Polygon polygon = new PolygonBuilder(new CoordinatesBuilder() + PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder() .coordinates(new Coordinate(-45, 30), new Coordinate(45, 30), new Coordinate(45, -30), new Coordinate(-45, -30), new Coordinate(-45, 30)) - ).toPolygon(); + ); - LineString exterior = polygon.getExteriorRing(); + Polygon poly = pb.toPolygonS4J(); + LineString exterior = poly.getExteriorRing(); assertEquals(exterior.getCoordinateN(0), new Coordinate(-45, 30)); assertEquals(exterior.getCoordinateN(1), new Coordinate(45, 30)); assertEquals(exterior.getCoordinateN(2), new Coordinate(45, -30)); assertEquals(exterior.getCoordinateN(3), new Coordinate(-45, -30)); + + org.apache.lucene.geo.Polygon lucenePoly = (org.apache.lucene.geo.Polygon)(pb.toPolygonLucene()); + assertEquals(lucenePoly.getPolyLat(0), 30, 0d); + assertEquals(lucenePoly.getPolyLon(0), -45, 0d); + assertEquals(lucenePoly.getPolyLat(1), 30, 0d); + assertEquals(lucenePoly.getPolyLon(1), 45, 0d); + assertEquals(lucenePoly.getPolyLat(2), -30, 0d); + assertEquals(lucenePoly.getPolyLon(2), 45, 0d); + assertEquals(lucenePoly.getPolyLat(3), -30, 0d); + assertEquals(lucenePoly.getPolyLon(3), -45, 0d); } public void testLineStringBuilder() { // Building a simple LineString - new LineStringBuilder(new CoordinatesBuilder() + LineStringBuilder lsb = new LineStringBuilder(new CoordinatesBuilder() .coordinate(-130.0, 55.0) .coordinate(-130.0, -40.0) .coordinate(-15.0, -40.0) @@ -115,10 +158,13 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(-45.0, 50.0) .coordinate(-45.0, -15.0) .coordinate(-110.0, -15.0) - .coordinate(-110.0, 55.0)).build(); + .coordinate(-110.0, 55.0)); + + lsb.buildS4J(); + lsb.buildLucene(); // Building a linestring that needs to be wrapped - new LineStringBuilder(new CoordinatesBuilder() + lsb = new LineStringBuilder(new CoordinatesBuilder() .coordinate(100.0, 50.0) .coordinate(110.0, -40.0) .coordinate(240.0, -40.0) @@ -126,31 +172,34 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(200.0, 60.0) .coordinate(200.0, -30.0) .coordinate(130.0, -30.0) - .coordinate(130.0, 60.0) - ) - .build(); + .coordinate(130.0, 60.0)); + + lsb.buildS4J(); + lsb.buildLucene(); // Building a lineString on the dateline - new LineStringBuilder(new CoordinatesBuilder() + lsb = new LineStringBuilder(new CoordinatesBuilder() .coordinate(-180.0, 80.0) .coordinate(-180.0, 40.0) .coordinate(-180.0, -40.0) - .coordinate(-180.0, -80.0) - ) - .build(); + .coordinate(-180.0, -80.0)); + + lsb.buildS4J(); + lsb.buildLucene(); // Building a lineString on the dateline - new LineStringBuilder(new CoordinatesBuilder() + lsb = new LineStringBuilder(new CoordinatesBuilder() .coordinate(180.0, 80.0) .coordinate(180.0, 40.0) .coordinate(180.0, -40.0) - .coordinate(180.0, -80.0) - ) - .build(); + .coordinate(180.0, -80.0)); + + lsb.buildS4J(); + lsb.buildLucene(); } public void testMultiLineString() { - new MultiLineStringBuilder() + MultiLineStringBuilder mlsb = new MultiLineStringBuilder() .linestring(new LineStringBuilder(new CoordinatesBuilder() .coordinate(-100.0, 50.0) .coordinate(50.0, 50.0) @@ -164,8 +213,9 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(50.0, 0.0) .coordinate(-100.0, 0.0) ) - ) - .build(); + ); + mlsb.buildS4J(); + mlsb.buildLucene(); // LineString that needs to be wrapped new MultiLineStringBuilder() @@ -182,8 +232,10 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(200.0, 0.0) .coordinate(150.0, 0.0) ) - ) - .build(); + ); + + mlsb.buildS4J(); + mlsb.buildLucene(); } public void testPolygonSelfIntersection() { @@ -192,58 +244,58 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(40.0, 50.0) .coordinate(-40.0, -50.0) .coordinate(40.0, -50.0).close()); - Exception e = expectThrows(InvalidShapeException.class, () -> newPolygon.build()); + Exception e = expectThrows(InvalidShapeException.class, () -> newPolygon.buildS4J()); assertThat(e.getMessage(), containsString("Self-intersection at or near point (0.0")); } + /** note: only supported by S4J at the moment */ public void testGeoCircle() { double earthCircumference = 40075016.69; - Circle circle = new CircleBuilder().center(0, 0).radius("100m").build(); + Circle circle = new CircleBuilder().center(0, 0).radius("100m").buildS4J(); assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001); assertEquals(new PointImpl(0, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); - circle = new CircleBuilder().center(+180, 0).radius("100m").build(); + circle = new CircleBuilder().center(+180, 0).radius("100m").buildS4J(); assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001); assertEquals(new PointImpl(180, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); - circle = new CircleBuilder().center(-180, 0).radius("100m").build(); + circle = new CircleBuilder().center(-180, 0).radius("100m").buildS4J(); assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001); assertEquals(new PointImpl(-180, 0, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); - circle = new CircleBuilder().center(0, 90).radius("100m").build(); + circle = new CircleBuilder().center(0, 90).radius("100m").buildS4J(); assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001); assertEquals(new PointImpl(0, 90, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); - circle = new CircleBuilder().center(0, -90).radius("100m").build(); + circle = new CircleBuilder().center(0, -90).radius("100m").buildS4J(); assertEquals((360 * 100) / earthCircumference, circle.getRadius(), 0.00000001); assertEquals(new PointImpl(0, -90, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); double randomLat = (randomDouble() * 180) - 90; double randomLon = (randomDouble() * 360) - 180; double randomRadius = randomIntBetween(1, (int) earthCircumference / 4); - circle = new CircleBuilder().center(randomLon, randomLat).radius(randomRadius + "m").build(); + circle = new CircleBuilder().center(randomLon, randomLat).radius(randomRadius + "m").buildS4J(); assertEquals((360 * randomRadius) / earthCircumference, circle.getRadius(), 0.00000001); assertEquals(new PointImpl(randomLon, randomLat, ShapeBuilder.SPATIAL_CONTEXT), circle.getCenter()); } public void testPolygonWrapping() { - Shape shape = new PolygonBuilder(new CoordinatesBuilder() + PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder() .coordinate(-150.0, 65.0) .coordinate(-250.0, 65.0) .coordinate(-250.0, -65.0) .coordinate(-150.0, -65.0) - .close() - ) - .build(); + .close()); - assertMultiPolygon(shape); + assertMultiPolygon(pb.buildS4J(), true); + assertMultiPolygon(pb.buildLucene(), false); } public void testLineStringWrapping() { - Shape shape = new LineStringBuilder(new CoordinatesBuilder() + LineStringBuilder lsb = new LineStringBuilder(new CoordinatesBuilder() .coordinate(-150.0, 65.0) .coordinate(-250.0, 65.0) .coordinate(-250.0, -65.0) .coordinate(-150.0, -65.0) - .close() - ) - .build(); - assertMultiLineString(shape); + .close()); + + assertMultiLineString(lsb.buildS4J(), true); + assertMultiLineString(lsb.buildLucene(), false); } public void testDatelineOGC() { @@ -286,8 +338,8 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(-179,1) )); - Shape shape = builder.close().build(); - assertMultiPolygon(shape); + assertMultiPolygon(builder.close().buildS4J(), true); + assertMultiPolygon(builder.close().buildLucene(), false); } public void testDateline() { @@ -330,8 +382,8 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(-179,1) )); - Shape shape = builder.close().build(); - assertMultiPolygon(shape); + assertMultiPolygon(builder.close().buildS4J(), true); + assertMultiPolygon(builder.close().buildLucene(), false); } public void testComplexShapeWithHole() { @@ -405,9 +457,8 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(-85.0000002,37.1317672) ) ); - - Shape shape = builder.close().build(); - assertPolygon(shape); + assertPolygon(builder.close().buildS4J(), true); + assertPolygon(builder.close().buildLucene(), false); } public void testShapeWithHoleAtEdgeEndPoints() { @@ -428,9 +479,8 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(-4, 1) .coordinate(4, 1) )); - - Shape shape = builder.close().build(); - assertPolygon(shape); + assertPolygon(builder.close().buildS4J(), true); + assertPolygon(builder.close().buildLucene(), false); } public void testShapeWithPointOnDateline() { @@ -440,9 +490,8 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(176, -4) .coordinate(180, 0) ); - - Shape shape = builder.close().build(); - assertPolygon(shape); + assertPolygon(builder.close().buildS4J(), true); + assertPolygon(builder.close().buildLucene(), false); } public void testShapeWithEdgeAlongDateline() { @@ -454,8 +503,8 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(180, 0) ); - Shape shape = builder.close().build(); - assertPolygon(shape); + assertPolygon(builder.close().buildS4J(), true); + assertPolygon(builder.close().buildLucene(), false); // test case 2: test the negative side of the dateline builder = new PolygonBuilder(new CoordinatesBuilder() @@ -465,8 +514,8 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(-176, 4) ); - shape = builder.close().build(); - assertPolygon(shape); + assertPolygon(builder.close().buildS4J(), true); + assertPolygon(builder.close().buildLucene(), false); } public void testShapeWithBoundaryHoles() { @@ -486,8 +535,9 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(176, -10) .coordinate(176, 10) )); - Shape shape = builder.close().build(); - assertMultiPolygon(shape); + + assertMultiPolygon(builder.close().buildS4J(), true); + assertMultiPolygon(builder.close().buildLucene(), false); // test case 2: test the negative side of the dateline builder = new PolygonBuilder( @@ -508,8 +558,9 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(-176, 10) .close() )); - shape = builder.close().build(); - assertMultiPolygon(shape); + + assertMultiPolygon(builder.close().buildS4J(), true); + assertMultiPolygon(builder.close().buildLucene(), false); } public void testShapeWithTangentialHole() { @@ -529,8 +580,9 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(-180, 5) .coordinate(-177, 10) )); - Shape shape = builder.close().build(); - assertMultiPolygon(shape); + + assertMultiPolygon(builder.close().buildS4J(), true); + assertMultiPolygon(builder.close().buildLucene(), false); } public void testShapeWithInvalidTangentialHole() { @@ -550,7 +602,11 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(179, -10) .coordinate(164, 0) )); - Exception e = expectThrows(InvalidShapeException.class, () -> builder.close().build()); + Exception e; + + e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J()); + assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior")); + e = expectThrows(InvalidShapeException.class, () -> builder.close().buildLucene()); assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior")); } @@ -577,8 +633,8 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(176, -5) .coordinate(172, 0) )); - Shape shape = builder.close().build(); - assertMultiPolygon(shape); + assertMultiPolygon(builder.close().buildS4J(), true); + assertMultiPolygon(builder.close().buildLucene(), false); } public void testBoundaryShapeWithInvalidTangentialHole() { @@ -598,7 +654,10 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(176, -10) .coordinate(-177, 10) )); - Exception e = expectThrows(InvalidShapeException.class, () -> builder.close().build()); + Exception e; + e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J()); + assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior")); + e = expectThrows(InvalidShapeException.class, () -> builder.close().buildLucene()); assertThat(e.getMessage(), containsString("interior cannot share more than one point with the exterior")); } @@ -613,9 +672,8 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(-180, 90) ); - Shape shape = builder.close().build(); - - assertPolygon(shape); + assertPolygon(builder.close().buildS4J(), true); + assertPolygon(builder.close().buildLucene(), false); } public void testShapeWithAlternateOrientation() { @@ -627,8 +685,8 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(180, 0) ); - Shape shape = builder.close().build(); - assertPolygon(shape); + assertPolygon(builder.close().buildS4J(), true); + assertPolygon(builder.close().buildLucene(), false); // cw: geo core will convert to ccw across the dateline builder = new PolygonBuilder(new CoordinatesBuilder() @@ -638,9 +696,8 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(180, 0) ); - shape = builder.close().build(); - - assertMultiPolygon(shape); + assertMultiPolygon(builder.close().buildS4J(), true); + assertMultiPolygon(builder.close().buildLucene(), false); } public void testInvalidShapeWithConsecutiveDuplicatePoints() { @@ -651,7 +708,10 @@ public class ShapeBuilderTests extends ESTestCase { .coordinate(-176, 4) .coordinate(180, 0) ); - Exception e = expectThrows(InvalidShapeException.class, () -> builder.close().build()); + + Exception e = expectThrows(InvalidShapeException.class, () -> builder.close().buildS4J()); + assertThat(e.getMessage(), containsString("duplicate consecutive coordinates at: (")); + e = expectThrows(InvalidShapeException.class, () -> builder.close().buildLucene()); assertThat(e.getMessage(), containsString("duplicate consecutive coordinates at: (")); } diff --git a/server/src/test/java/org/elasticsearch/common/geo/builders/PolygonBuilderTests.java b/server/src/test/java/org/elasticsearch/common/geo/builders/PolygonBuilderTests.java index 83bf2bae6a6..0d4f1427854 100644 --- a/server/src/test/java/org/elasticsearch/common/geo/builders/PolygonBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/common/geo/builders/PolygonBuilderTests.java @@ -128,7 +128,7 @@ public class PolygonBuilderTests extends AbstractShapeBuilderTestCase { PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder().coordinate(4, 3).coordinate(3, 2).coordinate(3, 3).close()); pb.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(4, 2).coordinate(3, 1).coordinate(4, 1).close())); - pb.build(); + pb.buildS4J(); }); assertEquals("Hole lies outside shell at or near point (4.0, 1.0, NaN)", e.getMessage()); @@ -138,7 +138,7 @@ public class PolygonBuilderTests extends AbstractShapeBuilderTestCase { PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder().coordinate(3, 2).coordinate(4, 1).coordinate(3, 1).close()); pb.hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(3, 3).coordinate(4, 2).coordinate(4, 3).close())); - pb.build(); + pb.buildS4J(); }); assertEquals("Hole lies outside shell at or near point (4.0, 3.0, NaN)", e.getMessage()); @@ -152,13 +152,13 @@ public class PolygonBuilderTests extends AbstractShapeBuilderTestCase polygonPoints = new ArrayList<>(); for (Coordinate coord : coordinates) { diff --git a/server/src/test/java/org/elasticsearch/search/geo/GeoFilterIT.java b/server/src/test/java/org/elasticsearch/search/geo/GeoFilterIT.java index ce6c00f359f..2f26e448273 100644 --- a/server/src/test/java/org/elasticsearch/search/geo/GeoFilterIT.java +++ b/server/src/test/java/org/elasticsearch/search/geo/GeoFilterIT.java @@ -123,7 +123,7 @@ public class GeoFilterIT extends ESIntegTestCase { .coordinate(-10, 10) .coordinate(10, -10) .close()) - .build(); + .buildS4J(); fail("Self intersection not detected"); } catch (InvalidShapeException e) { } @@ -132,14 +132,14 @@ public class GeoFilterIT extends ESIntegTestCase { new PolygonBuilder(new CoordinatesBuilder() .coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close()) .hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(-5, -5).coordinate(-5, 5).coordinate(5, 5).coordinate(5, -5).close())) - .build(); + .buildS4J(); try { // polygon with overlapping hole new PolygonBuilder(new CoordinatesBuilder() .coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close()) .hole(new LineStringBuilder(new CoordinatesBuilder() .coordinate(-5, -5).coordinate(-5, 11).coordinate(5, 11).coordinate(5, -5).close())) - .build(); + .buildS4J(); fail("Self intersection not detected"); } catch (InvalidShapeException e) { @@ -151,7 +151,7 @@ public class GeoFilterIT extends ESIntegTestCase { .coordinate(-10, -10).coordinate(-10, 10).coordinate(10, 10).coordinate(10, -10).close()) .hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(-5, -5).coordinate(-5, 5).coordinate(5, 5).coordinate(5, -5).close())) .hole(new LineStringBuilder(new CoordinatesBuilder().coordinate(-5, -6).coordinate(5, -6).coordinate(5, -4).coordinate(-5, -4).close())) - .build(); + .buildS4J(); fail("Intersection of holes not detected"); } catch (InvalidShapeException e) { } @@ -167,7 +167,7 @@ public class GeoFilterIT extends ESIntegTestCase { .coordinate(10, 20) .coordinate(10, -10) .close()) - .build(); + .buildS4J(); fail("Self intersection not detected"); } catch (InvalidShapeException e) { } @@ -190,7 +190,7 @@ public class GeoFilterIT extends ESIntegTestCase { .coordinate(-4, 4) .coordinate(4, 4) .coordinate(4, -4).close())) - .build(); + .buildS4J(); } public void testShapeRelations() throws Exception { diff --git a/server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java b/server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java index d0d05f6a369..9e0f51b9985 100644 --- a/server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java +++ b/server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java @@ -240,7 +240,7 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase { private void assertUnmodified(ShapeBuilder builder) throws IOException { String before = Strings.toString(jsonBuilder().startObject().field("area", builder).endObject()); - builder.build(); + builder.buildS4J(); String after = Strings.toString(jsonBuilder().startObject().field("area", builder).endObject()); assertThat(before, equalTo(after)); } diff --git a/server/src/test/java/org/elasticsearch/test/geo/RandomShapeGenerator.java b/server/src/test/java/org/elasticsearch/test/geo/RandomShapeGenerator.java index e94d31b16a3..76d18a59f9f 100644 --- a/server/src/test/java/org/elasticsearch/test/geo/RandomShapeGenerator.java +++ b/server/src/test/java/org/elasticsearch/test/geo/RandomShapeGenerator.java @@ -32,7 +32,6 @@ import org.elasticsearch.common.geo.builders.MultiPointBuilder; import org.elasticsearch.common.geo.builders.PointBuilder; import org.elasticsearch.common.geo.builders.PolygonBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder; -import org.elasticsearch.search.geo.GeoShapeQueryTests; import org.junit.Assert; import org.locationtech.spatial4j.context.jts.JtsSpatialContext; import org.locationtech.spatial4j.distance.DistanceUtils; @@ -154,7 +153,6 @@ public class RandomShapeGenerator extends RandomGeoGenerator { /** * Creates a random shape useful for randomized testing, NOTE: exercise caution when using this to build random GeometryCollections * as creating a large random number of random shapes can result in massive resource consumption - * see: {@link GeoShapeQueryTests#testShapeFilterWithRandomGeoCollection} * * The following options are included * @param nearPoint Create a shape near a provided point @@ -206,7 +204,7 @@ public class RandomShapeGenerator extends RandomGeoGenerator { numPoints = RandomNumbers.randomIntBetween(r, 5, 25); Coordinate[] coordinates = new Coordinate[numPoints]; for (int i=0; i match) { @@ -257,11 +292,11 @@ public class ElasticsearchGeoAssertions { public static void assertValidException(XContentParser parser, Class expectedException) { try { - ShapeParser.parse(parser).build(); + ShapeParser.parse(parser).buildS4J(); Assert.fail("process completed successfully when " + expectedException.getName() + " expected"); } catch (Exception e) { - assert(e.getClass().equals(expectedException)): - "expected " + expectedException.getName() + " but found " + e.getClass().getName(); + assertTrue("expected " + expectedException.getName() + " but found " + e.getClass().getName(), + e.getClass().equals(expectedException)); } } }