mirror of https://github.com/apache/lucene.git
LUCENE-7212: Add outside distance classes and methods.
This commit is contained in:
parent
2a810938ba
commit
cc26341345
|
@ -22,10 +22,12 @@ import org.apache.lucene.search.FieldDoc;
|
|||
import org.apache.lucene.search.SortField;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
|
||||
import org.apache.lucene.spatial3d.geom.PlanetModel;
|
||||
import org.apache.lucene.spatial3d.geom.GeoPoint;
|
||||
import org.apache.lucene.spatial3d.geom.GeoDistanceShape;
|
||||
import org.apache.lucene.spatial3d.geom.GeoOutsideDistance;
|
||||
|
||||
/**
|
||||
* An per-document 3D location field.
|
||||
|
@ -256,7 +258,7 @@ public class Geo3DDocValuesField extends Field {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a SortField for sorting by distance from a point.
|
||||
* Creates a SortField for sorting by distance within a circle.
|
||||
* <p>
|
||||
* This sort orders documents by ascending distance from the location. The value returned in {@link FieldDoc} for
|
||||
* the hits contains a Double instance with the distance in meters.
|
||||
|
@ -264,14 +266,14 @@ public class Geo3DDocValuesField extends Field {
|
|||
* If a document is missing the field, then by default it is treated as having {@link Double#POSITIVE_INFINITY} distance
|
||||
* (missing values sort last).
|
||||
* <p>
|
||||
* If a document contains multiple values for the field, the <i>closest</i> distance to the location is used.
|
||||
* If a document contains multiple values for the field, the <i>closest</i> distance from the circle center is used.
|
||||
*
|
||||
* @param field field name. must not be null.
|
||||
* @param latitude latitude at the center: must be within standard +/-90 coordinate bounds.
|
||||
* @param longitude longitude at the center: must be within standard +/-180 coordinate bounds.
|
||||
* @param maxRadiusMeters is the maximum radius in meters.
|
||||
* @return SortField ordering documents by distance
|
||||
* @throws IllegalArgumentException if {@code field} is null or location has invalid coordinates.
|
||||
* @throws IllegalArgumentException if {@code field} is null or circle has invalid coordinates.
|
||||
*/
|
||||
public static SortField newDistanceSort(final String field, final double latitude, final double longitude, final double maxRadiusMeters) {
|
||||
final GeoDistanceShape shape = Geo3DUtil.fromDistance(latitude, longitude, maxRadiusMeters);
|
||||
|
@ -287,18 +289,138 @@ public class Geo3DDocValuesField extends Field {
|
|||
* If a document is missing the field, then by default it is treated as having {@link Double#POSITIVE_INFINITY} distance
|
||||
* (missing values sort last).
|
||||
* <p>
|
||||
* If a document contains multiple values for the field, the <i>closest</i> distance to the location is used.
|
||||
* If a document contains multiple values for the field, the <i>closest</i> distance along the path is used.
|
||||
*
|
||||
* @param field field name. must not be null.
|
||||
* @param pathLatitudes latitude values for points of the path: must be within standard +/-90 coordinate bounds.
|
||||
* @param pathLongitudes longitude values for points of the path: must be within standard +/-180 coordinate bounds.
|
||||
* @param pathWidthMeters width of the path in meters.
|
||||
* @return SortField ordering documents by distance
|
||||
* @throws IllegalArgumentException if {@code field} is null or location has invalid coordinates.
|
||||
* @throws IllegalArgumentException if {@code field} is null or path has invalid coordinates.
|
||||
*/
|
||||
public static SortField newPathSort(final String field, final double[] pathLatitudes, final double[] pathLongitudes, final double pathWidthMeters) {
|
||||
final GeoDistanceShape shape = Geo3DUtil.fromPath(pathLatitudes, pathLongitudes, pathWidthMeters);
|
||||
return new Geo3DPointSortField(field, shape);
|
||||
}
|
||||
|
||||
// Outside distances
|
||||
|
||||
/**
|
||||
* Creates a SortField for sorting by outside distance from a circle.
|
||||
* <p>
|
||||
* This sort orders documents by ascending outside distance from the circle. Points within the circle have distance 0.0.
|
||||
* The value returned in {@link FieldDoc} for
|
||||
* the hits contains a Double instance with the distance in meters.
|
||||
* <p>
|
||||
* If a document is missing the field, then by default it is treated as having {@link Double#POSITIVE_INFINITY} distance
|
||||
* (missing values sort last).
|
||||
* <p>
|
||||
* If a document contains multiple values for the field, the <i>closest</i> distance to the circle is used.
|
||||
*
|
||||
* @param field field name. must not be null.
|
||||
* @param latitude latitude at the center: must be within standard +/-90 coordinate bounds.
|
||||
* @param longitude longitude at the center: must be within standard +/-180 coordinate bounds.
|
||||
* @param maxRadiusMeters is the maximum radius in meters.
|
||||
* @return SortField ordering documents by distance
|
||||
* @throws IllegalArgumentException if {@code field} is null or location has invalid coordinates.
|
||||
*/
|
||||
public static SortField newOutsideDistanceSort(final String field, final double latitude, final double longitude, final double maxRadiusMeters) {
|
||||
final GeoOutsideDistance shape = Geo3DUtil.fromDistance(latitude, longitude, maxRadiusMeters);
|
||||
return new Geo3DPointOutsideSortField(field, shape);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SortField for sorting by outside distance from a box.
|
||||
* <p>
|
||||
* This sort orders documents by ascending outside distance from the box. Points within the box have distance 0.0.
|
||||
* The value returned in {@link FieldDoc} for
|
||||
* the hits contains a Double instance with the distance in meters.
|
||||
* <p>
|
||||
* If a document is missing the field, then by default it is treated as having {@link Double#POSITIVE_INFINITY} distance
|
||||
* (missing values sort last).
|
||||
* <p>
|
||||
* If a document contains multiple values for the field, the <i>closest</i> distance to the box is used.
|
||||
*
|
||||
* @param field field name. must not be null.
|
||||
* @param minLatitude latitude lower bound: must be within standard +/-90 coordinate bounds.
|
||||
* @param maxLatitude latitude upper bound: must be within standard +/-90 coordinate bounds.
|
||||
* @param minLongitude longitude lower bound: must be within standard +/-180 coordinate bounds.
|
||||
* @param maxLongitude longitude upper bound: must be within standard +/-180 coordinate bounds.
|
||||
* @return SortField ordering documents by distance
|
||||
* @throws IllegalArgumentException if {@code field} is null or box has invalid coordinates.
|
||||
*/
|
||||
public static SortField newOutsideBoxSort(final String field, final double minLatitude, final double maxLatitude, final double minLongitude, final double maxLongitude) {
|
||||
final GeoOutsideDistance shape = Geo3DUtil.fromBox(minLatitude, maxLatitude, minLongitude, maxLongitude);
|
||||
return new Geo3DPointOutsideSortField(field, shape);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SortField for sorting by outside distance from a polygon.
|
||||
* <p>
|
||||
* This sort orders documents by ascending outside distance from the polygon. Points within the polygon have distance 0.0.
|
||||
* The value returned in {@link FieldDoc} for
|
||||
* the hits contains a Double instance with the distance in meters.
|
||||
* <p>
|
||||
* If a document is missing the field, then by default it is treated as having {@link Double#POSITIVE_INFINITY} distance
|
||||
* (missing values sort last).
|
||||
* <p>
|
||||
* If a document contains multiple values for the field, the <i>closest</i> distance to the polygon is used.
|
||||
*
|
||||
* @param field field name. must not be null.
|
||||
* @param polygons is the list of polygons to use to construct the query; must be at least one.
|
||||
* @return SortField ordering documents by distance
|
||||
* @throws IllegalArgumentException if {@code field} is null or polygon has invalid coordinates.
|
||||
*/
|
||||
public static SortField newOutsidePolygonSort(final String field, final Polygon... polygons) {
|
||||
final GeoOutsideDistance shape = Geo3DUtil.fromPolygon(polygons);
|
||||
return new Geo3DPointOutsideSortField(field, shape);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SortField for sorting by outside distance from a large polygon. This differs from the related newOutsideLargePolygonSort in that it
|
||||
* does little or no legality checking and is optimized for very large numbers of polygon edges.
|
||||
* <p>
|
||||
* This sort orders documents by ascending outside distance from the polygon. Points within the polygon have distance 0.0.
|
||||
* The value returned in {@link FieldDoc} for
|
||||
* the hits contains a Double instance with the distance in meters.
|
||||
* <p>
|
||||
* If a document is missing the field, then by default it is treated as having {@link Double#POSITIVE_INFINITY} distance
|
||||
* (missing values sort last).
|
||||
* <p>
|
||||
* If a document contains multiple values for the field, the <i>closest</i> distance to the polygon is used.
|
||||
*
|
||||
* @param field field name. must not be null.
|
||||
* @param polygons is the list of polygons to use to construct the query; must be at least one.
|
||||
* @return SortField ordering documents by distance
|
||||
* @throws IllegalArgumentException if {@code field} is null or polygon has invalid coordinates.
|
||||
*/
|
||||
public static SortField newOutsideLargePolygonSort(final String field, final Polygon... polygons) {
|
||||
final GeoOutsideDistance shape = Geo3DUtil.fromLargePolygon(polygons);
|
||||
return new Geo3DPointOutsideSortField(field, shape);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SortField for sorting by outside distance from a path.
|
||||
* <p>
|
||||
* This sort orders documents by ascending outside distance from the described path. Points within the path
|
||||
* are given the distance of 0.0. The value returned in {@link FieldDoc} for
|
||||
* the hits contains a Double instance with the distance in meters.
|
||||
* <p>
|
||||
* If a document is missing the field, then by default it is treated as having {@link Double#POSITIVE_INFINITY} distance
|
||||
* (missing values sort last).
|
||||
* <p>
|
||||
* If a document contains multiple values for the field, the <i>closest</i> distance from the path is used.
|
||||
*
|
||||
* @param field field name. must not be null.
|
||||
* @param pathLatitudes latitude values for points of the path: must be within standard +/-90 coordinate bounds.
|
||||
* @param pathLongitudes longitude values for points of the path: must be within standard +/-180 coordinate bounds.
|
||||
* @param pathWidthMeters width of the path in meters.
|
||||
* @return SortField ordering documents by distance
|
||||
* @throws IllegalArgumentException if {@code field} is null or path has invalid coordinates.
|
||||
*/
|
||||
public static SortField newOutsidePathSort(final String field, final double[] pathLatitudes, final double[] pathLongitudes, final double pathWidthMeters) {
|
||||
final GeoOutsideDistance shape = Geo3DUtil.fromPath(pathLatitudes, pathLongitudes, pathWidthMeters);
|
||||
return new Geo3DPointOutsideSortField(field, shape);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -129,9 +129,6 @@ public final class Geo3DPoint extends Field {
|
|||
* @return query matching points within this polygon
|
||||
*/
|
||||
public static Query newLargePolygonQuery(final String field, final Polygon... polygons) {
|
||||
if (polygons.length < 1) {
|
||||
throw new IllegalArgumentException("need at least one polygon");
|
||||
}
|
||||
final GeoShape shape = Geo3DUtil.fromLargePolygon(polygons);
|
||||
return newShapeQuery(field, shape);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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.spatial3d;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.index.FieldInfo;
|
||||
import org.apache.lucene.index.LeafReader;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.SortedNumericDocValues;
|
||||
import org.apache.lucene.search.FieldComparator;
|
||||
import org.apache.lucene.search.LeafFieldComparator;
|
||||
import org.apache.lucene.search.Scorer;
|
||||
|
||||
import org.apache.lucene.spatial3d.geom.GeoOutsideDistance;
|
||||
import org.apache.lucene.spatial3d.geom.DistanceStyle;
|
||||
import org.apache.lucene.spatial3d.geom.PlanetModel;
|
||||
|
||||
/**
|
||||
* Compares documents by outside distance, using a GeoOutsideDistance to compute the distance
|
||||
*/
|
||||
class Geo3DPointOutsideDistanceComparator extends FieldComparator<Double> implements LeafFieldComparator {
|
||||
final String field;
|
||||
|
||||
final GeoOutsideDistance distanceShape;
|
||||
|
||||
final double[] values;
|
||||
double bottomDistance;
|
||||
double topValue;
|
||||
SortedNumericDocValues currentDocs;
|
||||
|
||||
public Geo3DPointOutsideDistanceComparator(String field, final GeoOutsideDistance distanceShape, int numHits) {
|
||||
this.field = field;
|
||||
this.distanceShape = distanceShape;
|
||||
this.values = new double[numHits];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScorer(Scorer scorer) {}
|
||||
|
||||
@Override
|
||||
public int compare(int slot1, int slot2) {
|
||||
return Double.compare(values[slot1], values[slot2]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBottom(int slot) {
|
||||
bottomDistance = values[slot];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTopValue(Double value) {
|
||||
topValue = value.doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareBottom(int doc) throws IOException {
|
||||
currentDocs.setDocument(doc);
|
||||
|
||||
int numValues = currentDocs.count();
|
||||
if (numValues == 0) {
|
||||
return Double.compare(bottomDistance, Double.POSITIVE_INFINITY);
|
||||
}
|
||||
|
||||
int cmp = -1;
|
||||
for (int i = 0; i < numValues; i++) {
|
||||
long encoded = currentDocs.valueAt(i);
|
||||
|
||||
// Test against bounds.
|
||||
// First we need to decode...
|
||||
final double x = Geo3DDocValuesField.decodeXValue(encoded);
|
||||
final double y = Geo3DDocValuesField.decodeYValue(encoded);
|
||||
final double z = Geo3DDocValuesField.decodeZValue(encoded);
|
||||
|
||||
cmp = Math.max(cmp, Double.compare(bottomDistance, distanceShape.computeOutsideDistance(DistanceStyle.ARC, x, y, z)));
|
||||
}
|
||||
return cmp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(int slot, int doc) throws IOException {
|
||||
values[slot] = computeMinimumDistance(doc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException {
|
||||
LeafReader reader = context.reader();
|
||||
FieldInfo info = reader.getFieldInfos().fieldInfo(field);
|
||||
if (info != null) {
|
||||
Geo3DDocValuesField.checkCompatible(info);
|
||||
}
|
||||
currentDocs = DocValues.getSortedNumeric(reader, field);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double value(int slot) {
|
||||
// Return the arc distance
|
||||
return Double.valueOf(values[slot] * PlanetModel.WGS84_MEAN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTop(int doc) throws IOException {
|
||||
return Double.compare(topValue, computeMinimumDistance(doc));
|
||||
}
|
||||
|
||||
double computeMinimumDistance(final int doc) {
|
||||
currentDocs.setDocument(doc);
|
||||
double minValue = Double.POSITIVE_INFINITY;
|
||||
final int numValues = currentDocs.count();
|
||||
for (int i = 0; i < numValues; i++) {
|
||||
final long encoded = currentDocs.valueAt(i);
|
||||
final double distance = distanceShape.computeOutsideDistance(DistanceStyle.ARC,
|
||||
Geo3DDocValuesField.decodeXValue(encoded),
|
||||
Geo3DDocValuesField.decodeYValue(encoded),
|
||||
Geo3DDocValuesField.decodeZValue(encoded));
|
||||
minValue = Math.min(minValue, distance);
|
||||
}
|
||||
return minValue;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* 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.spatial3d;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.search.FieldComparator;
|
||||
import org.apache.lucene.search.SortField;
|
||||
|
||||
import org.apache.lucene.spatial3d.geom.GeoOutsideDistance;
|
||||
|
||||
/**
|
||||
* Sorts by outside distance from an origin location.
|
||||
*/
|
||||
final class Geo3DPointOutsideSortField extends SortField {
|
||||
final GeoOutsideDistance distanceShape;
|
||||
|
||||
Geo3DPointOutsideSortField(final String field, final GeoOutsideDistance distanceShape) {
|
||||
super(field, SortField.Type.CUSTOM);
|
||||
if (field == null) {
|
||||
throw new IllegalArgumentException("field must not be null");
|
||||
}
|
||||
if (distanceShape == null) {
|
||||
throw new IllegalArgumentException("distanceShape must not be null");
|
||||
}
|
||||
this.distanceShape = distanceShape;
|
||||
setMissingValue(Double.POSITIVE_INFINITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldComparator<?> getComparator(int numHits, int sortPos) throws IOException {
|
||||
return new Geo3DPointOutsideDistanceComparator(getField(), distanceShape, numHits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getMissingValue() {
|
||||
return (Double) super.getMissingValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMissingValue(Object missingValue) {
|
||||
if (Double.valueOf(Double.POSITIVE_INFINITY).equals(missingValue) == false) {
|
||||
throw new IllegalArgumentException("Missing value can only be Double.POSITIVE_INFINITY (missing values last), but got " + missingValue);
|
||||
}
|
||||
this.missingValue = missingValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
long temp;
|
||||
temp = distanceShape.hashCode();
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!super.equals(obj)) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
final Geo3DPointSortField other = (Geo3DPointSortField) obj;
|
||||
return distanceShape.equals(other.distanceShape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<outsideDistanceShape:");
|
||||
builder.append('"');
|
||||
builder.append(getField());
|
||||
builder.append('"');
|
||||
builder.append(" shape=");
|
||||
builder.append(distanceShape);
|
||||
if (Double.POSITIVE_INFINITY != getMissingValue()) {
|
||||
builder.append(" missingValue=" + getMissingValue());
|
||||
}
|
||||
builder.append('>');
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
|
@ -136,6 +136,9 @@ class Geo3DUtil {
|
|||
* @return the large GeoPolygon.
|
||||
*/
|
||||
static GeoPolygon fromLargePolygon(final Polygon... polygons) {
|
||||
if (polygons.length < 1) {
|
||||
throw new IllegalArgumentException("need at least one polygon");
|
||||
}
|
||||
return GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.WGS84, convertToDescription(polygons));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue