mirror of https://github.com/apache/lucene.git
LUCENE-2366, LUCENE-2367, LUCENE-2359: Fix a bunch of issues related to spatial
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@933754 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
a0de834403
commit
994189c221
|
@ -66,7 +66,11 @@ Bug fixes
|
||||||
not an indexed field. Note, this change affects the APIs. (Grant Ingersoll)
|
not an indexed field. Note, this change affects the APIs. (Grant Ingersoll)
|
||||||
|
|
||||||
* LUCENE-2359: Fix bug in CartesianPolyFilterBuilder related to handling of behavior around
|
* LUCENE-2359: Fix bug in CartesianPolyFilterBuilder related to handling of behavior around
|
||||||
the 180th meridian (Grant Ingersoll)
|
the 180th meridian (Grant Ingersoll, Nicolas Hellinger)
|
||||||
|
|
||||||
|
* LUCENE-2366: Fix LLRect issue with properly calculating box points (Nicolas Hellinger via Grant Ingersoll)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
API Changes
|
API Changes
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,19 @@ package org.apache.lucene.spatial.geometry;
|
||||||
*/
|
*/
|
||||||
public abstract class LatLng {
|
public abstract class LatLng {
|
||||||
|
|
||||||
|
public final static int LONGITUDE_DEGREE_RANGE = 360;
|
||||||
|
public final static int LONGITUDE_DEGREE_MIN = -LONGITUDE_DEGREE_RANGE / 2;
|
||||||
|
public final static int LONGITUDE_DEGREE_MAX = LONGITUDE_DEGREE_RANGE / 2;
|
||||||
|
public final static int LATITUDE_DEGREE_RANGE = 180;
|
||||||
|
public final static int LATITUDE_DEGREE_MIN = -LATITUDE_DEGREE_RANGE / 2;
|
||||||
|
public final static int LATITUDE_DEGREE_MAX = LATITUDE_DEGREE_RANGE / 2;
|
||||||
|
|
||||||
|
public final static int HEADING_NORTH= 0;
|
||||||
|
public final static int HEADING_SOUTH= 180;
|
||||||
|
public final static int HEADING_EAST= 90;
|
||||||
|
public final static int HEADING_WEST= 270;
|
||||||
|
|
||||||
|
|
||||||
public abstract boolean isNormalized();
|
public abstract boolean isNormalized();
|
||||||
|
|
||||||
public abstract boolean isFixedPoint();
|
public abstract boolean isFixedPoint();
|
||||||
|
@ -69,6 +82,47 @@ public abstract class LatLng {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static LatLng computeDestination(LatLng startPoint, double distance,
|
||||||
|
double heading) {
|
||||||
|
return computeDestination(startPoint, distance, heading,
|
||||||
|
DistanceUnits.MILES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method for calculating a target point given a start, a heading (in degrees), a distance and a distance unit
|
||||||
|
* @param startPoint
|
||||||
|
* beginning point of the path
|
||||||
|
* @param distance
|
||||||
|
* distance to be travel from the starting point
|
||||||
|
* @param heading
|
||||||
|
* heading in degrees to follow during the travel
|
||||||
|
* @param distanceUnit
|
||||||
|
* unit of the distance to to travel
|
||||||
|
* @return arrival point of the described travel
|
||||||
|
*/
|
||||||
|
public static LatLng computeDestination(LatLng startPoint, double distance, double heading, DistanceUnits distanceUnit) {
|
||||||
|
double startPointLatitude = startPoint.getLat();
|
||||||
|
double startPointLongitude = startPoint.getLng();
|
||||||
|
double earthRadius = distanceUnit.earthRadius();
|
||||||
|
double headingRad = Math.toRadians(heading);
|
||||||
|
double startPointLatitudeRad = Math.toRadians(startPointLatitude);
|
||||||
|
double startPointLongitudeRad = Math.toRadians(startPointLongitude);
|
||||||
|
|
||||||
|
// Haversine formula (http://www.movable-type.co.uk/scripts/latlong.html)
|
||||||
|
double destinationLatitudeRad = Math.asin(Math.sin(startPointLatitudeRad)
|
||||||
|
* Math.cos(distance / earthRadius) + Math.cos(startPointLatitudeRad)
|
||||||
|
* Math.sin(distance / earthRadius) * Math.cos(headingRad));
|
||||||
|
|
||||||
|
double destinationLongitudeRad = startPointLongitudeRad
|
||||||
|
+ Math.atan2(Math.sin(headingRad) * Math.sin(distance / earthRadius)
|
||||||
|
* Math.cos(startPointLatitudeRad), Math.cos(distance / earthRadius)
|
||||||
|
- Math.sin(startPointLatitudeRad)
|
||||||
|
* Math.sin(destinationLatitudeRad));
|
||||||
|
|
||||||
|
return new FloatLatLng(Math.toDegrees(destinationLatitudeRad), Math
|
||||||
|
.toDegrees(destinationLongitudeRad));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The inverse of toCartesian(). Always returns a FixedLatLng.
|
* The inverse of toCartesian(). Always returns a FixedLatLng.
|
||||||
* @param pt
|
* @param pt
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.apache.lucene.spatial.geometry.shape;
|
package org.apache.lucene.spatial.geometry.shape;
|
||||||
|
|
||||||
|
import org.apache.lucene.spatial.geometry.DistanceUnits;
|
||||||
import org.apache.lucene.spatial.geometry.FloatLatLng;
|
import org.apache.lucene.spatial.geometry.FloatLatLng;
|
||||||
import org.apache.lucene.spatial.geometry.LatLng;
|
import org.apache.lucene.spatial.geometry.LatLng;
|
||||||
|
|
||||||
|
@ -74,14 +75,34 @@ public class LLRect {
|
||||||
* @param heightMi
|
* @param heightMi
|
||||||
*/
|
*/
|
||||||
public static LLRect createBox(LatLng center, double widthMi, double heightMi) {
|
public static LLRect createBox(LatLng center, double widthMi, double heightMi) {
|
||||||
double d = widthMi;
|
double minLat;
|
||||||
LatLng ur = boxCorners(center, d, 45.0); // assume right angles
|
double maxLat;
|
||||||
LatLng ll = boxCorners(center, d, 225.0);
|
double minLng;
|
||||||
|
double maxLng;
|
||||||
|
double radius= Math.max(widthMi, heightMi);
|
||||||
|
|
||||||
|
if (radius > center.arcDistance(new FloatLatLng(LatLng.LATITUDE_DEGREE_MAX, LatLng.HEADING_NORTH))) {
|
||||||
|
maxLat = LatLng.LATITUDE_DEGREE_MAX;
|
||||||
|
} else {
|
||||||
|
maxLat = LatLng.computeDestination(center, radius, LatLng.HEADING_NORTH).getLat();
|
||||||
|
|
||||||
|
}
|
||||||
|
if (radius > center.arcDistance(new FloatLatLng(LatLng.LATITUDE_DEGREE_MIN, LatLng.HEADING_NORTH))) {
|
||||||
|
minLat = LatLng.LATITUDE_DEGREE_MIN;
|
||||||
|
} else {
|
||||||
|
minLat = LatLng.computeDestination(center, radius, LatLng.HEADING_SOUTH).getLat();
|
||||||
|
}
|
||||||
|
|
||||||
//System.err.println("boxCorners: ur " + ur.getLat() + ',' + ur.getLng());
|
if((radius > 2 * Math.PI * DistanceUnits.MILES.earthRadius() * Math.cos(Math.toRadians(minLat))) ||
|
||||||
//System.err.println("boxCorners: cnt " + center.getLat() + ',' + center.getLng());
|
(radius > 2 * Math.PI * DistanceUnits.MILES.earthRadius() * Math.cos(Math.toRadians(maxLat)))) {
|
||||||
//System.err.println("boxCorners: ll " + ll.getLat() + ',' + ll.getLng());
|
maxLng = LatLng.LONGITUDE_DEGREE_MAX;
|
||||||
return new LLRect(ll, ur);
|
minLng = LatLng.LONGITUDE_DEGREE_MIN;
|
||||||
|
} else {
|
||||||
|
maxLng = LatLng.computeDestination(new FloatLatLng(Math.max(Math.abs(minLat), Math.abs(maxLat)), center.getLng()), radius, LatLng.HEADING_EAST).getLng();
|
||||||
|
minLng = LatLng.computeDestination(new FloatLatLng(Math.max(Math.abs(minLat), Math.abs(maxLat)), center.getLng()), radius, LatLng.HEADING_WEST).getLng();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LLRect((new FloatLatLng(minLat, minLng).normalize()), (new FloatLatLng(maxLat, maxLng)).normalize());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,9 +17,6 @@
|
||||||
|
|
||||||
package org.apache.lucene.spatial.tier;
|
package org.apache.lucene.spatial.tier;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.math.RoundingMode;
|
|
||||||
|
|
||||||
import org.apache.lucene.search.Filter;
|
import org.apache.lucene.search.Filter;
|
||||||
import org.apache.lucene.spatial.tier.projections.CartesianTierPlotter;
|
import org.apache.lucene.spatial.tier.projections.CartesianTierPlotter;
|
||||||
import org.apache.lucene.spatial.tier.projections.IProjector;
|
import org.apache.lucene.spatial.tier.projections.IProjector;
|
||||||
|
@ -64,121 +61,71 @@ public class CartesianPolyFilterBuilder {
|
||||||
miles = MILES_FLOOR;
|
miles = MILES_FLOOR;
|
||||||
}
|
}
|
||||||
LLRect box1 = LLRect.createBox( new FloatLatLng( latitude, longitude ), miles, miles );
|
LLRect box1 = LLRect.createBox( new FloatLatLng( latitude, longitude ), miles, miles );
|
||||||
LatLng ll = box1.getLowerLeft();
|
LatLng lowerLeft = box1.getLowerLeft();
|
||||||
LatLng ur = box1.getUpperRight();
|
LatLng upperRight = box1.getUpperRight();
|
||||||
|
|
||||||
double latY = ur.getLat();
|
double latUpperRight = upperRight.getLat();
|
||||||
double latX = ll.getLat();
|
double latLowerLeft = lowerLeft.getLat();
|
||||||
double longY = ur.getLng();
|
double longUpperRight = upperRight.getLng();
|
||||||
double longX = ll.getLng();
|
double longLowerLeft = lowerLeft.getLng();
|
||||||
double longX2 = 0.0;
|
|
||||||
//These two if checks setup us up to deal with issues around the prime meridian and the 180th meridian
|
|
||||||
//In these two cases, we need to get tiles (tiers) from the lower left up to the meridian and then
|
|
||||||
//from the meridan to the upper right
|
|
||||||
//Are we crossing the 180 deg. longitude, if so, we need to do some special things
|
|
||||||
if (ur.getLng() < 0.0 && ll.getLng() > 0.0) {
|
|
||||||
longX2 = ll.getLng();
|
|
||||||
longX = -180.0;
|
|
||||||
}
|
|
||||||
//are we crossing the prime meridian (0 degrees)? If so, we need to account for it and boxes on both sides
|
|
||||||
if (ur.getLng() > 0.0 && ll.getLng() < 0.0) {
|
|
||||||
longX2 = ll.getLng();
|
|
||||||
longX = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//System.err.println("getBoxShape:"+latY+"," + longY);
|
|
||||||
//System.err.println("getBoxShape:"+latX+"," + longX);
|
|
||||||
CartesianTierPlotter ctp = new CartesianTierPlotter(2, projector,tierPrefix);
|
|
||||||
int bestFit = ctp.bestFit(miles);
|
|
||||||
if (bestFit < minTier){
|
|
||||||
bestFit = minTier;
|
|
||||||
} else if (bestFit > maxTier){
|
|
||||||
bestFit = maxTier;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctp = new CartesianTierPlotter(bestFit, projector,tierPrefix);
|
|
||||||
Shape shape = new Shape(ctp.getTierFieldName());
|
|
||||||
|
|
||||||
// generate shape
|
|
||||||
// iterate from startX->endX
|
|
||||||
// iterate from startY -> endY
|
|
||||||
// shape.add(currentLat.currentLong);
|
|
||||||
//for the edge cases (prime meridian and the 180th meridian), this call handles all tiles East of the meridian
|
|
||||||
//for all other cases, it handles the whole set of tiles
|
|
||||||
shape = getShapeLoop(shape,ctp,latX,longX,latY,longY);
|
|
||||||
if (longX2 != 0.0) {
|
|
||||||
if (longX == 0.0) {
|
|
||||||
longX = longX2;
|
|
||||||
longY = 0.0;
|
|
||||||
//handles the lower left longitude to the prime meridian
|
|
||||||
//shape = getShapeLoop(shape, ctp, latX, longX, latY, longY);
|
|
||||||
} else {
|
|
||||||
//this clause handles the lower left longitude up to the 180 meridian
|
|
||||||
longX = longX2;
|
|
||||||
longY = 180.0;
|
|
||||||
}
|
|
||||||
shape = getShapeLoop(shape, ctp, latX, longX, latY, longY);
|
|
||||||
|
|
||||||
//System.err.println("getBoxShape2:"+latY+"," + longY);
|
CartesianTierPlotter ctp = new CartesianTierPlotter( miles, projector, tierPrefix );
|
||||||
//System.err.println("getBoxShape2:"+latX+"," + longX);
|
Shape shape = new Shape(ctp.getTierLevelId());
|
||||||
}
|
|
||||||
|
if (longUpperRight < longLowerLeft) { // Box cross the 180 meridian
|
||||||
|
addBoxes(shape, ctp, latLowerLeft, longLowerLeft, latUpperRight, LatLng.LONGITUDE_DEGREE_MAX);
|
||||||
|
addBoxes(shape, ctp, latLowerLeft, -LatLng.LONGITUDE_DEGREE_MIN, latUpperRight, longUpperRight);
|
||||||
|
} else {
|
||||||
|
addBoxes(shape, ctp, latLowerLeft, longLowerLeft, latUpperRight, longUpperRight);
|
||||||
|
}
|
||||||
|
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Shape getShapeLoop(Shape shape, CartesianTierPlotter ctp, double latX, double longX, double latY, double longY)
|
private void addBoxes(Shape shape, CartesianTierPlotter tierPlotter, double lat1, double long1, double lat2, double long2) {
|
||||||
{
|
double boxId1 = tierPlotter.getTierBoxId(lat1, long1);
|
||||||
|
double boxId2 = tierPlotter.getTierBoxId(lat2, long2);
|
||||||
//System.err.println("getShapeLoop:"+latY+"," + longY);
|
|
||||||
//System.err.println("getShapeLoop:"+latX+"," + longX);
|
double tierVert = tierPlotter.getTierVerticalPosDivider();
|
||||||
double beginAt = ctp.getTierBoxId(latX, longX);
|
|
||||||
double endAt = ctp.getTierBoxId(latY, longY);
|
int LongIndex1 = (int) Math.round(boxId1);
|
||||||
if (beginAt > endAt){
|
int LatIndex1 = (int) Math.round((boxId1 - LongIndex1) * tierVert);
|
||||||
double tmp = beginAt;
|
|
||||||
beginAt = endAt;
|
int LongIndex2 = (int) Math.round(boxId2);
|
||||||
endAt = tmp;
|
int LatIndex2 = (int) Math.round((boxId2 - LongIndex2) * tierVert);
|
||||||
}
|
|
||||||
double tierVert = ctp.getTierVerticalPosDivider();
|
int startLong, endLong;
|
||||||
//System.err.println(" | "+ beginAt+" | "+ endAt);
|
int startLat, endLat;
|
||||||
|
|
||||||
double startX = beginAt - (beginAt %1);
|
if (LongIndex1 > LongIndex2) {
|
||||||
double startY = beginAt - startX ; //should give a whole number
|
startLong = LongIndex2;
|
||||||
|
endLong = LongIndex1;
|
||||||
double endX = endAt - (endAt %1);
|
} else {
|
||||||
double endY = endAt -endX; //should give a whole number
|
startLong = LongIndex1;
|
||||||
|
endLong = LongIndex2;
|
||||||
int scale = (int)Math.log10(tierVert);
|
}
|
||||||
endY = new BigDecimal(endY).setScale(scale, RoundingMode.HALF_EVEN).doubleValue();
|
|
||||||
startY = new BigDecimal(startY).setScale(scale, RoundingMode.HALF_EVEN).doubleValue();
|
if (LatIndex1 > LatIndex2) {
|
||||||
double xInc = 1.0d / tierVert;
|
startLat = LatIndex2;
|
||||||
xInc = new BigDecimal(xInc).setScale(scale, RoundingMode.HALF_EVEN).doubleValue();
|
endLat = LatIndex1;
|
||||||
|
} else {
|
||||||
//System.err.println("go from startX:"+startX+" to:" + endX);
|
startLat = LatIndex1;
|
||||||
for (; startX <= endX; startX++){
|
endLat = LatIndex2;
|
||||||
|
}
|
||||||
double itY = startY;
|
|
||||||
//System.err.println("go from startY:"+startY+" to:" + endY);
|
int LatIndex, LongIndex;
|
||||||
while (itY <= endY){
|
for (LongIndex = startLong; LongIndex <= endLong; LongIndex++) {
|
||||||
//create a boxId
|
for (LatIndex = startLat; LatIndex <= endLat; LatIndex++) {
|
||||||
// startX.startY
|
// create a boxId
|
||||||
double boxId = startX + itY ;
|
double boxId = LongIndex + LatIndex / tierVert;
|
||||||
shape.addBox(boxId);
|
shape.addBox(boxId);
|
||||||
//System.err.println("----"+startX+" and "+itY);
|
|
||||||
//System.err.println("----"+boxId);
|
|
||||||
itY += xInc;
|
|
||||||
|
|
||||||
// java keeps 0.0001 as 1.0E-1
|
|
||||||
// which ends up as 0.00011111
|
|
||||||
itY = new BigDecimal(itY).setScale(scale, RoundingMode.HALF_EVEN).doubleValue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return shape;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Filter getBoundingArea(double latitude, double longitude, double miles)
|
public Filter getBoundingArea(double latitude, double longitude, double miles)
|
||||||
{
|
{
|
||||||
Shape shape = getBoxShape(latitude, longitude, miles);
|
Shape shape = getBoxShape(latitude, longitude, miles);
|
||||||
return new CartesianShapeFilter(shape, shape.getTierId());
|
return new CartesianShapeFilter(shape, tierPrefix + shape.getTierId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,9 @@ import java.util.List;
|
||||||
public class Shape implements Serializable{
|
public class Shape implements Serializable{
|
||||||
|
|
||||||
private List<Double> area = new ArrayList<Double>();
|
private List<Double> area = new ArrayList<Double>();
|
||||||
private String tierId;
|
private int tierId;
|
||||||
|
|
||||||
public Shape (String tierId){
|
public Shape (int tierId){
|
||||||
this.tierId = tierId;
|
this.tierId = tierId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ public class Shape implements Serializable{
|
||||||
return area;
|
return area;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTierId(){
|
public int getTierId(){
|
||||||
return tierId;
|
return tierId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
package org.apache.lucene.spatial.tier.projections;
|
package org.apache.lucene.spatial.tier.projections;
|
||||||
|
|
||||||
|
import org.apache.lucene.spatial.geometry.DistanceUnits;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p><font color="red"><b>NOTE:</b> This API is still in
|
* <p><font color="red"><b>NOTE:</b> This API is still in
|
||||||
* flux and might change in incompatible ways in the next
|
* flux and might change in incompatible ways in the next
|
||||||
|
@ -33,6 +35,8 @@ public class CartesianTierPlotter {
|
||||||
final String fieldPrefix;
|
final String fieldPrefix;
|
||||||
Double idd = Double.valueOf(180);
|
Double idd = Double.valueOf(180);
|
||||||
|
|
||||||
|
private static final double LOG_2 = Math.log(2);
|
||||||
|
|
||||||
public CartesianTierPlotter (int tierLevel, IProjector projector, String fieldPrefix) {
|
public CartesianTierPlotter (int tierLevel, IProjector projector, String fieldPrefix) {
|
||||||
|
|
||||||
this.tierLevel = tierLevel;
|
this.tierLevel = tierLevel;
|
||||||
|
@ -43,6 +47,11 @@ public class CartesianTierPlotter {
|
||||||
setTierBoxes();
|
setTierBoxes();
|
||||||
setTierVerticalPosDivider();
|
setTierVerticalPosDivider();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CartesianTierPlotter(double radius, IProjector projector,
|
||||||
|
String fieldPrefix) {
|
||||||
|
this(CartesianTierPlotter.bestFit(radius), projector, fieldPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
private void setTierLength (){
|
private void setTierLength (){
|
||||||
this.tierLength = (int) Math.pow(2 , this.tierLevel);
|
this.tierLength = (int) Math.pow(2 , this.tierLevel);
|
||||||
|
@ -133,17 +142,15 @@ public class CartesianTierPlotter {
|
||||||
* Distances less than a mile return 15, finer granularity is
|
* Distances less than a mile return 15, finer granularity is
|
||||||
* in accurate
|
* in accurate
|
||||||
*/
|
*/
|
||||||
public int bestFit(double miles){
|
static public int bestFit(double range) {
|
||||||
|
return bestFit(range, DistanceUnits.MILES);
|
||||||
//28,892 a rough circumference of the earth
|
}
|
||||||
int circ = 28892;
|
|
||||||
|
static public int bestFit(double range, DistanceUnits distanceUnit) {
|
||||||
double r = miles / 2.0;
|
double times = distanceUnit.earthCircumference() / (2.0d * range);
|
||||||
|
|
||||||
double corner = r - Math.sqrt(Math.pow(r, 2) / 2.0d);
|
int bestFit = (int) Math.ceil(log2(times));
|
||||||
double times = circ / corner;
|
|
||||||
int bestFit = (int)Math.ceil(log2(times)) + 1;
|
|
||||||
|
|
||||||
if (bestFit > 15) {
|
if (bestFit > 15) {
|
||||||
// 15 is the granularity of about 1 mile
|
// 15 is the granularity of about 1 mile
|
||||||
// finer granularity isn't accurate with standard java math
|
// finer granularity isn't accurate with standard java math
|
||||||
|
@ -153,12 +160,23 @@ public class CartesianTierPlotter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a log to the base 2 formula
|
* Computes log to base 2 of the given value
|
||||||
* <code>Math.log(value) / Math.log(2)</code>
|
*
|
||||||
* @param value
|
* @param value
|
||||||
|
* Value to compute the log of
|
||||||
|
* @return Log_2 of the value
|
||||||
*/
|
*/
|
||||||
public double log2(double value) {
|
public static double log2(double value) {
|
||||||
|
return Math.log(value) / LOG_2;
|
||||||
return Math.log(value) / Math.log(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ID of the tier level plotting is occuring at
|
||||||
|
*
|
||||||
|
* @return ID of the tier level plotting is occuring at
|
||||||
|
*/
|
||||||
|
public int getTierLevelId() {
|
||||||
|
return this.tierLevel;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,8 @@ public class TestCartesian extends LuceneTestCase {
|
||||||
addPoint(writer, "Wonga Wongue Reserve, Gabon", -0.546562,9.459229);
|
addPoint(writer, "Wonga Wongue Reserve, Gabon", -0.546562,9.459229);
|
||||||
addPoint(writer,"Midway Island",25.7, -171.7);
|
addPoint(writer,"Midway Island",25.7, -171.7);
|
||||||
addPoint(writer,"North Pole Way",55.0, 4.0);
|
addPoint(writer,"North Pole Way",55.0, 4.0);
|
||||||
|
addPoint(writer,"Close to the North Pole",89.9, 4.0);
|
||||||
|
addPoint(writer,"Close to the North Pole, other side",89.9, -176.0);
|
||||||
|
|
||||||
writer.commit();
|
writer.commit();
|
||||||
writer.close();
|
writer.close();
|
||||||
|
@ -362,8 +364,8 @@ public class TestCartesian extends LuceneTestCase {
|
||||||
System.out.println("Results should be 18 "+ results);
|
System.out.println("Results should be 18 "+ results);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(18, distances.size()); // fixed a store of only needed distances
|
assertEquals(20, distances.size()); // fixed a store of only needed distances
|
||||||
assertEquals(18, results);
|
assertEquals(20, results);
|
||||||
double lastDistance = 0;
|
double lastDistance = 0;
|
||||||
for(int i =0 ; i < results; i++){
|
for(int i =0 ; i < results; i++){
|
||||||
Document d = searcher.doc(scoreDocs[i].doc);
|
Document d = searcher.doc(scoreDocs[i].doc);
|
||||||
|
@ -383,6 +385,103 @@ public class TestCartesian extends LuceneTestCase {
|
||||||
lastDistance = geo_distance;
|
lastDistance = geo_distance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testPoles() throws IOException, InvalidGeoException {
|
||||||
|
searcher = new IndexSearcher(directory, true);
|
||||||
|
|
||||||
|
final double miles = 50.0;
|
||||||
|
lat = 89.99;
|
||||||
|
lng = 4;
|
||||||
|
|
||||||
|
if (VERBOSE) System.out.println("testPoleFlipping");
|
||||||
|
|
||||||
|
// create a distance query
|
||||||
|
final DistanceQueryBuilder dq = new DistanceQueryBuilder(lat, lng, miles,
|
||||||
|
latField, lngField, CartesianTierPlotter.DEFALT_FIELD_PREFIX, true, 2, 15);
|
||||||
|
|
||||||
|
if (VERBOSE) System.out.println(dq);
|
||||||
|
//create a term query to search against all documents
|
||||||
|
Query tq = new TermQuery(new Term("metafile", "doc"));
|
||||||
|
|
||||||
|
FieldScoreQuery fsQuery = new FieldScoreQuery("geo_distance", Type.FLOAT);
|
||||||
|
|
||||||
|
CustomScoreQuery customScore = new CustomScoreQuery(dq.getQuery(tq),fsQuery){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||||
|
return new CustomScoreProvider(reader) {
|
||||||
|
@Override // TODO: broken, as reader is not used!
|
||||||
|
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
||||||
|
if (VERBOSE) System.out.println(doc);
|
||||||
|
if (dq.distanceFilter.getDistance(doc) == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
double distance = dq.distanceFilter.getDistance(doc);
|
||||||
|
// boost score shouldn't exceed 1
|
||||||
|
if (distance < 1.0d)
|
||||||
|
distance = 1.0d;
|
||||||
|
//boost by distance is invertly proportional to
|
||||||
|
// to distance from center point to location
|
||||||
|
float score = (float) ((miles - distance) / miles );
|
||||||
|
return score * subQueryScore;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
// Create a distance sort
|
||||||
|
// As the radius filter has performed the distance calculations
|
||||||
|
// already, pass in the filter to reuse the results.
|
||||||
|
//
|
||||||
|
DistanceFieldComparatorSource dsort = new DistanceFieldComparatorSource(dq.distanceFilter);
|
||||||
|
Sort sort = new Sort(new SortField("foo", dsort,false));
|
||||||
|
|
||||||
|
// Perform the search, using the term query, the serial chain filter, and the
|
||||||
|
// distance sort
|
||||||
|
TopDocs hits = searcher.search(customScore.createWeight(searcher),null, 1000, sort);
|
||||||
|
int results = hits.totalHits;
|
||||||
|
ScoreDoc[] scoreDocs = hits.scoreDocs;
|
||||||
|
|
||||||
|
// Get a list of distances
|
||||||
|
Map<Integer,Double> distances = dq.distanceFilter.getDistances();
|
||||||
|
|
||||||
|
// distances calculated from filter first pass must be less than total
|
||||||
|
// docs, from the above test of 20 items, 12 will come from the boundary box
|
||||||
|
// filter, but only 5 are actually in the radius of the results.
|
||||||
|
|
||||||
|
// Note Boundary Box filtering, is not accurate enough for most systems.
|
||||||
|
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
System.out.println("Distance Filter filtered: " + distances.size());
|
||||||
|
System.out.println("Results: " + results);
|
||||||
|
System.out.println("=============================");
|
||||||
|
System.out.println("Distances should be 18 "+ distances.size());
|
||||||
|
System.out.println("Results should be 18 "+ results);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(2, distances.size()); // fixed a store of only needed distances
|
||||||
|
assertEquals(2, results);
|
||||||
|
double lastDistance = 0;
|
||||||
|
for(int i =0 ; i < results; i++){
|
||||||
|
Document d = searcher.doc(scoreDocs[i].doc);
|
||||||
|
String name = d.get("name");
|
||||||
|
double rsLat = Double.parseDouble(d.get(latField));
|
||||||
|
double rsLng = Double.parseDouble(d.get(lngField));
|
||||||
|
Double geo_distance = distances.get(scoreDocs[i].doc);
|
||||||
|
|
||||||
|
double distance = DistanceUtils.getInstance().getDistanceMi(lat, lng, rsLat, rsLng);
|
||||||
|
double llm = DistanceUtils.getInstance().getLLMDistance(lat, lng, rsLat, rsLng);
|
||||||
|
if (VERBOSE) System.out.println("Name: "+ name +", Distance "+ distance); //(res, ortho, harvesine):"+ distance +" |"+ geo_distance +"|"+ llm +" | score "+ hits.score(i));
|
||||||
|
assertTrue(Math.abs((distance - llm)) < 1);
|
||||||
|
if (VERBOSE) System.out.println("checking limit "+ distance + " < " + miles);
|
||||||
|
assertTrue((distance < miles ));
|
||||||
|
if (VERBOSE) System.out.println("checking sort "+ geo_distance + " >= " + lastDistance);
|
||||||
|
assertTrue(geo_distance >= lastDistance);
|
||||||
|
lastDistance = geo_distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void testRange() throws IOException, InvalidGeoException {
|
public void testRange() throws IOException, InvalidGeoException {
|
||||||
searcher = new IndexSearcher(directory, true);
|
searcher = new IndexSearcher(directory, true);
|
||||||
|
|
|
@ -30,7 +30,7 @@ import junit.framework.TestCase;
|
||||||
public class TestCartesianShapeFilter extends TestCase {
|
public class TestCartesianShapeFilter extends TestCase {
|
||||||
|
|
||||||
public void testSerializable() throws IOException {
|
public void testSerializable() throws IOException {
|
||||||
CartesianShapeFilter filter = new CartesianShapeFilter(new Shape("1"),
|
CartesianShapeFilter filter = new CartesianShapeFilter(new Shape(1),
|
||||||
"test");
|
"test");
|
||||||
try {
|
try {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
|
Loading…
Reference in New Issue