/** 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 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); }
/** 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); }
LatLonDocValuesBoxQuery(String field, double minLatitude, double maxLatitude, double minLongitude, double maxLongitude) { GeoUtils.checkLatitude(minLatitude); GeoUtils.checkLatitude(maxLatitude); GeoUtils.checkLongitude(minLongitude); GeoUtils.checkLongitude(maxLongitude); if (field == null) { throw new IllegalArgumentException("field must not be null"); } this.field = field; this.crossesDateline = minLongitude > maxLongitude; // make sure to compute this before rounding this.minLatitude = GeoEncodingUtils.encodeLatitudeCeil(minLatitude); this.maxLatitude = GeoEncodingUtils.encodeLatitude(maxLatitude); this.minLongitude = GeoEncodingUtils.encodeLongitudeCeil(minLongitude); this.maxLongitude = GeoEncodingUtils.encodeLongitude(maxLongitude); }
/** 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); }
/** * Change the values of this field * @param latitude latitude value: must be within standard +/-90 coordinate bounds. * @param longitude longitude value: must be within standard +/-180 coordinate bounds. * @throws IllegalArgumentException if latitude or longitude are out of bounds */ public void setLocationValue(double latitude, double longitude) { int latitudeEncoded = encodeLatitude(latitude); int longitudeEncoded = encodeLongitude(longitude); fieldsData = Long.valueOf((((long)latitudeEncoded) << 32) | (longitudeEncoded & 0xFFFFFFFFL)); }
/** * 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 }
/** sugar encodes a single point as a byte array, rounding values up */ private static byte[] encodeCeil(double latitude, double longitude) { byte[] bytes = new byte[2 * Integer.BYTES]; NumericUtils.intToSortableBytes(encodeLatitudeCeil(latitude), bytes, 0); NumericUtils.intToSortableBytes(encodeLongitudeCeil(longitude), bytes, Integer.BYTES); return bytes; }
/** 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; }
/** * Turns quantized value from byte array back into a double. * @param src byte array containing 4 bytes to decode at {@code offset} * @param offset offset into {@code src} to decode from. * @return decoded longitude value. */ public static double decodeLongitude(byte[] src, int offset) { return decodeLongitude(NumericUtils.sortableBytesToInt(src, offset)); }
private Polygon2D(Polygon polygon, Polygon2D holes) { super(polygon.minLat, polygon.maxLat, polygon.minLon, polygon.maxLon, polygon.getPolyLats(), polygon.getPolyLons()); this.holes = holes; }
private void readEnd() throws ParseException { while (upto < input.length()) { char ch = input.charAt(upto); if (isJSONWhitespace(ch) == false) { throw newParseException("unexpected character '" + ch + "' after end of GeoJSON object"); } upto++; } }
/** Parses a standard GeoJSON polygon string. The type of the incoming GeoJSON object must be a Polygon or MultiPolygon, optionally * embedded under a "type: Feature". A Polygon will return as a length 1 array, while a MultiPolygon will be 1 or more in length. * * <p>See <a href="http://geojson.org/geojson-spec.html">the GeoJSON specification</a>. */ public static Polygon[] fromGeoJSON(String geojson) throws ParseException { return new SimpleGeoJSONPolygonParser(geojson).parse(); } }
/** Returns relation to the provided rectangle for this component */ protected Relation internalComponentRelate(double minLat, double maxLat, double minLon, double maxLon) { // if the bounding boxes are disjoint then the shape does not cross if (maxLon < this.minLon || minLon > this.maxLon || maxLat < this.minLat || minLat > this.maxLat) { return Relation.CELL_OUTSIDE_QUERY; } // if the rectangle fully encloses us, we cross. if (minLat <= this.minLat && maxLat >= this.maxLat && minLon <= this.minLon && maxLon >= this.maxLon) { return Relation.CELL_CROSSES_QUERY; } Relation shapeRelation = componentRelate(minLat, maxLat, minLon, maxLon); if (shapeRelation != null) { return shapeRelation; } // we cross if (tree.crosses(minLat, maxLat, minLon, maxLon)) { return Relation.CELL_CROSSES_QUERY; } return Relation.CELL_OUTSIDE_QUERY; }
protected EdgeTree(final double minLat, final double maxLat, final double minLon, final double maxLon, double[] lats, double[] lons) { this.minLat = minLat; this.maxLat = maxLat; this.minLon = minLon; this.maxLon = maxLon; this.maxY = maxLat; this.maxX = maxLon; // create interval tree of edges this.tree = createTree(lats, lons); }
/** 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; }
/** Scans the expected string, or throws {@code ParseException} */ private void scan(String expected) throws ParseException { if (upto + expected.length() > input.length()) { throw newParseException("expected \"" + expected + "\" but hit EOF"); } String subString = input.substring(upto, upto+expected.length()); if (subString.equals(expected) == false) { throw newParseException("expected \"" + expected + "\" but got \"" + subString + "\""); } upto += expected.length(); }
/** sugar encodes a single point as a byte array */ private static byte[] encode(double latitude, double longitude) { byte[] bytes = new byte[2 * Integer.BYTES]; NumericUtils.intToSortableBytes(encodeLatitude(latitude), bytes, 0); NumericUtils.intToSortableBytes(encodeLongitude(longitude), bytes, Integer.BYTES); return bytes; }
private char peek() throws ParseException { while (upto < input.length()) { char ch = input.charAt(upto); if (isJSONWhitespace(ch)) { upto++; continue; } return ch; } throw newParseException("unexpected EOF"); }