LUCENE-6997: refactor sandboxed GeoPointField and query classes to lucene-spatial module

This commit is contained in:
nknize 2016-02-05 10:58:39 -06:00
parent 4569fd732a
commit 665041c52f
30 changed files with 412 additions and 183 deletions

View File

@ -23,6 +23,7 @@
<orderEntry type="library" scope="TEST" name="JUnit" level="project" />
<orderEntry type="module" scope="TEST" module-name="lucene-test-framework" />
<orderEntry type="module" scope="TEST" module-name="codecs" />
<orderEntry type="module" module-name="spatial" />
<orderEntry type="module" module-name="lucene-core" />
</component>
</module>

View File

@ -180,15 +180,15 @@ public class SloppyMath {
}
// haversin
static final double TO_RADIANS = Math.PI / 180D;
static final double TO_DEGREES = 180D / Math.PI;
public static final double TO_RADIANS = Math.PI / 180D;
public static final double TO_DEGREES = 180D / Math.PI;
// cos/asin
private static final double ONE_DIV_F2 = 1/2.0;
private static final double ONE_DIV_F3 = 1/6.0;
private static final double ONE_DIV_F4 = 1/24.0;
static final double PIO2 = Math.PI / 2D;
public static final double PIO2 = Math.PI / 2D;
private static final double PIO2_HI = Double.longBitsToDouble(0x3FF921FB54400000L); // 1.57079632673412561417e+00 first 33 bits of pi/2
private static final double PIO2_LO = Double.longBitsToDouble(0x3DD0B4611A626331L); // 6.07710050650619224932e-11 pi/2 - PIO2_HI
private static final double TWOPI_HI = 4*PIO2_HI;

View File

@ -23,4 +23,29 @@
<import file="../module-build.xml"/>
<target name="compile-test-spatial" depends="init" if="module.has.tests">
<ant dir="${common.dir}/spatial" target="compile-test" inheritAll="false"/>
</target>
<path id="classpath">
<path refid="base.classpath"/>
<pathelement path="${spatial.jar}"/>
</path>
<target name="compile-core" depends="jar-spatial,common.compile-core" />
<path id="test.classpath">
<pathelement location="${build.dir}/classes/java"/>
<pathelement location="${build.dir}/classes/test"/>
<pathelement location="${common.dir}/build/spatial/classes/test"/>
<path refid="test.base.classpath"/>
<pathelement path="${spatial.jar}"/>
<path refid="junit-path"/>
</path>
<path id="junit.classpath">
<path refid="test.classpath"/>
<pathelement path="${java.class.path}"/>
</path>
<target name="compile-test" depends="jar-spatial,compile-test-spatial,common.compile-test" />
</project>

View File

@ -17,8 +17,8 @@
package org.apache.lucene.document;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.util.NumericUtils;
import org.apache.lucene.spatial.util.GeoUtils;
/** Add this to a document to index lat/lon point dimensionally */
public class LatLonPoint extends Field {

View File

@ -26,9 +26,9 @@ import org.apache.lucene.index.PointValues;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.util.DocIdSetBuilder;
import org.apache.lucene.util.GeoRelationUtils;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.util.NumericUtils;
import org.apache.lucene.spatial.util.GeoRelationUtils;
import org.apache.lucene.spatial.util.GeoUtils;
/** Finds all previously indexed points that fall within the specified polygon.
*

View File

@ -26,8 +26,8 @@ import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.util.DocIdSetBuilder;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.util.NumericUtils;
import org.apache.lucene.spatial.util.GeoUtils;
/** Finds all previously indexed points that fall within the specified boundings box.
*

View File

@ -1,28 +0,0 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<!--
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.
-->
<!-- not a package-info.java, because we already defined this package in core/ -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
This package contains utility APIs, currently for geospatial searching.
</body>
</html>

View File

@ -18,9 +18,9 @@ package org.apache.lucene.search;
import org.apache.lucene.document.LatLonPoint;
import org.apache.lucene.document.Document;
import org.apache.lucene.util.BaseGeoPointTestCase;
import org.apache.lucene.util.GeoDistanceUtils;
import org.apache.lucene.util.GeoRect;
import org.apache.lucene.spatial.util.BaseGeoPointTestCase;
import org.apache.lucene.spatial.util.GeoDistanceUtils;
import org.apache.lucene.spatial.util.GeoRect;
public class TestLatLonPointQueries extends BaseGeoPointTestCase {
// todo deconflict GeoPoint and BKD encoding methods and error tolerance

View File

@ -14,11 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.document;
package org.apache.lucene.spatial.document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.spatial.util.GeoUtils;
/**
* <p>
@ -32,8 +34,8 @@ import org.apache.lucene.util.GeoUtils;
* </pre>
*
* <p>To perform simple geospatial queries against a <code>GeoPointField</code>,
* see {@link org.apache.lucene.search.GeoPointInBBoxQuery}, {@link org.apache.lucene.search.GeoPointInPolygonQuery},
* or {@link org.apache.lucene.search.GeoPointDistanceQuery}
* see {@link org.apache.lucene.spatial.search.GeoPointInBBoxQuery}, {@link org.apache.lucene.spatial.search.GeoPointInPolygonQuery},
* or {@link org.apache.lucene.spatial.search.GeoPointDistanceQuery}
*
* NOTE: This indexes only high precision encoded terms which may result in visiting a high number
* of terms for large queries. See LUCENE-6481 for a future improvement.
@ -41,6 +43,7 @@ import org.apache.lucene.util.GeoUtils;
* @lucene.experimental
*/
public final class GeoPointField extends Field {
/** encoding step value for GeoPoint prefix terms */
public static final int PRECISION_STEP = 9;
/**
@ -109,10 +112,12 @@ public final class GeoPointField extends Field {
fieldsData = GeoUtils.mortonHash(lon, lat);
}
/** access longitude value */
public double getLon() {
return GeoUtils.mortonUnhashLon((long) fieldsData);
}
/** access latitude value */
public double getLat() {
return GeoUtils.mortonUnhashLat((long) fieldsData);
}

View File

@ -0,0 +1,21 @@
/*
* 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.
*/
/**
* Geospatial Field Implementations for Core Lucene
*/
package org.apache.lucene.spatial.document;

View File

@ -14,17 +14,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
package org.apache.lucene.spatial.search;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.spatial.util.GeoUtils;
/** NOTE: package private; just used so {@link GeoPointInPolygonQuery} can communicate its bounding box to {@link GeoPointInBBoxQuery}. */
class GeoBoundingBox {
/** minimum longitude value (in degrees) */
public final double minLon;
/** minimum latitude value (in degrees) */
public final double maxLon;
/** maximum longitude value (in degrees) */
public final double minLat;
/** maximum latitude value (in degrees) */
public final double maxLat;
/**
* Constructs a bounding box by first validating the provided latitude and longitude coordinates
*/
public GeoBoundingBox(double minLon, double maxLon, double minLat, double maxLat) {
if (GeoUtils.isValidLon(minLon) == false) {
throw new IllegalArgumentException("invalid minLon " + minLon);

View File

@ -14,22 +14,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
package org.apache.lucene.spatial.search;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.util.GeoDistanceUtils;
import org.apache.lucene.util.GeoRect;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.util.GeoDistanceUtils;
import org.apache.lucene.spatial.util.GeoRect;
import org.apache.lucene.spatial.util.GeoUtils;
/** Implements a simple point distance query on a GeoPoint field. This is based on
* {@link org.apache.lucene.search.GeoPointInBBoxQuery} and is implemented using a two phase approach. First,
* {@link GeoPointInBBoxQuery} and is implemented using a two phase approach. First,
* like {@code GeoPointInBBoxQueryImpl} candidate terms are queried using the numeric ranges based on
* the morton codes of the min and max lat/lon pairs that intersect the boundary of the point-radius
* circle. Terms
* passing this initial filter are then passed to a secondary {@code postFilter} method that verifies whether the
* decoded lat/lon point fall within the specified query distance (see {@link org.apache.lucene.util.SloppyMath#haversin}.
* All morton value comparisons are subject to the same precision tolerance defined in
* {@value org.apache.lucene.util.GeoUtils#TOLERANCE} and distance comparisons are subject to the accuracy of the
* {@value org.apache.lucene.spatial.util.GeoUtils#TOLERANCE} and distance comparisons are subject to the accuracy of the
* haversine formula (from R.W. Sinnott, "Virtues of the Haversine", Sky and Telescope, vol. 68, no. 2, 1984, p. 159)
*
* <p>Note: This query currently uses haversine which is a sloppy distance calculation (see above reference). For large
@ -38,11 +41,17 @@ import org.apache.lucene.util.GeoUtils;
*
* @lucene.experimental */
public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
/** longitude value (in degrees) for query location */
protected final double centerLon;
/** latitude value (in degrees) for query location */
protected final double centerLat;
/** distance (in meters) from lon, lat center location */
protected final double radiusMeters;
/** NOTE: radius is in meters. */
/**
* Constructs a Query for all {@link org.apache.lucene.spatial.document.GeoPointField} types within a
* distance (in meters) from a given point
**/
public GeoPointDistanceQuery(final String field, final double centerLon, final double centerLat, final double radiusMeters) {
this(field, GeoUtils.circleToBBox(centerLon, centerLat, radiusMeters), centerLon, centerLat, radiusMeters);
}
@ -156,14 +165,17 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
.toString();
}
/** getter method for center longitude value */
public double getCenterLon() {
return this.centerLon;
}
/** getter method for center latitude value */
public double getCenterLat() {
return this.centerLat;
}
/** getter method for distance value (in meters) */
public double getRadiusMeters() {
return this.radiusMeters;
}

View File

@ -14,17 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
package org.apache.lucene.spatial.search;
import java.io.IOException;
import org.apache.lucene.document.GeoPointField;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.util.AttributeSource;
import org.apache.lucene.util.GeoRect;
import org.apache.lucene.util.GeoRelationUtils;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.spatial.document.GeoPointField;
import org.apache.lucene.spatial.util.GeoRect;
import org.apache.lucene.spatial.util.GeoRelationUtils;
import org.apache.lucene.util.SloppyMath;
/** Package private implementation for the public facing GeoPointDistanceQuery delegate class.
@ -48,7 +48,7 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
}
@Override
public void setRewriteMethod(RewriteMethod method) {
public void setRewriteMethod(MultiTermQuery.RewriteMethod method) {
throw new UnsupportedOperationException("cannot change rewrite method");
}
@ -94,7 +94,7 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
}
/**
* The two-phase query approach. The parent {@link org.apache.lucene.search.GeoPointTermsEnum} class matches
* The two-phase query approach. The parent {@link GeoPointTermsEnum} class matches
* encoded terms that fall within the minimum bounding box of the point-radius circle. Those documents that pass
* the initial bounding box filter are then post filter compared to the provided distance using the
* {@link org.apache.lucene.util.SloppyMath#haversin} method.

View File

@ -14,13 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
package org.apache.lucene.spatial.search;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.util.GeoProjectionUtils;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
/** Implements a point distance range query on a GeoPoint field. This is based on
* {@code org.apache.lucene.search.GeoPointDistanceQuery} and is implemented using a
* {@code org.apache.lucene.spatial.search.GeoPointDistanceQuery} and is implemented using a
* {@code org.apache.lucene.search.BooleanClause.MUST_NOT} clause to exclude any points that fall within
* minRadiusMeters from the provided point.
*
@ -29,6 +31,10 @@ import org.apache.lucene.util.GeoProjectionUtils;
public final class GeoPointDistanceRangeQuery extends GeoPointDistanceQuery {
protected final double minRadiusMeters;
/**
* Constructs a query for all {@link org.apache.lucene.spatial.document.GeoPointField} types within a minimum / maximum
* distance (in meters) range from a given point
*/
public GeoPointDistanceRangeQuery(final String field, final double centerLon, final double centerLat,
final double minRadiusMeters, final double maxRadius) {
super(field, centerLon, centerLat, maxRadius);
@ -92,10 +98,12 @@ public final class GeoPointDistanceRangeQuery extends GeoPointDistanceQuery {
.toString();
}
/** getter method for minimum distance */
public double getMinRadiusMeters() {
return this.minRadiusMeters;
}
/** getter method for maximum distance */
public double getMaxRadiusMeters() {
return this.radiusMeters;
}

View File

@ -14,10 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
package org.apache.lucene.spatial.search;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.FieldValueQuery;
import org.apache.lucene.search.LegacyNumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.util.GeoUtils;
/** Implements a simple bounding box query on a GeoPoint field. This is inspired by
* {@link LegacyNumericRangeQuery} and is implemented using a
@ -26,7 +31,7 @@ import org.apache.lucene.util.GeoUtils;
* passing this initial filter are passed to a final check that verifies whether
* the decoded lat/lon falls within (or on the boundary) of the query bounding box.
* The value comparisons are subject to a precision tolerance defined in
* {@value org.apache.lucene.util.GeoUtils#TOLERANCE}
* {@value org.apache.lucene.spatial.util.GeoUtils#TOLERANCE}
*
* NOTES:
* 1. All latitude/longitude values must be in decimal degrees.
@ -45,6 +50,10 @@ public class GeoPointInBBoxQuery extends Query {
protected final double maxLon;
protected final double maxLat;
/**
* Constructs a query for all {@link org.apache.lucene.spatial.document.GeoPointField} types that fall within a
* defined bounding box
*/
public GeoPointInBBoxQuery(final String field, final double minLon, final double minLat, final double maxLon, final double maxLat) {
this.field = field;
this.minLon = minLon;
@ -130,22 +139,27 @@ public class GeoPointInBBoxQuery extends Query {
return result;
}
/** getter method for retrieving the field name */
public final String getField() {
return this.field;
}
/** getter method for retrieving the minimum longitude (in degrees) */
public final double getMinLon() {
return this.minLon;
}
/** getter method for retrieving the minimum latitude (in degrees) */
public final double getMinLat() {
return this.minLat;
}
/** getter method for retrieving the maximum longitude (in degrees) */
public final double getMaxLon() {
return this.maxLon;
}
/** getter method for retrieving the maximum latitude (in degrees) */
public final double getMaxLat() {
return this.maxLat;
}

View File

@ -14,16 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
package org.apache.lucene.spatial.search;
import java.io.IOException;
import org.apache.lucene.document.GeoPointField;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.util.AttributeSource;
import org.apache.lucene.util.GeoRelationUtils;
import org.apache.lucene.util.SloppyMath;
import org.apache.lucene.spatial.document.GeoPointField;
import org.apache.lucene.spatial.util.GeoRelationUtils;
/** Package private implementation for the public facing GeoPointInBBoxQuery delegate class.
*
@ -49,7 +50,7 @@ class GeoPointInBBoxQueryImpl extends GeoPointTermQuery {
}
@Override
public void setRewriteMethod(RewriteMethod method) {
public void setRewriteMethod(MultiTermQuery.RewriteMethod method) {
throw new UnsupportedOperationException("cannot change rewrite method");
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
package org.apache.lucene.spatial.search;
import java.io.IOException;
import java.util.Arrays;
@ -22,9 +22,9 @@ import java.util.Arrays;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.util.AttributeSource;
import org.apache.lucene.util.GeoRect;
import org.apache.lucene.util.GeoRelationUtils;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.spatial.util.GeoRect;
import org.apache.lucene.spatial.util.GeoRelationUtils;
import org.apache.lucene.spatial.util.GeoUtils;
/** Implements a simple point in polygon query on a GeoPoint field. This is based on
* {@code GeoPointInBBoxQueryImpl} and is implemented using a
@ -34,7 +34,7 @@ import org.apache.lucene.util.GeoUtils;
* to a secondary filter that verifies whether the decoded lat/lon point falls within
* (or on the boundary) of the bounding box query. Finally, the remaining candidate
* term is passed to the final point in polygon check. All value comparisons are subject
* to the same precision tolerance defined in {@value org.apache.lucene.util.GeoUtils#TOLERANCE}
* to the same precision tolerance defined in {@value org.apache.lucene.spatial.util.GeoUtils#TOLERANCE}
*
* <p>NOTES:
* 1. The polygon coordinates need to be in either clockwise or counter-clockwise order.
@ -52,7 +52,7 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQueryImpl {
private final double[] y;
/**
* Constructs a new GeoPolygonQuery that will match encoded {@link org.apache.lucene.document.GeoPointField} terms
* Constructs a new GeoPolygonQuery that will match encoded {@link org.apache.lucene.spatial.document.GeoPointField} terms
* that fall within or on the boundary of the polygon defined by the input parameters.
*/
public GeoPointInPolygonQuery(final String field, final double[] polyLons, final double[] polyLats) {
@ -84,6 +84,7 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQueryImpl {
return new GeoPolygonTermsEnum(terms.iterator(), this.minLon, this.minLat, this.maxLon, this.maxLat);
}
/** throw exception if trying to change rewrite method */
@Override
public void setRewriteMethod(RewriteMethod method) {
throw new UnsupportedOperationException("cannot change rewrite method");
@ -111,6 +112,7 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQueryImpl {
return result;
}
/** print out this polygon query */
@Override
public String toString(String field) {
assert x.length == y.length;
@ -165,10 +167,10 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQueryImpl {
/**
* The two-phase query approach. The parent
* {@link org.apache.lucene.search.GeoPointTermsEnum#accept} method is called to match
* {@link GeoPointTermsEnum#accept} method is called to match
* encoded terms that fall within the bounding box of the polygon. Those documents that pass the initial
* bounding box filter are then compared to the provided polygon using the
* {@link org.apache.lucene.util.GeoRelationUtils#pointInPolygon} method.
* {@link org.apache.lucene.spatial.util.GeoRelationUtils#pointInPolygon} method.
*/
@Override
protected boolean postFilter(final double lon, final double lat) {

View File

@ -14,10 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
package org.apache.lucene.spatial.search;
import java.io.IOException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.util.GeoUtils;
import org.apache.lucene.util.AttributeSource;
/**
* TermQuery for GeoPointField for overriding {@link org.apache.lucene.search.MultiTermQuery} methods specific to
@ -27,9 +34,13 @@ import org.apache.lucene.util.GeoUtils;
*/
abstract class GeoPointTermQuery extends MultiTermQuery {
// simple bounding box optimization - no objects used to avoid dependencies
/** minimum longitude value (in degrees) */
protected final double minLon;
/** minimum latitude value (in degrees) */
protected final double minLat;
/** maximum longitude value (in degrees) */
protected final double maxLon;
/** maximum latitude value (in degrees) */
protected final double maxLat;
/**
@ -59,12 +70,45 @@ abstract class GeoPointTermQuery extends MultiTermQuery {
this.rewriteMethod = GEO_CONSTANT_SCORE_REWRITE;
}
public static final RewriteMethod GEO_CONSTANT_SCORE_REWRITE = new RewriteMethod() {
private static final RewriteMethod GEO_CONSTANT_SCORE_REWRITE = new RewriteMethod() {
@Override
public Query rewrite(IndexReader reader, MultiTermQuery query) {
return new GeoPointTermQueryConstantScoreWrapper<>((GeoPointTermQuery)query);
}
};
/** override package protected method */
@Override
protected abstract TermsEnum getTermsEnum(final Terms terms, AttributeSource atts) throws IOException;
/** check if this instance equals another instance */
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
GeoPointTermQuery that = (GeoPointTermQuery) o;
if (Double.compare(that.minLon, minLon) != 0) return false;
if (Double.compare(that.minLat, minLat) != 0) return false;
if (Double.compare(that.maxLon, maxLon) != 0) return false;
return Double.compare(that.maxLat, maxLat) == 0;
}
/** compute hashcode */
@Override
public int hashCode() {
int result = super.hashCode();
long temp;
temp = Double.doubleToLongBits(minLon);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(minLat);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(maxLon);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(maxLat);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
package org.apache.lucene.spatial.search;
import java.io.IOException;
@ -23,8 +23,17 @@ import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.index.SortedNumericDocValues;
import org.apache.lucene.index.Terms;
import org.apache.lucene.search.BulkScorer;
import org.apache.lucene.search.ConstantScoreScorer;
import org.apache.lucene.search.ConstantScoreWeight;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Weight;
import org.apache.lucene.util.DocIdSetBuilder;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.spatial.util.GeoUtils;
/**
* Custom ConstantScoreWrapper for {@code GeoPointTermQuery} that cuts over to DocValues
@ -63,18 +72,18 @@ final class GeoPointTermQueryConstantScoreWrapper <Q extends GeoPointTermQuery>
return new ConstantScoreWeight(this) {
private DocIdSet getDocIDs(LeafReaderContext context) throws IOException {
final Terms terms = context.reader().terms(query.field);
final Terms terms = context.reader().terms(query.getField());
if (terms == null) {
return DocIdSet.EMPTY;
}
final GeoPointTermsEnum termsEnum = (GeoPointTermsEnum)(query.getTermsEnum(terms));
final GeoPointTermsEnum termsEnum = (GeoPointTermsEnum)(query.getTermsEnum(terms, null));
assert termsEnum != null;
LeafReader reader = context.reader();
DocIdSetBuilder builder = new DocIdSetBuilder(reader.maxDoc());
PostingsEnum docs = null;
SortedNumericDocValues sdv = reader.getSortedNumericDocValues(query.field);
SortedNumericDocValues sdv = reader.getSortedNumericDocValues(query.getField());
while (termsEnum.next() != null) {
docs = termsEnum.postings(docs, PostingsEnum.NONE);

View File

@ -14,19 +14,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
package org.apache.lucene.spatial.search;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.apache.lucene.document.GeoPointField;
import org.apache.lucene.spatial.document.GeoPointField;
import org.apache.lucene.index.FilteredTermsEnum;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.GeoRelationUtils;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.spatial.util.GeoRelationUtils;
import org.apache.lucene.spatial.util.GeoUtils;
import org.apache.lucene.util.LegacyNumericUtils;
/**

View File

@ -0,0 +1,21 @@
/*
* 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.
*/
/**
* Geospatial Query Implementations for Core Lucene
*/
package org.apache.lucene.spatial.search;

View File

@ -14,7 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.util;
package org.apache.lucene.spatial.util;
import org.apache.lucene.util.SloppyMath;
import static org.apache.lucene.util.SloppyMath.TO_RADIANS;
@ -27,6 +29,10 @@ public class GeoDistanceUtils {
/** error threshold for point-distance queries (in percent) NOTE: Guideline from USGS is 0.005 **/
public static final double DISTANCE_PCT_ERR = 0.005;
// No instance:
private GeoDistanceUtils() {
}
/**
* Compute the great-circle distance using original haversine implementation published by Sinnot in:
* R.W. Sinnott, "Virtues of the Haversine", Sky and Telescope, vol. 68, no. 2, 1984, p. 159

View File

@ -14,11 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.util;
package org.apache.lucene.spatial.util;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.lucene.util.BitUtil;
/**
* Utilities for converting to/from the GeoHash standard
*
@ -30,15 +32,20 @@ import java.util.Collection;
* @lucene.experimental
*/
public class GeoHashUtils {
public static final char[] BASE_32 = {'0', '1', '2', '3', '4', '5', '6',
private static final char[] BASE_32 = {'0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
public static final String BASE_32_STRING = new String(BASE_32);
private static final String BASE_32_STRING = new String(BASE_32);
/** maximum precision for geohash strings */
public static final int PRECISION = 12;
private static final short MORTON_OFFSET = (GeoUtils.BITS<<1) - (PRECISION*5);
// No instance:
private GeoHashUtils() {
}
/**
* Encode lon/lat to the geohash based long format (lon/lat interleaved, 4 least significant bits = level)
*/
@ -75,6 +82,9 @@ public class GeoHashUtils {
return ((geohash >>> 4) << (((level - precision) * 5) + 4) | level);
}
/**
* Convert from a morton encoded long from a geohash encoded long
*/
public static long fromMorton(long morton, int level) {
long mFlipped = BitUtil.flipFlop(morton);
mFlipped >>>= (((GeoHashUtils.PRECISION - level) * 5) + MORTON_OFFSET);

View File

@ -14,32 +14,60 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.util;
package org.apache.lucene.spatial.util;
import static java.lang.StrictMath.sqrt;
import static org.apache.lucene.util.SloppyMath.asin;
import static org.apache.lucene.util.SloppyMath.cos;
import static org.apache.lucene.util.SloppyMath.sin;
import static org.apache.lucene.util.SloppyMath.tan;
import static org.apache.lucene.util.SloppyMath.PIO2;
import static org.apache.lucene.util.SloppyMath.TO_DEGREES;
import static org.apache.lucene.util.SloppyMath.TO_RADIANS;
import static org.apache.lucene.spatial.util.GeoUtils.MAX_LAT_INCL;
import static org.apache.lucene.spatial.util.GeoUtils.MAX_LON_INCL;
import static org.apache.lucene.spatial.util.GeoUtils.MIN_LAT_INCL;
import static org.apache.lucene.spatial.util.GeoUtils.MIN_LON_INCL;
import static org.apache.lucene.spatial.util.GeoUtils.normalizeLat;
import static org.apache.lucene.spatial.util.GeoUtils.normalizeLon;
/**
* Reusable geo-spatial projection utility methods.
*
* @lucene.experimental
*/
public class GeoProjectionUtils {
// WGS84 earth-ellipsoid major (a) minor (b) radius, (f) flattening and eccentricity (e)
// WGS84 earth-ellipsoid parameters
/** major (a) axis in meters */
public static final double SEMIMAJOR_AXIS = 6_378_137; // [m]
/** earth flattening factor (f) */
public static final double FLATTENING = 1.0/298.257223563;
/** minor (b) axis in meters */
public static final double SEMIMINOR_AXIS = SEMIMAJOR_AXIS * (1.0 - FLATTENING); //6_356_752.31420; // [m]
public static final double ECCENTRICITY = StrictMath.sqrt((2.0 - FLATTENING) * FLATTENING);
static final double SEMIMAJOR_AXIS2 = SEMIMAJOR_AXIS * SEMIMAJOR_AXIS;
static final double SEMIMINOR_AXIS2 = SEMIMINOR_AXIS * SEMIMINOR_AXIS;
public static final double MIN_LON_RADIANS = TO_RADIANS * GeoUtils.MIN_LON_INCL;
public static final double MIN_LAT_RADIANS = TO_RADIANS * GeoUtils.MIN_LAT_INCL;
public static final double MAX_LON_RADIANS = TO_RADIANS * GeoUtils.MAX_LON_INCL;
public static final double MAX_LAT_RADIANS = TO_RADIANS * GeoUtils.MAX_LAT_INCL;
/** first eccentricity (e) */
public static final double ECCENTRICITY = sqrt((2.0 - FLATTENING) * FLATTENING);
/** major axis squared (a2) */
public static final double SEMIMAJOR_AXIS2 = SEMIMAJOR_AXIS * SEMIMAJOR_AXIS;
/** minor axis squared (b2) */
public static final double SEMIMINOR_AXIS2 = SEMIMINOR_AXIS * SEMIMINOR_AXIS;
private static final double E2 = (SEMIMAJOR_AXIS2 - SEMIMINOR_AXIS2)/(SEMIMAJOR_AXIS2);
private static final double EP2 = (SEMIMAJOR_AXIS2 - SEMIMINOR_AXIS2)/(SEMIMINOR_AXIS2);
/** min longitude value in radians */
public static final double MIN_LON_RADIANS = TO_RADIANS * MIN_LON_INCL;
/** min latitude value in radians */
public static final double MIN_LAT_RADIANS = TO_RADIANS * MIN_LAT_INCL;
/** max longitude value in radians */
public static final double MAX_LON_RADIANS = TO_RADIANS * MAX_LON_INCL;
/** max latitude value in radians */
public static final double MAX_LAT_RADIANS = TO_RADIANS * MAX_LAT_INCL;
// No instance:
private GeoProjectionUtils() {
}
/**
* Converts from geocentric earth-centered earth-fixed to geodesic lat/lon/alt
* @param x Cartesian x coordinate
@ -121,9 +149,9 @@ public class GeoProjectionUtils {
lon = TO_RADIANS * lon;
lat = TO_RADIANS * lat;
final double sl = SloppyMath.sin(lat);
final double sl = sin(lat);
final double s2 = sl*sl;
final double cl = SloppyMath.cos(lat);
final double cl = cos(lat);
if (ecf == null) {
ecf = new double[3];
@ -141,8 +169,8 @@ public class GeoProjectionUtils {
}
final double rn = SEMIMAJOR_AXIS / StrictMath.sqrt(1.0D - E2 * s2);
ecf[0] = (rn+alt) * cl * SloppyMath.cos(lon);
ecf[1] = (rn+alt) * cl * SloppyMath.sin(lon);
ecf[0] = (rn+alt) * cl * cos(lon);
ecf[1] = (rn+alt) * cl * sin(lon);
ecf[2] = ((rn*(1.0-E2))+alt)*sl;
return ecf;
@ -276,10 +304,10 @@ public class GeoProjectionUtils {
originLon = TO_RADIANS * originLon;
originLat = TO_RADIANS * originLat;
final double sLon = SloppyMath.sin(originLon);
final double cLon = SloppyMath.cos(originLon);
final double sLat = SloppyMath.sin(originLat);
final double cLat = SloppyMath.cos(originLat);
final double sLon = sin(originLon);
final double cLon = cos(originLon);
final double sLat = sin(originLat);
final double cLat = cos(originLat);
phiMatrix[0][0] = -sLon;
phiMatrix[0][1] = cLon;
@ -310,10 +338,10 @@ public class GeoProjectionUtils {
originLon = TO_RADIANS * originLon;
originLat = TO_RADIANS * originLat;
final double sLat = SloppyMath.sin(originLat);
final double cLat = SloppyMath.cos(originLat);
final double sLon = SloppyMath.sin(originLon);
final double cLon = SloppyMath.cos(originLon);
final double sLat = sin(originLat);
final double cLat = cos(originLat);
final double sLon = sin(originLon);
final double cLon = cos(originLon);
phiMatrix[0][0] = -sLon;
phiMatrix[1][0] = cLon;
@ -345,9 +373,9 @@ public class GeoProjectionUtils {
}
final double alpha1 = TO_RADIANS * bearing;
final double cosA1 = SloppyMath.cos(alpha1);
final double sinA1 = SloppyMath.sin(alpha1);
final double tanU1 = (1-FLATTENING) * SloppyMath.tan(TO_RADIANS * lat);
final double cosA1 = cos(alpha1);
final double sinA1 = sin(alpha1);
final double tanU1 = (1-FLATTENING) * tan(TO_RADIANS * lat);
final double cosU1 = 1 / StrictMath.sqrt((1+tanU1*tanU1));
final double sinU1 = tanU1*cosU1;
final double sig1 = StrictMath.atan2(tanU1, cosA1);
@ -362,9 +390,9 @@ public class GeoProjectionUtils {
double sinSigma, cosSigma, cos2SigmaM, deltaSigma;
do {
cos2SigmaM = SloppyMath.cos(2*sig1 + sigma);
sinSigma = SloppyMath.sin(sigma);
cosSigma = SloppyMath.cos(sigma);
cos2SigmaM = cos(2*sig1 + sigma);
sinSigma = sin(sigma);
cosSigma = cos(sigma);
deltaSigma = B * sinSigma * (cos2SigmaM + (B/4D) * (cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
(B/6) * cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
@ -380,8 +408,8 @@ public class GeoProjectionUtils {
final double lam = lambda - (1-c) * FLATTENING * sinAlpha *
(sigma + c * sinSigma * (cos2SigmaM + c * cosSigma * (-1 + 2* cos2SigmaM*cos2SigmaM)));
pt[0] = GeoUtils.normalizeLon(lon + TO_DEGREES * lam);
pt[1] = GeoUtils.normalizeLat(TO_DEGREES * lat2);
pt[0] = normalizeLon(lon + TO_DEGREES * lam);
pt[1] = normalizeLat(TO_DEGREES * lat2);
return pt;
}
@ -406,13 +434,13 @@ public class GeoProjectionUtils {
lat *= TO_RADIANS;
bearing *= TO_RADIANS;
final double cLat = SloppyMath.cos(lat);
final double sLat = SloppyMath.sin(lat);
final double sinDoR = SloppyMath.sin(dist / GeoProjectionUtils.SEMIMAJOR_AXIS);
final double cosDoR = SloppyMath.cos(dist / GeoProjectionUtils.SEMIMAJOR_AXIS);
final double cLat = cos(lat);
final double sLat = sin(lat);
final double sinDoR = sin(dist / GeoProjectionUtils.SEMIMAJOR_AXIS);
final double cosDoR = cos(dist / GeoProjectionUtils.SEMIMAJOR_AXIS);
pt[1] = SloppyMath.asin(sLat*cosDoR + cLat * sinDoR * SloppyMath.cos(bearing));
pt[0] = TO_DEGREES * (lon + Math.atan2(SloppyMath.sin(bearing) * sinDoR * cLat, cosDoR - sLat * SloppyMath.sin(pt[1])));
pt[1] = asin(sLat*cosDoR + cLat * sinDoR * cos(bearing));
pt[0] = TO_DEGREES * (lon + Math.atan2(sin(bearing) * sinDoR * cLat, cosDoR - sLat * sin(pt[1])));
pt[1] *= TO_DEGREES;
return pt;
@ -430,8 +458,8 @@ public class GeoProjectionUtils {
double dLon = (lon2 - lon1) * TO_RADIANS;
lat2 *= TO_RADIANS;
lat1 *= TO_RADIANS;
double y = SloppyMath.sin(dLon) * SloppyMath.cos(lat2);
double x = SloppyMath.cos(lat1) * SloppyMath.sin(lat2) - SloppyMath.sin(lat1) * SloppyMath.cos(lat2) * SloppyMath.cos(dLon);
double y = sin(dLon) * cos(lat2);
double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon);
return Math.atan2(y, x) * TO_DEGREES;
}
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.util;
package org.apache.lucene.spatial.util;
/** Represents a lat/lon rectangle. */
public class GeoRect {

View File

@ -14,13 +14,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.util;
package org.apache.lucene.spatial.util;
import org.apache.lucene.util.SloppyMath;
/**
* Reusable geo-relation utility methods
*/
public class GeoRelationUtils {
// No instance:
private GeoRelationUtils() {
}
/**
* Determine if a bbox (defined by minLon, minLat, maxLon, maxLat) contains the provided point (defined by lon, lat)
* NOTE: this is a basic method that does not handle dateline or pole crossing. Unwrapping must be done before
@ -62,6 +68,9 @@ public class GeoRelationUtils {
// Rectangle relations
/////////////////////////
/**
* Computes whether two rectangles are disjoint
*/
public static boolean rectDisjoint(final double aMinX, final double aMinY, final double aMaxX, final double aMaxY,
final double bMinX, final double bMinY, final double bMaxX, final double bMaxY) {
return (aMaxX < bMinX || aMinX > bMaxX || aMaxY < bMinY || aMinY > bMaxY);
@ -75,6 +84,9 @@ public class GeoRelationUtils {
return !(aMinX < bMinX || aMinY < bMinY || aMaxX > bMaxX || aMaxY > bMaxY);
}
/**
* Computes whether two rectangles cross
*/
public static boolean rectCrosses(final double aMinX, final double aMinY, final double aMaxX, final double aMaxY,
final double bMinX, final double bMinY, final double bMaxX, final double bMaxY) {
return !(rectDisjoint(aMinX, aMinY, aMaxX, aMaxY, bMinX, bMinY, bMaxX, bMaxY) ||
@ -380,11 +392,18 @@ public class GeoRelationUtils {
|| SloppyMath.haversin(centerLat, centerLon, rMinY, rMaxX)*1000.0 > radiusMeters;
}
/**
* Convenience method for computing whether a rectangle is within a circle using additional precision checks
*/
public static boolean rectWithinCircle(final double rMinX, final double rMinY, final double rMaxX, final double rMaxY,
final double centerLon, final double centerLat, final double radiusMeters) {
return rectWithinCircle(rMinX, rMinY, rMaxX, rMaxY, centerLon, centerLat, radiusMeters, false);
}
/**
* Computes whether a rectangle is within a circle. Note: approx == true will be faster but less precise and may
* fail on large rectangles
*/
public static boolean rectWithinCircle(final double rMinX, final double rMinY, final double rMaxX, final double rMaxY,
final double centerLon, final double centerLat, final double radiusMeters,
final boolean approx) {
@ -401,6 +420,10 @@ public class GeoRelationUtils {
return rectCrossesCircle(rMinX, rMinY, rMaxX, rMaxY, centerLon, centerLat, radiusMeters, false);
}
/**
* Computes whether a rectangle crosses a circle. Note: approx == true will be faster but less precise and may
* fail on large rectangles
*/
public static boolean rectCrossesCircle(final double rMinX, final double rMinY, final double rMaxX, final double rMaxY,
final double centerLon, final double centerLat, final double radiusMeters,
final boolean approx) {

View File

@ -14,12 +14,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.util;
package org.apache.lucene.spatial.util;
import java.util.ArrayList;
import org.apache.lucene.util.BitUtil;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Math.PI;
import static java.lang.Math.abs;
import static org.apache.lucene.util.SloppyMath.asin;
import static org.apache.lucene.util.SloppyMath.cos;
import static org.apache.lucene.util.SloppyMath.sin;
import static org.apache.lucene.util.SloppyMath.TO_DEGREES;
import static org.apache.lucene.util.SloppyMath.TO_RADIANS;
import static org.apache.lucene.spatial.util.GeoProjectionUtils.MAX_LAT_RADIANS;
import static org.apache.lucene.spatial.util.GeoProjectionUtils.MAX_LON_RADIANS;
import static org.apache.lucene.spatial.util.GeoProjectionUtils.MIN_LAT_RADIANS;
import static org.apache.lucene.spatial.util.GeoProjectionUtils.MIN_LON_RADIANS;
import static org.apache.lucene.spatial.util.GeoProjectionUtils.pointFromLonLatBearingGreatCircle;
import static org.apache.lucene.spatial.util.GeoProjectionUtils.SEMIMAJOR_AXIS;
/**
* Basic reusable geo-spatial utility methods
@ -27,9 +43,11 @@ import static org.apache.lucene.util.SloppyMath.TO_RADIANS;
* @lucene.experimental
*/
public final class GeoUtils {
/** number of bits used for quantizing latitude and longitude values */
public static final short BITS = 31;
private static final double LON_SCALE = (0x1L<<BITS)/360.0D;
private static final double LAT_SCALE = (0x1L<<BITS)/180.0D;
/** rounding error for quantized latitude and longitude values */
public static final double TOLERANCE = 1E-6;
/** Minimum longitude value. */
@ -48,14 +66,20 @@ public final class GeoUtils {
private GeoUtils() {
}
/**
* encode longitude, latitude geopoint values using morton encoding method
* https://en.wikipedia.org/wiki/Z-order_curve
*/
public static final Long mortonHash(final double lon, final double lat) {
return BitUtil.interleave(scaleLon(lon), scaleLat(lat));
}
/** decode longitude value from morton encoded geo point */
public static final double mortonUnhashLon(final long hash) {
return unscaleLon(BitUtil.deinterleave(hash));
}
/** decode latitude value from morton encoded geo point */
public static final double mortonUnhashLat(final long hash) {
return unscaleLat(BitUtil.deinterleave(hash >>> 1));
}
@ -76,17 +100,13 @@ public final class GeoUtils {
return (val / LAT_SCALE) + MIN_LAT_INCL;
}
/**
* Compare two position values within a {@link org.apache.lucene.util.GeoUtils#TOLERANCE} factor
*/
/** Compare two position values within a {@link GeoUtils#TOLERANCE} factor */
public static double compare(final double v1, final double v2) {
final double delta = v1-v2;
return Math.abs(delta) <= TOLERANCE ? 0 : delta;
return abs(delta) <= TOLERANCE ? 0 : delta;
}
/**
* Puts longitude in range of -180 to +180.
*/
/** Puts longitude in range of -180 to +180. */
public static double normalizeLon(double lon_deg) {
if (lon_deg >= -180 && lon_deg <= 180) {
return lon_deg; //common case, and avoids slight double precision shifting
@ -101,17 +121,16 @@ public final class GeoUtils {
}
}
/**
* Puts latitude in range of -90 to 90.
*/
/** Puts latitude in range of -90 to 90. */
public static double normalizeLat(double lat_deg) {
if (lat_deg >= -90 && lat_deg <= 90) {
return lat_deg; //common case, and avoids slight double precision shifting
}
double off = Math.abs((lat_deg + 90) % 360);
double off = abs((lat_deg + 90) % 360);
return (off <= 180 ? off : 360-off) - 90;
}
/** Converts long value to bit string (useful for debugging) */
public static String geoTermToString(long term) {
StringBuilder s = new StringBuilder(64);
final int numberOfLeadingZeros = Long.numberOfLeadingZeros(term);
@ -145,7 +164,7 @@ public final class GeoUtils {
final int sidesLen = sides-1;
for (int i=0; i<sidesLen; ++i) {
angle = (i*360/sides);
pt = GeoProjectionUtils.pointFromLonLatBearingGreatCircle(lon, lat, angle, radiusMeters, pt);
pt = pointFromLonLatBearingGreatCircle(lon, lat, angle, radiusMeters, pt);
lons[i] = pt[0];
lats[i] = pt[1];
}
@ -158,42 +177,38 @@ public final class GeoUtils {
return geometry;
}
/**
* Compute Bounding Box for a circle using WGS-84 parameters
*/
/** Compute Bounding Box for a circle using WGS-84 parameters */
public static GeoRect circleToBBox(final double centerLon, final double centerLat, final double radiusMeters) {
final double radLat = TO_RADIANS * centerLat;
final double radLon = TO_RADIANS * centerLon;
double radDistance = radiusMeters / GeoProjectionUtils.SEMIMAJOR_AXIS;
double radDistance = radiusMeters / SEMIMAJOR_AXIS;
double minLat = radLat - radDistance;
double maxLat = radLat + radDistance;
double minLon;
double maxLon;
if (minLat > GeoProjectionUtils.MIN_LAT_RADIANS && maxLat < GeoProjectionUtils.MAX_LAT_RADIANS) {
double deltaLon = SloppyMath.asin(SloppyMath.sin(radDistance) / SloppyMath.cos(radLat));
if (minLat > MIN_LAT_RADIANS && maxLat < MAX_LAT_RADIANS) {
double deltaLon = asin(sin(radDistance) / cos(radLat));
minLon = radLon - deltaLon;
if (minLon < GeoProjectionUtils.MIN_LON_RADIANS) {
minLon += 2d * StrictMath.PI;
if (minLon < MIN_LON_RADIANS) {
minLon += 2d * PI;
}
maxLon = radLon + deltaLon;
if (maxLon > GeoProjectionUtils.MAX_LON_RADIANS) {
maxLon -= 2d * StrictMath.PI;
if (maxLon > MAX_LON_RADIANS) {
maxLon -= 2d * PI;
}
} else {
// a pole is within the distance
minLat = StrictMath.max(minLat, GeoProjectionUtils.MIN_LAT_RADIANS);
maxLat = StrictMath.min(maxLat, GeoProjectionUtils.MAX_LAT_RADIANS);
minLon = GeoProjectionUtils.MIN_LON_RADIANS;
maxLon = GeoProjectionUtils.MAX_LON_RADIANS;
minLat = max(minLat, MIN_LAT_RADIANS);
maxLat = min(maxLat, MAX_LAT_RADIANS);
minLon = MIN_LON_RADIANS;
maxLon = MAX_LON_RADIANS;
}
return new GeoRect(TO_DEGREES * minLon, TO_DEGREES * maxLon, TO_DEGREES * minLat, TO_DEGREES * maxLat);
}
/**
* Compute Bounding Box for a polygon using WGS-84 parameters
*/
/** Compute Bounding Box for a polygon using WGS-84 parameters */
public static GeoRect polyToBBox(double[] polyLons, double[] polyLats) {
if (polyLons.length != polyLats.length) {
throw new IllegalArgumentException("polyLons and polyLats must be equal length");
@ -211,26 +226,22 @@ public final class GeoUtils {
if (GeoUtils.isValidLat(polyLats[i]) == false) {
throw new IllegalArgumentException("invalid polyLats[" + i + "]=" + polyLats[i]);
}
minLon = Math.min(polyLons[i], minLon);
maxLon = Math.max(polyLons[i], maxLon);
minLat = Math.min(polyLats[i], minLat);
maxLat = Math.max(polyLats[i], maxLat);
minLon = min(polyLons[i], minLon);
maxLon = max(polyLons[i], maxLon);
minLat = min(polyLats[i], minLat);
maxLat = max(polyLats[i], maxLat);
}
// expand bounding box by TOLERANCE factor to handle round-off error
return new GeoRect(Math.max(minLon - TOLERANCE, MIN_LON_INCL), Math.min(maxLon + TOLERANCE, MAX_LON_INCL),
Math.max(minLat - TOLERANCE, MIN_LAT_INCL), Math.min(maxLat + TOLERANCE, MAX_LAT_INCL));
return new GeoRect(max(minLon - TOLERANCE, MIN_LON_INCL), min(maxLon + TOLERANCE, MAX_LON_INCL),
max(minLat - TOLERANCE, MIN_LAT_INCL), min(maxLat + TOLERANCE, MAX_LAT_INCL));
}
/**
* validates latitude value is within standard +/-90 coordinate bounds
*/
/** validates latitude value is within standard +/-90 coordinate bounds */
public static boolean isValidLat(double lat) {
return Double.isNaN(lat) == false && lat >= MIN_LAT_INCL && lat <= MAX_LAT_INCL;
}
/**
* validates longitude value is within standard +/-180 coordinate bounds
*/
/** validates longitude value is within standard +/-180 coordinate bounds */
public static boolean isValidLon(double lon) {
return Double.isNaN(lon) == false && lon >= MIN_LON_INCL && lon <= MAX_LON_INCL;
}

View File

@ -14,27 +14,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.search;
package org.apache.lucene.spatial.search;
import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.GeoPointField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.BaseGeoPointTestCase;
import org.apache.lucene.util.GeoRect;
import org.apache.lucene.util.GeoRelationUtils;
import org.apache.lucene.util.GeoUtils;
import org.apache.lucene.spatial.document.GeoPointField;
import org.apache.lucene.spatial.util.BaseGeoPointTestCase;
import org.apache.lucene.spatial.util.GeoRect;
import org.apache.lucene.spatial.util.GeoRelationUtils;
import org.apache.lucene.spatial.util.GeoUtils;
import org.apache.lucene.util.SloppyMath;
import org.apache.lucene.util.TestUtil;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import static org.apache.lucene.util.GeoDistanceUtils.DISTANCE_PCT_ERR;
import static org.apache.lucene.spatial.util.GeoDistanceUtils.DISTANCE_PCT_ERR;
/**
* Unit testing for basic GeoPoint query logic

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.util;
package org.apache.lucene.spatial.util;
import java.io.IOException;
import java.text.DecimalFormat;
@ -45,6 +45,11 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.SimpleCollector;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.MockDirectoryWrapper;
import org.apache.lucene.util.FixedBitSet;
import org.apache.lucene.util.IOUtils;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.SloppyMath;
import org.apache.lucene.util.TestUtil;
import org.junit.BeforeClass;
// TODO: cutover TestGeoUtils too?

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.util;
package org.apache.lucene.spatial.util;
import java.io.PrintWriter;
import java.io.StringWriter;
@ -24,11 +24,12 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.lucene.util.LuceneTestCase;
import org.junit.BeforeClass;
import com.carrotsearch.randomizedtesting.generators.RandomInts;
import static org.apache.lucene.util.GeoDistanceUtils.DISTANCE_PCT_ERR;
import static org.apache.lucene.spatial.util.GeoDistanceUtils.DISTANCE_PCT_ERR;
/**
* Tests class for methods in GeoUtils