LUCENE-9250: Add support for Circle2d#intersectsLine around the dateline. (#1289)

This commit is contained in:
Ignacio Vera 2020-02-28 10:22:27 +01:00 committed by GitHub
parent 3ad9915547
commit 988ce9bff7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 64 additions and 26 deletions

View File

@ -208,6 +208,8 @@ Bug Fixes
so an query for UNORDERED(foo, foo) would match a document containing 'foo' so an query for UNORDERED(foo, foo) would match a document containing 'foo'
only once. (Alan Woodward) only once. (Alan Woodward)
* LUCENE-9250: Add support for Circle2d#intersectsLine around the dateline. (Ignacio Vera)
Other Other
--------------------- ---------------------

View File

@ -112,7 +112,7 @@ class Circle2D implements Component2D {
// if any of the edges intersects an the edge belongs to the shape then it cannot be within. // if any of the edges intersects an the edge belongs to the shape then it cannot be within.
// if it only intersects edges that do not belong to the shape, then it is a candidate // if it only intersects edges that do not belong to the shape, then it is a candidate
// we skip edges at the dateline to support shapes crossing it // we skip edges at the dateline to support shapes crossing it
if (intersectsLine(ax, ay, bx, by)) { if (calculator.intersectsLine(ax, ay, bx, by)) {
if (ab == true) { if (ab == true) {
return WithinRelation.NOTWITHIN; return WithinRelation.NOTWITHIN;
} else { } else {
@ -120,14 +120,14 @@ class Circle2D implements Component2D {
} }
} }
if (intersectsLine(bx, by, cx, cy)) { if (calculator.intersectsLine(bx, by, cx, cy)) {
if (bc == true) { if (bc == true) {
return WithinRelation.NOTWITHIN; return WithinRelation.NOTWITHIN;
} else { } else {
relation = WithinRelation.CANDIDATE; relation = WithinRelation.CANDIDATE;
} }
} }
if (intersectsLine(cx, cy, ax, ay)) { if (calculator.intersectsLine(cx, cy, ax, ay)) {
if (ca == true) { if (ca == true) {
return WithinRelation.NOTWITHIN; return WithinRelation.NOTWITHIN;
} else { } else {
@ -162,7 +162,7 @@ class Circle2D implements Component2D {
if (numCorners == 2) { if (numCorners == 2) {
return Relation.CELL_INSIDE_QUERY; return Relation.CELL_INSIDE_QUERY;
} else if (numCorners == 0) { } else if (numCorners == 0) {
if (intersectsLine(a2x, a2y, b2x, b2y)) { if (calculator.intersectsLine(a2x, a2y, b2x, b2y)) {
return Relation.CELL_CROSSES_QUERY; return Relation.CELL_CROSSES_QUERY;
} }
return Relation.CELL_OUTSIDE_QUERY; return Relation.CELL_OUTSIDE_QUERY;
@ -181,9 +181,9 @@ class Circle2D implements Component2D {
if (Component2D.pointInTriangle(minX, maxX, minY, maxY, calculator.geX(), calculator.getY(), ax, ay, bx, by, cx, cy) == true) { if (Component2D.pointInTriangle(minX, maxX, minY, maxY, calculator.geX(), calculator.getY(), ax, ay, bx, by, cx, cy) == true) {
return Relation.CELL_CROSSES_QUERY; return Relation.CELL_CROSSES_QUERY;
} }
if (intersectsLine(ax, ay, bx, by) || if (calculator.intersectsLine(ax, ay, bx, by) ||
intersectsLine(bx, by, cx, cy) || calculator.intersectsLine(bx, by, cx, cy) ||
intersectsLine(cx, cy, ax, ay)) { calculator.intersectsLine(cx, cy, ax, ay)) {
return Relation.CELL_CROSSES_QUERY; return Relation.CELL_CROSSES_QUERY;
} }
return Relation.CELL_OUTSIDE_QUERY; return Relation.CELL_OUTSIDE_QUERY;
@ -210,14 +210,16 @@ class Circle2D implements Component2D {
return containsCount; return containsCount;
} }
// This methods in a new helper class XYUtil? private static boolean intersectsLine(double centerX, double centerY, double aX, double aY, double bX, double bY, DistanceCalculator calculator) {
private boolean intersectsLine(double aX, double aY, double bX, double bY) {
//Algorithm based on this thread : https://stackoverflow.com/questions/3120357/get-closest-point-to-a-line //Algorithm based on this thread : https://stackoverflow.com/questions/3120357/get-closest-point-to-a-line
final double[] vectorAP = new double[] {calculator.geX() - aX, calculator.getY() - aY}; final double vectorAPX = centerX - aX;
final double[] vectorAB = new double[] {bX - aX, bY - aY}; final double vectorAPY = centerY - aY;
final double magnitudeAB = vectorAB[0] * vectorAB[0] + vectorAB[1] * vectorAB[1]; final double vectorABX = bX - aX;
final double dotProduct = vectorAP[0] * vectorAB[0] + vectorAP[1] * vectorAB[1]; final double vectorABY = bY - aY;
final double magnitudeAB = vectorABX * vectorABX + vectorABY * vectorABY;
final double dotProduct = vectorAPX * vectorABX + vectorAPY * vectorABY;
final double distance = dotProduct / magnitudeAB; final double distance = dotProduct / magnitudeAB;
@ -225,8 +227,8 @@ class Circle2D implements Component2D {
return false; return false;
} }
final double pX = aX + vectorAB[0] * distance; final double pX = aX + vectorABX * distance;
final double pY = aY + vectorAB[1] * distance; final double pY = aY + vectorABY * distance;
final double minX = StrictMath.min(aX, bX); final double minX = StrictMath.min(aX, bX);
final double minY = StrictMath.min(aY, bY); final double minY = StrictMath.min(aY, bY);
@ -234,31 +236,34 @@ class Circle2D implements Component2D {
final double maxY = StrictMath.max(aY, bY); final double maxY = StrictMath.max(aY, bY);
if (pX >= minX && pX <= maxX && pY >= minY && pY <= maxY) { if (pX >= minX && pX <= maxX && pY >= minY && pY <= maxY) {
return contains(pX, pY); return calculator.contains(pX, pY);
} }
return false; return false;
} }
private interface DistanceCalculator { private interface DistanceCalculator {
Relation relate(double minX, double maxX, double minY, double maxY); /** check if the point is within a distance */
boolean contains(double x, double y); boolean contains(double x, double y);
/** check if the line is within a distance */
boolean intersectsLine(double aX, double aY, double bX, double bY);
/** Relates this calculator to the provided bounding box */
Relation relate(double minX, double maxX, double minY, double maxY);
/** check if the bounding box is disjoint with this calculator bounding box */
boolean disjoint(double minX, double maxX, double minY, double maxY); boolean disjoint(double minX, double maxX, double minY, double maxY);
/** check if the bounding box is contains this calculator bounding box */
boolean within(double minX, double maxX, double minY, double maxY); boolean within(double minX, double maxX, double minY, double maxY);
/** get min X of this calculator */
double getMinX(); double getMinX();
/** get max X of this calculator */
double getMaxX(); double getMaxX();
/** get min Y of this calculator */
double getMinY(); double getMinY();
/** get max Y of this calculator */
double getMaxY(); double getMaxY();
/** get center X */
double geX(); double geX();
/** get center Y */
double getY(); double getY();
} }
@ -321,6 +326,11 @@ class Circle2D implements Component2D {
return diffX * diffX + diffY * diffY <= radiusSquared; return diffX * diffX + diffY * diffY <= radiusSquared;
} }
@Override
public boolean intersectsLine(double aX, double aY, double bX, double bY) {
return Circle2D.intersectsLine(centerX, centerY, aX, aY, bX, bY, this);
}
@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(this.minX, this.maxX, this.minY, this.maxY, minX, maxX, minY, maxY);
@ -390,6 +400,19 @@ class Circle2D implements Component2D {
return SloppyMath.haversinSortKey(y, x, this.centerLat, this.centerLon) <= sortKey; return SloppyMath.haversinSortKey(y, x, this.centerLat, this.centerLon) <= sortKey;
} }
@Override
public boolean intersectsLine(double aX, double aY, double bX, double bY) {
if (Circle2D.intersectsLine(centerLon, centerLat, aX, aY, bX, bY, this)) {
return true;
}
if (crossesDateline) {
double newCenterLon = (centerLon > 0) ? centerLon - 360 : centerLon + 360;
return Circle2D.intersectsLine(newCenterLon, centerLat, aX, aY, bX, bY, this);
}
return false;
}
@Override @Override
public boolean disjoint(double minX, double maxX, double minY, double maxY) { public boolean disjoint(double minX, double maxX, double minY, double maxY) {
if (crossesDateline) { if (crossesDateline) {

View File

@ -60,6 +60,19 @@ public class TestCircle2D extends LuceneTestCase {
assertEquals(Component2D.WithinRelation.NOTWITHIN, circle2D.withinTriangle(ax, ay, true, bx, by, true, cx, cy, true)); assertEquals(Component2D.WithinRelation.NOTWITHIN, circle2D.withinTriangle(ax, ay, true, bx, by, true, cx, cy, true));
} }
public void testTriangleDateLineIntersects() {
Component2D circle2D = LatLonGeometry.create(new Circle(0, 179, 222400));
double ax = -179;
double ay = 1;
double bx = -179;
double by = -1;
double cx = -178;
double cy = 0;
// we just touch the edge from the dateline
assertEquals(PointValues.Relation.CELL_CROSSES_QUERY, circle2D.relateTriangle(ax, ay, bx, by , cx, cy));
assertEquals(Component2D.WithinRelation.NOTWITHIN, circle2D.withinTriangle(ax, ay, true, bx, by, true, cx, cy, true));
}
public void testTriangleContains() { public void testTriangleContains() {
Component2D circle2D; Component2D circle2D;
if (random().nextBoolean()) { if (random().nextBoolean()) {