Index: lucene/spatial/src/test/org/apache/lucene/spatial/SpatialExample.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/test/org/apache/lucene/spatial/SpatialExample.java (revision 1503302) +++ lucene/spatial/src/test/org/apache/lucene/spatial/SpatialExample.java (revision ) @@ -162,7 +162,8 @@ //--Match all, order by distance ascending { Point pt = ctx.makePoint(60, -50); - ValueSource valueSource = strategy.makeDistanceValueSource(pt);//the distance (in degrees) + double degToKm = DistanceUtils.degrees2Dist(1, DistanceUtils.EARTH_MEAN_RADIUS_KM); + ValueSource valueSource = strategy.makeDistanceValueSource(pt, degToKm);//the distance (in km) Sort distSort = new Sort(valueSource.getSortField(false)).rewrite(indexSearcher);//false=asc dist TopDocs docs = indexSearcher.search(new MatchAllDocsQuery(), 10, distSort); assertDocMatchedIds(indexSearcher, docs, 4, 20, 2); Index: lucene/spatial/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java (revision 1503302) +++ lucene/spatial/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java (revision ) @@ -120,8 +120,8 @@ } @Override - public ValueSource makeDistanceValueSource(Point queryPoint) { - return new DistanceValueSource(this, queryPoint); + public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) { + return new DistanceValueSource(this, queryPoint, multiplier); } @Override Index: solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java (revision 1503302) +++ solr/core/src/java/org/apache/solr/schema/AbstractSpatialFieldType.java (revision ) @@ -240,12 +240,14 @@ //We get the valueSource for the score then the filter and combine them. ValueSource valueSource; - if ("distance".equals(score)) - valueSource = strategy.makeDistanceValueSource(spatialArgs.getShape().getCenter()); - else if ("recipDistance".equals(score)) + if ("distance".equals(score)) { + double multiplier = 1.0;//TODO support units=kilometers + valueSource = strategy.makeDistanceValueSource(spatialArgs.getShape().getCenter(), multiplier); + } else if ("recipDistance".equals(score)) { valueSource = strategy.makeRecipDistanceValueSource(spatialArgs.getShape()); - else + } else { throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "'score' local-param must be one of 'none', 'distance', or 'recipDistance'"); + } FunctionQuery functionQuery = new FunctionQuery(valueSource); if (localParams != null && !localParams.getBool(FILTER_PARAM, true)) Index: lucene/spatial/src/test/org/apache/lucene/spatial/DistanceStrategyTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/test/org/apache/lucene/spatial/DistanceStrategyTest.java (revision 1503302) +++ lucene/spatial/src/test/org/apache/lucene/spatial/DistanceStrategyTest.java (revision ) @@ -34,6 +34,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class DistanceStrategyTest extends StrategyTestCase { @@ -121,6 +122,11 @@ void checkDistValueSource(String ptStr, float... distances) throws IOException { Point pt = (Point) ctx.readShape(ptStr); - checkValueSource(strategy.makeDistanceValueSource(pt), distances, 1.0e-4f); + float multiplier = random().nextFloat() * 100f; + float[] dists2 = Arrays.copyOf(distances, distances.length); + for (int i = 0; i < dists2.length; i++) { + dists2[i] *= multiplier; + } + checkValueSource(strategy.makeDistanceValueSource(pt, multiplier), dists2, 1.0e-3f); } } Index: lucene/spatial/src/java/org/apache/lucene/spatial/prefix/PrefixTreeStrategy.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/prefix/PrefixTreeStrategy.java (revision 1503302) +++ lucene/spatial/src/java/org/apache/lucene/spatial/prefix/PrefixTreeStrategy.java (revision ) @@ -56,7 +56,7 @@ *
  • Only {@link org.apache.lucene.spatial.query.SpatialOperation#Intersects} * is supported. If only points are indexed then this is effectively equivalent * to IsWithin.
  • - *
  • The strategy supports {@link #makeDistanceValueSource(com.spatial4j.core.shape.Point)} + *
  • The strategy supports {@link #makeDistanceValueSource(com.spatial4j.core.shape.Point,double)} * even for multi-valued data, so long as the indexed data is all points; the * behavior is undefined otherwise. However, it will likely be removed in * the future in lieu of using another strategy with a more scalable @@ -182,7 +182,7 @@ } @Override - public ValueSource makeDistanceValueSource(Point queryPoint) { + public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) { PointPrefixTreeFieldCacheProvider p = provider.get( getFieldName() ); if( p == null ) { synchronized (this) {//double checked locking idiom is okay since provider is threadsafe @@ -194,7 +194,7 @@ } } - return new ShapeFieldCacheDistanceValueSource(ctx, p, queryPoint); + return new ShapeFieldCacheDistanceValueSource(ctx, p, queryPoint, multiplier); } public SpatialPrefixTree getGrid() { Index: lucene/spatial/src/java/org/apache/lucene/spatial/SpatialStrategy.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/SpatialStrategy.java (revision 1503302) +++ lucene/spatial/src/java/org/apache/lucene/spatial/SpatialStrategy.java (revision ) @@ -102,11 +102,20 @@ public abstract Field[] createIndexableFields(Shape shape); /** + * See {@link #makeDistanceValueSource(com.spatial4j.core.shape.Point, double)} called with + * a multiplier of 1.0 (i.e. units of degrees). + */ + public ValueSource makeDistanceValueSource(Point queryPoint) { + return makeDistanceValueSource(queryPoint, 1.0); + } + + /** * Make a ValueSource returning the distance between the center of the * indexed shape and {@code queryPoint}. If there are multiple indexed shapes - * then the closest one is chosen. + * then the closest one is chosen. The result is multiplied by {@code multiplier}, which + * conveniently is used to get the desired units. */ - public abstract ValueSource makeDistanceValueSource(Point queryPoint); + public abstract ValueSource makeDistanceValueSource(Point queryPoint, double multiplier); /** * Make a Query based principally on {@link org.apache.lucene.spatial.query.SpatialOperation} @@ -139,7 +148,7 @@ /** * Returns a ValueSource with values ranging from 1 to 0, depending inversely - * on the distance from {@link #makeDistanceValueSource(com.spatial4j.core.shape.Point)}. + * on the distance from {@link #makeDistanceValueSource(com.spatial4j.core.shape.Point,double)}. * The formula is {@code c/(d + c)} where 'd' is the distance and 'c' is * one tenth the distance to the farthest edge from the center. Thus the * scores will be 1 for indexed points at the center of the query shape and as @@ -151,7 +160,7 @@ ctx.makePoint(bbox.getMinX(), bbox.getMinY()), bbox.getMaxX(), bbox.getMaxY()); double distToEdge = diagonalDist * 0.5; float c = (float)distToEdge * 0.1f;//one tenth - return new ReciprocalFloatFunction(makeDistanceValueSource(queryShape.getCenter()), 1f, c, c); + return new ReciprocalFloatFunction(makeDistanceValueSource(queryShape.getCenter(), 1.0), 1f, c, c); } @Override Index: lucene/spatial/src/java/org/apache/lucene/spatial/bbox/DistanceSimilarity.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/bbox/DistanceSimilarity.java (revision 1503302) +++ lucene/spatial/src/java/org/apache/lucene/spatial/bbox/DistanceSimilarity.java (revision ) @@ -30,13 +30,15 @@ */ public class DistanceSimilarity implements BBoxSimilarity { private final Point queryPoint; + private final double multiplier; private final DistanceCalculator distCalc; private final double nullValue; - public DistanceSimilarity(SpatialContext ctx, Point queryPoint) { + public DistanceSimilarity(SpatialContext ctx, Point queryPoint, double multiplier) { this.queryPoint = queryPoint; + this.multiplier = multiplier; this.distCalc = ctx.getDistCalc(); - this.nullValue = (ctx.isGeo() ? 180 : Double.MAX_VALUE); + this.nullValue = (ctx.isGeo() ? 180 * multiplier : Double.MAX_VALUE); } @Override @@ -45,14 +47,43 @@ if (indexRect == null) { score = nullValue; } else { - score = distCalc.distance(queryPoint, indexRect.getCenter()); + score = distCalc.distance(queryPoint, indexRect.getCenter()) * multiplier; } if (exp != null) { exp.setValue((float)score); exp.setDescription(this.getClass().getSimpleName()); - exp.addDetail(new Explanation(-1f,""+queryPoint)); + exp.addDetail(new Explanation(-1f, "" + queryPoint)); exp.addDetail(new Explanation(-1f,""+indexRect)); + exp.addDetail(new Explanation((float)multiplier,"multiplier")); } return score; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DistanceSimilarity that = (DistanceSimilarity) o; + + if (Double.compare(that.multiplier, multiplier) != 0) return false; + if (Double.compare(that.nullValue, nullValue) != 0) return false; + if (!distCalc.equals(that.distCalc)) return false; + if (!queryPoint.equals(that.queryPoint)) return false; + + return true; + } + + @Override + public int hashCode() { + int result; + long temp; + result = queryPoint.hashCode(); + temp = Double.doubleToLongBits(multiplier); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + result = 31 * result + distCalc.hashCode(); + temp = Double.doubleToLongBits(nullValue); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; } } Index: lucene/spatial/src/java/org/apache/lucene/spatial/vector/DistanceValueSource.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/vector/DistanceValueSource.java (revision 1503302) +++ lucene/spatial/src/java/org/apache/lucene/spatial/vector/DistanceValueSource.java (revision ) @@ -39,13 +39,15 @@ private PointVectorStrategy strategy; private final Point from; + private final double multiplier; /** * Constructor. */ - public DistanceValueSource(PointVectorStrategy strategy, Point from) { + public DistanceValueSource(PointVectorStrategy strategy, Point from, double multiplier) { this.strategy = strategy; this.from = from; + this.multiplier = multiplier; } /** @@ -72,7 +74,8 @@ private final Point from = DistanceValueSource.this.from; private final DistanceCalculator calculator = strategy.getSpatialContext().getDistCalc(); - private final double nullValue = (strategy.getSpatialContext().isGeo() ? 180 : Double.MAX_VALUE); + private final double nullValue = + (strategy.getSpatialContext().isGeo() ? 180 * multiplier : Double.MAX_VALUE); @Override public float floatVal(int doc) { @@ -84,7 +87,7 @@ // make sure it has minX and area if (validX.get(doc)) { assert validY.get(doc); - return calculator.distance(from, ptX.get(doc), ptY.get(doc)); + return calculator.distance(from, ptX.get(doc), ptY.get(doc)) * multiplier; } return nullValue; } @@ -105,6 +108,7 @@ if (!from.equals(that.from)) return false; if (!strategy.equals(that.strategy)) return false; + if (multiplier != that.multiplier) return false; return true; } Index: lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java (revision 1503302) +++ lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java (revision ) @@ -135,9 +135,9 @@ //--------------------------------- @Override - public ValueSource makeDistanceValueSource(Point queryPoint) { + public ValueSource makeDistanceValueSource(Point queryPoint, double multiplier) { return new BBoxSimilarityValueSource( - this, new DistanceSimilarity(this.getSpatialContext(), queryPoint)); + this, new DistanceSimilarity(this.getSpatialContext(), queryPoint, multiplier)); } public ValueSource makeBBoxAreaSimilarityValueSource(Rectangle queryBox) { Index: lucene/spatial/src/java/org/apache/lucene/spatial/util/ShapeFieldCacheDistanceValueSource.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/java/org/apache/lucene/spatial/util/ShapeFieldCacheDistanceValueSource.java (revision 1503302) +++ lucene/spatial/src/java/org/apache/lucene/spatial/util/ShapeFieldCacheDistanceValueSource.java (revision ) @@ -38,14 +38,17 @@ */ public class ShapeFieldCacheDistanceValueSource extends ValueSource { - private final ShapeFieldCacheProvider provider; private final SpatialContext ctx; private final Point from; + private final ShapeFieldCacheProvider provider; + private final double multiplier; - public ShapeFieldCacheDistanceValueSource(SpatialContext ctx, ShapeFieldCacheProvider provider, Point from) { + public ShapeFieldCacheDistanceValueSource(SpatialContext ctx, + ShapeFieldCacheProvider provider, Point from, double multiplier) { this.ctx = ctx; this.from = from; this.provider = provider; + this.multiplier = multiplier; } @Override @@ -60,7 +63,7 @@ provider.getCache(readerContext.reader()); private final Point from = ShapeFieldCacheDistanceValueSource.this.from; private final DistanceCalculator calculator = ctx.getDistCalc(); - private final double nullValue = (ctx.isGeo() ? 180 : Double.MAX_VALUE); + private final double nullValue = (ctx.isGeo() ? 180 * multiplier : Double.MAX_VALUE); @Override public float floatVal(int doc) { @@ -69,13 +72,14 @@ @Override public double doubleVal(int doc) { + List vals = cache.getShapes( doc ); if( vals != null ) { double v = calculator.distance(from, vals.get(0)); for( int i=1; i