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) { MILES(3959, 24902) {
@Override public String toString() { @Override public String toString() {
return "miles"; return "miles";
}@Override public double toMiles(double distance) { }
@Override public double toMiles(double distance) {
return distance; return distance;
}@Override public double toKilometers(double distance) { }
@Override public double toKilometers(double distance) {
return distance * MILES_KILOMETRES_RATIO; return distance * MILES_KILOMETRES_RATIO;
} }
@Override public String toString(double distance) { @Override public String toString(double distance) {
return distance + "mi"; return distance + "mi";
}}, }
},
KILOMETERS(6371, 40076) { KILOMETERS(6371, 40076) {
@Override public String toString() { @Override public String toString() {
return "km"; return "km";
}@Override public double toMiles(double distance) { }
@Override public double toMiles(double distance) {
return distance / MILES_KILOMETRES_RATIO; return distance / MILES_KILOMETRES_RATIO;
}@Override public double toKilometers(double distance) { }
@Override public double toKilometers(double distance) {
return distance; return distance;
} }
@Override public String toString(double distance) { @Override public String toString(double distance) {
return distance + "km"; return distance + "km";
}}; }
};
static final double MILES_KILOMETRES_RATIO = 1.609344; static final double MILES_KILOMETRES_RATIO = 1.609344;
@ -95,10 +107,24 @@ public enum DistanceUnit {
protected final double earthCircumference; protected final double earthCircumference;
protected final double earthRadius; protected final double earthRadius;
protected final double distancePerDegree;
DistanceUnit(double earthRadius, double earthCircumference) { DistanceUnit(double earthRadius, double earthCircumference) {
this.earthCircumference = earthCircumference; this.earthCircumference = earthCircumference;
this.earthRadius = earthRadius; 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); public abstract double toMiles(double distance);

View File

@ -39,6 +39,18 @@ public class GeoPointDocFieldData extends DocFieldData<GeoPointFieldData> {
return fieldData.values(docId); 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) { public double distance(double lat, double lon) {
return fieldData.distance(docId, DistanceUnit.MILES, lat, 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); 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) { public double distanceGeohash(int docId, DistanceUnit unit, String geoHash) {
GeoPointHash geoPointHash = geoHashCache.get().get(); GeoPointHash geoPointHash = geoHashCache.get().get();
if (geoPointHash.geoHash != geoHash) { if (geoPointHash.geoHash != geoHash) {

View File

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

View File

@ -205,11 +205,13 @@ public class GeoDistanceRangeFilterParser implements FilterParser {
} else { } else {
from = DistanceUnit.parse((String) vFrom, unit, DistanceUnit.MILES); from = DistanceUnit.parse((String) vFrom, unit, DistanceUnit.MILES);
} }
from = geoDistance.normalize(from, DistanceUnit.MILES);
if (vTo instanceof Number) { if (vTo instanceof Number) {
to = unit.toMiles(((Number) vTo).doubleValue()); to = unit.toMiles(((Number) vTo).doubleValue());
} else { } else {
to = DistanceUnit.parse((String) vTo, unit, DistanceUnit.MILES); to = DistanceUnit.parse((String) vTo, unit, DistanceUnit.MILES);
} }
to = geoDistance.normalize(to, DistanceUnit.MILES);
MapperService mapperService = parseContext.mapperService(); MapperService mapperService = parseContext.mapperService();
FieldMapper mapper = mapperService.smartNameFieldMapper(fieldName); 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}. * Calculates distance as points on a plane. Faster, but less accurate than {@link #ARC}.
*/ */
PLANE() { 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) { @Override public double calculate(double sourceLatitude, double sourceLongitude, double targetLatitude, double targetLongitude, DistanceUnit unit) {
double px = targetLongitude - sourceLongitude; double px = targetLongitude - sourceLongitude;
double py = targetLatitude - sourceLatitude; double py = targetLatitude - sourceLatitude;
double distanceMiles = Math.sqrt(px * px + py * py) * DISTANCE_PER_DEGREE; return Math.sqrt(px * px + py * py) * unit.getDistancePerDegree();
return DistanceUnit.convert(distanceMiles, DistanceUnit.MILES, unit); }
@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() { ARC() {
@Override public double calculate(double sourceLatitude, double sourceLongitude, double targetLatitude, double targetLongitude, DistanceUnit unit) { @Override public double calculate(double sourceLatitude, double sourceLongitude, double targetLatitude, double targetLongitude, DistanceUnit unit) {
LatLng sourcePoint = new LatLng(sourceLatitude, sourceLongitude); // TODO: we might want to normalize longitude as we did in LatLng...
LatLng targetPoint = new LatLng(targetLatitude, targetLongitude); double longitudeDifference = targetLongitude - sourceLongitude;
return DistanceUnit.convert(sourcePoint.arcDistance(targetPoint, DistanceUnit.MILES), DistanceUnit.MILES, unit); 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); public abstract double calculate(double sourceLatitude, double sourceLongitude, double targetLatitude, double targetLongitude, DistanceUnit unit);
@ -59,6 +92,8 @@ public enum GeoDistance {
return PLANE; return PLANE;
} else if ("arc".equals(s)) { } else if ("arc".equals(s)) {
return ARC; return ARC;
} else if ("factor".equals(s)) {
return FACTOR;
} }
throw new ElasticSearchIllegalArgumentException("No geo distance for [" + s + "]"); 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.action.search.SearchResponse;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.search.geo.GeoDistance;
import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder; 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"))); 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 searchResponse = client.prepareSearch() // from NY
.setQuery(filteredQuery(matchAllQuery(), geoDistanceFilter("location").distance("2km").point(40.7143528, -74.0059731))) .setQuery(filteredQuery(matchAllQuery(), geoDistanceFilter("location").distance("2km").point(40.7143528, -74.0059731)))
.execute().actionGet(); .execute().actionGet();