lucene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gsing...@apache.org
Subject svn commit: r962727 [1/2] - in /lucene/dev/trunk: lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geohash/ lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geometry/ lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/ luc...
Date Sat, 10 Jul 2010 00:12:42 GMT
Author: gsingers
Date: Sat Jul 10 00:12:41 2010
New Revision: 962727

URL: http://svn.apache.org/viewvc?rev=962727&view=rev
Log:
SOLR-1568: Spatial filtering support

Added:
    lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/DistanceUtilsTest.java   (with props)
    lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/projections/
    lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/projections/SinusoidalProjectorTest.java   (with props)
    lucene/dev/trunk/solr/src/common/org/apache/solr/common/params/SpatialParams.java   (with props)
    lucene/dev/trunk/solr/src/java/org/apache/solr/schema/LatLonType.java   (with props)
    lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialQueryable.java   (with props)
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParser.java   (with props)
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialFilterQParserPlugin.java   (with props)
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/SpatialOptions.java   (with props)
    lucene/dev/trunk/solr/src/test/org/apache/solr/search/SpatialFilterTest.java   (with props)
Modified:
    lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashDistanceFilter.java
    lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geometry/DistanceUnits.java
    lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceHandler.java
    lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceUtils.java
    lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/LatLongDistanceFilter.java
    lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/projections/SinusoidalProjector.java
    lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/DistanceCheck.java
    lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/TestCartesian.java
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/example/solr/conf/schema.xml
    lucene/dev/trunk/solr/src/java/org/apache/solr/core/SolrResourceLoader.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/schema/GeoHashField.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/schema/PointType.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialTileField.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/QParserPlugin.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/ValueSourceParser.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/Constants.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/DistanceUtils.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/GeohashHaversineFunction.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/HaversineFunction.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/SquaredEuclideanFunction.java
    lucene/dev/trunk/solr/src/java/org/apache/solr/search/function/distance/VectorDistanceFunction.java
    lucene/dev/trunk/solr/src/test/org/apache/solr/schema/PolyFieldTest.java
    lucene/dev/trunk/solr/src/test/org/apache/solr/search/function/distance/DistanceFunctionTest.java
    lucene/dev/trunk/solr/src/test/test-files/solr/conf/schema.xml

Modified: lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashDistanceFilter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashDistanceFilter.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashDistanceFilter.java (original)
+++ lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashDistanceFilter.java Sat Jul 10 00:12:41 2010
@@ -90,7 +90,7 @@ public class GeoHashDistanceFilter exten
         if (cachedDistance != null) {
           d = cachedDistance.doubleValue();
         } else {
-          d = DistanceUtils.getInstance().getDistanceMi(lat, lng, x, y);
+          d = DistanceUtils.getDistanceMi(lat, lng, x, y);
           distanceLookupCache.put(geoHash, d);
         }
 

Modified: lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geometry/DistanceUnits.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geometry/DistanceUnits.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geometry/DistanceUnits.java (original)
+++ lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/geometry/DistanceUnits.java Sat Jul 10 00:12:41 2010
@@ -55,11 +55,11 @@ public enum DistanceUnits {
    * @throws IllegalArgumentException if no DistanceUnit which represents the given unit is found
    */
   public static DistanceUnits findDistanceUnit(String unit) {
-    if (MILES.getUnit().equals(unit)) {
+    if (MILES.getUnit().equalsIgnoreCase(unit) || unit.equalsIgnoreCase("mi")) {
       return MILES;
     }
 
-    if (KILOMETERS.getUnit().equals(unit)) {
+    if (KILOMETERS.getUnit().equalsIgnoreCase(unit)) {
       return KILOMETERS;
     }
 

Modified: lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceHandler.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceHandler.java (original)
+++ lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceHandler.java Sat Jul 10 00:12:41 2010
@@ -68,7 +68,7 @@ public class DistanceHandler {
     // check to see if we have distances
     // if not calculate the distance
     if(distances == null){
-      return DistanceUtils.getInstance().getDistanceMi(centerLat, centerLng, lat, lng);
+      return DistanceUtils.getDistanceMi(centerLat, centerLng, lat, lng);
     }
     
     // check to see if the doc id has a cached distance
@@ -93,7 +93,7 @@ public class DistanceHandler {
     }
     
     //all else fails calculate the distances    
-    return DistanceUtils.getInstance().getDistanceMi(centerLat, centerLng, lat, lng);
+    return DistanceUtils.getDistanceMi(centerLat, centerLng, lat, lng);
   }
   
   

Modified: lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceUtils.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceUtils.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceUtils.java (original)
+++ lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceUtils.java Sat Jul 10 00:12:41 2010
@@ -28,17 +28,32 @@ import org.apache.lucene.spatial.geometr
  * flux and might change in incompatible ways in the next
  * release.</font>
  */
+//TODO: Move this up one package level
 public class DistanceUtils {
 
-  static DistanceUtils instance = new DistanceUtils();
-  
-  public static DistanceUtils getInstance()
-  {
-    return instance;
-  }
+  public static final double DEGREES_TO_RADIANS = Math.PI / 180.0;
+  public static final double RADIANS_TO_DEGREES = 180.0 / Math.PI;
+  public static final double DEG_45 = Math.PI / 4.0;
+  public static final double DEG_225 = 5 * DEG_45;
+  public static final double DEG_90 = Math.PI / 2;
+  public static final double DEG_180 = Math.PI;
+  public static final double SIN_45 = Math.sin(DEG_45);
+  public static final double KM_TO_MILES = 0.621371192;
+  public static final double MILES_TO_KM = 1.609344;
+    /**
+   * The International Union of Geodesy and Geophysics says the Earth's mean radius in KM is:
+   *
+   * [1] http://en.wikipedia.org/wiki/Earth_radius
+   */
+  public static final double EARTH_MEAN_RADIUS_KM = 6371.009;
 
-  
-  public double getDistanceMi(double x1, double y1, double x2, double y2) {
+  public static final double EARTH_MEAN_RADIUS_MI = EARTH_MEAN_RADIUS_KM / MILES_TO_KM;
+
+  public static final double EARTH_EQUATORIAL_RADIUS_MI = 3963.205;
+  public static final double EARTH_EQUATORIAL_RADIUS_KM = EARTH_EQUATORIAL_RADIUS_MI * MILES_TO_KM;
+
+
+  public static double getDistanceMi(double x1, double y1, double x2, double y2) {
     return getLLMDistance(x1, y1, x2, y2);
   }
 
@@ -49,7 +64,7 @@ public class DistanceUtils {
    * @param miles
    * @return boundary rectangle where getY/getX is top left, getMinY/getMinX is bottom right
    */
-  public Rectangle getBoundary (double x1, double y1, double miles) {
+  public static Rectangle getBoundary (double x1, double y1, double miles) {
 
     LLRect box = LLRect.createBox( new FloatLatLng( x1, y1 ), miles, miles );
     
@@ -58,10 +73,362 @@ public class DistanceUtils {
 
   }
   
-  public double getLLMDistance (double x1, double y1, double x2, double y2) {  
+  public static double getLLMDistance (double x1, double y1, double x2, double y2) {
 
     LatLng p1 = new FloatLatLng( x1, y1 );
     LatLng p2 = new FloatLatLng( x2, y2 );
     return p1.arcDistance( p2, DistanceUnits.MILES );
   }
+
+  /**
+   * distance/radius.
+   * @param distance The distance travelled
+   * @param radius The radius of the sphere
+   * @return The angular distance, in radians
+   */
+  public static double angularDistance(double distance, double radius){
+    return distance/radius;
+  }
+
+  /**
+   * Calculate the p-norm (i.e. length) beteen two vectors
+   *
+   * @param vec1  The first vector
+   * @param vec2  The second vector
+   * @param power The power (2 for Euclidean distance, 1 for manhattan, etc.)
+   * @return The length.
+   *         <p/>
+   *         See http://en.wikipedia.org/wiki/Lp_space
+   * @see #vectorDistance(double[], double[], double, double)
+   */
+  public static double vectorDistance(double[] vec1, double[] vec2, double power) {
+    return vectorDistance(vec1, vec2, power, 1.0 / power);
+  }
+
+  /**
+   * Calculate the p-norm (i.e. length) between two vectors
+   *
+   * @param vec1         The first vector
+   * @param vec2         The second vector
+   * @param power        The power (2 for Euclidean distance, 1 for manhattan, etc.)
+   * @param oneOverPower If you've precalculated oneOverPower and cached it, use this method to save one division operation over {@link #vectorDistance(double[], double[], double)}.
+   * @return The length.
+   */
+  public static double vectorDistance(double[] vec1, double[] vec2, double power, double oneOverPower) {
+    double result = 0;
+
+    if (power == 0) {
+      for (int i = 0; i < vec1.length; i++) {
+        result += vec1[i] - vec2[i] == 0 ? 0 : 1;
+      }
+
+    } else if (power == 1.0) {
+      for (int i = 0; i < vec1.length; i++) {
+        result += vec1[i] - vec2[i];
+      }
+    } else if (power == 2.0) {
+      result = Math.sqrt(squaredEuclideanDistance(vec1, vec2));
+    } else if (power == Integer.MAX_VALUE || Double.isInfinite(power)) {//infinite norm?
+      for (int i = 0; i < vec1.length; i++) {
+        result = Math.max(result, Math.max(vec1[i], vec2[i]));
+      }
+    } else {
+      for (int i = 0; i < vec1.length; i++) {
+        result += Math.pow(vec1[i] - vec2[i], power);
+      }
+      result = Math.pow(result, oneOverPower);
+    }
+    return result;
+  }
+
+  /**
+   * Return the coordinates of a vector that is the corner of a box (upper right or lower left), assuming a Rectangular
+   * coordinate system.  Note, this does not apply for points on a sphere or ellipse (although it could be used as an approximatation).
+   *
+   * @param center     The center point
+   * @param result Holds the result, potentially resizing if needed.
+   * @param distance   The d from the center to the corner
+   * @param upperRight If true, return the coords for the upper right corner, else return the lower left.
+   * @return The point, either the upperLeft or the lower right
+   */
+  public static double[] vectorBoxCorner(double[] center, double[] result, double distance, boolean upperRight) {
+    if (result == null || result.length != center.length) {
+      result = new double[center.length];
+    }
+    if (upperRight == false) {
+      distance = -distance;
+    }
+    //We don't care about the power here,
+    // b/c we are always in a rectangular coordinate system, so any norm can be used by
+    //using the definition of sine
+    distance = SIN_45 * distance; // sin(Pi/4) == (2^0.5)/2 == opp/hyp == opp/distance, solve for opp, similarily for cosine
+    for (int i = 0; i < center.length; i++) {
+      result[i] = center[i] + distance;
+    }
+    return result;
+  }
+
+  /**
+   * @param latCenter  In degrees
+   * @param lonCenter  In degrees
+   * @param distance The distance
+   * @param result A preallocated array to hold the results.  If null, a new one is constructed.
+   * @param upperRight If true, calculate the upper right corner, else the lower left
+   * @param radius The radius of the sphere to use.
+   * @return The Lat/Lon in degrees
+   *
+   * @see #latLonCorner(double, double, double, double[], boolean, double)
+   */
+  public static double[] latLonCornerDegs(double latCenter, double lonCenter,
+                                          double distance, double [] result,
+                                          boolean upperRight, double radius) {
+    result = latLonCorner(latCenter * DEGREES_TO_RADIANS,
+            lonCenter * DEGREES_TO_RADIANS, distance, result, upperRight, radius);
+    result[0] = result[0] * RADIANS_TO_DEGREES;
+    result[1] = result[1] * RADIANS_TO_DEGREES;
+    return result;
+  }
+
+  /**
+   * Uses Haversine to calculate the corner
+   *
+   * @param latCenter  In radians
+   * @param lonCenter  In radians
+   * @param distance   The distance
+   * @param result A preallocated array to hold the results.  If null, a new one is constructed.
+   * @param upperRight If true, give lat/lon for the upper right corner, else lower left
+   * @param radius     The radius to use for the calculation
+   * @return The Lat/Lon in Radians
+
+   */
+  public static double[] latLonCorner(double latCenter, double lonCenter,
+                                      double distance, double [] result, boolean upperRight, double radius) {
+    // Haversine formula
+    double brng = upperRight ? DEG_45 : DEG_225;
+    double lat2 = Math.asin(Math.sin(latCenter) * Math.cos(distance / radius) +
+            Math.cos(latCenter) * Math.sin(distance / radius) * Math.cos(brng));
+    double lon2 = lonCenter + Math.atan2(Math.sin(brng) * Math.sin(distance / radius) * Math.cos(latCenter),
+            Math.cos(distance / radius) - Math.sin(latCenter) * Math.sin(lat2));
+
+    /*lat2 = (lat2*180)/Math.PI;
+    lon2 = (lon2*180)/Math.PI;*/
+    //From Lucene.  Move back to Lucene when synced
+    // normalize long first
+    if (result == null || result.length != 2){
+      result = new double[2];
+    }
+    result[0] = lat2;
+    result[1] = lon2;
+    normLng(result);
+
+    // normalize lat - could flip poles
+    normLat(result);
+
+    return result;
+  }
+
+  /**
+   * @param latLng The lat/lon, in radians. lat in position 0, long in position 1
+   */
+  public static void normLat(double[] latLng) {
+
+    if (latLng[0] > DEG_90) {
+      latLng[0] = DEG_90 - (latLng[0] - DEG_90);
+      if (latLng[1] < 0) {
+        latLng[1] = latLng[1] + DEG_180;
+      } else {
+        latLng[1] = latLng[1] - DEG_180;
+      }
+    } else if (latLng[0] < -DEG_90) {
+      latLng[0] = -DEG_90 - (latLng[0] + DEG_90);
+      if (latLng[1] < 0) {
+        latLng[1] = latLng[1] + DEG_180;
+      } else {
+        latLng[1] = latLng[1] - DEG_180;
+      }
+    }
+
+  }
+
+  /**
+   * Returns a normalized Lng rectangle shape for the bounding box
+   *
+   * @param latLng The lat/lon, in radians, lat in position 0, long in position 1
+   */
+  public static void normLng(double[] latLng) {
+    if (latLng[1] > DEG_180) {
+      latLng[1] = -1.0 * (DEG_180 - (latLng[1] - DEG_180));
+    } else if (latLng[1] < -DEG_180) {
+      latLng[1] = (latLng[1] + DEG_180) + DEG_180;
+    }
+  }
+
+  /**
+   * The square of the Euclidean Distance.  Not really a distance, but useful if all that matters is
+   * comparing the result to another one.
+   *
+   * @param vec1 The first point
+   * @param vec2 The second point
+   * @return The squared Euclidean distance
+   */
+  public static double squaredEuclideanDistance(double[] vec1, double[] vec2) {
+    double result = 0;
+    for (int i = 0; i < vec1.length; i++) {
+      double v = vec1[i] - vec2[i];
+      result += v * v;
+    }
+    return result;
+  }
+
+  /**
+   * @param x1     The x coordinate of the first point, in radians
+   * @param y1     The y coordinate of the first point, in radians
+   * @param x2     The x coordinate of the second point, in radians
+   * @param y2     The y coordinate of the second point, in radians
+   * @param radius The radius of the sphere
+   * @return The distance between the two points, as determined by the Haversine formula.
+
+   */
+  public static double haversine(double x1, double y1, double x2, double y2, double radius) {
+    double result = 0;
+    //make sure they aren't all the same, as then we can just return 0
+    if ((x1 != x2) || (y1 != y2)) {
+      double diffX = x1 - x2;
+      double diffY = y1 - y2;
+      double hsinX = Math.sin(diffX * 0.5);
+      double hsinY = Math.sin(diffY * 0.5);
+      double h = hsinX * hsinX +
+              (Math.cos(x1) * Math.cos(x2) * hsinY * hsinY);
+      result = (radius * 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h)));
+    }
+    return result;
+  }
+
+  /**
+   * Given a string containing <i>dimension</i> values encoded in it, separated by commas, return a String array of length <i>dimension</i>
+   * containing the values.
+   *
+   * @param out         A preallocated array.  Must be size dimension.  If it is not it will be resized.
+   * @param externalVal The value to parse
+   * @param dimension   The expected number of values for the point
+   * @return An array of the values that make up the point (aka vector)
+   * @throws InvalidGeoException if the dimension specified does not match the number of values in the externalValue.
+   */
+  public static String[] parsePoint(String[] out, String externalVal, int dimension) throws InvalidGeoException{
+    //TODO: Should we support sparse vectors?
+    if (out == null || out.length != dimension) out = new String[dimension];
+    int idx = externalVal.indexOf(',');
+    int end = idx;
+    int start = 0;
+    int i = 0;
+    if (idx == -1 && dimension == 1 && externalVal.length() > 0) {//we have a single point, dimension better be 1
+      out[0] = externalVal.trim();
+      i = 1;
+    } else if (idx > 0) {//if it is zero, that is an error
+      //Parse out a comma separated list of point values, as in: 73.5,89.2,7773.4
+      for (; i < dimension; i++) {
+        while (start < end && externalVal.charAt(start) == ' ') start++;
+        while (end > start && externalVal.charAt(end - 1) == ' ') end--;
+        if (start == end) {
+          break;
+        }
+        out[i] = externalVal.substring(start, end);
+        start = idx + 1;
+        end = externalVal.indexOf(',', start);
+        idx = end;
+        if (end == -1) {
+          end = externalVal.length();
+        }
+      }
+    }
+    if (i != dimension) {
+      throw new InvalidGeoException("incompatible dimension (" + dimension +
+              ") and values (" + externalVal + ").  Only " + i + " values specified");
+    }
+    return out;
+  }
+
+  /**
+   * Given a string containing <i>dimension</i> values encoded in it, separated by commas, return a double array of length <i>dimension</i>
+   * containing the values.
+   *
+   * @param out         A preallocated array.  Must be size dimension.  If it is not it will be resized.
+   * @param externalVal The value to parse
+   * @param dimension   The expected number of values for the point
+   * @return An array of the values that make up the point (aka vector)
+   * @throws InvalidGeoException if the dimension specified does not match the number of values in the externalValue.
+   */
+  public static double[] parsePointDouble(double[] out, String externalVal, int dimension) throws InvalidGeoException{
+    if (out == null || out.length != dimension) out = new double[dimension];
+    int idx = externalVal.indexOf(',');
+    int end = idx;
+    int start = 0;
+    int i = 0;
+    if (idx == -1 && dimension == 1 && externalVal.length() > 0) {//we have a single point, dimension better be 1
+      out[0] = Double.parseDouble(externalVal.trim());
+      i = 1;
+    } else if (idx > 0) {//if it is zero, that is an error
+      //Parse out a comma separated list of point values, as in: 73.5,89.2,7773.4
+      for (; i < dimension; i++) {
+        //TODO: abstract common code with other parsePoint
+        while (start < end && externalVal.charAt(start) == ' ') start++;
+        while (end > start && externalVal.charAt(end - 1) == ' ') end--;
+        if (start == end) {
+          break;
+        }
+        out[i] = Double.parseDouble(externalVal.substring(start, end));
+        start = idx + 1;
+        end = externalVal.indexOf(',', start);
+        idx = end;
+        if (end == -1) {
+          end = externalVal.length();
+        }
+      }
+    }
+    if (i != dimension) {
+      throw new InvalidGeoException("incompatible dimension (" + dimension +
+              ") and values (" + externalVal + ").  Only " + i + " values specified");
+    }
+    return out;
+  }
+
+  public static final double[] parseLatitudeLongitude(String latLonStr) throws InvalidGeoException {
+    return parseLatitudeLongitude(null, latLonStr);
+  }
+
+  /**
+   * extract (by calling {@link #parsePoint(String[], String, int)} and validate the latitude and longitude contained
+   * in the String by making sure the latitude is between 90 & -90 and longitude is between -180 and 180.
+   * <p/>
+   * The latitude is assumed to be the first part of the string and the longitude the second part.
+   *
+   * @param latLon    A preallocated array to hold the result
+   * @param latLonStr The string to parse.  Latitude is the first value, longitude is the second.
+   * @return The lat long
+   * @throws InvalidGeoException if there was an error parsing
+   */
+  public static final double[] parseLatitudeLongitude(double[] latLon, String latLonStr) throws InvalidGeoException {
+    if (latLon == null) {
+      latLon = new double[2];
+    }
+    double[] toks = parsePointDouble(null, latLonStr, 2);
+
+    if (toks[0] < -90.0 || toks[0] > 90.0) {
+      throw new InvalidGeoException(
+              "Invalid latitude: latitudes are range -90 to 90: provided lat: ["
+                      + toks[0] + "]");
+    }
+    latLon[0] = toks[0];
+
+
+    if (toks[1] < -180.0 || toks[1] > 180.0) {
+
+      throw new InvalidGeoException(
+              "Invalid longitude: longitudes are range -180 to 180: provided lon: ["
+                      + toks[1] + "]");
+    }
+    latLon[1] = toks[1];
+
+    return latLon;
+  }
 }

Modified: lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/LatLongDistanceFilter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/LatLongDistanceFilter.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/LatLongDistanceFilter.java (original)
+++ lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/LatLongDistanceFilter.java Sat Jul 10 00:12:41 2010
@@ -88,7 +88,7 @@ public class LatLongDistanceFilter exten
         if (cachedDistance != null){
           d = cachedDistance.doubleValue();
         } else {
-          d = DistanceUtils.getInstance().getDistanceMi(lat, lng, x, y);
+          d = DistanceUtils.getDistanceMi(lat, lng, x, y);
           distanceLookupCache.put(ck, d);
         }
 

Modified: lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/projections/SinusoidalProjector.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/projections/SinusoidalProjector.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/projections/SinusoidalProjector.java (original)
+++ lucene/dev/trunk/lucene/contrib/spatial/src/java/org/apache/lucene/spatial/tier/projections/SinusoidalProjector.java Sat Jul 10 00:12:41 2010
@@ -17,21 +17,28 @@
 
 package org.apache.lucene.spatial.tier.projections;
 
+import org.apache.lucene.spatial.tier.DistanceUtils;
+
 /**
  * Based on Sinusoidal Projections
  * Project a latitude / longitude on a 2D cartesian map
+ * <p/>
+ * THIS PROJECTION IS WRONG, but it's not going to be fixed b/c it will break a lot of existing tests, plus we are deprecating
+ * most of the existing spatial and replacing with a more reliable approach.
  *
  * <p><font color="red"><b>NOTE:</b> This API is still in
  * flux and might change in incompatible ways in the next
  * release.</font>
+ *
+ * @deprecated Until we can put in place proper tests and a proper fix. 
  */
 public class SinusoidalProjector implements IProjector {
 
-  
+
   public String coordsAsString(double latitude, double longitude) {
     return null;
   }
-  
+
   public double[] coords(double latitude, double longitude) {
     double rlat = Math.toRadians(latitude);
     double rlong = Math.toRadians(longitude);
@@ -42,3 +49,39 @@ public class SinusoidalProjector impleme
   }
   
 }
+
+/*
+This whole file should really be:*/
+
+/**
+ * Based on Sinusoidal Projections
+ * Project a latitude / longitude on a 2D cartesian map using the Prime Meridian as the "central meridian"
+ *
+ * See http://en.wikipedia.org/wiki/Sinusoidal_projection
+ *
+ * <p><font color="red"><b>NOTE:</b> This API is still in
+ * flux and might change in incompatible ways in the next
+ * release.</font>
+ */
+/*
+public class SinusoidalProjector implements IProjector {
+
+
+  public String coordsAsString(double latitude, double longitude) {
+    double [] coords = coords(latitude, longitude);
+    return coords[0] + "," + coords[1];
+  }
+
+  public double[] coords(double latitude, double longitude) {
+    double rlat = latitude * DistanceUtils.DEGREES_TO_RADIANS;
+    double rlong = longitude * DistanceUtils.DEGREES_TO_RADIANS;
+    double x = rlong * Math.cos(rlat);
+    return new double[]{x, rlat};
+
+  }
+
+}
+*/
+
+
+

Added: lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/DistanceUtilsTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/DistanceUtilsTest.java?rev=962727&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/DistanceUtilsTest.java (added)
+++ lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/DistanceUtilsTest.java Sat Jul 10 00:12:41 2010
@@ -0,0 +1,234 @@
+package org.apache.lucene.spatial;
+
+import junit.framework.TestCase;
+import org.apache.lucene.spatial.tier.DistanceUtils;
+import org.apache.lucene.spatial.tier.InvalidGeoException;
+
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ *
+ *
+ **/
+public class DistanceUtilsTest extends TestCase {
+
+  public void testBoxCorner() throws Exception {
+    double[] zero = new double[]{0, 0};
+    double[] zeroOne = new double[]{0, 1};
+    double[] oneOne = new double[]{1, 1};
+    double[] pt1 = new double[]{1.5, 110.3};
+    double[] result = DistanceUtils.vectorBoxCorner(zero, null, Math.sqrt(2), true);
+    assertEquals(1.0, result[0]);
+    assertEquals(1.0, result[1]);
+
+    result = DistanceUtils.vectorBoxCorner(zero, null, Math.sqrt(2), false);
+    assertEquals(-1.0, result[0]);
+    assertEquals(-1.0, result[1]);
+
+    result = DistanceUtils.vectorBoxCorner(oneOne, null, Math.sqrt(2), true);
+    assertEquals(2.0, result[0]);
+    assertEquals(2.0, result[1]);
+
+    result = DistanceUtils.vectorBoxCorner(zeroOne, null, Math.sqrt(2), true);
+    assertEquals(1.0, result[0]);
+    assertEquals(2.0, result[1]);
+
+    result = DistanceUtils.vectorBoxCorner(pt1, null, Math.sqrt(2), true);
+    assertEquals(2.5, result[0]);
+    assertEquals(111.3, result[1]);
+
+    result = DistanceUtils.vectorBoxCorner(pt1, null, Math.sqrt(2), false);
+    assertEquals(0.5, result[0]);
+    assertEquals(109.3, result[1]);
+
+  }
+
+  public void testNormLatLon() throws Exception {
+
+  }
+
+  public void testLatLonCorner() throws Exception {
+    double[] zero = new double[]{0, 0};
+    double[] zero45 = new double[]{0, DistanceUtils.DEG_45};
+    double[] result;
+    // 	00°38′09″N, 000°38′09″E
+    //Verify at http://www.movable-type.co.uk/scripts/latlong.html
+    result = DistanceUtils.latLonCorner(zero[0], zero[1], 100, null, true, DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    assertEquals(0.63583 * DistanceUtils.DEGREES_TO_RADIANS, result[0], 0.001);
+    assertEquals(0.63583 * DistanceUtils.DEGREES_TO_RADIANS, result[1], 0.001);
+
+    result = DistanceUtils.latLonCornerDegs(zero[0], zero[1], 100, null, true, DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    // 	00°38′09″N, 000°38′09″E
+    assertEquals(0.63583, result[0], 0.001);
+    assertEquals(0.63583, result[1], 0.001);
+
+    result = DistanceUtils.latLonCornerDegs(zero[0], zero[1], 100, null, false, DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    // 	00°38′09″N, 000°38′09″E
+    assertEquals(-0.63583, result[0], 0.001);
+    assertEquals(-0.63583, result[1], 0.001);
+
+    //test some edge cases
+    //89°16′02″N, 060°12′35″E
+    result = DistanceUtils.latLonCornerDegs(89.0, 0, 100, null, true, DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    assertEquals(89.26722, result[0], 0.001);
+    assertEquals(60.20972, result[1], 0.001);
+
+    result = DistanceUtils.latLonCornerDegs(0, -179.0, 100, null, true, DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    assertEquals(0.63583, result[0], 0.001);
+    assertEquals(-178.36417, result[1], 0.001);
+
+  }
+
+  public void testVectorDistance() throws Exception {
+    double[] zero = new double[]{0, 0};
+    double[] zeroOne = new double[]{0, 1};
+    double[] oneZero = new double[]{1, 0};
+    double[] oneOne = new double[]{1, 1};
+    double distance;
+    distance = DistanceUtils.vectorDistance(zero, zeroOne, 2);
+    assertEquals(1.0, distance);
+    distance = DistanceUtils.vectorDistance(zero, oneZero, 2);
+    assertEquals(1.0, distance);
+    distance = DistanceUtils.vectorDistance(zero, oneOne, 2);
+    assertEquals(Math.sqrt(2), distance, 0.001);
+
+    distance = DistanceUtils.squaredEuclideanDistance(zero, oneOne);
+    assertEquals(2, distance, 0.001);
+  }
+
+  public void testHaversine() throws Exception {
+    double distance;
+    //compare to http://www.movable-type.co.uk/scripts/latlong.html
+    distance = DistanceUtils.haversine(0, 0, Math.PI / 4.0, Math.PI / 4.0, DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    assertEquals(6672.0, distance, 0.5);
+
+    distance = DistanceUtils.haversine(0, 0, Math.toRadians(20), Math.toRadians(20), DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    assertEquals(3112, distance, 0.5);
+
+    distance = DistanceUtils.haversine(0, 0, Math.toRadians(1), Math.toRadians(1), DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    assertEquals(157.2, distance, 0.5);
+
+    //Try some around stuff
+    distance = DistanceUtils.haversine(Math.toRadians(1), Math.toRadians(-1),
+            Math.toRadians(1), Math.toRadians(1), DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    assertEquals(222.4, distance, 0.5);
+
+    distance = DistanceUtils.haversine(Math.toRadians(89), Math.toRadians(-1),
+            Math.toRadians(89), Math.toRadians(179), DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    assertEquals(222.4, distance, 0.5);
+
+    distance = DistanceUtils.haversine(Math.toRadians(89), Math.toRadians(-1),
+            Math.toRadians(49), Math.toRadians(179), DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    assertEquals(4670, distance, 0.5);
+
+    distance = DistanceUtils.haversine(Math.toRadians(0), Math.toRadians(-179),
+            Math.toRadians(0), Math.toRadians(179), DistanceUtils.EARTH_MEAN_RADIUS_KM);
+    assertEquals(222.4, distance, 0.5);
+
+  }
+
+  public void testParse() throws Exception {
+    String[] parse;
+    parse = DistanceUtils.parsePoint(null, "89.0,73.2", 2);
+    assertEquals(2, parse.length);
+    assertEquals("89.0", parse[0]);
+    assertEquals("73.2", parse[1]);
+
+    parse = DistanceUtils.parsePoint(null, "89.0,73.2,-92.3", 3);
+    assertEquals(3, parse.length);
+    assertEquals("89.0", parse[0]);
+    assertEquals("73.2", parse[1]);
+    assertEquals("-92.3", parse[2]);
+
+    parse = DistanceUtils.parsePoint(null, "    89.0         ,   73.2  ,              -92.3   ", 3);
+    assertEquals(3, parse.length);
+    assertEquals("89.0", parse[0]);
+    assertEquals("73.2", parse[1]);
+    assertEquals("-92.3", parse[2]);
+
+
+    String[] foo = DistanceUtils.parsePoint(parse, "89.0         ,   73.2 ,              -92.3", 3);
+    //should be same piece of memory
+    assertTrue(foo == parse);
+    assertEquals(3, parse.length);
+    assertEquals("89.0", parse[0]);
+    assertEquals("73.2", parse[1]);
+    assertEquals("-92.3", parse[2]);
+    //array should get automatically resized
+    parse = DistanceUtils.parsePoint(new String[1], "89.0         ,   73.2 ,              -92.3", 3);
+    assertEquals(3, parse.length);
+    assertEquals("89.0", parse[0]);
+    assertEquals("73.2", parse[1]);
+    assertEquals("-92.3", parse[2]);
+
+
+    try {
+      parse = org.apache.lucene.spatial.tier.DistanceUtils.parsePoint(null, "89.0         ,   ", 3);
+      assertTrue(false);
+    } catch (InvalidGeoException e) {
+    }
+    try {
+      parse = DistanceUtils.parsePoint(null, " , 89.0          ", 3);
+      assertTrue(false);
+    } catch (InvalidGeoException e) {
+    }
+
+    try {
+      parse = DistanceUtils.parsePoint(null, "", 3);
+      assertTrue(false);
+    } catch (InvalidGeoException e) {
+    }
+
+
+    double[] dbls = DistanceUtils.parsePointDouble(null, "89.0         ,   73.2 ,              -92.3", 3);
+    assertEquals(3, dbls.length);
+    assertEquals(89.0, dbls[0]);
+    assertEquals(73.2, dbls[1]);
+    assertEquals(-92.3, dbls[2]);
+
+    try {
+      dbls = DistanceUtils.parsePointDouble(null, "89.0         ,   foo ,              -92.3", 3);
+      assertTrue(false);
+    } catch (NumberFormatException e) {
+    }
+
+    dbls = DistanceUtils.parseLatitudeLongitude(null, "89.0         ,   73.2    ");
+    assertEquals(2, dbls.length);
+    assertEquals(89.0, dbls[0]);
+    assertEquals(73.2, dbls[1]);
+
+    //test some bad lat/long pairs
+    try {
+      dbls = DistanceUtils.parseLatitudeLongitude(null, "189.0         ,   73.2    ");
+      assertTrue(false);
+    } catch (InvalidGeoException e) {
+
+    }
+
+    try {
+      dbls = DistanceUtils.parseLatitudeLongitude(null, "89.0         ,   273.2    ");
+      assertTrue(false);
+    } catch (InvalidGeoException e) {
+
+    }
+
+  }
+
+}

Propchange: lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/DistanceUtilsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/DistanceCheck.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/DistanceCheck.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/DistanceCheck.java (original)
+++ lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/DistanceCheck.java Sat Jul 10 00:12:41 2010
@@ -31,7 +31,7 @@ public class DistanceCheck {
     double long2 = 0;
     
     for (int i =0; i < 90; i++){
-      double dis = DistanceUtils.getInstance().getDistanceMi(lat1, long1, lat2, long2);
+      double dis = DistanceUtils.getDistanceMi(lat1, long1, lat2, long2);
       lat1 +=1;
       lat2 = lat1 + 0.001;
       

Modified: lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/TestCartesian.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/TestCartesian.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/TestCartesian.java (original)
+++ lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/TestCartesian.java Sat Jul 10 00:12:41 2010
@@ -278,8 +278,8 @@ public class TestCartesian extends Lucen
       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);
+      double distance = DistanceUtils.getDistanceMi(lat, lng, rsLat, rsLng);
+      double llm = DistanceUtils.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);
       assertTrue((distance < miles ));
@@ -372,8 +372,8 @@ public class TestCartesian extends Lucen
       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);
+      double distance = DistanceUtils.getDistanceMi(lat, lng, rsLat, rsLng);
+      double llm = DistanceUtils.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);
@@ -467,8 +467,8 @@ public class TestCartesian extends Lucen
         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);
+        double distance = DistanceUtils.getDistanceMi(lat, lng, rsLat, rsLng);
+        double llm = DistanceUtils.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);
         assertTrue((distance < miles ));
@@ -561,8 +561,8 @@ public class TestCartesian extends Lucen
         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);
+        double distance = DistanceUtils.getDistanceMi(lat, lng, rsLat, rsLng);
+        double llm = DistanceUtils.getLLMDistance(lat, lng, rsLat, rsLng);
         if (VERBOSE) System.out.println("Name: "+ name +", Distance (res, ortho, harvesine):"+ distance +" |"+ geo_distance +"|"+ llm +" | score "+ scoreDocs[i].score);
         assertTrue(Math.abs((distance - llm)) < 1);
         assertTrue((distance < miles ));

Added: lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/projections/SinusoidalProjectorTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/projections/SinusoidalProjectorTest.java?rev=962727&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/projections/SinusoidalProjectorTest.java (added)
+++ lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/projections/SinusoidalProjectorTest.java Sat Jul 10 00:12:41 2010
@@ -0,0 +1,67 @@
+package org.apache.lucene.spatial.tier.projections;
+
+import org.apache.lucene.spatial.tier.DistanceUtils;
+import org.junit.Test;
+
+import static junit.framework.Assert.*;
+
+
+/**
+ *
+ *
+ **/
+public class SinusoidalProjectorTest {
+
+  @Test
+  public void testProjection() throws Exception {
+    SinusoidalProjector prj = new SinusoidalProjector();
+    //TODO: uncomment once SinusoidalProjector is fixed.  Unfortunately, fixing it breaks a lot of other stuff
+    /*double[] doubles;
+    doubles = prj.coords(-89.0, 10);
+    assertEquals(0.003, doubles[0], 0.001);//x
+    assertEquals(-89.0 * DistanceUtils.DEGREES_TO_RADIANS, doubles[1]);
+    
+    doubles = prj.coords(89.0, 0);
+    assertEquals(0.0, doubles[0]);//x
+    assertEquals(89.0 * DistanceUtils.DEGREES_TO_RADIANS, doubles[1]);
+
+    doubles = prj.coords(89.0, 10);
+    assertEquals(0.003, doubles[0], 0.001);//x
+    assertEquals(89.0 * DistanceUtils.DEGREES_TO_RADIANS, doubles[1]);
+
+
+    doubles = prj.coords(-89.0, 0);
+    assertEquals(0.0, doubles[0]);//x
+    assertEquals(-89.0 * DistanceUtils.DEGREES_TO_RADIANS, doubles[1]);*/
+
+
+  }
+}
+
+//This code demonstrates that the SinusoidalProjector is incorrect
+  /*@Test
+  public void testFoo() throws Exception {
+    CartesianTierPlotter plotter = new CartesianTierPlotter(11, new SinusoidalProjector(), "foo");
+    SinusoidalProjector prj = new SinusoidalProjector();
+    System.out.println("---- Equator ---");
+    printValues(plotter, prj, 0);
+    System.out.println("---- North ---");
+    printValues(plotter, prj, 89.0);
+    System.out.println("---- South ---");
+    printValues(plotter, prj, -89.0);
+  }
+
+  private void printValues(CartesianTierPlotter plotter, SinusoidalProjector prj, double latitude){
+    for (int i = 0; i <= 10; i++){
+      double boxId = plotter.getTierBoxId(latitude, i);
+      double[] doubles = prj.coords(latitude, i);
+      System.out.println("Box[" + latitude + ", " + i + "] = " + boxId + " sinusoidal: [" + doubles[0] + ", " + doubles[1] + "]");
+    }
+    for (int i = -10; i <= 0; i++){
+      double boxId = plotter.getTierBoxId(latitude, i);
+      double[] doubles = prj.coords(latitude, i);
+      System.out.println("Box[" + latitude + ", " + i + "] = " + boxId + " sinusoidal: [" + doubles[0] + ", " + doubles[1] + "]");
+    }
+
+  }
+  */
\ No newline at end of file

Propchange: lucene/dev/trunk/lucene/contrib/spatial/src/test/org/apache/lucene/spatial/tier/projections/SinusoidalProjectorTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Sat Jul 10 00:12:41 2010
@@ -122,7 +122,7 @@ New Features
 * SOLR-1131: FieldTypes can now output multiple Fields per Type and still be searched.  This can be handy for hiding the details of a particular
   implementation such as in the spatial case. (Chris Mattmann, shalin, noble, gsingers, yonik)
 
-* SOLR-1586: Add support for Geohash and Spatial Tile FieldType (Chris Mattmann, gsingers)
+* SOLR-1586: Add support for Geohash FieldType (Chris Mattmann, gsingers)
 
 * SOLR-1697: PluginInfo should load plugins w/o class attribute also (noble)
 
@@ -198,6 +198,10 @@ New Features
 * SOLR-1985: FastVectorHighlighter: add wrapper class for Lucene's SingleFragListBuilder (koji)
 
 * SOLR-1984: Add HyphenationCompoundWordTokenFilterFactory. (PB via rmuir)
+
+* SOLR-1568: Added "native" filtering support for PointType, GeohashField.  Added LatLonType with filtering support too.  See
+  	     http://wiki.apache.org/solr/SpatialSearch and the example.  Refactored some items in Lucene spatial. 
+	     Removed SpatialTileField as the underlying CartesianTier is broken beyond repair and is going to be moved. (gsingers) 
    
 Optimizations
 ----------------------

Modified: lucene/dev/trunk/solr/example/solr/conf/schema.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/example/solr/conf/schema.xml?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/solr/example/solr/conf/schema.xml (original)
+++ lucene/dev/trunk/solr/example/solr/conf/schema.xml Sat Jul 10 00:12:41 2010
@@ -416,15 +416,11 @@
    -->
     <fieldtype name="geohash" class="solr.GeoHashField"/>
 
+
     <!--
-      A SpatialTileField is like a set of zoom levels on an interactive map (i.e. Google Maps or MapQuest).  It takes a lat/lon
-      field and indexes it into (end - start) different fields, each representing a different zoom level.
-      This can then be leveraged to quickly narrow the search space by creating a filter, at an appropriate tier level,
-      that only has to enumerate a minimum number of terms.
 
-      See http://wiki.apache.org/solr/SpatialSearch
      -->
-    <fieldType name="tile" class="solr.SpatialTileField" start="4" end="15" subFieldSuffix="_tiled"/>
+    <fieldType name="latLon" class="solr.LatLonType" subFieldSuffix="_latLon"/>
     
  </types>
 
@@ -467,9 +463,14 @@
    <field name="popularity" type="int" indexed="true" stored="true" />
    <field name="inStock" type="boolean" indexed="true" stored="true" />
 
+   <!--
+   The following store examples are used to demonstrate the various ways one might _CHOOSE_ to
+    implement spatial.  It is highly unlikely that you would ever have ALL of these fields defined.
+    -->
    <field name="store" type="location" indexed="true" stored="true"/>
+   <field name="store_lat_lon" type="latLon" indexed="true" stored="true"/>
    <field name="store_hash" type="geohash" indexed="true" stored="false"/>
-   <field name="store_tiles" type="tile" indexed="true" stored="false"/>
+
 
    <!-- Common metadata fields, named specifically to match up with
      SolrCell metadata when parsing rich documents such as Word, PDF.
@@ -490,7 +491,7 @@
 
    <!-- catchall field, containing all other searchable text fields (implemented
         via copyField further on in this schema  -->
-   <field name="text" type="text" indexed="true" stored="false" multiValued="true"/>
+   <field name="text" type="text" indexed="true" stored="true" multiValued="true" termVectors="true"/>
 
    <!-- catchall text field that indexes tokens both normally and in reverse for efficient
         leading wildcard queries. -->
@@ -525,7 +526,7 @@
    <dynamicField name="*_f"  type="float"  indexed="true"  stored="true"/>
    <dynamicField name="*_d"  type="double" indexed="true"  stored="true"/>
 
-   <dynamicField name="*_tiled"  type="double" indexed="true"  stored="false"/>
+   <dynamicField name="*_latLon"  type="double" indexed="true"  stored="false"/>
 
    <dynamicField name="*_dt" type="date"    indexed="true"  stored="true"/>
    <dynamicField name="*_p"  type="location" indexed="true" stored="true"/>
@@ -569,7 +570,7 @@
 
    <copyField source="cat" dest="text"/>
    <copyField source="store" dest="store_hash"/>
-   <copyField source="store" dest="store_tiles"/>
+  <copyField source="store" dest="store_lat_lon"/>
    <copyField source="name" dest="text"/>
    <copyField source="manu" dest="text"/>
    <copyField source="features" dest="text"/>

Added: lucene/dev/trunk/solr/src/common/org/apache/solr/common/params/SpatialParams.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/common/org/apache/solr/common/params/SpatialParams.java?rev=962727&view=auto
==============================================================================
--- lucene/dev/trunk/solr/src/common/org/apache/solr/common/params/SpatialParams.java (added)
+++ lucene/dev/trunk/solr/src/common/org/apache/solr/common/params/SpatialParams.java Sat Jul 10 00:12:41 2010
@@ -0,0 +1,40 @@
+package org.apache.solr.common.params;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ *
+ *
+ **/
+public interface SpatialParams {
+  public static final String POINT = "pt";
+  public static final String DISTANCE = "d";
+  /**
+   * km - kilometers
+   * mi - miles
+   */
+  public static final String UNITS = "units";
+  /**
+   * The distance measure to use.
+   */
+  public static final String MEASURE = "meas";
+  /**
+   * The radius of the sphere to use to in calculating spherical distances like Haversine
+   */
+  public static final String SPHERE_RADIUS = "sphere_radius";
+}

Propchange: lucene/dev/trunk/solr/src/common/org/apache/solr/common/params/SpatialParams.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/core/SolrResourceLoader.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/core/SolrResourceLoader.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/core/SolrResourceLoader.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/core/SolrResourceLoader.java Sat Jul 10 00:12:41 2010
@@ -53,6 +53,7 @@ import org.apache.solr.schema.FieldType;
 import org.apache.solr.update.processor.UpdateRequestProcessorFactory;
 import org.apache.solr.util.plugin.ResourceLoaderAware;
 import org.apache.solr.util.plugin.SolrCoreAware;
+import org.apache.solr.search.QParserPlugin;
 
 /**
  * @since solr 1.3
@@ -645,6 +646,7 @@ public class SolrResourceLoader implemen
         CharFilterFactory.class,
         TokenFilterFactory.class,
         TokenizerFactory.class,
+        QParserPlugin.class,
         FieldType.class
       }
     );

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/schema/GeoHashField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/schema/GeoHashField.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/schema/GeoHashField.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/schema/GeoHashField.java Sat Jul 10 00:12:41 2010
@@ -18,13 +18,22 @@
 package org.apache.solr.schema;
 
 import org.apache.lucene.document.Fieldable;
+import org.apache.lucene.search.Query;
 import org.apache.lucene.search.SortField;
 import org.apache.lucene.spatial.geohash.GeoHashUtils;
+import org.apache.lucene.spatial.tier.DistanceUtils;
+import org.apache.lucene.spatial.tier.InvalidGeoException;
+import org.apache.solr.common.SolrException;
 import org.apache.solr.response.TextResponseWriter;
 import org.apache.solr.response.XMLWriter;
 import org.apache.solr.search.QParser;
+import org.apache.solr.search.SolrConstantScoreQuery;
+import org.apache.solr.search.SpatialOptions;
+import org.apache.solr.search.function.LiteralValueSource;
 import org.apache.solr.search.function.ValueSource;
-import org.apache.solr.search.function.distance.DistanceUtils;
+import org.apache.solr.search.function.ValueSourceRangeFilter;
+import org.apache.solr.search.function.distance.GeohashHaversineFunction;
+
 
 import java.io.IOException;
 
@@ -33,9 +42,9 @@ import java.io.IOException;
  * href="http://en.wikipedia.org/wiki/Geohash">Geohash</a> field. The field is
  * provided as a lat/lon pair and is internally represented as a string.
  *
- * @see org.apache.solr.search.function.distance.DistanceUtils#parseLatitudeLongitude(double[], String)
+ * @see org.apache.lucene.spatial.tier.DistanceUtils#parseLatitudeLongitude(double[], String)
  */
-public class GeoHashField extends FieldType {
+public class GeoHashField extends FieldType implements SpatialQueryable {
 
 
   @Override
@@ -43,6 +52,22 @@ public class GeoHashField extends FieldT
     return getStringSort(field, top);
   }
 
+    //QUESTION: Should we do a fast and crude one?  Or actually check distances
+  //Fast and crude could use EdgeNGrams, but that would require a different
+  //encoding.  Plus there are issues around the Equator/Prime Meridian
+  public Query createSpatialQuery(QParser parser, SpatialOptions options) {
+    double [] point = new double[0];
+    try {
+      point = DistanceUtils.parsePointDouble(null, options.pointStr, 2);
+    } catch (InvalidGeoException e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
+    }
+    String geohash = GeoHashUtils.encode(point[0], point[1]);
+    //TODO: optimize this
+    return new SolrConstantScoreQuery(new ValueSourceRangeFilter(new GeohashHaversineFunction(getValueSource(options.field, parser),
+            new LiteralValueSource(geohash), options.radius), "0", String.valueOf(options.distance), true, true));
+  }
+
   @Override
   public void write(XMLWriter xmlWriter, String name, Fieldable f)
           throws IOException {
@@ -67,7 +92,12 @@ public class GeoHashField extends FieldT
   public String toInternal(String val) {
     // validate that the string is of the form
     // latitude, longitude
-    double[] latLon = DistanceUtils.parseLatitudeLongitude(null, val);
+    double[] latLon = new double[0];
+    try {
+      latLon = DistanceUtils.parseLatitudeLongitude(null, val);
+    } catch (InvalidGeoException e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
+    }
     return GeoHashUtils.encode(latLon[0], latLon[1]);
   }
 

Added: lucene/dev/trunk/solr/src/java/org/apache/solr/schema/LatLonType.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/schema/LatLonType.java?rev=962727&view=auto
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/schema/LatLonType.java (added)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/schema/LatLonType.java Sat Jul 10 00:12:41 2010
@@ -0,0 +1,306 @@
+package org.apache.solr.schema;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Fieldable;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.spatial.tier.DistanceUtils;
+import org.apache.lucene.spatial.tier.InvalidGeoException;
+import org.apache.lucene.spatial.tier.projections.CartesianTierPlotter;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.response.TextResponseWriter;
+import org.apache.solr.response.XMLWriter;
+import org.apache.solr.search.QParser;
+import org.apache.solr.search.SpatialOptions;
+import org.apache.solr.search.function.ValueSource;
+import org.apache.solr.search.function.VectorValueSource;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Represents a Latitude/Longitude as a 2 dimensional point.  Latitude is <b>always</b> specified first.
+ * Can also, optionally, integrate in Spatial Tile capabilities.  The default is for tile fields from 4 - 15,
+ * just as in the SpatialTileField that we are extending.
+ */
+public class LatLonType extends AbstractSubTypeFieldType implements SpatialQueryable {
+  protected static final int LAT = 0;
+  protected static final int LONG = 1;
+
+  @Override
+  protected void init(IndexSchema schema, Map<String, String> args) {
+    super.init(schema, args);
+    //TODO: refactor this, as we are creating the suffix cache twice, since the super.init does it too
+    createSuffixCache(3);//we need three extra fields: one for the storage field, two for the lat/lon
+  }
+
+  @Override
+  public Fieldable[] createFields(SchemaField field, String externalVal, float boost) {
+    //we could have tileDiff + 3 fields (two for the lat/lon, one for storage)
+    Fieldable[] f = new Fieldable[(field.indexed() ? 2 : 0) + (field.stored() ? 1 : 0)];
+    if (field.indexed()) {
+      int i = 0;
+      double[] latLon = new double[0];
+      try {
+        latLon = DistanceUtils.parseLatitudeLongitude(null, externalVal);
+      } catch (InvalidGeoException e) {
+        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
+      }
+      //latitude
+      f[i] = subField(field, i).createField(String.valueOf(latLon[LAT]), boost);
+      i++;
+      //longitude
+      f[i] = subField(field, i).createField(String.valueOf(latLon[LONG]), boost);
+
+    }
+
+    if (field.stored()) {
+      f[f.length - 1] = createField(field.getName(), externalVal,
+              getFieldStore(field, externalVal), Field.Index.NO, Field.TermVector.NO,
+              false, false, boost);
+    }
+    return f;
+  }
+
+  @Override
+  public Query createSpatialQuery(QParser parser, SpatialOptions options) {
+    BooleanQuery result = new BooleanQuery();
+    double[] point = new double[0];
+    try {
+      point = DistanceUtils.parseLatitudeLongitude(options.pointStr);
+    } catch (InvalidGeoException e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
+    }
+
+    //Get the distance
+    double[] ur;
+    double[] ll;
+    if (options.measStr == null || options.measStr.equals("hsin")) {
+      ur = DistanceUtils.latLonCornerDegs(point[LAT], point[LONG], options.distance, null, true, options.radius);
+      ll = DistanceUtils.latLonCornerDegs(point[LAT], point[LONG], options.distance, null, false, options.radius);
+    } else {
+      ur = DistanceUtils.vectorBoxCorner(point, null, options.distance, true);
+      ll = DistanceUtils.vectorBoxCorner(point, null, options.distance, false);
+    }
+
+    SchemaField subSF;
+    Query range;
+
+    double angDistDegs = DistanceUtils.angularDistance(options.distance,
+            DistanceUtils.EARTH_MEAN_RADIUS_MI) * DistanceUtils.RADIANS_TO_DEGREES;
+    //for the poles, do something slightly different
+    if (point[LAT] + angDistDegs > 90.0) { //we cross the north pole
+      //we don't need a longitude boundary at all
+
+      double minLat = Math.min(ll[LAT], ur[LAT]);
+      subSF = subField(options.field, LAT);
+      range = subSF.getType().getRangeQuery(parser, subSF,
+              String.valueOf(minLat),
+              "90", true, true);
+      result.add(range, BooleanClause.Occur.MUST);
+    } else if (point[LAT] - angDistDegs < -90.0) {//we cross the south pole
+      subSF = subField(options.field, LAT);
+      double maxLat = Math.max(ll[LAT], ur[LAT]);
+      range = subSF.getType().getRangeQuery(parser, subSF,
+              "-90", String.valueOf(maxLat), true, true);
+      result.add(range, BooleanClause.Occur.MUST);
+    } else{
+        //Latitude
+        //we may need to generate multiple queries depending on the range
+        //Are we crossing the 180 deg. longitude, if so, we need to do some special things
+        if (ll[LONG] > 0.0 && ur[LONG] < 0.0) {
+          //TODO: refactor into common code, etc.
+          //Now check other side of the Equator
+          if (ll[LAT] < 0.0 && ur[LAT] > 0.0) {
+            addEquatorialBoundary(parser, options, result, ur[LAT], ll[LAT]);
+          } //check poles
+          else {
+            subSF = subField(options.field, LAT);
+            //not crossing the equator
+            range = subSF.getType().getRangeQuery(parser, subSF,
+                    String.valueOf(ll[LAT]),
+                    String.valueOf(ur[LAT]), true, true);
+            result.add(range, BooleanClause.Occur.MUST);
+          }
+          //Longitude
+          addMeridianBoundary(parser, options, result, ur[LONG], ll[LONG], "180.0", "-180.0");
+
+        } else if (ll[LONG] < 0.0 && ur[LONG] > 0.0) {//prime meridian (0 degrees
+          //Now check other side of the Equator
+          if (ll[LAT] < 0.0 && ur[LAT] > 0.0) {
+            addEquatorialBoundary(parser, options, result, ur[LAT], ll[LAT]);
+          } else {
+            subSF = subField(options.field, LAT);
+            //not crossing the equator
+            range = subSF.getType().getRangeQuery(parser, subSF,
+                    String.valueOf(ll[LAT]),
+                    String.valueOf(ur[LAT]), true, true);
+            result.add(range, BooleanClause.Occur.MUST);
+          }
+          //Longitude
+          addMeridianBoundary(parser, options, result, ur[LONG], ll[LONG], "0.0", ".0");
+
+        } else {// we are all in the Eastern or Western hemi
+          //Now check other side of the Equator
+          if (ll[LAT] < 0.0 && ur[LAT] > 0.0) {
+            addEquatorialBoundary(parser, options, result, ur[LAT], ll[LAT]);
+          } else {//we are all in either the Northern or the Southern Hemi.
+            //TODO: nice to move this up so that it is the first thing and we can avoid the extra checks since
+            //this is actually the most likely case
+            subSF = subField(options.field, LAT);
+            range = subSF.getType().getRangeQuery(parser, subSF,
+                    String.valueOf(ll[LAT]),
+                    String.valueOf(ur[LAT]), true, true);
+            result.add(range, BooleanClause.Occur.MUST);
+
+          }
+          //Longitude, all in the same hemi
+          subSF = subField(options.field, LONG);
+          range = subSF.getType().getRangeQuery(parser, subSF,
+                  String.valueOf(ll[LONG]),
+                  String.valueOf(ur[LONG]), true, true);
+          result.add(range, BooleanClause.Occur.MUST);
+        }
+      }
+
+      return result;
+    }
+
+    /**
+     * Add a boundary condition around a meridian
+     * @param parser
+     * @param options
+     * @param result
+     * @param upperRightLon
+     * @param lowerLeftLon
+     * @param eastern
+     * @param western
+     */
+
+  private void addMeridianBoundary(QParser parser, SpatialOptions options, BooleanQuery result, double upperRightLon,
+                                    double lowerLeftLon, String eastern, String western) {
+    SchemaField subSF;
+    Query range;
+    BooleanQuery lonQ = new BooleanQuery();
+    subSF = subField(options.field, LONG);
+    //Eastern Hemisphere
+    range = subSF.getType().getRangeQuery(parser, subSF,
+            String.valueOf(lowerLeftLon),
+            eastern, true, true);
+    lonQ.add(range, BooleanClause.Occur.SHOULD);
+    //Western hemi
+    range = subSF.getType().getRangeQuery(parser, subSF,
+            western,
+            String.valueOf(upperRightLon), true, true);
+    lonQ.add(range, BooleanClause.Occur.SHOULD);
+    //One or the other must occur
+    result.add(lonQ, BooleanClause.Occur.MUST);
+  }
+
+  /**
+   * Add query conditions for boundaries like the equator, poles and meridians
+   *
+   * @param parser
+   * @param options
+   * @param result
+   * @param upperRight
+   * @param lowerLeft
+   */
+  protected void addEquatorialBoundary(QParser parser, SpatialOptions options, BooleanQuery result, double upperRight, double lowerLeft) {
+    SchemaField subSF;
+    Query range;
+    BooleanQuery tmpQ = new BooleanQuery();
+    subSF = subField(options.field, LAT);
+    //southern hemi.
+    range = subSF.getType().getRangeQuery(parser, subSF,
+            String.valueOf(lowerLeft),
+            "0", true, true);
+    tmpQ.add(range, BooleanClause.Occur.SHOULD);
+    //northern hemi
+    range = subSF.getType().getRangeQuery(parser, subSF,
+            "0", String.valueOf(upperRight), true, true);
+    tmpQ.add(range, BooleanClause.Occur.SHOULD);
+    //One or the other must occur
+    result.add(tmpQ, BooleanClause.Occur.MUST);
+  }
+
+  @Override
+  public ValueSource getValueSource(SchemaField field, QParser parser) {
+    ArrayList<ValueSource> vs = new ArrayList<ValueSource>(2);
+    for (int i = 0; i < 2; i++) {
+      SchemaField sub = subField(field, i);
+      vs.add(sub.getType().getValueSource(sub, parser));
+    }
+    return new LatLonValueSource(field, vs);
+  }
+
+  @Override
+  public boolean isPolyField() {
+    return true;
+  }
+
+  @Override
+  public void write(XMLWriter xmlWriter, String name, Fieldable f) throws IOException {
+    xmlWriter.writeStr(name, f.stringValue());
+  }
+
+  @Override
+  public void write(TextResponseWriter writer, String name, Fieldable f) throws IOException {
+    writer.writeStr(name, f.stringValue(), false);
+  }
+
+  @Override
+  public SortField getSortField(SchemaField field, boolean top) {
+    throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Sorting not supported on SpatialTileField " + field.getName());
+  }
+
+
+
+  //It never makes sense to create a single field, so make it impossible to happen
+
+  @Override
+  public Field createField(SchemaField field, String externalVal, float boost) {
+    throw new UnsupportedOperationException("SpatialTileField uses multiple fields.  field=" + field.getName());
+  }
+
+}
+
+class LatLonValueSource extends VectorValueSource {
+  private final SchemaField sf;
+
+  public LatLonValueSource(SchemaField sf, List<ValueSource> sources) {
+    super(sources);
+    this.sf = sf;
+  }
+
+  @Override
+  public String name() {
+    return "latlon";
+  }
+
+  @Override
+  public String description() {
+    return name() + "(" + sf.getName() + ")";
+  }
+}

Propchange: lucene/dev/trunk/solr/src/java/org/apache/solr/schema/LatLonType.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/schema/PointType.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/schema/PointType.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/schema/PointType.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/schema/PointType.java Sat Jul 10 00:12:41 2010
@@ -23,15 +23,17 @@ import org.apache.lucene.search.BooleanC
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.SortField;
+import org.apache.lucene.spatial.tier.DistanceUtils;
+import org.apache.lucene.spatial.tier.InvalidGeoException;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.common.params.SolrParams;
 import org.apache.solr.response.TextResponseWriter;
 import org.apache.solr.response.XMLWriter;
 import org.apache.solr.search.QParser;
+import org.apache.solr.search.SpatialOptions;
 import org.apache.solr.search.function.VectorValueSource;
 import org.apache.solr.search.function.ValueSource;
-import org.apache.solr.search.function.distance.DistanceUtils;
 
 import java.io.IOException;
 import java.util.Map;
@@ -45,7 +47,7 @@ import java.util.ArrayList;
  * <p/>
  * NOTE: There can only be one sub type
  */
-public class PointType extends CoordinateFieldType {
+public class PointType extends CoordinateFieldType implements SpatialQueryable {
 
   @Override
   protected void init(IndexSchema schema, Map<String, String> args) {
@@ -71,7 +73,12 @@ public class PointType extends Coordinat
 
   @Override
   public Fieldable[] createFields(SchemaField field, String externalVal, float boost) {
-    String[] point = DistanceUtils.parsePoint(null, externalVal, dimension);
+    String[] point = new String[0];
+    try {
+      point = DistanceUtils.parsePoint(null, externalVal, dimension);
+    } catch (InvalidGeoException e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
+    }
 
     // TODO: this doesn't currently support polyFields as sub-field types
     Fieldable[] f = new Fieldable[ (field.indexed() ? dimension : 0) + (field.stored() ? 1 : 0) ];
@@ -103,7 +110,11 @@ public class PointType extends Coordinat
   }
 
 
-  //It never makes sense to create a single field, so make it impossible to happen
+  /**
+   * It never makes sense to create a single field, so make it impossible to happen by
+   * throwing UnsupportedOperationException
+   *
+   */
   @Override
   public Field createField(SchemaField field, String externalVal, float boost) {
     throw new UnsupportedOperationException("PointType uses multiple fields.  field=" + field.getName());
@@ -131,8 +142,14 @@ public class PointType extends Coordinat
   public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, boolean minInclusive, boolean maxInclusive) {
     //Query could look like: [x1,y1 TO x2,y2] for 2 dimension, but could look like: [x1,y1,z1 TO x2,y2,z2], and can be extrapolated to n-dimensions
     //thus, this query essentially creates a box, cube, etc.
-    String[] p1 = DistanceUtils.parsePoint(null, part1, dimension);
-    String[] p2 = DistanceUtils.parsePoint(null, part2, dimension);
+    String[] p1;
+    String[] p2;
+    try {
+      p1 = DistanceUtils.parsePoint(null, part1, dimension);
+      p2 = DistanceUtils.parsePoint(null, part2, dimension);
+    } catch (InvalidGeoException e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
+    }
     BooleanQuery result = new BooleanQuery(true);
     for (int i = 0; i < dimension; i++) {
       SchemaField subSF = subField(field, i);
@@ -144,7 +161,12 @@ public class PointType extends Coordinat
 
   @Override
   public Query getFieldQuery(QParser parser, SchemaField field, String externalVal) {
-    String[] p1 = DistanceUtils.parsePoint(null, externalVal, dimension);
+    String[] p1 = new String[0];
+    try {
+      p1 = DistanceUtils.parsePoint(null, externalVal, dimension);
+    } catch (InvalidGeoException e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
+    }
     //TODO: should we assert that p1.length == dimension?
     BooleanQuery bq = new BooleanQuery(true);
     for (int i = 0; i < dimension; i++) {
@@ -154,6 +176,43 @@ public class PointType extends Coordinat
     }
     return bq;
   }
+
+  /**
+   * Calculates the range and creates a RangeQuery (bounding box) wrapped in a BooleanQuery (unless the dimension is 1, one range for every dimension, AND'd together by a Boolean
+   * @param parser The parser
+   * @param options The {@link org.apache.solr.search.SpatialOptions} for this filter.
+   * @return The Query representing the bounding box around the point.
+   */
+  public Query createSpatialQuery(QParser parser, SpatialOptions options) {
+    Query result = null;
+    double [] point = new double[0];
+    try {
+      point = DistanceUtils.parsePointDouble(null, options.pointStr, dimension);
+    } catch (InvalidGeoException e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
+    }
+    if (dimension == 1){
+      //TODO: Handle distance measures
+      String lower = String.valueOf(point[0] - options.distance);
+      String upper = String.valueOf(point[0] + options.distance);
+      SchemaField subSF = subField(options.field, 0);
+      // points must currently be ordered... should we support specifying any two opposite corner points?
+      result = subSF.getType().getRangeQuery(parser, subSF, lower, upper, true, true);
+    } else {
+      BooleanQuery tmp = new BooleanQuery();
+      //TODO: Handle distance measures, as this assumes Euclidean
+      double [] ur = org.apache.lucene.spatial.tier.DistanceUtils.vectorBoxCorner(point, null, options.distance, true);
+      double [] ll = org.apache.lucene.spatial.tier.DistanceUtils.vectorBoxCorner(point, null, options.distance, false);
+      for (int i = 0; i < ur.length; i++) {
+        SchemaField subSF = subField(options.field, i);
+        Query range = subSF.getType().getRangeQuery(parser, subSF, String.valueOf(ll[i]), String.valueOf(ur[i]), true, true);
+        tmp.add(range, BooleanClause.Occur.MUST);
+
+      }
+      result = tmp;
+    }
+    return result;
+  }
 }
 
 

Added: lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialQueryable.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialQueryable.java?rev=962727&view=auto
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialQueryable.java (added)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialQueryable.java Sat Jul 10 00:12:41 2010
@@ -0,0 +1,35 @@
+package org.apache.solr.schema;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.Query;
+import org.apache.solr.search.QParser;
+import org.apache.solr.search.SpatialOptions;
+
+
+/**
+ * Indicate that the implementing class is capable of generating a Query against spatial resources.
+ * For example, the PointType is capable of creating a query that restricts the document space down
+ * to documents that are within a certain distance of a given point. *
+ *
+ **/
+public interface SpatialQueryable {
+
+  public Query createSpatialQuery(QParser parser, SpatialOptions options);
+}

Propchange: lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialQueryable.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialTileField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialTileField.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialTileField.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/schema/SpatialTileField.java Sat Jul 10 00:12:41 2010
@@ -1,190 +0,0 @@
-package org.apache.solr.schema;
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.Fieldable;
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.SortField;
-import org.apache.lucene.spatial.tier.projections.CartesianTierPlotter;
-import org.apache.lucene.spatial.tier.projections.IProjector;
-import org.apache.lucene.spatial.tier.projections.SinusoidalProjector;
-import org.apache.solr.common.ResourceLoader;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.params.MapSolrParams;
-import org.apache.solr.common.params.SolrParams;
-import org.apache.solr.response.TextResponseWriter;
-import org.apache.solr.response.XMLWriter;
-import org.apache.solr.search.QParser;
-import org.apache.solr.search.function.ValueSource;
-import org.apache.solr.search.function.distance.DistanceUtils;
-import org.apache.solr.util.plugin.ResourceLoaderAware;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * Represents a Tiling system for spatial data representation (lat/lon).  A Tile is like a zoom level on an
- * interactive map.
- * <p/>
- * Specify a lower and upper tile, and this will create tiles for all the levels in between, inclusive of the upper tile.
- * <p/>
- * Querying directly against this field is probably not all that useful unless you specifically know the box id
- * <p/>
- * <p/>
- * See http://wiki.apache.org/solr/SpatialSearch
- */
-public class SpatialTileField extends AbstractSubTypeFieldType implements ResourceLoaderAware {
-
-  public static final String START_LEVEL = "start";
-  public static final String END_LEVEL = "end";
-  public static final String PROJECTOR_CLASS = "projector";
-
-  private static final int DEFAULT_END_LEVEL = 15;
-
-  private static final int DEFAULT_START_LEVEL = 4;
-
-  private int start = DEFAULT_START_LEVEL, end = DEFAULT_END_LEVEL;
-  private int tileDiff;//we're going to need this over and over, so cache it.
-  private String projectorName;
-  protected List<CartesianTierPlotter> plotters;
-
-
-  @Override
-  protected void init(IndexSchema schema, Map<String, String> args) {
-    SolrParams p = new MapSolrParams(args);
-    start = p.getInt(START_LEVEL, DEFAULT_START_LEVEL);
-    end = p.getInt(END_LEVEL, DEFAULT_END_LEVEL);
-    if (end < start) {
-      //flip them around
-      int tmp = start;
-      start = end;
-      end = tmp;
-    }
-    args.remove(START_LEVEL);
-    args.remove(END_LEVEL);
-    projectorName = p.get(PROJECTOR_CLASS, SinusoidalProjector.class.getName());
-    args.remove(PROJECTOR_CLASS);
-    super.init(schema, args);
-    tileDiff = (end - start) + 1;//add one since we are inclusive of the upper tier
-    createSuffixCache(tileDiff);
-
-
-  }
-
-  public void inform(ResourceLoader loader) {
-    IProjector projector = (IProjector) loader.newInstance(projectorName);
-    if (projector != null) {
-      plotters = new ArrayList<CartesianTierPlotter>(tileDiff);
-      for (int i = start; i <= end; i++) {
-        plotters.add(new CartesianTierPlotter(i, projector, ""));
-      }
-    } else {
-      throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Could not instantiate a Projector Instance for: "
-              + projectorName + ". Make sure the " + PROJECTOR_CLASS + " attribute is set properly in the schema");
-    }
-
-  }
-
-  @Override
-  public Fieldable[] createFields(SchemaField field, String externalVal, float boost) {
-    Fieldable[] f = new Fieldable[(field.indexed() ? tileDiff : 0) + (field.stored() ? 1 : 0)];
-    if (field.indexed()) {
-      int i = 0;
-      double[] latLon = DistanceUtils.parseLatitudeLongitude(null, externalVal);
-      for (CartesianTierPlotter plotter : plotters) {
-        double boxId = plotter.getTierBoxId(latLon[0], latLon[1]);
-        f[i] = subField(field, i).createField(String.valueOf(boxId), boost);
-        i++;
-      }
-    }
-
-    if (field.stored()) {
-      String storedVal = externalVal;  // normalize or not?
-      f[f.length - 1] = createField(field.getName(), storedVal,
-              getFieldStore(field, storedVal), Field.Index.NO, Field.TermVector.NO,
-              false, false, boost);
-    }
-    return f;
-  }
-
-  //The externalVal here is a box id, as it doesn't make sense to pick a specific tile since that requires a distance
-  //so, just OR together a search against all the tile
-
-  @Override
-  public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, boolean minInclusive,
-                             boolean maxInclusive) {
-    BooleanQuery bq = new BooleanQuery(true);
-    for (int i = 0; i < tileDiff; i++) {
-      SchemaField sf = subField(field, i);
-      Query tq = sf.getType().getRangeQuery(parser, sf, part1, part2, minInclusive, maxInclusive);
-      bq.add(tq, BooleanClause.Occur.SHOULD);
-    }
-    return bq;
-  }
-
-  @Override
-  public Query getFieldQuery(QParser parser, SchemaField field, String externalVal) {
-    //The externalVal here is a box id, as it doesn't make sense to pick a specific tile since that requires a distance
-    //so, just OR together a search against all the tiles
-    BooleanQuery bq = new BooleanQuery(true);
-    for (int i = 0; i < tileDiff; i++) {
-      SchemaField sf = subField(field, i);
-      Query tq = sf.getType().getFieldQuery(parser, sf, externalVal);
-      bq.add(tq, BooleanClause.Occur.SHOULD);
-    }
-    return bq;
-  }
-
-  @Override
-  public boolean isPolyField() {
-    return true;
-  }
-
-  @Override
-  public void write(XMLWriter xmlWriter, String name, Fieldable f) throws IOException {
-    xmlWriter.writeStr(name, f.stringValue());
-  }
-
-  @Override
-  public void write(TextResponseWriter writer, String name, Fieldable f) throws IOException {
-    writer.writeStr(name, f.stringValue(), false);
-  }
-
-  @Override
-  public SortField getSortField(SchemaField field, boolean top) {
-    throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Sorting not supported on SpatialTileField " + field.getName());
-  }
-
-  @Override
-  public ValueSource getValueSource(SchemaField field, QParser parser) {
-    //TODO: Should this really throw UOE?  What does it mean for a function to use the values of a tier?  Let's leave it unsupported for now
-    throw new UnsupportedOperationException("SpatialTileField uses multiple fields and does not support ValueSource");
-  }
-
-  //It never makes sense to create a single field, so make it impossible to happen
-
-  @Override
-  public Field createField(SchemaField field, String externalVal, float boost) {
-    throw new UnsupportedOperationException("SpatialTileField uses multiple fields.  field=" + field.getName());
-  }
-}

Modified: lucene/dev/trunk/solr/src/java/org/apache/solr/search/QParserPlugin.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/src/java/org/apache/solr/search/QParserPlugin.java?rev=962727&r1=962726&r2=962727&view=diff
==============================================================================
--- lucene/dev/trunk/solr/src/java/org/apache/solr/search/QParserPlugin.java (original)
+++ lucene/dev/trunk/solr/src/java/org/apache/solr/search/QParserPlugin.java Sat Jul 10 00:12:41 2010
@@ -37,6 +37,7 @@ public abstract class QParserPlugin impl
     RawQParserPlugin.NAME, RawQParserPlugin.class,
     NestedQParserPlugin.NAME, NestedQParserPlugin.class,
     FunctionRangeQParserPlugin.NAME, FunctionRangeQParserPlugin.class,
+    SpatialFilterQParserPlugin.NAME, SpatialFilterQParserPlugin.class,
   };
 
   /** return a {@link QParser} */



Mime
View raw message