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 Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP <+>package org.apache.lucene.spatial.prefix;\n\n/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements. See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport com.spatial4j.core.context.simple.SimpleSpatialContext;\nimport com.spatial4j.core.distance.DistanceUtils;\nimport com.spatial4j.core.shape.Point;\nimport com.spatial4j.core.shape.Rectangle;\nimport com.spatial4j.core.shape.Shape;\nimport com.spatial4j.core.shape.simple.PointImpl;\nimport com.spatial4j.core.util.GeohashUtils;\nimport org.apache.lucene.document.Document;\nimport org.apache.lucene.document.Field;\nimport org.apache.lucene.document.StoredField;\nimport org.apache.lucene.document.StringField;\nimport org.apache.lucene.index.IndexableField;\nimport org.apache.lucene.spatial.SpatialMatchConcern;\nimport org.apache.lucene.spatial.StrategyTestCase;\nimport org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;\nimport org.apache.lucene.spatial.query.SpatialArgs;\nimport org.apache.lucene.spatial.query.SpatialOperation;\nimport org.junit.Test;\n\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static java.lang.Math.toRadians;\n\npublic class TestRecursivePrefixTreeStrategy extends StrategyTestCase {\n\n private int maxLength;\n\n //Tests should call this first.\n private void init(int maxLength) {\n this.maxLength = maxLength;\n this.ctx = SimpleSpatialContext.GEO_KM;\n GeohashPrefixTree grid = new GeohashPrefixTree(ctx, maxLength);\n this.strategy = new RecursivePrefixTreeStrategy(grid, getClass().getSimpleName());\n }\n\n @Test\n public void testFilterWithVariableScanLevel() throws IOException {\n init(GeohashPrefixTree.getMaxLevelsPossible());\n getAddAndVerifyIndexedDocuments(DATA_WORLD_CITIES_POINTS);\n\n //execute queries for each prefix grid scan level\n for(int i = 0; i <= maxLength; i++) {\n ((RecursivePrefixTreeStrategy)strategy).setPrefixGridScanLevel(i);\n executeQueries(SpatialMatchConcern.FILTER, QTEST_Cities_IsWithin_BBox);\n }\n }\n\n @Test\n public void geohashRecursiveRandom() throws IOException {\n init(12);\n\n //1. Iterate test with the cluster at some worldly point of interest\n Point[] clusterCenters = new Point[]{new PointImpl(0,0), new PointImpl(0,90),new PointImpl(0,-90)};\n for (Point clusterCenter : clusterCenters) {\n //2. Iterate on size of cluster (a really small one and a large one)\n String hashCenter = GeohashUtils.encodeLatLon(clusterCenter.getY(), clusterCenter.getX(), maxLength);\n //calculate the number of degrees in the smallest grid box size (use for both lat & lon)\n String smallBox = hashCenter.substring(0,hashCenter.length()-1);//chop off leaf precision\n Rectangle clusterDims = GeohashUtils.decodeBoundary(smallBox,ctx);\n double smallDegrees = Math.max(clusterDims.getMaxX()-clusterDims.getMinX(),clusterDims.getMaxY()-clusterDims.getMinY());\n assert smallDegrees < 1;\n double largeDegrees = 20d;//good large size; don't use >=45 for this test code to work\n double[] sideDegrees = {largeDegrees,smallDegrees};\n for (double sideDegree : sideDegrees) {\n //3. Index random points in this cluster box\n deleteAll();\n List points = new ArrayList();\n for(int i = 0; i < 20; i++) {\n double x = random().nextDouble()*sideDegree - sideDegree/2 + clusterCenter.getX();\n double y = random().nextDouble()*sideDegree - sideDegree/2 + clusterCenter.getY();\n final Point pt = normPointXY(x, y);\n points.add(pt);\n addDocument(newDoc(\"\" + i, pt));\n }\n commit();\n\n //3. Use 4 query centers. Each is radially out from each corner of cluster box by twice distance to box edge.\n for(double qcXoff : new double[]{sideDegree,-sideDegree}) {//query-center X offset from cluster center\n for(double qcYoff : new double[]{sideDegree,-sideDegree}) {//query-center Y offset from cluster center\n Point queryCenter = normPointXY(qcXoff + clusterCenter.getX(),\n qcYoff + clusterCenter.getY());\n double[] distRange = calcDistRange(queryCenter,clusterCenter,sideDegree);\n //4.1 query a small box getting nothing\n checkHits(queryCenter, distRange[0]*0.99, 0, null);\n //4.2 Query a large box enclosing the cluster, getting everything\n checkHits(queryCenter, distRange[1]*1.01, points.size(), null);\n //4.3 Query a medium box getting some (calculate the correct solution and verify)\n double queryDist = distRange[0] + (distRange[1]-distRange[0])/2;//average\n\n //Find matching points. Put into int[] of doc ids which is the same thing as the index into points list.\n int[] ids = new int[points.size()];\n int ids_sz = 0;\n for (int i = 0; i < points.size(); i++) {\n Point point = points.get(i);\n if (ctx.getDistCalc().distance(queryCenter, point) <= queryDist)\n ids[ids_sz++] = i;\n }\n ids = Arrays.copyOf(ids, ids_sz);\n //assert ids_sz > 0 (can't because randomness keeps us from being able to)\n\n checkHits(queryCenter, queryDist, ids.length, ids);\n }\n }\n\n }//for sideDegree\n\n }//for clusterCenter\n\n }//randomTest()\n\n //TODO can we use super.runTestQueries() ?\n private void checkHits(Point pt, double dist, int assertNumFound, int[] assertIds) {\n Shape shape = ctx.makeCircle(pt,dist);\n SpatialArgs args = new SpatialArgs(SpatialOperation.Intersects,shape);\n args.setDistPrecision(0.0);\n SearchResults got = executeQuery(strategy.makeQuery(args), 100);\n assertEquals(\"\"+shape,assertNumFound,got.numFound);\n if (assertIds != null) {\n Set gotIds = new HashSet();\n for (SearchResult result : got.results) {\n gotIds.add(Integer.valueOf(result.document.get(\"id\")));\n }\n for (int assertId : assertIds) {\n assertTrue(\"has \"+assertId,gotIds.contains(assertId));\n }\n }\n }\n\n //\n private Document newDoc(String id, Shape shape) {\n Document doc = new Document();\n doc.add(new StringField(\"id\", id, Field.Store.YES));\n for (IndexableField f : strategy.createIndexableFields(shape)) {\n doc.add(f);\n }\n if (storeShape)\n doc.add(new StoredField(strategy.getFieldName(), ctx.toString(shape)));\n return doc;\n }\n\n private double[] calcDistRange(Point startPoint, Point targetCenter, double targetSideDegrees) {\n double min = Double.MAX_VALUE;\n double max = Double.MIN_VALUE;\n for(double xLen : new double[]{targetSideDegrees,-targetSideDegrees}) {\n for(double yLen : new double[]{targetSideDegrees,-targetSideDegrees}) {\n Point p2 = normPointXY(targetCenter.getX() + xLen / 2, targetCenter.getY() + yLen / 2);\n double d = ctx.getDistCalc().distance(startPoint, p2);\n min = Math.min(min,d);\n max = Math.max(max,d);\n }\n }\n return new double[]{min,max};\n }\n\n /** Normalize x & y (put in lon-lat ranges) & ensure geohash round-trip for given precision. */\n private Point normPointXY(double x, double y) {\n //put x,y as degrees into double[] as radians\n double[] latLon = {y*DistanceUtils.DEG_180_AS_RADS, toRadians(x)};\n DistanceUtils.normLatRAD(latLon);\n DistanceUtils.normLatRAD(latLon);\n double x2 = Math.toDegrees(latLon[1]);\n double y2 = Math.toDegrees(latLon[0]);\n //overwrite latLon, units is now degrees\n\n return GeohashUtils.decode(GeohashUtils.encodeLatLon(y2, x2, maxLength),ctx);\n }\n}\n =================================================================== --- lucene/spatial/src/test/org/apache/lucene/spatial/prefix/TestRecursivePrefixTreeStrategy.java (revision 261e82f1abb3335981f46bcb4030f55be542ba25) +++ lucene/spatial/src/test/org/apache/lucene/spatial/prefix/TestRecursivePrefixTreeStrategy.java (revision ) @@ -70,6 +70,37 @@ } @Test + public void testPointAndRadius() throws IOException{ + init(GeohashPrefixTree.getMaxLevelsPossible()); + + addDocument(newDoc("spatials/1", ctx.makePoint(2.8028712999999925, 48.3708044)));//lon, lat + commit(); + + Point queryShape = ctx.makePoint(2.4632387000000335, 48.6003516); + checkHitsBroken(queryShape, 35.75, 1, null); + checkHitsBroken(queryShape, 30, 0, null); + checkHitsBroken(queryShape, 33, 0, null); + checkHitsBroken(queryShape, 34, 0, null); + } + + private void checkHitsBroken(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), 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)); + } + } + } + + @Test public void geohashRecursiveRandom() throws IOException { init(12);