/** * Constructs a bounding box by first validating the provided latitude and longitude coordinates */ public Rectangle(double minLat, double maxLat, double minLon, double maxLon) { GeoUtils.checkLatitude(minLat); GeoUtils.checkLatitude(maxLat); GeoUtils.checkLongitude(minLon); GeoUtils.checkLongitude(maxLon); this.minLon = minLon; this.maxLon = maxLon; this.minLat = minLat; this.maxLat = maxLat; assert maxLat >= minLat; // NOTE: cannot assert maxLon >= minLon since this rect could cross the dateline }
/** uses orient method to compute whether two line segments cross */ public static boolean lineCrossesLine(double a1x, double a1y, double b1x, double b1y, double a2x, double a2y, double b2x, double b2y) { // shortcut: either "line" is actually a point if ((a1x == b1x && a1y == b1y) || (a2x == b2x && a2y == b2y)) { return false; } if (orient(a2x, a2y, b2x, b2y, a1x, a1y) * orient(a2x, a2y, b2x, b2y, b1x, b1y) <= 0 && orient(a1x, a1y, b1x, b1y, a2x, a2y) * orient(a1x, a1y, b1x, b1y, b2x, b2y) <= 0) { return true; } return false; }
/** Create a predicate that checks whether points are within a distance of a given point. * It works by computing the bounding box around the circle that is defined * by the given points/distance and splitting it into between 1024 and 4096 * smaller boxes (4096*0.75^2=2304 on average). Then for each sub box, it * computes the relation between this box and the distance query. Finally at * search time, it first computes the sub box that the point belongs to, * most of the time, no distance computation will need to be performed since * all points from the sub box will either be in or out of the circle. * @lucene.internal */ public static DistancePredicate createDistancePredicate(double lat, double lon, double radiusMeters) { final Rectangle boundingBox = Rectangle.fromPointDistance(lat, lon, radiusMeters); final double axisLat = Rectangle.axisLat(lat, radiusMeters); final double distanceSortKey = GeoUtils.distanceQuerySortKey(radiusMeters); final Function<Rectangle, Relation> boxToRelation = box -> GeoUtils.relate( box.minLat, box.maxLat, box.minLon, box.maxLon, lat, lon, distanceSortKey, axisLat); final Grid subBoxes = createSubBoxes(boundingBox, boxToRelation); return new DistancePredicate( subBoxes.latShift, subBoxes.lonShift, subBoxes.latBase, subBoxes.lonBase, subBoxes.maxLatDelta, subBoxes.maxLonDelta, subBoxes.relations, lat, lon, distanceSortKey); }
checkLatitude(centerLat); checkLongitude(centerLon); final double radLat = toRadians(centerLat); final double radLon = toRadians(centerLon); double deltaLon = asin(sloppySin(radDistance) / cos(radLat)); minLon = radLon - deltaLon; if (minLon < MIN_LON_RADIANS) {
/** * Quantizes double (64 bit) latitude into 32 bits (rounding up: in the direction of +90) * @param latitude latitude value: must be within standard +/-90 coordinate bounds. * @return encoded value as a 32-bit {@code int} * @throws IllegalArgumentException if latitude is out of bounds */ public static int encodeLatitudeCeil(double latitude) { GeoUtils.checkLatitude(latitude); // the maximum possible value cannot be encoded without overflow if (latitude == 90.0D) { latitude = Math.nextDown(latitude); } return (int) Math.ceil(latitude / LAT_DECODE); }
/** * Quantizes double (64 bit) longitude into 32 bits (rounding down: in the direction of -180) * @param longitude longitude value: must be within standard +/-180 coordinate bounds. * @return encoded value as a 32-bit {@code int} * @throws IllegalArgumentException if longitude is out of bounds */ public static int encodeLongitude(double longitude) { checkLongitude(longitude); // the maximum possible value cannot be encoded without overflow if (longitude == 180.0D) { longitude = Math.nextDown(longitude); } return (int) Math.floor(longitude / LON_DECODE); }
/** * Compute the relation between the provided box and distance query. * This only works for boxes that do not cross the dateline. */ public static PointValues.Relation relate( double minLat, double maxLat, double minLon, double maxLon, double lat, double lon, double distanceSortKey, double axisLat) { if (minLon > maxLon) { throw new IllegalArgumentException("Box crosses the dateline"); } if ((lon < minLon || lon > maxLon) && (axisLat + Rectangle.AXISLAT_ERROR < minLat || axisLat - Rectangle.AXISLAT_ERROR > maxLat)) { // circle not fully inside / crossing axis if (SloppyMath.haversinSortKey(lat, lon, minLat, minLon) > distanceSortKey && SloppyMath.haversinSortKey(lat, lon, minLat, maxLon) > distanceSortKey && SloppyMath.haversinSortKey(lat, lon, maxLat, minLon) > distanceSortKey && SloppyMath.haversinSortKey(lat, lon, maxLat, maxLon) > distanceSortKey) { // no points inside return Relation.CELL_OUTSIDE_QUERY; } } if (within90LonDegrees(lon, minLon, maxLon) && SloppyMath.haversinSortKey(lat, lon, minLat, minLon) <= distanceSortKey && SloppyMath.haversinSortKey(lat, lon, minLat, maxLon) <= distanceSortKey && SloppyMath.haversinSortKey(lat, lon, maxLat, minLon) <= distanceSortKey && SloppyMath.haversinSortKey(lat, lon, maxLat, maxLon) <= distanceSortKey) { // we are fully enclosed, collect everything within this subtree return Relation.CELL_INSIDE_QUERY; } return Relation.CELL_CROSSES_QUERY; }
if (lineCrossesLine(ax, ay, bx, by, dx, dy, ex, ey)) { return true; if (lineCrossesLine(bx, by, cx, cy, dx, dy, ex, ey)) { return true; if (lineCrossesLine(cx, cy, ax, ay, dx, dy, ex, ey)) { return true;
final double sortKey = GeoUtils.distanceQuerySortKey(radiusMeters);
checkLatitude(centerLat); checkLongitude(centerLon); final double radLat = toRadians(centerLat); final double radLon = toRadians(centerLon); double deltaLon = asin(sloppySin(radDistance) / cos(radLat)); minLon = radLon - deltaLon; if (minLon < MIN_LON_RADIANS) {
/** * Quantizes double (64 bit) latitude into 32 bits (rounding down: in the direction of -90) * @param latitude latitude value: must be within standard +/-90 coordinate bounds. * @return encoded value as a 32-bit {@code int} * @throws IllegalArgumentException if latitude is out of bounds */ public static int encodeLatitude(double latitude) { checkLatitude(latitude); // the maximum possible value cannot be encoded without overflow if (latitude == 90.0D) { latitude = Math.nextDown(latitude); } return (int) Math.floor(latitude / LAT_DECODE); }
/** * Quantizes double (64 bit) longitude into 32 bits (rounding up: in the direction of +180) * @param longitude longitude value: must be within standard +/-180 coordinate bounds. * @return encoded value as a 32-bit {@code int} * @throws IllegalArgumentException if longitude is out of bounds */ public static int encodeLongitudeCeil(double longitude) { GeoUtils.checkLongitude(longitude); // the maximum possible value cannot be encoded without overflow if (longitude == 180.0D) { longitude = Math.nextDown(longitude); } return (int) Math.ceil(longitude / LON_DECODE); }
/** * Compute the relation between the provided box and distance query. * This only works for boxes that do not cross the dateline. */ public static PointValues.Relation relate( double minLat, double maxLat, double minLon, double maxLon, double lat, double lon, double distanceSortKey, double axisLat) { if (minLon > maxLon) { throw new IllegalArgumentException("Box crosses the dateline"); } if ((lon < minLon || lon > maxLon) && (axisLat + Rectangle.AXISLAT_ERROR < minLat || axisLat - Rectangle.AXISLAT_ERROR > maxLat)) { // circle not fully inside / crossing axis if (SloppyMath.haversinSortKey(lat, lon, minLat, minLon) > distanceSortKey && SloppyMath.haversinSortKey(lat, lon, minLat, maxLon) > distanceSortKey && SloppyMath.haversinSortKey(lat, lon, maxLat, minLon) > distanceSortKey && SloppyMath.haversinSortKey(lat, lon, maxLat, maxLon) > distanceSortKey) { // no points inside return Relation.CELL_OUTSIDE_QUERY; } } if (within90LonDegrees(lon, minLon, maxLon) && SloppyMath.haversinSortKey(lat, lon, minLat, minLon) <= distanceSortKey && SloppyMath.haversinSortKey(lat, lon, minLat, maxLon) <= distanceSortKey && SloppyMath.haversinSortKey(lat, lon, maxLat, minLon) <= distanceSortKey && SloppyMath.haversinSortKey(lat, lon, maxLat, maxLon) <= distanceSortKey) { // we are fully enclosed, collect everything within this subtree return Relation.CELL_INSIDE_QUERY; } return Relation.CELL_CROSSES_QUERY; }
if (lineCrossesLine(ax, ay, bx, by, dx, dy, ex, ey)) { return true; if (lineCrossesLine(bx, by, cx, cy, dx, dy, ex, ey)) { return true; if (lineCrossesLine(cx, cy, ax, ay, dx, dy, ex, ey)) { return true;
final double sortKey = GeoUtils.distanceQuerySortKey(radiusMeters);
LatLonPointSortField(String field, double latitude, double longitude) { super(field, SortField.Type.CUSTOM); if (field == null) { throw new IllegalArgumentException("field must not be null"); } GeoUtils.checkLatitude(latitude); GeoUtils.checkLongitude(longitude); this.latitude = latitude; this.longitude = longitude; setMissingValue(Double.POSITIVE_INFINITY); }
if (orient(cx, cy, dx, dy, minLon, maxLat) * orient(cx, cy, dx, dy, maxLon, maxLat) <= 0 && orient(minLon, maxLat, maxLon, maxLat, cx, cy) * orient(minLon, maxLat, maxLon, maxLat, dx, dy) <= 0) { return true; if (orient(cx, cy, dx, dy, maxLon, maxLat) * orient(cx, cy, dx, dy, maxLon, minLat) <= 0 && orient(maxLon, maxLat, maxLon, minLat, cx, cy) * orient(maxLon, maxLat, maxLon, minLat, dx, dy) <= 0) { return true; if (orient(cx, cy, dx, dy, maxLon, minLat) * orient(cx, cy, dx, dy, minLon, minLat) <= 0 && orient(maxLon, minLat, minLon, minLat, cx, cy) * orient(maxLon, minLat, minLon, minLat, dx, dy) <= 0) { return true; if (orient(cx, cy, dx, dy, minLon, minLat) * orient(cx, cy, dx, dy, minLon, maxLat) <= 0 && orient(minLon, minLat, minLon, maxLat, cx, cy) * orient(minLon, minLat, minLon, maxLat, dx, dy) <= 0) { return true;
/** * Quantizes double (64 bit) latitude into 32 bits (rounding down: in the direction of -90) * @param latitude latitude value: must be within standard +/-90 coordinate bounds. * @return encoded value as a 32-bit {@code int} * @throws IllegalArgumentException if latitude is out of bounds */ public static int encodeLatitude(double latitude) { checkLatitude(latitude); // the maximum possible value cannot be encoded without overflow if (latitude == 90.0D) { latitude = Math.nextDown(latitude); } return (int) Math.floor(latitude / LAT_DECODE); }
/** * Quantizes double (64 bit) longitude into 32 bits (rounding up: in the direction of +180) * @param longitude longitude value: must be within standard +/-180 coordinate bounds. * @return encoded value as a 32-bit {@code int} * @throws IllegalArgumentException if longitude is out of bounds */ public static int encodeLongitudeCeil(double longitude) { GeoUtils.checkLongitude(longitude); // the maximum possible value cannot be encoded without overflow if (longitude == 180.0D) { longitude = Math.nextDown(longitude); } return (int) Math.ceil(longitude / LON_DECODE); }
/** Create a predicate that checks whether points are within a distance of a given point. * It works by computing the bounding box around the circle that is defined * by the given points/distance and splitting it into between 1024 and 4096 * smaller boxes (4096*0.75^2=2304 on average). Then for each sub box, it * computes the relation between this box and the distance query. Finally at * search time, it first computes the sub box that the point belongs to, * most of the time, no distance computation will need to be performed since * all points from the sub box will either be in or out of the circle. * @lucene.internal */ public static DistancePredicate createDistancePredicate(double lat, double lon, double radiusMeters) { final Rectangle boundingBox = Rectangle.fromPointDistance(lat, lon, radiusMeters); final double axisLat = Rectangle.axisLat(lat, radiusMeters); final double distanceSortKey = GeoUtils.distanceQuerySortKey(radiusMeters); final Function<Rectangle, Relation> boxToRelation = box -> GeoUtils.relate( box.minLat, box.maxLat, box.minLon, box.maxLon, lat, lon, distanceSortKey, axisLat); final Grid subBoxes = createSubBoxes(boundingBox, boxToRelation); return new DistancePredicate( subBoxes.latShift, subBoxes.lonShift, subBoxes.latBase, subBoxes.lonBase, subBoxes.maxLatDelta, subBoxes.maxLonDelta, subBoxes.relations, lat, lon, distanceSortKey); }