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