Index: lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java
===================================================================
--- lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java	(revision 0)
+++ lucene/spatial/src/java/org/apache/lucene/spatial/bbox/AreaSimilarity.java	(working copy)
@@ -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();
+  }
+}
Index: lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java
===================================================================
--- lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java	(revision 0)
+++ lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxFieldInfo.java	(working copy)
@@ -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;
+  }
+}
Index: lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java
===================================================================
--- lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java	(revision 0)
+++ lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarity.java	(working copy)
@@ -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);
+}
Index: lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java
===================================================================
--- lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java	(revision 0)
+++ lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxSimilarityValueSource.java	(working copy)
@@ -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();
+  }
+}
Index: lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java
===================================================================
--- lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java	(revision 0)
+++ lucene/spatial/src/java/org/apache/lucene/spatial/bbox/BBoxStrategy.java	(working copy)
@@ -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;
+  }
+}
+
+
+
Index: lucene/spatial/src/test-files/data/simple-bbox.txt
===================================================================
--- lucene/spatial/src/test-files/data/simple-bbox.txt	(revision 0)
+++ lucene/spatial/src/test-files/data/simple-bbox.txt	(working copy)
@@ -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
+
Index: lucene/spatial/src/test-files/simple-Queries-BBox.txt
===================================================================
--- lucene/spatial/src/test-files/simple-Queries-BBox.txt	(revision 0)
+++ lucene/spatial/src/test-files/simple-Queries-BBox.txt	(working copy)
@@ -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)
+
+
+
+
+
Index: lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java
===================================================================
--- lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java	(revision 0)
+++ lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java	(working copy)
@@ -0,0 +1,58 @@
+/*
+ * 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 testCitiesWithinBBox() throws IOException {
+    getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS);
+    
+    executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_IsWithin_BBox);
+  }
+  
+
+  @Test
+  public void testBasicOperaions() throws IOException {
+    getAddAndVerifyIndexedDocuments(DATA_SIMPLE_BBOX);
+    
+    executeQueries(SpatialMatchConcern.EXACT, QTEST_Simple_Queries_BBox);
+  }
+}
Index: lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java
===================================================================
--- lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java	(revision 1354835)
+++ lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java	(working copy)
@@ -109,6 +109,22 @@
       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 @@
       this.score = score;
       this.document = document;
     }
+    
+    @Override
+    public String toString() {
+      return "["+score+"="+document+"]";
+    }
   }
 }
 
Index: lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java
===================================================================
--- lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java	(revision 1354835)
+++ lucene/spatial/src/test/org/apache/lucene/spatial/StrategyTestCase.java	(working copy)
@@ -36,6 +36,7 @@
 
 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 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 @@
         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());
         }
