mirror of
https://github.com/apache/lucene.git
synced 2025-02-07 10:38:40 +00:00
LUCENE-9243: Add fudge factor when creating a bounding box of a xycircle (#1278)
This commit is contained in:
parent
4897a64713
commit
c653c04bb1
@ -214,6 +214,8 @@ Bug Fixes
|
|||||||
|
|
||||||
* LUCENE-9250: Add support for Circle2d#intersectsLine around the dateline. (Ignacio Vera)
|
* LUCENE-9250: Add support for Circle2d#intersectsLine around the dateline. (Ignacio Vera)
|
||||||
|
|
||||||
|
* LUCENE-9243: Add fudge factor when creating a bounding box of a XYCircle. (Ignacio Vera)
|
||||||
|
|
||||||
Other
|
Other
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ package org.apache.lucene.document;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.lucene.geo.XYEncodingUtils;
|
import org.apache.lucene.geo.XYEncodingUtils;
|
||||||
|
import org.apache.lucene.geo.XYRectangle;
|
||||||
import org.apache.lucene.index.DocValues;
|
import org.apache.lucene.index.DocValues;
|
||||||
import org.apache.lucene.index.FieldInfo;
|
import org.apache.lucene.index.FieldInfo;
|
||||||
import org.apache.lucene.index.LeafReader;
|
import org.apache.lucene.index.LeafReader;
|
||||||
@ -84,14 +85,15 @@ class XYPointDistanceComparator extends FieldComparator<Double> implements LeafF
|
|||||||
// make bounding box(es) to exclude non-competitive hits, but start
|
// make bounding box(es) to exclude non-competitive hits, but start
|
||||||
// sampling if we get called way too much: don't make gobs of bounding
|
// sampling if we get called way too much: don't make gobs of bounding
|
||||||
// boxes if comparator hits a worst case order (e.g. backwards distance order)
|
// boxes if comparator hits a worst case order (e.g. backwards distance order)
|
||||||
if (setBottomCounter < 1024 || (setBottomCounter & 0x3F) == 0x3F) {
|
if (bottom < Float.MAX_VALUE && (setBottomCounter < 1024 || (setBottomCounter & 0x3F) == 0x3F)) {
|
||||||
|
|
||||||
|
XYRectangle rectangle = XYRectangle.fromPointDistance((float) x, (float) y, (float) bottom);
|
||||||
// pre-encode our box to our integer encoding, so we don't have to decode
|
// pre-encode our box to our integer encoding, so we don't have to decode
|
||||||
// to double values for uncompetitive hits. This has some cost!
|
// to double values for uncompetitive hits. This has some cost!
|
||||||
this.minX = XYEncodingUtils.encode((float) Math.max(-Float.MAX_VALUE, x - bottom));
|
this.minX = XYEncodingUtils.encode(rectangle.minX);
|
||||||
this.maxX = XYEncodingUtils.encode((float) Math.min(Float.MAX_VALUE, x + bottom));
|
this.maxX = XYEncodingUtils.encode(rectangle.maxX);
|
||||||
this.minY = XYEncodingUtils.encode((float) Math.max(-Float.MAX_VALUE, y - bottom));
|
this.minY = XYEncodingUtils.encode(rectangle.minY);
|
||||||
this.maxY = XYEncodingUtils.encode((float) Math.min(Float.MAX_VALUE, y + bottom));
|
this.maxY = XYEncodingUtils.encode(rectangle.maxY);
|
||||||
}
|
}
|
||||||
setBottomCounter++;
|
setBottomCounter++;
|
||||||
}
|
}
|
||||||
|
@ -272,19 +272,14 @@ class Circle2D implements Component2D {
|
|||||||
private final double centerX;
|
private final double centerX;
|
||||||
private final double centerY;
|
private final double centerY;
|
||||||
private final double radiusSquared;
|
private final double radiusSquared;
|
||||||
private final double minX;
|
private final XYRectangle rectangle;
|
||||||
private final double maxX;
|
|
||||||
private final double minY;
|
|
||||||
private final double maxY;
|
|
||||||
|
|
||||||
public CartesianDistance(double centerX, double centerY, double radius) {
|
public CartesianDistance(float centerX, float centerY, float radius) {
|
||||||
this.centerX = centerX;
|
this.centerX = centerX;
|
||||||
this.centerY = centerY;
|
this.centerY = centerY;
|
||||||
this.minX = Math.max(-Float.MAX_VALUE, centerX - radius);
|
this.rectangle = XYRectangle.fromPointDistance(centerX, centerY, radius);
|
||||||
this.maxX = Math.min(Float.MAX_VALUE, centerX + radius);
|
// product performed with doubles
|
||||||
this.minY = Math.max(-Float.MAX_VALUE, centerY - radius);
|
this.radiusSquared = (double) radius * radius;
|
||||||
this.maxY = Math.min(Float.MAX_VALUE, centerY + radius);
|
|
||||||
this.radiusSquared = radius * radius;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -333,32 +328,32 @@ class Circle2D implements Component2D {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean disjoint(double minX, double maxX, double minY, double maxY) {
|
public boolean disjoint(double minX, double maxX, double minY, double maxY) {
|
||||||
return Component2D.disjoint(this.minX, this.maxX, this.minY, this.maxY, minX, maxX, minY, maxY);
|
return Component2D.disjoint(rectangle.minX, rectangle.maxX, rectangle.minY, rectangle.maxY, minX, maxX, minY, maxY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean within(double minX, double maxX, double minY, double maxY) {
|
public boolean within(double minX, double maxX, double minY, double maxY) {
|
||||||
return Component2D.within(this.minX, this.maxX, this.minY, this.maxY, minX, maxX, minY, maxY);
|
return Component2D.within(rectangle.minX, rectangle.maxX, rectangle.minY, rectangle.maxY, minX, maxX, minY, maxY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getMinX() {
|
public double getMinX() {
|
||||||
return minX;
|
return rectangle.minX;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getMaxX() {
|
public double getMaxX() {
|
||||||
return maxX;
|
return rectangle.maxX;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getMinY() {
|
public double getMinY() {
|
||||||
return minY;
|
return rectangle.minY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getMaxY() {
|
public double getMaxY() {
|
||||||
return maxY;
|
return rectangle.maxY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -62,6 +62,26 @@ public final class XYRectangle extends XYGeometry {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Compute Bounding Box for a circle in cartesian geometry */
|
||||||
|
public static XYRectangle fromPointDistance(final float x, final float y, final float radius) {
|
||||||
|
checkVal(x);
|
||||||
|
checkVal(y);
|
||||||
|
if (radius < 0) {
|
||||||
|
throw new IllegalArgumentException("radius must be bigger than 0, got " + radius);
|
||||||
|
}
|
||||||
|
if (Float.isFinite(radius) == false) {
|
||||||
|
throw new IllegalArgumentException("radius must be finite, got " + radius);
|
||||||
|
}
|
||||||
|
// LUCENE-9243: We round up the bounding box to avoid
|
||||||
|
// numerical errors.
|
||||||
|
float distanceBox = Math.nextUp(radius);
|
||||||
|
float minX = Math.max(-Float.MAX_VALUE, x - distanceBox);
|
||||||
|
float maxX = Math.min(Float.MAX_VALUE, x + distanceBox);
|
||||||
|
float minY = Math.max(-Float.MAX_VALUE, y - distanceBox);
|
||||||
|
float maxY = Math.min(Float.MAX_VALUE, y + distanceBox);
|
||||||
|
return new XYRectangle(minX, maxX, minY, maxY);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result;
|
int result;
|
||||||
|
@ -18,6 +18,7 @@ package org.apache.lucene.geo;
|
|||||||
|
|
||||||
|
|
||||||
import org.apache.lucene.util.LuceneTestCase;
|
import org.apache.lucene.util.LuceneTestCase;
|
||||||
|
import org.apache.lucene.util.TestUtil;
|
||||||
|
|
||||||
public class TestXYRectangle extends LuceneTestCase {
|
public class TestXYRectangle extends LuceneTestCase {
|
||||||
|
|
||||||
@ -103,4 +104,56 @@ public class TestXYRectangle extends LuceneTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** make sure that if a point is inside a circle, it is inside of the bbox as well */
|
||||||
|
public void testRandomCircleToBBox() {
|
||||||
|
int iters = atLeast(100);
|
||||||
|
for(int iter= 0;iter < iters; iter++) {
|
||||||
|
|
||||||
|
float centerX = ShapeTestUtil.nextFloat(random());
|
||||||
|
float centerY = ShapeTestUtil.nextFloat(random());
|
||||||
|
|
||||||
|
final float radius;
|
||||||
|
if (random().nextBoolean()) {
|
||||||
|
radius = random().nextFloat() * TestUtil.nextInt(random(), 1, 100000);
|
||||||
|
} else {
|
||||||
|
radius = Math.abs(ShapeTestUtil.nextFloat(random()));
|
||||||
|
}
|
||||||
|
|
||||||
|
XYRectangle bbox = XYRectangle.fromPointDistance(centerX, centerY, radius);
|
||||||
|
Component2D component2D = bbox.toComponent2D();
|
||||||
|
|
||||||
|
int numPointsToTry = 1000;
|
||||||
|
for(int i = 0; i < numPointsToTry; i++) {
|
||||||
|
|
||||||
|
double x;
|
||||||
|
if (random().nextBoolean()) {
|
||||||
|
x = Math.min(Float.MAX_VALUE, centerX + radius + random().nextDouble());
|
||||||
|
} else {
|
||||||
|
x = Math.max(-Float.MAX_VALUE, centerX + radius - random().nextDouble());
|
||||||
|
}
|
||||||
|
double y;
|
||||||
|
if (random().nextBoolean()) {
|
||||||
|
y = Math.min(Float.MAX_VALUE, centerY + radius + random().nextDouble());
|
||||||
|
} else {
|
||||||
|
y = Math.max(-Float.MAX_VALUE, centerY + radius - random().nextDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
// cartesian says it's within the circle:
|
||||||
|
boolean cartesianSays = component2D.contains(x, y);
|
||||||
|
// BBox says its within the box:
|
||||||
|
boolean bboxSays = x >= bbox.minX && x <= bbox.maxX && y >= bbox.minY && y <= bbox.maxY;
|
||||||
|
|
||||||
|
if (cartesianSays) {
|
||||||
|
if (bboxSays == false) {
|
||||||
|
System.out.println(" centerX=" + centerX + " centerY=" + centerY + " radius=" + radius);
|
||||||
|
System.out.println(" bbox: x=" + bbox.minX + " to " + bbox.maxX + " y=" + bbox.minY + " to " + bbox.maxY);
|
||||||
|
System.out.println(" point: x=" + x + " y=" + y);
|
||||||
|
fail("point was within the distance according to cartesian distance, but the bbox doesn't contain it");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// it's fine if cartesian said it was outside the radius and bbox said it was inside the box
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user