Index: lucene/spatial/src/test/org/apache/lucene/spatial/prefix/TestRecursivePrefixTreeStrategy.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/test/org/apache/lucene/spatial/prefix/TestRecursivePrefixTreeStrategy.java (revision 1351928) +++ lucene/spatial/src/test/org/apache/lucene/spatial/prefix/TestRecursivePrefixTreeStrategy.java (revision ) @@ -17,20 +17,170 @@ package org.apache.lucene.spatial.prefix; -import com.spatial4j.core.context.SpatialContext; import com.spatial4j.core.context.simple.SimpleSpatialContext; -import org.junit.Before; +import com.spatial4j.core.distance.DistanceUtils; +import com.spatial4j.core.query.SpatialArgs; +import com.spatial4j.core.query.SpatialOperation; +import com.spatial4j.core.shape.Point; +import com.spatial4j.core.shape.Rectangle; +import com.spatial4j.core.shape.Shape; +import com.spatial4j.core.shape.simple.PointImpl; +import com.spatial4j.core.util.GeohashUtils; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.StringField; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.spatial.SimpleSpatialFieldInfo; +import org.apache.lucene.spatial.SpatialMatchConcern; +import org.apache.lucene.spatial.StrategyTestCase; +import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree; +import org.junit.Test; +import java.io.IOException; +import java.util.*; -public class TestRecursivePrefixTreeStrategy extends BaseRecursivePrefixTreeStrategyTestCase { +import static java.lang.Math.toRadians; - @Before - public void setUp() throws Exception { - super.setUp(); +public class TestRecursivePrefixTreeStrategy extends StrategyTestCase { + + private int maxLength; + + //Tests should call this first. + private void init(int maxLength) { + this.maxLength = maxLength; + this.ctx = SimpleSpatialContext.GEO_KM; + this.strategy = new RecursivePrefixTreeStrategy(new GeohashPrefixTree( + ctx, maxLength )); + this.fieldInfo = new SimpleSpatialFieldInfo( getClass().getSimpleName() ); } - @Override - protected SpatialContext getSpatialContext() { - return SimpleSpatialContext.GEO_KM; + @Test + public void testFilterWithVariableScanLevel() throws IOException { + init(GeohashPrefixTree.getMaxLevelsPossible()); + getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS); + + //execute queries for each prefix grid scan level + for(int i = 0; i <= maxLength; i++) { + ((RecursivePrefixTreeStrategy)strategy).setPrefixGridScanLevel(i); + executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_IsWithin_BBox); + } + } + + @Test + public void geohashRecursiveRandom() throws IOException { + init(12); + + //1. Iterate test with the cluster at some worldly point of interest + Point[] clusterCenters = new Point[]{new PointImpl(0,0), new PointImpl(0,90),new PointImpl(0,-90)}; + for (Point clusterCenter : clusterCenters) { + //2. Iterate on size of cluster (a really small one and a large one) + String hashCenter = GeohashUtils.encodeLatLon(clusterCenter.getY(), clusterCenter.getX(), maxLength); + //calculate the number of degrees in the smallest grid box size (use for both lat & lon) + String smallBox = hashCenter.substring(0,hashCenter.length()-1);//chop off leaf precision + Rectangle clusterDims = GeohashUtils.decodeBoundary(smallBox,ctx); + double smallDegrees = Math.max(clusterDims.getMaxX()-clusterDims.getMinX(),clusterDims.getMaxY()-clusterDims.getMinY()); + assert smallDegrees < 1; + double largeDegrees = 20d;//good large size; don't use >=45 for this test code to work + double[] sideDegrees = {largeDegrees,smallDegrees}; + for (double sideDegree : sideDegrees) { + //3. Index random points in this cluster box + deleteAll(); + List points = new ArrayList(); + for(int i = 0; i < 20; i++) { + double x = random().nextDouble()*sideDegree - sideDegree/2 + clusterCenter.getX(); + double y = random().nextDouble()*sideDegree - sideDegree/2 + clusterCenter.getY(); + final Point pt = normPointXY(x, y); + points.add(pt); + addDocument(newDoc("" + i, pt)); + } + commit(); + + //3. Use 4 query centers. Each is radially out from each corner of cluster box by twice distance to box edge. + for(double qcXoff : new double[]{sideDegree,-sideDegree}) {//query-center X offset from cluster center + for(double qcYoff : new double[]{sideDegree,-sideDegree}) {//query-center Y offset from cluster center + Point queryCenter = normPointXY(qcXoff + clusterCenter.getX(), + qcYoff + clusterCenter.getY()); + double[] distRange = calcDistRange(queryCenter,clusterCenter,sideDegree); + //4.1 query a small box getting nothing + checkHits(queryCenter, distRange[0]*0.99, 0, null); + //4.2 Query a large box enclosing the cluster, getting everything + checkHits(queryCenter, distRange[1]*1.01, points.size(), null); + //4.3 Query a medium box getting some (calculate the correct solution and verify) + double queryDist = distRange[0] + (distRange[1]-distRange[0])/2;//average + + //Find matching points. Put into int[] of doc ids which is the same thing as the index into points list. + int[] ids = new int[points.size()]; + int ids_sz = 0; + for (int i = 0; i < points.size(); i++) { + Point point = points.get(i); + if (ctx.getDistCalc().distance(queryCenter, point) <= queryDist) + ids[ids_sz++] = i; + } + ids = Arrays.copyOf(ids, ids_sz); + //assert ids_sz > 0 (can't because randomness keeps us from being able to) + + checkHits(queryCenter, queryDist, ids.length, ids); + } + } + + }//for sideDegree + + }//for clusterCenter + + }//randomTest() + + //TODO can we use super.runTestQueries() ? + private void checkHits(Point pt, double dist, int assertNumFound, int[] assertIds) { + Shape shape = ctx.makeCircle(pt,dist); + SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects,shape); + args.setDistPrecision(0.0); + SearchResults got = executeQuery(strategy.makeQuery(args, fieldInfo), 100); + assertEquals(""+shape,assertNumFound,got.numFound); + if (assertIds != null) { + Set gotIds = new HashSet(); + for (SearchResult result : got.results) { + gotIds.add(Integer.valueOf(result.document.get("id"))); + } + for (int assertId : assertIds) { + assertTrue("has "+assertId,gotIds.contains(assertId)); + } + } + } + + // + private Document newDoc(String id, Shape shape) { + Document doc = new Document(); + doc.add(new StringField("id", id, Field.Store.YES)); + for (IndexableField f : strategy.createFields(fieldInfo, shape, true, storeShape)) { + doc.add(f); + } + return doc; + } + + private double[] calcDistRange(Point startPoint, Point targetCenter, double targetSideDegrees) { + double min = Double.MAX_VALUE; + double max = Double.MIN_VALUE; + for(double xLen : new double[]{targetSideDegrees,-targetSideDegrees}) { + for(double yLen : new double[]{targetSideDegrees,-targetSideDegrees}) { + Point p2 = normPointXY(targetCenter.getX() + xLen / 2, targetCenter.getY() + yLen / 2); + double d = ctx.getDistCalc().distance(startPoint, p2); + min = Math.min(min,d); + max = Math.max(max,d); + } + } + return new double[]{min,max}; + } + + /** Normalize x & y (put in lon-lat ranges) & ensure geohash round-trip for given precision. */ + private Point normPointXY(double x, double y) { + //put x,y as degrees into double[] as radians + double[] latLon = {y*DistanceUtils.DEG_180_AS_RADS, toRadians(x)}; + DistanceUtils.normLatRAD(latLon); + DistanceUtils.normLatRAD(latLon); + double x2 = Math.toDegrees(latLon[1]); + double y2 = Math.toDegrees(latLon[0]); + //overwrite latLon, units is now degrees + + return GeohashUtils.decode(GeohashUtils.encodeLatLon(y2, x2, maxLength),ctx); } } Index: lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java (revision 1351928) +++ lucene/spatial/src/test/org/apache/lucene/spatial/SpatialTestCase.java (revision ) @@ -17,16 +17,15 @@ package org.apache.lucene.spatial; -import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.index.DirectoryReader; -import org.apache.lucene.index.IndexWriter; -import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; +import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.LuceneTestCase; import org.junit.After; import org.junit.Before; @@ -37,7 +36,7 @@ public abstract class SpatialTestCase extends LuceneTestCase { private DirectoryReader indexReader; - private IndexWriter indexWriter; + private RandomIndexWriter indexWriter; private Directory directory; private IndexSearcher indexSearcher; @@ -45,26 +44,15 @@ @Before public void setUp() throws Exception { super.setUp(); - Random random = random(); directory = newDirectory(); - - IndexWriterConfig writerConfig = newIndexWriterConfig(random, TEST_VERSION_CURRENT, new MockAnalyzer(random)); - indexWriter = new IndexWriter(directory, writerConfig); + indexWriter = new RandomIndexWriter(random(),directory); } @Override @After public void tearDown() throws Exception { - if (indexWriter != null) { - indexWriter.close(); - } - if (indexReader != null) { - indexReader.close(); - } - if (directory != null) { - directory.close(); - } + IOUtils.close(indexWriter,indexReader,directory); super.tearDown(); } @@ -87,11 +75,8 @@ protected void commit() throws IOException { indexWriter.commit(); - if (indexReader == null) { - indexReader = DirectoryReader.open(directory); - } else { - indexReader = DirectoryReader.openIfChanged(indexReader); - } + IOUtils.close(indexReader); + indexReader = indexWriter.getReader(); indexSearcher = newSearcher(indexReader); } Index: lucene/spatial/src/test/org/apache/lucene/spatial/vector/BaseTwoDoublesStrategyTestCase.java =================================================================== --- lucene/spatial/src/test/org/apache/lucene/spatial/vector/BaseTwoDoublesStrategyTestCase.java (revision 1351928) +++ lucene/spatial/src/test/org/apache/lucene/spatial/vector/BaseTwoDoublesStrategyTestCase.java (revision 1351928) @@ -1,48 +0,0 @@ -/* - * 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.vector; - -import com.spatial4j.core.context.SpatialContext; -import org.apache.lucene.search.FieldCache; -import org.apache.lucene.spatial.SpatialMatchConcern; -import org.apache.lucene.spatial.StrategyTestCase; -import org.apache.lucene.spatial.util.NumericFieldInfo; -import org.junit.Test; - -import java.io.IOException; - - -public abstract class BaseTwoDoublesStrategyTestCase extends StrategyTestCase { - - protected abstract SpatialContext getSpatialContext(); - - @Override - public void setUp() throws Exception { - super.setUp(); - this.ctx = getSpatialContext(); - this.strategy = new TwoDoublesStrategy(ctx, - new NumericFieldInfo(), FieldCache.NUMERIC_UTILS_DOUBLE_PARSER); - this.fieldInfo = new TwoDoublesFieldInfo(getClass().getSimpleName()); - } - - @Test - public void testCitiesWithinBBox() throws IOException { - getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS); - executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_IsWithin_BBox); - } -} Index: lucene/spatial/src/test/org/apache/lucene/spatial/vector/TestTwoDoublesStrategy.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- lucene/spatial/src/test/org/apache/lucene/spatial/vector/TestTwoDoublesStrategy.java (revision 1351928) +++ lucene/spatial/src/test/org/apache/lucene/spatial/vector/TestTwoDoublesStrategy.java (revision ) @@ -17,13 +17,31 @@ package org.apache.lucene.spatial.vector; -import com.spatial4j.core.context.SpatialContext; import com.spatial4j.core.context.simple.SimpleSpatialContext; +import org.apache.lucene.search.FieldCache; +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; -public class TestTwoDoublesStrategy extends BaseTwoDoublesStrategyTestCase { +import java.io.IOException; +public class TestTwoDoublesStrategy extends StrategyTestCase { + + @Before @Override - protected SpatialContext getSpatialContext() { - return SimpleSpatialContext.GEO_KM; + public void setUp() throws Exception { + super.setUp(); + this.ctx = SimpleSpatialContext.GEO_KM; + this.strategy = new TwoDoublesStrategy(ctx, + new NumericFieldInfo(), FieldCache.NUMERIC_UTILS_DOUBLE_PARSER); + this.fieldInfo = new TwoDoublesFieldInfo(getClass().getSimpleName()); + } + + @Test + public void testCitiesWithinBBox() throws IOException { + getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS); + executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_IsWithin_BBox); } } Index: lucene/spatial/src/test/org/apache/lucene/spatial/prefix/BaseRecursivePrefixTreeStrategyTestCase.java =================================================================== --- lucene/spatial/src/test/org/apache/lucene/spatial/prefix/BaseRecursivePrefixTreeStrategyTestCase.java (revision 1351928) +++ lucene/spatial/src/test/org/apache/lucene/spatial/prefix/BaseRecursivePrefixTreeStrategyTestCase.java (revision 1351928) @@ -1,57 +0,0 @@ -/* - * 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.prefix; - -import com.spatial4j.core.context.SpatialContext; -import org.apache.lucene.spatial.SimpleSpatialFieldInfo; -import org.apache.lucene.spatial.SpatialMatchConcern; -import org.apache.lucene.spatial.StrategyTestCase; -import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree; -import org.junit.Test; - -import java.io.IOException; - - -public abstract class BaseRecursivePrefixTreeStrategyTestCase extends StrategyTestCase { - - private int maxLength; - - protected abstract SpatialContext getSpatialContext(); - - @Override - public void setUp() throws Exception { - super.setUp(); - maxLength = GeohashPrefixTree.getMaxLevelsPossible(); - // SimpleIO - this.ctx = getSpatialContext(); - this.strategy = new RecursivePrefixTreeStrategy(new GeohashPrefixTree( - ctx, maxLength )); - this.fieldInfo = new SimpleSpatialFieldInfo( getClass().getSimpleName() ); - } - - @Test - public void testFilterWithVariableScanLevel() throws IOException { - getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS); - - //execute queries for each prefix grid scan level - for(int i = 0; i <= maxLength; i++) { - ((RecursivePrefixTreeStrategy)strategy).setPrefixGridScanLevel(i); - executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_IsWithin_BBox); - } - } -}