mirror of https://github.com/apache/lucene.git
LUCENE-6997: refactor sandboxed GeoPointField and query classes to lucene-spatial module
This commit is contained in:
parent
4569fd732a
commit
665041c52f
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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.
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -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) {
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
|
||||
/**
|
|
@ -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;
|
|
@ -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
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 {
|
|
@ -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) {
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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?
|
|
@ -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
|
Loading…
Reference in New Issue