From 4a42e408c5034740c7ee7586a81ee7b853b84a84 Mon Sep 17 00:00:00 2001 From: Igor Motov Date: Wed, 13 Mar 2019 15:35:18 -0400 Subject: [PATCH] GEO: Add support for z values to libs/geo classes (#38921) Adds support for z-values to all Geometry objects in the libs/geo library. --- .../elasticsearch/geo/geometry/Circle.java | 25 ++++- .../elasticsearch/geo/geometry/Geometry.java | 4 + .../geo/geometry/GeometryCollection.java | 23 +++++ .../org/elasticsearch/geo/geometry/Line.java | 35 ++++++- .../geo/geometry/LinearRing.java | 17 +++- .../geo/geometry/MultiPoint.java | 2 +- .../org/elasticsearch/geo/geometry/Point.java | 25 ++++- .../elasticsearch/geo/geometry/Polygon.java | 11 +++ .../elasticsearch/geo/geometry/Rectangle.java | 51 +++++++++- .../geo/utils/WellKnownText.java | 97 +++++++++++++------ .../geo/geometry/BaseGeometryTestCase.java | 82 ++++++++++++---- .../geo/geometry/CircleTests.java | 12 ++- .../geo/geometry/GeometryCollectionTests.java | 10 +- .../elasticsearch/geo/geometry/LineTests.java | 9 +- .../geo/geometry/LinearRingTests.java | 9 +- .../geo/geometry/MultiLineTests.java | 4 +- .../geo/geometry/MultiPointTests.java | 15 ++- .../geo/geometry/MultiPolygonTests.java | 4 +- .../geo/geometry/PointTests.java | 7 +- .../geo/geometry/PolygonTests.java | 15 ++- .../geo/geometry/RectangleTests.java | 6 +- 21 files changed, 382 insertions(+), 81 deletions(-) diff --git a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Circle.java b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Circle.java index fea582e07b3..7140540f5c1 100644 --- a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Circle.java +++ b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Circle.java @@ -20,24 +20,32 @@ package org.elasticsearch.geo.geometry; /** - * Circle geometry (not part of WKT standard, but used in elasticsearch) + * Circle geometry (not part of WKT standard, but used in elasticsearch) defined by lat/lon coordinates of the center in degrees + * and optional altitude in meters. */ public class Circle implements Geometry { public static final Circle EMPTY = new Circle(); private final double lat; private final double lon; + private final double alt; private final double radiusMeters; private Circle() { lat = 0; lon = 0; + alt = Double.NaN; radiusMeters = -1; } public Circle(final double lat, final double lon, final double radiusMeters) { + this(lat, lon, Double.NaN, radiusMeters); + } + + public Circle(final double lat, final double lon, final double alt, final double radiusMeters) { this.lat = lat; this.lon = lon; this.radiusMeters = radiusMeters; + this.alt = alt; if (radiusMeters < 0 ) { throw new IllegalArgumentException("Circle radius [" + radiusMeters + "] cannot be negative"); } @@ -62,6 +70,10 @@ public class Circle implements Geometry { return radiusMeters; } + public double getAlt() { + return alt; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -70,7 +82,8 @@ public class Circle implements Geometry { Circle circle = (Circle) o; if (Double.compare(circle.lat, lat) != 0) return false; if (Double.compare(circle.lon, lon) != 0) return false; - return (Double.compare(circle.radiusMeters, radiusMeters) == 0); + if (Double.compare(circle.radiusMeters, radiusMeters) != 0) return false; + return (Double.compare(circle.alt, alt) == 0); } @Override @@ -83,6 +96,8 @@ public class Circle implements Geometry { result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(radiusMeters); result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(alt); + result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } @@ -98,7 +113,11 @@ public class Circle implements Geometry { @Override public String toString() { - return "lat=" + lat + ", lon=" + lon + ", radius=" + radiusMeters; + return "lat=" + lat + ", lon=" + lon + ", radius=" + radiusMeters + (Double.isNaN(alt) ? ", alt=" + alt : ""); } + @Override + public boolean hasAlt() { + return Double.isNaN(alt) == false; + } } diff --git a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Geometry.java b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Geometry.java index 4557780effc..9322193326f 100644 --- a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Geometry.java +++ b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Geometry.java @@ -29,4 +29,8 @@ public interface Geometry { T visit(GeometryVisitor visitor); boolean isEmpty(); + + default boolean hasAlt() { + return false; + } } diff --git a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/GeometryCollection.java b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/GeometryCollection.java index a6bad62efad..56e59f94983 100644 --- a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/GeometryCollection.java +++ b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/GeometryCollection.java @@ -21,6 +21,7 @@ package org.elasticsearch.geo.geometry; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Objects; /** @@ -31,6 +32,8 @@ public class GeometryCollection implements Geometry, Iterabl private final List shapes; + private boolean hasAlt; + public GeometryCollection() { shapes = Collections.emptyList(); } @@ -39,6 +42,12 @@ public class GeometryCollection implements Geometry, Iterabl if (shapes == null || shapes.isEmpty()) { throw new IllegalArgumentException("the list of shapes cannot be null or empty"); } + hasAlt = shapes.get(0).hasAlt(); + for (G shape : shapes) { + if (shape.hasAlt() != hasAlt) { + throw new IllegalArgumentException("all elements of the collection should have the same number of dimension"); + } + } this.shapes = shapes; } @@ -82,4 +91,18 @@ public class GeometryCollection implements Geometry, Iterabl public Iterator iterator() { return shapes.iterator(); } + + @Override + public boolean hasAlt() { + return hasAlt; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(type().name().toLowerCase(Locale.ROOT)).append("(shapes="); + sb.append(shapes); + sb.append(")"); + return sb.toString(); + } } diff --git a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Line.java b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Line.java index 348537205af..e06ccc555aa 100644 --- a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Line.java +++ b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Line.java @@ -22,21 +22,28 @@ package org.elasticsearch.geo.geometry; import java.util.Arrays; /** - * Represents a Line on the earth's surface in lat/lon decimal degrees. + * Represents a Line on the earth's surface in lat/lon decimal degrees and optional altitude in meters. */ public class Line implements Geometry { public static final Line EMPTY = new Line(); private final double[] lats; private final double[] lons; + private final double[] alts; protected Line() { lats = new double[0]; lons = new double[0]; + alts = null; } public Line(double[] lats, double[] lons) { + this(lats, lons, null); + } + + public Line(double[] lats, double[] lons, double[] alts) { this.lats = lats; this.lons = lons; + this.alts = alts; if (lats == null) { throw new IllegalArgumentException("lats must not be null"); } @@ -49,6 +56,9 @@ public class Line implements Geometry { if (lats.length < 2) { throw new IllegalArgumentException("at least two points in the line is required"); } + if (alts != null && alts.length != lats.length) { + throw new IllegalArgumentException("alts and lats must be equal length"); + } for (int i = 0; i < lats.length; i++) { GeometryUtils.checkLatitude(lats[i]); GeometryUtils.checkLongitude(lons[i]); @@ -67,6 +77,14 @@ public class Line implements Geometry { return lons[i]; } + public double getAlt(int i) { + if (alts != null) { + return alts[i]; + } else { + return Double.NaN; + } + } + public double[] getLats() { return lats.clone(); } @@ -75,6 +93,10 @@ public class Line implements Geometry { return lons.clone(); } + public double[] getAlts() { + return alts == null ? null : alts.clone(); + } + @Override public ShapeType type() { return ShapeType.LINESTRING; @@ -96,19 +118,26 @@ public class Line implements Geometry { if (o == null || getClass() != o.getClass()) return false; Line line = (Line) o; return Arrays.equals(lats, line.lats) && - Arrays.equals(lons, line.lons); + Arrays.equals(lons, line.lons) && Arrays.equals(alts, line.alts); } @Override public int hashCode() { int result = Arrays.hashCode(lats); result = 31 * result + Arrays.hashCode(lons); + result = 31 * result + Arrays.hashCode(alts); return result; } + @Override + public boolean hasAlt() { + return alts != null; + } + @Override public String toString() { return "lats=" + Arrays.toString(lats) + - ", lons=" + Arrays.toString(lons); + ", lons=" + Arrays.toString(lons) + + (hasAlt() ? ", alts=" + Arrays.toString(alts) : ""); } } diff --git a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/LinearRing.java b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/LinearRing.java index 20b1a46dd9d..7d66a93ea6d 100644 --- a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/LinearRing.java +++ b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/LinearRing.java @@ -20,7 +20,7 @@ package org.elasticsearch.geo.geometry; /** - * Represents a closed line on the earth's surface in lat/lon decimal degrees. + * Represents a closed line on the earth's surface in lat/lon decimal degrees and optional altitude in meters. *

* Cannot be serialized by WKT directly but used as a part of polygon */ @@ -31,13 +31,20 @@ public class LinearRing extends Line { } public LinearRing(double[] lats, double[] lons) { - super(lats, lons); + this(lats, lons, null); + } + + public LinearRing(double[] lats, double[] lons, double[] alts) { + super(lats, lons, alts); if (lats.length < 2) { throw new IllegalArgumentException("linear ring cannot contain less than 2 points, found " + lats.length); } - if (lats[0] != lats[lats.length - 1] || lons[0] != lons[lons.length - 1]) { - throw new IllegalArgumentException("first and last points of the linear ring must be the same (it must close itself): lats[0]=" - + lats[0] + " lats[" + (lats.length - 1) + "]=" + lats[lats.length - 1]); + int last = lats.length - 1; + if (lats[0] != lats[last] || lons[0] != lons[last] || (alts != null && alts[0] != alts[last])) { + throw new IllegalArgumentException("first and last points of the linear ring must be the same (it must close itself):" + + " lats[0]=" + lats[0] + " lats[" + last + "]=" + lats[last] + + " lons[0]=" + lons[0] + " lons[" + last + "]=" + lons[last] + + (alts == null ? "" : " alts[0]=" + alts[0] + " alts[" + last + "]=" + alts[last] )); } } diff --git a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/MultiPoint.java b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/MultiPoint.java index 383fef81219..7d57b66ca56 100644 --- a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/MultiPoint.java +++ b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/MultiPoint.java @@ -22,7 +22,7 @@ package org.elasticsearch.geo.geometry; import java.util.List; /** - * Represents a MultiPoint object on the earth's surface in decimal degrees. + * Represents a MultiPoint object on the earth's surface in decimal degrees and optional altitude in meters. */ public class MultiPoint extends GeometryCollection { public static final MultiPoint EMPTY = new MultiPoint(); diff --git a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Point.java b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Point.java index c272261ee3a..189968fdd40 100644 --- a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Point.java +++ b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Point.java @@ -20,26 +20,33 @@ package org.elasticsearch.geo.geometry; /** - * Represents a Point on the earth's surface in decimal degrees. + * Represents a Point on the earth's surface in decimal degrees and optional altitude in meters. */ public class Point implements Geometry { public static final Point EMPTY = new Point(); private final double lat; private final double lon; + private final double alt; private final boolean empty; private Point() { lat = 0; lon = 0; + alt = Double.NaN; empty = true; } public Point(double lat, double lon) { + this(lat, lon, Double.NaN); + } + + public Point(double lat, double lon, double alt) { GeometryUtils.checkLatitude(lat); GeometryUtils.checkLongitude(lon); this.lat = lat; this.lon = lon; + this.alt = alt; this.empty = false; } @@ -56,6 +63,10 @@ public class Point implements Geometry { return lon; } + public double getAlt() { + return alt; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -64,7 +75,8 @@ public class Point implements Geometry { Point point = (Point) o; if (point.empty != empty) return false; if (Double.compare(point.lat, lat) != 0) return false; - return Double.compare(point.lon, lon) == 0; + if (Double.compare(point.lon, lon) != 0) return false; + return Double.compare(point.alt, alt) == 0; } @Override @@ -75,6 +87,8 @@ public class Point implements Geometry { result = (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(lon); result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(alt); + result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } @@ -88,8 +102,13 @@ public class Point implements Geometry { return empty; } + @Override + public boolean hasAlt() { + return Double.isNaN(alt) == false; + } + @Override public String toString() { - return "lat=" + lat + ", lon=" + lon; + return "lat=" + lat + ", lon=" + lon + (hasAlt() ? ", alt=" + alt : ""); } } diff --git a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Polygon.java b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Polygon.java index 9f28c4b81b6..1dee1c69fc8 100644 --- a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Polygon.java +++ b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Polygon.java @@ -30,10 +30,12 @@ public final class Polygon implements Geometry { public static final Polygon EMPTY = new Polygon(); private final LinearRing polygon; private final List holes; + private final boolean hasAlt; private Polygon() { polygon = LinearRing.EMPTY; holes = Collections.emptyList(); + hasAlt = false; } /** @@ -45,10 +47,15 @@ public final class Polygon implements Geometry { if (holes == null) { throw new IllegalArgumentException("holes must not be null"); } + boolean hasAlt = polygon.hasAlt(); checkRing(polygon); for (LinearRing hole : holes) { + if (hole.hasAlt() != hasAlt) { + throw new IllegalArgumentException("holes must have the same number of dimensions as the polygon"); + } checkRing(hole); } + this.hasAlt = hasAlt; } /** @@ -94,6 +101,10 @@ public final class Polygon implements Geometry { return polygon.isEmpty(); } + @Override + public boolean hasAlt() { + return hasAlt; + } @Override public String toString() { diff --git a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Rectangle.java b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Rectangle.java index 8170bf95a6e..0507a5e60cc 100644 --- a/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Rectangle.java +++ b/libs/geo/src/main/java/org/elasticsearch/geo/geometry/Rectangle.java @@ -20,7 +20,7 @@ package org.elasticsearch.geo.geometry; /** - * Represents a lat/lon rectangle in decimal degrees. + * Represents a lat/lon rectangle in decimal degrees and optional altitude in meters. */ public class Rectangle implements Geometry { public static final Rectangle EMPTY = new Rectangle(); @@ -32,6 +32,10 @@ public class Rectangle implements Geometry { * minimum longitude value (in degrees) */ private final double minLon; + /** + * maximum altitude value (in meters) + */ + private final double minAlt; /** * maximum latitude value (in degrees) */ @@ -40,6 +44,10 @@ public class Rectangle implements Geometry { * minimum latitude value (in degrees) */ private final double maxLon; + /** + * minimum altitude value (in meters) + */ + private final double maxAlt; private final boolean empty; @@ -48,6 +56,8 @@ public class Rectangle implements Geometry { minLon = 0; maxLat = 0; maxLon = 0; + minAlt = Double.NaN; + maxAlt = Double.NaN; empty = true; } @@ -55,6 +65,12 @@ public class Rectangle implements Geometry { * Constructs a bounding box by first validating the provided latitude and longitude coordinates */ public Rectangle(double minLat, double maxLat, double minLon, double maxLon) { + this(minLat, maxLat, minLon, maxLon, Double.NaN, Double.NaN); + } + /** + * Constructs a bounding box by first validating the provided latitude and longitude coordinates + */ + public Rectangle(double minLat, double maxLat, double minLon, double maxLon, double minAlt, double maxAlt) { GeometryUtils.checkLatitude(minLat); GeometryUtils.checkLatitude(maxLat); GeometryUtils.checkLongitude(minLon); @@ -63,10 +79,15 @@ public class Rectangle implements Geometry { this.maxLon = maxLon; this.minLat = minLat; this.maxLat = maxLat; + this.minAlt = minAlt; + this.maxAlt = maxAlt; empty = false; if (maxLat < minLat) { throw new IllegalArgumentException("max lat cannot be less than min lat"); } + if (Double.isNaN(minAlt) != Double.isNaN(maxAlt)) { + throw new IllegalArgumentException("only one altitude value is specified"); + } } public double getWidth() { @@ -88,6 +109,11 @@ public class Rectangle implements Geometry { return minLon; } + + public double getMinAlt() { + return minAlt; + } + public double getMaxLat() { return maxLat; } @@ -96,6 +122,10 @@ public class Rectangle implements Geometry { return maxLon; } + public double getMaxAlt() { + return maxAlt; + } + @Override public ShapeType type() { return ShapeType.ENVELOPE; @@ -115,6 +145,12 @@ public class Rectangle implements Geometry { if (maxLon < minLon) { b.append(" [crosses dateline!]"); } + if (hasAlt()) { + b.append(" alt="); + b.append(minAlt); + b.append(" TO "); + b.append(maxAlt); + } b.append(")"); return b.toString(); @@ -137,7 +173,9 @@ public class Rectangle implements Geometry { if (Double.compare(rectangle.minLat, minLat) != 0) return false; if (Double.compare(rectangle.minLon, minLon) != 0) return false; if (Double.compare(rectangle.maxLat, maxLat) != 0) return false; - return Double.compare(rectangle.maxLon, maxLon) == 0; + if (Double.compare(rectangle.maxLon, maxLon) != 0) return false; + if (Double.compare(rectangle.minAlt, minAlt) != 0) return false; + return Double.compare(rectangle.maxAlt, maxAlt) == 0; } @@ -153,6 +191,10 @@ public class Rectangle implements Geometry { result = 31 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(maxLon); result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(minAlt); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(maxAlt); + result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } @@ -165,4 +207,9 @@ public class Rectangle implements Geometry { public boolean isEmpty() { return empty; } + + @Override + public boolean hasAlt() { + return Double.isNaN(maxAlt) == false; + } } diff --git a/libs/geo/src/main/java/org/elasticsearch/geo/utils/WellKnownText.java b/libs/geo/src/main/java/org/elasticsearch/geo/utils/WellKnownText.java index c6e96d9bdf3..ac0b8d9abfe 100644 --- a/libs/geo/src/main/java/org/elasticsearch/geo/utils/WellKnownText.java +++ b/libs/geo/src/main/java/org/elasticsearch/geo/utils/WellKnownText.java @@ -72,9 +72,13 @@ public class WellKnownText { @Override public Void visit(Circle circle) { sb.append(LPAREN); - visitPoint(circle.getLon(), circle.getLat()); + visitPoint(circle.getLon(), circle.getLat(), Double.NaN); sb.append(SPACE); sb.append(circle.getRadiusMeters()); + if (circle.hasAlt()) { + sb.append(SPACE); + sb.append(circle.getAlt()); + } sb.append(RPAREN); return null; } @@ -98,11 +102,11 @@ public class WellKnownText { @Override public Void visit(Line line) { sb.append(LPAREN); - visitPoint(line.getLon(0), line.getLat(0)); + visitPoint(line.getLon(0), line.getLat(0), line.getAlt(0)); for (int i = 1; i < line.length(); ++i) { sb.append(COMMA); sb.append(SPACE); - visitPoint(line.getLon(i), line.getLat(i)); + visitPoint(line.getLon(i), line.getLat(i), line.getAlt(i)); } sb.append(RPAREN); return null; @@ -127,12 +131,12 @@ public class WellKnownText { } // walk through coordinates: sb.append(LPAREN); - visitPoint(multiPoint.get(0).getLon(), multiPoint.get(0).getLat()); + visitPoint(multiPoint.get(0).getLon(), multiPoint.get(0).getLat(), multiPoint.get(0).getAlt()); for (int i = 1; i < multiPoint.size(); ++i) { sb.append(COMMA); sb.append(SPACE); Point point = multiPoint.get(i); - visitPoint(point.getLon(), point.getLat()); + visitPoint(point.getLon(), point.getLat(), point.getAlt()); } sb.append(RPAREN); return null; @@ -150,14 +154,17 @@ public class WellKnownText { sb.append(EMPTY); } else { sb.append(LPAREN); - visitPoint(point.getLon(), point.getLat()); + visitPoint(point.getLon(), point.getLat(), point.getAlt()); sb.append(RPAREN); } return null; } - private void visitPoint(double lon, double lat) { + private void visitPoint(double lon, double lat, double alt) { sb.append(lon).append(SPACE).append(lat); + if (Double.isNaN(alt) == false) { + sb.append(SPACE).append(alt); + } } private void visitCollection(GeometryCollection collection) { @@ -191,6 +198,7 @@ public class WellKnownText { public Void visit(Rectangle rectangle) { sb.append(LPAREN); // minX, maxX, maxY, minY + // TODO: Add 3D support sb.append(rectangle.getMinLon()); sb.append(COMMA); sb.append(SPACE); @@ -278,28 +286,33 @@ public class WellKnownText { } double lon = nextNumber(stream); double lat = nextNumber(stream); - Point pt = new Point(lat, lon); - if (isNumberNext(stream) == true) { - nextNumber(stream); + Point pt; + if (isNumberNext(stream)) { + pt = new Point(lat, lon, nextNumber(stream)); + } else { + pt = new Point(lat, lon); } nextCloser(stream); return pt; } - private static void parseCoordinates(StreamTokenizer stream, ArrayList lats, ArrayList lons) + private static void parseCoordinates(StreamTokenizer stream, ArrayList lats, ArrayList lons, ArrayList alts) throws IOException, ParseException { - parseCoordinate(stream, lats, lons); + parseCoordinate(stream, lats, lons, alts); while (nextCloserOrComma(stream).equals(COMMA)) { - parseCoordinate(stream, lats, lons); + parseCoordinate(stream, lats, lons, alts); } } - private static void parseCoordinate(StreamTokenizer stream, ArrayList lats, ArrayList lons) + private static void parseCoordinate(StreamTokenizer stream, ArrayList lats, ArrayList lons, ArrayList alts) throws IOException, ParseException { lons.add(nextNumber(stream)); lats.add(nextNumber(stream)); if (isNumberNext(stream)) { - nextNumber(stream); + alts.add(nextNumber(stream)); + } + if (alts.isEmpty() == false && alts.size() != lons.size()) { + throw new ParseException("coordinate dimensions do not match: " + tokenString(stream), stream.lineno()); } } @@ -310,10 +323,15 @@ public class WellKnownText { } ArrayList lats = new ArrayList<>(); ArrayList lons = new ArrayList<>(); + ArrayList alts = new ArrayList<>(); ArrayList points = new ArrayList<>(); - parseCoordinates(stream, lats, lons); + parseCoordinates(stream, lats, lons, alts); for (int i = 0; i < lats.size(); i++) { - points.add(new Point(lats.get(i), lons.get(i))); + if (alts.isEmpty()) { + points.add(new Point(lats.get(i), lons.get(i))); + } else { + points.add(new Point(lats.get(i), lons.get(i), alts.get(i))); + } } return new MultiPoint(Collections.unmodifiableList(points)); } @@ -325,8 +343,13 @@ public class WellKnownText { } ArrayList lats = new ArrayList<>(); ArrayList lons = new ArrayList<>(); - parseCoordinates(stream, lats, lons); - return new Line(lats.stream().mapToDouble(i -> i).toArray(), lons.stream().mapToDouble(i -> i).toArray()); + ArrayList alts = new ArrayList<>(); + parseCoordinates(stream, lats, lons, alts); + if (alts.isEmpty()) { + return new Line(toArray(lats), toArray(lons)); + } else { + return new Line(toArray(lats), toArray(lons), toArray(alts)); + } } private static MultiLine parseMultiLine(StreamTokenizer stream) throws IOException, ParseException { @@ -346,8 +369,13 @@ public class WellKnownText { nextOpener(stream); ArrayList lats = new ArrayList<>(); ArrayList lons = new ArrayList<>(); - parseCoordinates(stream, lats, lons); - return new LinearRing(lats.stream().mapToDouble(i -> i).toArray(), lons.stream().mapToDouble(i -> i).toArray()); + ArrayList alts = new ArrayList<>(); + parseCoordinates(stream, lats, lons, alts); + if (alts.isEmpty()) { + return new LinearRing(toArray(lats), toArray(lons)); + } else { + return new LinearRing(toArray(lats), toArray(lons), toArray(alts)); + } } private static Polygon parsePolygon(StreamTokenizer stream) throws IOException, ParseException { @@ -357,17 +385,22 @@ public class WellKnownText { nextOpener(stream); ArrayList lats = new ArrayList<>(); ArrayList lons = new ArrayList<>(); - parseCoordinates(stream, lats, lons); + ArrayList alts = new ArrayList<>(); + parseCoordinates(stream, lats, lons, alts); ArrayList holes = new ArrayList<>(); while (nextCloserOrComma(stream).equals(COMMA)) { holes.add(parsePolygonHole(stream)); } - if (holes.isEmpty()) { - return new Polygon(new LinearRing(lats.stream().mapToDouble(i -> i).toArray(), lons.stream().mapToDouble(i -> i).toArray())); + LinearRing shell; + if (alts.isEmpty()) { + shell = new LinearRing(toArray(lats), toArray(lons)); } else { - return new Polygon( - new LinearRing(lats.stream().mapToDouble(i -> i).toArray(), lons.stream().mapToDouble(i -> i).toArray()), - Collections.unmodifiableList(holes)); + shell = new LinearRing(toArray(lats), toArray(lons), toArray(alts)); + } + if (holes.isEmpty()) { + return new Polygon(shell); + } else { + return new Polygon(shell, Collections.unmodifiableList(holes)); } } @@ -388,6 +421,7 @@ public class WellKnownText { if (nextEmptyOrOpen(stream).equals(EMPTY)) { return Rectangle.EMPTY; } + // TODO: Add 3D support double minLon = nextNumber(stream); nextComma(stream); double maxLon = nextNumber(stream); @@ -407,10 +441,11 @@ public class WellKnownText { double lon = nextNumber(stream); double lat = nextNumber(stream); double radius = nextNumber(stream); - Circle circle = new Circle(lat, lon, radius); + double alt = Double.NaN; if (isNumberNext(stream) == true) { - nextNumber(stream); + alt = nextNumber(stream); } + Circle circle = new Circle(lat, lon, alt, radius); nextCloser(stream); return circle; } @@ -561,4 +596,8 @@ public class WellKnownText { }); } + private static double[] toArray(ArrayList doubles) { + return doubles.stream().mapToDouble(i -> i).toArray(); + } + } diff --git a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/BaseGeometryTestCase.java b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/BaseGeometryTestCase.java index 3ffabd21343..b3cc834faea 100644 --- a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/BaseGeometryTestCase.java +++ b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/BaseGeometryTestCase.java @@ -30,10 +30,20 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; +import java.util.function.Function; abstract class BaseGeometryTestCase extends AbstractWireTestCase { + @Override + protected final T createTestInstance() { + boolean hasAlt = randomBoolean(); + T obj = createTestInstance(hasAlt); + assertEquals(hasAlt, obj.hasAlt()); + return obj; + } + + protected abstract T createTestInstance(boolean hasAlt); + @Override protected Writeable.Reader instanceReader() { throw new IllegalStateException("shouldn't be called in this test"); @@ -127,48 +137,86 @@ abstract class BaseGeometryTestCase extends AbstractWireTest return randomDoubleBetween(-180, 180, true); } - public static Circle randomCircle() { - return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDoubleBetween(0, 100, false)); + public static Circle randomCircle(boolean hasAlt) { + if (hasAlt) { + return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDouble(), + randomDoubleBetween(0, 100, false)); + } else { + return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDoubleBetween(0, 100, false)); + } } public static Line randomLine() { + return randomLine(randomBoolean()); + } + + public static Line randomLine(boolean hasAlts) { int size = randomIntBetween(2, 10); double[] lats = new double[size]; double[] lons = new double[size]; + double[] alts = hasAlts ? new double[size] : null; for (int i = 0; i < size; i++) { lats[i] = randomLat(); lons[i] = randomLon(); + if (hasAlts) { + alts[i] = randomDouble(); + } + } + if (hasAlts) { + return new Line(lats, lons, alts); } return new Line(lats, lons); } public static Point randomPoint() { - return new Point(randomLat(), randomLon()); + return randomPoint(randomBoolean()); } - public static LinearRing randomLinearRing() { + public static Point randomPoint(boolean hasAlt) { + if (hasAlt) { + return new Point(randomLat(), randomLon(), randomDouble()); + } else { + return new Point(randomLat(), randomLon()); + } + } + + public static LinearRing randomLinearRing(boolean hasAlt) { int size = randomIntBetween(3, 10); double[] lats = new double[size + 1]; double[] lons = new double[size + 1]; + double[] alts; + if (hasAlt) { + alts = new double[size + 1]; + } else { + alts = null; + } for (int i = 0; i < size; i++) { lats[i] = randomLat(); lons[i] = randomLon(); + if (hasAlt) { + alts[i] = randomDouble(); + } } lats[size] = lats[0]; lons[size] = lons[0]; - return new LinearRing(lats, lons); + if (hasAlt) { + alts[size] = alts[0]; + return new LinearRing(lats, lons, alts); + } else { + return new LinearRing(lats, lons); + } } - public static Polygon randomPolygon() { + public static Polygon randomPolygon(boolean hasAlt) { int size = randomIntBetween(0, 10); List holes = new ArrayList<>(); for (int i = 0; i < size; i++) { - holes.add(randomLinearRing()); + holes.add(randomLinearRing(hasAlt)); } if (holes.size() > 0) { - return new Polygon(randomLinearRing(), holes); + return new Polygon(randomLinearRing(hasAlt), holes); } else { - return new Polygon(randomLinearRing()); + return new Polygon(randomLinearRing(hasAlt)); } } @@ -180,23 +228,23 @@ abstract class BaseGeometryTestCase extends AbstractWireTest return new Rectangle(Math.min(lat1, lat2), Math.max(lat1, lat2), minLon, maxLon); } - public static GeometryCollection randomGeometryCollection() { - return randomGeometryCollection(0); + public static GeometryCollection randomGeometryCollection(boolean hasAlt) { + return randomGeometryCollection(0, hasAlt); } - private static GeometryCollection randomGeometryCollection(int level) { + private static GeometryCollection randomGeometryCollection(int level, boolean hasAlt) { int size = randomIntBetween(1, 10); List shapes = new ArrayList<>(); for (int i = 0; i < size; i++) { - @SuppressWarnings("unchecked") Supplier geometry = randomFrom( + @SuppressWarnings("unchecked") Function geometry = randomFrom( BaseGeometryTestCase::randomCircle, BaseGeometryTestCase::randomLine, BaseGeometryTestCase::randomPoint, BaseGeometryTestCase::randomPolygon, - BaseGeometryTestCase::randomRectangle, - level < 3 ? () -> randomGeometryCollection(level + 1) : BaseGeometryTestCase::randomPoint // don't build too deep + hasAlt ? BaseGeometryTestCase::randomPoint : (b) -> randomRectangle(), + level < 3 ? (b) -> randomGeometryCollection(level + 1, b) : BaseGeometryTestCase::randomPoint // don't build too deep ); - shapes.add(geometry.get()); + shapes.add(geometry.apply(hasAlt)); } return new GeometryCollection<>(shapes); } diff --git a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/CircleTests.java b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/CircleTests.java index 0f2292792a7..a38a29af24b 100644 --- a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/CircleTests.java +++ b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/CircleTests.java @@ -26,14 +26,22 @@ import java.text.ParseException; public class CircleTests extends BaseGeometryTestCase { @Override - protected Circle createTestInstance() { - return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDoubleBetween(0, 100, false)); + protected Circle createTestInstance(boolean hasAlt) { + if (hasAlt) { + return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDouble(), + randomDoubleBetween(0, 100, false)); + } else { + return new Circle(randomDoubleBetween(-90, 90, true), randomDoubleBetween(-180, 180, true), randomDoubleBetween(0, 100, false)); + } } public void testBasicSerialization() throws IOException, ParseException { assertEquals("circle (20.0 10.0 15.0)", WellKnownText.toWKT(new Circle(10, 20, 15))); assertEquals(new Circle(10, 20, 15), WellKnownText.fromWKT("circle (20.0 10.0 15.0)")); + assertEquals("circle (20.0 10.0 15.0 25.0)", WellKnownText.toWKT(new Circle(10, 20, 25, 15))); + assertEquals(new Circle(10, 20, 25, 15), WellKnownText.fromWKT("circle (20.0 10.0 15.0 25.0)")); + assertEquals("circle EMPTY", WellKnownText.toWKT(Circle.EMPTY)); assertEquals(Circle.EMPTY, WellKnownText.fromWKT("circle EMPTY)")); } diff --git a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/GeometryCollectionTests.java b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/GeometryCollectionTests.java index 21b3c654103..8b641527063 100644 --- a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/GeometryCollectionTests.java +++ b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/GeometryCollectionTests.java @@ -28,10 +28,12 @@ import java.util.Collections; public class GeometryCollectionTests extends BaseGeometryTestCase> { @Override - protected GeometryCollection createTestInstance() { - return randomGeometryCollection(); + protected GeometryCollection createTestInstance(boolean hasAlt) { + return randomGeometryCollection(hasAlt); } + + public void testBasicSerialization() throws IOException, ParseException { assertEquals("geometrycollection (point (20.0 10.0),point EMPTY)", WellKnownText.toWKT(new GeometryCollection(Arrays.asList(new Point(10, 20), Point.EMPTY)))); @@ -50,5 +52,9 @@ public class GeometryCollectionTests extends BaseGeometryTestCase new GeometryCollection<>(null)); assertEquals("the list of shapes cannot be null or empty", ex.getMessage()); + + ex = expectThrows(IllegalArgumentException.class, () -> new GeometryCollection<>( + Arrays.asList(new Point(10, 20), new Point(10, 20, 30)))); + assertEquals("all elements of the collection should have the same number of dimension", ex.getMessage()); } } diff --git a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/LineTests.java b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/LineTests.java index 9914481df44..7156039f908 100644 --- a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/LineTests.java +++ b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/LineTests.java @@ -26,14 +26,19 @@ import java.text.ParseException; public class LineTests extends BaseGeometryTestCase { @Override - protected Line createTestInstance() { - return randomLine(); + protected Line createTestInstance(boolean hasAlt) { + return randomLine(hasAlt); } public void testBasicSerialization() throws IOException, ParseException { assertEquals("linestring (3.0 1.0, 4.0 2.0)", WellKnownText.toWKT(new Line(new double[]{1, 2}, new double[]{3, 4}))); assertEquals(new Line(new double[]{1, 2}, new double[]{3, 4}), WellKnownText.fromWKT("linestring (3 1, 4 2)")); + assertEquals("linestring (3.0 1.0 5.0, 4.0 2.0 6.0)", WellKnownText.toWKT(new Line(new double[]{1, 2}, new double[]{3, 4}, + new double[]{5, 6}))); + assertEquals(new Line(new double[]{1, 2}, new double[]{3, 4}, new double[]{6, 5}), + WellKnownText.fromWKT("linestring (3 1 6, 4 2 5)")); + assertEquals("linestring EMPTY", WellKnownText.toWKT(Line.EMPTY)); assertEquals(Line.EMPTY, WellKnownText.fromWKT("linestring EMPTY)")); } diff --git a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/LinearRingTests.java b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/LinearRingTests.java index 73f6ac9a2f9..d5b708f558c 100644 --- a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/LinearRingTests.java +++ b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/LinearRingTests.java @@ -33,7 +33,14 @@ public class LinearRingTests extends ESTestCase { public void testInitValidation() { IllegalArgumentException ex = expectThrows(IllegalArgumentException.class, () -> new LinearRing(new double[]{1, 2, 3}, new double[]{3, 4, 5})); - assertEquals("first and last points of the linear ring must be the same (it must close itself): lats[0]=1.0 lats[2]=3.0", + assertEquals("first and last points of the linear ring must be the same (it must close itself): lats[0]=1.0 lats[2]=3.0 " + + "lons[0]=3.0 lons[2]=5.0", + ex.getMessage()); + + ex = expectThrows(IllegalArgumentException.class, + () -> new LinearRing(new double[]{1, 2, 1}, new double[]{3, 4, 3}, new double[]{1, 2, 3})); + assertEquals("first and last points of the linear ring must be the same (it must close itself): lats[0]=1.0 lats[2]=1.0 " + + "lons[0]=3.0 lons[2]=3.0 alts[0]=1.0 alts[2]=3.0", ex.getMessage()); ex = expectThrows(IllegalArgumentException.class, () -> new LinearRing(new double[]{1}, new double[]{3})); diff --git a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiLineTests.java b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiLineTests.java index ec5a8c2dd38..3fcb84d93b7 100644 --- a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiLineTests.java +++ b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiLineTests.java @@ -30,11 +30,11 @@ import java.util.List; public class MultiLineTests extends BaseGeometryTestCase { @Override - protected MultiLine createTestInstance() { + protected MultiLine createTestInstance(boolean hasAlt) { int size = randomIntBetween(1, 10); List arr = new ArrayList(); for (int i = 0; i < size; i++) { - arr.add(randomLine()); + arr.add(randomLine(hasAlt)); } return new MultiLine(arr); } diff --git a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiPointTests.java b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiPointTests.java index 81c8c6f3fac..ecdcc0815a8 100644 --- a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiPointTests.java +++ b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiPointTests.java @@ -24,17 +24,18 @@ import org.elasticsearch.geo.utils.WellKnownText; import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; public class MultiPointTests extends BaseGeometryTestCase { @Override - protected MultiPoint createTestInstance() { + protected MultiPoint createTestInstance(boolean hasAlt) { int size = randomIntBetween(1, 10); List arr = new ArrayList<>(); for (int i = 0; i < size; i++) { - arr.add(randomPoint()); + arr.add(randomPoint(hasAlt)); } return new MultiPoint(arr); } @@ -45,6 +46,16 @@ public class MultiPointTests extends BaseGeometryTestCase { assertEquals(new MultiPoint(Collections.singletonList(new Point(1 ,2))), WellKnownText.fromWKT("multipoint (2 1)")); + assertEquals("multipoint (2.0 1.0, 3.0 4.0)", + WellKnownText.toWKT(new MultiPoint(Arrays.asList(new Point(1, 2), new Point(4, 3))))); + assertEquals(new MultiPoint(Arrays.asList(new Point(1, 2), new Point(4, 3))), + WellKnownText.fromWKT("multipoint (2 1, 3 4)")); + + assertEquals("multipoint (2.0 1.0 10.0, 3.0 4.0 20.0)", + WellKnownText.toWKT(new MultiPoint(Arrays.asList(new Point(1, 2, 10), new Point(4, 3, 20))))); + assertEquals(new MultiPoint(Arrays.asList(new Point(1, 2, 10), new Point(4, 3, 20))), + WellKnownText.fromWKT("multipoint (2 1 10, 3 4 20)")); + assertEquals("multipoint EMPTY", WellKnownText.toWKT(MultiPoint.EMPTY)); assertEquals(MultiPoint.EMPTY, WellKnownText.fromWKT("multipoint EMPTY)")); } diff --git a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiPolygonTests.java b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiPolygonTests.java index 38257245603..1b82cfd3871 100644 --- a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiPolygonTests.java +++ b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/MultiPolygonTests.java @@ -30,11 +30,11 @@ import java.util.List; public class MultiPolygonTests extends BaseGeometryTestCase { @Override - protected MultiPolygon createTestInstance() { + protected MultiPolygon createTestInstance(boolean hasAlt) { int size = randomIntBetween(1, 10); List arr = new ArrayList<>(); for (int i = 0; i < size; i++) { - arr.add(randomPolygon()); + arr.add(randomPolygon(hasAlt)); } return new MultiPolygon(arr); } diff --git a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/PointTests.java b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/PointTests.java index bfdb369d7a8..d480f44e30a 100644 --- a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/PointTests.java +++ b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/PointTests.java @@ -26,14 +26,17 @@ import java.text.ParseException; public class PointTests extends BaseGeometryTestCase { @Override - protected Point createTestInstance() { - return randomPoint(); + protected Point createTestInstance(boolean hasAlt) { + return randomPoint(hasAlt); } public void testBasicSerialization() throws IOException, ParseException { assertEquals("point (20.0 10.0)", WellKnownText.toWKT(new Point(10, 20))); assertEquals(new Point(10, 20), WellKnownText.fromWKT("point (20.0 10.0)")); + assertEquals("point (20.0 10.0 100.0)", WellKnownText.toWKT(new Point(10, 20, 100))); + assertEquals(new Point(10, 20, 100), WellKnownText.fromWKT("point (20.0 10.0 100.0)")); + assertEquals("point EMPTY", WellKnownText.toWKT(Point.EMPTY)); assertEquals(Point.EMPTY, WellKnownText.fromWKT("point EMPTY)")); } diff --git a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/PolygonTests.java b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/PolygonTests.java index 69a6d232083..fd303243589 100644 --- a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/PolygonTests.java +++ b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/PolygonTests.java @@ -23,11 +23,12 @@ import org.elasticsearch.geo.utils.WellKnownText; import java.io.IOException; import java.text.ParseException; +import java.util.Collections; public class PolygonTests extends BaseGeometryTestCase { @Override - protected Polygon createTestInstance() { - return randomPolygon(); + protected Polygon createTestInstance(boolean hasAlt) { + return randomPolygon(hasAlt); } public void testBasicSerialization() throws IOException, ParseException { @@ -36,6 +37,11 @@ public class PolygonTests extends BaseGeometryTestCase { assertEquals(new Polygon(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3})), WellKnownText.fromWKT("polygon ((3 1, 4 2, 5 3, 3 1))")); + assertEquals("polygon ((3.0 1.0 5.0, 4.0 2.0 4.0, 5.0 3.0 3.0, 3.0 1.0 5.0))", + WellKnownText.toWKT(new Polygon(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3}, new double[]{5, 4, 3, 5})))); + assertEquals(new Polygon(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3}, new double[]{5, 4, 3, 5})), + WellKnownText.fromWKT("polygon ((3 1 5, 4 2 4, 5 3 3, 3 1 5))")); + assertEquals("polygon EMPTY", WellKnownText.toWKT(Polygon.EMPTY)); assertEquals(Polygon.EMPTY, WellKnownText.fromWKT("polygon EMPTY)")); } @@ -48,5 +54,10 @@ public class PolygonTests extends BaseGeometryTestCase { ex = expectThrows(IllegalArgumentException.class, () -> new Polygon(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3}), null)); assertEquals("holes must not be null", ex.getMessage()); + + ex = expectThrows(IllegalArgumentException.class, + () -> new Polygon(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3}, new double[]{5, 4, 3, 5}), + Collections.singletonList(new LinearRing(new double[]{1, 2, 3, 1}, new double[]{3, 4, 5, 3})))); + assertEquals("holes must have the same number of dimensions as the polygon", ex.getMessage()); } } diff --git a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/RectangleTests.java b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/RectangleTests.java index fa5cbcd0a8f..14cb777f94b 100644 --- a/libs/geo/src/test/java/org/elasticsearch/geo/geometry/RectangleTests.java +++ b/libs/geo/src/test/java/org/elasticsearch/geo/geometry/RectangleTests.java @@ -26,7 +26,8 @@ import java.text.ParseException; public class RectangleTests extends BaseGeometryTestCase { @Override - protected Rectangle createTestInstance() { + protected Rectangle createTestInstance(boolean hasAlt) { + assumeFalse("3rd dimension is not supported yet", hasAlt); return randomRectangle(); } @@ -47,5 +48,8 @@ public class RectangleTests extends BaseGeometryTestCase { ex = expectThrows(IllegalArgumentException.class, () -> new Rectangle(2, 1, 2, 3)); assertEquals("max lat cannot be less than min lat", ex.getMessage()); + + ex = expectThrows(IllegalArgumentException.class, () -> new Rectangle(1, 2, 2, 3, 5, Double.NaN)); + assertEquals("only one altitude value is specified", ex.getMessage()); } }