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()); }
private Query getQuery(Function inner, Context context) { RefLiteralPair innerPair = new RefLiteralPair(inner); if (!innerPair.isValid()) { return null; } GeoPointFieldMapper mapper = getGeoPointFieldMapper( innerPair.reference().info().ident().columnIdent().fqn(), context.mapperService); Shape shape = (Shape) innerPair.input().value(); Geometry geometry = JtsSpatialContext.GEO.getGeometryFrom(shape); IndexGeoPointFieldData fieldData = context.fieldDataService.getForField(mapper); Filter filter; if (geometry.isRectangle()) { Rectangle boundingBox = shape.getBoundingBox(); filter = new InMemoryGeoBoundingBoxFilter( new GeoPoint(boundingBox.getMaxY(), boundingBox.getMinX()), new GeoPoint(boundingBox.getMinY(), boundingBox.getMaxX()), fieldData); } else { Coordinate[] coordinates = geometry.getCoordinates(); GeoPoint[] points = new GeoPoint[coordinates.length]; for (int i = 0; i < coordinates.length; i++) { Coordinate coordinate = coordinates[i]; points[i] = new GeoPoint(coordinate.y, coordinate.x); } filter = new GeoPolygonFilter(fieldData, points); } return new FilteredQuery( Queries.newMatchAllQuery(), context.indexCache.filter().cache(filter)); }
protected Rectangle randomRectangle() { final Rectangle WB = ctx.getWorldBounds(); int rW = (int) randomGaussianMeanMax(10, WB.getWidth()); double xMin = randomIntBetween((int) WB.getMinX(), (int) WB.getMaxX() - rW); double xMax = xMin + rW; int yH = (int) randomGaussianMeanMax(Math.min(rW, WB.getHeight()), WB.getHeight()); double yMin = randomIntBetween((int) WB.getMinY(), (int) WB.getMaxY() - yH); double yMax = yMin + yH; return ctx.makeRectangle(xMin, xMax, yMin, yMax); }
/** * Spatial4J shapes have no knowledge of directed edges. For this reason, a bounding box that * wraps the dateline can have a min longitude that is mathematically > than the Rectangles' * minX value. This is an issue for geometric collections (e.g., MultiPolygon and ShapeCollection) * Until geometry logic can be cleaned up in Spatial4J, ES provides the following expansion * algorithm for GeometryCollections */ private Rectangle expandBBox(Rectangle bbox, Rectangle expand) { if (bbox.equals(expand) || bbox.equals(SpatialContext.GEO.getWorldBounds())) { return bbox; } double minX = bbox.getMinX(); double eMinX = expand.getMinX(); double maxX = bbox.getMaxX(); double eMaxX = expand.getMaxX(); double minY = bbox.getMinY(); double eMinY = expand.getMinY(); double maxY = bbox.getMaxY(); double eMaxY = expand.getMaxY(); bbox.reset( Math.min(Math.min(minX, maxX), Math.min(eMinX, eMaxX)), Math.max(Math.max(minX, maxX), Math.max(eMinX, eMaxX)), Math.min(Math.min(minY, maxY), Math.min(eMinY, eMaxY)), Math.max(Math.max(minY, maxY), Math.max(eMinY, eMaxY))); return bbox; }
/** Returns either 0 for none, 1 for some, or 4 for all. */ private int numCornersIntersect(Rectangle r) { // We play some logic games to avoid calling contains() which can be expensive. boolean bool; // if true then all corners intersect, if false then no corners intersect // for partial, we exit early with 1 and ignore bool. bool = (contains(r.getMinX(), r.getMinY())); if (contains(r.getMinX(), r.getMaxY())) { if (!bool) return 1; // partial } else { if (bool) return 1; // partial } if (contains(r.getMaxX(), r.getMinY())) { if (!bool) return 1; // partial } else { if (bool) return 1; // partial } if (contains(r.getMaxX(), r.getMaxY())) { if (!bool) return 1; // partial } else { if (bool) return 1; // partial } return bool ? 4 : 0; }
private SpatialRelation relateRectangleCircleWrapsPole(Rectangle r, SpatialContext ctx) { // This method handles the case where the circle wraps ONE pole, but not both. For both, // there is the inverseCircle case handled before now. The only exception is for the case where // the circle covers the entire globe, and we'll check that first. if (radiusDEG == 180) // whole globe return SpatialRelation.CONTAINS; // Check if r is within the pole wrap region: double yTop = getCenter().getY() + radiusDEG; if (yTop > 90) { double yTopOverlap = yTop - 90; assert yTopOverlap <= 90; if (r.getMinY() >= 90 - yTopOverlap) return SpatialRelation.CONTAINS; } else { double yBot = point.getY() - radiusDEG; if (yBot < -90) { double yBotOverlap = -90 - yBot; assert yBotOverlap <= 90; if (r.getMaxY() <= -90 + yBotOverlap) return SpatialRelation.CONTAINS; } else { // This point is probably not reachable ?? assert yTop == 90 || yBot == -90; // we simply touch a pole // continue } } // If there are no corners to check intersection because r wraps completely... if (r.getWidth() == 360) return SpatialRelation.INTERSECTS; // Check corners: int cornersIntersect = numCornersIntersect(r); // (It might be possible to reduce contains() calls within nCI() to exactly two, but this // intersection // code is complicated enough as it is.) double frontX = getCenter().getX(); if (cornersIntersect == 4) { // all double backX = frontX <= 0 ? frontX + 180 : frontX - 180; if (r.relateXRange(backX, backX).intersects()) return SpatialRelation.INTERSECTS; else return SpatialRelation.CONTAINS; } else if (cornersIntersect == 0) { // none if (r.relateXRange(frontX, frontX).intersects()) return SpatialRelation.INTERSECTS; else return SpatialRelation.DISJOINT; } else // partial return SpatialRelation.INTERSECTS; }
protected Point randomPoint() { final Rectangle WB = ctx.getWorldBounds(); return ctx.makePoint( randomIntBetween((int) WB.getMinX(), (int) WB.getMaxX()), randomIntBetween((int) WB.getMinY(), (int) WB.getMaxY())); }
@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()