From 4a54ffb553ad1e45da147dd93f63a20bb4564c91 Mon Sep 17 00:00:00 2001 From: Ignacio Vera Date: Fri, 14 Feb 2020 11:39:10 +0100 Subject: [PATCH] LUCENE-9218: XYGeometries should expose values as floats (#1252) --- lucene/CHANGES.txt | 2 + .../java/org/apache/lucene/geo/Line2D.java | 2 +- .../java/org/apache/lucene/geo/Polygon2D.java | 4 +- .../org/apache/lucene/geo/Tessellator.java | 14 ++- .../apache/lucene/geo/XYEncodingUtils.java | 26 +++-- .../java/org/apache/lucene/geo/XYLine.java | 50 ++++---- .../java/org/apache/lucene/geo/XYPoint.java | 18 +-- .../java/org/apache/lucene/geo/XYPolygon.java | 54 ++++----- .../org/apache/lucene/geo/XYRectangle.java | 44 +++---- .../org/apache/lucene/geo/XYRectangle2D.java | 5 +- .../lucene/document/BaseXYShapeTestCase.java | 18 +-- .../document/TestXYLineShapeQueries.java | 10 +- .../document/TestXYMultiLineShapeQueries.java | 2 +- .../TestXYMultiPointShapeQueries.java | 2 +- .../TestXYMultiPolygonShapeQueries.java | 2 +- .../document/TestXYPointShapeQueries.java | 6 +- .../document/TestXYPolygonShapeQueries.java | 2 +- .../apache/lucene/document/TestXYShape.java | 34 +++--- .../lucene/document/TestXYShapeEncoding.java | 8 +- .../org/apache/lucene/geo/ShapeTestUtil.java | 77 +++++++------ .../org/apache/lucene/geo/TestXYLine.java | 99 ++++++++++++++++ .../org/apache/lucene/geo/TestXYPoint.java | 77 +++++++++++++ .../org/apache/lucene/geo/TestXYPolygon.java | 107 ++++++++++++++++++ .../apache/lucene/geo/TestXYRectangle.java | 106 +++++++++++++++++ .../apache/lucene/geo/TestXYRectangle2D.java | 16 +-- 25 files changed, 589 insertions(+), 196 deletions(-) create mode 100644 lucene/core/src/test/org/apache/lucene/geo/TestXYLine.java create mode 100644 lucene/core/src/test/org/apache/lucene/geo/TestXYPoint.java create mode 100644 lucene/core/src/test/org/apache/lucene/geo/TestXYPolygon.java create mode 100644 lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle.java diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index ee8a60d0e31..41d3a493e41 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -108,6 +108,8 @@ API Changes * LUCENE-8621: Refactor LatLonShape, XYShape, and all query and utility classes to core. (Nick Knize) +* LUCENE-9218: XY geometries API works in float space. (Ignacio Vera) + New Features --------------------- diff --git a/lucene/core/src/java/org/apache/lucene/geo/Line2D.java b/lucene/core/src/java/org/apache/lucene/geo/Line2D.java index 5c6593ad76e..1d826210982 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/Line2D.java +++ b/lucene/core/src/java/org/apache/lucene/geo/Line2D.java @@ -50,7 +50,7 @@ final class Line2D implements Component2D { this.maxY = line.maxY; this.minX = line.minX; this.maxX = line.maxX; - this.tree = EdgeTree.createTree(line.getX(), line.getY()); + this.tree = EdgeTree.createTree(XYEncodingUtils.floatArrayToDoubleArray(line.getX()), XYEncodingUtils.floatArrayToDoubleArray(line.getY())); } @Override diff --git a/lucene/core/src/java/org/apache/lucene/geo/Polygon2D.java b/lucene/core/src/java/org/apache/lucene/geo/Polygon2D.java index 3e5fdb03a06..b69b66c1bb7 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/Polygon2D.java +++ b/lucene/core/src/java/org/apache/lucene/geo/Polygon2D.java @@ -49,10 +49,10 @@ final class Polygon2D implements Component2D { } private Polygon2D(XYPolygon polygon, Component2D holes) { - this(polygon.minX, polygon.maxX, polygon.minY, polygon.maxY, polygon.getPolyX(), polygon.getPolyY(), holes); + this(polygon.minX, polygon.maxX, polygon.minY, polygon.maxY, XYEncodingUtils.floatArrayToDoubleArray(polygon.getPolyX()), XYEncodingUtils.floatArrayToDoubleArray(polygon.getPolyY()), holes); } - protected Polygon2D(Polygon polygon, Component2D holes) { + private Polygon2D(Polygon polygon, Component2D holes) { this(polygon.minLon, polygon.maxLon, polygon.minLat, polygon.maxLat, polygon.getPolyLons(), polygon.getPolyLats(), holes); } diff --git a/lucene/core/src/java/org/apache/lucene/geo/Tessellator.java b/lucene/core/src/java/org/apache/lucene/geo/Tessellator.java index 12695d8dcb1..061c50ac424 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/Tessellator.java +++ b/lucene/core/src/java/org/apache/lucene/geo/Tessellator.java @@ -122,9 +122,9 @@ final public class Tessellator { public static final List tessellate(final XYPolygon polygon) { // Attempt to establish a doubly-linked list of the provided shell points (should be CCW, but this will correct); - // then filter instances of intersections. - Node outerNode = createDoublyLinkedList(polygon.getPolyX(), polygon.getPolyY(), polygon.getWindingOrder(), false, - 0, WindingOrder.CW); + // then filter instances of intersections.0 + Node outerNode = createDoublyLinkedList(XYEncodingUtils.floatArrayToDoubleArray(polygon.getPolyX()), XYEncodingUtils.floatArrayToDoubleArray(polygon.getPolyY()), + polygon.getWindingOrder(), false, 0, WindingOrder.CW); // If an outer node hasn't been detected, the shape is malformed. (must comply with OGC SFA specification) if(outerNode == null) { throw new IllegalArgumentException("Malformed shape detected in Tessellator!"); @@ -193,7 +193,8 @@ final public class Tessellator { int nodeIndex = polygon.numPoints() ; for(int i = 0; i < polygon.numHoles(); ++i) { // create the doubly-linked hole list - Node list = createDoublyLinkedList(holes[i].getPolyX(), holes[i].getPolyY(), holes[i].getWindingOrder(), false, nodeIndex, WindingOrder.CCW); + Node list = createDoublyLinkedList(XYEncodingUtils.floatArrayToDoubleArray(holes[i].getPolyX()), + XYEncodingUtils.floatArrayToDoubleArray(holes[i].getPolyY()), holes[i].getWindingOrder(), false, nodeIndex, WindingOrder.CCW); // Determine if the resulting hole polygon was successful. if(list != null) { // Add the leftmost vertex of the hole. @@ -1059,8 +1060,9 @@ final public class Tessellator { this.vrtxIdx = vertexIndex; this.polyX = x; this.polyY = y; - this.y = isGeo ? encodeLatitude(polyY[vrtxIdx]) : XYEncodingUtils.encode(polyY[vrtxIdx]); - this.x = isGeo ? encodeLongitude(polyX[vrtxIdx]) : XYEncodingUtils.encode(polyX[vrtxIdx]); + // casting to float is safe as original values for non-geo are represented as floats + this.y = isGeo ? encodeLatitude(polyY[vrtxIdx]) : XYEncodingUtils.encode((float) polyY[vrtxIdx]); + this.x = isGeo ? encodeLongitude(polyX[vrtxIdx]) : XYEncodingUtils.encode((float) polyX[vrtxIdx]); this.morton = BitUtil.interleave(this.x ^ 0x80000000, this.y ^ 0x80000000); this.previous = null; this.next = null; diff --git a/lucene/core/src/java/org/apache/lucene/geo/XYEncodingUtils.java b/lucene/core/src/java/org/apache/lucene/geo/XYEncodingUtils.java index b2c1d011314..4f2a64ff725 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/XYEncodingUtils.java +++ b/lucene/core/src/java/org/apache/lucene/geo/XYEncodingUtils.java @@ -33,11 +33,12 @@ public final class XYEncodingUtils { private XYEncodingUtils() { } - /** validates value is within +/-{@link Float#MAX_VALUE} coordinate bounds */ - public static void checkVal(double x) { - if (Double.isNaN(x) || x < MIN_VAL_INCL || x > MAX_VAL_INCL) { + /** validates value is a number and finite */ + static float checkVal(float x) { + if (Float.isFinite(x) == false) { throw new IllegalArgumentException("invalid value " + x + "; must be between " + MIN_VAL_INCL + " and " + MAX_VAL_INCL); } + return x; } /** @@ -46,9 +47,8 @@ public final class XYEncodingUtils { * @return encoded value as a 32-bit {@code int} * @throws IllegalArgumentException if value is out of bounds */ - public static int encode(double x) { - checkVal(x); - return NumericUtils.floatToSortableInt((float)x); + public static int encode(float x) { + return NumericUtils.floatToSortableInt(checkVal(x)); } /** @@ -56,8 +56,8 @@ public final class XYEncodingUtils { * @param encoded encoded value: 32-bit quantized value. * @return decoded value value. */ - public static double decode(int encoded) { - double result = NumericUtils.sortableIntToFloat(encoded); + public static float decode(int encoded) { + float result = NumericUtils.sortableIntToFloat(encoded); assert result >= MIN_VAL_INCL && result <= MAX_VAL_INCL; return result; } @@ -68,7 +68,15 @@ public final class XYEncodingUtils { * @param offset offset into {@code src} to decode from. * @return decoded value. */ - public static double decode(byte[] src, int offset) { + public static float decode(byte[] src, int offset) { return decode(NumericUtils.sortableBytesToInt(src, offset)); } + + static double[] floatArrayToDoubleArray(float[] f) { + double[] d = new double[f.length]; + for (int i = 0; i < f.length; i++) { + d[i] = f[i]; + } + return d; + } } diff --git a/lucene/core/src/java/org/apache/lucene/geo/XYLine.java b/lucene/core/src/java/org/apache/lucene/geo/XYLine.java index c3f59ac32aa..f2e446ad487 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/XYLine.java +++ b/lucene/core/src/java/org/apache/lucene/geo/XYLine.java @@ -18,24 +18,26 @@ package org.apache.lucene.geo; import java.util.Arrays; +import static org.apache.lucene.geo.XYEncodingUtils.checkVal; + /** * Represents a line in cartesian space. You can construct the Line directly with {@code float[]}, {@code float[]} x, y arrays * coordinates. */ public class XYLine extends XYGeometry { /** array of x coordinates */ - private final double[] x; + private final float[] x; /** array of y coordinates */ - private final double[] y; + private final float[] y; /** minimum x of this line's bounding box */ - public final double minX; + public final float minX; /** maximum y of this line's bounding box */ - public final double maxX; + public final float maxX; /** minimum y of this line's bounding box */ - public final double minY; + public final float minY; /** maximum y of this line's bounding box */ - public final double maxY; + public final float maxY; /** * Creates a new Line from the supplied X/Y array. @@ -55,23 +57,19 @@ public class XYLine extends XYGeometry { } // compute bounding box - double minX = x[0]; - double minY = y[0]; - double maxX = x[0]; - double maxY = y[0]; + float minX = Float.MAX_VALUE; + float minY = Float.MAX_VALUE; + float maxX = -Float.MAX_VALUE; + float maxY = -Float.MAX_VALUE; for (int i = 0; i < x.length; ++i) { - minX = Math.min(x[i], minX); - minY = Math.min(y[i], minY); + minX = Math.min(checkVal(x[i]), minX); + minY = Math.min(checkVal(y[i]), minY); maxX = Math.max(x[i], maxX); maxY = Math.max(y[i], maxY); } + this.x = x.clone(); + this.y = y.clone(); - this.x = new double[x.length]; - this.y = new double[y.length]; - for (int i = 0; i < x.length; ++i) { - this.x[i] = (double)x[i]; - this.y[i] = (double)y[i]; - } this.minX = minX; this.maxX = maxX; this.minY = minY; @@ -84,22 +82,22 @@ public class XYLine extends XYGeometry { } /** Returns x value at given index */ - public double getX(int vertex) { + public float getX(int vertex) { return x[vertex]; } /** Returns y value at given index */ - public double getY(int vertex) { + public float getY(int vertex) { return y[vertex]; } /** Returns a copy of the internal x array */ - public double[] getX() { + public float[] getX() { return x.clone(); } /** Returns a copy of the internal y array */ - public double[] getY() { + public float[] getY() { return y.clone(); } @@ -108,14 +106,6 @@ public class XYLine extends XYGeometry { return Line2D.create(this); } - public String toGeoJSON() { - StringBuilder sb = new StringBuilder(); - sb.append("["); - sb.append(Polygon.verticesToGeoJSON(x, y)); - sb.append("]"); - return sb.toString(); - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/lucene/core/src/java/org/apache/lucene/geo/XYPoint.java b/lucene/core/src/java/org/apache/lucene/geo/XYPoint.java index 9b6d0eeaa11..ad33133e13e 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/XYPoint.java +++ b/lucene/core/src/java/org/apache/lucene/geo/XYPoint.java @@ -17,6 +17,8 @@ package org.apache.lucene.geo; +import static org.apache.lucene.geo.XYEncodingUtils.checkVal; + /** * Represents a point on the earth's surface. You can construct the point directly with {@code double} * coordinates. @@ -30,25 +32,25 @@ package org.apache.lucene.geo; public final class XYPoint extends XYGeometry { /** latitude coordinate */ - private final double x; + private final float x; /** longitude coordinate */ - private final double y; + private final float y; /** * Creates a new Point from the supplied latitude/longitude. */ public XYPoint(float x, float y) { - this.x = x; - this.y = y; + this.x = checkVal(x); + this.y = checkVal(y); } /** Returns latitude value at given index */ - public double getX() { + public float getX() { return x; } /** Returns longitude value at given index */ - public double getY() { + public float getY() { return y; } @@ -67,8 +69,8 @@ public final class XYPoint extends XYGeometry { @Override public int hashCode() { - int result = Double.hashCode(x); - result = 31 * result + Double.hashCode(y); + int result = Float.hashCode(x); + result = 31 * result + Float.hashCode(y); return result; } diff --git a/lucene/core/src/java/org/apache/lucene/geo/XYPolygon.java b/lucene/core/src/java/org/apache/lucene/geo/XYPolygon.java index 00ec377fbae..9ed55926fd4 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/XYPolygon.java +++ b/lucene/core/src/java/org/apache/lucene/geo/XYPolygon.java @@ -18,23 +18,25 @@ package org.apache.lucene.geo; import java.util.Arrays; +import static org.apache.lucene.geo.XYEncodingUtils.checkVal; + /** * Represents a polygon in cartesian space. You can construct the Polygon directly with {@code float[]}, {@code float[]} x, y arrays * coordinates. */ public final class XYPolygon extends XYGeometry { - private final double[] x; - private final double[] y; + private final float[] x; + private final float[] y; private final XYPolygon[] holes; /** minimum x of this polygon's bounding box area */ - public final double minX; + public final float minX; /** maximum x of this polygon's bounding box area */ - public final double maxX; + public final float maxX; /** minimum y of this polygon's bounding box area */ - public final double minY; + public final float minY; /** maximum y of this polygon's bounding box area */ - public final double maxY; + public final float maxY; /** winding order of the vertices */ private final GeoUtils.WindingOrder windingOrder; @@ -69,26 +71,22 @@ public final class XYPolygon extends XYGeometry { throw new IllegalArgumentException("holes may not contain holes: polygons may not nest."); } } - this.x = new double[x.length]; - this.y = new double[y.length]; - for (int i = 0; i < x.length; ++i) { - this.x[i] = (double)x[i]; - this.y[i] = (double)y[i]; - } + this.x = x.clone(); + this.y = y.clone(); this.holes = holes.clone(); // compute bounding box - double minX = x[0]; - double maxX = x[0]; - double minY = y[0]; - double maxY = y[0]; + float minX = checkVal(x[0]); + float maxX = x[0]; + float minY = checkVal(y[0]); + float maxY = y[0]; double windingSum = 0d; final int numPts = x.length - 1; for (int i = 1, j = 0; i < numPts; j = i++) { - minX = Math.min(x[i], minX); + minX = Math.min(checkVal(x[i]), minX); maxX = Math.max(x[i], maxX); - minY = Math.min(y[i], minY); + minY = Math.min(checkVal(y[i]), minY); maxY = Math.max(y[i], maxY); // compute signed area windingSum += (x[j] - x[numPts])*(y[i] - y[numPts]) @@ -107,22 +105,22 @@ public final class XYPolygon extends XYGeometry { } /** Returns a copy of the internal x array */ - public double[] getPolyX() { + public float[] getPolyX() { return x.clone(); } /** Returns x value at given index */ - public double getPolyX(int vertex) { + public float getPolyX(int vertex) { return x[vertex]; } /** Returns a copy of the internal y array */ - public double[] getPolyY() { + public float[] getPolyY() { return y.clone(); } /** Returns y value at given index */ - public double getPolyY(int vertex) { + public float getPolyY(int vertex) { return y[vertex]; } @@ -150,18 +148,6 @@ public final class XYPolygon extends XYGeometry { return Polygon2D.create(this); } - public String toGeoJSON() { - StringBuilder sb = new StringBuilder(); - sb.append("["); - sb.append(Polygon.verticesToGeoJSON(y, x)); - for (XYPolygon hole : holes) { - sb.append(","); - sb.append(Polygon.verticesToGeoJSON(hole.y, hole.x)); - } - sb.append("]"); - return sb.toString(); - } - @Override public int hashCode() { final int prime = 31; diff --git a/lucene/core/src/java/org/apache/lucene/geo/XYRectangle.java b/lucene/core/src/java/org/apache/lucene/geo/XYRectangle.java index 23175b181fb..8012d791b04 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/XYRectangle.java +++ b/lucene/core/src/java/org/apache/lucene/geo/XYRectangle.java @@ -16,25 +16,31 @@ */ package org.apache.lucene.geo; +import static org.apache.lucene.geo.XYEncodingUtils.checkVal; + /** Represents a x/y cartesian rectangle. */ public final class XYRectangle extends XYGeometry { /** minimum x value */ - public final double minX; + public final float minX; /** minimum y value */ - public final double maxX; + public final float maxX; /** maximum x value */ - public final double minY; + public final float minY; /** maximum y value */ - public final double maxY; + public final float maxY; /** Constructs a bounding box by first validating the provided x and y coordinates */ - public XYRectangle(double minX, double maxX, double minY, double maxY) { - this.minX = minX; - this.maxX = maxX; - this.minY = minY; - this.maxY = maxY; - assert minX <= maxX; - assert minY <= maxY; + public XYRectangle(float minX, float maxX, float minY, float maxY) { + if (minX > maxX) { + throw new IllegalArgumentException("minX must be lower than maxX, got " + minX + " > " + maxX); + } + if (minY > maxY) { + throw new IllegalArgumentException("minY must be lower than maxY, got " + minY + " > " + maxY); + } + this.minX = checkVal(minX); + this.maxX = checkVal(maxX); + this.minY = checkVal(minY); + this.maxY = checkVal(maxY); } @Override @@ -49,10 +55,10 @@ public final class XYRectangle extends XYGeometry { XYRectangle rectangle = (XYRectangle) o; - if (Double.compare(rectangle.minX, minX) != 0) return false; - if (Double.compare(rectangle.minY, minY) != 0) return false; - if (Double.compare(rectangle.maxX, maxX) != 0) return false; - return Double.compare(rectangle.maxY, maxY) == 0; + if (Float.compare(rectangle.minX, minX) != 0) return false; + if (Float.compare(rectangle.minY, minY) != 0) return false; + if (Float.compare(rectangle.maxX, maxX) != 0) return false; + return Float.compare(rectangle.maxY, maxY) == 0; } @@ -60,13 +66,13 @@ public final class XYRectangle extends XYGeometry { public int hashCode() { int result; long temp; - temp = Double.doubleToLongBits(minX); + temp = Float.floatToIntBits(minX); result = (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(minY); + temp = Float.floatToIntBits(minY); result = 31 * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(maxX); + temp = Float.floatToIntBits(maxX); result = 31 * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(maxY); + temp = Float.floatToIntBits(maxY); result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } diff --git a/lucene/core/src/java/org/apache/lucene/geo/XYRectangle2D.java b/lucene/core/src/java/org/apache/lucene/geo/XYRectangle2D.java index b15d797827e..3267d44a400 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/XYRectangle2D.java +++ b/lucene/core/src/java/org/apache/lucene/geo/XYRectangle2D.java @@ -235,9 +235,6 @@ final class XYRectangle2D implements Component2D { /** create a component2D from the provided XY rectangle */ static Component2D create(XYRectangle rectangle) { - return new XYRectangle2D(XYEncodingUtils.decode(XYEncodingUtils.encode(rectangle.minX)), - XYEncodingUtils.decode(XYEncodingUtils.encode(rectangle.maxX)), - XYEncodingUtils.decode(XYEncodingUtils.encode(rectangle.minY)), - XYEncodingUtils.decode(XYEncodingUtils.encode(rectangle.maxY))); + return new XYRectangle2D(rectangle.minX, rectangle.maxX, rectangle.minY, rectangle.maxY); } } \ No newline at end of file diff --git a/lucene/core/src/test/org/apache/lucene/document/BaseXYShapeTestCase.java b/lucene/core/src/test/org/apache/lucene/document/BaseXYShapeTestCase.java index 40bca2b07ae..ef34cfe0765 100644 --- a/lucene/core/src/test/org/apache/lucene/document/BaseXYShapeTestCase.java +++ b/lucene/core/src/test/org/apache/lucene/document/BaseXYShapeTestCase.java @@ -126,8 +126,8 @@ public abstract class BaseXYShapeTestCase extends BaseShapeTestCase { float[] x = new float[poly.numPoints() - 1]; float[] y = new float[x.length]; for (int i = 0; i < x.length; ++i) { - x[i] = (float) poly.getPolyX(i); - y[i] = (float) poly.getPolyY(i); + x[i] = poly.getPolyX(i); + y[i] = poly.getPolyY(i); } return new XYLine(x, y); @@ -144,8 +144,8 @@ public abstract class BaseXYShapeTestCase extends BaseShapeTestCase { int numPoints = TestUtil.nextInt(random, 1, 20); float[][] points = new float[numPoints][2]; for (int i = 0; i < numPoints; i++) { - points[i][0] = (float) ShapeTestUtil.nextDouble(random); - points[i][1] = (float) ShapeTestUtil.nextDouble(random); + points[i][0] = ShapeTestUtil.nextFloat(random); + points[i][1] = ShapeTestUtil.nextFloat(random); } return points; } @@ -164,22 +164,22 @@ public abstract class BaseXYShapeTestCase extends BaseShapeTestCase { } @Override double quantizeX(double raw) { - return decode(encode(raw)); + return decode(encode((float) raw)); } @Override double quantizeXCeil(double raw) { - return decode(encode(raw)); + return decode(encode((float) raw)); } @Override double quantizeY(double raw) { - return decode(encode(raw)); + return decode(encode((float) raw)); } @Override double quantizeYCeil(double raw) { - return decode(encode(raw)); + return decode(encode((float) raw)); } @Override @@ -191,7 +191,7 @@ public abstract class BaseXYShapeTestCase extends BaseShapeTestCase { @Override ShapeField.DecodedTriangle encodeDecodeTriangle(double ax, double ay, boolean ab, double bx, double by, boolean bc, double cx, double cy, boolean ca) { byte[] encoded = new byte[7 * ShapeField.BYTES]; - ShapeField.encodeTriangle(encoded, encode(ay), encode(ax), ab, encode(by), encode(bx), bc, encode(cy), encode(cx), ca); + ShapeField.encodeTriangle(encoded, encode((float) ay), encode((float) ax), ab, encode((float) by), encode((float) bx), bc, encode((float) cy), encode((float) cx), ca); ShapeField.DecodedTriangle triangle = new ShapeField.DecodedTriangle(); ShapeField.decodeTriangle(encoded, triangle); return triangle; diff --git a/lucene/core/src/test/org/apache/lucene/document/TestXYLineShapeQueries.java b/lucene/core/src/test/org/apache/lucene/document/TestXYLineShapeQueries.java index 6c884a42b26..5f3c7a293df 100644 --- a/lucene/core/src/test/org/apache/lucene/document/TestXYLineShapeQueries.java +++ b/lucene/core/src/test/org/apache/lucene/document/TestXYLineShapeQueries.java @@ -51,11 +51,11 @@ public class TestXYLineShapeQueries extends BaseXYShapeTestCase { XYLine l = (XYLine) (shapes[i]); if (random.nextBoolean() && l != null) { int v = random.nextInt(l.numPoints() - 1); - x[j] = (float)l.getX(v); - y[j] = (float)l.getY(v); + x[j] = l.getX(v); + y[j] = l.getY(v); } else { - x[j] = (float)ShapeTestUtil.nextDouble(random); - y[j] = (float)ShapeTestUtil.nextDouble(random); + x[j] = ShapeTestUtil.nextFloat(random); + y[j] = ShapeTestUtil.nextFloat(random); } } return new XYLine(x, y); @@ -80,7 +80,7 @@ public class TestXYLineShapeQueries extends BaseXYShapeTestCase { @Override public boolean testBBoxQuery(double minY, double maxY, double minX, double maxX, Object shape) { - Component2D rectangle2D = XYGeometry.create(new XYRectangle(minX, maxX, minY, maxY)); + Component2D rectangle2D = XYGeometry.create(new XYRectangle((float) minX, (float) maxX, (float) minY, (float) maxY)); return testComponentQuery(rectangle2D, shape); } diff --git a/lucene/core/src/test/org/apache/lucene/document/TestXYMultiLineShapeQueries.java b/lucene/core/src/test/org/apache/lucene/document/TestXYMultiLineShapeQueries.java index 399eea9e4aa..d530e664998 100644 --- a/lucene/core/src/test/org/apache/lucene/document/TestXYMultiLineShapeQueries.java +++ b/lucene/core/src/test/org/apache/lucene/document/TestXYMultiLineShapeQueries.java @@ -76,7 +76,7 @@ public class TestXYMultiLineShapeQueries extends BaseXYShapeTestCase { @Override public boolean testBBoxQuery(double minY, double maxY, double minX, double maxX, Object shape) { - Component2D rectangle2D = XYGeometry.create(new XYRectangle(minX, maxX, minY, maxY)); + Component2D rectangle2D = XYGeometry.create(new XYRectangle((float) minX, (float) maxX, (float) minY, (float) maxY)); return testComponentQuery(rectangle2D, shape); } diff --git a/lucene/core/src/test/org/apache/lucene/document/TestXYMultiPointShapeQueries.java b/lucene/core/src/test/org/apache/lucene/document/TestXYMultiPointShapeQueries.java index 8b6db6554c1..9c573dae063 100644 --- a/lucene/core/src/test/org/apache/lucene/document/TestXYMultiPointShapeQueries.java +++ b/lucene/core/src/test/org/apache/lucene/document/TestXYMultiPointShapeQueries.java @@ -75,7 +75,7 @@ public class TestXYMultiPointShapeQueries extends BaseXYShapeTestCase { @Override public boolean testBBoxQuery(double minY, double maxY, double minX, double maxX, Object shape) { - Component2D rectangle2D = XYGeometry.create(new XYRectangle(minX, maxX, minY, maxY)); + Component2D rectangle2D = XYGeometry.create(new XYRectangle((float) minX, (float) maxX, (float) minY, (float) maxY)); return testComponentQuery(rectangle2D, shape); } diff --git a/lucene/core/src/test/org/apache/lucene/document/TestXYMultiPolygonShapeQueries.java b/lucene/core/src/test/org/apache/lucene/document/TestXYMultiPolygonShapeQueries.java index d45c0292ab2..6803af95fd5 100644 --- a/lucene/core/src/test/org/apache/lucene/document/TestXYMultiPolygonShapeQueries.java +++ b/lucene/core/src/test/org/apache/lucene/document/TestXYMultiPolygonShapeQueries.java @@ -115,7 +115,7 @@ public class TestXYMultiPolygonShapeQueries extends BaseXYShapeTestCase { @Override public boolean testBBoxQuery(double minY, double maxY, double minX, double maxX, Object shape) { - Component2D rectangle2D = XYGeometry.create(new XYRectangle(minX, maxX, minY, maxY)); + Component2D rectangle2D = XYGeometry.create(new XYRectangle((float) minX, (float) maxX, (float) minY, (float) maxY)); return testComponentQuery(rectangle2D, shape); } diff --git a/lucene/core/src/test/org/apache/lucene/document/TestXYPointShapeQueries.java b/lucene/core/src/test/org/apache/lucene/document/TestXYPointShapeQueries.java index 20850953391..3b2f0dfdf95 100644 --- a/lucene/core/src/test/org/apache/lucene/document/TestXYPointShapeQueries.java +++ b/lucene/core/src/test/org/apache/lucene/document/TestXYPointShapeQueries.java @@ -53,8 +53,8 @@ public class TestXYPointShapeQueries extends BaseXYShapeTestCase { x[j] = p.x; y[j] = p.y; } else { - x[j] = (float)ShapeTestUtil.nextDouble(random); - y[j] = (float)ShapeTestUtil.nextDouble(random); + x[j] = ShapeTestUtil.nextFloat(random); + y[j] = ShapeTestUtil.nextFloat(random); } } return new XYLine(x, y); @@ -80,7 +80,7 @@ public class TestXYPointShapeQueries extends BaseXYShapeTestCase { @Override public boolean testBBoxQuery(double minY, double maxY, double minX, double maxX, Object shape) { - Component2D rectangle2D = XYGeometry.create(new XYRectangle(minX, maxX, minY, maxY)); + Component2D rectangle2D = XYGeometry.create(new XYRectangle((float) minX, (float) maxX, (float) minY, (float) maxY)); return testComponentQuery(rectangle2D, shape); } diff --git a/lucene/core/src/test/org/apache/lucene/document/TestXYPolygonShapeQueries.java b/lucene/core/src/test/org/apache/lucene/document/TestXYPolygonShapeQueries.java index 80b34367484..5da69dd40d6 100644 --- a/lucene/core/src/test/org/apache/lucene/document/TestXYPolygonShapeQueries.java +++ b/lucene/core/src/test/org/apache/lucene/document/TestXYPolygonShapeQueries.java @@ -66,7 +66,7 @@ public class TestXYPolygonShapeQueries extends BaseXYShapeTestCase { @Override public boolean testBBoxQuery(double minY, double maxY, double minX, double maxX, Object shape) { - Component2D rectangle2D = XYGeometry.create(new XYRectangle(minX, maxX, minY, maxY)); + Component2D rectangle2D = XYGeometry.create(new XYRectangle((float) minX, (float) maxX, (float) minY, (float) maxY)); return testComponentQuery(rectangle2D, shape); } diff --git a/lucene/core/src/test/org/apache/lucene/document/TestXYShape.java b/lucene/core/src/test/org/apache/lucene/document/TestXYShape.java index 838ab4de466..fad04cbaf38 100644 --- a/lucene/core/src/test/org/apache/lucene/document/TestXYShape.java +++ b/lucene/core/src/test/org/apache/lucene/document/TestXYShape.java @@ -51,8 +51,8 @@ public class TestXYShape extends LuceneTestCase { } } - protected Query newRectQuery(String field, double minX, double maxX, double minY, double maxY) { - return XYShape.newBoxQuery(field, QueryRelation.INTERSECTS, (float)minX, (float)maxX, (float)minY, (float)maxY); + protected Query newRectQuery(String field, float minX, float maxX, float minY, float maxY) { + return XYShape.newBoxQuery(field, QueryRelation.INTERSECTS, minX, maxX, minY, maxY); } /** test we can search for a point with a standard number of vertices*/ @@ -73,8 +73,8 @@ public class TestXYShape extends LuceneTestCase { float x[] = new float[p.numPoints() - 1]; float y[] = new float[p.numPoints() - 1]; for (int i = 0; i < x.length; ++i) { - x[i] = (float)p.getPolyX(i); - y[i] = (float)p.getPolyY(i); + x[i] = p.getPolyX(i); + y[i] = p.getPolyY(i); } XYLine l = new XYLine(x, y); addLineToDoc(FIELDNAME, document, l); @@ -85,28 +85,28 @@ public class TestXYShape extends LuceneTestCase { IndexReader reader = writer.getReader(); writer.close(); IndexSearcher searcher = newSearcher(reader); - double minX = Math.min(x[0], x[1]); - double minY = Math.min(y[0], y[1]); - double maxX = Math.max(x[0], x[1]); - double maxY = Math.max(y[0], y[1]); + float minX = Math.min(x[0], x[1]); + float minY = Math.min(y[0], y[1]); + float maxX = Math.max(x[0], x[1]); + float maxY = Math.max(y[0], y[1]); Query q = newRectQuery(FIELDNAME, minX, maxX, minY, maxY); assertEquals(2, searcher.count(q)); // search a disjoint bbox - q = newRectQuery(FIELDNAME, p.minX-1d, p.minX+1, p.minY-1d, p.minY+1d); + q = newRectQuery(FIELDNAME, p.minX-1f, p.minX + 1f, p.minY - 1f, p.minY + 1f); assertEquals(0, searcher.count(q)); // search w/ an intersecting polygon q = XYShape.newPolygonQuery(FIELDNAME, QueryRelation.INTERSECTS, new XYPolygon( - new float[] {(float)minX, (float)minX, (float)maxX, (float)maxX, (float)minX}, - new float[] {(float)minY, (float)maxY, (float)maxY, (float)minY, (float)minY} + new float[] {minX, minX, maxX, maxX, minX}, + new float[] {minY, maxY, maxY, minY, minY} )); assertEquals(2, searcher.count(q)); // search w/ an intersecting line q = XYShape.newLineQuery(FIELDNAME, QueryRelation.INTERSECTS, new XYLine( - new float[] {(float)minX, (float)minX, (float)maxX, (float)maxX}, - new float[] {(float)minY, (float)maxY, (float)maxY, (float)minY} + new float[] {minX, minX, maxX, maxX}, + new float[] {minY, maxY, maxY, minY} )); assertEquals(2, searcher.count(q)); @@ -153,18 +153,18 @@ public class TestXYShape extends LuceneTestCase { q = newRectQuery(FIELDNAME, r1.minX, r1.maxX, r1.minY, r1.maxY); assertEquals(1, searcher.count(q)); // r1 contains r2, WITHIN should match - q = XYShape.newBoxQuery(FIELDNAME, QueryRelation.WITHIN, (float) r1.minX, (float) r1.maxX, (float) r1.minY, (float) r1.maxY); + q = XYShape.newBoxQuery(FIELDNAME, QueryRelation.WITHIN, r1.minX, r1.maxX, r1.minY, r1.maxY); assertEquals(1, searcher.count(q)); IOUtils.close(reader, dir); } private static boolean areBoxDisjoint(XYRectangle r1, XYRectangle r2) { - return ((float) r1.minX <= (float) r2.minX && (float) r1.minY <= (float) r2.minY && (float) r1.maxX >= (float) r2.maxX && (float) r1.maxY >= (float) r2.maxY); + return ( r1.minX <= r2.minX && r1.minY <= r2.minY && r1.maxX >= r2.maxX && r1.maxY >= r2.maxY); } private static XYPolygon toPolygon(XYRectangle r) { - return new XYPolygon(new float[]{(float) r.minX, (float) r.maxX, (float) r.maxX, (float) r.minX, (float) r.minX}, - new float[]{(float) r.minY, (float) r.minY, (float) r.maxY, (float) r.maxY, (float) r.minY}); + return new XYPolygon(new float[]{ r.minX, r.maxX, r.maxX, r.minX, r.minX}, + new float[]{ r.minY, r.minY, r.maxY, r.maxY, r.minY}); } } diff --git a/lucene/core/src/test/org/apache/lucene/document/TestXYShapeEncoding.java b/lucene/core/src/test/org/apache/lucene/document/TestXYShapeEncoding.java index 5d3e201cbbd..030ea6b60bd 100644 --- a/lucene/core/src/test/org/apache/lucene/document/TestXYShapeEncoding.java +++ b/lucene/core/src/test/org/apache/lucene/document/TestXYShapeEncoding.java @@ -26,12 +26,12 @@ import org.apache.lucene.geo.XYPolygon; public class TestXYShapeEncoding extends BaseShapeEncodingTestCase { @Override protected int encodeX(double x) { - return XYEncodingUtils.encode(x); + return XYEncodingUtils.encode((float) x); } @Override protected int encodeY(double y) { - return XYEncodingUtils.encode(y); + return XYEncodingUtils.encode((float) y); } @Override @@ -46,12 +46,12 @@ public class TestXYShapeEncoding extends BaseShapeEncodingTestCase { @Override protected double nextX() { - return ShapeTestUtil.nextDouble(random()); + return ShapeTestUtil.nextFloat(random()); } @Override protected double nextY() { - return ShapeTestUtil.nextDouble(random()); + return ShapeTestUtil.nextFloat(random()); } @Override diff --git a/lucene/core/src/test/org/apache/lucene/geo/ShapeTestUtil.java b/lucene/core/src/test/org/apache/lucene/geo/ShapeTestUtil.java index b57d8423738..a8d08f4d11a 100644 --- a/lucene/core/src/test/org/apache/lucene/geo/ShapeTestUtil.java +++ b/lucene/core/src/test/org/apache/lucene/geo/ShapeTestUtil.java @@ -40,7 +40,7 @@ public class ShapeTestUtil { // So the poly can cover at most 50% of the earth's surface: double radius = random.nextDouble() * 0.5 * Float.MAX_VALUE + 1.0; try { - return createRegularPolygon(nextDouble(random), nextDouble(random), radius, gons); + return createRegularPolygon(nextFloat(random), nextFloat(random), radius, gons); } catch (IllegalArgumentException iae) { // we tried to cross dateline or pole ... try again } @@ -57,42 +57,53 @@ public class ShapeTestUtil { } } + public static XYLine nextLine() { + XYPolygon poly = ShapeTestUtil.nextPolygon(); + float[] x = new float[poly.numPoints() - 1]; + float[] y = new float[x.length]; + for (int i = 0; i < x.length; ++i) { + x[i] = poly.getPolyX(i); + y[i] = poly.getPolyY(i); + } + return new XYLine(x, y); + } + private static XYPolygon trianglePolygon(XYRectangle box) { final float[] polyX = new float[4]; final float[] polyY = new float[4]; - polyX[0] = (float)box.minX; - polyY[0] = (float)box.minY; - polyX[1] = (float)box.minX; - polyY[1] = (float)box.minY; - polyX[2] = (float)box.minX; - polyY[2] = (float)box.minY; - polyX[3] = (float)box.minX; - polyY[3] = (float)box.minY; + polyX[0] = box.minX; + polyY[0] = box.minY; + polyX[1] = box.minX; + polyY[1] = box.minY; + polyX[2] = box.minX; + polyY[2] = box.minY; + polyX[3] = box.minX; + polyY[3] = box.minY; return new XYPolygon(polyX, polyY); } public static XYRectangle nextBox(Random random) { // prevent lines instead of boxes - double x0 = nextDouble(random); - double x1 = nextDouble(random); + float x0 = nextFloat(random); + float x1 = nextFloat(random); while (x0 == x1) { - x1 = nextDouble(random); + x1 = nextFloat(random); } // prevent lines instead of boxes - double y0 = nextDouble(random); - double y1 = nextDouble(random); + float y0 = nextFloat(random); + float y1 = nextFloat(random); while (y0 == y1) { - y1 = nextDouble(random); + y1 = nextFloat(random); } if (x1 < x0) { - double x = x0; + float x = x0; x0 = x1; x1 = x; } if (y1 < y0) { - double y = y0; + float y = y0; y0 = y1; y1 = y; } @@ -103,16 +114,16 @@ public class ShapeTestUtil { private static XYPolygon boxPolygon(XYRectangle box) { final float[] polyX = new float[5]; final float[] polyY = new float[5]; - polyX[0] = (float)box.minX; - polyY[0] = (float)box.minY; - polyX[1] = (float)box.minX; - polyY[1] = (float)box.minY; - polyX[2] = (float)box.minX; - polyY[2] = (float)box.minY; - polyX[3] = (float)box.minX; - polyY[3] = (float)box.minY; - polyX[4] = (float)box.minX; - polyY[4] = (float)box.minY; + polyX[0] = box.minX; + polyY[0] = box.minY; + polyX[1] = box.minX; + polyY[1] = box.minY; + polyX[2] = box.minX; + polyY[2] = box.minY; + polyX[3] = box.minX; + polyY[3] = box.minY; + polyX[4] = box.minX; + polyY[4] = box.minY; return new XYPolygon(polyX, polyY); } @@ -120,8 +131,8 @@ public class ShapeTestUtil { // repeat until we get a poly that doesn't cross dateline: while (true) { //System.out.println("\nPOLY ITER"); - double centerX = nextDouble(random); - double centerY = nextDouble(random); + float centerX = nextFloat(random); + float centerY = nextFloat(random); double radius = 0.1 + 20 * random.nextDouble(); double radiusDelta = random.nextDouble(); @@ -135,8 +146,8 @@ public class ShapeTestUtil { break; } double len = radius * (1.0 - radiusDelta + radiusDelta * random.nextDouble()); - double maxX = StrictMath.min(StrictMath.abs(Float.MAX_VALUE - centerX), StrictMath.abs(-Float.MAX_VALUE - centerX)); - double maxY = StrictMath.min(StrictMath.abs(Float.MAX_VALUE - centerY), StrictMath.abs(-Float.MAX_VALUE - centerY)); + float maxX = StrictMath.min(StrictMath.abs(Float.MAX_VALUE - centerX), StrictMath.abs(-Float.MAX_VALUE - centerX)); + float maxY = StrictMath.min(StrictMath.abs(Float.MAX_VALUE - centerY), StrictMath.abs(-Float.MAX_VALUE - centerY)); len = StrictMath.min(len, StrictMath.min(maxX, maxY)); @@ -195,8 +206,8 @@ public class ShapeTestUtil { return new XYPolygon(result[0], result[1]); } - public static double nextDouble(Random random) { - return BiasedNumbers.randomDoubleBetween(random, -Float.MAX_VALUE, Float.MAX_VALUE); + public static float nextFloat(Random random) { + return BiasedNumbers.randomFloatBetween(random, -Float.MAX_VALUE, Float.MAX_VALUE); } /** Keep it simple, we don't need to take arbitrary Random for geo tests */ diff --git a/lucene/core/src/test/org/apache/lucene/geo/TestXYLine.java b/lucene/core/src/test/org/apache/lucene/geo/TestXYLine.java new file mode 100644 index 00000000000..64d7e148251 --- /dev/null +++ b/lucene/core/src/test/org/apache/lucene/geo/TestXYLine.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.lucene.geo; + + +import java.util.Arrays; + +import org.apache.lucene.util.LuceneTestCase; + +public class TestXYLine extends LuceneTestCase { + + /** null x not allowed */ + public void testLineNullXs() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYLine(null, new float[] { -66, -65, -65, -66, -66 }); + }); + assertTrue(expected.getMessage().contains("x must not be null")); + } + + /** null y not allowed */ + public void testPolygonNullYs() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYLine(new float[] {18, 18, 19, 19, 18 }, null); + }); + assertTrue(expected.getMessage().contains("y must not be null")); + } + + /** needs at least 3 vertices */ + public void testLineEnoughPoints() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYLine(new float[] {18}, new float[] { -66}); + }); + assertTrue(expected.getMessage().contains("at least 2 line points required")); + } + + /** lines needs same number of x as y */ + public void testLinesBogus() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYLine(new float[] { 18, 18, 19, 19 }, new float[] { -66, -65, -65, -66, -66 }); + }); + assertTrue(expected.getMessage().contains("must be equal length")); + } + + /** line values cannot be NaN */ + public void testLineNaN() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYLine(new float[] { 18, 18, 19, Float.NaN, 18 }, new float[] { -66, -65, -65, -66, -66 }); + }); + assertTrue(expected.getMessage(), expected.getMessage().contains("invalid value NaN")); + } + + /** line values cannot be finite */ + public void testLinePositiveInfinite() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYLine(new float[] { 18, 18, 19, 19, 18 }, new float[] { -66, Float.POSITIVE_INFINITY, -65, -66, -66 }); + }); + assertTrue(expected.getMessage(), expected.getMessage().contains("invalid value Inf")); + } + + /** line values cannot be finite */ + public void testLineNegativeInfinite() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYLine(new float[] { 18, 18, 19, 19, 18 }, new float[] { -66, -65, -65, Float.NEGATIVE_INFINITY, -66 }); + }); + assertTrue(expected.getMessage(), expected.getMessage().contains("invalid value -Inf")); + } + + /** equals and hashcode */ + public void testEqualsAndHashCode() { + XYLine line = ShapeTestUtil.nextLine(); + XYLine copy = new XYLine(line.getX(), line.getY()); + assertEquals(line, copy); + assertEquals(line.hashCode(), copy.hashCode()); + XYLine otherLine = ShapeTestUtil.nextLine(); + if (Arrays.equals(line.getX(), otherLine.getX()) == false || + Arrays.equals(line.getY(), otherLine.getY()) == false) { + assertNotEquals(line, otherLine); + assertNotEquals(line.hashCode(), otherLine.hashCode()); + } else { + assertEquals(line, otherLine); + assertEquals(line.hashCode(), otherLine.hashCode()); + } + + } +} \ No newline at end of file diff --git a/lucene/core/src/test/org/apache/lucene/geo/TestXYPoint.java b/lucene/core/src/test/org/apache/lucene/geo/TestXYPoint.java new file mode 100644 index 00000000000..7fb48a8cc60 --- /dev/null +++ b/lucene/core/src/test/org/apache/lucene/geo/TestXYPoint.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.lucene.geo; + +import org.apache.lucene.util.LuceneTestCase; + +public class TestXYPoint extends LuceneTestCase { + + /** point values cannot be NaN */ + public void testNaN() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPoint(Float.NaN, 45.23f); + }); + assertTrue(expected.getMessage().contains("invalid value NaN")); + + expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPoint(43.5f, Float.NaN); + }); + assertTrue(expected.getMessage().contains("invalid value NaN")); + } + + /** point values mist be finite */ + public void testPositiveInf() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPoint(Float.POSITIVE_INFINITY, 45.23f); + }); + assertTrue(expected.getMessage().contains("invalid value Inf")); + + expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPoint(43.5f, Float.POSITIVE_INFINITY); + }); + assertTrue(expected.getMessage().contains("invalid value Inf")); + } + + /** point values mist be finite */ + public void testNegativeInf() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPoint(Float.NEGATIVE_INFINITY, 45.23f); + }); + assertTrue(expected.getMessage().contains("invalid value -Inf")); + + expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPoint(43.5f, Float.NEGATIVE_INFINITY); + }); + assertTrue(expected.getMessage().contains("invalid value -Inf")); + } + + /** equals and hashcode */ + public void testEqualsAndHashCode() { + XYPoint point = new XYPoint(ShapeTestUtil.nextFloat(random()), ShapeTestUtil.nextFloat(random())); + XYPoint copy = new XYPoint(point.getX(), point.getY()); + assertEquals(point, copy); + assertEquals(point.hashCode(), copy.hashCode()); + XYPoint otherPoint = new XYPoint(ShapeTestUtil.nextFloat(random()), ShapeTestUtil.nextFloat(random())); + if (point.getX() != otherPoint.getX() || point.getY() != otherPoint.getY()) { + assertNotEquals(point, otherPoint); + assertNotEquals(point.hashCode(), otherPoint.hashCode()); + } else { + assertEquals(point, otherPoint); + assertEquals(point.hashCode(), otherPoint.hashCode()); + } + } +} \ No newline at end of file diff --git a/lucene/core/src/test/org/apache/lucene/geo/TestXYPolygon.java b/lucene/core/src/test/org/apache/lucene/geo/TestXYPolygon.java new file mode 100644 index 00000000000..480580029aa --- /dev/null +++ b/lucene/core/src/test/org/apache/lucene/geo/TestXYPolygon.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.lucene.geo; + + +import java.util.Arrays; + +import org.apache.lucene.util.LuceneTestCase; + +public class TestXYPolygon extends LuceneTestCase { + + /** null x not allowed */ + public void testPolygonNullPolyLats() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPolygon(null, new float[] { -66, -65, -65, -66, -66 }); + }); + assertTrue(expected.getMessage().contains("x must not be null")); + } + + /** null y not allowed */ + public void testPolygonNullPolyLons() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPolygon(new float[] {18, 18, 19, 19, 18 }, null); + }); + assertTrue(expected.getMessage().contains("y must not be null")); + } + + /** polygon needs at least 3 vertices */ + public void testPolygonLine() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPolygon(new float[] { 18, 18, 18 }, new float[] { -66, -65, -66 }); + }); + assertTrue(expected.getMessage().contains("at least 4 polygon points required")); + } + + /** polygon needs same number of latitudes as longitudes */ + public void testPolygonBogus() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPolygon(new float[] { 18, 18, 19, 19 }, new float[] { -66, -65, -65, -66, -66 }); + }); + assertTrue(expected.getMessage().contains("must be equal length")); + } + + /** polygon must be closed */ + public void testPolygonNotClosed() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPolygon(new float[] { 18, 18, 19, 19, 19 }, new float[] { -66, -65, -65, -66, -67 }); + }); + assertTrue(expected.getMessage(), expected.getMessage().contains("it must close itself")); + } + + /** polygon values cannot be NaN */ + public void testPolygonNaN() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPolygon(new float[] { 18, 18, 19, Float.NaN, 18 }, new float[] { -66, -65, -65, -66, -66 }); + }); + assertTrue(expected.getMessage(), expected.getMessage().contains("invalid value NaN")); + } + + /** polygon values cannot be finite */ + public void testPolygonPositiveInfinite() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPolygon(new float[] { 18, 18, 19, 19, 18 }, new float[] { -66, Float.POSITIVE_INFINITY, -65, -66, -66 }); + }); + assertTrue(expected.getMessage(), expected.getMessage().contains("invalid value Inf")); + } + + /** polygon values cannot be finite */ + public void testPolygonNegativeInfinite() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYPolygon(new float[] { 18, 18, 19, 19, 18 }, new float[] { -66, -65, -65, Float.NEGATIVE_INFINITY, -66 }); + }); + assertTrue(expected.getMessage(), expected.getMessage().contains("invalid value -Inf")); + } + + /** equals and hashcode */ + public void testEqualsAndHashCode() { + XYPolygon polygon = ShapeTestUtil.nextPolygon(); + XYPolygon copy = new XYPolygon(polygon.getPolyX(), polygon.getPolyY(), polygon.getHoles()); + assertEquals(polygon, copy); + assertEquals(polygon.hashCode(), copy.hashCode()); + XYPolygon otherPolygon = ShapeTestUtil.nextPolygon(); + if (Arrays.equals(polygon.getPolyX(), otherPolygon.getPolyX()) == false || + Arrays.equals(polygon.getPolyY(), otherPolygon.getPolyY()) == false || + Arrays.equals(polygon.getHoles(), otherPolygon.getHoles()) == false) { + assertNotEquals(polygon, otherPolygon); + assertNotEquals(polygon.hashCode(), otherPolygon.hashCode()); + } else { + assertEquals(polygon, otherPolygon); + assertEquals(polygon.hashCode(), otherPolygon.hashCode()); + } + } +} \ No newline at end of file diff --git a/lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle.java b/lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle.java new file mode 100644 index 00000000000..e3a46890f3b --- /dev/null +++ b/lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.lucene.geo; + + +import org.apache.lucene.util.LuceneTestCase; + +public class TestXYRectangle extends LuceneTestCase { + + /** maxX must be gte minX */ + public void tesInvalidMinMaxX() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYRectangle(5, 4, 3 ,4); + }); + assertTrue(expected.getMessage().contains("5 > 4")); + } + + /** maxY must be gte minY */ + public void tesInvalidMinMaxY() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYRectangle(4, 5, 5 ,4); + }); + assertTrue(expected.getMessage().contains("5 > 4")); + } + + /** rectangle values cannot be NaN */ + public void testNaN() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYRectangle(Float.NaN, 4, 3 ,4); + }); + assertTrue(expected.getMessage().contains("invalid value NaN")); + + expected = expectThrows(IllegalArgumentException.class, () -> { + new XYRectangle(3, Float.NaN, 3 ,4); + }); + assertTrue(expected.getMessage().contains("invalid value NaN")); + + expected = expectThrows(IllegalArgumentException.class, () -> { + new XYRectangle(3, 4, Float.NaN ,4); + }); + assertTrue(expected.getMessage().contains("invalid value NaN")); + + expected = expectThrows(IllegalArgumentException.class, () -> { + new XYRectangle(3, 4, 3 , Float.NaN); + }); + assertTrue(expected.getMessage().contains("invalid value NaN")); + } + + /** rectangle values must be finite */ + public void testPositiveInf() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYRectangle(3, Float.POSITIVE_INFINITY, 3 ,4); + }); + assertTrue(expected.getMessage().contains("invalid value Inf")); + + expected = expectThrows(IllegalArgumentException.class, () -> { + new XYRectangle(3, 4, 3 , Float.POSITIVE_INFINITY); + }); + assertTrue(expected.getMessage().contains("invalid value Inf")); + } + + /** rectangle values must be finite */ + public void testNegativeInf() { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + new XYRectangle(Float.NEGATIVE_INFINITY, 4, 3 ,4); + }); + assertTrue(expected.getMessage().contains("invalid value -Inf")); + + expected = expectThrows(IllegalArgumentException.class, () -> { + new XYRectangle(3, 4, Float.NEGATIVE_INFINITY ,4); + }); + assertTrue(expected.getMessage().contains("invalid value -Inf")); + } + + /** equals and hashcode */ + public void testEqualsAndHashCode() { + XYRectangle rectangle = ShapeTestUtil.nextBox(random()); + XYRectangle copy = new XYRectangle(rectangle.minX, rectangle.maxX, rectangle.minY, rectangle.maxY); + assertEquals(rectangle, copy); + assertEquals(rectangle.hashCode(), copy.hashCode()); + XYRectangle otherRectangle = ShapeTestUtil.nextBox(random()); + if (rectangle.minX != otherRectangle.minX || rectangle.maxX != otherRectangle.maxX || + rectangle.minY != otherRectangle.minY || rectangle.maxY != otherRectangle.maxY) { + assertNotEquals(rectangle, otherRectangle); + assertNotEquals(rectangle.hashCode(), otherRectangle.hashCode()); + } else { + assertEquals(rectangle, otherRectangle); + assertEquals(rectangle.hashCode(), otherRectangle.hashCode()); + } + } + +} \ No newline at end of file diff --git a/lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle2D.java b/lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle2D.java index 4ced0b83f6f..88b29daa508 100644 --- a/lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle2D.java +++ b/lucene/core/src/test/org/apache/lucene/geo/TestXYRectangle2D.java @@ -26,7 +26,7 @@ import org.apache.lucene.util.LuceneTestCase; public class TestXYRectangle2D extends LuceneTestCase { public void testTriangleDisjoint() { - XYRectangle rectangle = new XYRectangle(0d, 1d, 0d, 1d); + XYRectangle rectangle = new XYRectangle(0f, 1f, 0f, 1f); Component2D rectangle2D = XYRectangle2D.create(rectangle); float ax = 4f; float ay = 4f; @@ -38,7 +38,7 @@ public class TestXYRectangle2D extends LuceneTestCase { } public void testTriangleIntersects() { - XYRectangle rectangle = new XYRectangle(0d, 1d, 0d, 1d); + XYRectangle rectangle = new XYRectangle(0f, 1f, 0f, 1f); Component2D rectangle2D = XYRectangle2D.create(rectangle); float ax = 0.5f; float ay = 0.5f; @@ -66,12 +66,12 @@ public class TestXYRectangle2D extends LuceneTestCase { XYRectangle rectangle = ShapeTestUtil.nextBox(random); Component2D rectangle2D = XYRectangle2D.create(rectangle); for (int i =0; i < 100; i++) { - float ax = (float) ShapeTestUtil.nextDouble(random); - float ay = (float) ShapeTestUtil.nextDouble(random); - float bx = (float) ShapeTestUtil.nextDouble(random); - float by = (float) ShapeTestUtil.nextDouble(random); - float cx = (float) ShapeTestUtil.nextDouble(random); - float cy = (float) ShapeTestUtil.nextDouble(random); + float ax = ShapeTestUtil.nextFloat(random); + float ay = ShapeTestUtil.nextFloat(random); + float bx = ShapeTestUtil.nextFloat(random); + float by = ShapeTestUtil.nextFloat(random); + float cx = ShapeTestUtil.nextFloat(random); + float cy = ShapeTestUtil.nextFloat(random); float tMinX = StrictMath.min(StrictMath.min(ax, bx), cx); float tMaxX = StrictMath.max(StrictMath.max(ax, bx), cx);