Index: contrib/spatial/src/test/org/apache/lucene/spatial/tier/TestCartesianShapeFilter.java =================================================================== --- contrib/spatial/src/test/org/apache/lucene/spatial/tier/TestCartesianShapeFilter.java (révision 930484) +++ contrib/spatial/src/test/org/apache/lucene/spatial/tier/TestCartesianShapeFilter.java (copie de travail) @@ -30,7 +30,7 @@ public class TestCartesianShapeFilter extends TestCase { public void testSerializable() throws IOException { - CartesianShapeFilter filter = new CartesianShapeFilter(new Shape("1"), + CartesianShapeFilter filter = new CartesianShapeFilter(new Shape(1), "test"); try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); Index: contrib/spatial/src/java/org/apache/lucene/spatial/tier/projections/CartesianTierPlotter.java =================================================================== --- contrib/spatial/src/java/org/apache/lucene/spatial/tier/projections/CartesianTierPlotter.java (révision 930484) +++ contrib/spatial/src/java/org/apache/lucene/spatial/tier/projections/CartesianTierPlotter.java (copie de travail) @@ -17,6 +17,8 @@ package org.apache.lucene.spatial.tier.projections; +import org.apache.lucene.spatial.geometry.DistanceUnits; + /** *
NOTE: This API is still in
* flux and might change in incompatible ways in the next
@@ -32,118 +34,115 @@
final IProjector projector;
final String fieldPrefix;
Double idd = Double.valueOf(180);
-
- public CartesianTierPlotter (int tierLevel, IProjector projector, String fieldPrefix) {
-
- this.tierLevel = tierLevel;
+ private static final double LOG_2 = Math.log(2);
+
+ public CartesianTierPlotter(int tierLevel, IProjector projector,
+ String fieldPrefix) {
+
+ this.tierLevel = tierLevel;
this.projector = projector;
this.fieldPrefix = fieldPrefix;
-
+
setTierLength();
setTierBoxes();
setTierVerticalPosDivider();
}
-
- private void setTierLength (){
- this.tierLength = (int) Math.pow(2 , this.tierLevel);
+
+ public CartesianTierPlotter(double radius, IProjector projector,
+ String fieldPrefix) {
+ this(CartesianTierPlotter.bestFit(radius), projector, fieldPrefix);
}
-
- private void setTierBoxes () {
- this.tierBoxes = (int)Math.pow(this.tierLength, 2);
+
+ private void setTierLength() {
+ this.tierLength = (int) Math.pow(2, this.tierLevel);
}
-
+
+ private void setTierBoxes() {
+ this.tierBoxes = (int) Math.pow(this.tierLength, 2);
+ }
+
/**
- * Get nearest max power of 10 greater than
- * the tierlen
- * e.g
- * tierId of 13 has tierLen 8192
- * nearest max power of 10 greater than tierLen
- * would be 10,000
+ * Get nearest max power of 10 greater than the tierlen e.g tierId of 13 has
+ * tierLen 8192 nearest max power of 10 greater than tierLen would be 10,000
*/
-
+
private void setTierVerticalPosDivider() {
-
+
// ceiling of log base 10 of tierLen
-
- tierVerticalPosDivider = Double.valueOf(Math.ceil(
- Math.log10(Integer.valueOf(this.tierLength).doubleValue()))).intValue();
-
+
+ tierVerticalPosDivider = Double.valueOf(
+ Math.ceil(Math.log10(Integer.valueOf(this.tierLength).doubleValue())))
+ .intValue();
+
//
- tierVerticalPosDivider = (int)Math.pow(10, tierVerticalPosDivider );
-
+ tierVerticalPosDivider = (int) Math.pow(10, tierVerticalPosDivider);
+
}
-
- public double getTierVerticalPosDivider(){
+
+ public double getTierVerticalPosDivider() {
return tierVerticalPosDivider;
}
-
+
/**
- * TierBoxId is latitude box id + longitude box id
- * where latitude box id, and longitude box id are transposed in to position
- * coordinates.
+ * TierBoxId is latitude box id + longitude box id where latitude box id, and
+ * longitude box id are transposed in to position coordinates.
*
* @param latitude
* @param longitude
*/
- public double getTierBoxId (double latitude, double longitude) {
-
+ public double getTierBoxId(double latitude, double longitude) {
+
double[] coords = projector.coords(latitude, longitude);
-
- double id = getBoxId(coords[0]) + (getBoxId(coords[1]) / tierVerticalPosDivider);
- return id ;
+
+ double id = getBoxId(coords[0])
+ + (getBoxId(coords[1]) / tierVerticalPosDivider);
+ return id;
}
-
-
- private double getBoxId (double coord){
-
-
+
+ private double getBoxId(double coord) {
+
return Math.floor(coord / (idd / this.tierLength));
}
-
+
@SuppressWarnings("unused")
- private double getBoxId (double coord, int tierLen){
- return Math.floor(coord / (idd / tierLen) );
+ private double getBoxId(double coord, int tierLen) {
+ return Math.floor(coord / (idd / tierLen));
}
+
/**
- * get the string name representing current tier
- * _localTier<tiedId>
+ * get the string name representing current tier _localTier<tiedId>
*/
- public String getTierFieldName (){
-
+ public String getTierFieldName() {
+
return fieldPrefix + this.tierLevel;
}
-
+
/**
- * get the string name representing tierId
- * _localTier<tierId>
+ * get the string name representing tierId _localTier<tierId>
+ *
* @param tierId
*/
- public String getTierFieldName (int tierId){
-
+ public String getTierFieldName(int tierId) {
+
return fieldPrefix + tierId;
}
-
+
/**
- * Find the tier with the best fit for a bounding box
- * Best fit is defined as the ceiling of
- * log2 (circumference of earth / distance)
- * distance is defined as the smallest box fitting
- * the corner between a radius and a bounding box.
- *
- * Distances less than a mile return 15, finer granularity is
- * in accurate
+ * Find the tier with the best fit for a bounding box Best fit is defined as
+ * the ceiling of log2 (circumference of earth / distance) distance is defined
+ * as the smallest box fitting the corner between a radius and a bounding box.
+ *
+ * Distances less than a mile return 15, finer granularity is in accurate
*/
- public int bestFit(double miles){
-
- //28,892 a rough circumference of the earth
- int circ = 28892;
-
- double r = miles / 2.0;
-
- double corner = r - Math.sqrt(Math.pow(r, 2) / 2.0d);
- double times = circ / corner;
- int bestFit = (int)Math.ceil(log2(times)) + 1;
-
+ static public int bestFit(double range) {
+ return bestFit(range, DistanceUnits.MILES);
+ }
+
+ static public int bestFit(double range, DistanceUnits distanceUnit) {
+ double times = distanceUnit.earthCircumference() / (2.0d * range);
+
+ int bestFit = (int) Math.ceil(log2(times));
+
if (bestFit > 15) {
// 15 is the granularity of about 1 mile
// finer granularity isn't accurate with standard java math
@@ -151,14 +150,25 @@
}
return bestFit;
}
-
+
/**
- * a log to the base 2 formula
- * NOTE: This API is still in
- * flux and might change in incompatible ways in the next
- * release.
+ *
+ * NOTE: This API is still in flux and might change in
+ * incompatible ways in the next release.
*/
public class CartesianPolyFilterBuilder {
// Finer granularity than 1 mile isn't accurate with
- // standard java math. Also, there's already a 2nd
+ // standard java math. Also, there's already a 2nd
// precise filter, if needed, in DistanceQueryBuilder,
// that will make the filtering exact.
public static final double MILES_FLOOR = 1.0;
private IProjector projector = new SinusoidalProjector();
private final String tierPrefix;
- private int minTier;
- private int maxTier;
+ private int minTier;
+ private int maxTier;
+
/**
*
- * @param tierPrefix The prefix for the name of the fields containing the tier info
- * @param minTierIndexed The minimum tier level indexed
- * @param maxTierIndexed The maximum tier level indexed
+ * @param tierPrefix
+ * The prefix for the name of the fields containing the tier info
+ * @param minTierIndexed
+ * The minimum tier level indexed
+ * @param maxTierIndexed
+ * The maximum tier level indexed
*/
- public CartesianPolyFilterBuilder( String tierPrefix, int minTierIndexed, int maxTierIndexed ) {
+ public CartesianPolyFilterBuilder(String tierPrefix, int minTierIndexed,
+ int maxTierIndexed) {
this.tierPrefix = tierPrefix;
- this.minTier = minTierIndexed;
- this.maxTier = maxTierIndexed;
+ this.minTier = minTierIndexed;
+ this.maxTier = maxTierIndexed;
}
-
- public Shape getBoxShape(double latitude, double longitude, double miles)
- {
+
+ public Shape getBoxShape(double latitude, double longitude, double miles) {
if (miles < MILES_FLOOR) {
miles = MILES_FLOOR;
}
- LLRect box1 = LLRect.createBox( new FloatLatLng( latitude, longitude ), miles, miles );
- LatLng ll = box1.getLowerLeft();
- LatLng ur = box1.getUpperRight();
+ LLRect box1 = LLRect.createBox(new FloatLatLng(latitude, longitude), miles,
+ miles);
+ LatLng lowerLeft = box1.getLowerLeft();
+ LatLng upperRight = box1.getUpperRight();
- double latY = ur.getLat();
- double latX = ll.getLat();
- double longY = ur.getLng();
- double longX = ll.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;
+ double latUpperRight = upperRight.getLat();
+ double latLowerLeft = lowerLeft.getLat();
+ double longUpperRight = upperRight.getLng();
+ double longLowerLeft = lowerLeft.getLng();
+
+ CartesianTierPlotter ctp = new CartesianTierPlotter(miles, projector,
+ tierPrefix);
+ Shape shape = new Shape(ctp.getTierLevelId());
+
+ if (longUpperRight < longLowerLeft) { // Box cross the 180 meridian
+ addBoxes(shape, ctp, latLowerLeft, longLowerLeft, latUpperRight,
+ LatLng.LONGITUDE_DEGREE_RANGE / 2);
+ addBoxes(shape, ctp, latLowerLeft, -LatLng.LONGITUDE_DEGREE_RANGE / 2,
+ latUpperRight, longUpperRight);
+ } else {
+ addBoxes(shape, ctp, latLowerLeft, longLowerLeft, latUpperRight,
+ longUpperRight);
}
- //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;
+
+ return shape;
+ }
+
+ 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);
+
+ double tierVert = tierPlotter.getTierVerticalPosDivider();
+
+ int LongIndex1 = (int) Math.round(boxId1);
+ int LatIndex1 = (int) Math.round((boxId1 - LongIndex1) * tierVert);
+
+ int LongIndex2 = (int) Math.round(boxId2);
+ int LatIndex2 = (int) Math.round((boxId2 - LongIndex2) * tierVert);
+
+ int startLong, endLong;
+ int startLat, endLat;
+
+ if (LongIndex1 > LongIndex2) {
+ startLong = LongIndex2;
+ endLong = LongIndex1;
+ } else {
+ startLong = LongIndex1;
+ endLong = LongIndex2;
}
-
- //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);
- //System.err.println("getBoxShape2:"+latX+"," + longX);
- }
-
-
- return shape;
- }
-
- public Shape getShapeLoop(Shape shape, CartesianTierPlotter ctp, double latX, double longX, double latY, double longY)
- {
-
- //System.err.println("getShapeLoop:"+latY+"," + longY);
- //System.err.println("getShapeLoop:"+latX+"," + longX);
- double beginAt = ctp.getTierBoxId(latX, longX);
- double endAt = ctp.getTierBoxId(latY, longY);
- if (beginAt > endAt){
- double tmp = beginAt;
- beginAt = endAt;
- endAt = tmp;
- }
- double tierVert = ctp.getTierVerticalPosDivider();
- //System.err.println(" | "+ beginAt+" | "+ endAt);
-
- double startX = beginAt - (beginAt %1);
- double startY = beginAt - startX ; //should give a whole number
-
- double endX = endAt - (endAt %1);
- double endY = endAt -endX; //should give a whole number
-
- 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();
- double xInc = 1.0d / tierVert;
- xInc = new BigDecimal(xInc).setScale(scale, RoundingMode.HALF_EVEN).doubleValue();
-
- //System.err.println("go from startX:"+startX+" to:" + endX);
- for (; startX <= endX; startX++){
-
- double itY = startY;
- //System.err.println("go from startY:"+startY+" to:" + endY);
- while (itY <= endY){
- //create a boxId
- // startX.startY
- double boxId = startX + itY ;
+ if (LatIndex1 > LatIndex2) {
+ startLat = LatIndex2;
+ endLat = LatIndex1;
+ } else {
+ startLat = LatIndex1;
+ endLat = LatIndex2;
+ }
+
+ int LatIndex, LongIndex;
+ for (LongIndex = startLong; LongIndex <= endLong; LongIndex++) {
+ for (LatIndex = startLat; LatIndex <= endLat; LatIndex++) {
+ // create a boxId
+ double boxId = LongIndex + LatIndex / tierVert;
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);
- return new CartesianShapeFilter(shape, shape.getTierId());
+ return new CartesianShapeFilter(shape, tierPrefix + shape.getTierId());
}
}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/geometry/LatLng.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/geometry/LatLng.java (révision 930484)
+++ contrib/spatial/src/java/org/apache/lucene/spatial/geometry/LatLng.java (copie de travail)
@@ -30,6 +30,13 @@
*/
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 abstract boolean isNormalized();
public abstract boolean isFixedPoint();
Math.log(value) / Math.log(2)
+ * Computes log to base 2 of the given value
+ *
* @param value
+ * Value to compute the log of
+ * @return Log_2 of the value
*/
- public double log2(double value) {
-
- return Math.log(value) / Math.log(2);
+ public static double log2(double value) {
+ return Math.log(value) / 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;
+ }
+
}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/tier/Shape.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/tier/Shape.java (révision 930484)
+++ contrib/spatial/src/java/org/apache/lucene/spatial/tier/Shape.java (copie de travail)
@@ -29,9 +29,9 @@
public class Shape implements Serializable{
private List