/** Builds a Polygon2D from multipolygon */ public static Polygon2D create(Polygon... polygons) { Polygon2D components[] = new Polygon2D[polygons.length]; for (int i = 0; i < components.length; i++) { Polygon gon = polygons[i]; Polygon gonHoles[] = gon.getHoles(); Polygon2D holes = null; if (gonHoles.length > 0) { holes = create(gonHoles); } components[i] = new Polygon2D(gon, holes); } return (Polygon2D)createTree(components, 0, components.length - 1, false); }
/** Returns relation to the provided rectangle for this component */ @Override protected Relation componentRelate(double minLat, double maxLat, double minLon, double maxLon) { // check any holes if (holes != null) { Relation holeRelation = holes.relate(minLat, maxLat, minLon, maxLon); if (holeRelation == Relation.CELL_CROSSES_QUERY) { return Relation.CELL_CROSSES_QUERY; } else if (holeRelation == Relation.CELL_INSIDE_QUERY) { return Relation.CELL_OUTSIDE_QUERY; } } // check each corner: if < 4 are present, its cheaper than crossesSlowly int numCorners = numberOfCorners(minLat, maxLat, minLon, maxLon); if (numCorners == 4) { if (tree.crosses(minLat, maxLat, minLon, maxLon)) { return Relation.CELL_CROSSES_QUERY; } return Relation.CELL_INSIDE_QUERY; } else if (numCorners > 0) { return Relation.CELL_CROSSES_QUERY; } return null; }
@Override protected Relation componentRelateTriangle(double ax, double ay, double bx, double by, double cx, double cy) { // check any holes if (holes != null) { Relation holeRelation = holes.relateTriangle(ax, ay, bx, by, cx, cy); if (holeRelation == Relation.CELL_CROSSES_QUERY) { return Relation.CELL_CROSSES_QUERY; } else if (holeRelation == Relation.CELL_INSIDE_QUERY) { return Relation.CELL_OUTSIDE_QUERY; } } // check each corner: if < 3 are present, its cheaper than crossesSlowly int numCorners = numberOfTriangleCorners(ax, ay, bx, by, cx, cy); if (numCorners == 3) { if (tree.crossesTriangle(ax, ay, bx, by, cx, cy)) { return Relation.CELL_CROSSES_QUERY; } return Relation.CELL_INSIDE_QUERY; } else if (numCorners > 0) { return Relation.CELL_CROSSES_QUERY; } return null; }
/** * Returns true if the point is contained within this polygon. * <p> * See <a href="https://www.ecse.rpi.edu/~wrf/Research/Short_Notes/pnpoly.html"> * https://www.ecse.rpi.edu/~wrf/Research/Short_Notes/pnpoly.html</a> for more information. */ public boolean contains(double latitude, double longitude) { if (latitude <= maxY && longitude <= maxX) { if (componentContains(latitude, longitude)) { return true; } if (left != null) { if (((Polygon2D)left).contains(latitude, longitude)) { return true; } } if (right != null && ((splitX == false && latitude >= minLat) || (splitX && longitude >= minLon))) { if (((Polygon2D)right).contains(latitude, longitude)) { return true; } } } return false; }
/** Create a predicate that checks whether points are within a polygon. * It works the same way as {@link #createDistancePredicate}. * @lucene.internal */ public static PolygonPredicate createPolygonPredicate(Polygon[] polygons, Polygon2D tree) { final Rectangle boundingBox = Rectangle.fromPolygon(polygons); final Function<Rectangle, Relation> boxToRelation = box -> tree.relate( box.minLat, box.maxLat, box.minLon, box.maxLon); final Grid subBoxes = createSubBoxes(boundingBox, boxToRelation); return new PolygonPredicate( subBoxes.latShift, subBoxes.lonShift, subBoxes.latBase, subBoxes.lonBase, subBoxes.maxLatDelta, subBoxes.maxLonDelta, subBoxes.relations, tree); }
NumericUtils.intToSortableBytes(encodeLongitude(box.maxLon), maxLon, 0); final Polygon2D tree = Polygon2D.create(polygons); final GeoEncodingUtils.PolygonPredicate polygonPredicate = GeoEncodingUtils.createPolygonPredicate(polygons, tree);
@Override protected boolean queryMatches(byte[] t, int[] scratchTriangle) { XLatLonShape.decodeTriangle(t, scratchTriangle); double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle[0]); double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle[1]); double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle[2]); double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle[3]); double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle[4]); double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle[5]); if (queryRelation == QueryRelation.WITHIN) { return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY; } // INTERSECTS return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) != Relation.CELL_OUTSIDE_QUERY; }
/** Returns true if the point is contained within this polygon component. */ private boolean componentContains(double latitude, double longitude) { // check bounding box if (latitude < minLat || latitude > maxLat || longitude < minLon || longitude > maxLon) { return false; } if (contains(tree, latitude, longitude)) { if (holes != null && holes.contains(latitude, longitude)) { return false; } return true; } return false; }
private int numberOfTriangleCorners(double ax, double ay, double bx, double by, double cx, double cy) { int containsCount = 0; if (componentContains(ay, ax)) { containsCount++; } if (componentContains(by, bx)) { containsCount++; } if (containsCount == 1) { return containsCount; } if (componentContains(cy, cx)) { containsCount++; } return containsCount; }
@Override protected Relation relateRangeBBoxToQuery(int minXOffset, int minYOffset, byte[] minTriangle, int maxXOffset, int maxYOffset, byte[] maxTriangle) { double minLat = GeoEncodingUtils.decodeLatitude(NumericUtils.sortableBytesToInt(minTriangle, minYOffset)); double minLon = GeoEncodingUtils.decodeLongitude(NumericUtils.sortableBytesToInt(minTriangle, minXOffset)); double maxLat = GeoEncodingUtils.decodeLatitude(NumericUtils.sortableBytesToInt(maxTriangle, maxYOffset)); double maxLon = GeoEncodingUtils.decodeLongitude(NumericUtils.sortableBytesToInt(maxTriangle, maxXOffset)); // check internal node against query return poly2D.relate(minLat, maxLat, minLon, maxLon); }
/** * Creates a query that matches all indexed shapes to the provided polygons */ XLatLonShapePolygonQuery(String field, QueryRelation queryRelation, Polygon... polygons) { super(field, queryRelation); if (polygons == null) { throw new IllegalArgumentException("polygons must not be null"); } if (polygons.length == 0) { throw new IllegalArgumentException("polygons must not be empty"); } for (int i = 0; i < polygons.length; i++) { if (polygons[i] == null) { throw new IllegalArgumentException("polygon[" + i + "] must not be null"); } else if (polygons[i].minLon > polygons[i].maxLon) { throw new IllegalArgumentException("LatLonShapePolygonQuery does not currently support querying across dateline."); } } this.polygons = polygons.clone(); this.poly2D = Polygon2D.create(polygons); }
/** * Returns true if the point is contained within this polygon. * <p> * See <a href="https://www.ecse.rpi.edu/~wrf/Research/Short_Notes/pnpoly.html"> * https://www.ecse.rpi.edu/~wrf/Research/Short_Notes/pnpoly.html</a> for more information. */ public boolean contains(double latitude, double longitude) { if (latitude <= maxY && longitude <= maxX) { if (componentContains(latitude, longitude)) { return true; } if (left != null) { if (((Polygon2D)left).contains(latitude, longitude)) { return true; } } if (right != null && ((splitX == false && latitude >= minLat) || (splitX && longitude >= minLon))) { if (((Polygon2D)right).contains(latitude, longitude)) { return true; } } } return false; }
@Override protected boolean queryMatches(byte[] t) { long a = NumericUtils.sortableBytesToLong(t, 4 * LatLonShape.BYTES); long b = NumericUtils.sortableBytesToLong(t, 5 * LatLonShape.BYTES); long c = NumericUtils.sortableBytesToLong(t, 6 * LatLonShape.BYTES); int aX = (int)((a >>> 32) & 0x00000000FFFFFFFFL); int bX = (int)((b >>> 32) & 0x00000000FFFFFFFFL); int cX = (int)((c >>> 32) & 0x00000000FFFFFFFFL); int aY = (int)(a & 0x00000000FFFFFFFFL); int bY = (int)(b & 0x00000000FFFFFFFFL); int cY = (int)(c & 0x00000000FFFFFFFFL); double alat = GeoEncodingUtils.decodeLatitude(aY); double alon = GeoEncodingUtils.decodeLongitude(aX); double blat = GeoEncodingUtils.decodeLatitude(bY); double blon = GeoEncodingUtils.decodeLongitude(bX); double clat = GeoEncodingUtils.decodeLatitude(cY); double clon = GeoEncodingUtils.decodeLongitude(cX); if (queryRelation == QueryRelation.WITHIN) { return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY; } // INTERSECTS return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) != Relation.CELL_OUTSIDE_QUERY; }
private int numberOfCorners(double minLat, double maxLat, double minLon, double maxLon) { int containsCount = 0; if (componentContains(minLat, minLon)) { containsCount++; } if (componentContains(minLat, maxLon)) { containsCount++; } if (containsCount == 1) { return containsCount; } if (componentContains(maxLat, maxLon)) { containsCount++; } if (containsCount == 2) { return containsCount; } if (componentContains(maxLat, minLon)) { containsCount++; } return containsCount; }
/** Builds a Polygon2D from multipolygon */ public static Polygon2D create(Polygon... polygons) { Polygon2D components[] = new Polygon2D[polygons.length]; for (int i = 0; i < components.length; i++) { Polygon gon = polygons[i]; Polygon gonHoles[] = gon.getHoles(); Polygon2D holes = null; if (gonHoles.length > 0) { holes = create(gonHoles); } components[i] = new Polygon2D(gon, holes); } return (Polygon2D)createTree(components, 0, components.length - 1, false); }
/** Returns relation to the provided rectangle for this component */ @Override protected Relation componentRelate(double minLat, double maxLat, double minLon, double maxLon) { // check any holes if (holes != null) { Relation holeRelation = holes.relate(minLat, maxLat, minLon, maxLon); if (holeRelation == Relation.CELL_CROSSES_QUERY) { return Relation.CELL_CROSSES_QUERY; } else if (holeRelation == Relation.CELL_INSIDE_QUERY) { return Relation.CELL_OUTSIDE_QUERY; } } // check each corner: if < 4 are present, its cheaper than crossesSlowly int numCorners = numberOfCorners(minLat, maxLat, minLon, maxLon); if (numCorners == 4) { if (tree.crosses(minLat, maxLat, minLon, maxLon)) { return Relation.CELL_CROSSES_QUERY; } return Relation.CELL_INSIDE_QUERY; } else if (numCorners > 0) { return Relation.CELL_CROSSES_QUERY; } return null; }
@Override protected Relation componentRelateTriangle(double ax, double ay, double bx, double by, double cx, double cy) { // check any holes if (holes != null) { Relation holeRelation = holes.relateTriangle(ax, ay, bx, by, cx, cy); if (holeRelation == Relation.CELL_CROSSES_QUERY) { return Relation.CELL_CROSSES_QUERY; } else if (holeRelation == Relation.CELL_INSIDE_QUERY) { return Relation.CELL_OUTSIDE_QUERY; } } // check each corner: if < 3 are present, its cheaper than crossesSlowly int numCorners = numberOfTriangleCorners(ax, ay, bx, by, cx, cy); if (numCorners == 3) { if (tree.crossesTriangle(ax, ay, bx, by, cx, cy)) { return Relation.CELL_CROSSES_QUERY; } return Relation.CELL_INSIDE_QUERY; } else if (numCorners > 0) { return Relation.CELL_CROSSES_QUERY; } return null; }
/** Create a predicate that checks whether points are within a polygon. * It works the same way as {@link #createDistancePredicate}. * @lucene.internal */ public static PolygonPredicate createPolygonPredicate(Polygon[] polygons, Polygon2D tree) { final Rectangle boundingBox = Rectangle.fromPolygon(polygons); final Function<Rectangle, Relation> boxToRelation = box -> tree.relate( box.minLat, box.maxLat, box.minLon, box.maxLon); final Grid subBoxes = createSubBoxes(boundingBox, boxToRelation); return new PolygonPredicate( subBoxes.latShift, subBoxes.lonShift, subBoxes.latBase, subBoxes.lonBase, subBoxes.maxLatDelta, subBoxes.maxLonDelta, subBoxes.relations, tree); }
/** * Creates a query that matches all indexed shapes to the provided polygons */ public LatLonShapePolygonQuery(String field, QueryRelation queryRelation, Polygon... polygons) { super(field, queryRelation); if (polygons == null) { throw new IllegalArgumentException("polygons must not be null"); } if (polygons.length == 0) { throw new IllegalArgumentException("polygons must not be empty"); } for (int i = 0; i < polygons.length; i++) { if (polygons[i] == null) { throw new IllegalArgumentException("polygon[" + i + "] must not be null"); } else if (polygons[i].minLon > polygons[i].maxLon) { throw new IllegalArgumentException("LatLonShapePolygonQuery does not currently support querying across dateline."); } } this.polygons = polygons.clone(); this.poly2D = Polygon2D.create(polygons); }