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:
Nick Knize 2015-11-25 16:17:20 +00:00
parent 77d89d6037
commit 1daa11f328
4 changed files with 44 additions and 13 deletions

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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 {