mirror of https://github.com/apache/lucene.git
LUCENE-9141: Simplify LatLonShapeXQuery API by adding a new abstract class called LatLonGeometry. (#1170)
This commit is contained in:
parent
29469b454f
commit
a9482911a8
|
@ -134,6 +134,9 @@ Improvements
|
|||
|
||||
* LUCENE-9152: Improve line intersections with polygons when they are touching from the outside. (Ignacio Vera)
|
||||
|
||||
* LUCENE-9141: Simplify LatLonShapeXQuery API by adding a new abstract class called LatLonGeometry. Queries are
|
||||
executed with input objects that extend such interface. (Ignacio Vera)
|
||||
|
||||
Optimizations
|
||||
---------------------
|
||||
|
||||
|
|
|
@ -22,7 +22,9 @@ import java.util.List;
|
|||
import org.apache.lucene.document.ShapeField.QueryRelation; // javadoc
|
||||
import org.apache.lucene.document.ShapeField.Triangle;
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
import org.apache.lucene.geo.LatLonGeometry;
|
||||
import org.apache.lucene.geo.Line;
|
||||
import org.apache.lucene.geo.Point;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
import org.apache.lucene.geo.Tessellator;
|
||||
import org.apache.lucene.index.PointValues; // javadoc
|
||||
|
@ -37,7 +39,7 @@ import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude;
|
|||
* An geo shape utility class for indexing and searching gis geometries
|
||||
* whose vertices are latitude, longitude values (in decimal degrees).
|
||||
* <p>
|
||||
* This class defines six static factory methods for common indexing and search operations:
|
||||
* This class defines seven static factory methods for common indexing and search operations:
|
||||
* <ul>
|
||||
* <li>{@link #createIndexableFields(String, Polygon)} for indexing a geo polygon.
|
||||
* <li>{@link #createIndexableFields(String, Line)} for indexing a geo linestring.
|
||||
|
@ -45,6 +47,8 @@ import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude;
|
|||
* <li>{@link #newBoxQuery newBoxQuery()} for matching geo shapes that have some {@link QueryRelation} with a bounding box.
|
||||
* <li>{@link #newLineQuery newLineQuery()} for matching geo shapes that have some {@link QueryRelation} with a linestring.
|
||||
* <li>{@link #newPolygonQuery newPolygonQuery()} for matching geo shapes that have some {@link QueryRelation} with a polygon.
|
||||
* <li>{@link #newGeometryQuery newGeometryQuery()} for matching geo shapes that have some {@link QueryRelation}
|
||||
* with one or more {@link LatLonGeometry}.
|
||||
* </ul>
|
||||
|
||||
* <b>WARNING</b>: Like {@link LatLonPoint}, vertex values are indexed with some loss of precision from the
|
||||
|
@ -107,40 +111,36 @@ public class LatLonShape {
|
|||
* note: does not support dateline crossing
|
||||
**/
|
||||
public static Query newLineQuery(String field, QueryRelation queryRelation, Line... lines) {
|
||||
if (queryRelation == QueryRelation.CONTAINS && lines.length > 1) {
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
for (int i =0; i < lines.length; i++) {
|
||||
builder.add(newLineQuery(field, queryRelation, lines[i]), BooleanClause.Occur.MUST);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
return new LatLonShapeLineQuery(field, queryRelation, lines);
|
||||
return newGeometryQuery(field, queryRelation, lines);
|
||||
}
|
||||
|
||||
/** create a query to find all indexed geo shapes that intersect a provided polygon (or array of polygons)
|
||||
* note: does not support dateline crossing
|
||||
**/
|
||||
public static Query newPolygonQuery(String field, QueryRelation queryRelation, Polygon... polygons) {
|
||||
if (queryRelation == QueryRelation.CONTAINS && polygons.length > 1) {
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
for (int i =0; i < polygons.length; i++) {
|
||||
builder.add(newPolygonQuery(field, queryRelation, polygons[i]), BooleanClause.Occur.MUST);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
return new LatLonShapePolygonQuery(field, queryRelation, polygons);
|
||||
return newGeometryQuery(field, queryRelation, polygons);
|
||||
}
|
||||
|
||||
/** create a query to find all indexed shapes that comply the {@link QueryRelation} with the provided point
|
||||
/** create a query to find all indexed shapes that comply the {@link QueryRelation} with the provided points
|
||||
**/
|
||||
public static Query newPointQuery(String field, QueryRelation queryRelation, double[]... points) {
|
||||
if (queryRelation == QueryRelation.CONTAINS && points.length > 1) {
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
Point[] pointArray = new Point[points.length];
|
||||
for (int i =0; i < points.length; i++) {
|
||||
builder.add(newPointQuery(field, queryRelation, points[i]), BooleanClause.Occur.MUST);
|
||||
pointArray[i] = new Point(points[i][0], points[i][1]);
|
||||
}
|
||||
return newGeometryQuery(field, queryRelation, pointArray);
|
||||
}
|
||||
|
||||
/** create a query to find all indexed geo shapes that intersect a provided geometry (or array of geometries).
|
||||
**/
|
||||
public static Query newGeometryQuery(String field, QueryRelation queryRelation, LatLonGeometry... latLonGeometries) {
|
||||
if (queryRelation == QueryRelation.CONTAINS && latLonGeometries.length > 1) {
|
||||
BooleanQuery.Builder builder = new BooleanQuery.Builder();
|
||||
for (int i = 0; i < latLonGeometries.length; i++) {
|
||||
builder.add(newGeometryQuery(field, queryRelation, latLonGeometries[i]), BooleanClause.Occur.MUST);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
return new LatLonShapePointQuery(field, queryRelation, points);
|
||||
return new LatLonShapeQuery(field, queryRelation, latLonGeometries);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.document;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.lucene.document.ShapeField.QueryRelation;
|
||||
import org.apache.lucene.geo.Component2D;
|
||||
import org.apache.lucene.geo.GeoEncodingUtils;
|
||||
import org.apache.lucene.geo.Line;
|
||||
import org.apache.lucene.geo.Line2D;
|
||||
import org.apache.lucene.index.PointValues.Relation;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
|
||||
/**
|
||||
* Finds all previously indexed geo shapes that intersect the specified arbitrary {@code Line}.
|
||||
* <p>
|
||||
* Note:
|
||||
* <ul>
|
||||
* <li>{@code QueryRelation.WITHIN} queries are not yet supported</li>
|
||||
* <li>Dateline crossing is not yet supported</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* todo:
|
||||
* <ul>
|
||||
* <li>Add distance support for buffered queries</li>
|
||||
* </ul>
|
||||
* <p>The field must be indexed using
|
||||
* {@link org.apache.lucene.document.LatLonShape#createIndexableFields} added per document.
|
||||
**/
|
||||
final class LatLonShapeLineQuery extends ShapeQuery {
|
||||
final Line[] lines;
|
||||
final private Component2D line2D;
|
||||
|
||||
public LatLonShapeLineQuery(String field, QueryRelation queryRelation, Line... lines) {
|
||||
super(field, queryRelation);
|
||||
/** line queries do not support within relations, only intersects and disjoint */
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
throw new IllegalArgumentException("LatLonShapeLineQuery does not support " + QueryRelation.WITHIN + " queries");
|
||||
}
|
||||
|
||||
if (lines == null) {
|
||||
throw new IllegalArgumentException("lines must not be null");
|
||||
}
|
||||
if (lines.length == 0) {
|
||||
throw new IllegalArgumentException("lines must not be empty");
|
||||
}
|
||||
for (int i = 0; i < lines.length; ++i) {
|
||||
if (lines[i] == null) {
|
||||
throw new IllegalArgumentException("line[" + i + "] must not be null");
|
||||
} else if (lines[i].minLon > lines[i].maxLon) {
|
||||
throw new IllegalArgumentException("LatLonShapeLineQuery does not currently support querying across dateline.");
|
||||
}
|
||||
}
|
||||
this.lines = lines.clone();
|
||||
this.line2D = Line2D.create(lines);
|
||||
}
|
||||
|
||||
@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 line2D.relate(minLon, maxLon, minLat, maxLat);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, QueryRelation queryRelation) {
|
||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||
|
||||
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle.aY);
|
||||
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle.aX);
|
||||
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle.bY);
|
||||
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle.bX);
|
||||
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle.cY);
|
||||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle.cX);
|
||||
|
||||
switch (queryRelation) {
|
||||
case INTERSECTS: return line2D.relateTriangle(alon, alat, blon, blat, clon, clat) != Relation.CELL_OUTSIDE_QUERY;
|
||||
case WITHIN: return line2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
||||
case DISJOINT: return line2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_OUTSIDE_QUERY;
|
||||
default: throw new IllegalArgumentException("Unsupported query type :[" + queryRelation + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component2D.WithinRelation queryWithin(byte[] t, ShapeField.DecodedTriangle scratchTriangle) {
|
||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||
|
||||
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle.aY);
|
||||
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle.aX);
|
||||
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle.bY);
|
||||
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle.bX);
|
||||
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle.cY);
|
||||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle.cX);
|
||||
|
||||
return line2D.withinTriangle(alon, alat, scratchTriangle.ab, blon, blat, scratchTriangle.bc, clon, clat, scratchTriangle.ca);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(String field) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(getClass().getSimpleName());
|
||||
sb.append(':');
|
||||
if (this.field.equals(field) == false) {
|
||||
sb.append(" field=");
|
||||
sb.append(this.field);
|
||||
sb.append(':');
|
||||
}
|
||||
sb.append("Line(").append(lines[0].toGeoJSON()).append(')');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean equalsTo(Object o) {
|
||||
return super.equalsTo(o) && Arrays.equals(lines, ((LatLonShapeLineQuery)o).lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = super.hashCode();
|
||||
hash = 31 * hash + Arrays.hashCode(lines);
|
||||
return hash;
|
||||
}
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.document;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.lucene.geo.Component2D;
|
||||
import org.apache.lucene.geo.GeoEncodingUtils;
|
||||
import org.apache.lucene.geo.Point2D;
|
||||
import org.apache.lucene.index.PointValues.Relation;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
|
||||
/**
|
||||
* Finds all previously indexed shapes that intersect the specified bounding box.
|
||||
*
|
||||
* <p>The field must be indexed using
|
||||
* {@link LatLonShape#createIndexableFields} added per document.
|
||||
**/
|
||||
final class LatLonShapePointQuery extends ShapeQuery {
|
||||
final Component2D point2D;
|
||||
final double[][] points;
|
||||
|
||||
public LatLonShapePointQuery(String field, ShapeField.QueryRelation queryRelation, double[][] points) {
|
||||
super(field, queryRelation);
|
||||
this.points = points;
|
||||
this.point2D = Point2D.create(points);
|
||||
}
|
||||
|
||||
@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 point2D.relate(minLon, maxLon, minLat, maxLat);
|
||||
}
|
||||
|
||||
/** returns true if the query matches the encoded triangle */
|
||||
@Override
|
||||
protected boolean queryMatches(byte[] t, ShapeField.DecodedTriangle scratchTriangle, ShapeField.QueryRelation queryRelation) {
|
||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||
|
||||
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle.aY);
|
||||
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle.aX);
|
||||
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle.bY);
|
||||
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle.bX);
|
||||
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle.cY);
|
||||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle.cX);
|
||||
|
||||
switch (queryRelation) {
|
||||
case INTERSECTS:
|
||||
return point2D.relateTriangle(alon, alat, blon, blat, clon, clat) != Relation.CELL_OUTSIDE_QUERY;
|
||||
case WITHIN:
|
||||
return point2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
||||
case DISJOINT:
|
||||
return point2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_OUTSIDE_QUERY;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported query type :[" + queryRelation + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component2D.WithinRelation queryWithin(byte[] t, ShapeField.DecodedTriangle scratchTriangle) {
|
||||
ShapeField.decodeTriangle(t, scratchTriangle);
|
||||
|
||||
double alat = GeoEncodingUtils.decodeLatitude(scratchTriangle.aY);
|
||||
double alon = GeoEncodingUtils.decodeLongitude(scratchTriangle.aX);
|
||||
double blat = GeoEncodingUtils.decodeLatitude(scratchTriangle.bY);
|
||||
double blon = GeoEncodingUtils.decodeLongitude(scratchTriangle.bX);
|
||||
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle.cY);
|
||||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle.cX);
|
||||
|
||||
return point2D.withinTriangle(alon, alat, scratchTriangle.ab, blon, blat, scratchTriangle.bc, clon, clat, scratchTriangle.ca);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return sameClassAs(o) && equalsTo(getClass().cast(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean equalsTo(Object o) {
|
||||
return super.equalsTo(o) && Arrays.equals(points, ((LatLonShapePointQuery)o).points);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = super.hashCode();
|
||||
hash = 31 * hash + Arrays.hashCode(points);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(String field) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(getClass().getSimpleName());
|
||||
sb.append(':');
|
||||
if (this.field.equals(field) == false) {
|
||||
sb.append(" field=");
|
||||
sb.append(this.field);
|
||||
sb.append(':');
|
||||
}
|
||||
sb.append("lat = " + points[0][0] + " , lon = " + points[0][1]);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -21,46 +21,38 @@ import java.util.Arrays;
|
|||
import org.apache.lucene.document.ShapeField.QueryRelation;
|
||||
import org.apache.lucene.geo.Component2D;
|
||||
import org.apache.lucene.geo.GeoEncodingUtils;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
import org.apache.lucene.geo.Polygon2D;
|
||||
import org.apache.lucene.geo.LatLonGeometry;
|
||||
import org.apache.lucene.geo.Line;
|
||||
import org.apache.lucene.index.PointValues.Relation;
|
||||
import org.apache.lucene.util.NumericUtils;
|
||||
|
||||
/**
|
||||
* Finds all previously indexed geo shapes that intersect the specified arbitrary.
|
||||
* <p>
|
||||
* Note:
|
||||
* <ul>
|
||||
* <li>Dateline crossing is not yet supported. Polygons should be cut at the dateline and provided as a multipolygon query</li>
|
||||
* </ul>
|
||||
* Finds all previously indexed cartesian shapes that comply the given {@link QueryRelation} with
|
||||
* the specified array of {@link LatLonGeometry}.
|
||||
*
|
||||
* <p>The field must be indexed using {@link LatLonShape#createIndexableFields} added per document.
|
||||
*
|
||||
* <p>The field must be indexed using
|
||||
* {@link org.apache.lucene.document.LatLonShape#createIndexableFields} added per document.
|
||||
**/
|
||||
final class LatLonShapePolygonQuery extends ShapeQuery {
|
||||
final Polygon[] polygons;
|
||||
final private Component2D poly2D;
|
||||
final class LatLonShapeQuery extends ShapeQuery {
|
||||
final private LatLonGeometry[] geometries;
|
||||
final private Component2D component2D;
|
||||
|
||||
/**
|
||||
* Creates a query that matches all indexed shapes to the provided polygons
|
||||
* Creates a query that matches all indexed shapes to the provided array of {@link LatLonGeometry}
|
||||
*/
|
||||
public LatLonShapePolygonQuery(String field, QueryRelation queryRelation, Polygon... polygons) {
|
||||
LatLonShapeQuery(String field, QueryRelation queryRelation, LatLonGeometry[] geometries) {
|
||||
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.");
|
||||
if (queryRelation == QueryRelation.WITHIN) {
|
||||
for (LatLonGeometry geometry : geometries) {
|
||||
if (geometry instanceof Line) {
|
||||
// TODO: line queries do not support within relations
|
||||
throw new IllegalArgumentException("LatLonShapeQuery does not support " + QueryRelation.WITHIN + " queries with line geometries");
|
||||
}
|
||||
}
|
||||
this.polygons = polygons.clone();
|
||||
this.poly2D = Polygon2D.create(polygons);
|
||||
|
||||
}
|
||||
this.component2D = LatLonGeometry.create(geometries);
|
||||
this.geometries = geometries.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -73,7 +65,7 @@ final class LatLonShapePolygonQuery extends ShapeQuery {
|
|||
double maxLon = GeoEncodingUtils.decodeLongitude(NumericUtils.sortableBytesToInt(maxTriangle, maxXOffset));
|
||||
|
||||
// check internal node against query
|
||||
return poly2D.relate(minLon, maxLon, minLat, maxLat);
|
||||
return component2D.relate(minLon, maxLon, minLat, maxLat);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -88,9 +80,9 @@ final class LatLonShapePolygonQuery extends ShapeQuery {
|
|||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle.cX);
|
||||
|
||||
switch (queryRelation) {
|
||||
case INTERSECTS: return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) != Relation.CELL_OUTSIDE_QUERY;
|
||||
case WITHIN: return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
||||
case DISJOINT: return poly2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_OUTSIDE_QUERY;
|
||||
case INTERSECTS: return component2D.relateTriangle(alon, alat, blon, blat, clon, clat) != Relation.CELL_OUTSIDE_QUERY;
|
||||
case WITHIN: return component2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_INSIDE_QUERY;
|
||||
case DISJOINT: return component2D.relateTriangle(alon, alat, blon, blat, clon, clat) == Relation.CELL_OUTSIDE_QUERY;
|
||||
default: throw new IllegalArgumentException("Unsupported query type :[" + queryRelation + "]");
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +98,7 @@ final class LatLonShapePolygonQuery extends ShapeQuery {
|
|||
double clat = GeoEncodingUtils.decodeLatitude(scratchTriangle.cY);
|
||||
double clon = GeoEncodingUtils.decodeLongitude(scratchTriangle.cX);
|
||||
|
||||
return poly2D.withinTriangle(alon, alat, scratchTriangle.ab, blon, blat, scratchTriangle.bc, clon, clat, scratchTriangle.ca);
|
||||
return component2D.withinTriangle(alon, alat, scratchTriangle.ab, blon, blat, scratchTriangle.bc, clon, clat, scratchTriangle.ca);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -119,19 +111,24 @@ final class LatLonShapePolygonQuery extends ShapeQuery {
|
|||
sb.append(this.field);
|
||||
sb.append(':');
|
||||
}
|
||||
sb.append("Polygon(").append(polygons[0].toGeoJSON()).append(')');
|
||||
sb.append("[");
|
||||
for (int i = 0; i < geometries.length; i++) {
|
||||
sb.append(geometries[i].toString());
|
||||
sb.append(',');
|
||||
}
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean equalsTo(Object o) {
|
||||
return super.equalsTo(o) && Arrays.equals(polygons, ((LatLonShapePolygonQuery)o).polygons);
|
||||
return super.equalsTo(o) && Arrays.equals(geometries, ((LatLonShapeQuery)o).geometries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = super.hashCode();
|
||||
hash = 31 * hash + Arrays.hashCode(polygons);
|
||||
hash = 31 * hash + Arrays.hashCode(geometries);
|
||||
return hash;
|
||||
}
|
||||
}
|
|
@ -79,7 +79,7 @@ abstract class ShapeQuery extends Query {
|
|||
/**
|
||||
* relates an internal node (bounding box of a range of triangles) to the target query
|
||||
* Note: logic is specific to query type
|
||||
* see {@link LatLonShapeBoundingBoxQuery#relateRangeToQuery} and {@link LatLonShapePolygonQuery#relateRangeToQuery}
|
||||
* see {@link LatLonShapeBoundingBoxQuery#relateRangeToQuery} and {@link LatLonShapeQuery#relateRangeToQuery}
|
||||
*/
|
||||
protected abstract Relation relateRangeBBoxToQuery(int minXOffset, int minYOffset, byte[] minTriangle,
|
||||
int maxXOffset, int maxYOffset, byte[] maxTriangle);
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.geo;
|
||||
|
||||
/**
|
||||
* Lat/Lon Geometry object.
|
||||
*/
|
||||
public abstract class LatLonGeometry {
|
||||
|
||||
/** get a {@link Component2D} from this geometry */
|
||||
protected abstract Component2D toComponent2D();
|
||||
|
||||
/** Creates a Component2D from the provided LatLonGeometry array */
|
||||
public static Component2D create(LatLonGeometry... latLonGeometries) {
|
||||
if (latLonGeometries == null) {
|
||||
throw new IllegalArgumentException("geometries must not be null");
|
||||
}
|
||||
if (latLonGeometries.length == 0) {
|
||||
throw new IllegalArgumentException("geometries must not be empty");
|
||||
}
|
||||
if (latLonGeometries.length == 1) {
|
||||
if (latLonGeometries[0] == null) {
|
||||
throw new IllegalArgumentException("geometries[0] must not be null");
|
||||
}
|
||||
return latLonGeometries[0].toComponent2D();
|
||||
}
|
||||
Component2D[] components = new Component2D[latLonGeometries.length];
|
||||
for (int i = 0; i < latLonGeometries.length; i++) {
|
||||
if (latLonGeometries[i] == null) {
|
||||
throw new IllegalArgumentException("geometries[" + i + "] must not be null");
|
||||
}
|
||||
components[i] = latLonGeometries[i].toComponent2D();
|
||||
}
|
||||
return ComponentTree.create(components);
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ import java.util.Arrays;
|
|||
* <li>For more advanced GeoSpatial indexing and query operations see the {@code spatial-extras} module
|
||||
* </ol>
|
||||
*/
|
||||
public class Line {
|
||||
public class Line extends LatLonGeometry {
|
||||
/** array of latitude coordinates */
|
||||
private final double[] lats;
|
||||
/** array of longitude coordinates */
|
||||
|
@ -107,6 +107,11 @@ public class Line {
|
|||
return lons.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component2D toComponent2D() {
|
||||
return Line2D.create(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
@ -137,7 +142,7 @@ public class Line {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
/** prints polygons as geojson */
|
||||
/** prints lines as geojson */
|
||||
public String toGeoJSON() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("[");
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.lucene.geo;
|
||||
|
||||
/**
|
||||
* Represents a point on the earth's surface. You can construct the point directly with {@code double}
|
||||
* coordinates.
|
||||
* <p>
|
||||
* NOTES:
|
||||
* <ol>
|
||||
* <li>latitude/longitude values must be in decimal degrees.
|
||||
* <li>For more advanced GeoSpatial indexing and query operations see the {@code spatial-extras} module
|
||||
* </ol>
|
||||
*/
|
||||
public final class Point extends LatLonGeometry {
|
||||
|
||||
/** latitude coordinate */
|
||||
private final double lat;
|
||||
/** longitude coordinate */
|
||||
private final double lon;
|
||||
|
||||
/**
|
||||
* Creates a new Point from the supplied latitude/longitude.
|
||||
*/
|
||||
public Point(double lat, double lon) {
|
||||
GeoUtils.checkLatitude(lat);
|
||||
GeoUtils.checkLongitude(lon);
|
||||
this.lat = lat;
|
||||
this.lon = lon;
|
||||
}
|
||||
|
||||
/** Returns latitude value at given index */
|
||||
public double getLat() {
|
||||
return lat;
|
||||
}
|
||||
|
||||
/** Returns longitude value at given index */
|
||||
public double getLon() {
|
||||
return lon;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component2D toComponent2D() {
|
||||
double qLat = GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(lat));
|
||||
double qLon = GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(lon));
|
||||
return Point2D.create(new double[] {qLat, qLon});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof Point)) return false;
|
||||
Point point = (Point) o;
|
||||
return point.lat == lat && point.lon == lon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Double.hashCode(lat);
|
||||
result = 31 * result + Double.hashCode(lon);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Point(");
|
||||
sb.append(lon);
|
||||
sb.append(",");
|
||||
sb.append(lat);
|
||||
sb.append(')');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ import org.apache.lucene.geo.GeoUtils.WindingOrder;
|
|||
* </ol>
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public final class Polygon {
|
||||
public final class Polygon extends LatLonGeometry {
|
||||
private final double[] polyLats;
|
||||
private final double[] polyLons;
|
||||
private final Polygon[] holes;
|
||||
|
@ -163,6 +163,11 @@ public final class Polygon {
|
|||
return holes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Component2D toComponent2D() {
|
||||
return Polygon2D.create(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.geo;
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
|
||||
public class TestPoint extends LuceneTestCase {
|
||||
|
||||
public void testInvalidLat() {
|
||||
IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new Point(134.14, 45.23);
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("invalid latitude 134.14; must be between -90.0 and 90.0"));
|
||||
}
|
||||
|
||||
public void testInvalidLon() {
|
||||
IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> {
|
||||
new Point(43.5, 180.5);
|
||||
});
|
||||
assertTrue(expected.getMessage().contains("invalid longitude 180.5; must be between -180.0 and 180.0"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue