private double calcDist(Point p1, Point p2) { // TODO use ctx.calc? return DistanceUtils.distHaversineRAD( Math.toRadians(p1.getY()), Math.toRadians(p1.getX()), Math.toRadians(p2.getY()), Math.toRadians(p2.getX())) * ctx.getUnits().earthRadius(); }
@Test public void testBug1() { // AN ISSUE OF distPrec being 0 or the default clearIndex(); final String fieldName = "geohashRecursiveRandom"; Point pt = ctx.makePoint(8.751008491963148, -6.406441060826182); final int ID = 2; assertU(adoc("id", "" + ID, fieldName, pt.getY() + "," + pt.getX())); assertU(commit()); final Point centerPt = ctx.makePoint(19.99999998137355, 20.00000006519258); final double queryDist = 3112.7; checkHits(fieldName, centerPt.getY() + "," + centerPt.getX(), queryDist, 0); }
private static Shape fromSpatial4JShape(com.spatial4j.core.shape.Shape shape) { if (shape instanceof com.spatial4j.core.shape.Point) { com.spatial4j.core.shape.Point point = (com.spatial4j.core.shape.Point) shape; return point(point.getX(), point.getY()); } if (shape instanceof com.spatial4j.core.shape.Circle) { com.spatial4j.core.shape.Circle circle = (com.spatial4j.core.shape.Circle) shape; return circle( point(circle.getCenter().getX(), circle.getCenter().getY()), circle.getRadius()); } if (shape instanceof com.spatial4j.core.shape.Rectangle) { com.spatial4j.core.shape.Rectangle rectangle = (com.spatial4j.core.shape.Rectangle) shape; return rectangle( rectangle.getMinX(), rectangle.getMaxX(), rectangle.getMinY(), rectangle.getMaxY()); } if (shape instanceof com.spatial4j.core.shape.impl.BufferedLineString) { com.spatial4j.core.shape.impl.BufferedLineString spatialLineString = (com.spatial4j.core.shape.impl.BufferedLineString) shape; List<com.spatial4j.core.shape.Point> spatialPoints = spatialLineString.getPoints(); Point[] points = new Point[spatialPoints.size()]; for (int i = 0; i < points.length; i++) points[i] = point(spatialPoints.get(i).getX(), spatialPoints.get(i).getY()); return lineString(points); } if (shape instanceof com.spatial4j.core.shape.jts.JtsGeometry) return fromJtsGeometry((JtsGeometry) shape); throw new IllegalArgumentException("Unsupported shape type: " + shape.getClass().getName()); }
@Override public Point pointOnBearing( Point from, double distDEG, double bearingDEG, SpatialContext ctx, Point reuse) { if (distDEG == 0) { if (reuse == null) return from; reuse.reset(from.getX(), from.getY()); return reuse; } double bearingRAD = DistanceUtils.toRadians(bearingDEG); double x = from.getX() + Math.sin(bearingRAD) * distDEG; double y = from.getY() + Math.cos(bearingRAD) * distDEG; if (reuse == null) { return ctx.makePoint(x, y); } else { reuse.reset(x, y); return reuse; } }
@Override public double distance(Point from, double toX, double toY) { double deltaX = from.getX() - toX; double deltaY = from.getY() - toY; double xSquaredPlusYSquared = deltaX * deltaX + deltaY * deltaY; if (squared) return xSquaredPlusYSquared; return Math.sqrt(xSquaredPlusYSquared); }
private Map<String, Object> buildGeoJsonPolygon(List<Point> points) throws IOException { Map<String, Object> json = new HashMap<String, Object>(); json.put("type", "Polygon"); List<double[]> newPoints = new ArrayList<double[]>(); for (Point point : points) { newPoints.add(new double[] {point.getX(), point.getY()}); } Object[] pointsArray = newPoints.toArray(); Object[] envelopeArray = new Object[] {pointsArray}; json.put("coordinates", envelopeArray); return json; }
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 = calcDist(startPoint, p2); min = Math.min(min, d); max = Math.max(max, d); } } return new double[] {min, max}; }
@Override public Rectangle calcBoxByDistFromPt( Point from, double distDEG, SpatialContext ctx, Rectangle reuse) { double minX = from.getX() - distDEG; double maxX = from.getX() + distDEG; double minY = from.getY() - distDEG; double maxY = from.getY() + distDEG; if (reuse == null) { return ctx.makeRectangle(minX, maxX, minY, maxY); } else { reuse.reset(minX, maxX, minY, maxY); return reuse; } }
@Override public double calcBoxByDistFromPt_yHorizAxisDEG(Point from, double distDEG, SpatialContext ctx) { return from.getY(); }
@Override public boolean within(Point from, double toX, double toY, double distance) { double deltaX = from.getX() - toX; double deltaY = from.getY() - toY; return deltaX * deltaX + deltaY * deltaY <= distance * distance; }
@Test public void geohashRecursiveRandom() { final String fieldName = "geohashRecursiveRandom"; // has length 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(), PRECISION); // 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 clearIndex(); List<Point> points = new ArrayList<Point>(); 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); assertU(adoc("id", "" + i, fieldName, pt.getY() + "," + pt.getX())); } assertU(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 final String queryCenterStr = queryCenter.getY() + "," + queryCenter.getX(); checkHits(fieldName, queryCenterStr, distRange[0] * 0.99, 0); // 4.2 Query a large box enclosing the cluster, getting everything checkHits(fieldName, queryCenterStr, distRange[1] * 1.01, points.size()); // 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 (calcDist(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(fieldName, queryCenterStr, queryDist, ids.length, ids); } } } // for sideDegree } // for clusterCenter } // randomTest()
@Override public double distance(Point from, Point to) { return distance(from, to.getX(), to.getY()); }