You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ry...@apache.org on 2012/06/28 09:36:14 UTC

svn commit: r1354841 - in /lucene/dev/trunk/lucene/spatial/src: java/org/apache/lucene/spatial/bbox/ test-files/ test-files/data/ test/org/apache/lucene/spatial/ test/org/apache/lucene/spatial/bbox/

Author: ryan
Date: Thu Jun 28 07:36:12 2012
New Revision: 1354841

URL: http://svn.apache.org/viewvc?rev=1354841&view=rev
Log:
LUCENE-4175: adding bbox strategy

Added:
    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/
    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java
    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java
    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java
    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java
    lucene/dev/trunk/lucene/spatial/src/test-files/data/simple-bbox.txt
    lucene/dev/trunk/lucene/spatial/src/test-files/simple-Queries-BBox.txt
    lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/
    lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java
Modified:
    lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java
    lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java

Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java?rev=1354841&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java (added)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java Thu Jun 28 07:36:12 2012
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial.bbox;
+
+import org.apache.lucene.search.Explanation;
+
+import com.spatial4j.core.shape.Rectangle;
+
+/**
+ * The algorithm is implemented as envelope on envelope overlays rather than
+ * complex polygon on complex polygon overlays.
+ * <p/>
+ * <p/>
+ * Spatial relevance scoring algorithm:
+ * <p/>
+ * <br/>  queryArea = the area of the input query envelope
+ * <br/>  targetArea = the area of the target envelope (per Lucene document)
+ * <br/>  intersectionArea = the area of the intersection for the query/target envelopes
+ * <br/>  queryPower = the weighting power associated with the query envelope (default = 1.0)
+ * <br/>  targetPower =  the weighting power associated with the target envelope (default = 1.0)
+ * <p/>
+ * <br/>  queryRatio  = intersectionArea / queryArea;
+ * <br/>  targetRatio = intersectionArea / targetArea;
+ * <br/>  queryFactor  = Math.pow(queryRatio,queryPower);
+ * <br/>  targetFactor = Math.pow(targetRatio,targetPower);
+ * <br/>  score = queryFactor * targetFactor;
+ * <p/>
+ * original:
+ * http://geoportal.svn.sourceforge.net/svnroot/geoportal/Geoportal/trunk/src/com/esri/gpt/catalog/lucene/SpatialRankingValueSource.java
+ */
+public class AreaSimilarity implements BBoxSimilarity {
+  /**
+   * Properties associated with the query envelope
+   */
+  private final Rectangle queryExtent;
+  private final double queryArea;
+
+  private final double targetPower;
+  private final double queryPower;
+
+  public AreaSimilarity(Rectangle queryExtent, double queryPower, double targetPower) {
+    this.queryExtent = queryExtent;
+    this.queryArea = queryExtent.getArea();
+
+    this.queryPower = queryPower;
+    this.targetPower = targetPower;
+
+//  if (this.qryMinX > queryExtent.getMaxX()) {
+//    this.qryCrossedDateline = true;
+//    this.qryArea = Math.abs(qryMaxX + 360.0 - qryMinX) * Math.abs(qryMaxY - qryMinY);
+//  } else {
+//    this.qryArea = Math.abs(qryMaxX - qryMinX) * Math.abs(qryMaxY - qryMinY);
+//  }
+  }
+
+  public AreaSimilarity(Rectangle queryExtent) {
+    this(queryExtent, 2.0, 0.5);
+  }
+
+
+  public String getDelimiterQueryParameters() {
+    return queryExtent.toString() + ";" + queryPower + ";" + targetPower;
+  }
+
+  @Override
+  public double score(Rectangle target, Explanation exp) {
+    if (target == null || queryArea <= 0) {
+      return 0;
+    }
+    double targetArea = target.getArea();
+    if (targetArea <= 0) {
+      return 0;
+    }
+    double score = 0;
+
+    double top = Math.min(queryExtent.getMaxY(), target.getMaxY());
+    double bottom = Math.max(queryExtent.getMinY(), target.getMinY());
+    double height = top - bottom;
+    double width = 0;
+
+    // queries that cross the date line
+    if (queryExtent.getCrossesDateLine()) {
+      // documents that cross the date line
+      if (target.getCrossesDateLine()) {
+        double left = Math.max(queryExtent.getMinX(), target.getMinX());
+        double right = Math.min(queryExtent.getMaxX(), target.getMaxX());
+        width = right + 360.0 - left;
+      } else {
+        double qryWestLeft = Math.max(queryExtent.getMinX(), target.getMaxX());
+        double qryWestRight = Math.min(target.getMaxX(), 180.0);
+        double qryWestWidth = qryWestRight - qryWestLeft;
+        if (qryWestWidth > 0) {
+          width = qryWestWidth;
+        } else {
+          double qryEastLeft = Math.max(target.getMaxX(), -180.0);
+          double qryEastRight = Math.min(queryExtent.getMaxX(), target.getMaxX());
+          double qryEastWidth = qryEastRight - qryEastLeft;
+          if (qryEastWidth > 0) {
+            width = qryEastWidth;
+          }
+        }
+      }
+    } else { // queries that do not cross the date line
+
+      if (target.getCrossesDateLine()) {
+        double tgtWestLeft = Math.max(queryExtent.getMinX(), target.getMinX());
+        double tgtWestRight = Math.min(queryExtent.getMaxX(), 180.0);
+        double tgtWestWidth = tgtWestRight - tgtWestLeft;
+        if (tgtWestWidth > 0) {
+          width = tgtWestWidth;
+        } else {
+          double tgtEastLeft = Math.max(queryExtent.getMinX(), -180.0);
+          double tgtEastRight = Math.min(queryExtent.getMaxX(), target.getMaxX());
+          double tgtEastWidth = tgtEastRight - tgtEastLeft;
+          if (tgtEastWidth > 0) {
+            width = tgtEastWidth;
+          }
+        }
+      } else {
+        double left = Math.max(queryExtent.getMinX(), target.getMinX());
+        double right = Math.min(queryExtent.getMaxX(), target.getMaxX());
+        width = right - left;
+      }
+    }
+
+
+    // calculate the score
+    if ((width > 0) && (height > 0)) {
+      double intersectionArea = width * height;
+      double queryRatio = intersectionArea / queryArea;
+      double targetRatio = intersectionArea / targetArea;
+      double queryFactor = Math.pow(queryRatio, queryPower);
+      double targetFactor = Math.pow(targetRatio, targetPower);
+      score = queryFactor * targetFactor * 10000.0;
+
+      if (exp!=null) {
+//        StringBuilder sb = new StringBuilder();
+//        sb.append("\nscore=").append(score);
+//        sb.append("\n  query=").append();
+//        sb.append("\n  target=").append(target.toString());
+//        sb.append("\n  intersectionArea=").append(intersectionArea);
+//        
+//        sb.append(" queryArea=").append(queryArea).append(" targetArea=").append(targetArea);
+//        sb.append("\n  queryRatio=").append(queryRatio).append(" targetRatio=").append(targetRatio);
+//        sb.append("\n  queryFactor=").append(queryFactor).append(" targetFactor=").append(targetFactor);
+//        sb.append(" (queryPower=").append(queryPower).append(" targetPower=").append(targetPower).append(")");
+        
+        exp.setValue((float)score);
+        exp.setDescription(this.getClass().getSimpleName());
+        
+        Explanation e = null;
+        
+        exp.addDetail( e = new Explanation((float)intersectionArea, "IntersectionArea") );
+        e.addDetail(new Explanation((float)width,  "width; Query: "+queryExtent.toString()));
+        e.addDetail(new Explanation((float)height, "height; Target: "+target.toString()));
+
+        exp.addDetail( e = new Explanation((float)queryFactor, "Query") );
+        e.addDetail(new Explanation((float)queryArea, "area"));
+        e.addDetail(new Explanation((float)queryRatio, "ratio"));
+        e.addDetail(new Explanation((float)queryPower, "power"));
+
+        exp.addDetail( e = new Explanation((float)targetFactor, "Target") );
+        e.addDetail(new Explanation((float)targetArea, "area"));
+        e.addDetail(new Explanation((float)targetRatio, "ratio"));
+        e.addDetail(new Explanation((float)targetPower, "power"));
+      }
+    }
+    else if(exp !=null) {
+      exp.setValue(0);
+      exp.setDescription("Shape does not intersect");
+    }
+    return score;
+  }
+
+
+  /**
+   * Determines if this ValueSource is equal to another.
+   *
+   * @param o the ValueSource to compare
+   * @return <code>true</code> if the two objects are based upon the same query envelope
+   */
+  @Override
+  public boolean equals(Object o) {
+    if (o.getClass() != AreaSimilarity.class)
+      return false;
+
+    AreaSimilarity other = (AreaSimilarity) o;
+    return getDelimiterQueryParameters().equals(other.getDelimiterQueryParameters());
+  }
+
+  /**
+   * Returns the ValueSource hash code.
+   *
+   * @return the hash code
+   */
+  @Override
+  public int hashCode() {
+    return getDelimiterQueryParameters().hashCode();
+  }
+}

Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java?rev=1354841&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java (added)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java Thu Jun 28 07:36:12 2012
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial.bbox;
+
+import org.apache.lucene.spatial.SpatialFieldInfo;
+
+
+/**
+ * The Bounding Box gets stored as four fields for x/y min/max and a flag 
+ * that says if the box crosses the dateline (xdl)
+ */
+public class BBoxFieldInfo implements SpatialFieldInfo {
+
+  public static final String SUFFIX_MINX = "__minX";
+  public static final String SUFFIX_MAXX = "__maxX";
+  public static final String SUFFIX_MINY = "__minY";
+  public static final String SUFFIX_MAXY = "__maxY";
+  public static final String SUFFIX_XDL  = "__xdl";
+
+  public String bbox = "bbox";
+  public String minX = "bbox.minx";
+  public String minY = "bbox.miny";
+  public String maxX = "bbox.maxx";
+  public String maxY = "bbox.maxy";
+  public String xdl  = "bbox.xdl"; // crosses dateline
+
+  public BBoxFieldInfo() {
+
+  }
+
+  public BBoxFieldInfo( String p ) {
+    this.setFieldsPrefix( p );
+  }
+
+  public void setFieldsPrefix(String prefix) {
+    bbox = prefix;
+    minX = prefix + SUFFIX_MINX;
+    maxX = prefix + SUFFIX_MAXX;
+    minY = prefix + SUFFIX_MINY;
+    maxY = prefix + SUFFIX_MAXY;
+    xdl  = prefix + SUFFIX_XDL;
+  }
+}

Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java?rev=1354841&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java (added)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java Thu Jun 28 07:36:12 2012
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial.bbox;
+
+import org.apache.lucene.search.Explanation;
+
+import com.spatial4j.core.shape.Rectangle;
+
+
+
+public interface BBoxSimilarity {
+
+  public double score(Rectangle extent, Explanation exp);
+}

Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java?rev=1354841&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java (added)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java Thu Jun 28 07:36:12 2012
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial.bbox;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.AtomicReader;
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.util.Bits;
+
+import com.spatial4j.core.shape.Rectangle;
+import com.spatial4j.core.shape.simple.RectangleImpl;
+
+/**
+ * An implementation of the Lucene ValueSource model to support spatial relevance ranking.
+ */
+public class BBoxSimilarityValueSource extends ValueSource {
+
+  private final BBoxFieldInfo field;
+  private final BBoxSimilarity similarity;
+
+  /**
+   * Constructor.
+   *
+   * @param queryEnvelope the query envelope
+   * @param queryPower the query power (scoring algorithm)
+   * @param targetPower the target power (scoring algorithm)
+   */
+  public BBoxSimilarityValueSource(BBoxSimilarity similarity, BBoxFieldInfo field) {
+    this.similarity = similarity;
+    this.field = field;
+  }
+
+  /**
+   * Returns the ValueSource description.
+   *
+   * @return the description
+   */
+  @Override
+  public String description() {
+    return "BBoxSimilarityValueSource(" + similarity + ")";
+  }
+
+
+  /**
+   * Returns the DocValues used by the function query.
+   *
+   * @param reader the index reader
+   * @return the values
+   */
+  @Override
+  public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
+    AtomicReader reader = readerContext.reader();
+    final double[] minX = FieldCache.DEFAULT.getDoubles(reader, field.minX, true);
+    final double[] minY = FieldCache.DEFAULT.getDoubles(reader, field.minY, true);
+    final double[] maxX = FieldCache.DEFAULT.getDoubles(reader, field.maxX, true);
+    final double[] maxY = FieldCache.DEFAULT.getDoubles(reader, field.maxY, true);
+
+    final Bits validMinX = FieldCache.DEFAULT.getDocsWithField(reader, field.minX);
+    final Bits validMaxX = FieldCache.DEFAULT.getDocsWithField(reader, field.maxX);
+
+    return new FunctionValues() {
+      @Override
+      public float floatVal(int doc) {
+        // make sure it has minX and area
+        if (validMinX.get(doc) && validMaxX.get(doc)) {
+          Rectangle rect = new RectangleImpl(
+              minX[doc], maxX[doc],
+              minY[doc], maxY[doc]);
+          return (float) similarity.score(rect, null);
+        }
+        return 0;
+      }
+
+      public Explanation explain(int doc) {
+        // make sure it has minX and area
+        if (validMinX.get(doc) && validMaxX.get(doc)) {
+          Rectangle rect = new RectangleImpl(
+              minX[doc], maxX[doc],
+              minY[doc], maxY[doc]);
+          Explanation exp = new Explanation();
+          similarity.score(rect, exp);
+          return exp;
+        }
+        return new Explanation(0, "No BBox");
+      }
+
+      @Override
+      public String toString(int doc) {
+        return description() + "=" + floatVal(doc);
+      }
+    };
+  }
+
+  /**
+   * Determines if this ValueSource is equal to another.
+   *
+   * @param o the ValueSource to compare
+   * @return <code>true</code> if the two objects are based upon the same query envelope
+   */
+  @Override
+  public boolean equals(Object o) {
+    if (o.getClass() != BBoxSimilarityValueSource.class) {
+      return false;
+    }
+
+    BBoxSimilarityValueSource other = (BBoxSimilarityValueSource) o;
+    return similarity.equals(other.similarity);
+  }
+
+  @Override
+  public int hashCode() {
+    return BBoxSimilarityValueSource.class.hashCode() + similarity.hashCode();
+  }
+}

Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java?rev=1354841&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java (added)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java Thu Jun 28 07:36:12 2012
@@ -0,0 +1,472 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial.bbox;
+
+import java.text.NumberFormat;
+import java.util.Locale;
+
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.FieldType;
+import org.apache.lucene.index.FieldInfo.IndexOptions;
+import org.apache.lucene.index.IndexableField;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queries.function.FunctionQuery;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.ConstantScoreQuery;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.NumericRangeQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryWrapperFilter;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.spatial.SpatialStrategy;
+import org.apache.lucene.spatial.util.NumericFieldInfo;
+
+import com.spatial4j.core.context.*;
+import com.spatial4j.core.exception.UnsupportedSpatialOperation;
+import com.spatial4j.core.query.*;
+import com.spatial4j.core.shape.*;
+
+
+/**
+ * original:
+ * http://geoportal.svn.sourceforge.net/svnroot/geoportal/Geoportal/trunk/src/com/esri/gpt/catalog/lucene/SpatialClauseAdapter.java
+ */
+public class BBoxStrategy extends SpatialStrategy<BBoxFieldInfo> {
+  public double queryPower = 1.0;
+  public double targetPower = 1.0f;
+
+  public NumericFieldInfo finfo = null;
+
+  public BBoxStrategy(SpatialContext ctx) {
+    super(ctx);
+  }
+
+  //---------------------------------
+  // Indexing
+  //---------------------------------
+
+  @Override
+  public IndexableField[] createFields(BBoxFieldInfo fieldInfo,
+      Shape shape, boolean index, boolean store) {
+
+    Rectangle bbox = shape.getBoundingBox();
+    IndexableField[] fields = new IndexableField[store?6:5];
+    fields[0] = finfo.createDouble(fieldInfo.minX, bbox.getMinX());
+    fields[1] = finfo.createDouble(fieldInfo.maxX, bbox.getMaxX());
+    fields[2] = finfo.createDouble(fieldInfo.minY, bbox.getMinY());
+    fields[3] = finfo.createDouble(fieldInfo.maxY, bbox.getMaxY());
+
+    FieldType ft = new FieldType();
+    ft.setIndexed(index);
+    ft.setStored(store);
+    ft.setTokenized(false);
+    ft.setOmitNorms(true);
+    ft.setIndexOptions(IndexOptions.DOCS_ONLY);
+    ft.freeze();
+
+    Field xdl = new Field( fieldInfo.xdl, bbox.getCrossesDateLine()?"T":"F", ft );
+    fields[4] = xdl;
+    if( store ) {
+      FieldType ff = new FieldType();
+      ff.setIndexed(false);
+      ff.setStored(true);
+      ff.setOmitNorms(true);
+      ff.setIndexOptions(IndexOptions.DOCS_ONLY);
+      ff.freeze();
+
+      NumberFormat nf = NumberFormat.getInstance( Locale.US );
+      nf.setMaximumFractionDigits( 5 );
+      nf.setMinimumFractionDigits( 5 );
+      nf.setGroupingUsed(false);
+      String ext =
+        nf.format( bbox.getMinX() ) + ' ' +
+        nf.format( bbox.getMinY() ) + ' ' +
+        nf.format( bbox.getMaxX() ) + ' ' +
+        nf.format( bbox.getMaxY() ) + ' ';
+      fields[5] = new Field( fieldInfo.bbox, ext, ff );
+    }
+    return fields;
+  }
+
+  @Override
+  public IndexableField createField(BBoxFieldInfo fieldInfo, Shape shape,
+      boolean index, boolean store) {
+    throw new UnsupportedOperationException("BBOX is poly field");
+  }
+
+  @Override
+  public boolean isPolyField() {
+    return true;
+  }
+
+  //---------------------------------
+  // Query Builder
+  //---------------------------------
+
+  @Override
+  public ValueSource makeValueSource(SpatialArgs args, BBoxFieldInfo fields) {
+    return new BBoxSimilarityValueSource(
+        new AreaSimilarity(args.getShape().getBoundingBox(), queryPower, targetPower), fields );
+  }
+
+
+  @Override
+  public Filter makeFilter(SpatialArgs args, BBoxFieldInfo fieldInfo) {
+    Query spatial = makeSpatialQuery(args, fieldInfo);
+    return new QueryWrapperFilter( spatial );
+  }
+
+  @Override
+  public Query makeQuery(SpatialArgs args, BBoxFieldInfo fieldInfo) {
+    BooleanQuery bq = new BooleanQuery();
+    Query spatial = makeSpatialQuery(args, fieldInfo);
+    bq.add(new ConstantScoreQuery(spatial), BooleanClause.Occur.MUST);
+    
+    // This part does the scoring
+    Query spatialRankingQuery = new FunctionQuery(makeValueSource(args, fieldInfo));
+    bq.add(spatialRankingQuery, BooleanClause.Occur.MUST);
+    return bq;
+  }
+
+
+  private Query makeSpatialQuery(SpatialArgs args, BBoxFieldInfo fieldInfo) {
+    Rectangle bbox = args.getShape().getBoundingBox();
+    Query spatial = null;
+
+    // Useful for understanding Relations:
+    // http://edndoc.esri.com/arcsde/9.1/general_topics/understand_spatial_relations.htm
+    SpatialOperation op = args.getOperation();
+         if( op == SpatialOperation.BBoxIntersects ) spatial = makeIntersects(bbox, fieldInfo);
+    else if( op == SpatialOperation.BBoxWithin     ) spatial = makeWithin(bbox, fieldInfo);
+    else if( op == SpatialOperation.Contains       ) spatial = makeContains(bbox, fieldInfo);
+    else if( op == SpatialOperation.Intersects     ) spatial = makeIntersects(bbox, fieldInfo);
+    else if( op == SpatialOperation.IsEqualTo      ) spatial = makeEquals(bbox, fieldInfo);
+    else if( op == SpatialOperation.IsDisjointTo   ) spatial = makeDisjoint(bbox, fieldInfo);
+    else if( op == SpatialOperation.IsWithin       ) spatial = makeWithin(bbox, fieldInfo);
+    else if( op == SpatialOperation.Overlaps       ) spatial = makeIntersects(bbox, fieldInfo);
+    else {
+        throw new UnsupportedSpatialOperation(op);
+    }
+    return spatial;
+  }
+
+
+  //-------------------------------------------------------------------------------
+  //
+  //-------------------------------------------------------------------------------
+
+  /**
+   * Constructs a query to retrieve documents that fully contain the input envelope.
+   *
+   * @return the spatial query
+   */
+  Query makeContains(Rectangle bbox, BBoxFieldInfo fieldInfo) {
+
+    // general case
+    // docMinX <= queryExtent.getMinX() AND docMinY <= queryExtent.getMinY() AND docMaxX >= queryExtent.getMaxX() AND docMaxY >= queryExtent.getMaxY()
+
+    // Y conditions
+    // docMinY <= queryExtent.getMinY() AND docMaxY >= queryExtent.getMaxY()
+    Query qMinY = NumericRangeQuery.newDoubleRange(fieldInfo.minY, finfo.precisionStep, null, bbox.getMinY(), false, true);
+    Query qMaxY = NumericRangeQuery.newDoubleRange(fieldInfo.maxY, finfo.precisionStep, bbox.getMaxY(), null, true, false);
+    Query yConditions = this.makeQuery(new Query[]{qMinY, qMaxY}, BooleanClause.Occur.MUST);
+
+    // X conditions
+    Query xConditions = null;
+
+    // queries that do not cross the date line
+    if (!bbox.getCrossesDateLine()) {
+
+      // X Conditions for documents that do not cross the date line,
+      // documents that contain the min X and max X of the query envelope,
+      // docMinX <= queryExtent.getMinX() AND docMaxX >= queryExtent.getMaxX()
+      Query qMinX = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, null, bbox.getMinX(), false, true);
+      Query qMaxX = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, bbox.getMaxX(), null, true, false);
+      Query qMinMax = this.makeQuery(new Query[]{qMinX, qMaxX}, BooleanClause.Occur.MUST);
+      Query qNonXDL = this.makeXDL(false, qMinMax, fieldInfo);
+
+      // X Conditions for documents that cross the date line,
+      // the left portion of the document contains the min X of the query
+      // OR the right portion of the document contains the max X of the query,
+      // docMinXLeft <= queryExtent.getMinX() OR docMaxXRight >= queryExtent.getMaxX()
+      Query qXDLLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, null, bbox.getMinX(), false, true);
+      Query qXDLRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, bbox.getMaxX(), null, true, false);
+      Query qXDLLeftRight = this.makeQuery(new Query[]{qXDLLeft, qXDLRight}, BooleanClause.Occur.SHOULD);
+      Query qXDL = this.makeXDL(true, qXDLLeftRight, fieldInfo);
+
+      // apply the non-XDL and XDL conditions
+      xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD);
+
+      // queries that cross the date line
+    } else {
+
+      // No need to search for documents that do not cross the date line
+
+      // X Conditions for documents that cross the date line,
+      // the left portion of the document contains the min X of the query
+      // AND the right portion of the document contains the max X of the query,
+      // docMinXLeft <= queryExtent.getMinX() AND docMaxXRight >= queryExtent.getMaxX()
+      Query qXDLLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, null, bbox.getMinX(), false, true);
+      Query qXDLRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, bbox.getMaxX(), null, true, false);
+      Query qXDLLeftRight = this.makeQuery(new Query[]{qXDLLeft, qXDLRight}, BooleanClause.Occur.MUST);
+
+      xConditions = this.makeXDL(true, qXDLLeftRight, fieldInfo);
+    }
+
+    // both X and Y conditions must occur
+    return this.makeQuery(new Query[]{xConditions, yConditions}, BooleanClause.Occur.MUST);
+  }
+
+  /**
+   * Constructs a query to retrieve documents that are disjoint to the input envelope.
+   *
+   * @return the spatial query
+   */
+  Query makeDisjoint(Rectangle bbox, BBoxFieldInfo fieldInfo) {
+
+    // general case
+    // docMinX > queryExtent.getMaxX() OR docMaxX < queryExtent.getMinX() OR docMinY > queryExtent.getMaxY() OR docMaxY < queryExtent.getMinY()
+
+    // Y conditions
+    // docMinY > queryExtent.getMaxY() OR docMaxY < queryExtent.getMinY()
+    Query qMinY = NumericRangeQuery.newDoubleRange(fieldInfo.minY, finfo.precisionStep, bbox.getMaxY(), null, false, false);
+    Query qMaxY = NumericRangeQuery.newDoubleRange(fieldInfo.maxY, finfo.precisionStep, null, bbox.getMinY(), false, false);
+    Query yConditions = this.makeQuery(new Query[]{qMinY, qMaxY}, BooleanClause.Occur.SHOULD);
+
+    // X conditions
+    Query xConditions = null;
+
+    // queries that do not cross the date line
+    if (!bbox.getCrossesDateLine()) {
+
+      // X Conditions for documents that do not cross the date line,
+      // docMinX > queryExtent.getMaxX() OR docMaxX < queryExtent.getMinX()
+      Query qMinX = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMaxX(), null, false, false);
+      Query qMaxX = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMinX(), false, false);
+      Query qMinMax = this.makeQuery(new Query[]{qMinX, qMaxX}, BooleanClause.Occur.SHOULD);
+      Query qNonXDL = this.makeXDL(false, qMinMax, fieldInfo);
+
+      // X Conditions for documents that cross the date line,
+      // both the left and right portions of the document must be disjoint to the query
+      // (docMinXLeft > queryExtent.getMaxX() OR docMaxXLeft < queryExtent.getMinX()) AND
+      // (docMinXRight > queryExtent.getMaxX() OR docMaxXRight < queryExtent.getMinX())
+      // where: docMaxXLeft = 180.0, docMinXRight = -180.0
+      // (docMaxXLeft  < queryExtent.getMinX()) equates to (180.0  < queryExtent.getMinX()) and is ignored
+      // (docMinXRight > queryExtent.getMaxX()) equates to (-180.0 > queryExtent.getMaxX()) and is ignored
+      Query qMinXLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMaxX(), null, false, false);
+      Query qMaxXRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMinX(), false, false);
+      Query qLeftRight = this.makeQuery(new Query[]{qMinXLeft, qMaxXRight}, BooleanClause.Occur.MUST);
+      Query qXDL = this.makeXDL(true, qLeftRight, fieldInfo);
+
+      // apply the non-XDL and XDL conditions
+      xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD);
+
+      // queries that cross the date line
+    } else {
+
+      // X Conditions for documents that do not cross the date line,
+      // the document must be disjoint to both the left and right query portions
+      // (docMinX > queryExtent.getMaxX()Left OR docMaxX < queryExtent.getMinX()) AND (docMinX > queryExtent.getMaxX() OR docMaxX < queryExtent.getMinX()Left)
+      // where: queryExtent.getMaxX()Left = 180.0, queryExtent.getMinX()Left = -180.0
+      Query qMinXLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, 180.0, null, false, false);
+      Query qMaxXLeft = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMinX(), false, false);
+      Query qMinXRight = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMaxX(), null, false, false);
+      Query qMaxXRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, -180.0, false, false);
+      Query qLeft = this.makeQuery(new Query[]{qMinXLeft, qMaxXLeft}, BooleanClause.Occur.SHOULD);
+      Query qRight = this.makeQuery(new Query[]{qMinXRight, qMaxXRight}, BooleanClause.Occur.SHOULD);
+      Query qLeftRight = this.makeQuery(new Query[]{qLeft, qRight}, BooleanClause.Occur.MUST);
+
+      // No need to search for documents that do not cross the date line
+
+      xConditions = this.makeXDL(false, qLeftRight, fieldInfo);
+    }
+
+    // either X or Y conditions should occur
+    return this.makeQuery(new Query[]{xConditions, yConditions}, BooleanClause.Occur.SHOULD);
+  }
+
+  /**
+   * Constructs a query to retrieve documents that equal the input envelope.
+   *
+   * @return the spatial query
+   */
+  Query makeEquals(Rectangle bbox, BBoxFieldInfo fieldInfo) {
+
+    // docMinX = queryExtent.getMinX() AND docMinY = queryExtent.getMinY() AND docMaxX = queryExtent.getMaxX() AND docMaxY = queryExtent.getMaxY()
+    Query qMinX = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMinX(), bbox.getMinX(), true, true);
+    Query qMinY = NumericRangeQuery.newDoubleRange(fieldInfo.minY, finfo.precisionStep, bbox.getMinY(), bbox.getMinY(), true, true);
+    Query qMaxX = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, bbox.getMaxX(), bbox.getMaxX(), true, true);
+    Query qMaxY = NumericRangeQuery.newDoubleRange(fieldInfo.maxY, finfo.precisionStep, bbox.getMaxY(), bbox.getMaxY(), true, true);
+    BooleanQuery bq = new BooleanQuery();
+    bq.add(qMinX, BooleanClause.Occur.MUST);
+    bq.add(qMinY, BooleanClause.Occur.MUST);
+    bq.add(qMaxX, BooleanClause.Occur.MUST);
+    bq.add(qMaxY, BooleanClause.Occur.MUST);
+    return bq;
+  }
+
+  /**
+   * Constructs a query to retrieve documents that intersect the input envelope.
+   *
+   * @return the spatial query
+   */
+  Query makeIntersects(Rectangle bbox, BBoxFieldInfo fieldInfo) {
+
+    // the original intersects query does not work for envelopes that cross the date line,
+    // switch to a NOT Disjoint query
+
+    // MUST_NOT causes a problem when it's the only clause type within a BooleanQuery,
+    // to get round it we add all documents as a SHOULD
+
+    // there must be an envelope, it must not be disjoint
+    Query qDisjoint = makeDisjoint(bbox, fieldInfo);
+    Query qIsNonXDL = this.makeXDL(false, fieldInfo);
+    Query qIsXDL = this.makeXDL(true, fieldInfo);
+    Query qHasEnv = this.makeQuery(new Query[]{qIsNonXDL, qIsXDL}, BooleanClause.Occur.SHOULD);
+    BooleanQuery qNotDisjoint = new BooleanQuery();
+    qNotDisjoint.add(qHasEnv, BooleanClause.Occur.MUST);
+    qNotDisjoint.add(qDisjoint, BooleanClause.Occur.MUST_NOT);
+
+    //Query qDisjoint = makeDisjoint();
+    //BooleanQuery qNotDisjoint = new BooleanQuery();
+    //qNotDisjoint.add(new MatchAllDocsQuery(),BooleanClause.Occur.SHOULD);
+    //qNotDisjoint.add(qDisjoint,BooleanClause.Occur.MUST_NOT);
+    return qNotDisjoint;
+  }
+
+  /**
+   * Makes a boolean query based upon a collection of queries and a logical operator.
+   *
+   * @param queries the query collection
+   * @param occur the logical operator
+   * @return the query
+   */
+  BooleanQuery makeQuery(Query[] queries, BooleanClause.Occur occur) {
+    BooleanQuery bq = new BooleanQuery();
+    for (Query query : queries) {
+      bq.add(query, occur);
+    }
+    return bq;
+  }
+
+  /**
+   * Constructs a query to retrieve documents are fully within the input envelope.
+   *
+   * @return the spatial query
+   */
+  Query makeWithin(Rectangle bbox, BBoxFieldInfo fieldInfo) {
+
+    // general case
+    // docMinX >= queryExtent.getMinX() AND docMinY >= queryExtent.getMinY() AND docMaxX <= queryExtent.getMaxX() AND docMaxY <= queryExtent.getMaxY()
+
+    // Y conditions
+    // docMinY >= queryExtent.getMinY() AND docMaxY <= queryExtent.getMaxY()
+    Query qMinY = NumericRangeQuery.newDoubleRange(fieldInfo.minY, finfo.precisionStep, bbox.getMinY(), null, true, false);
+    Query qMaxY = NumericRangeQuery.newDoubleRange(fieldInfo.maxY, finfo.precisionStep, null, bbox.getMaxY(), false, true);
+    Query yConditions = this.makeQuery(new Query[]{qMinY, qMaxY}, BooleanClause.Occur.MUST);
+
+    // X conditions
+    Query xConditions = null;
+
+    // X Conditions for documents that cross the date line,
+    // the left portion of the document must be within the left portion of the query,
+    // AND the right portion of the document must be within the right portion of the query
+    // docMinXLeft >= queryExtent.getMinX() AND docMaxXLeft <= 180.0
+    // AND docMinXRight >= -180.0 AND docMaxXRight <= queryExtent.getMaxX()
+    Query qXDLLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMinX(), null, true, false);
+    Query qXDLRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMaxX(), false, true);
+    Query qXDLLeftRight = this.makeQuery(new Query[]{qXDLLeft, qXDLRight}, BooleanClause.Occur.MUST);
+    Query qXDL = this.makeXDL(true, qXDLLeftRight, fieldInfo);
+
+    // queries that do not cross the date line
+    if (!bbox.getCrossesDateLine()) {
+
+      // X Conditions for documents that do not cross the date line,
+      // docMinX >= queryExtent.getMinX() AND docMaxX <= queryExtent.getMaxX()
+      Query qMinX = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMinX(), null, true, false);
+      Query qMaxX = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMaxX(), false, true);
+      Query qMinMax = this.makeQuery(new Query[]{qMinX, qMaxX}, BooleanClause.Occur.MUST);
+      Query qNonXDL = this.makeXDL(false, qMinMax, fieldInfo);
+
+      // apply the non-XDL or XDL X conditions
+      if ((bbox.getMinX() <= -180.0) && bbox.getMaxX() >= 180.0) {
+        xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD);
+      } else {
+        xConditions = qNonXDL;
+      }
+
+      // queries that cross the date line
+    } else {
+
+      // X Conditions for documents that do not cross the date line
+
+      // the document should be within the left portion of the query
+      // docMinX >= queryExtent.getMinX() AND docMaxX <= 180.0
+      Query qMinXLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMinX(), null, true, false);
+      Query qMaxXLeft = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, 180.0, false, true);
+      Query qLeft = this.makeQuery(new Query[]{qMinXLeft, qMaxXLeft}, BooleanClause.Occur.MUST);
+
+      // the document should be within the right portion of the query
+      // docMinX >= -180.0 AND docMaxX <= queryExtent.getMaxX()
+      Query qMinXRight = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, -180.0, null, true, false);
+      Query qMaxXRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMaxX(), false, true);
+      Query qRight = this.makeQuery(new Query[]{qMinXRight, qMaxXRight}, BooleanClause.Occur.MUST);
+
+      // either left or right conditions should occur,
+      // apply the left and right conditions to documents that do not cross the date line
+      Query qLeftRight = this.makeQuery(new Query[]{qLeft, qRight}, BooleanClause.Occur.SHOULD);
+      Query qNonXDL = this.makeXDL(false, qLeftRight, fieldInfo);
+
+      // apply the non-XDL and XDL conditions
+      xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD);
+    }
+
+    // both X and Y conditions must occur
+    return this.makeQuery(new Query[]{xConditions, yConditions}, BooleanClause.Occur.MUST);
+  }
+
+  /**
+   * Constructs a query to retrieve documents that do or do not cross the date line.
+   *
+   * @param crossedDateLine <code>true</true> for documents that cross the date line
+   * @return the query
+   */
+  Query makeXDL(boolean crossedDateLine, BBoxFieldInfo fieldInfo) {
+    // The 'T' and 'F' values match solr fields
+    return new TermQuery(new Term(fieldInfo.xdl, crossedDateLine ? "T" : "F"));
+  }
+
+  /**
+   * Constructs a query to retrieve documents that do or do not cross the date line
+   * and match the supplied spatial query.
+   *
+   * @param crossedDateLine <code>true</true> for documents that cross the date line
+   * @param query the spatial query
+   * @return the query
+   */
+  Query makeXDL(boolean crossedDateLine, Query query, BBoxFieldInfo fieldInfo) {
+    BooleanQuery bq = new BooleanQuery();
+    bq.add(this.makeXDL(crossedDateLine, fieldInfo), BooleanClause.Occur.MUST);
+    bq.add(query, BooleanClause.Occur.MUST);
+    return bq;
+  }
+}
+
+
+

Added: lucene/dev/trunk/lucene/spatial/src/test-files/data/simple-bbox.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test-files/data/simple-bbox.txt?rev=1354841&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/test-files/data/simple-bbox.txt (added)
+++ lucene/dev/trunk/lucene/spatial/src/test-files/data/simple-bbox.txt Thu Jun 28 07:36:12 2012
@@ -0,0 +1,5 @@
+#id	name	shape	
+C5	CenterAt5	-5 -5 5 5
+C10	CenterAt10	-10 -10 10 10
+NW15	NorthWest	15 15 20 20
+

Added: lucene/dev/trunk/lucene/spatial/src/test-files/simple-Queries-BBox.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test-files/simple-Queries-BBox.txt?rev=1354841&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/test-files/simple-Queries-BBox.txt (added)
+++ lucene/dev/trunk/lucene/spatial/src/test-files/simple-Queries-BBox.txt Thu Jun 28 07:36:12 2012
@@ -0,0 +1,13 @@
+C5 @ IsWithin(-6 -6 6 6)
+C5 @ BBoxWithin(-6 -6 6 6)
+C10 @ Contains(-6 -6 6 6)
+C10 @ IsEqualTo(-10 -10 10 10)
+C5 C10 @ Intersects(-2 -2 2 2)
+C5 C10 @ Overlaps(-2 -2 2 2)
+C5 C10 @ BBoxIntersects(-2 -2 2 2)
+NW15 @ IsDisjointTo(-10 -10 10 10)
+
+
+
+
+

Modified: lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java?rev=1354841&r1=1354840&r2=1354841&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java Thu Jun 28 07:36:12 2012
@@ -109,6 +109,22 @@ public abstract class SpatialTestCase ex
       this.numFound = numFound;
       this.results = results;
     }
+
+    public StringBuilder toDebugString() {
+      StringBuilder str = new StringBuilder();
+      str.append("found: ").append(numFound).append('[');
+      for(SearchResult r : results) {
+        String id = r.document.get("id");
+        str.append(id).append(", ");
+      }
+      str.append(']');
+      return str;
+    }
+
+    @Override
+    public String toString() {
+      return "[found:"+numFound+" "+results+"]";
+    }
   }
 
   protected static class SearchResult {
@@ -120,6 +136,11 @@ public abstract class SpatialTestCase ex
       this.score = score;
       this.document = document;
     }
+    
+    @Override
+    public String toString() {
+      return "["+score+"="+document+"]";
+    }
   }
 }
 

Modified: lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java?rev=1354841&r1=1354840&r2=1354841&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java Thu Jun 28 07:36:12 2012
@@ -36,6 +36,7 @@ import java.util.logging.Logger;
 
 public abstract class StrategyTestCase<T extends SpatialFieldInfo> extends SpatialTestCase {
 
+  public static final String DATA_SIMPLE_BBOX = "simple-bbox.txt";
   public static final String DATA_STATES_POLY = "states-poly.txt";
   public static final String DATA_STATES_BBOX = "states-bbox.txt";
   public static final String DATA_COUNTRIES_POLY = "countries-poly.txt";
@@ -44,8 +45,8 @@ public abstract class StrategyTestCase<T
 
   public static final String QTEST_States_IsWithin_BBox   = "states-IsWithin-BBox.txt";
   public static final String QTEST_States_Intersects_BBox = "states-Intersects-BBox.txt";
-
   public static final String QTEST_Cities_IsWithin_BBox = "cities-IsWithin-BBox.txt";
+  public static final String QTEST_Simple_Queries_BBox = "simple-Queries-BBox.txt";
 
   private Logger log = Logger.getLogger(getClass().getName());
 
@@ -112,8 +113,12 @@ public abstract class StrategyTestCase<T
         Iterator<String> ids = q.ids.iterator();
         for (SearchResult r : got.results) {
           String id = r.document.get("id");
+          if(!ids.hasNext()) {
+            Assert.fail(msg + " :: Did not get enough results.  Expect" + q.ids+", got: "+got.toDebugString());
+          }
           Assert.assertEquals( "out of order: " + msg, ids.next(), id);
         }
+        
         if (ids.hasNext()) {
           Assert.fail(msg + " :: expect more results then we got: " + ids.next());
         }

Added: lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java?rev=1354841&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java (added)
+++ lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java Thu Jun 28 07:36:12 2012
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package org.apache.lucene.spatial.bbox;
+
+import com.spatial4j.core.context.simple.SimpleSpatialContext;
+import org.apache.lucene.spatial.SpatialMatchConcern;
+import org.apache.lucene.spatial.StrategyTestCase;
+import org.apache.lucene.spatial.util.NumericFieldInfo;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class TestBBoxStrategy extends StrategyTestCase<BBoxFieldInfo> {
+
+  @Before
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    this.ctx = SimpleSpatialContext.GEO_KM;
+    
+    BBoxStrategy s = new BBoxStrategy(ctx);
+    s.finfo = new NumericFieldInfo();
+    
+    this.strategy = s;
+    this.fieldInfo = new BBoxFieldInfo("bbox");
+  }
+
+  @Test
+  public void testBasicOperaions() throws IOException {
+    getAddAndVerifyIndexedDocuments(DATA_SIMPLE_BBOX);
+    
+    executeQueries(SpatialMatchConcern.EXACT, QTEST_Simple_Queries_BBox);
+  }
+  
+  @Test
+  public void testStatesBBox() throws IOException {
+    getAddAndVerifyIndexedDocuments(DATA_STATES_BBOX);
+    
+    executeQueries(SpatialMatchConcern.FILTER, QTEST_States_IsWithin_BBox);
+    executeQueries(SpatialMatchConcern.FILTER, QTEST_States_Intersects_BBox);
+  }
+
+  @Test
+  public void testCitiesWithinBBox() throws IOException {
+    getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS);
+    
+    executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_IsWithin_BBox);
+  }
+  
+}



Re: svn commit: r1354841 - in /lucene/dev/trunk/lucene/spatial/src: java/org/apache/lucene/spatial/bbox/ test-files/ test-files/data/ test/org/apache/lucene/spatial/ test/org/apache/lucene/spatial/bbox/

Posted by Robert Muir <rc...@gmail.com>.
This break's hudson's "javadocs-lint" check, because it adds new java
packages without package.html.

On Thu, Jun 28, 2012 at 3:36 AM,  <ry...@apache.org> wrote:
> Author: ryan
> Date: Thu Jun 28 07:36:12 2012
> New Revision: 1354841
>
> URL: http://svn.apache.org/viewvc?rev=1354841&view=rev
> Log:
> LUCENE-4175: adding bbox strategy
>
> Added:
>    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/
>    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java
>    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java
>    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java
>    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
>    lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java
>    lucene/dev/trunk/lucene/spatial/src/test-files/data/simple-bbox.txt
>    lucene/dev/trunk/lucene/spatial/src/test-files/simple-Queries-BBox.txt
>    lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/
>    lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java
> Modified:
>    lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java
>    lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java
>
> Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java?rev=1354841&view=auto
> ==============================================================================
> --- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java (added)
> +++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java Thu Jun 28 07:36:12 2012
> @@ -0,0 +1,214 @@
> +/*
> + * 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.
> + */
> +package org.apache.lucene.spatial.bbox;
> +
> +import org.apache.lucene.search.Explanation;
> +
> +import com.spatial4j.core.shape.Rectangle;
> +
> +/**
> + * The algorithm is implemented as envelope on envelope overlays rather than
> + * complex polygon on complex polygon overlays.
> + * <p/>
> + * <p/>
> + * Spatial relevance scoring algorithm:
> + * <p/>
> + * <br/>  queryArea = the area of the input query envelope
> + * <br/>  targetArea = the area of the target envelope (per Lucene document)
> + * <br/>  intersectionArea = the area of the intersection for the query/target envelopes
> + * <br/>  queryPower = the weighting power associated with the query envelope (default = 1.0)
> + * <br/>  targetPower =  the weighting power associated with the target envelope (default = 1.0)
> + * <p/>
> + * <br/>  queryRatio  = intersectionArea / queryArea;
> + * <br/>  targetRatio = intersectionArea / targetArea;
> + * <br/>  queryFactor  = Math.pow(queryRatio,queryPower);
> + * <br/>  targetFactor = Math.pow(targetRatio,targetPower);
> + * <br/>  score = queryFactor * targetFactor;
> + * <p/>
> + * original:
> + * http://geoportal.svn.sourceforge.net/svnroot/geoportal/Geoportal/trunk/src/com/esri/gpt/catalog/lucene/SpatialRankingValueSource.java
> + */
> +public class AreaSimilarity implements BBoxSimilarity {
> +  /**
> +   * Properties associated with the query envelope
> +   */
> +  private final Rectangle queryExtent;
> +  private final double queryArea;
> +
> +  private final double targetPower;
> +  private final double queryPower;
> +
> +  public AreaSimilarity(Rectangle queryExtent, double queryPower, double targetPower) {
> +    this.queryExtent = queryExtent;
> +    this.queryArea = queryExtent.getArea();
> +
> +    this.queryPower = queryPower;
> +    this.targetPower = targetPower;
> +
> +//  if (this.qryMinX > queryExtent.getMaxX()) {
> +//    this.qryCrossedDateline = true;
> +//    this.qryArea = Math.abs(qryMaxX + 360.0 - qryMinX) * Math.abs(qryMaxY - qryMinY);
> +//  } else {
> +//    this.qryArea = Math.abs(qryMaxX - qryMinX) * Math.abs(qryMaxY - qryMinY);
> +//  }
> +  }
> +
> +  public AreaSimilarity(Rectangle queryExtent) {
> +    this(queryExtent, 2.0, 0.5);
> +  }
> +
> +
> +  public String getDelimiterQueryParameters() {
> +    return queryExtent.toString() + ";" + queryPower + ";" + targetPower;
> +  }
> +
> +  @Override
> +  public double score(Rectangle target, Explanation exp) {
> +    if (target == null || queryArea <= 0) {
> +      return 0;
> +    }
> +    double targetArea = target.getArea();
> +    if (targetArea <= 0) {
> +      return 0;
> +    }
> +    double score = 0;
> +
> +    double top = Math.min(queryExtent.getMaxY(), target.getMaxY());
> +    double bottom = Math.max(queryExtent.getMinY(), target.getMinY());
> +    double height = top - bottom;
> +    double width = 0;
> +
> +    // queries that cross the date line
> +    if (queryExtent.getCrossesDateLine()) {
> +      // documents that cross the date line
> +      if (target.getCrossesDateLine()) {
> +        double left = Math.max(queryExtent.getMinX(), target.getMinX());
> +        double right = Math.min(queryExtent.getMaxX(), target.getMaxX());
> +        width = right + 360.0 - left;
> +      } else {
> +        double qryWestLeft = Math.max(queryExtent.getMinX(), target.getMaxX());
> +        double qryWestRight = Math.min(target.getMaxX(), 180.0);
> +        double qryWestWidth = qryWestRight - qryWestLeft;
> +        if (qryWestWidth > 0) {
> +          width = qryWestWidth;
> +        } else {
> +          double qryEastLeft = Math.max(target.getMaxX(), -180.0);
> +          double qryEastRight = Math.min(queryExtent.getMaxX(), target.getMaxX());
> +          double qryEastWidth = qryEastRight - qryEastLeft;
> +          if (qryEastWidth > 0) {
> +            width = qryEastWidth;
> +          }
> +        }
> +      }
> +    } else { // queries that do not cross the date line
> +
> +      if (target.getCrossesDateLine()) {
> +        double tgtWestLeft = Math.max(queryExtent.getMinX(), target.getMinX());
> +        double tgtWestRight = Math.min(queryExtent.getMaxX(), 180.0);
> +        double tgtWestWidth = tgtWestRight - tgtWestLeft;
> +        if (tgtWestWidth > 0) {
> +          width = tgtWestWidth;
> +        } else {
> +          double tgtEastLeft = Math.max(queryExtent.getMinX(), -180.0);
> +          double tgtEastRight = Math.min(queryExtent.getMaxX(), target.getMaxX());
> +          double tgtEastWidth = tgtEastRight - tgtEastLeft;
> +          if (tgtEastWidth > 0) {
> +            width = tgtEastWidth;
> +          }
> +        }
> +      } else {
> +        double left = Math.max(queryExtent.getMinX(), target.getMinX());
> +        double right = Math.min(queryExtent.getMaxX(), target.getMaxX());
> +        width = right - left;
> +      }
> +    }
> +
> +
> +    // calculate the score
> +    if ((width > 0) && (height > 0)) {
> +      double intersectionArea = width * height;
> +      double queryRatio = intersectionArea / queryArea;
> +      double targetRatio = intersectionArea / targetArea;
> +      double queryFactor = Math.pow(queryRatio, queryPower);
> +      double targetFactor = Math.pow(targetRatio, targetPower);
> +      score = queryFactor * targetFactor * 10000.0;
> +
> +      if (exp!=null) {
> +//        StringBuilder sb = new StringBuilder();
> +//        sb.append("\nscore=").append(score);
> +//        sb.append("\n  query=").append();
> +//        sb.append("\n  target=").append(target.toString());
> +//        sb.append("\n  intersectionArea=").append(intersectionArea);
> +//
> +//        sb.append(" queryArea=").append(queryArea).append(" targetArea=").append(targetArea);
> +//        sb.append("\n  queryRatio=").append(queryRatio).append(" targetRatio=").append(targetRatio);
> +//        sb.append("\n  queryFactor=").append(queryFactor).append(" targetFactor=").append(targetFactor);
> +//        sb.append(" (queryPower=").append(queryPower).append(" targetPower=").append(targetPower).append(")");
> +
> +        exp.setValue((float)score);
> +        exp.setDescription(this.getClass().getSimpleName());
> +
> +        Explanation e = null;
> +
> +        exp.addDetail( e = new Explanation((float)intersectionArea, "IntersectionArea") );
> +        e.addDetail(new Explanation((float)width,  "width; Query: "+queryExtent.toString()));
> +        e.addDetail(new Explanation((float)height, "height; Target: "+target.toString()));
> +
> +        exp.addDetail( e = new Explanation((float)queryFactor, "Query") );
> +        e.addDetail(new Explanation((float)queryArea, "area"));
> +        e.addDetail(new Explanation((float)queryRatio, "ratio"));
> +        e.addDetail(new Explanation((float)queryPower, "power"));
> +
> +        exp.addDetail( e = new Explanation((float)targetFactor, "Target") );
> +        e.addDetail(new Explanation((float)targetArea, "area"));
> +        e.addDetail(new Explanation((float)targetRatio, "ratio"));
> +        e.addDetail(new Explanation((float)targetPower, "power"));
> +      }
> +    }
> +    else if(exp !=null) {
> +      exp.setValue(0);
> +      exp.setDescription("Shape does not intersect");
> +    }
> +    return score;
> +  }
> +
> +
> +  /**
> +   * Determines if this ValueSource is equal to another.
> +   *
> +   * @param o the ValueSource to compare
> +   * @return <code>true</code> if the two objects are based upon the same query envelope
> +   */
> +  @Override
> +  public boolean equals(Object o) {
> +    if (o.getClass() != AreaSimilarity.class)
> +      return false;
> +
> +    AreaSimilarity other = (AreaSimilarity) o;
> +    return getDelimiterQueryParameters().equals(other.getDelimiterQueryParameters());
> +  }
> +
> +  /**
> +   * Returns the ValueSource hash code.
> +   *
> +   * @return the hash code
> +   */
> +  @Override
> +  public int hashCode() {
> +    return getDelimiterQueryParameters().hashCode();
> +  }
> +}
>
> Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java?rev=1354841&view=auto
> ==============================================================================
> --- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java (added)
> +++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java Thu Jun 28 07:36:12 2012
> @@ -0,0 +1,57 @@
> +/*
> + * 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.
> + */
> +package org.apache.lucene.spatial.bbox;
> +
> +import org.apache.lucene.spatial.SpatialFieldInfo;
> +
> +
> +/**
> + * The Bounding Box gets stored as four fields for x/y min/max and a flag
> + * that says if the box crosses the dateline (xdl)
> + */
> +public class BBoxFieldInfo implements SpatialFieldInfo {
> +
> +  public static final String SUFFIX_MINX = "__minX";
> +  public static final String SUFFIX_MAXX = "__maxX";
> +  public static final String SUFFIX_MINY = "__minY";
> +  public static final String SUFFIX_MAXY = "__maxY";
> +  public static final String SUFFIX_XDL  = "__xdl";
> +
> +  public String bbox = "bbox";
> +  public String minX = "bbox.minx";
> +  public String minY = "bbox.miny";
> +  public String maxX = "bbox.maxx";
> +  public String maxY = "bbox.maxy";
> +  public String xdl  = "bbox.xdl"; // crosses dateline
> +
> +  public BBoxFieldInfo() {
> +
> +  }
> +
> +  public BBoxFieldInfo( String p ) {
> +    this.setFieldsPrefix( p );
> +  }
> +
> +  public void setFieldsPrefix(String prefix) {
> +    bbox = prefix;
> +    minX = prefix + SUFFIX_MINX;
> +    maxX = prefix + SUFFIX_MAXX;
> +    minY = prefix + SUFFIX_MINY;
> +    maxY = prefix + SUFFIX_MAXY;
> +    xdl  = prefix + SUFFIX_XDL;
> +  }
> +}
>
> Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java?rev=1354841&view=auto
> ==============================================================================
> --- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java (added)
> +++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java Thu Jun 28 07:36:12 2012
> @@ -0,0 +1,28 @@
> +/*
> + * 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.
> + */
> +package org.apache.lucene.spatial.bbox;
> +
> +import org.apache.lucene.search.Explanation;
> +
> +import com.spatial4j.core.shape.Rectangle;
> +
> +
> +
> +public interface BBoxSimilarity {
> +
> +  public double score(Rectangle extent, Explanation exp);
> +}
>
> Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java?rev=1354841&view=auto
> ==============================================================================
> --- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java (added)
> +++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java Thu Jun 28 07:36:12 2012
> @@ -0,0 +1,134 @@
> +/*
> + * 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.
> + */
> +package org.apache.lucene.spatial.bbox;
> +
> +import java.io.IOException;
> +import java.util.Map;
> +
> +import org.apache.lucene.index.AtomicReader;
> +import org.apache.lucene.index.AtomicReaderContext;
> +import org.apache.lucene.queries.function.FunctionValues;
> +import org.apache.lucene.queries.function.ValueSource;
> +import org.apache.lucene.search.Explanation;
> +import org.apache.lucene.search.FieldCache;
> +import org.apache.lucene.util.Bits;
> +
> +import com.spatial4j.core.shape.Rectangle;
> +import com.spatial4j.core.shape.simple.RectangleImpl;
> +
> +/**
> + * An implementation of the Lucene ValueSource model to support spatial relevance ranking.
> + */
> +public class BBoxSimilarityValueSource extends ValueSource {
> +
> +  private final BBoxFieldInfo field;
> +  private final BBoxSimilarity similarity;
> +
> +  /**
> +   * Constructor.
> +   *
> +   * @param queryEnvelope the query envelope
> +   * @param queryPower the query power (scoring algorithm)
> +   * @param targetPower the target power (scoring algorithm)
> +   */
> +  public BBoxSimilarityValueSource(BBoxSimilarity similarity, BBoxFieldInfo field) {
> +    this.similarity = similarity;
> +    this.field = field;
> +  }
> +
> +  /**
> +   * Returns the ValueSource description.
> +   *
> +   * @return the description
> +   */
> +  @Override
> +  public String description() {
> +    return "BBoxSimilarityValueSource(" + similarity + ")";
> +  }
> +
> +
> +  /**
> +   * Returns the DocValues used by the function query.
> +   *
> +   * @param reader the index reader
> +   * @return the values
> +   */
> +  @Override
> +  public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
> +    AtomicReader reader = readerContext.reader();
> +    final double[] minX = FieldCache.DEFAULT.getDoubles(reader, field.minX, true);
> +    final double[] minY = FieldCache.DEFAULT.getDoubles(reader, field.minY, true);
> +    final double[] maxX = FieldCache.DEFAULT.getDoubles(reader, field.maxX, true);
> +    final double[] maxY = FieldCache.DEFAULT.getDoubles(reader, field.maxY, true);
> +
> +    final Bits validMinX = FieldCache.DEFAULT.getDocsWithField(reader, field.minX);
> +    final Bits validMaxX = FieldCache.DEFAULT.getDocsWithField(reader, field.maxX);
> +
> +    return new FunctionValues() {
> +      @Override
> +      public float floatVal(int doc) {
> +        // make sure it has minX and area
> +        if (validMinX.get(doc) && validMaxX.get(doc)) {
> +          Rectangle rect = new RectangleImpl(
> +              minX[doc], maxX[doc],
> +              minY[doc], maxY[doc]);
> +          return (float) similarity.score(rect, null);
> +        }
> +        return 0;
> +      }
> +
> +      public Explanation explain(int doc) {
> +        // make sure it has minX and area
> +        if (validMinX.get(doc) && validMaxX.get(doc)) {
> +          Rectangle rect = new RectangleImpl(
> +              minX[doc], maxX[doc],
> +              minY[doc], maxY[doc]);
> +          Explanation exp = new Explanation();
> +          similarity.score(rect, exp);
> +          return exp;
> +        }
> +        return new Explanation(0, "No BBox");
> +      }
> +
> +      @Override
> +      public String toString(int doc) {
> +        return description() + "=" + floatVal(doc);
> +      }
> +    };
> +  }
> +
> +  /**
> +   * Determines if this ValueSource is equal to another.
> +   *
> +   * @param o the ValueSource to compare
> +   * @return <code>true</code> if the two objects are based upon the same query envelope
> +   */
> +  @Override
> +  public boolean equals(Object o) {
> +    if (o.getClass() != BBoxSimilarityValueSource.class) {
> +      return false;
> +    }
> +
> +    BBoxSimilarityValueSource other = (BBoxSimilarityValueSource) o;
> +    return similarity.equals(other.similarity);
> +  }
> +
> +  @Override
> +  public int hashCode() {
> +    return BBoxSimilarityValueSource.class.hashCode() + similarity.hashCode();
> +  }
> +}
>
> Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java?rev=1354841&view=auto
> ==============================================================================
> --- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java (added)
> +++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java Thu Jun 28 07:36:12 2012
> @@ -0,0 +1,472 @@
> +/*
> + * 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.
> + */
> +package org.apache.lucene.spatial.bbox;
> +
> +import java.text.NumberFormat;
> +import java.util.Locale;
> +
> +import org.apache.lucene.document.Field;
> +import org.apache.lucene.document.FieldType;
> +import org.apache.lucene.index.FieldInfo.IndexOptions;
> +import org.apache.lucene.index.IndexableField;
> +import org.apache.lucene.index.Term;
> +import org.apache.lucene.queries.function.FunctionQuery;
> +import org.apache.lucene.queries.function.ValueSource;
> +import org.apache.lucene.search.BooleanClause;
> +import org.apache.lucene.search.BooleanQuery;
> +import org.apache.lucene.search.ConstantScoreQuery;
> +import org.apache.lucene.search.Filter;
> +import org.apache.lucene.search.NumericRangeQuery;
> +import org.apache.lucene.search.Query;
> +import org.apache.lucene.search.QueryWrapperFilter;
> +import org.apache.lucene.search.TermQuery;
> +import org.apache.lucene.spatial.SpatialStrategy;
> +import org.apache.lucene.spatial.util.NumericFieldInfo;
> +
> +import com.spatial4j.core.context.*;
> +import com.spatial4j.core.exception.UnsupportedSpatialOperation;
> +import com.spatial4j.core.query.*;
> +import com.spatial4j.core.shape.*;
> +
> +
> +/**
> + * original:
> + * http://geoportal.svn.sourceforge.net/svnroot/geoportal/Geoportal/trunk/src/com/esri/gpt/catalog/lucene/SpatialClauseAdapter.java
> + */
> +public class BBoxStrategy extends SpatialStrategy<BBoxFieldInfo> {
> +  public double queryPower = 1.0;
> +  public double targetPower = 1.0f;
> +
> +  public NumericFieldInfo finfo = null;
> +
> +  public BBoxStrategy(SpatialContext ctx) {
> +    super(ctx);
> +  }
> +
> +  //---------------------------------
> +  // Indexing
> +  //---------------------------------
> +
> +  @Override
> +  public IndexableField[] createFields(BBoxFieldInfo fieldInfo,
> +      Shape shape, boolean index, boolean store) {
> +
> +    Rectangle bbox = shape.getBoundingBox();
> +    IndexableField[] fields = new IndexableField[store?6:5];
> +    fields[0] = finfo.createDouble(fieldInfo.minX, bbox.getMinX());
> +    fields[1] = finfo.createDouble(fieldInfo.maxX, bbox.getMaxX());
> +    fields[2] = finfo.createDouble(fieldInfo.minY, bbox.getMinY());
> +    fields[3] = finfo.createDouble(fieldInfo.maxY, bbox.getMaxY());
> +
> +    FieldType ft = new FieldType();
> +    ft.setIndexed(index);
> +    ft.setStored(store);
> +    ft.setTokenized(false);
> +    ft.setOmitNorms(true);
> +    ft.setIndexOptions(IndexOptions.DOCS_ONLY);
> +    ft.freeze();
> +
> +    Field xdl = new Field( fieldInfo.xdl, bbox.getCrossesDateLine()?"T":"F", ft );
> +    fields[4] = xdl;
> +    if( store ) {
> +      FieldType ff = new FieldType();
> +      ff.setIndexed(false);
> +      ff.setStored(true);
> +      ff.setOmitNorms(true);
> +      ff.setIndexOptions(IndexOptions.DOCS_ONLY);
> +      ff.freeze();
> +
> +      NumberFormat nf = NumberFormat.getInstance( Locale.US );
> +      nf.setMaximumFractionDigits( 5 );
> +      nf.setMinimumFractionDigits( 5 );
> +      nf.setGroupingUsed(false);
> +      String ext =
> +        nf.format( bbox.getMinX() ) + ' ' +
> +        nf.format( bbox.getMinY() ) + ' ' +
> +        nf.format( bbox.getMaxX() ) + ' ' +
> +        nf.format( bbox.getMaxY() ) + ' ';
> +      fields[5] = new Field( fieldInfo.bbox, ext, ff );
> +    }
> +    return fields;
> +  }
> +
> +  @Override
> +  public IndexableField createField(BBoxFieldInfo fieldInfo, Shape shape,
> +      boolean index, boolean store) {
> +    throw new UnsupportedOperationException("BBOX is poly field");
> +  }
> +
> +  @Override
> +  public boolean isPolyField() {
> +    return true;
> +  }
> +
> +  //---------------------------------
> +  // Query Builder
> +  //---------------------------------
> +
> +  @Override
> +  public ValueSource makeValueSource(SpatialArgs args, BBoxFieldInfo fields) {
> +    return new BBoxSimilarityValueSource(
> +        new AreaSimilarity(args.getShape().getBoundingBox(), queryPower, targetPower), fields );
> +  }
> +
> +
> +  @Override
> +  public Filter makeFilter(SpatialArgs args, BBoxFieldInfo fieldInfo) {
> +    Query spatial = makeSpatialQuery(args, fieldInfo);
> +    return new QueryWrapperFilter( spatial );
> +  }
> +
> +  @Override
> +  public Query makeQuery(SpatialArgs args, BBoxFieldInfo fieldInfo) {
> +    BooleanQuery bq = new BooleanQuery();
> +    Query spatial = makeSpatialQuery(args, fieldInfo);
> +    bq.add(new ConstantScoreQuery(spatial), BooleanClause.Occur.MUST);
> +
> +    // This part does the scoring
> +    Query spatialRankingQuery = new FunctionQuery(makeValueSource(args, fieldInfo));
> +    bq.add(spatialRankingQuery, BooleanClause.Occur.MUST);
> +    return bq;
> +  }
> +
> +
> +  private Query makeSpatialQuery(SpatialArgs args, BBoxFieldInfo fieldInfo) {
> +    Rectangle bbox = args.getShape().getBoundingBox();
> +    Query spatial = null;
> +
> +    // Useful for understanding Relations:
> +    // http://edndoc.esri.com/arcsde/9.1/general_topics/understand_spatial_relations.htm
> +    SpatialOperation op = args.getOperation();
> +         if( op == SpatialOperation.BBoxIntersects ) spatial = makeIntersects(bbox, fieldInfo);
> +    else if( op == SpatialOperation.BBoxWithin     ) spatial = makeWithin(bbox, fieldInfo);
> +    else if( op == SpatialOperation.Contains       ) spatial = makeContains(bbox, fieldInfo);
> +    else if( op == SpatialOperation.Intersects     ) spatial = makeIntersects(bbox, fieldInfo);
> +    else if( op == SpatialOperation.IsEqualTo      ) spatial = makeEquals(bbox, fieldInfo);
> +    else if( op == SpatialOperation.IsDisjointTo   ) spatial = makeDisjoint(bbox, fieldInfo);
> +    else if( op == SpatialOperation.IsWithin       ) spatial = makeWithin(bbox, fieldInfo);
> +    else if( op == SpatialOperation.Overlaps       ) spatial = makeIntersects(bbox, fieldInfo);
> +    else {
> +        throw new UnsupportedSpatialOperation(op);
> +    }
> +    return spatial;
> +  }
> +
> +
> +  //-------------------------------------------------------------------------------
> +  //
> +  //-------------------------------------------------------------------------------
> +
> +  /**
> +   * Constructs a query to retrieve documents that fully contain the input envelope.
> +   *
> +   * @return the spatial query
> +   */
> +  Query makeContains(Rectangle bbox, BBoxFieldInfo fieldInfo) {
> +
> +    // general case
> +    // docMinX <= queryExtent.getMinX() AND docMinY <= queryExtent.getMinY() AND docMaxX >= queryExtent.getMaxX() AND docMaxY >= queryExtent.getMaxY()
> +
> +    // Y conditions
> +    // docMinY <= queryExtent.getMinY() AND docMaxY >= queryExtent.getMaxY()
> +    Query qMinY = NumericRangeQuery.newDoubleRange(fieldInfo.minY, finfo.precisionStep, null, bbox.getMinY(), false, true);
> +    Query qMaxY = NumericRangeQuery.newDoubleRange(fieldInfo.maxY, finfo.precisionStep, bbox.getMaxY(), null, true, false);
> +    Query yConditions = this.makeQuery(new Query[]{qMinY, qMaxY}, BooleanClause.Occur.MUST);
> +
> +    // X conditions
> +    Query xConditions = null;
> +
> +    // queries that do not cross the date line
> +    if (!bbox.getCrossesDateLine()) {
> +
> +      // X Conditions for documents that do not cross the date line,
> +      // documents that contain the min X and max X of the query envelope,
> +      // docMinX <= queryExtent.getMinX() AND docMaxX >= queryExtent.getMaxX()
> +      Query qMinX = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, null, bbox.getMinX(), false, true);
> +      Query qMaxX = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, bbox.getMaxX(), null, true, false);
> +      Query qMinMax = this.makeQuery(new Query[]{qMinX, qMaxX}, BooleanClause.Occur.MUST);
> +      Query qNonXDL = this.makeXDL(false, qMinMax, fieldInfo);
> +
> +      // X Conditions for documents that cross the date line,
> +      // the left portion of the document contains the min X of the query
> +      // OR the right portion of the document contains the max X of the query,
> +      // docMinXLeft <= queryExtent.getMinX() OR docMaxXRight >= queryExtent.getMaxX()
> +      Query qXDLLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, null, bbox.getMinX(), false, true);
> +      Query qXDLRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, bbox.getMaxX(), null, true, false);
> +      Query qXDLLeftRight = this.makeQuery(new Query[]{qXDLLeft, qXDLRight}, BooleanClause.Occur.SHOULD);
> +      Query qXDL = this.makeXDL(true, qXDLLeftRight, fieldInfo);
> +
> +      // apply the non-XDL and XDL conditions
> +      xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD);
> +
> +      // queries that cross the date line
> +    } else {
> +
> +      // No need to search for documents that do not cross the date line
> +
> +      // X Conditions for documents that cross the date line,
> +      // the left portion of the document contains the min X of the query
> +      // AND the right portion of the document contains the max X of the query,
> +      // docMinXLeft <= queryExtent.getMinX() AND docMaxXRight >= queryExtent.getMaxX()
> +      Query qXDLLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, null, bbox.getMinX(), false, true);
> +      Query qXDLRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, bbox.getMaxX(), null, true, false);
> +      Query qXDLLeftRight = this.makeQuery(new Query[]{qXDLLeft, qXDLRight}, BooleanClause.Occur.MUST);
> +
> +      xConditions = this.makeXDL(true, qXDLLeftRight, fieldInfo);
> +    }
> +
> +    // both X and Y conditions must occur
> +    return this.makeQuery(new Query[]{xConditions, yConditions}, BooleanClause.Occur.MUST);
> +  }
> +
> +  /**
> +   * Constructs a query to retrieve documents that are disjoint to the input envelope.
> +   *
> +   * @return the spatial query
> +   */
> +  Query makeDisjoint(Rectangle bbox, BBoxFieldInfo fieldInfo) {
> +
> +    // general case
> +    // docMinX > queryExtent.getMaxX() OR docMaxX < queryExtent.getMinX() OR docMinY > queryExtent.getMaxY() OR docMaxY < queryExtent.getMinY()
> +
> +    // Y conditions
> +    // docMinY > queryExtent.getMaxY() OR docMaxY < queryExtent.getMinY()
> +    Query qMinY = NumericRangeQuery.newDoubleRange(fieldInfo.minY, finfo.precisionStep, bbox.getMaxY(), null, false, false);
> +    Query qMaxY = NumericRangeQuery.newDoubleRange(fieldInfo.maxY, finfo.precisionStep, null, bbox.getMinY(), false, false);
> +    Query yConditions = this.makeQuery(new Query[]{qMinY, qMaxY}, BooleanClause.Occur.SHOULD);
> +
> +    // X conditions
> +    Query xConditions = null;
> +
> +    // queries that do not cross the date line
> +    if (!bbox.getCrossesDateLine()) {
> +
> +      // X Conditions for documents that do not cross the date line,
> +      // docMinX > queryExtent.getMaxX() OR docMaxX < queryExtent.getMinX()
> +      Query qMinX = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMaxX(), null, false, false);
> +      Query qMaxX = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMinX(), false, false);
> +      Query qMinMax = this.makeQuery(new Query[]{qMinX, qMaxX}, BooleanClause.Occur.SHOULD);
> +      Query qNonXDL = this.makeXDL(false, qMinMax, fieldInfo);
> +
> +      // X Conditions for documents that cross the date line,
> +      // both the left and right portions of the document must be disjoint to the query
> +      // (docMinXLeft > queryExtent.getMaxX() OR docMaxXLeft < queryExtent.getMinX()) AND
> +      // (docMinXRight > queryExtent.getMaxX() OR docMaxXRight < queryExtent.getMinX())
> +      // where: docMaxXLeft = 180.0, docMinXRight = -180.0
> +      // (docMaxXLeft  < queryExtent.getMinX()) equates to (180.0  < queryExtent.getMinX()) and is ignored
> +      // (docMinXRight > queryExtent.getMaxX()) equates to (-180.0 > queryExtent.getMaxX()) and is ignored
> +      Query qMinXLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMaxX(), null, false, false);
> +      Query qMaxXRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMinX(), false, false);
> +      Query qLeftRight = this.makeQuery(new Query[]{qMinXLeft, qMaxXRight}, BooleanClause.Occur.MUST);
> +      Query qXDL = this.makeXDL(true, qLeftRight, fieldInfo);
> +
> +      // apply the non-XDL and XDL conditions
> +      xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD);
> +
> +      // queries that cross the date line
> +    } else {
> +
> +      // X Conditions for documents that do not cross the date line,
> +      // the document must be disjoint to both the left and right query portions
> +      // (docMinX > queryExtent.getMaxX()Left OR docMaxX < queryExtent.getMinX()) AND (docMinX > queryExtent.getMaxX() OR docMaxX < queryExtent.getMinX()Left)
> +      // where: queryExtent.getMaxX()Left = 180.0, queryExtent.getMinX()Left = -180.0
> +      Query qMinXLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, 180.0, null, false, false);
> +      Query qMaxXLeft = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMinX(), false, false);
> +      Query qMinXRight = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMaxX(), null, false, false);
> +      Query qMaxXRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, -180.0, false, false);
> +      Query qLeft = this.makeQuery(new Query[]{qMinXLeft, qMaxXLeft}, BooleanClause.Occur.SHOULD);
> +      Query qRight = this.makeQuery(new Query[]{qMinXRight, qMaxXRight}, BooleanClause.Occur.SHOULD);
> +      Query qLeftRight = this.makeQuery(new Query[]{qLeft, qRight}, BooleanClause.Occur.MUST);
> +
> +      // No need to search for documents that do not cross the date line
> +
> +      xConditions = this.makeXDL(false, qLeftRight, fieldInfo);
> +    }
> +
> +    // either X or Y conditions should occur
> +    return this.makeQuery(new Query[]{xConditions, yConditions}, BooleanClause.Occur.SHOULD);
> +  }
> +
> +  /**
> +   * Constructs a query to retrieve documents that equal the input envelope.
> +   *
> +   * @return the spatial query
> +   */
> +  Query makeEquals(Rectangle bbox, BBoxFieldInfo fieldInfo) {
> +
> +    // docMinX = queryExtent.getMinX() AND docMinY = queryExtent.getMinY() AND docMaxX = queryExtent.getMaxX() AND docMaxY = queryExtent.getMaxY()
> +    Query qMinX = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMinX(), bbox.getMinX(), true, true);
> +    Query qMinY = NumericRangeQuery.newDoubleRange(fieldInfo.minY, finfo.precisionStep, bbox.getMinY(), bbox.getMinY(), true, true);
> +    Query qMaxX = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, bbox.getMaxX(), bbox.getMaxX(), true, true);
> +    Query qMaxY = NumericRangeQuery.newDoubleRange(fieldInfo.maxY, finfo.precisionStep, bbox.getMaxY(), bbox.getMaxY(), true, true);
> +    BooleanQuery bq = new BooleanQuery();
> +    bq.add(qMinX, BooleanClause.Occur.MUST);
> +    bq.add(qMinY, BooleanClause.Occur.MUST);
> +    bq.add(qMaxX, BooleanClause.Occur.MUST);
> +    bq.add(qMaxY, BooleanClause.Occur.MUST);
> +    return bq;
> +  }
> +
> +  /**
> +   * Constructs a query to retrieve documents that intersect the input envelope.
> +   *
> +   * @return the spatial query
> +   */
> +  Query makeIntersects(Rectangle bbox, BBoxFieldInfo fieldInfo) {
> +
> +    // the original intersects query does not work for envelopes that cross the date line,
> +    // switch to a NOT Disjoint query
> +
> +    // MUST_NOT causes a problem when it's the only clause type within a BooleanQuery,
> +    // to get round it we add all documents as a SHOULD
> +
> +    // there must be an envelope, it must not be disjoint
> +    Query qDisjoint = makeDisjoint(bbox, fieldInfo);
> +    Query qIsNonXDL = this.makeXDL(false, fieldInfo);
> +    Query qIsXDL = this.makeXDL(true, fieldInfo);
> +    Query qHasEnv = this.makeQuery(new Query[]{qIsNonXDL, qIsXDL}, BooleanClause.Occur.SHOULD);
> +    BooleanQuery qNotDisjoint = new BooleanQuery();
> +    qNotDisjoint.add(qHasEnv, BooleanClause.Occur.MUST);
> +    qNotDisjoint.add(qDisjoint, BooleanClause.Occur.MUST_NOT);
> +
> +    //Query qDisjoint = makeDisjoint();
> +    //BooleanQuery qNotDisjoint = new BooleanQuery();
> +    //qNotDisjoint.add(new MatchAllDocsQuery(),BooleanClause.Occur.SHOULD);
> +    //qNotDisjoint.add(qDisjoint,BooleanClause.Occur.MUST_NOT);
> +    return qNotDisjoint;
> +  }
> +
> +  /**
> +   * Makes a boolean query based upon a collection of queries and a logical operator.
> +   *
> +   * @param queries the query collection
> +   * @param occur the logical operator
> +   * @return the query
> +   */
> +  BooleanQuery makeQuery(Query[] queries, BooleanClause.Occur occur) {
> +    BooleanQuery bq = new BooleanQuery();
> +    for (Query query : queries) {
> +      bq.add(query, occur);
> +    }
> +    return bq;
> +  }
> +
> +  /**
> +   * Constructs a query to retrieve documents are fully within the input envelope.
> +   *
> +   * @return the spatial query
> +   */
> +  Query makeWithin(Rectangle bbox, BBoxFieldInfo fieldInfo) {
> +
> +    // general case
> +    // docMinX >= queryExtent.getMinX() AND docMinY >= queryExtent.getMinY() AND docMaxX <= queryExtent.getMaxX() AND docMaxY <= queryExtent.getMaxY()
> +
> +    // Y conditions
> +    // docMinY >= queryExtent.getMinY() AND docMaxY <= queryExtent.getMaxY()
> +    Query qMinY = NumericRangeQuery.newDoubleRange(fieldInfo.minY, finfo.precisionStep, bbox.getMinY(), null, true, false);
> +    Query qMaxY = NumericRangeQuery.newDoubleRange(fieldInfo.maxY, finfo.precisionStep, null, bbox.getMaxY(), false, true);
> +    Query yConditions = this.makeQuery(new Query[]{qMinY, qMaxY}, BooleanClause.Occur.MUST);
> +
> +    // X conditions
> +    Query xConditions = null;
> +
> +    // X Conditions for documents that cross the date line,
> +    // the left portion of the document must be within the left portion of the query,
> +    // AND the right portion of the document must be within the right portion of the query
> +    // docMinXLeft >= queryExtent.getMinX() AND docMaxXLeft <= 180.0
> +    // AND docMinXRight >= -180.0 AND docMaxXRight <= queryExtent.getMaxX()
> +    Query qXDLLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMinX(), null, true, false);
> +    Query qXDLRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMaxX(), false, true);
> +    Query qXDLLeftRight = this.makeQuery(new Query[]{qXDLLeft, qXDLRight}, BooleanClause.Occur.MUST);
> +    Query qXDL = this.makeXDL(true, qXDLLeftRight, fieldInfo);
> +
> +    // queries that do not cross the date line
> +    if (!bbox.getCrossesDateLine()) {
> +
> +      // X Conditions for documents that do not cross the date line,
> +      // docMinX >= queryExtent.getMinX() AND docMaxX <= queryExtent.getMaxX()
> +      Query qMinX = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMinX(), null, true, false);
> +      Query qMaxX = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMaxX(), false, true);
> +      Query qMinMax = this.makeQuery(new Query[]{qMinX, qMaxX}, BooleanClause.Occur.MUST);
> +      Query qNonXDL = this.makeXDL(false, qMinMax, fieldInfo);
> +
> +      // apply the non-XDL or XDL X conditions
> +      if ((bbox.getMinX() <= -180.0) && bbox.getMaxX() >= 180.0) {
> +        xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD);
> +      } else {
> +        xConditions = qNonXDL;
> +      }
> +
> +      // queries that cross the date line
> +    } else {
> +
> +      // X Conditions for documents that do not cross the date line
> +
> +      // the document should be within the left portion of the query
> +      // docMinX >= queryExtent.getMinX() AND docMaxX <= 180.0
> +      Query qMinXLeft = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, bbox.getMinX(), null, true, false);
> +      Query qMaxXLeft = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, 180.0, false, true);
> +      Query qLeft = this.makeQuery(new Query[]{qMinXLeft, qMaxXLeft}, BooleanClause.Occur.MUST);
> +
> +      // the document should be within the right portion of the query
> +      // docMinX >= -180.0 AND docMaxX <= queryExtent.getMaxX()
> +      Query qMinXRight = NumericRangeQuery.newDoubleRange(fieldInfo.minX, finfo.precisionStep, -180.0, null, true, false);
> +      Query qMaxXRight = NumericRangeQuery.newDoubleRange(fieldInfo.maxX, finfo.precisionStep, null, bbox.getMaxX(), false, true);
> +      Query qRight = this.makeQuery(new Query[]{qMinXRight, qMaxXRight}, BooleanClause.Occur.MUST);
> +
> +      // either left or right conditions should occur,
> +      // apply the left and right conditions to documents that do not cross the date line
> +      Query qLeftRight = this.makeQuery(new Query[]{qLeft, qRight}, BooleanClause.Occur.SHOULD);
> +      Query qNonXDL = this.makeXDL(false, qLeftRight, fieldInfo);
> +
> +      // apply the non-XDL and XDL conditions
> +      xConditions = this.makeQuery(new Query[]{qNonXDL, qXDL}, BooleanClause.Occur.SHOULD);
> +    }
> +
> +    // both X and Y conditions must occur
> +    return this.makeQuery(new Query[]{xConditions, yConditions}, BooleanClause.Occur.MUST);
> +  }
> +
> +  /**
> +   * Constructs a query to retrieve documents that do or do not cross the date line.
> +   *
> +   * @param crossedDateLine <code>true</true> for documents that cross the date line
> +   * @return the query
> +   */
> +  Query makeXDL(boolean crossedDateLine, BBoxFieldInfo fieldInfo) {
> +    // The 'T' and 'F' values match solr fields
> +    return new TermQuery(new Term(fieldInfo.xdl, crossedDateLine ? "T" : "F"));
> +  }
> +
> +  /**
> +   * Constructs a query to retrieve documents that do or do not cross the date line
> +   * and match the supplied spatial query.
> +   *
> +   * @param crossedDateLine <code>true</true> for documents that cross the date line
> +   * @param query the spatial query
> +   * @return the query
> +   */
> +  Query makeXDL(boolean crossedDateLine, Query query, BBoxFieldInfo fieldInfo) {
> +    BooleanQuery bq = new BooleanQuery();
> +    bq.add(this.makeXDL(crossedDateLine, fieldInfo), BooleanClause.Occur.MUST);
> +    bq.add(query, BooleanClause.Occur.MUST);
> +    return bq;
> +  }
> +}
> +
> +
> +
>
> Added: lucene/dev/trunk/lucene/spatial/src/test-files/data/simple-bbox.txt
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test-files/data/simple-bbox.txt?rev=1354841&view=auto
> ==============================================================================
> --- lucene/dev/trunk/lucene/spatial/src/test-files/data/simple-bbox.txt (added)
> +++ lucene/dev/trunk/lucene/spatial/src/test-files/data/simple-bbox.txt Thu Jun 28 07:36:12 2012
> @@ -0,0 +1,5 @@
> +#id    name    shape
> +C5     CenterAt5       -5 -5 5 5
> +C10    CenterAt10      -10 -10 10 10
> +NW15   NorthWest       15 15 20 20
> +
>
> Added: lucene/dev/trunk/lucene/spatial/src/test-files/simple-Queries-BBox.txt
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test-files/simple-Queries-BBox.txt?rev=1354841&view=auto
> ==============================================================================
> --- lucene/dev/trunk/lucene/spatial/src/test-files/simple-Queries-BBox.txt (added)
> +++ lucene/dev/trunk/lucene/spatial/src/test-files/simple-Queries-BBox.txt Thu Jun 28 07:36:12 2012
> @@ -0,0 +1,13 @@
> +C5 @ IsWithin(-6 -6 6 6)
> +C5 @ BBoxWithin(-6 -6 6 6)
> +C10 @ Contains(-6 -6 6 6)
> +C10 @ IsEqualTo(-10 -10 10 10)
> +C5 C10 @ Intersects(-2 -2 2 2)
> +C5 C10 @ Overlaps(-2 -2 2 2)
> +C5 C10 @ BBoxIntersects(-2 -2 2 2)
> +NW15 @ IsDisjointTo(-10 -10 10 10)
> +
> +
> +
> +
> +
>
> Modified: lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java?rev=1354841&r1=1354840&r2=1354841&view=diff
> ==============================================================================
> --- lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java (original)
> +++ lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java Thu Jun 28 07:36:12 2012
> @@ -109,6 +109,22 @@ public abstract class SpatialTestCase ex
>       this.numFound = numFound;
>       this.results = results;
>     }
> +
> +    public StringBuilder toDebugString() {
> +      StringBuilder str = new StringBuilder();
> +      str.append("found: ").append(numFound).append('[');
> +      for(SearchResult r : results) {
> +        String id = r.document.get("id");
> +        str.append(id).append(", ");
> +      }
> +      str.append(']');
> +      return str;
> +    }
> +
> +    @Override
> +    public String toString() {
> +      return "[found:"+numFound+" "+results+"]";
> +    }
>   }
>
>   protected static class SearchResult {
> @@ -120,6 +136,11 @@ public abstract class SpatialTestCase ex
>       this.score = score;
>       this.document = document;
>     }
> +
> +    @Override
> +    public String toString() {
> +      return "["+score+"="+document+"]";
> +    }
>   }
>  }
>
>
> Modified: lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java?rev=1354841&r1=1354840&r2=1354841&view=diff
> ==============================================================================
> --- lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java (original)
> +++ lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java Thu Jun 28 07:36:12 2012
> @@ -36,6 +36,7 @@ import java.util.logging.Logger;
>
>  public abstract class StrategyTestCase<T extends SpatialFieldInfo> extends SpatialTestCase {
>
> +  public static final String DATA_SIMPLE_BBOX = "simple-bbox.txt";
>   public static final String DATA_STATES_POLY = "states-poly.txt";
>   public static final String DATA_STATES_BBOX = "states-bbox.txt";
>   public static final String DATA_COUNTRIES_POLY = "countries-poly.txt";
> @@ -44,8 +45,8 @@ public abstract class StrategyTestCase<T
>
>   public static final String QTEST_States_IsWithin_BBox   = "states-IsWithin-BBox.txt";
>   public static final String QTEST_States_Intersects_BBox = "states-Intersects-BBox.txt";
> -
>   public static final String QTEST_Cities_IsWithin_BBox = "cities-IsWithin-BBox.txt";
> +  public static final String QTEST_Simple_Queries_BBox = "simple-Queries-BBox.txt";
>
>   private Logger log = Logger.getLogger(getClass().getName());
>
> @@ -112,8 +113,12 @@ public abstract class StrategyTestCase<T
>         Iterator<String> ids = q.ids.iterator();
>         for (SearchResult r : got.results) {
>           String id = r.document.get("id");
> +          if(!ids.hasNext()) {
> +            Assert.fail(msg + " :: Did not get enough results.  Expect" + q.ids+", got: "+got.toDebugString());
> +          }
>           Assert.assertEquals( "out of order: " + msg, ids.next(), id);
>         }
> +
>         if (ids.hasNext()) {
>           Assert.fail(msg + " :: expect more results then we got: " + ids.next());
>         }
>
> Added: lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java
> URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java?rev=1354841&view=auto
> ==============================================================================
> --- lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java (added)
> +++ lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java Thu Jun 28 07:36:12 2012
> @@ -0,0 +1,66 @@
> +/*
> + * 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.
> + */
> +
> +package org.apache.lucene.spatial.bbox;
> +
> +import com.spatial4j.core.context.simple.SimpleSpatialContext;
> +import org.apache.lucene.spatial.SpatialMatchConcern;
> +import org.apache.lucene.spatial.StrategyTestCase;
> +import org.apache.lucene.spatial.util.NumericFieldInfo;
> +import org.junit.Before;
> +import org.junit.Test;
> +
> +import java.io.IOException;
> +
> +public class TestBBoxStrategy extends StrategyTestCase<BBoxFieldInfo> {
> +
> +  @Before
> +  @Override
> +  public void setUp() throws Exception {
> +    super.setUp();
> +    this.ctx = SimpleSpatialContext.GEO_KM;
> +
> +    BBoxStrategy s = new BBoxStrategy(ctx);
> +    s.finfo = new NumericFieldInfo();
> +
> +    this.strategy = s;
> +    this.fieldInfo = new BBoxFieldInfo("bbox");
> +  }
> +
> +  @Test
> +  public void testBasicOperaions() throws IOException {
> +    getAddAndVerifyIndexedDocuments(DATA_SIMPLE_BBOX);
> +
> +    executeQueries(SpatialMatchConcern.EXACT, QTEST_Simple_Queries_BBox);
> +  }
> +
> +  @Test
> +  public void testStatesBBox() throws IOException {
> +    getAddAndVerifyIndexedDocuments(DATA_STATES_BBOX);
> +
> +    executeQueries(SpatialMatchConcern.FILTER, QTEST_States_IsWithin_BBox);
> +    executeQueries(SpatialMatchConcern.FILTER, QTEST_States_Intersects_BBox);
> +  }
> +
> +  @Test
> +  public void testCitiesWithinBBox() throws IOException {
> +    getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS);
> +
> +    executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_IsWithin_BBox);
> +  }
> +
> +}
>
>



-- 
lucidimagination.com

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@lucene.apache.org
For additional commands, e-mail: dev-help@lucene.apache.org