mirror of https://github.com/apache/lucene.git
LUCENE-6905: Unwrap center longitude for dateline crossing GeoPointDistanceQuery.
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1716462 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
77d89d6037
commit
1daa11f328
|
@ -18,9 +18,9 @@ package org.apache.lucene.search;
|
|||
*/
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.util.GeoDistanceUtils;
|
||||
import org.apache.lucene.util.GeoRect;
|
||||
import org.apache.lucene.util.GeoUtils;
|
||||
import org.apache.lucene.util.SloppyMath;
|
||||
|
||||
/** Implements a simple point distance query on a GeoPoint field. This is based on
|
||||
* {@link org.apache.lucene.search.GeoPointInBBoxQuery} and is implemented using a two phase approach. First,
|
||||
|
@ -53,7 +53,7 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
|
|||
super(field, bbox.minLon, bbox.minLat, bbox.maxLon, bbox.maxLat);
|
||||
{
|
||||
// check longitudinal overlap (limits radius)
|
||||
final double maxRadius = SloppyMath.haversin(centerLat, centerLon, centerLat, (180.0 + centerLon) % 360)*1000.0;
|
||||
final double maxRadius = GeoDistanceUtils.maxRadialDistanceMeters(centerLon, centerLat);
|
||||
if (radiusMeters > maxRadius) {
|
||||
throw new IllegalArgumentException("radiusMeters " + radiusMeters + " exceeds maxRadius [" + maxRadius
|
||||
+ "] at location [" + centerLon + " " + centerLat + "]");
|
||||
|
@ -79,18 +79,32 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
|
|||
|
||||
@Override
|
||||
public Query rewrite(IndexReader reader) {
|
||||
// query crosses dateline; split into left and right queries
|
||||
if (maxLon < minLon) {
|
||||
BooleanQuery.Builder bqb = new BooleanQuery.Builder();
|
||||
|
||||
GeoPointDistanceQueryImpl left = new GeoPointDistanceQueryImpl(field, this, new GeoRect(GeoUtils.MIN_LON_INCL, maxLon,
|
||||
minLat, maxLat));
|
||||
// unwrap the longitude iff outside the specified min/max lon range
|
||||
double unwrappedLon = centerLon;
|
||||
if (unwrappedLon > maxLon) {
|
||||
// unwrap left
|
||||
unwrappedLon += -360.0D;
|
||||
}
|
||||
GeoPointDistanceQueryImpl left = new GeoPointDistanceQueryImpl(field, this, unwrappedLon,
|
||||
new GeoRect(GeoUtils.MIN_LON_INCL, maxLon, minLat, maxLat));
|
||||
bqb.add(new BooleanClause(left, BooleanClause.Occur.SHOULD));
|
||||
GeoPointDistanceQueryImpl right = new GeoPointDistanceQueryImpl(field, this, new GeoRect(minLon, GeoUtils.MAX_LON_INCL,
|
||||
minLat, maxLat));
|
||||
|
||||
if (unwrappedLon < maxLon) {
|
||||
// unwrap right
|
||||
unwrappedLon += 360.0D;
|
||||
}
|
||||
GeoPointDistanceQueryImpl right = new GeoPointDistanceQueryImpl(field, this, unwrappedLon,
|
||||
new GeoRect(minLon, GeoUtils.MAX_LON_INCL, minLat, maxLat));
|
||||
bqb.add(new BooleanClause(right, BooleanClause.Occur.SHOULD));
|
||||
|
||||
return bqb.build();
|
||||
}
|
||||
return new GeoPointDistanceQueryImpl(field, this, new GeoRect(this.minLon, this.maxLon, this.minLat, this.maxLat));
|
||||
return new GeoPointDistanceQueryImpl(field, this, centerLon,
|
||||
new GeoRect(this.minLon, this.maxLon, this.minLat, this.maxLat));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,10 +33,13 @@ import org.apache.lucene.util.SloppyMath;
|
|||
*/
|
||||
final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
|
||||
private final GeoPointDistanceQuery query;
|
||||
private final double centerLon;
|
||||
|
||||
GeoPointDistanceQueryImpl(final String field, final GeoPointDistanceQuery q, final GeoRect bbox) {
|
||||
GeoPointDistanceQueryImpl(final String field, final GeoPointDistanceQuery q, final double centerLonUnwrapped,
|
||||
final GeoRect bbox) {
|
||||
super(field, bbox.minLon, bbox.minLat, bbox.maxLon, bbox.maxLat);
|
||||
query = q;
|
||||
centerLon = centerLonUnwrapped;
|
||||
}
|
||||
|
||||
@Override @SuppressWarnings("unchecked")
|
||||
|
@ -74,12 +77,12 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
|
|||
|
||||
@Override
|
||||
protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat) {
|
||||
return GeoUtils.rectCrossesCircle(minLon, minLat, maxLon, maxLat, query.centerLon, query.centerLat, query.radiusMeters);
|
||||
return GeoUtils.rectCrossesCircle(minLon, minLat, maxLon, maxLat, centerLon, query.centerLat, query.radiusMeters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat) {
|
||||
return GeoUtils.rectWithinCircle(minLon, minLat, maxLon, maxLat, query.centerLon, query.centerLat, query.radiusMeters);
|
||||
return GeoUtils.rectWithinCircle(minLon, minLat, maxLon, maxLat, centerLon, query.centerLat, query.radiusMeters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,7 +99,7 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
|
|||
*/
|
||||
@Override
|
||||
protected boolean postFilter(final double lon, final double lat) {
|
||||
return (SloppyMath.haversin(query.centerLat, query.centerLon, lat, lon) * 1000.0 <= query.radiusMeters);
|
||||
return (SloppyMath.haversin(query.centerLat, centerLon, lat, lon) * 1000.0 <= query.radiusMeters);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -149,6 +149,14 @@ public class GeoDistanceUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/** Returns the maximum distance/radius (in meters) from the point 'center' before overlapping */
|
||||
public static double maxRadialDistanceMeters(final double centerLon, final double centerLat) {
|
||||
if (Math.abs(centerLat) == GeoUtils.MAX_LAT_INCL) {
|
||||
return SloppyMath.haversin(centerLat, centerLon, 0, centerLon)*1000.0;
|
||||
}
|
||||
return SloppyMath.haversin(centerLat, centerLon, centerLat, (GeoUtils.MAX_LON_INCL + centerLon) % 360)*1000.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the inverse haversine to determine distance in degrees longitude for provided distance in meters
|
||||
* @param lat latitude to compute delta degrees lon
|
||||
|
|
|
@ -101,7 +101,7 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
|
|||
new GeoPointField(FIELD_NAME, -96.4538113027811, 32.94823588839368, storedPoint),
|
||||
new GeoPointField(FIELD_NAME, -96.65084838867188, 33.06047141970814, storedPoint),
|
||||
new GeoPointField(FIELD_NAME, -96.7772, 32.778650, storedPoint),
|
||||
new GeoPointField(FIELD_NAME, -83.99724648980559, 58.29438379542874, storedPoint),
|
||||
new GeoPointField(FIELD_NAME, -177.23537676036358, -88.56029371730983, storedPoint),
|
||||
new GeoPointField(FIELD_NAME, -26.779373834241003, 33.541429799076354, storedPoint),
|
||||
new GeoPointField(FIELD_NAME, -77.35379276106497, 26.774024500421728, storedPoint),
|
||||
new GeoPointField(FIELD_NAME, -14.796283808944777, -90.0, storedPoint),
|
||||
|
@ -295,6 +295,12 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
|
|||
assertEquals("GeoDistanceQuery failed", 2, td.totalHits);
|
||||
}
|
||||
|
||||
/** see https://issues.apache.org/jira/browse/LUCENE-6905 */
|
||||
public void testNonEmptyTermsEnum() throws Exception {
|
||||
TopDocs td = geoDistanceQuery(-177.23537676036358, -88.56029371730983, 7757.999232959935, 20);
|
||||
assertEquals("GeoDistanceQuery failed", 2, td.totalHits);
|
||||
}
|
||||
|
||||
public void testMultiValuedQuery() throws Exception {
|
||||
TopDocs td = bboxQuery(-96.4538113027811, 32.7559529921407, -96.7706036567688, 32.7756745755423, 20);
|
||||
// 3 single valued docs + 2 multi-valued docs
|
||||
|
@ -314,7 +320,7 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
|
|||
*/
|
||||
public void testGeoDistanceQueryHuge() throws Exception {
|
||||
TopDocs td = geoDistanceQuery(-96.4538113027811, 32.94823588839368, 6000000, 20);
|
||||
assertEquals("GeoDistanceQuery failed",18, td.totalHits);
|
||||
assertEquals("GeoDistanceQuery failed", 16, td.totalHits);
|
||||
}
|
||||
|
||||
public void testGeoDistanceQueryCrossDateline() throws Exception {
|
||||
|
|
Loading…
Reference in New Issue