add an option to just compute distance factor

This commit is contained in:
Shay Banon 2011-08-19 03:21:46 +03:00
parent fcaa0e3261
commit d371619dd8
7 changed files with 112 additions and 15 deletions

View File

@ -32,25 +32,37 @@ public enum DistanceUnit {
MILES(3959, 24902) {
@Override public String toString() {
return "miles";
}@Override public double toMiles(double distance) {
}
@Override public double toMiles(double distance) {
return distance;
}@Override public double toKilometers(double distance) {
}
@Override public double toKilometers(double distance) {
return distance * MILES_KILOMETRES_RATIO;
}
@Override public String toString(double distance) {
return distance + "mi";
}},
}
},
KILOMETERS(6371, 40076) {
@Override public String toString() {
return "km";
}@Override public double toMiles(double distance) {
}
@Override public double toMiles(double distance) {
return distance / MILES_KILOMETRES_RATIO;
}@Override public double toKilometers(double distance) {
}
@Override public double toKilometers(double distance) {
return distance;
}
@Override public String toString(double distance) {
return distance + "km";
}};
}
};
static final double MILES_KILOMETRES_RATIO = 1.609344;
@ -95,10 +107,24 @@ public enum DistanceUnit {
protected final double earthCircumference;
protected final double earthRadius;
protected final double distancePerDegree;
DistanceUnit(double earthRadius, double earthCircumference) {
this.earthCircumference = earthCircumference;
this.earthRadius = earthRadius;
this.distancePerDegree = earthCircumference / 360;
}
public double getEarthCircumference() {
return earthCircumference;
}
public double getEarthRadius() {
return earthRadius;
}
public double getDistancePerDegree() {
return distancePerDegree;
}
public abstract double toMiles(double distance);

View File

@ -39,6 +39,18 @@ public class GeoPointDocFieldData extends DocFieldData<GeoPointFieldData> {
return fieldData.values(docId);
}
public double factorDistance(double lat, double lon) {
return fieldData.factorDistance(docId, DistanceUnit.MILES, lat, lon);
}
public double arcDistance(double lat, double lon) {
return fieldData.arcDistance(docId, DistanceUnit.MILES, lat, lon);
}
public double arcDistanceInKm(double lat, double lon) {
return fieldData.arcDistance(docId, DistanceUnit.KILOMETERS, lat, lon);
}
public double distance(double lat, double lon) {
return fieldData.distance(docId, DistanceUnit.MILES, lat, lon);
}

View File

@ -82,6 +82,14 @@ public abstract class GeoPointFieldData extends FieldData<GeoPointDocFieldData>
return GeoDistance.PLANE.calculate(latValue(docId), lonValue(docId), lat, lon, unit);
}
public double arcDistance(int docId, DistanceUnit unit, double lat, double lon) {
return GeoDistance.ARC.calculate(latValue(docId), lonValue(docId), lat, lon, unit);
}
public double factorDistance(int docId, DistanceUnit unit, double lat, double lon) {
return GeoDistance.FACTOR.calculate(latValue(docId), lonValue(docId), lat, lon, unit);
}
public double distanceGeohash(int docId, DistanceUnit unit, String geoHash) {
GeoPointHash geoPointHash = geoHashCache.get().get();
if (geoPointHash.geoHash != geoHash) {

View File

@ -154,6 +154,7 @@ public class GeoDistanceFilterParser implements FilterParser {
} else {
distance = DistanceUnit.parse((String) vDistance, unit, DistanceUnit.MILES);
}
distance = geoDistance.normalize(distance, DistanceUnit.MILES);
MapperService mapperService = parseContext.mapperService();
FieldMapper mapper = mapperService.smartNameFieldMapper(fieldName);

View File

@ -205,11 +205,13 @@ public class GeoDistanceRangeFilterParser implements FilterParser {
} else {
from = DistanceUnit.parse((String) vFrom, unit, DistanceUnit.MILES);
}
from = geoDistance.normalize(from, DistanceUnit.MILES);
if (vTo instanceof Number) {
to = unit.toMiles(((Number) vTo).doubleValue());
} else {
to = DistanceUnit.parse((String) vTo, unit, DistanceUnit.MILES);
}
to = geoDistance.normalize(to, DistanceUnit.MILES);
MapperService mapperService = parseContext.mapperService();
FieldMapper mapper = mapperService.smartNameFieldMapper(fieldName);

View File

@ -32,14 +32,30 @@ public enum GeoDistance {
* Calculates distance as points on a plane. Faster, but less accurate than {@link #ARC}.
*/
PLANE() {
private final static double EARTH_CIRCUMFERENCE_MILES = 24901;
private final static double DISTANCE_PER_DEGREE = EARTH_CIRCUMFERENCE_MILES / 360;
@Override public double calculate(double sourceLatitude, double sourceLongitude, double targetLatitude, double targetLongitude, DistanceUnit unit) {
double px = targetLongitude - sourceLongitude;
double py = targetLatitude - sourceLatitude;
double distanceMiles = Math.sqrt(px * px + py * py) * DISTANCE_PER_DEGREE;
return DistanceUnit.convert(distanceMiles, DistanceUnit.MILES, unit);
return Math.sqrt(px * px + py * py) * unit.getDistancePerDegree();
}
@Override public double normalize(double distance, DistanceUnit unit) {
return distance;
}
},
/**
* Calculates distance factor.
*/
FACTOR() {
@Override public double calculate(double sourceLatitude, double sourceLongitude, double targetLatitude, double targetLongitude, DistanceUnit unit) {
// TODO: we might want to normalize longitude as we did in LatLng...
double longitudeDifference = targetLongitude - sourceLongitude;
double a = Math.toRadians(90D - sourceLatitude);
double c = Math.toRadians(90D - targetLatitude);
return (Math.cos(a) * Math.cos(c)) + (Math.sin(a) * Math.sin(c) * Math.cos(Math.toRadians(longitudeDifference)));
}
@Override public double normalize(double distance, DistanceUnit unit) {
return Math.cos(distance / unit.getEarthRadius());
}
},
/**
@ -47,10 +63,27 @@ public enum GeoDistance {
*/
ARC() {
@Override public double calculate(double sourceLatitude, double sourceLongitude, double targetLatitude, double targetLongitude, DistanceUnit unit) {
LatLng sourcePoint = new LatLng(sourceLatitude, sourceLongitude);
LatLng targetPoint = new LatLng(targetLatitude, targetLongitude);
return DistanceUnit.convert(sourcePoint.arcDistance(targetPoint, DistanceUnit.MILES), DistanceUnit.MILES, unit);
}};
// TODO: we might want to normalize longitude as we did in LatLng...
double longitudeDifference = targetLongitude - sourceLongitude;
double a = Math.toRadians(90D - sourceLatitude);
double c = Math.toRadians(90D - targetLatitude);
double factor = (Math.cos(a) * Math.cos(c)) + (Math.sin(a) * Math.sin(c) * Math.cos(Math.toRadians(longitudeDifference)));
if (factor < -1D) {
return Math.PI * unit.getEarthRadius();
} else if (factor >= 1D) {
return 0;
} else {
return Math.acos(factor) * unit.getEarthRadius();
}
}
@Override public double normalize(double distance, DistanceUnit unit) {
return distance;
}
};
public abstract double normalize(double distance, DistanceUnit unit);
public abstract double calculate(double sourceLatitude, double sourceLongitude, double targetLatitude, double targetLongitude, DistanceUnit unit);
@ -59,6 +92,8 @@ public enum GeoDistance {
return PLANE;
} else if ("arc".equals(s)) {
return ARC;
} else if ("factor".equals(s)) {
return FACTOR;
}
throw new ElasticSearchIllegalArgumentException("No geo distance for [" + s + "]");
}

View File

@ -22,6 +22,7 @@ package org.elasticsearch.test.integration.search.geo;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.search.geo.GeoDistance;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
@ -122,6 +123,18 @@ public class GeoDistanceTests extends AbstractNodesTests {
assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("4"), equalTo("5"), equalTo("6")));
}
// now with a PLANE type
searchResponse = client.prepareSearch() // from NY
.setQuery(filteredQuery(matchAllQuery(), geoDistanceFilter("location").distance("3km").geoDistance(GeoDistance.PLANE).point(40.7143528, -74.0059731)))
.execute().actionGet();
assertThat(searchResponse.hits().getTotalHits(), equalTo(5l));
assertThat(searchResponse.hits().hits().length, equalTo(5));
for (SearchHit hit : searchResponse.hits()) {
assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("4"), equalTo("5"), equalTo("6")));
}
// factor type is really too small for this resolution
searchResponse = client.prepareSearch() // from NY
.setQuery(filteredQuery(matchAllQuery(), geoDistanceFilter("location").distance("2km").point(40.7143528, -74.0059731)))
.execute().actionGet();